diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2024-03-01 21:17:01 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-03-01 21:17:01 +0900 |
| commit | 7e706ea6693781fb8973800bb3d0c91b5ab91cdf (patch) | |
| tree | 8abd91edd288284cf3cc47949e749f881cfaff8e | |
| parent | Merge pull request #13045 from misskey-dev/develop (diff) | |
| parent | New translations ja-jp.yml (Chinese Traditional) (#13480) (diff) | |
| download | sharkey-7e706ea6693781fb8973800bb3d0c91b5ab91cdf.tar.gz sharkey-7e706ea6693781fb8973800bb3d0c91b5ab91cdf.tar.bz2 sharkey-7e706ea6693781fb8973800bb3d0c91b5ab91cdf.zip | |
Merge pull request #13447 from misskey-dev/develop
Release: 2024.3.0
187 files changed, 5046 insertions, 2988 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 32c9bd0aec..15028e7008 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,44 @@ --> +## 2024.3.0 + +### General +- Enhance: 投稿者ã®ãƒãƒ¼ãƒ«ã«å¿œã˜ã¦ã€ä¸€ã¤ã®ãƒŽãƒ¼ãƒˆã«å«ã‚€ã“ã¨ã®ã§ãるメンションã¨ãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆæŠ•稿ã®å®›å…ˆã®äººæ•°ã«ä¸Šé™ã‚’è¨å®šã§ãるよã†ã« + * デフォルトã®ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ä¸Šé™ã¯20アカウントã«è¨å®šã•れã¾ã™ã€‚(管ç†è€…ã¯ãƒ™ãƒ¼ã‚¹ãƒãƒ¼ãƒ«ã®è¨å®šã§å¤‰æ›´å¯èƒ½ã§ã™ã€‚) + * 連åˆã®å•ã„åˆã‚ã›ã«å¿œç”ã—ãªã„サーãƒãƒ¼ã®ãƒªãƒ¢ãƒ¼ãƒˆãƒ¦ãƒ¼ã‚¶ãƒ¼ã¸ã®ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ã¯ã€ä¸Šé™ã®äººæ•°ã«å«ã‚ãªã„実装ã«ãªã£ã¦ã„ã¾ã™ã€‚ +- Enhance: 通知ãŒãƒŸãƒ¥ãƒ¼ãƒˆã€å‡çµã‚’考慮ã™ã‚‹ã‚ˆã†ã«ãªã‚Šã¾ã—㟠+- Enhance: サーãƒãƒ¼ã”ã¨ã«ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãƒŽãƒ¼ãƒˆã‚’残ã›ã‚‹ã‚ˆã†ã« +- Enhance: コンディショナルãƒãƒ¼ãƒ«ã®æ¡ä»¶ã«ã€Œãƒžãƒ‹ãƒ¥ã‚¢ãƒ«ãƒãƒ¼ãƒ«ã¸ã®ã‚¢ã‚µã‚¤ãƒ³ã€ã‚’è¿½åŠ +- Enhance: 通知ã®å—ä¿¡è¨å®šã«ã€Œãƒ•ã‚©ãƒãƒ¼ä¸ã¾ãŸã¯ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã€ã‚’è¿½åŠ +- Enhance: 通知ã®å±¥æ´ã‚’リセットã§ãるよã†ã« +- Fix: ダイレクトãªãƒŽãƒ¼ãƒˆã«å¯¾ã—ã¦ã¯ãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆã§ã—ã‹è¿”ä¿¡ã§ããªã„よã†ã« + +### Client +- Enhance: ノート作æˆç”»é¢ã®ãƒ•ァイル添付メニューã®åŒºåˆ‡ã‚Šç·šã®ä½ç½®ã‚’調整 +- Fix: syuilo/misskeyã®æ™‚代ã‹ã‚‰ã‚ã‚‹ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ãŒæ”¹å¤‰ã•れãŸãƒãƒ¼ã‚¸ãƒ§ãƒ³ã§ã‚ã‚‹ã¨èª¤èªè˜ã•れるå•題 +- Fix: MFMã®ã‚ªãƒ¼ãƒˆã‚³ãƒ³ãƒ—リートãŒå‡ºã‚‹ã¹ã状æ³ã§å‡ºãªã„ã“ã¨ãŒã‚ã‚‹å•é¡Œã‚’ä¿®æ£ +- Fix: ãƒãƒ£ãƒ¼ãƒˆã®ãƒ©ãƒ™ãƒ«ãŒæ¶ˆãˆã¦ã„ã‚‹å•é¡Œã‚’ä¿®æ£ +- Fix: ç”»é¢è¡¨ç¤ºå¾Œæœ€åˆã®éŸ³å£°å†ç”ŸãŒçˆ†éŸ³ã«ãªã‚‹ã“ã¨ãŒã‚ã‚‹å•é¡Œã‚’ä¿®æ£ +- Fix: è¨å®šã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ä½œæˆæ™‚ã«åå‰ã‚’入力ã—ãªã‹ã£ãŸå ´åˆã€ãƒãƒ¼ã‚«ãƒ©ã‚¤ã‚¼ãƒ¼ã‚·ãƒ§ãƒ³ãŒãŠã‹ã—ããªã‚‹å•é¡Œã‚’ä¿®æ£ +- Fix: ページ`/admin/emojis`ã®çµµæ–‡å—編集ダイアãƒã‚°ã§ã€Œãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¨ã—ã¦ä½¿ãˆã‚‹ãƒãƒ¼ãƒ«ã€ã‚’è¿½åŠ ã™ã‚‹éš›ã«ä½•ã‚‚é¸æŠžã›ãšOKを押下ã™ã‚‹ã¨ç”»é¢ãŒå›ºã¾ã‚‹å•é¡Œã‚’ä¿®æ£ +- Fix: 絵文å—サジェストã®é †ä½ã§ã€çµµæ–‡å—自体ã®åå‰ãŒåŒã˜ã‚‚ã®ã‚ˆã‚Šã‚‚ã‚¿ã‚°ã§ä¸€è‡´ã—ã¦ã„ã‚‹ã‚‚ã®ãŒå„ªå…ˆã•れã¦ã—ã¾ã†å•é¡Œã‚’ä¿®æ£ +- Fix: ãƒ¦ãƒ¼ã‚¶ã®æƒ…å ±ã®ãƒãƒƒãƒ—ã‚¢ãƒƒãƒ—ãŒæ¶ˆãˆãªããªã‚‹ã“ã¨ãŒã‚ã‚‹å•é¡Œã‚’ä¿®æ£ + +### Server +- Enhance: エンドãƒã‚¤ãƒ³ãƒˆ`flash/update`ã®`flashId`以外ã®ãƒ‘ラメータã¯å¿…é ˆã§ã¯ãªããªã‚Šã¾ã—㟠+- Fix: nodeinfoã«enableMcaptchaã¨enableTurnstileãŒç„¡ã„ã®ã‚’ä¿®æ£ +- Fix: ç ´æã—ãŸé€šçŸ¥ã‚’クライアントã«é€ä¿¡ã—ãªã„よã†ã« + * 通知欄ãŒç„¡é™ã«ãƒªãƒãƒ¼ãƒ‰ã•れるå•é¡ŒãŒæ”¹å–„ã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ +- Fix: ç¦æ¢ã‚ーワードをå«ã‚€ãƒŽãƒ¼ãƒˆãŒDelayed Queueã«è¿½åŠ ã•れã¦å†å‡¦ç†ã•れるå•é¡Œã‚’ä¿®æ£ +- Fix: 自分ãŒãƒ•ã‚©ãƒãƒ¼ã—ã¦ã„ãªã„アカウントã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼é™å®šãƒŽãƒ¼ãƒˆãŒé–²è¦§ã§ãã‚‹ã“ã¨ãŒã‚ã‚‹å•é¡Œã‚’ä¿®æ£ +- Fix: タイムラインã®ã‚ªãƒ—ションã§ã€ŒãƒªãƒŽãƒ¼ãƒˆã‚’表示ã€ã‚’無効ã«ã—ã¦ã„ã‚‹éš›ã€æŠ•ç¥¨ã®ã¿ã®å¼•ç”¨ãƒªãƒŽãƒ¼ãƒˆãŒæµã‚Œã¦ã“ãªã„å•é¡Œã‚’ä¿®æ£ +- Fix: エンドãƒã‚¤ãƒ³ãƒˆ`admin/emoji/update`ã®å„ç¨®ä¿®æ£ + - å¿…é ˆãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ã‚’`id`ã¾ãŸã¯`name`ã®ã„ãšã‚Œã‹ã®ã¿ã« + - `id`ã®ä»£ã‚りã«`name`ã§çµµæ–‡å—を指定å¯èƒ½ã«ï¼ˆ`id`・`name`両指定時ã¯å¾“æ¥é€šã‚Š`name`を変更ã™ã‚‹æŒ™å‹•) + - `category`ãŠã‚ˆã³`licence`ãŒæŒ‡å®šãªã—ã®æ™‚勿‰‹ã«nullã«ä¸Šæ›¸ãã•ã‚Œã‚‹æŒ™å‹•ã‚’ä¿®æ£ +- Fix: 通知ã®å—ä¿¡è¨å®šã§ã€Œç›¸äº’フォãƒãƒ¼ã€ãŒæ£ã—ã動作ã—ãªã„å•é¡Œã‚’ä¿®æ£ + ## 2024.2.0 ### Note diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index b0f7408587..17c8f24fa5 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -1014,6 +1014,7 @@ renotes: "أعد النشر" sourceCode: "Ø§Ù„Ø´ÙØ±Ø© المصدرية" flip: "اقلب" lastNDays: "آخر {n} أيام" +surrender: "ألغÙ" _initialAccountSetting: accountCreated: "Ù†Ø¬Ø Ø¥Ù†Ø´Ø§Ø¡ ØØ³Ø§Ø¨Ùƒ!" letsStartAccountSetup: "إذا كنت جديدًا لنعدّ ØØ³Ø§Ø¨Ùƒ الشخصي." diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index af5329dc7e..2ea6bd9309 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -1210,6 +1210,7 @@ hemisphere: "Geolocalització" withSensitive: "Incloure notes amb fitxers sensibles" userSaysSomethingSensitive: "La publicació de {name} conte material sensible" enableHorizontalSwipe: "Lliscar per canviar de pestanya" +surrender: "Cancel·lar " _bubbleGame: howToPlay: "Com es juga" _howToPlay: diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index 3161ff275a..cbf5c33c18 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -1098,6 +1098,7 @@ renotes: "PÅ™eposlat" sourceCode: "Zdrojový kód" flip: "OtoÄit" lastNDays: "PoslednÃch {n} dnů" +surrender: "ZruÅ¡it" _initialAccountSetting: accountCreated: "Váš úÄet byl úspěšnÄ› vytvoÅ™en!" letsStartAccountSetup: "Pro zaÄátek si nastavte svůj profil." diff --git a/locales/de-DE.yml b/locales/de-DE.yml index f733fa1ee9..9a22a7b445 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -1184,6 +1184,7 @@ decorate: "Dekorieren" addMfmFunction: "MFM hinzufügen" sfx: "Soundeffekte" lastNDays: "Letzten {n} Tage" +surrender: "Abbrechen" _announcement: forExistingUsers: "Nur für existierende Nutzer" forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt." diff --git a/locales/en-US.yml b/locales/en-US.yml index 084f6b23b9..d00f23632c 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -991,6 +991,7 @@ neverShow: "Don't show again" remindMeLater: "Maybe later" didYouLikeMisskey: "Have you taken a liking to Misskey?" pleaseDonate: "{host} uses the free software, Misskey. We would highly appreciate your donations so development of Misskey can continue!" +correspondingSourceIsAvailable: "The corresponding source code is available at {anchor}" roles: "Roles" role: "Role" noRole: "Role not found" @@ -1041,6 +1042,8 @@ resetPasswordConfirm: "Really reset your password?" sensitiveWords: "Sensitive words" sensitiveWordsDescription: "The visibility of all notes containing any of the configured words will be set to \"Home\" automatically. You can list multiple by separating them via line breaks." sensitiveWordsDescription2: "Using spaces will create AND expressions and surrounding keywords with slashes will turn them into a regular expression." +prohibitedWords: "Prohibited words" +prohibitedWordsDescription: "Enables an error when attempting to post a note containing the set word(s). Multiple words can be set, separated by a new line." prohibitedWordsDescription2: "Using spaces will create AND expressions and surrounding keywords with slashes will turn them into a regular expression." hiddenTags: "Hidden hashtags" hiddenTagsDescription: "Select tags which will not shown on trend list.\nMultiple tags could be registered by lines." @@ -1157,6 +1160,7 @@ showRenotes: "Show renotes" edited: "Edited" notificationRecieveConfig: "Notification Settings" mutualFollow: "Mutual follow" +followingOrFollower: "Following or follower" fileAttachedOnly: "Only notes with files" showRepliesToOthersInTimeline: "Show replies to others in timeline" hideRepliesToOthersInTimeline: "Hide replies to others from timeline" @@ -1166,6 +1170,12 @@ confirmShowRepliesAll: "This operation is irreversible. Would you really like to confirmHideRepliesAll: "This operation is irreversible. Would you really like to hide replies to others from everyone you follow in your timeline?" externalServices: "External Services" sourceCode: "Source code" +sourceCodeIsNotYetProvided: "Source code is not yet available. Contact the administrator to fix this problem." +repositoryUrl: "Repository URL" +repositoryUrlDescription: "If you are using Misskey as is (without any changes to the source code), enter https://github.com/misskey-dev/misskey" +repositoryUrlOrTarballRequired: "If you have not published a repository, you must provide a tarball instead. See .config/example.yml for more information." +feedback: "Feedback" +feedbackUrl: "Feedback URL" impressum: "Impressum" impressumUrl: "Impressum URL" impressumDescription: "In some countries, like germany, the inclusion of operator contact information (an Impressum) is legally required for commercial websites." @@ -1201,6 +1211,8 @@ soundWillBePlayed: "Sound will be played" showReplay: "View Replay" replay: "Replay" replaying: "Showing replay" +endReplay: "Exit Replay" +copyReplayData: "Copy replay data" ranking: "Ranking" lastNDays: "Last {n} days" backToTitle: "Go back to title" @@ -1208,8 +1220,20 @@ hemisphere: "Where are you located" withSensitive: "Include notes with sensitive files" userSaysSomethingSensitive: "Post by {name} contains sensitive content" enableHorizontalSwipe: "Swipe to switch tabs" +loading: "Loading" +surrender: "Cancel" +gameRetry: "Retry" _bubbleGame: howToPlay: "How to play" + hold: "Hold" + _score: + score: "Score" + scoreYen: "Amount of money earned" + highScore: "High score" + maxChain: "Maximum number of chains" + yen: "{yen} Yen" + estimatedQty: "{qty} Pieces" + scoreSweets: "{onigiriQtyWithUnit} Onigiri" _howToPlay: section1: "Adjust the position and drop the object into the box." section2: "When two objects of the same type touch each other, they will change into a different object and you score points." @@ -1631,6 +1655,7 @@ _role: gtlAvailable: "Can view the global timeline" ltlAvailable: "Can view the local timeline" canPublicNote: "Can send public notes" + mentionMax: "Maximum number of mentions in a note" canInvite: "Can create instance invite codes" inviteLimit: "Invite limit" inviteLimitCycle: "Invite limit cooldown" @@ -1654,6 +1679,7 @@ _role: canUseTranslator: "Translator usage" avatarDecorationLimit: "Maximum number of avatar decorations that can be applied" _condition: + roleAssignedTo: "Assigned to manual roles" isLocal: "Local user" isRemote: "Remote user" createdLessThan: "Less than X has passed since account creation" @@ -1754,6 +1780,8 @@ _aboutMisskey: contributors: "Main contributors" allContributors: "All contributors" source: "Source code" + original: "Original" + thisIsModifiedVersion: "{name} uses a modified version of the original Misskey." translation: "Translate Misskey" donate: "Donate to Misskey" morePatrons: "We also appreciate the support of many other helpers not listed here. Thank you! 🥰" @@ -2272,6 +2300,7 @@ _notification: reactedBySomeUsers: "{n} users reacted" renotedBySomeUsers: "Renote from {n} users" followedBySomeUsers: "Followed by {n} users" + flushNotification: "Clear notifications" _types: all: "All" note: "New notes" @@ -2369,6 +2398,7 @@ _moderationLogTypes: resetPassword: "Password reset" suspendRemoteInstance: "Remote instance suspended" unsuspendRemoteInstance: "Remote instance unsuspended" + updateRemoteInstanceNote: "Moderation note updated for remote instance." markSensitiveDriveFile: "File marked as sensitive" unmarkSensitiveDriveFile: "File unmarked as sensitive" resolveAbuseReport: "Report resolved" @@ -2489,6 +2519,8 @@ _reversi: opponentHasSettingsChanged: "The opponent has changed their settings." allowIrregularRules: "Irregular rules (completely free)" disallowIrregularRules: "No irregular rules" + showBoardLabels: "Display row and column numbering on the board" + useAvatarAsStone: "Turn stones into user avatars" _offlineScreen: title: "Offline - cannot connect to the server" header: "Unable to connect to the server" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 2952e89f83..246ec23604 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -1209,6 +1209,7 @@ hemisphere: "Región" withSensitive: "Mostrar notas que contengan material sensible" userSaysSomethingSensitive: "La publicación de {name} contiene material sensible" enableHorizontalSwipe: "Deslice para cambiar de pestaña" +surrender: "detener" _bubbleGame: howToPlay: "Cómo jugar" _howToPlay: diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index 35fac49cdd..b7ab4d37c4 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -1164,7 +1164,11 @@ remainingN: "Restants : {n}" overwriteContentConfirm: "Voulez-vous remplacer le contenu actuel ?" seasonalScreenEffect: "Effet d'écran saisonnier" decorate: "Décorer" +sfx: "Effets sonores" +showReplay: "Voir le replay" +ranking: "Classement" lastNDays: "Derniers {n} jours" +surrender: "Annuler" _announcement: forExistingUsers: "Pour les utilisateurs existants seulement" readConfirmTitle: "Marquer comme lu ?" @@ -1302,10 +1306,13 @@ _achievements: title: "Régulier III" description: "Se connecter pour un total de 400 jours" _login500: + title: "Expert I" description: "Se connecter pour un total de 500 jours" _login600: + title: "Expert II" description: "Se connecter pour un total de 600 jours" _login700: + title: "Expert III" description: "Se connecter pour un total de 700 jours" _login800: description: "Se connecter pour un total de 800 jours" @@ -1400,9 +1407,12 @@ _role: description: "Description du rôle" permission: "Rôle et autorisations" assignTarget: "Attribuer" + manual: "Manuel" manualRoles: "Rôles manuels" + conditional: "Conditionnel" conditionalRoles: "Rôles conditionnels" condition: "Condition" + isConditionalRole: "Ceci est un rôle conditionnel." isPublic: "Rôle public" options: "Options" policies: "Stratégies" diff --git a/locales/id-ID.yml b/locales/id-ID.yml index 58a248996b..514a2866ca 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -1209,6 +1209,7 @@ hemisphere: "Letak kamu tinggal" withSensitive: "Lampirkan catatan dengan berkas sensitif" userSaysSomethingSensitive: "Postingan oleh {name} mengandung konten sensitif" enableHorizontalSwipe: "Geser untuk mengganti tab" +surrender: "Batalkan" _bubbleGame: howToPlay: "Cara bermain" _howToPlay: diff --git a/locales/index.d.ts b/locales/index.d.ts index 1bc99ab849..c1aa163f98 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -4657,6 +4657,10 @@ export interface Locale extends ILocale { */ "mutualFollow": string; /** + * フォãƒãƒ¼ä¸ã¾ãŸã¯ãƒ•ã‚©ãƒãƒ¯ãƒ¼ + */ + "followingOrFollower": string; + /** * ファイル付ãã®ã¿ */ "fileAttachedOnly": string; @@ -4857,6 +4861,14 @@ export interface Locale extends ILocale { */ "replaying": string; /** + * リプレイを終了 + */ + "endReplay": string; + /** + * リプレイデータをコピー + */ + "copyReplayData": string; + /** * ランã‚ング */ "ranking": string; @@ -4884,11 +4896,57 @@ export interface Locale extends ILocale { * スワイプã—ã¦ã‚¿ãƒ–を切り替ãˆã‚‹ */ "enableHorizontalSwipe": string; + /** + * èªã¿è¾¼ã¿ä¸ + */ + "loading": string; + /** + * ã‚„ã‚ã‚‹ + */ + "surrender": string; + /** + * リトライ + */ + "gameRetry": string; "_bubbleGame": { /** * éŠã³æ–¹ */ "howToPlay": string; + /** + * ホールド + */ + "hold": string; + "_score": { + /** + * スコア + */ + "score": string; + /** + * 稼ã„ã é‡‘é¡ + */ + "scoreYen": string; + /** + * ãƒã‚¤ã‚¹ã‚³ã‚¢ + */ + "highScore": string; + /** + * 最大ãƒã‚§ãƒ¼ãƒ³æ•° + */ + "maxChain": string; + /** + * {yen}円 + */ + "yen": ParameterizedString<"yen">; + /** + * {qty}個分 + */ + "estimatedQty": ParameterizedString<"qty">; + /** + * ãŠã«ãŽã‚Š {onigiriQtyWithUnit} + */ + "scoreSweets": ParameterizedString<"onigiriQtyWithUnit">; + }; "_howToPlay": { /** * ä½ç½®ã‚’調整ã—ã¦ãƒã‚³ã«ãƒ¢ãƒŽã‚’è½ã¨ã—ã¾ã™ã€‚ @@ -6385,6 +6443,10 @@ export interface Locale extends ILocale { */ "canPublicNote": string; /** + * ãƒŽãƒ¼ãƒˆå†…ã®æœ€å¤§ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³æ•° + */ + "mentionMax": string; + /** * サーãƒãƒ¼æ‹›å¾…コードã®ç™ºè¡Œ */ "canInvite": string; @@ -6475,6 +6537,10 @@ export interface Locale extends ILocale { }; "_condition": { /** + * マニュアルãƒãƒ¼ãƒ«ã«ã‚¢ã‚µã‚¤ãƒ³æ¸ˆã¿ + */ + "roleAssignedTo": string; + /** * ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ */ "isLocal": string; @@ -8851,6 +8917,10 @@ export interface Locale extends ILocale { * {n}人ã«ãƒ•ã‚©ãƒãƒ¼ã•れã¾ã—㟠*/ "followedBySomeUsers": ParameterizedString<"n">; + /** + * 通知ã®å±¥æ´ã‚’リセットã™ã‚‹ + */ + "flushNotification": string; "_types": { /** * ã™ã¹ã¦ @@ -9172,7 +9242,7 @@ export interface Locale extends ILocale { */ "updateServerSettings": string; /** - * モデレーションノート更新 + * ユーザーã®ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãƒŽãƒ¼ãƒˆæ›´æ–° */ "updateUserNote": string; /** @@ -9220,6 +9290,10 @@ export interface Locale extends ILocale { */ "unsuspendRemoteInstance": string; /** + * リモートサーãƒãƒ¼ã®ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãƒŽãƒ¼ãƒˆæ›´æ–° + */ + "updateRemoteInstanceNote": string; + /** * ファイルをセンシティブ付与 */ "markSensitiveDriveFile": string; @@ -9655,6 +9729,14 @@ export interface Locale extends ILocale { * 変則ãªã— */ "disallowIrregularRules": string; + /** + * 盤é¢ã«è¡Œãƒ»åˆ—番å·ã‚’表示 + */ + "showBoardLabels": string; + /** + * 石をアイコンã«ã™ã‚‹ + */ + "useAvatarAsStone": string; }; "_offlineScreen": { /** diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 378036af6d..480d11b6ba 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -991,6 +991,7 @@ neverShow: "Non mostrare più" remindMeLater: "Rimanda" didYouLikeMisskey: "Ti piace Misskey?" pleaseDonate: "Misskey è il software libero utilizzato su {host}. Offrendo una donazione è più facile continuare a svilupparlo!" +correspondingSourceIsAvailable: "" roles: "Ruoli" role: "Ruolo" noRole: "Ruolo non trovato" @@ -1168,6 +1169,12 @@ confirmShowRepliesAll: "Questa è una attività irreversibile. Vuoi davvero incl confirmHideRepliesAll: "Questa è una attività irreversibile. Vuoi davvero escludere tutte le risposte dei following in TL?" externalServices: "Servizi esterni" sourceCode: "Codice sorgente" +sourceCodeIsNotYetProvided: "" +repositoryUrl: "URL della repository" +repositoryUrlDescription: "Se esiste un repository il cui il codice sorgente è disponibile pubblicamente, inserisci il suo URL. Se stai utilizzando Misskey così com'è (senza alcuna modifica al codice sorgente), inserisci https://github.com/misskey-dev/misskey." +repositoryUrlOrTarballRequired: "Se non disponi di un repository pubblico, dovrai fornire un file tarball (tar). Vedere .config/example.yml per i dettagli." +feedback: "Feedback" +feedbackUrl: "URL di feedback" impressum: "Dichiarazione di proprietà " impressumUrl: "URL della dichiarazione di proprietà " impressumDescription: "La dichiarazione di proprietà , è obbligatoria in alcuni paesi come la Germania (Impressum)." @@ -1199,7 +1206,7 @@ addMfmFunction: "Aggiungi decorazioni" enableQuickAddMfmFunction: "Attiva il selettore di funzioni MFM" bubbleGame: "Bubble Game" sfx: "Effetti sonori" -soundWillBePlayed: "Verrà riprodotto il suono" +soundWillBePlayed: "Con musica ed effetti sonori" showReplay: "Vedi i replay" replay: "Replay" replaying: "Replay in corso" @@ -1210,12 +1217,13 @@ hemisphere: "Geolocalizzazione" withSensitive: "Mostra le Note con allegati espliciti" userSaysSomethingSensitive: "Note da {name} con allegati espliciti" enableHorizontalSwipe: "Trascina per invertire i tab" +surrender: "Annulla" _bubbleGame: howToPlay: "Come giocare" _howToPlay: - section1: "Regola la posizione e rilascia l'oggetto nella casella." - section2: "Ottieni un punteggio, quando due oggetti dello stesso tipo si toccano e si trasformano in un oggetto diverso." - section3: "Se gli oggetti traboccano dalla scatola, il gioco finisce. Cerca di ottenere un punteggio elevato fondendo gli oggetti, evitando che escano dalla scatola!" + section1: "Scegli la posizione e rilascia l'oggetto nel contenitore." + section2: "Se due oggetti dello stesso tipo si toccano, si trasformano in un oggetto diverso, aumentando il punteggio." + section3: "Se gli oggetti escono dal limite superiore del contenitore, il gioco finisce. Cerca di ottenere un punteggio elevato fondendo gli oggetti, evitando che escano dal contenitore!" _announcement: forExistingUsers: "Solo ai profili attuali" forExistingUsersDescription: "L'annuncio sarà visibile solo ai profili esistenti in questo momento. Se disabilitato, sarà visibile anche ai profili che verranno creati dopo la pubblicazione di questo annuncio." @@ -1756,6 +1764,8 @@ _aboutMisskey: contributors: "Principali sostenitori" allContributors: "Tutti i sostenitori" source: "Codice sorgente" + original: "Originale" + thisIsModifiedVersion: "{name} sta usando una versione modificata diversa da Misskey originale." translation: "Tradurre Misskey" donate: "Sostieni Misskey" morePatrons: "Apprezziamo sinceramente il supporto di tante altre persone. Grazie mille! 🥰" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 5993ec80d0..51380e49c5 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1160,6 +1160,7 @@ showRenotes: "リノートを表示" edited: "編集済ã¿" notificationRecieveConfig: "通知ã®å—ä¿¡è¨å®š" mutualFollow: "相互フォãƒãƒ¼" +followingOrFollower: "フォãƒãƒ¼ä¸ã¾ãŸã¯ãƒ•ã‚©ãƒãƒ¯ãƒ¼" fileAttachedOnly: "ファイル付ãã®ã¿" showRepliesToOthersInTimeline: "TLã«ä»–ã®äººã¸ã®è¿”ä¿¡ã‚’å«ã‚ã‚‹" hideRepliesToOthersInTimeline: "TLã«ä»–ã®äººã¸ã®è¿”ä¿¡ã‚’å«ã‚ãªã„" @@ -1210,6 +1211,8 @@ soundWillBePlayed: "サウンドãŒå†ç”Ÿã•れã¾ã™" showReplay: "リプレイを見る" replay: "リプレイ" replaying: "リプレイä¸" +endReplay: "リプレイを終了" +copyReplayData: "リプレイデータをコピー" ranking: "ランã‚ング" lastNDays: "ç›´è¿‘{n}æ—¥" backToTitle: "タイトルã¸" @@ -1217,9 +1220,21 @@ hemisphere: "ãŠä½ã¾ã„ã®åœ°åŸŸ" withSensitive: "センシティブãªãƒ•ァイルをå«ã‚€ãƒŽãƒ¼ãƒˆã‚’表示" userSaysSomethingSensitive: "{name}ã®ã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ãªãƒ•ァイルをå«ã‚€æŠ•稿" enableHorizontalSwipe: "スワイプã—ã¦ã‚¿ãƒ–を切り替ãˆã‚‹" +loading: "èªã¿è¾¼ã¿ä¸" +surrender: "ã‚„ã‚ã‚‹" +gameRetry: "リトライ" _bubbleGame: howToPlay: "éŠã³æ–¹" + hold: "ホールド" + _score: + score: "スコア" + scoreYen: "稼ã„ã 金é¡" + highScore: "ãƒã‚¤ã‚¹ã‚³ã‚¢" + maxChain: "最大ãƒã‚§ãƒ¼ãƒ³æ•°" + yen: "{yen}円" + estimatedQty: "{qty}個分" + scoreSweets: "ãŠã«ãŽã‚Š {onigiriQtyWithUnit}" _howToPlay: section1: "ä½ç½®ã‚’調整ã—ã¦ãƒã‚³ã«ãƒ¢ãƒŽã‚’è½ã¨ã—ã¾ã™ã€‚" section2: "åŒã˜ç¨®é¡žã®ãƒ¢ãƒŽãŒãã£ã¤ãã¨åˆ¥ã®ãƒ¢ãƒŽã«å¤‰åŒ–ã—ã¦ã€ã‚¹ã‚³ã‚¢ãŒå¾—られã¾ã™ã€‚" @@ -1650,6 +1665,7 @@ _role: gtlAvailable: "ã‚°ãƒãƒ¼ãƒãƒ«ã‚¿ã‚¤ãƒ ラインã®é–²è¦§" ltlAvailable: "ãƒãƒ¼ã‚«ãƒ«ã‚¿ã‚¤ãƒ ラインã®é–²è¦§" canPublicNote: "パブリック投稿ã®è¨±å¯" + mentionMax: "ãƒŽãƒ¼ãƒˆå†…ã®æœ€å¤§ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³æ•°" canInvite: "サーãƒãƒ¼æ‹›å¾…コードã®ç™ºè¡Œ" inviteLimit: "招待コードã®ä½œæˆå¯èƒ½æ•°" inviteLimitCycle: "招待コードã®ç™ºè¡Œé–“éš”" @@ -1673,6 +1689,7 @@ _role: canUseTranslator: "翻訳機能ã®åˆ©ç”¨" avatarDecorationLimit: "ã‚¢ã‚¤ã‚³ãƒ³ãƒ‡ã‚³ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã®æœ€å¤§å–付個数" _condition: + roleAssignedTo: "マニュアルãƒãƒ¼ãƒ«ã«ã‚¢ã‚µã‚¤ãƒ³æ¸ˆã¿" isLocal: "ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼" isRemote: "リモートユーザー" createdLessThan: "アカウント作æˆã‹ã‚‰ï½žä»¥å†…" @@ -2340,6 +2357,7 @@ _notification: reactedBySomeUsers: "{n}人ãŒãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã—ã¾ã—ãŸ" renotedBySomeUsers: "{n}人ãŒãƒªãƒŽãƒ¼ãƒˆã—ã¾ã—ãŸ" followedBySomeUsers: "{n}人ã«ãƒ•ã‚©ãƒãƒ¼ã•れã¾ã—ãŸ" + flushNotification: "通知ã®å±¥æ´ã‚’リセットã™ã‚‹" _types: all: "ã™ã¹ã¦" @@ -2434,7 +2452,7 @@ _moderationLogTypes: updateCustomEmoji: "ã‚«ã‚¹ã‚¿ãƒ çµµæ–‡å—æ›´æ–°" deleteCustomEmoji: "カスタム絵文å—削除" updateServerSettings: "サーãƒãƒ¼è¨å®šæ›´æ–°" - updateUserNote: "モデレーションノート更新" + updateUserNote: "ユーザーã®ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãƒŽãƒ¼ãƒˆæ›´æ–°" deleteDriveFile: "ファイルを削除" deleteNote: "ノートを削除" createGlobalAnnouncement: "全体ã®ãŠçŸ¥ã‚‰ã›ã‚’作æˆ" @@ -2446,6 +2464,7 @@ _moderationLogTypes: resetPassword: "パスワードをリセット" suspendRemoteInstance: "リモートサーãƒãƒ¼ã‚’åœæ¢" unsuspendRemoteInstance: "リモートサーãƒãƒ¼ã‚’å†é–‹" + updateRemoteInstanceNote: "リモートサーãƒãƒ¼ã®ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãƒŽãƒ¼ãƒˆæ›´æ–°" markSensitiveDriveFile: "ファイルをセンシティブ付与" unmarkSensitiveDriveFile: "ファイルをセンシティブ解除" resolveAbuseReport: "é€šå ±ã‚’è§£æ±º" @@ -2571,6 +2590,8 @@ _reversi: opponentHasSettingsChanged: "相手ãŒè¨å®šã‚’変更ã—ã¾ã—ãŸ" allowIrregularRules: "å¤‰å‰‡è¨±å¯ (完全フリー)" disallowIrregularRules: "変則ãªã—" + showBoardLabels: "盤é¢ã«è¡Œãƒ»åˆ—番å·ã‚’表示" + useAvatarAsStone: "石をアイコンã«ã™ã‚‹" _offlineScreen: title: "オフライン - サーãƒãƒ¼ã«æŽ¥ç¶šã§ãã¾ã›ã‚“" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 4b5f98e803..7ff26c757d 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -991,6 +991,7 @@ neverShow: "今後表示ã—ãªã„" remindMeLater: "ã¾ãŸå¾Œã§" didYouLikeMisskey: "Misskeyæ°—ã«å…¥ã£ã¦ãれãŸï¼Ÿ" pleaseDonate: "Misskeyã¯{host}ãŒä½¿ã†ã¨ã‚‹ç„¡æ–™ã®ã‚½ãƒ•トウェアやã§ã€‚ã“れã‹ã‚‰ã‚‚開発を続ã‘れるよã†ã«ã€å¯„付ã—ãŸã£ã¦ãªï½žã€‚" +correspondingSourceIsAvailable: "{anchor}" roles: "ãƒãƒ¼ãƒ«" role: "ãƒãƒ¼ãƒ«" noRole: "ãƒãƒ¼ãƒ«ã¯ã‚りã¾ã¸ã‚“" @@ -1208,6 +1209,7 @@ hemisphere: "ä½ã‚“ã§ã‚‹åœ°åŸŸ" withSensitive: "センシティブãªãƒ•ァイルをå«ã‚€ãƒŽãƒ¼ãƒˆã‚’表示" userSaysSomethingSensitive: "{name}ã®ã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ãªãƒ•ァイルをå«ã‚€æŠ•稿" enableHorizontalSwipe: "スワイプã—ã¦ã‚¿ãƒ–を切り替ãˆã‚‹" +surrender: "ã‚„ã‚ã¨ã" _bubbleGame: howToPlay: "éŠã³æ–¹" _howToPlay: diff --git a/locales/ko-GS.yml b/locales/ko-GS.yml index b1702114be..39492d902f 100644 --- a/locales/ko-GS.yml +++ b/locales/ko-GS.yml @@ -640,6 +640,7 @@ icon: "아바타" replies: "답하기" renotes: "리노트" attach: "옇기" +surrender: "ì•„ì´ì˜ˆ" _initialAccountSetting: startTutorial: "길ë¼ìž¡ì´ 하기" _initialTutorial: diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index c4646b6a86..877ae6b217 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -991,6 +991,7 @@ neverShow: "다시 ë³´ì§€ 않기" remindMeLater: "ë‚˜ì¤‘ì— ì•Œë¦¼" didYouLikeMisskey: "Misskeyê°€ 마ìŒì— 드시나요?" pleaseDonate: "Misskey는 {host} ì„œë²„ì˜ ë¬´ë£Œ 소프트웨어입니다. ì•žìœ¼ë¡œë„ ê°œë°œì„ ì´ì–´ ë‚˜ê°€ë ¤ë©´ 후ì›ì´ ì ˆì‹¤ížˆ 필요합니다!" +correspondingSourceIsAvailable: "소스 코드는 {anchor}ì—서 받아보실 수 있습니다." roles: "ì—í• " role: "ì—í• " noRole: "ì—í• ì´ ì—†ìŠµë‹ˆë‹¤" @@ -1168,6 +1169,12 @@ confirmShowRepliesAll: "ì´ ì¡°ìž‘ì€ ë˜ëŒë¦´ 수 없습니다. ì •ë§ë¡œ 타ì confirmHideRepliesAll: "ì´ ì¡°ìž‘ì€ ë˜ëŒë¦´ 수 없습니다. ì •ë§ë¡œ 타임ë¼ì¸ì— 현재 팔로우 ì¤‘ì¸ ì‚¬ëžŒ ì „ì›ì˜ ë‹µê¸€ì´ ë‚˜ì˜¤ì§€ 않게 í•˜ì‹œê² ìŠµë‹ˆê¹Œ?" externalServices: "외부 서비스" sourceCode: "소스 코드" +sourceCodeIsNotYetProvided: "소스 코드를 ì•„ì§ ì œê³µí•˜ì§€ 않습니다. ì´ ë¬¸ì œë¥¼ í•´ê²°í•˜ë ¤ë©´ 관리ìžì—게 문ì˜í•´ 주세요." +repositoryUrl: "ì €ìž¥ì†Œ URL" +repositoryUrlDescription: "소스 코드를 공개한 ì €ìž¥ì†Œê°€ 있는 경우, ê·¸ URLì„ ì 습니다. Misskey를 ì›ë³¸ 그대로 (소스 코드를 ì–´ë–¤ ì‹ìœ¼ë¡œë„ 변경하지 ì•Šê³ ) ì“°ê³ ìžˆëŠ” 경우 https://github.com/misskey-dev/misskey ë¼ê³ ì 습니다." +repositoryUrlOrTarballRequired: "ì €ìž¥ì†Œë¥¼ 공개하지 ì•Šì€ ê²½ìš° ëŒ€ì‹ tarballì„ ì œê³µí• í•„ìš”ê°€ 있습니다. 세부사í•ì€ .config/example.ymlì„ ì°¸ì¡°í•´ 주세요." +feedback: "피드백" +feedbackUrl: "피드백 URL" impressum: "ìš´ì˜ìž ì •ë³´" impressumUrl: "ìš´ì˜ìž ì •ë³´ URL" impressumDescription: "ë…ì¼ ë“±ì˜ ì¼ë¶€ 나ë¼ì™€ ì§€ì—ì—서는 ê¼ í‘œì‹œí•´ì•¼ 합니다(Impressum)." @@ -1210,6 +1217,7 @@ hemisphere: "거주 ì§€ì—" withSensitive: "민ê°í•œ 파ì¼ì´ í¬í•¨ëœ 노트 보기" userSaysSomethingSensitive: "{name}ì˜ ë¯¼ê°í•œ 파ì¼ì´ í¬í•¨ëœ 게시물" enableHorizontalSwipe: "스와ì´í”„하여 íƒ ì „í™˜" +surrender: "그만ë‘기" _bubbleGame: howToPlay: "설명" _howToPlay: @@ -1756,6 +1764,8 @@ _aboutMisskey: contributors: "주요 기여ìž" allContributors: "ëª¨ë“ ê¸°ì—¬ìž" source: "소스 코드" + original: "ì›ë³¸" + thisIsModifiedVersion: "{name}ì—서는 ì›ë³¸ 미스키를 ìˆ˜ì •í•œ ë²„ì „ì„ ì‚¬ìš©í•˜ê³ ìžˆìŠµë‹ˆë‹¤." translation: "Misskey를 번ì—하기" donate: "Misskeyì— ê¸°ë¶€í•˜ê¸°" morePatrons: "ì´ ì™¸ì—ë„ ë‹¤ë¥¸ ë§Žì€ ë¶„ë“¤ì´ ë„ì›€ì„ ì£¼ì‹œê³ ê³„ì‹ë‹ˆë‹¤. ê°ì‚¬í•©ë‹ˆë‹¤ðŸ¥°" @@ -2371,6 +2381,7 @@ _moderationLogTypes: resetPassword: "비밀번호 ìž¬ì„¤ì •" suspendRemoteInstance: "리모트 서버를 ì •ì§€" unsuspendRemoteInstance: "리모트 ì„œë²„ì˜ ì •ì§€ë¥¼ í•´ì œ" + updateRemoteInstanceNote: "리모트 ì„œë²„ì˜ ì¡°ì • ê¸°ë¡ ê°±ì‹ " markSensitiveDriveFile: "파ì¼ì— 열람주ì˜ë¥¼ ì„¤ì •" unmarkSensitiveDriveFile: "파ì¼ì— 열람주ì˜ë¥¼ í•´ì œ" resolveAbuseReport: "ì‹ ê³ ì²˜ë¦¬" diff --git a/locales/no-NO.yml b/locales/no-NO.yml index 85ccd62566..098faa8add 100644 --- a/locales/no-NO.yml +++ b/locales/no-NO.yml @@ -463,6 +463,7 @@ options: "Alternativ" icon: "Avatar" replies: "Svar" renotes: "Renote" +surrender: "Avbryt" _initialAccountSetting: theseSettingsCanEditLater: "Du kan endre disse innstillingene senere." _achievements: diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml index bf8a8ca38b..f62557fb23 100644 --- a/locales/pt-PT.yml +++ b/locales/pt-PT.yml @@ -1011,6 +1011,7 @@ renotes: "Repostar" keepScreenOn: "Manter a tela do dispositivo sempre ligada" flip: "Inversão" lastNDays: "Últimos {n} dias" +surrender: "Cancelar" _initialAccountSetting: followUsers: "Siga usuários que lhe interessam para criar a sua linha do tempo." _serverSettings: diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 6141eba5f0..d666b69490 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -1085,6 +1085,7 @@ loadReplies: "Показать ответы" sourceCode: "ИÑходный код" flip: "Переворот" lastNDays: "ПоÑледние {n} Ñут" +surrender: "Ðтот поÑÑ‚ не может быть отменен." _initialAccountSetting: accountCreated: "Ðккаунт уÑпешно Ñоздан!" letsStartAccountSetup: "Давайте наÑтроим вашу учётную запиÑÑŒ." diff --git a/locales/th-TH.yml b/locales/th-TH.yml index b5a54a39ec..c0e79d5e16 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -8,12 +8,12 @@ search: "ค้นหา" notifications: "à¸à¸²à¸£à¹€à¹€à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™" username: "ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" password: "รหัสผ่าน" -forgotPassword: "ลืมรหัสผ่านใช่ไหม" +forgotPassword: "ลืมรหัสผ่าน" fetchingAsApObject: "à¸à¸³à¸¥à¸±à¸‡à¸”ึงข้à¸à¸¡à¸¹à¸¥à¸ˆà¸²à¸à¸ªà¸«à¸žà¸±à¸™à¸˜à¹Œ..." ok: "ตà¸à¸¥à¸‡" gotIt: "เข้าใจà¹à¸¥à¹‰à¸§ !" cancel: "ยà¸à¹€à¸¥à¸´à¸" -noThankYou: "ไม่เป็นไร" +noThankYou: "ไม่เà¸à¸²à¸”ีà¸à¸§à¹ˆà¸²" enterUsername: "à¸à¸£à¸à¸à¸Šà¸·à¹ˆà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" renotedBy: "รีโน้ตโดย {user}" noNotes: "ไม่มีโน้ต" @@ -31,16 +31,16 @@ login: "เข้าสู่ระบบ" loggingIn: "à¸à¸³à¸¥à¸±à¸‡à¹€à¸‚้าสู่ระบบ" logout: "à¸à¸à¸à¸ˆà¸²à¸à¸£à¸°à¸šà¸š" signup: "สร้างบัà¸à¸Šà¸µà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" -uploading: "à¸à¸³à¸¥à¸±à¸‡à¸à¸±à¸žà¹‚หลด..." +uploading: "à¸à¸³à¸¥à¸±à¸‡à¸à¸±à¸›à¹‚หลด" save: "บันทึà¸" users: "ผู้ใช้งาน" addUser: "เพิ่มผู้ใช้" favorite: "รายà¸à¸²à¸£à¹‚ปรด" favorites: "รายà¸à¸²à¸£à¹‚ปรด" unfavorite: "ลบà¸à¸à¸à¸ˆà¸²à¸à¸£à¸²à¸¢à¸à¸²à¸£à¹‚ปรด" -favorited: "เพิ่มà¹à¸¥à¹‰à¸§à¹ƒà¸™à¸£à¸²à¸¢à¸à¸²à¸£à¹‚ปรด" -alreadyFavorited: "เพิ่มในรายà¸à¸²à¸£à¹‚ปรดà¸à¸¢à¸¹à¹ˆà¹à¸¥à¹‰à¸§" -cantFavorite: "ไม่สามารถเพิ่มในรายà¸à¸²à¸£à¹‚ปรดได้" +favorited: "เพิ่มลงรายà¸à¸²à¸£à¹‚ปรดà¹à¸¥à¹‰à¸§" +alreadyFavorited: "เพิ่มลงรายà¸à¸²à¸£à¹‚ปรดà¸à¸¢à¸¹à¹ˆà¹à¸¥à¹‰à¸§" +cantFavorite: "ไม่สามารถเพิ่มลงรายà¸à¸²à¸£à¹‚ปรดได้" pin: "ปัà¸à¸«à¸¡à¸¸à¸”" unpin: "เลิà¸à¸›à¸±à¸à¸«à¸¡à¸¸à¸”" copyContent: "คัดลà¸à¸à¹€à¸™à¸·à¹‰à¸à¸«à¸²" @@ -65,18 +65,18 @@ loadMore: "à¹à¸ªà¸”งเพิ่มเติม" showMore: "à¹à¸ªà¸”งเพิ่มเติม" showLess: "ปิด" youGotNewFollower: "ได้ติดตามคุณ" -receiveFollowRequest: "คำขà¸à¸œà¸¹à¹‰à¸•ิดตามที่ได้รับ" -followRequestAccepted: "à¸à¸™à¸¸à¸¡à¸±à¸•ิà¸à¸²à¸£à¸•ิดตามà¹à¸¥à¹‰à¸§" +receiveFollowRequest: "มีคำขà¸à¸•ิดตามส่งมาหา" +followRequestAccepted: "à¸à¸²à¸£à¸•ิดตามได้รับà¸à¸²à¸£à¸à¸™à¸¸à¸¡à¸±à¸•ิà¹à¸¥à¹‰à¸§" mention: "à¸à¸¥à¹ˆà¸²à¸§à¸–ึง" mentions: "พูดถึง" -directNotes: "ไดเร็คโน้ต" +directNotes: "โพสต์à¹à¸šà¸šà¹„ดเร็à¸à¸•์" importAndExport: "นำเข้า / ส่งà¸à¸à¸" import: "นำเข้า" export: "ส่งà¸à¸à¸" files: "ไฟล์" download: "ดาวน์โหลด" -driveFileDeleteConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¹„ฟล์ “{name}†ใช่หรืà¸à¹„ม่? โน้ตที่à¹à¸™à¸šà¸¡à¸²à¸à¸±à¸šà¹„ฟล์นี้à¸à¹‡à¸ˆà¸°à¸–ูà¸à¸¥à¸šà¹„ปด้วย" -unfollowConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸•ิดตาม {name}?" +driveFileDeleteConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¹„ฟล์ “{name}†ใช่ไหม? โน้ตที่à¹à¸™à¸šà¸¡à¸²à¸à¸±à¸šà¹„ฟล์นี้à¸à¹‡à¸ˆà¸°à¸–ูà¸à¸¥à¸šà¹„ปด้วย" +unfollowConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸•ิดตาม {name} ใช่ไหม?" exportRequested: "คุณได้ร้à¸à¸‡à¸‚à¸à¸à¸²à¸£à¸ªà¹ˆà¸‡à¸à¸à¸ à¸à¸²à¸ˆà¹ƒà¸Šà¹‰à¹€à¸§à¸¥à¸²à¸ªà¸±à¸à¸„รู่ à¹à¸¥à¸°à¸ˆà¸°à¸–ูà¸à¹€à¸žà¸´à¹ˆà¸¡à¹ƒà¸™à¹„ดรฟ์ขà¸à¸‡à¸„ุณเมื่à¸à¹€à¸ªà¸£à¹‡à¸ˆà¸ªà¸´à¹‰à¸™à¹à¸¥à¹‰à¸§" importRequested: "คุณได้ร้à¸à¸‡à¸‚à¸à¸à¸²à¸£à¸™à¸³à¹€à¸‚้า à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¸à¸²à¸ˆà¹ƒà¸Šà¹‰à¹€à¸§à¸¥à¸²à¸ªà¸±à¸à¸„รู่" lists: "รายชื่à¸" @@ -128,9 +128,9 @@ emojiPickerDisplay: "à¹à¸ªà¸”งตัวจิ้มเà¸à¹‚มจิ" overwriteFromPinnedEmojisForReaction: "เขียนทับà¸à¸²à¸£à¸•ั้งค่ารีà¹à¸à¸„ชั่น" overwriteFromPinnedEmojis: "เขียนทับà¸à¸²à¸£à¸•ั้งค่าทั่วไป" reactionSettingDescription2: "ลาà¸à¹€à¸žà¸·à¹ˆà¸à¸ˆà¸±à¸”ลำดับใหม่ คลิà¸à¸—ี่เà¸à¹‚มจินั้นเพื่à¸à¸¥à¸š à¸à¸” “+†เพื่à¸à¹€à¸žà¸´à¹ˆà¸¡" -rememberNoteVisibility: "จดจำà¸à¸²à¸£à¸•ั้งค่าà¸à¸²à¸£à¸¡à¸à¸‡à¹€à¸«à¹‡à¸™à¸•ัวโน้ต" -attachCancel: "ลบไฟล์à¸à¸à¸à¸—ี่à¹à¸™à¸šà¸¡à¸²" -deleteFile: "ลบไฟล์à¸à¸à¸à¹à¸¥à¹‰à¸§" +rememberNoteVisibility: "จำà¸à¸²à¸£à¸•ั้งค่าà¸à¸²à¸£à¸¡à¸à¸‡à¹€à¸«à¹‡à¸™à¹‚น้ต" +attachCancel: "ยà¸à¹€à¸¥à¸´à¸à¹à¸™à¸šà¹„ฟล์" +deleteFile: "ลบไฟล์à¸à¸à¸" markAsSensitive: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" unmarkAsSensitive: "ยà¸à¹€à¸¥à¸´à¸à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" enterFileName: "พิมพ์ชื่à¸à¹„ฟล์" @@ -138,14 +138,14 @@ mute: "ปิดเสียง" unmute: "ยà¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¸›à¸´à¸”เสียง" renoteMute: "ปิดเสียงรีโน้ต" renoteUnmute: "เปิดเสียง รีโน้ต" -block: "บล็à¸à¸„" -unblock: "เลิà¸à¸›à¸´à¸”à¸à¸±à¹‰à¸™" -suspend: "ถูà¸à¸£à¸°à¸‡à¸±à¸š" -unsuspend: "ยà¸à¹€à¸¥à¸´à¸à¸£à¸°à¸‡à¸±à¸š" -blockConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸šà¸¥à¹‡à¸à¸à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰?" -unblockConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸›à¸¥à¸”บล็à¸à¸„บัà¸à¸Šà¸µà¸™à¸µà¹‰?" -suspendConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸£à¸°à¸‡à¸±à¸šà¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰?" -unsuspendConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¢à¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¸£à¸°à¸‡à¸±à¸šà¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰?" +block: "บล็à¸à¸" +unblock: "เลิà¸à¸šà¸¥à¹‡à¸à¸" +suspend: "ระงับ" +unsuspend: "เลิà¸à¸£à¸°à¸‡à¸±à¸š" +blockConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸šà¸¥à¹‡à¸à¸à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹ƒà¸Šà¹ˆà¹„หม?" +unblockConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸šà¸¥à¹‡à¸à¸à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹ƒà¸Šà¹ˆà¹„หม?" +suspendConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸£à¸°à¸‡à¸±à¸šà¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹ƒà¸Šà¹ˆà¹„หม?" +unsuspendConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¢à¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¸£à¸°à¸‡à¸±à¸šà¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹ƒà¸Šà¹ˆà¹„หม?" selectList: "เลืà¸à¸à¸£à¸²à¸¢à¸Šà¸·à¹ˆà¸" editList: "à¹à¸à¹‰à¹„ขรายชื่à¸" selectChannel: "เลืà¸à¸à¸Šà¹ˆà¸à¸‡" @@ -162,13 +162,13 @@ emojiUrl: "URL ขà¸à¸‡à¹€à¸à¹‚มจิ" addEmoji: "à¹à¸—รà¸à¹€à¸à¹‚มจิ" settingGuide: "à¸à¸²à¸£à¸•ั้งค่าที่à¹à¸™à¸°à¸™à¸³" cacheRemoteFiles: "à¹à¸„ชไฟล์ระยะไà¸à¸¥" -cacheRemoteFilesDescription: "เมื่à¸à¸›à¸´à¸”ใช้งานà¸à¸²à¸£à¸•ั้งค่านี้ ไฟล์ระยะไà¸à¸¥à¸™à¸±à¹‰à¸™à¸ˆà¸°à¸–ูà¸à¹‚หลดโดยตรงจาà¸à¸à¸´à¸™à¸ªà¹à¸•นซ์ระยะไà¸à¸¥ à¹à¸•่à¸à¸£à¸“ีà¸à¸²à¸£à¸›à¸´à¸”ใช้งานนี้จะช่วยลดปริมาณà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸žà¸·à¹‰à¸™à¸—ี่จัดเà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥ à¹à¸•่เพิ่มปริมาณà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ เพราะเนื่à¸à¸‡à¸ˆà¸²à¸à¸ˆà¸°à¹„ม่มีà¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¸ าพขนาดย่à¸" +cacheRemoteFilesDescription: "หาà¸à¹€à¸›à¸´à¸”ใช้งาน ไฟล์ระยะไà¸à¸¥à¸ˆà¸°à¸–ูà¸à¹à¸„ชไว้ ทำให้à¹à¸ªà¸”งภาพเร็วขึ้น à¹à¸•่à¸à¹‡à¹ƒà¸Šà¹‰à¸žà¸·à¹‰à¸™à¸—ี่เà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥à¸‚à¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸¡à¸²à¸à¸‚ึ้นเช่นà¸à¸±à¸™ สำหรับขีดจำà¸à¸±à¸”ที่ผู้ใช้ระยะไà¸à¸¥à¸–ูà¸à¹à¸„ชไว้จะขึ้นà¸à¸¢à¸¹à¹ˆà¸à¸±à¸šà¸„วามจุไดรฟ์ตามบทบาทขà¸à¸‡à¹€à¸‚า เมื่à¸à¹€à¸à¸´à¸™à¹à¸¥à¹‰à¸§à¹„ฟล์เà¸à¹ˆà¸²à¸ˆà¸°à¸–ูà¸à¸¥à¸šà¸à¸à¸à¹à¸¥à¸°à¹€à¸à¹‡à¸šà¹€à¸›à¹‡à¸™à¸¥à¸´à¸‡à¸à¹Œà¹à¸—น หาà¸à¸›à¸´à¸”ใช้งาน ไฟล์ระยะไà¸à¸¥à¸ˆà¸°à¸–ูà¸à¹€à¸à¹‡à¸šà¹€à¸›à¹‡à¸™à¸¥à¸´à¸‡à¸à¹Œà¸•ั้งà¹à¸•่ต้น เราà¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¸•ั้งค่า proxyRemoteFiles ใน default.yml เป็น true เพื่à¸à¸ªà¸£à¹‰à¸²à¸‡à¸˜à¸±à¸¡à¸šà¹Œà¹€à¸™à¸¥à¹à¸¥à¸°à¸›à¸à¸›à¹‰à¸à¸‡à¸„วามเป็นส่วนตัวขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" youCanCleanRemoteFilesCache: "คุณสามารถล้างà¹à¸„ชได้โดยคลิà¸à¸—ี่ปุ่ม ðŸ—‘ï¸ à¹ƒà¸™à¸¡à¸¸à¸¡à¸¡à¸à¸‡à¸à¸²à¸£à¸ˆà¸±à¸”à¸à¸²à¸£à¹„ฟล์" -cacheRemoteSensitiveFiles: "à¹à¸„ชไฟล์ระยะไà¸à¸¥à¸—ี่มีเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" +cacheRemoteSensitiveFiles: "à¹à¸„ชไฟล์ระยะไà¸à¸¥à¸—ี่มีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" cacheRemoteSensitiveFilesDescription: "เมื่à¸à¸›à¸´à¸”à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸à¸²à¸£à¸•ั้งค่านี้ ไฟล์ระยะไà¸à¸¥à¸—ี่มีเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™à¸™à¸±à¹‰à¸™à¸ˆà¸°à¸–ูà¸à¹‚หลดโดยตรงจาà¸à¸à¸´à¸™à¸ªà¹à¸•นซ์ระยะไà¸à¸¥à¹‚ดยที่ไม่มีà¸à¸²à¸£à¹à¸„ช" -flagAsBot: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸šà¸à¸à¸§à¹ˆà¸²à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¸šà¸à¸—" +flagAsBot: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸šà¸à¸à¸§à¹ˆà¸²à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¸šà¸à¸•" flagAsBotDescription: "à¸à¸²à¸£à¹€à¸›à¸´à¸”ใช้งานตัวเลืà¸à¸à¸™à¸µà¹‰à¸«à¸²à¸à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¸–ูà¸à¸„วบคุมโดยนัà¸à¹€à¸‚ียนโปรà¹à¸à¸£à¸¡ หรืภถ้าหาà¸à¹€à¸›à¸´à¸”ใช้งาน มันจะทำหน้าที่เป็นà¹à¸Ÿà¸¥à¹‡à¸à¸ªà¸³à¸«à¸£à¸±à¸šà¸™à¸±à¸à¸žà¸±à¸’นารายà¸à¸·à¹ˆà¸™à¹† à¹à¸¥à¸°à¹€à¸žà¸·à¹ˆà¸à¸›à¹‰à¸à¸‡à¸à¸±à¸™à¸à¸²à¸£à¹‚ต้ตà¸à¸šà¹à¸šà¸šà¹„ม่มีที่สิ้นสุดà¸à¸±à¸šà¸šà¸à¸—ตัวà¸à¸·à¹ˆà¸™à¹† à¹à¸¥à¸°à¸¢à¸±à¸‡à¸ªà¸²à¸¡à¸²à¸£à¸–ปรับเปลี่ยนระบบภายในขà¸à¸‡ Misskey เพื่à¸à¸›à¸à¸´à¸šà¸±à¸•ิต่à¸à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¸šà¸à¸—" -flagAsCat: "เมี้ยววววววว!!!!!!!!!!! (ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¹à¸¡à¸§)" +flagAsCat: "เมี้ยววววววววววววววว!!!!!!!!!!!" flagAsCatDescription: "เหมียวเหมียวเมี้ยว??" flagShowTimelineReplies: "à¹à¸ªà¸”งตà¸à¸šà¸à¸¥à¸±à¸š ในไทม์ไลน์" flagShowTimelineRepliesDescription: "à¹à¸ªà¸”งà¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¸‚à¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¹„ปยังโน้ตขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸£à¸²à¸¢à¸à¸·à¹ˆà¸™à¹†à¹ƒà¸™à¹„ทม์ไลน์หาà¸à¹„ด้เปิดเà¸à¸²à¹„ว้" @@ -180,7 +180,7 @@ showOnRemote: "ดูบนà¸à¸´à¸™à¸ªà¹à¸•นซ์ระยะไà¸à¸¥" general: "ทั่วไป" wallpaper: "ภาพพื้นหลัง" setWallpaper: "ตั้งค่าภาพพื้นหลัง" -removeWallpaper: "น้ำภาพพื้นหลังà¸à¸à¸" +removeWallpaper: "นำภาพพื้นหลังà¸à¸à¸" searchWith: "ค้นหา: {q}" youHaveNoLists: "คุณไม่มีรายชื่à¸à¹ƒà¸”ๆ " followConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸•ิดตาม {name} ใช่ไหม?" @@ -189,11 +189,11 @@ proxyAccountDescription: "บัà¸à¸Šà¸µà¸žà¸£à¹‡à¸à¸à¸‹à¸µà¹ˆ คืภบà host: "โฮสต์" selectUser: "เลืà¸à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" recipient: "ผู้รับ" -annotation: "ความคิดเห็น" +annotation: "หมายเหตุประà¸à¸à¸š" federation: "สหพันธ์" instances: "à¸à¸´à¸™à¸ªà¹à¸•นซ์" -registeredAt: "จดทะเบียนที่" -latestRequestReceivedAt: "ได้รับคำขà¸à¸¥à¹ˆà¸²à¸ªà¸¸à¸”ไปà¹à¸¥à¹‰à¸§" +registeredAt: "วันที่ลงทะเบียน" +latestRequestReceivedAt: "คำขà¸à¸¥à¹ˆà¸²à¸ªà¸¸à¸”ที่ได้รับ" latestStatus: "สถานะล่าสุด" storageUsage: "พื้นที่จัดเà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥à¸—ี่ใช้ไป" charts: "โดดเด่น" @@ -215,10 +215,10 @@ disk: "ดิสà¸à¹Œ" instanceInfo: "ข้à¸à¸¡à¸¹à¸¥à¸à¸´à¸™à¸ªà¹à¸•นซ์" statistics: "สถิติà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" clearQueue: "ล้างคิว" -clearQueueConfirmTitle: "คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¸«à¸£à¸à¸§à¹ˆà¸²à¸•้à¸à¸‡à¸à¸²à¸£à¸—ี่จะล้างคิว?" +clearQueueConfirmTitle: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¹‰à¸²à¸‡à¸„ิวใช่ไหม?" clearQueueConfirmText: "โพสต์ที่ยังค้างในคิวจะไม่ถูà¸à¸ˆà¸±à¸”ส่งà¸à¸µà¸à¸•่à¸à¹„ป โดยปà¸à¸•ิà¹à¸¥à¹‰à¸§à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¹„ม่จำเป็น" clearCachedFiles: "ล้างà¹à¸„ช" -clearCachedFilesConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¹„ฟล์ระยะไà¸à¸¥à¸—ี่à¹à¸„ชไว้ทั้งหมด?" +clearCachedFilesConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¹„ฟล์ระยะไà¸à¸¥à¸—ี่à¹à¸„ชไว้ทั้งหมดใช่ไหม?" blockedInstances: "à¸à¸´à¸™à¸ªà¹à¸•นซ์ที่ถูà¸à¸šà¸¥à¹‡à¸à¸" blockedInstancesDescription: "ระบุชื่à¸à¹‚ฮสต์ขà¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•นซ์ที่คุณต้à¸à¸‡à¸à¸²à¸£à¸šà¸¥à¹‡à¸à¸ à¸à¸´à¸™à¸ªà¹à¸•นซ์ที่à¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¸£à¸²à¸¢à¸à¸²à¸£à¸™à¸±à¹‰à¸™à¸ˆà¸°à¹„ม่สามารถพูดคุยà¸à¸±à¸šà¸à¸´à¸™à¸ªà¹à¸•นซ์นี้ได้à¸à¸µà¸à¸•่à¸à¹„ป" silencedInstances: "ปิดปาà¸à¸à¸´à¸™à¸ªà¹à¸•นซ์นี้à¹à¸¥à¹‰à¸§" @@ -228,7 +228,7 @@ mutedUsers: "ผู้ใช้ที่ถูà¸à¸›à¸´à¸”เสียง" blockedUsers: "ผู้ใช้ที่ถูà¸à¸šà¸¥à¹‡à¸à¸" noUsers: "ไม่พบผู้ใช้งาน" editProfile: "à¹à¸à¹‰à¹„ขโปรไฟล์" -noteDeleteConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¹‚น้ตนี้?" +noteDeleteConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¹‚น้ตนี้ใช่ไหม?" pinLimitExceeded: "คุณไม่สามารถปัà¸à¸«à¸¡à¸¸à¸”โน้ตเพิ่มเติมใดๆได้à¸à¸µà¸" intro: "à¸à¸²à¸£à¸•ิดตั้ง Misskey เสร็จสิ้นà¹à¸¥à¹‰à¸§à¸™à¸°! โปรดสร้างผู้ใช้งานที่เป็นผู้ดูà¹à¸¥à¸£à¸°à¸šà¸š" done: "เสร็จสิ้น" @@ -237,7 +237,7 @@ preview: "à¹à¸ªà¸”งตัวà¸à¸¢à¹ˆà¸²à¸‡" default: "ค่าเริ่มต้น" defaultValueIs: "ค่าเริ่มต้น: {value}" noCustomEmojis: "ไม่มีเà¸à¹‚มจิ" -noJobs: "ไม่มีชิ้นงาน" +noJobs: "ไม่มีงาน" federating: "สหพันธ์" blocked: "ถูà¸à¸šà¸¥à¹‡à¸à¸" suspended: "ถูà¸à¸£à¸°à¸‡à¸±à¸š" @@ -261,11 +261,11 @@ usernameOrUserId: "ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸«à¸£à¸·à¸à¸£à¸«à¸±à¸ªà¸œà¸¹à noSuchUser: "ไม่พบผู้ใช้" lookup: "à¸à¸²à¸£à¸„้นหา" announcements: "ประà¸à¸²à¸¨" -imageUrl: "url รูปภาพ" +imageUrl: "URL รูปภาพ" remove: "ลบ" removed: "ถูà¸à¸¥à¸šà¹„ปà¹à¸¥à¹‰à¸§" -removeAreYouSure: "ต้à¸à¸‡à¸à¸²à¸£à¸—ี่จะลบ “{x}†à¸à¸à¸?" -deleteAreYouSure: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸š {x} หรืà¸à¹„ม่คะ?" +removeAreYouSure: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸š “{x}†ใช่ไหม?" +deleteAreYouSure: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸š “{x}†ใช่ไหม?" resetAreYouSure: "รีเซ็ตเลยไหม?" areYouSure: "à¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¹ƒà¸Šà¹ˆà¹„หมคะ?" saved: "บันทึà¸à¹à¸¥à¹‰à¸§" @@ -275,7 +275,7 @@ keepOriginalUploading: "เà¸à¹‡à¸šà¸ าพต้นฉบับ" keepOriginalUploadingDescription: "เà¸à¹‡à¸šà¸ าพต้นฉบับไว้เมื่à¸à¸à¸±à¸›à¹‚หลดภาพ หาà¸à¸›à¸´à¸” รูปภาพสำหรับà¸à¸²à¸£à¹€à¸œà¸¢à¹à¸žà¸£à¹ˆà¸—างเว็บจะถูà¸à¸ªà¸£à¹‰à¸²à¸‡à¸‚ึ้นในเบราว์เซà¸à¸£à¹Œà¹€à¸¡à¸·à¹ˆà¸à¸—ำà¸à¸²à¸£à¸à¸±à¸›à¹‚หลด" fromDrive: "จาà¸à¹„ดรฟ์" fromUrl: "จาภURL" -uploadFromUrl: "à¸à¸±à¸žà¹‚หลดจาภURL" +uploadFromUrl: "à¸à¸±à¸›à¹‚หลดจาภURL" uploadFromUrlDescription: "URL ขà¸à¸‡à¹„ฟล์ที่คุณต้à¸à¸‡à¸à¸²à¸£à¸à¸±à¸›à¹‚หลด" uploadFromUrlRequested: "ร้à¸à¸‡à¸‚à¸à¸à¸²à¸£à¸à¸±à¸›à¹‚หลดà¹à¸¥à¹‰à¸§" uploadFromUrlMayTakeTime: "à¸à¸²à¸£à¸à¸±à¸›à¹‚หลดà¸à¸²à¸ˆà¹ƒà¸Šà¹‰à¹€à¸§à¸¥à¸²à¸ªà¸±à¸à¸„รู่จึงจะเสร็จสมบูรณ์" @@ -289,7 +289,7 @@ agree: "ยà¸à¸¡à¸£à¸±à¸š" agreeBelow: "ฉันยà¸à¸¡à¸£à¸±à¸šà¸–ึงด้านล่าง" basicNotesBeforeCreateAccount: "หมายเหตุสำคัà¸" termsOfService: "เงื่à¸à¸™à¹„ขà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£" -start: "เริ่มต้น​ใช้งาน​" +start: "เริ่ม" home: "หน้าà¹à¸£à¸" remoteUserCaution: "ข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸ˆà¹„ม่สมบูรณ์เนื่à¸à¸‡à¸ˆà¸²à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸£à¸²à¸¢à¸™à¸µà¹‰à¸¡à¸²à¸ˆà¸²à¸à¸à¸´à¸™à¸ªà¹à¸•นซ์ระยะไà¸à¸¥" activity: "à¸à¸´à¸ˆà¸à¸£à¸£à¸¡" @@ -333,11 +333,11 @@ rename: "เปลี่ยนชื่à¸" avatar: "ไà¸à¸„à¸à¸™" banner: "à¹à¸šà¸™à¹€à¸™à¸à¸£à¹Œ" displayOfSensitiveMedia: "à¹à¸ªà¸”งสื่à¸à¸—ี่มีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" -whenServerDisconnected: "สูà¸à¹€à¸ªà¸µà¸¢à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•่à¸à¸à¸±à¸šà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" -disconnectedFromServer: "ถูà¸à¸•ัดà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•่à¸à¸à¸à¸à¸ˆà¸²à¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" +whenServerDisconnected: "เมื่à¸à¸ªà¸¹à¸à¹€à¸ªà¸µà¸¢à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•่à¸à¸à¸±à¸šà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" +disconnectedFromServer: "à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•่à¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸–ูà¸à¸•ัด" reload: "รีโหลด" doNothing: "เมิน" -reloadConfirm: "นายต้à¸à¸‡à¸à¸²à¸£à¸£à¸µà¹€à¸Ÿà¸£à¸Šà¹„ทม์ไลน์หรืà¸à¸›à¹ˆà¸²à¸§?" +reloadConfirm: "รีโหลดเลยไหม?" watch: "ดู" unwatch: "หยุดดู" accept: "ยà¸à¸¡à¸£à¸±à¸š" @@ -347,7 +347,7 @@ instanceName: "ชื่à¸à¸à¸´à¸™à¸ªà¹à¸•นซ์" instanceDescription: "คำà¸à¸˜à¸´à¸šà¸²à¸¢à¸à¸´à¸™à¸ªà¹à¸•นซ์" maintainerName: "ผู้ดูà¹à¸¥" maintainerEmail: "à¸à¸µà¹€à¸¡à¸¥à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¸£à¸°à¸šà¸š" -tosUrl: "เงื่à¸à¸™à¹„ขà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£ URL" +tosUrl: "URL เงื่à¸à¸™à¹„ขà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£" thisYear: "ปีนี้" thisMonth: "เดืà¸à¸™à¸™à¸µà¹‰" today: "วันนี้" @@ -370,7 +370,7 @@ inMb: "เป็นเมà¸à¸°à¹„บต์" bannerUrl: "URL รูปภาพà¹à¸šà¸™à¹€à¸™à¸à¸£à¹Œ" backgroundImageUrl: "URL ภาพพื้นหลัง" basicInfo: "ข้à¸à¸¡à¸¹à¸¥à¹€à¸šà¸·à¹‰à¸à¸‡à¸•้น" -pinnedUsers: "ผู้ใช้งานที่ได้รับà¸à¸²à¸£à¸›à¸±à¸à¸«à¸¡à¸¸à¸”" +pinnedUsers: "ผู้ใช้ที่ถูà¸à¸›à¸±à¸à¸«à¸¡à¸¸à¸”" pinnedUsersDescription: "ป้à¸à¸™à¸Šà¸·à¹ˆà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่คุณต้à¸à¸‡à¸à¸²à¸£à¸›à¸±à¸à¸«à¸¡à¸¸à¸”ในหน้า “ค้นพบ†ฯลฯ คั่นด้วยà¸à¸²à¸£à¸‚ึ้นบรรทัดใหม่" pinnedPages: "หน้าเพจที่ปัà¸à¸«à¸¡à¸¸à¸”" pinnedPagesDescription: "ป้à¸à¸™à¹€à¸ªà¹‰à¸™à¸—างขà¸à¸‡à¸«à¸™à¹‰à¸²à¹€à¸žà¸ˆà¸—ี่คุณต้à¸à¸‡à¸à¸²à¸£à¸›à¸±à¸à¸«à¸¡à¸¸à¸”ไว้ที่หน้าà¹à¸£à¸à¸‚à¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•นซ์นี้ คั่นด้วยขึ้นบรรทัดใหม่" @@ -409,16 +409,16 @@ caseSensitive: "à¸à¸±à¸à¸©à¸£à¸žà¸´à¸¡à¸žà¹Œà¹ƒà¸«à¸à¹ˆ-พิมพ์เล withReplies: "รวมตà¸à¸šà¸à¸¥à¸±à¸š" connectedTo: "บัà¸à¸Šà¸µà¸”ังต่à¸à¹„ปนี้มีà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•่à¸à¸à¸±à¸™" notesAndReplies: "โพสต์à¹à¸¥à¸°à¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸š" -withFiles: "รวบรวมไฟล์" +withFiles: "มีไฟล์" silence: "ถูà¸à¸›à¸´à¸”ปาà¸" -silenceConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸—ี่จะ ปิดปาภผู้ใช้รายนี้?" +silenceConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸›à¸´à¸”ปาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸£à¸²à¸¢à¸™à¸µà¹‰à¹ƒà¸Šà¹ˆà¹„หม?" unsilence: "ยà¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¸›à¸´à¸”ปาà¸" -unsilenceConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¢à¸à¹€à¸¥à¸´à¸à¸›à¸´à¸”ปาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸£à¸²à¸¢à¸™à¸µà¹‰?" +unsilenceConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸›à¸´à¸”ปาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸£à¸²à¸¢à¸™à¸µà¹‰à¹ƒà¸Šà¹ˆà¹„หม?" popularUsers: "ผู้ใช้ที่เป็นที่นิยม" recentlyUpdatedUsers: "ผู้ใช้ที่เพิ่งใช้งานล่าสุด" recentlyRegisteredUsers: "ผู้ใช้ที่เข้าร่วมใหม่" recentlyDiscoveredUsers: "ผู้ใช้ที่เพิ่งค้นพบใหม่" -exploreUsersCount: "มีผู้ใช้ {จำนวน} ราย" +exploreUsersCount: "มีผู้ใช้ {count} ราย" exploreFediverse: "สำรวจสหพันธ์" popularTags: "à¹à¸—็à¸à¸¢à¸à¸”นิยม" userList: "ลิสต์" @@ -435,7 +435,7 @@ moderation: "à¸à¸²à¸£à¸à¸¥à¸±à¹ˆà¸™à¸à¸£à¸à¸‡" moderationNote: "โน้ตà¸à¸²à¸£à¸à¸¥à¸±à¹ˆà¸™à¸à¸£à¸à¸‡" addModerationNote: "เพิ่มโน้ตà¸à¸²à¸£à¸à¸¥à¸±à¹ˆà¸™à¸à¸£à¸à¸‡" moderationLogs: "ปูมà¸à¸²à¸£à¹à¸à¹‰à¹„ข" -nUsersMentioned: "à¸à¸¥à¹ˆà¸²à¸§à¸–ึงโดยผู้ใช้ {n} รายนี้" +nUsersMentioned: "à¸à¸¥à¹ˆà¸²à¸§à¸–ึงโดยผู้ใช้ {n} ราย" securityKeyAndPasskey: "ความปลà¸à¸”ภัยà¹à¸¥à¸°à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™" securityKey: "à¸à¸¸à¸à¹à¸ˆà¸„วามปลà¸à¸”ภัย" lastUsed: "ใช้ล่าสุด" @@ -449,7 +449,7 @@ reduceUiAnimation: "ลดภาพเคลื่à¸à¸™à¹„หว UI" share: "à¹à¸šà¹ˆà¸‡à¸›à¸±à¸™" notFound: "ไม่พบหน้าที่ต้à¸à¸‡à¸à¸²à¸£" notFoundDescription: "ไม่พบหน้าตาม URL ที่ระบุ" -uploadFolder: "โฟลเดà¸à¸£à¹Œà¹€à¸£à¸´à¹ˆà¸¡à¸•้นสำหรับà¸à¸±à¸žà¹‚หลด" +uploadFolder: "โฟลเดà¸à¸£à¹Œà¹€à¸£à¸´à¹ˆà¸¡à¸•้นสำหรับà¸à¸±à¸›à¹‚หลด" markAsReadAllNotifications: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸—ั้งหมดว่าà¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" markAsReadAllUnreadNotes: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹‚น้ตทั้งหมดว่าà¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" markAsReadAllTalkMessages: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸‚้à¸à¸„วามทั้งหมดว่าà¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" @@ -464,7 +464,7 @@ text: "ข้à¸à¸„วาม" enable: "เปิดใช้งาน" next: "ถัด​ไป" retype: "พิมพ์รหัสà¸à¸µà¸à¸„รั้ง" -noteOf: "โน้ต โดย {user}" +noteOf: "โน้ตขà¸à¸‡ {user}" quoteAttached: "à¸à¹‰à¸²à¸‡à¸à¸´à¸‡" quoteQuestion: "ต้à¸à¸‡à¸à¸²à¸£à¸—ี่จะà¹à¸™à¸šà¸¡à¸±à¸™à¹€à¸žà¸·à¹ˆà¸à¸à¹‰à¸²à¸‡à¸à¸´à¸‡à¹ƒà¸Šà¹ˆà¹„หม?" noMessagesYet: "ยังไม่มีข้à¸à¸„วาม" @@ -472,7 +472,7 @@ newMessageExists: "คุณมีข้à¸à¸„วามใหม่" onlyOneFileCanBeAttached: "สามารถà¹à¸™à¸šà¹„ฟล์ได้เพียงไฟล์เดียวต่ภ1 ข้à¸à¸„วาม" signinRequired: "à¸à¸£à¸¸à¸“าลงทะเบียนหรืà¸à¸¥à¸‡à¸Šà¸·à¹ˆà¸à¹€à¸‚้าใช้à¸à¹ˆà¸à¸™à¸”ำเนินà¸à¸²à¸£à¸•่à¸" invitations: "คำเชิà¸" -invitationCode: "รหัสคำเชิà¸" +invitationCode: "รหัสเชิà¸" checking: "Checking" available: "พร้à¸à¸¡à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" unavailable: "ไม่พร้à¸à¸¡à¹ƒà¸Šà¹‰" @@ -557,7 +557,7 @@ popout: "ป๊à¸à¸›à¹€à¸à¸²à¸•์" volume: "ระดับเสียง" masterVolume: "ระดับเสียงหลัà¸" notUseSound: "ไม่ใช้เสียง" -useSoundOnlyWhenActive: "มีเสียงà¸à¸à¸à¹€à¸‰à¸žà¸²à¸°à¹€à¸¡à¸·à¹ˆà¸ Misskey ทำงานà¸à¸¢à¸¹à¹ˆ" +useSoundOnlyWhenActive: "มีเสียงà¸à¸à¸à¹€à¸‰à¸žà¸²à¸°à¸•à¸à¸™à¸à¸³à¸¥à¸±à¸‡à¹ƒà¸Šà¹‰ Misskey à¸à¸¢à¸¹à¹ˆà¹€à¸—่านั้น" details: "รายละเà¸à¸µà¸¢à¸”" chooseEmoji: "เลืà¸à¸à¹€à¸à¹‚มจิ" unableToProcess: "ไม่สามารถดำเนินà¸à¸²à¸£à¹ƒà¸«à¹‰à¹€à¸ªà¸£à¹‡à¸ˆà¸ªà¸´à¹‰à¸™à¹„ด้" @@ -570,8 +570,8 @@ installedDate: "วันที่ติดตั้ง" lastUsedDate: "ใช้งานครั้งล่าสุด" state: "สถานะ" sort: "เรียงลำดับ" -ascendingOrder: "เรียงจาà¸à¸™à¹‰à¸à¸¢à¹„ปมาà¸" -descendingOrder: "เรียงจาà¸à¸¡à¸²à¸à¹„ปน้à¸à¸¢" +ascendingOrder: "เรียงลำดับขึ้น" +descendingOrder: "เรียงลำดับลง" scratchpad: "Scratchpad" scratchpadDescription: "Scratchpad เป็นà¸à¸²à¸£à¸ˆà¸±à¸”เตรียมสภาพà¹à¸§à¸”ล้à¸à¸¡à¸ªà¸³à¸«à¸£à¸±à¸šà¸à¸²à¸£à¸—ดลà¸à¸‡ AiScript à¹à¸•่คุณสามารถเขียน ดำเนินà¸à¸²à¸£ à¹à¸¥à¸°à¸•รวจสà¸à¸šà¸œà¸¥à¸¥à¸±à¸žà¸˜à¹Œà¸‚à¸à¸‡à¸à¸²à¸£à¹‚ต้ตà¸à¸šà¸à¸±à¸š Misskey มันได้ด้วยนะ" output: "เà¸à¸²à¸—์พุต" @@ -579,15 +579,15 @@ script: "สคริปต์" disablePagesScript: "ปิดà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ AiScript บนเพจ" updateRemoteUser: "à¸à¸±à¸›à¹€à¸”ตข้à¸à¸¡à¸¹à¸¥à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸£à¸°à¸¢à¸°à¹„à¸à¸¥" unsetUserAvatar: "เลิà¸à¸•ั้งà¸à¸§à¸•าร" -unsetUserAvatarConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸•ั้งà¸à¸§à¸•าร?" +unsetUserAvatarConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸•ั้งà¸à¸§à¸•ารใข่ไหม?" unsetUserBanner: "เลิà¸à¸•ั้งà¹à¸šà¸™à¹€à¸™à¸à¸£à¹Œ" unsetUserBannerConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸•ั้งà¹à¸šà¸™à¹€à¸™à¸à¸£à¹Œ?" deleteAllFiles: "ลบไฟล์ทั้งหมด" -deleteAllFilesConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¹„ฟล์ทั้งหมดหรืà¸à¹„ม่?" +deleteAllFilesConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¹„ฟล์ทั้งหมดใช่ไหม?" removeAllFollowing: "เลิà¸à¸•ิดตามผู้ใช้ที่ติดตามทั้งหมด" removeAllFollowingDescription: "เลิà¸à¸•ิดตามทั้งหมดจาภ{host} โปรดเรียà¸à¹ƒà¸Šà¹‰à¸ªà¸´à¹ˆà¸‡à¸™à¸µà¹‰à¹€à¸¡à¸·à¹ˆà¸à¸à¸´à¸™à¸ªà¹à¸•นซ์ดังà¸à¸¥à¹ˆà¸²à¸§à¹„ด้สูà¸à¸«à¸²à¸¢à¸•ายจาà¸à¹„ปà¹à¸¥à¹‰à¸§" userSuspended: "ผู้ใช้รายนี้ถูà¸à¸£à¸°à¸‡à¸±à¸šà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" -userSilenced: "ผู้ใช้รายนี้à¸à¸³à¸¥à¸±à¸‡à¸–ูà¸à¸›à¸´à¸”à¸à¸±à¹‰à¸™" +userSilenced: "ผู้ใช้รายนี้ถูà¸à¸›à¸´à¸”ปาà¸à¸à¸¢à¸¹à¹ˆ" yourAccountSuspendedTitle: "บัà¸à¸Šà¸µà¸™à¸µà¹‰à¸™à¸±à¹‰à¸™à¸–ูà¸à¸£à¸°à¸‡à¸±à¸š" yourAccountSuspendedDescription: "บัà¸à¸Šà¸µà¸™à¸µà¹‰à¸–ูà¸à¸£à¸°à¸‡à¸±à¸š เนื่à¸à¸‡à¸ˆà¸²à¸à¸¥à¸°à¹€à¸¡à¸´à¸”ข้à¸à¸à¸³à¸«à¸™à¸”ในà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£à¸‚à¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸«à¸£à¸·à¸à¸à¸²à¸ˆà¸ˆà¸°à¸¥à¸°à¹€à¸¡à¸´à¸”หลัà¸à¹€à¸à¸“ฑ์ชุมชน หรืภà¸à¸²à¸ˆà¸ˆà¸°à¹‚ดนร้à¸à¸‡à¹€à¸£à¸µà¸¢à¸™à¹€à¸£à¸·à¹ˆà¸à¸‡à¸à¸²à¸£à¸¥à¸°à¹€à¸¡à¸´à¸”ลิขสิทธิ์à¹à¸¥à¸°à¸à¸·à¹ˆà¸™à¹†à¸à¸¢à¹ˆà¸²à¸‡à¸•่à¸à¹€à¸™à¸·à¹ˆà¸à¸‡à¸‹à¹‰à¸³à¹† หาà¸à¸„ุณคิดว่าไม่ได้ทำผิดจริงๆหรืà¸à¸•ัดสินผิดพลาด ได้โปรดà¸à¸£à¸¸à¸“าติดต่à¸à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¸£à¸°à¸šà¸šà¸«à¸²à¸à¸„ุณต้à¸à¸‡à¸à¸²à¸£à¸—ราบเหตุผลโดยละเà¸à¸µà¸¢à¸”เพิ่มเติม à¹à¸¥à¸°à¸‚à¸à¸„วามà¸à¸£à¸¸à¸“าà¸à¸¢à¹ˆà¸²à¸ªà¸£à¹‰à¸²à¸‡à¸šà¸±à¸à¸Šà¸µà¹ƒà¸«à¸¡à¹ˆ" tokenRevoked: "โทเค็นไม่ถูà¸à¸•้à¸à¸‡" @@ -600,7 +600,7 @@ addItem: "เพิ่มรายà¸à¸²à¸£" rearrange: "จัดใหม่" relays: "รีเลย์" addRelay: "เพิ่มรีเลย์" -inboxUrl: "à¸à¸´à¸™à¸šà¹‡à¸à¸à¸‹à¹Œ URL" +inboxUrl: "URL ขà¸à¸‡à¸à¸´à¸™à¸šà¹‡à¸à¸à¸‹à¹Œ" addedRelays: "เพิ่มรีเลย์à¹à¸¥à¹‰à¸§" serviceworkerInfo: "ต้à¸à¸‡à¹€à¸›à¸´à¸”ใช้งานสำหรับà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¹à¸šà¸šà¸žà¸¸à¸Š" deletedNote: "โน้ตที่ถูà¸à¸¥à¸š" @@ -617,7 +617,7 @@ description: "รายละเà¸à¸µà¸¢à¸”" describeFile: "เพิ่มà¹à¸„ปชั่น" enterFileDescription: "ใส่à¹à¸„ปชั่น" author: "ผู้เขียน" -leaveConfirm: "คุณมีà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸—ี่ไม่ได้บันทึà¸à¸™à¸° นายต้à¸à¸‡à¸à¸²à¸£à¸—ิ้งà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¹€à¸«à¸¥à¹ˆà¸²à¸™à¸±à¹‰à¸™à¸«à¸£à¸?" +leaveConfirm: "มีà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸—ี่ยังไม่ได้บันทึภต้à¸à¸‡à¸à¸²à¸£à¸¥à¸°à¸—ิ้งมันใช่ไหม?" manage: "à¸à¸²à¸£à¸ˆà¸±à¸”à¸à¸²à¸£" plugins: "ปลั๊à¸à¸à¸´à¸™" preferencesBackups: "ตั้งค่าà¸à¸²à¸£à¸ªà¸³à¸£à¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥" @@ -664,7 +664,7 @@ display: "à¹à¸ªà¸”งผล" copy: "คัดลà¸à¸" metrics: "เมตริà¸" overview: "ภาพรวม" -logs: "บันทึà¸à¸‚้à¸à¸¡à¸¹à¸¥à¸£à¸°à¸šà¸š" +logs: "ปูม" delayed: "ดีเลย์" database: "à¸à¸²à¸™à¸‚้à¸à¸¡à¸¹à¸¥" channel: "ช่à¸à¸‡" @@ -672,26 +672,26 @@ create: "สร้าง" notificationSetting: "ตั้งค่าà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™" notificationSettingDesc: "เลืà¸à¸à¸›à¸£à¸°à¹€à¸ ทà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸—ี่ต้à¸à¸‡à¸à¸²à¸£à¸ˆà¸°à¹à¸ªà¸”ง" useGlobalSetting: "ใช้à¸à¸²à¸£à¸•ั้งค่าส่วนà¸à¸¥à¸²à¸‡" -useGlobalSettingDesc: "หาà¸à¹€à¸›à¸´à¸”ไว้ ระบบจะใช้à¸à¸²à¸£à¸•ั้งค่าà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸‚à¸à¸‡à¸šà¸±à¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณ หาà¸à¸›à¸´à¸”à¸à¸¢à¸¹à¹ˆ สามารถทำà¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าà¹à¸•่ละรายà¸à¸²à¸£à¹„ด้นะ" +useGlobalSettingDesc: "เมื่à¸à¹€à¸›à¸´à¸”ใช้งาน ใช้à¸à¸²à¸£à¸•ั้งค่าà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸ˆà¸²à¸à¸šà¸±à¸à¸Šà¸µà¸„ุณ เมื่à¸à¸›à¸´à¸”ใช้งาน สามารถตั้งค่าได้à¸à¸¢à¹ˆà¸²à¸‡à¸à¸´à¸ªà¸£à¸°" other: "à¸à¸·à¹ˆà¸™ ๆ" regenerateLoginToken: "สร้างโทเค็นà¸à¸²à¸£à¹€à¸‚้าสู่ระบบà¸à¸µà¸à¸„รั้ง" regenerateLoginTokenDescription: "สร้างโทเค็นใหม่ที่ใช้ภายในระหว่างà¸à¸²à¸£à¹€à¸‚้าสู่ระบบ โดยตามหลัà¸à¸›à¸à¸•ิà¹à¸¥à¹‰à¸§à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¹„ม่จำเป็น หาà¸à¸ªà¸£à¹‰à¸²à¸‡à¹ƒà¸«à¸¡à¹ˆ à¸à¸¸à¸›à¸à¸£à¸“์ทั้งหมดจะถูà¸à¸à¸à¸à¸ˆà¸²à¸à¸£à¸°à¸šà¸šà¸™à¸°" -theKeywordWhenSearchingForCustomEmoji: "คีย์เวิร์ดสำหรับใช้ค้นหาà¸à¸µà¹‚มจิที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡" +theKeywordWhenSearchingForCustomEmoji: "คีย์เวิร์ดสำหรับใช้ค้นหาเà¸à¹‚มจิที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡" setMultipleBySeparatingWithSpace: "คั่นหลายรายà¸à¸²à¸£à¸”้วยช่à¸à¸‡à¸§à¹ˆà¸²à¸‡" -fileIdOrUrl: "ไฟล์ ID หรืภURL" +fileIdOrUrl: "ID ขà¸à¸‡à¹„ฟล์ หรืภURL" behavior: "พฤติà¸à¸£à¸£à¸¡" sample: "ตัวà¸à¸¢à¹ˆà¸²à¸‡" abuseReports: "รายงาน" reportAbuse: "รายงาน" reportAbuseRenote: "รายงานรีโน้ต" -reportAbuseOf: "รายงาน {ชื่à¸}" +reportAbuseOf: "รายงาน {name}" fillAbuseReportDescription: "à¸à¸£à¸¸à¸“าà¸à¸£à¸à¸à¸£à¸²à¸¢à¸¥à¸°à¹€à¸à¸µà¸¢à¸”เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸£à¸²à¸¢à¸‡à¸²à¸™à¸™à¸µà¹‰ หาà¸à¹€à¸›à¹‡à¸™à¹€à¸£à¸·à¹ˆà¸à¸‡à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¹‚น้ตโดยเฉพาะ ได้โปรดระบุ URL" abuseReported: "เราได้ส่งรายงานขà¸à¸‡à¸„ุณไปà¹à¸¥à¹‰à¸§ ขà¸à¸šà¸„ุณมาà¸à¹†à¸™à¸°" -reporter: "นัà¸à¸‚่าว" +reporter: "ผู้รายงาน" reporteeOrigin: "รายงานต้นทาง" -reporterOrigin: "นัà¸à¸‚่าวต้นทาง" +reporterOrigin: "à¹à¸«à¸¥à¹ˆà¸‡à¸œà¸¹à¹‰à¸£à¸²à¸¢à¸‡à¸²à¸™" forwardReport: "ส่งต่à¸à¸£à¸²à¸¢à¸‡à¸²à¸™à¹„ปยังà¸à¸´à¸™à¸ªà¹à¸•นซ์ระยะไà¸à¸¥" -forwardReportIsAnonymous: "à¹à¸—นที่จะเป็นบัà¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณ บัà¸à¸Šà¸µà¸£à¸°à¸šà¸šà¸—ี่ไม่ระบุตัวตนจะà¹à¸ªà¸”งเป็นนัà¸à¸‚่าวที่à¸à¸´à¸™à¸ªà¹à¸•นซ์ระยะไà¸à¸¥" +forwardReportIsAnonymous: "ข้à¸à¸¡à¸¹à¸¥à¸‚à¸à¸‡à¸„ุณจะไม่ปราà¸à¸à¸šà¸™à¸à¸´à¸™à¸ªà¹à¸•นซ์ระยะไà¸à¸¥à¹à¸¥à¸°à¸›à¸£à¸²à¸à¸à¹€à¸›à¹‡à¸™à¸šà¸±à¸à¸Šà¸µà¸£à¸°à¸šà¸šà¸—ี่ไม่ระบุชื่à¸" send: "ส่ง" abuseMarkAsResolved: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸£à¸²à¸¢à¸‡à¸²à¸™à¸§à¹ˆà¸²à¹à¸à¹‰à¹„ขà¹à¸¥à¹‰à¸§" openInNewTab: "เปิดในà¹à¸—็บใหม่" @@ -699,7 +699,7 @@ openInSideView: "เปิดในมุมมà¸à¸‡à¸”้านข้าง" defaultNavigationBehaviour: "พฤติà¸à¸£à¸£à¸¡à¸à¸²à¸£à¸™à¸³à¸—างที่เป็นค่าเริ่มต้น" editTheseSettingsMayBreakAccount: "à¸à¸²à¸£à¹à¸à¹‰à¹„ขà¸à¸²à¸£à¸•ั้งค่าเหล่านี้à¸à¸²à¸ˆà¸—ำให้บัà¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณเสียหายนะ" instanceTicker: "ข้à¸à¸¡à¸¹à¸¥à¸à¸´à¸™à¸ªà¹à¸•นซ์ขà¸à¸‡à¹‚น้ต" -waitingFor: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸à¸„à¸à¸¢ {x}" +waitingFor: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸ {x}" random: "สุ่มค่า" system: "ระบบ" switchUi: "สลับ UI" @@ -709,7 +709,7 @@ createNew: "สร้างใหม่" optional: "ไม่บังคับ" createNewClip: "สร้างคลิปใหม่" unclip: "ลบคลิป" -confirmToUnclipAlreadyClippedNote: "โน้ตนี้เป็นส่วนหนึ่งขà¸à¸‡à¸„ลิป \"{name}\" à¹à¸¥à¹‰à¸§ คุณต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¸à¸à¸à¸ˆà¸²à¸à¸„ลิปนี้à¹à¸—นà¸à¸¢à¹ˆà¸²à¸‡à¸‡à¸±à¹‰à¸™à¸«à¸£à¸?" +confirmToUnclipAlreadyClippedNote: "โน้ตนี้เป็นส่วนหนึ่งขà¸à¸‡à¸„ลิป “{name}†à¸à¸¢à¸¹à¹ˆà¹à¸¥à¹‰à¸§ ต้à¸à¸‡à¸à¸²à¸£à¸™à¸³à¸¡à¸±à¸™à¸à¸à¸à¸ˆà¸²à¸à¸„ลิปใช่ไหม?" public: "สาธารณะ" private: "ส่วนตัว" i18nInfo: "Misskey à¸à¸³à¸¥à¸±à¸‡à¹„ด้รับà¸à¸²à¸£à¹à¸›à¸¥à¹€à¸›à¹‡à¸™à¸ าษาต่างๆ โดยà¸à¸²à¸ªà¸²à¸ªà¸¡à¸±à¸„ร คุณสามารถช่วยเหลืà¸à¹„ด้ที่ {link}" @@ -732,7 +732,7 @@ driveFilesCount: "จำนวนไฟล์ไดรฟ์" driveUsage: "à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸žà¸·à¹‰à¸™à¸—ี่ไดรฟ์" noCrawle: "ปà¸à¸´à¹€à¸ªà¸˜à¸à¸²à¸£à¸ˆà¸±à¸”ทำดัชนีขà¸à¸‡à¹‚ปรà¹à¸à¸£à¸¡à¸£à¸§à¸šà¸£à¸§à¸¡à¸‚้à¸à¸¡à¸¹à¸¥" noCrawleDescription: "ขà¸à¹ƒà¸«à¹‰à¹€à¸„รื่à¸à¸‡à¸¡à¸·à¸à¸„้นหาไม่จัดทำดัชนีหน้าโปรไฟล์ โน้ต หน้าเพจ ฯลฯ" -lockedAccountInfo: "เว้นà¹à¸•่ว่าคุณจะต้à¸à¸‡à¸•ั้งค่าà¸à¸²à¸£à¹€à¸›à¸´à¸”เผยโน้ตเป็น \"ผู้ติดตามเท่านั้น\" โน้ตย่à¸à¸‚à¸à¸‡à¸„ุณจะปราà¸à¸à¹à¸à¹ˆà¸—ุà¸à¸„น ถึงà¹à¸¡à¹‰à¸§à¹ˆà¸²à¸„ุณจะเป็นà¸à¸³à¸«à¸™à¸”ให้ผู้ติดตามต้à¸à¸‡à¹„ด้รับà¸à¸²à¸£à¸à¸™à¸¸à¸¡à¸±à¸•ิด้วยตนเà¸à¸‡à¸à¹‡à¸•าม" +lockedAccountInfo: "à¹à¸¡à¹‰à¸§à¹ˆà¸²à¸à¸²à¸£à¸à¸™à¸¸à¸¡à¸±à¸•ิà¸à¸²à¸£à¸•ิดตามถูà¸à¹€à¸›à¸´à¸”ใช้งานà¸à¸¢à¸¹à¹ˆà¸—ุà¸à¸„นà¸à¹‡à¸¢à¸±à¸‡à¸„งสามารถเห็นโน้ตขà¸à¸‡à¸„ุณได้ เว้นà¹à¸•่ว่าคุณจะเปลี่ยนà¸à¸²à¸£à¹€à¸›à¸´à¸”เผยโน้ตขà¸à¸‡à¸„ุณเป็น “เฉพาะผู้ติดตามâ€" alwaysMarkSensitive: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™à¹€à¸›à¹‡à¸™à¸„่าเริ่มต้น" loadRawImages: "โหลดภาพต้นฉบับà¹à¸—นà¸à¸²à¸£à¹à¸ªà¸”งภาพขนาดย่à¸" disableShowingAnimatedImages: "ไม่ต้à¸à¸‡à¹€à¸¥à¹ˆà¸™à¸ าพเคลื่à¸à¸™à¹„หว" @@ -768,29 +768,29 @@ nNotes: "{n} โน้ต" sendErrorReports: "ส่งรายงานว่าข้à¸à¸œà¸´à¸”พลาด" sendErrorReportsDescription: "เมื่à¸à¹€à¸›à¸´à¸”ใช้งาน ข้à¸à¸¡à¸¹à¸¥à¸‚้à¸à¸œà¸´à¸”พลาดโดยรายละเà¸à¸µà¸¢à¸”นั้นจะถูà¸à¹à¸Šà¸£à¹Œà¹ƒà¸«à¹‰à¸à¸±à¸š Misskey เมื่à¸à¹€à¸à¸´à¸”ปัà¸à¸«à¸² ซึ่งช่วยปรับปรุงคุณภาพขà¸à¸‡ Misskey\nซึ่งจะรวมถึงข้à¸à¸¡à¸¹à¸¥ เช่น เวà¸à¸£à¹Œà¸Šà¸±à¹ˆà¸™à¸‚à¸à¸‡à¸£à¸°à¸šà¸šà¸›à¸à¸´à¸šà¸±à¸•ิà¸à¸²à¸£ เบราว์เซà¸à¸£à¹Œà¸—ี่คุณใช้ à¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸‚à¸à¸‡à¸„ุณใน Misskey เป็นต้น" myTheme: "ธีมขà¸à¸‡à¸‰à¸±à¸™" -backgroundColor: "ภาพพื้นหลัง" -accentColor: "รูปà¹à¸šà¸šà¸ªà¸µ" +backgroundColor: "สีพื้นหลัง" +accentColor: "สีหลัà¸" textColor: "สีข้à¸à¸„วาม" saveAs: "บันทึà¸à¹€à¸›à¹‡à¸™..." advanced: "ขั้นสูง" advancedSettings: "à¸à¸²à¸£à¸•ั้งค่าขั้นสูง" value: "ค่า" createdAt: "สร้างเมื่à¸" -updatedAt: "à¸à¸±à¸žà¹€à¸”ทล่าสุด" +updatedAt: "à¸à¸±à¸›à¹€à¸”ตล่าสุด" saveConfirm: "บันทึà¸à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸¡à¸±à¹‰à¸¢?" deleteConfirm: "ลบจริงๆเหรà¸?" invalidValue: "ค่านี้ไม่ถูà¸à¸•้à¸à¸‡" registry: "ทะเบียน" closeAccount: "ปิด บัà¸à¸Šà¸µ" currentVersion: "เวà¸à¸£à¹Œà¸Šà¸±à¹ˆà¸™à¸›à¸±à¸ˆà¸ˆà¸¸à¸šà¸±à¸™" -latestVersion: "รุ่นปัจจุบัน" +latestVersion: "เวà¸à¸£à¹Œà¸Šà¸±à¹ˆà¸™à¸¥à¹ˆà¸²à¸ªà¸¸à¸”" youAreRunningUpToDateClient: "คุณà¸à¸³à¸¥à¸±à¸‡à¹ƒà¸Šà¹‰à¹„คลเà¸à¹‡à¸™à¸•์เวà¸à¸£à¹Œà¸Šà¸±à¸™à¹ƒà¸«à¸¡à¹ˆà¸¥à¹ˆà¸²à¸ªà¸¸à¸”นะ" newVersionOfClientAvailable: "มีไคลเà¸à¹‡à¸™à¸•์เวà¸à¸£à¹Œà¸Šà¸±à¸™à¹ƒà¸«à¸¡à¹ˆà¸à¸§à¹ˆà¸²à¸‚à¸à¸‡à¸„ุณพร้à¸à¸¡à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸™à¸°" usageAmount: "à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" capacity: "ความจุ" inUse: "ใช้à¹à¸¥à¹‰à¸§" editCode: "à¹à¸à¹‰à¹„ขโค้ด" -apply: "ตà¸à¸¥à¸‡" +apply: "นำไปใช้" receiveAnnouncementFromInstance: "รับà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸ˆà¸²à¸à¸à¸´à¸™à¸ªà¹à¸•นซ์นี้" emailNotification: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸—างà¸à¸µà¹€à¸¡à¸¥" publish: "เผยà¹à¸žà¸£à¹ˆ" @@ -802,7 +802,7 @@ showingPastTimeline: "à¸à¸³à¸¥à¸±à¸‡à¹à¸ªà¸”งผลไทม์ไลน์à clear: "ล้าง" markAllAsRead: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸—ั้งหมดว่าà¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" goBack: "ย้à¸à¸™à¸à¸¥à¸±à¸š" -unlikeConfirm: "เลิà¸à¸–ูà¸à¹ƒà¸ˆà¸ˆà¸£à¸´à¸‡à¹† หรืà¸?" +unlikeConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸–ูà¸à¹ƒà¸ˆà¹ƒà¸Šà¹ˆà¹„หม?" fullView: "มุมมà¸à¸‡à¹à¸šà¸šà¹€à¸•็ม" quitFullView: "à¸à¸à¸à¸ˆà¸²à¸à¸¡à¸¸à¸¡à¸¡à¸à¸‡à¹à¸šà¸šà¹€à¸•็ม" addDescription: "เพิ่มคำà¸à¸˜à¸´à¸šà¸²à¸¢" @@ -813,12 +813,12 @@ userInfo: "ข้à¸à¸¡à¸¹à¸¥à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" unknown: "ไม่ทราบสถานะ" onlineStatus: "สถานะà¸à¸à¸™à¹„ลน์" hideOnlineStatus: "ซ่à¸à¸™à¸ªà¸–านะà¸à¸à¸™à¹„ลน์" -hideOnlineStatusDescription: "à¸à¸²à¸£à¸‹à¹ˆà¸à¸™à¸ªà¸–านะà¸à¸à¸™à¹„ลน์ขà¸à¸‡à¸„ุณช่วยลดความสะดวà¸à¸‚à¸à¸‡à¸„ุณสมบัติบางà¸à¸¢à¹ˆà¸²à¸‡ เช่น à¸à¸²à¸£à¸„้นหา à¸à¹ˆà¸°à¸™à¸°" +hideOnlineStatusDescription: "à¸à¸²à¸£à¸‹à¹ˆà¸à¸™à¸ªà¸–านะà¸à¸à¸™à¹„ลน์à¸à¸²à¸ˆà¸—ำให้ฟังà¸à¹Œà¸Šà¸±à¸™à¸šà¸²à¸‡à¸à¸¢à¹ˆà¸²à¸‡ เช่น à¸à¸²à¸£à¸„้นหา สะดวà¸à¸™à¹‰à¸à¸¢à¸¥à¸‡" online: "à¸à¸à¸™à¹„ลน์" active: "ใช้งานà¸à¸¢à¸¹à¹ˆ" offline: "à¸à¸à¸Ÿà¹„ลน์" notRecommended: "ไม่à¹à¸™à¸°à¸™à¸³" -botProtection: "à¸à¸²à¸£à¸›à¹‰à¸à¸‡à¸à¸±à¸™ Bot (or AI)" +botProtection: "à¸à¸²à¸£à¸›à¹‰à¸à¸‡à¸à¸±à¸™ Bot" instanceBlocking: "à¸à¸´à¸™à¸ªà¹à¸•นซ์ที่ถูà¸à¸šà¸¥à¹‡à¸à¸" selectAccount: "เลืà¸à¸à¸šà¸±à¸à¸Šà¸µ" switchAccount: "สลับบัà¸à¸Šà¸µà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" @@ -880,7 +880,7 @@ itsOff: "ปิดใช้งาน" on: "เปิด" off: "ปิด" emailRequiredForSignup: "จำเป็นต้à¸à¸‡à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸—ี่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸ªà¸³à¸«à¸£à¸±à¸šà¸à¸²à¸£à¸ªà¸¡à¸±à¸„ร" -unread: "ไม่ได้à¸à¹ˆà¸²à¸™" +unread: "ยังไม่ได้à¸à¹ˆà¸²à¸™" filter: "à¸à¸£à¸à¸‡" controlPanel: "à¹à¸œà¸‡à¸„วบคุม" manageAccounts: "จัดà¸à¸²à¸£à¸šà¸±à¸à¸Šà¸µ" @@ -888,13 +888,13 @@ makeReactionsPublic: "ตั้งค่าประวัติà¸à¸²à¸£à¸£à¸µà makeReactionsPublicDescription: "à¸à¸²à¸£à¸—ำเช่นนี้จะทำให้รายà¸à¸²à¸£à¸£à¸µà¹à¸à¸„ชั่นขà¸à¸‡à¸„ุณที่ผ่านมาทั้งหมดปราà¸à¸à¸•่à¸à¸ªà¸²à¸˜à¸²à¸£à¸“ะ" classic: "คลาสสิค" muteThread: "ปิดเสียงเธรด" -unmuteThread: "เปิดเสียงเธรด" +unmuteThread: "เลิà¸à¸›à¸´à¸”เสียงเธรด" followingVisibility: "à¸à¸²à¸£à¸¡à¸à¸‡à¹€à¸«à¹‡à¸™à¸—ี่เราà¸à¸³à¸¥à¸±à¸‡à¸•ิดตาม" followersVisibility: "à¸à¸²à¸£à¸¡à¸à¸‡à¹€à¸«à¹‡à¸™à¸œà¸¹à¹‰à¸—ี่à¸à¸³à¸¥à¸±à¸‡à¸•ิดตามเรา" continueThread: "ดูความต่à¸à¹€à¸™à¸·à¹ˆà¸à¸‡à¹€à¸˜à¸£à¸”" deleteAccountConfirm: "à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¸ˆà¸°à¸¥à¸šà¸šà¸±à¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณà¸à¸¢à¹ˆà¸²à¸‡à¸–าวรเลยนะ à¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸à¸”ำเนินà¸à¸²à¸£?" incorrectPassword: "รหัสผ่านไม่ถูà¸à¸•้à¸à¸‡" -voteConfirm: "ยืนยันà¸à¸²à¸£à¹‚หวต “{choice}†ไหม?" +voteConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹‚หวต “{choice}†ใช่ไหม?" hide: "ซ่à¸à¸™" useDrawerReactionPickerForMobile: "à¹à¸ªà¸”ง ตัวจิ้มรีà¹à¸à¸„ชั่น เป็นà¹à¸šà¸šà¸¥à¸´à¹‰à¸™à¸Šà¸±à¸ เมื่à¸à¹ƒà¸Šà¹‰à¸šà¸™à¸¡à¸·à¸à¸–ืà¸" welcomeBackWithName: "ยินดีต้à¸à¸™à¸£à¸±à¸šà¸à¸²à¸£à¸à¸¥à¸±à¸šà¸¡à¸²à¸™à¸°à¸„ะ, คุณ{name}" @@ -941,13 +941,13 @@ deleteAccount: "ลบบัà¸à¸Šà¸µ" document: "เà¸à¸à¸ªà¸²à¸£" numberOfPageCache: "จำนวนหน้าเพจที่à¹à¸„ช" numberOfPageCacheDescription: "à¸à¸²à¸£à¹€à¸žà¸´à¹ˆà¸¡à¸ˆà¸³à¸™à¸§à¸™à¸™à¸µà¹‰à¸ˆà¸°à¸Šà¹ˆà¸§à¸¢à¹€à¸žà¸´à¹ˆà¸¡à¸„วามสะดวà¸à¹ƒà¸«à¹‰à¸à¸±à¸šà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ à¹à¸•่จะทำให้เซิร์ฟเวà¸à¸£à¹Œà¹‚หลดมาà¸à¸‚ึ้นà¹à¸¥à¸°à¸•้à¸à¸‡à¹ƒà¸Šà¹‰à¸«à¸™à¹ˆà¸§à¸¢à¸„วามจำมาà¸à¸‚ึ้นà¸à¸µà¸à¸”้วย" -logoutConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸à¸à¸à¸ˆà¸²à¸à¸£à¸°à¸šà¸š?" -lastActiveDate: "ใช้งานล่าสุดที่" +logoutConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸à¸à¸à¸ˆà¸²à¸à¸£à¸°à¸šà¸šà¹ƒà¸Šà¹ˆà¹„หม?" +lastActiveDate: "ใช้งานล่าสุดเมื่à¸" statusbar: "à¹à¸–บสถานะ" pleaseSelect: "ตัวเลืà¸à¸" -reverse: "ย้à¸à¸™à¸à¸¥à¸±à¸š" +reverse: "พลิà¸" colored: "สี" -refreshInterval: "รà¸à¸šà¸à¸²à¸£à¸à¸±à¸žà¹€à¸”ต" +refreshInterval: "ความถี่ในà¸à¸²à¸£à¸à¸±à¸›à¹€à¸”ต" label: "ป้ายชื่à¸" type: "รูปà¹à¸šà¸š" speed: "ความเร็ว" @@ -974,8 +974,8 @@ unsubscribePushNotification: "ปิดà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¹à¸ pushNotificationAlreadySubscribed: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¹à¸šà¸šà¸žà¸¸à¸Šà¹„ด้เปิดใช้งานà¹à¸¥à¹‰à¸§" pushNotificationNotSupported: "เบราว์เซà¸à¸£à¹Œà¸«à¸£à¸·à¸à¸à¸´à¸™à¸ªà¹à¸•นซ์ขà¸à¸‡à¸„ุณนั้นไม่รà¸à¸‡à¸£à¸±à¸šà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¹à¸šà¸šà¸žà¸¸à¸Š" sendPushNotificationReadMessage: "ลบà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¹à¸šà¸šà¸žà¸¸à¸Šà¹€à¸¡à¸·à¹ˆà¸à¸à¹ˆà¸²à¸™à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸«à¸£à¸·à¸à¸‚้à¸à¸„วามที่เà¸à¸µà¹ˆà¸¢à¸§à¸‚้à¸à¸‡à¹à¸¥à¹‰à¸§" -sendPushNotificationReadMessageCaption: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸—ี่มีข้à¸à¸„วาม \"{emptyPushNotificationMessage}\" จะà¹à¸ªà¸”งขึ้นมาในช่วงระยะเวลาสั้นๆ à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¸à¸²à¸ˆà¸—ำให้เพิ่มà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¹à¸šà¸•เตà¸à¸£à¸µà¹ˆà¸‚à¸à¸‡à¸à¸¸à¸›à¸à¸£à¸“์ถ้าหาà¸à¸¡à¸µà¸™à¸°" -windowMaximize: "ขยายใหà¸à¹ˆà¸ªà¸¸à¸”à¹à¸¥à¹‰à¸§" +sendPushNotificationReadMessageCaption: "à¸à¸²à¸ˆà¸—ำให้à¸à¸¸à¸›à¸à¸£à¸“์ขà¸à¸‡à¸„ุณใช้พลังงานมาà¸à¸‚ึ้น" +windowMaximize: "ขยายใหà¸à¹ˆà¸ªà¸¸à¸”" windowMinimize: "ย่à¸à¹€à¸¥à¹‡à¸à¸—ี่สุด" windowRestore: "เลิà¸à¸—ำ" caption: "คำà¸à¸˜à¸´à¸šà¸²à¸¢" @@ -991,6 +991,7 @@ neverShow: "ไม่ต้à¸à¸‡à¹à¸ªà¸”งข้à¸à¸„วามนี้à¸à¸ remindMeLater: "ไว้ครั้งหน้าà¹à¸¥à¹‰à¸§à¸à¸±à¸™" didYouLikeMisskey: "คุณชà¸à¸š Misskey ไหม?" pleaseDonate: "Misskey เป็นซà¸à¸Ÿà¸•์à¹à¸§à¸£à¹Œà¸Ÿà¸£à¸µà¸—ี่ใช้งานโดย {host} เราขà¸à¸‚à¸à¸šà¸„ุณà¸à¸²à¸£à¸ªà¸™à¸±à¸šà¸ªà¸™à¸¸à¸™à¸‚à¸à¸‡à¸„ุณà¸à¸¢à¹ˆà¸²à¸‡à¸ªà¸¹à¸‡à¹€à¸žà¸·à¹ˆà¸à¹ƒà¸«à¹‰à¸à¸²à¸£à¸žà¸±à¸’นา Misskey สามารถดำเนินต่à¸à¹„ปได้!" +correspondingSourceIsAvailable: "ซà¸à¸£à¹Œà¸ªà¹‚ค้ดที่เà¸à¸µà¹ˆà¸¢à¸§à¸‚้à¸à¸‡à¸¡à¸µà¸à¸¢à¸¹à¹ˆà¸—ี่ {anchor}" roles: "บทบาท" role: "บทบาท" noRole: "ไม่พบบทบาท" @@ -1059,7 +1060,7 @@ enableChartsForFederatedInstances: "สร้างà¹à¸œà¸™à¸ ูมิข้ภshowClipButtonInNoteFooter: "เพิ่ม “คลิป†ไปยังเมนูสั่งà¸à¸²à¸£à¸‚à¸à¸‡à¹‚น้ต" reactionsDisplaySize: "ขนาดขà¸à¸‡à¸£à¸µà¹à¸à¸„ชั่น" limitWidthOfReaction: "จำà¸à¸±à¸”ความà¸à¸§à¹‰à¸²à¸‡à¸ªà¸¹à¸‡à¸ªà¸¸à¸”ขà¸à¸‡à¸£à¸µà¹à¸à¸„ชั่นà¹à¸¥à¸°à¹à¸ªà¸”งให้เล็à¸à¸¥à¸‡" -noteIdOrUrl: "โน้ต ID หรืภURL" +noteIdOrUrl: "ID ขà¸à¸‡à¹‚น้ต หรืภURL" video: "วีดีโà¸" videos: "วีดีโà¸" audio: "เสียง" @@ -1081,7 +1082,7 @@ leftBottom: "ล่างซ้าย" rightBottom: "ล่างขวา" stackAxis: "ทิศทางà¸à¸²à¸£à¸‹à¹‰à¸à¸™" vertical: "à¹à¸™à¸§à¸•ั้ง" -horizontal: "ด้านข้าง" +horizontal: "à¹à¸™à¸§à¸™à¸à¸™" position: "ตำà¹à¸«à¸™à¹ˆà¸‡" serverRules: "à¸à¸Žà¸‚à¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" pleaseConfirmBelowBeforeSignup: "โปรดยืนยันที่ด้านล่างà¸à¹ˆà¸à¸™à¸ªà¸¡à¸±à¸„รใช้งาน" @@ -1097,17 +1098,17 @@ thisChannelArchived: "ช่à¸à¸‡à¸™à¸µà¹‰à¸–ูà¸à¹€à¸à¹‡à¸šà¸–าวรà¹à displayOfNote: "à¸à¸²à¸£à¹à¸ªà¸”งโน้ต" initialAccountSetting: "ตั้งค่าโปรไฟล์" youFollowing: "ติดตามà¹à¸¥à¹‰à¸§" -preventAiLearning: "ปà¸à¸´à¹€à¸ªà¸˜à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ ในà¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸™à¸£à¸¹à¹‰à¸‚à¸à¸‡à¹€à¸„รื่à¸à¸‡ (Generative AI)" -preventAiLearningDescription: "à¸à¸²à¸£à¸ªà¹ˆà¸‡à¸„ำร้à¸à¸‡à¸‚à¸à¹‚ปรà¹à¸à¸£à¸¡à¸£à¸§à¸šà¸£à¸§à¸¡à¸‚้à¸à¸¡à¸¹à¸¥à¹„ม่ให้ใช้ข้à¸à¸„วามที่โพสต์หรืà¸à¸£à¸¹à¸›à¸ าพ ฯลฯ ในชุดข้à¸à¸¡à¸¹à¸¥à¹à¸¡à¸Šà¸Šà¸µà¸™à¹€à¸¥à¸´à¸£à¹Œà¸™à¸™à¸´à¸‡ (Predictive / Generative AI) สิ่งนี้นั้นทำได้โดยà¸à¸²à¸£à¹€à¸žà¸´à¹ˆà¸¡à¹à¸Ÿà¸¥à¹‡à¸à¸à¸²à¸£à¸•à¸à¸šà¸ªà¸™à¸à¸‡ \"noai\" HTML ให้à¸à¸±à¸šà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸—ี่เà¸à¸µà¹ˆà¸¢à¸§à¸‚้à¸à¸‡ à¹à¸•่à¸à¸¢à¹ˆà¸²à¸‡à¹„รà¸à¹‡à¸•ามà¹à¸¥à¹‰à¸§ à¸à¸²à¸£à¸›à¹‰à¸à¸‡à¸à¸±à¸™à¹‚ดยสมบูรณ์นั้นไม่สามารถทำได้ผ่านà¹à¸Ÿà¸¥à¹‡à¸à¸™à¸µà¹‰à¹€à¸™à¸·à¹ˆà¸à¸‡à¸ˆà¸²à¸à¸à¸²à¸ˆà¸ˆà¸°à¸—ำให้ถูà¸à¹€à¸žà¸´à¸à¹€à¸‰à¸¢à¹„ด้" +preventAiLearning: "ปà¸à¸´à¹€à¸ªà¸˜à¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸™à¸£à¸¹à¹‰à¸”้วย generative AI" +preventAiLearningDescription: "ส่งคำร้à¸à¸‡à¸‚à¸à¹„ม่ให้ใช้ ข้à¸à¸„วามในโน้ตที่โพสต์, หรืà¸à¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸£à¸¹à¸›à¸ าพ ฯลฯ ในà¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸™à¸£à¸¹à¹‰à¸‚à¸à¸‡à¹€à¸„รื่à¸à¸‡(machine learning) / Predictive AI / Generative AI โดยà¸à¸²à¸£à¹€à¸žà¸´à¹ˆà¸¡à¹à¸Ÿà¸¥à¹‡à¸ “noai†ลง HTML-Response ให้à¸à¸±à¸šà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸—ี่เà¸à¸µà¹ˆà¸¢à¸§à¸‚้à¸à¸‡ à¹à¸•่ทั้งนี้ ไม่ได้ป้à¸à¸‡à¸à¸±à¸™ AI จาà¸à¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸™à¸£à¸¹à¹‰à¹„ด้à¸à¸¢à¹ˆà¸²à¸‡à¸ªà¸¡à¸šà¸¹à¸£à¸“์ เนื่à¸à¸‡à¸ˆà¸²à¸à¸¡à¸µ AI บางตัวเท่านั้นที่จะเคารพคำขà¸à¸”ังà¸à¸¥à¹ˆà¸²à¸§" options: "ตัวเลืà¸à¸à¸šà¸—บาท" specifyUser: "ผู้ใช้เฉพาะ" failedToPreviewUrl: "ไม่สามารถดูตัวà¸à¸¢à¹ˆà¸²à¸‡à¹„ด้" update: "à¸à¸±à¸›à¹€à¸”ต" rolesThatCanBeUsedThisEmojiAsReaction: "บทบาทที่สามารถใช้เà¸à¹‚มจินี้เป็นรีà¹à¸à¸„ชั่นได้" -rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "ถ้าหาà¸à¹„ม่ได้ระบุบทบาท ทุà¸à¸„นนั้นà¸à¹‡à¸ªà¸²à¸¡à¸²à¸£à¸–ใช้เà¸à¹‚มจินี้เพื่à¸à¸£à¸µà¹à¸à¸„ชั่นได้นะ" +rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "ถ้าหาà¸à¹„ม่ได้ระบุบทบาท ใคร ๆ à¸à¹‡à¸ªà¸²à¸¡à¸²à¸£à¸–ใช้เà¸à¹‚มจินี้เพื่à¸à¸£à¸µà¹à¸à¸„ชั่นได้" rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "บทบาทเหล่านี้ต้à¸à¸‡à¹€à¸›à¹‡à¸™à¸ªà¸²à¸˜à¸²à¸£à¸“ะ" -cancelReactionConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¸£à¸µà¹à¸à¸„ชั่นขà¸à¸‡à¸„ุณจริงๆหรà¸?" -changeReactionConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸£à¸µà¹à¸à¸„ชั่นขà¸à¸‡à¸„ุณจริงๆหรà¸?" +cancelReactionConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¸£à¸µà¹à¸à¸„ชั่นใช่ไหม?" +changeReactionConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸£à¸µà¹à¸à¸„ชั่นใช่ไหม?" later: "ไว้ทีหลัง" goToMisskey: "ถึง Misskey" additionalEmojiDictionary: "พจนานุà¸à¸£à¸¡à¹€à¸à¹‚มจิเพิ่มเติม" @@ -1116,20 +1117,20 @@ branding: "à¹à¸šà¸£à¸™à¸”ิ้ง" enableServerMachineStats: "เผยà¹à¸žà¸£à¹ˆà¸ªà¸–านะฮาร์ดà¹à¸§à¸£à¹Œà¸‚à¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" enableIdenticonGeneration: "เปิดใช้งานผู้ใช้สร้างตัวระบุ" turnOffToImprovePerformance: "à¸à¸²à¸£à¸›à¸´à¸”ส่วนนี้สามารถเพิ่มประสิทธิภาพได้" -createInviteCode: "สร้างคำเชิà¸" +createInviteCode: "สร้างรหัสเชิà¸" createWithOptions: "สร้างด้วยตัวเลืà¸à¸" -createCount: "จำนวนà¸à¸²à¸£à¹€à¸Šà¸´à¸" -inviteCodeCreated: "สร้างคำเชิà¸à¹à¸¥à¹‰à¸§" -inviteLimitExceeded: "คุณสร้างคำเชิà¸à¹€à¸à¸´à¸™à¸–ึงขีดจำà¸à¸±à¸”à¹à¸¥à¹‰à¸§à¸™à¸°" -createLimitRemaining: "ขีดจำà¸à¸±à¸”à¸à¸²à¸£à¹€à¸Šà¸´à¸: {limit} ที่เหลืà¸à¸à¸¢à¸¹à¹ˆ" -inviteLimitResetCycle: "ขีดจำà¸à¸±à¸”นี้จะถูà¸à¸£à¸µà¹€à¸‹à¹‡à¸•เป็น {limit} ที่ {time}." +createCount: "จำนวนรหัสเชิà¸" +inviteCodeCreated: "สร้างรหัสเชิà¸à¹à¸¥à¹‰à¸§" +inviteLimitExceeded: "จำนวนรหัสเชิà¸à¸—ี่สามารถสร้างได้ถึงขีดจำà¸à¸±à¸”à¹à¸¥à¹‰à¸§" +createLimitRemaining: "รหัสเชิà¸à¸—ี่สามารถสร้างได้: เหลืà¸à¸à¸¢à¸¹à¹ˆ {limit} รหัส" +inviteLimitResetCycle: "สามารถสร้างรหัสเชิà¸à¹„ด้à¸à¸µà¸à¸ªà¸¹à¸‡à¸ªà¸¸à¸” {limit} รหัส ภายใน {time}" expirationDate: "วันที่หมดà¸à¸²à¸¢à¸¸" noExpirationDate: "ไม่มีหมดà¸à¸²à¸¢à¸¸" -inviteCodeUsedAt: "รหัสคำเชิà¸à¹ƒà¸Šà¹‰à¹à¸¥à¹‰à¸§à¸—ี่" -registeredUserUsingInviteCode: "ใช้คำเชิà¸à¹à¸¥à¹‰à¸§à¹‚ดย" +inviteCodeUsedAt: "วันเวลาที่ใช้รหัสเชิà¸" +registeredUserUsingInviteCode: "ผู้ใช้ที่ใช้รหัสเชิà¸" waitingForMailAuth: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸à¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸à¸µà¹€à¸¡à¸¥" -inviteCodeCreator: "สร้างà¸à¸²à¸£à¹€à¸Šà¸´à¸à¹à¸¥à¹‰à¸§à¹‚ดย" -usedAt: "ใช้à¹à¸¥à¹‰à¸§à¸—ี่" +inviteCodeCreator: "ผู้ใช้ที่สร้างรหัสเชิà¸" +usedAt: "วันเวลาที่ถูà¸à¹ƒà¸Šà¹‰" unused: "ยังไม่ได้ใช้" used: "ถูà¸à¹ƒà¸Šà¹‰à¹à¸¥à¹‰à¸§" expired: "หมดà¸à¸²à¸¢à¸¸à¹à¸¥à¹‰à¸§" @@ -1148,7 +1149,7 @@ renotes: "รีโน้ต" loadReplies: "à¹à¸ªà¸”งà¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸š" loadConversation: "à¹à¸ªà¸”งบทสนทนา" pinnedList: "รายชื่à¸à¸—ี่ปัà¸à¸«à¸¡à¸¸à¸”ไว้" -keepScreenOn: "เปิดหน้าจà¸à¹„ว้" +keepScreenOn: "เปิดหน้าจà¸à¸à¸¸à¸›à¸à¸£à¸“์ค้างไว้" verifiedLink: "ความเป็นเจ้าขà¸à¸‡à¸¥à¸´à¸‡à¸à¹Œà¹„ด้รับà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¹à¸¥à¹‰à¸§" notifyNotes: "à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¹‚พสต์ใหม่" unnotifyNotes: "หยุดà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¹‚น้ตใหม่" @@ -1159,6 +1160,7 @@ showRenotes: "à¹à¸ªà¸”งรีโน้ต" edited: "à¹à¸à¹‰à¹„ขà¹à¸¥à¹‰à¸§" notificationRecieveConfig: "à¸à¸²à¸£à¸•ั้งค่าà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™" mutualFollow: "ติดตามซึ่งà¸à¸±à¸™à¹à¸¥à¸°à¸à¸±à¸™" +followingOrFollower: "à¸à¸³à¸¥à¸±à¸‡à¸•ิดตามหรืà¸à¸œà¸¹à¹‰à¸•ิดตาม" fileAttachedOnly: "เฉพาะโน้ตที่มีไฟล์เท่านั้น" showRepliesToOthersInTimeline: "à¹à¸ªà¸”งà¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¸œà¸¹à¹‰à¸à¸·à¹ˆà¸™à¸¥à¸‡à¹ƒà¸™à¹„ทม์ไลน์" hideRepliesToOthersInTimeline: "ไม่à¹à¸ªà¸”งà¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¸œà¸¹à¹‰à¸à¸·à¹ˆà¸™à¸¥à¸‡à¹ƒà¸™à¹„ทม์ไลน์" @@ -1168,6 +1170,12 @@ confirmShowRepliesAll: "à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¹„ม่ภconfirmHideRepliesAll: "à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¹„ม่สามารถย้à¸à¸™à¸à¸¥à¸±à¸šà¹„ด้ คุณต้à¸à¸‡à¸à¸²à¸£à¸‹à¹ˆà¸à¸™à¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¸œà¸¹à¹‰à¸à¸·à¹ˆà¸™à¸ˆà¸²à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ุà¸à¸„นที่คุณติดตามà¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¹„ทม์ไลน์ขà¸à¸‡à¸„ุณหรืà¸à¹„ม่?" externalServices: "บริà¸à¸²à¸£à¸ ายนà¸à¸" sourceCode: "ซà¸à¸£à¹Œà¸ªà¹‚ค้ด" +sourceCodeIsNotYetProvided: "ซà¸à¸£à¹Œà¸ªà¹‚ค้ดยังไม่พร้à¸à¸¡à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ โปรดติดต่à¸à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¸£à¸°à¸šà¸šà¸‚à¸à¸‡à¸„ุณเพื่à¸à¹à¸à¹‰à¹„ขปัà¸à¸«à¸²à¸™à¸µà¹‰" +repositoryUrl: "URL ขà¸à¸‡ repository" +repositoryUrlDescription: "หาà¸à¸¡à¸µà¸—ี่เà¸à¹‡à¸šà¸‹à¸à¸£à¹Œà¸ªà¹‚ค้ดที่เปิดเผยต่à¸à¸ªà¸²à¸˜à¸²à¸£à¸“ะ ให้ป้à¸à¸™ URL ที่เà¸à¹‡à¸šà¸‹à¸à¸£à¹Œà¸ªà¹‚ค้ดนั้น à¹à¸•่หาà¸à¸„ุณใช้ Misskey ตามต้นฉบับ (ไม่มีà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸‹à¸à¸£à¹Œà¸ªà¹‚ค้ด) ให้ป้à¸à¸™ https://github.com/misskey-dev/misskey" +repositoryUrlOrTarballRequired: "หาà¸à¸„ุณไม่มี repository สาธารณะ คุณจะต้à¸à¸‡à¸ˆà¸±à¸”เตรียม tarball à¹à¸—น ดู .config/example.yml สำหรับรายละเà¸à¸µà¸¢à¸”" +feedback: "ฟีดà¹à¸šà¹‡à¸" +feedbackUrl: "URLขà¸à¸‡à¸Ÿà¸µà¸”à¹à¸šà¹‡à¸" impressum: "à¸à¸´à¸¡à¹€à¸žà¸£à¸ªà¸Šà¸±à¹ˆà¸™" impressumUrl: "URL à¸à¸´à¸¡à¹€à¸žà¸£à¸ªà¸Šà¸±à¹ˆà¸™" impressumDescription: "à¸à¸²à¸£à¸•ิดป้ายà¸à¸³à¸à¸±à¸š (Impressum) มีผลบังคับใช้ในบางประเทศà¹à¸¥à¸°à¸ ูมิภาค เช่น ประเทศเยà¸à¸£à¸¡à¸™à¸µ" @@ -1179,7 +1187,7 @@ attach: "à¹à¸™à¸š" detach: "นำà¸à¸à¸" detachAll: "เà¸à¸²à¸à¸à¸à¸—ั้งหมด" angle: "à¹à¸à¸‡à¹€à¸à¸´à¸¥" -flip: "ย้à¸à¸™à¸à¸¥à¸±à¸š" +flip: "พลิà¸" showAvatarDecorations: "à¹à¸ªà¸”งตà¸à¹à¸•่งà¸à¸§à¸•าร" releaseToRefresh: "ปล่à¸à¸¢à¹€à¸žà¸·à¹ˆà¸à¸£à¸µà¹€à¸Ÿà¸£à¸Š" refreshing: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸µà¹€à¸Ÿà¸£à¸Š..." @@ -1203,15 +1211,29 @@ soundWillBePlayed: "จะมีà¸à¸²à¸£à¹€à¸¥à¹ˆà¸™à¹€à¸à¸Ÿà¹€à¸Ÿà¸à¸•์เ showReplay: "ดูรีเพลย์" replay: "รีเพลย์" replaying: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸µà¹€à¸žà¸¥à¸¢à¹Œ" +endReplay: "à¸à¸à¸à¸ˆà¸²à¸à¸£à¸µà¹€à¸žà¸¥à¸¢à¹Œ" +copyReplayData: "คัดลà¸à¸à¸‚้à¸à¸¡à¸¹à¸¥à¸£à¸µà¹€à¸žà¸¥à¸¢à¹Œ" ranking: "à¸à¸±à¸™à¸”ับ" lastNDays: "ล่าสุด {n} วันที่à¹à¸¥à¹‰à¸§" backToTitle: "à¸à¸¥à¸±à¸šà¹„ปหน้าไตเติ้ล" hemisphere: "พื้นที่ที่à¸à¸²à¸¨à¸±à¸¢à¸à¸¢à¸¹à¹ˆ" -withSensitive: "à¹à¸ªà¸”งโน้ตที่มีไฟล์ที่ระบุว่ามีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" +withSensitive: "à¹à¸ªà¸”งโน้ตที่มีไฟล์เนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" userSaysSomethingSensitive: "โพสต์ที่มีไฟล์เนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™à¸‚à¸à¸‡ {name}" enableHorizontalSwipe: "ปัดเพื่à¸à¸ªà¸¥à¸±à¸šà¹à¸—็บ" +loading: "à¸à¸³à¸¥à¸±à¸‡à¹‚หลด" +surrender: "ยà¸à¸¡à¹à¸žà¹‰" +gameRetry: "เริ่มเà¸à¸¡à¹ƒà¸«à¸¡à¹ˆ" _bubbleGame: howToPlay: "วิธีเล่น" + hold: "หยุดชั่วคราว" + _score: + score: "คะà¹à¸™à¸™" + scoreYen: "จำนวนเงินที่ได้รับ" + highScore: "คะà¹à¸™à¸™à¸ªà¸¹à¸‡à¸ªà¸¸à¸”" + maxChain: "จำนวน chain สูงสุด" + yen: "{yen} เยน" + estimatedQty: "{qty} à¸à¸±à¸™" + scoreSweets: "โà¸à¸™à¸´à¸‡à¸´à¸£à¸´ {onigiriQtyWithUnit}" _howToPlay: section1: "ขยับตำà¹à¸«à¸™à¹ˆà¸‡à¹à¸¥à¸°à¸§à¸²à¸‡à¸§à¸±à¸•ถุลงในà¸à¸¥à¹ˆà¸à¸‡" section2: "เมื่à¸à¸§à¸±à¸•ถุประเภทเดียวà¸à¸±à¸™à¸¡à¸²à¸£à¸§à¸¡à¸à¸±à¸™ พวà¸à¸¡à¸±à¸™à¸ˆà¸°à¸à¸¥à¸²à¸¢à¹€à¸›à¹‡à¸™à¸§à¸±à¸•ถุใหม่à¹à¸¥à¸°à¸„ุณจะได้รับคะà¹à¸™à¸™" @@ -1219,16 +1241,16 @@ _bubbleGame: _announcement: forExistingUsers: "ผู้ใช้งานที่มีà¸à¸¢à¸¹à¹ˆà¹€à¸—่านั้น" forExistingUsersDescription: "à¸à¸²à¸£à¸›à¸£à¸°à¸à¸²à¸¨à¸™à¸µà¹‰à¸ˆà¸°à¹à¸ªà¸”งต่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่มีà¸à¸¢à¸¹à¹ˆ ณ จุดที่เผยà¹à¸žà¸£à¹ˆà¸™à¸±à¹‰à¸™à¹†à¸–้าหาà¸à¹€à¸›à¸´à¸”ใช้งาน ถ้าหาà¸à¸›à¸´à¸”ใช้งานผู้ที่à¸à¸³à¸¥à¸±à¸‡à¸ªà¸¡à¸±à¸„รใหม่หลังจาà¸à¹‚พสต์à¹à¸¥à¹‰à¸§à¸™à¸±à¹‰à¸™à¸à¹‡à¸ˆà¸°à¹€à¸«à¹‡à¸™à¹€à¸Šà¹ˆà¸™à¸à¸±à¸™" - needConfirmationToRead: "จำเป็นต้à¸à¸‡à¸¢à¸·à¸™à¸¢à¸±à¸™à¹€à¸žà¸·à¹ˆà¸à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸šà¸à¸à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" - needConfirmationToReadDescription: "ข้à¸à¸„วามà¹à¸ˆà¹‰à¸‡à¹à¸¢à¸ ถ้าหาà¸à¸•้à¸à¸‡à¸à¸²à¸£à¹€à¸žà¸·à¹ˆà¸à¸¢à¸·à¸™à¸¢à¸±à¸™à¸§à¹ˆà¸²à¸à¸³à¸¥à¸±à¸‡à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸›à¸£à¸°à¸à¸²à¸¨à¸™à¸µà¹‰à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§à¸ˆà¸°à¹à¸ªà¸”งขึ้นถ้าหาà¸à¹€à¸›à¸´à¸”ใช้งาน à¸à¸²à¸£à¸›à¸£à¸°à¸à¸²à¸¨à¸™à¸±à¹‰à¸™à¸ˆà¸°à¹„ม่รวมà¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¸Ÿà¸±à¸‡à¸à¹Œà¸Šà¸±à¹ˆà¸™à¸§à¹ˆà¸² \"ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸—ั้งหมดว่าà¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§\"" + needConfirmationToRead: "จำเป็นต้à¸à¸‡à¸¢à¸·à¸™à¸¢à¸±à¸™à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" + needConfirmationToReadDescription: "à¸à¸¥à¹ˆà¸à¸‡à¹‚ต้ตà¸à¸šà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸ˆà¸°à¸›à¸£à¸²à¸à¸à¸‚ึ้นเมื่à¸à¸ˆà¸°à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§ นà¸à¸à¸ˆà¸²à¸à¸™à¸µà¹‰à¸¢à¸±à¸‡à¸—ำให้ประà¸à¸²à¸¨à¸™à¸µà¹‰à¸¢à¸±à¸‡à¹„ม่ถูà¸à¸à¹ˆà¸²à¸™à¹€à¸¡à¸·à¹ˆà¸à¹ƒà¸Šà¹‰à¸Ÿà¸±à¸‡à¸à¹Œà¸Šà¸±à¹ˆà¸™ “ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸¯ ทั้งหมดว่าà¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§â€" end: "เà¸à¹‡à¸šà¸›à¸£à¸°à¸à¸²à¸¨" tooManyActiveAnnouncementDescription: "à¸à¸²à¸£à¸¡à¸µà¸›à¸£à¸°à¸à¸²à¸¨à¸—ี่ใช้งานมาà¸à¹€à¸à¸´à¸™à¹„ปนั้นà¸à¸²à¸ˆà¸ˆà¸°à¸—ำให้ประสบà¸à¸²à¸£à¸“์ขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸™à¸±à¹‰à¸™à¸”ูà¹à¸¢à¹ˆà¸¥à¸‡ โปรดà¸à¸£à¸¸à¸“าพิจารณาà¸à¸²à¸£à¹€à¸à¹‡à¸šà¸›à¸£à¸°à¸à¸²à¸¨à¸—ี่ล้าสมัยด้วยนะค่ะ" - readConfirmTitle: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸šà¸à¸à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§à¹€à¸¥à¸¢à¸¡à¸±à¹‰à¸¢?" - readConfirmText: "à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¸ˆà¸°à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸‚à¸à¸‡ \"{title}\" บà¸à¸à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§à¸™à¸°" + readConfirmTitle: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§à¹€à¸¥à¸¢à¹„หม?" + readConfirmText: "จะทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹ƒà¸ªà¹ˆ “{title}†ว่าà¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" shouldNotBeUsedToPresentPermanentInfo: "เราขà¸à¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¹ƒà¸Šà¹‰à¸›à¸£à¸°à¸à¸²à¸¨à¹€à¸žà¸·à¹ˆà¸à¹‚พสต์ข้à¸à¸¡à¸¹à¸¥à¹à¸šà¸š flow มาà¸à¸à¸§à¹ˆà¸²à¸‚้à¸à¸¡à¸¹à¸¥à¹à¸šà¸š stock เนื่à¸à¸‡à¸ˆà¸²à¸à¸¡à¸µà¹à¸™à¸§à¹‚น้มที่จะส่งผลเสียต่ภUX โดยเฉพาะสำหรับผู้ใช้ใหม่" dialogAnnouncementUxWarn: "เราขà¸à¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¹ƒà¸Šà¹‰à¸”้วยความระมัดระวัง เนื่à¸à¸‡à¸ˆà¸²à¸à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¹à¸šà¸šà¸à¸¥à¹ˆà¸à¸‡à¹‚ต้ตà¸à¸šà¸•ั้งà¹à¸•่ 2 รายà¸à¸²à¸£à¸‚ึ้นไปพร้à¸à¸¡à¸à¸±à¸™à¸à¸²à¸ˆà¸ªà¹ˆà¸‡à¸œà¸¥à¹€à¸ªà¸µà¸¢à¸•่ภUX ได้à¸à¸¢à¹ˆà¸²à¸‡à¸¡à¸²à¸" silence: "ไม่มีà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™" - silenceDescription: "หาà¸à¹€à¸›à¸´à¸”ใช้งาน จะไม่ได้à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸›à¸£à¸°à¸à¸²à¸¨à¸™à¸µà¹‰ à¹à¸¥à¸°à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸ˆà¸°à¹„ม่จำเป็นต้à¸à¸‡à¸à¹ˆà¸²à¸™" + silenceDescription: "หาà¸à¹€à¸›à¸´à¸”ใช้งาน จะไม่มีà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸›à¸£à¸°à¸à¸²à¸¨à¸™à¸µà¹‰ à¹à¸¥à¸°à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸ˆà¸°à¹„ม่จำเป็นต้à¸à¸‡à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" _initialAccountSetting: accountCreated: "คุณได้สร้างบัà¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณสำเร็จเรียบร้à¸à¸¢à¹à¸¥à¹‰à¸§!" letsStartAccountSetup: "สำหรับผู้เริ่มต้นมาตั้งค่าโปรไฟล์ขà¸à¸‡à¸„ุณà¸à¸±à¸™à¹€à¸–à¸à¸°" @@ -1315,7 +1337,7 @@ _timelineDescription: _serverRules: description: "ชุดขà¸à¸‡à¸à¸Žà¸—ี่จะà¹à¸ªà¸”งà¸à¹ˆà¸à¸™à¸à¸²à¸£à¸¥à¸‡à¸—ะเบียนเราขà¸à¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¸•ั้งค่าสรุปข้à¸à¸à¸³à¸«à¸™à¸”ในà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£" _serverSettings: - iconUrl: "ไà¸à¸„à¸à¸™ URL" + iconUrl: "URL ไà¸à¸„à¸à¸™" appIconDescription: "ระบุไà¸à¸„à¸à¸™à¸—ี่จะใช้เมื่ภ{host} à¹à¸ªà¸”งเป็นà¹à¸à¸›" appIconUsageExample: "E.g. เป็น PWA หรืà¸à¹€à¸¡à¸·à¹ˆà¸à¹à¸ªà¸”งผลเป็นบุ๊à¸à¸¡à¸²à¸£à¹Œà¸à¸«à¸™à¹‰à¸²à¸ˆà¸à¸«à¸¥à¸±à¸à¸šà¸™à¹‚ทรศัพท์" appIconStyleRecommendation: "เนื่à¸à¸‡à¸ˆà¸²à¸à¹„à¸à¸„à¸à¸™à¸à¸²à¸ˆà¸–ูà¸à¸„รà¸à¸šà¸•ัดเป็นสี่เหลี่ยมจัตุรัสหรืà¸à¸§à¸‡à¸à¸¥à¸¡ จึงà¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¹ƒà¸Šà¹‰à¹„à¸à¸„à¸à¸™à¸—ี่มีขà¸à¸šà¸ªà¸µà¸£à¸à¸šà¹† เนื้à¸à¸«à¸²" @@ -1603,7 +1625,7 @@ _role: assignTarget: "มà¸à¸šà¸«à¸¡à¸²à¸¢" descriptionOfAssignTarget: "à¹à¸šà¸š<b>ปรับเà¸à¸‡</b> เพิ่มถà¸à¸™à¸šà¸—บาทนี้à¹à¸à¹ˆà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸”้วยตัวเà¸à¸‡\nà¹à¸šà¸š<b>มีเงื่à¸à¸™à¹„ข</b> เพิ่มถà¸à¸™à¸šà¸—บาทนี้à¹à¸à¹ˆà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¹‚ดยà¸à¸±à¸•โนมัติหาà¸à¹€à¸‚้าเงื่à¸à¸™à¹„ขใดต่à¸à¹„ปนี้" manual: "ปรับเà¸à¸‡" - manualRoles: "บทบาทà¹à¸šà¸šà¸—ำเà¸à¸‡" + manualRoles: "บทบาทà¹à¸šà¸šà¸—ำมืà¸" conditional: "มีเงื่à¸à¸™à¹„ข" conditionalRoles: "บทบาทà¹à¸šà¸šà¸¡à¸µà¹€à¸‡à¸·à¹ˆà¸à¸™à¹„ข" condition: "เงื่à¸à¸™à¹„ข" @@ -1615,13 +1637,13 @@ _role: baseRole: "เทมเพลตบทบาท" useBaseValue: "ใช้ตามเทมเพลตบทบาท" chooseRoleToAssign: "เลืà¸à¸à¸šà¸—บาทที่ต้à¸à¸‡à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”" - iconUrl: "ไà¸à¸„à¸à¸™ URL" + iconUrl: "URL ไà¸à¸„à¸à¸™" asBadge: "à¹à¸ªà¸”งเป็นตรา" descriptionOfAsBadge: "เมื่à¸à¹€à¸›à¸´à¸”ใช้งาน ไà¸à¸„à¸à¸™à¸šà¸—บาทจะปราà¸à¸à¸–ัดจาà¸à¸Šà¸·à¹ˆà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" isExplorable: "ค้นหาผู้ใช้ได้ง่ายขึ้นโดยดูจาà¸à¸šà¸—บาท" descriptionOfIsExplorable: "เมื่à¸à¹€à¸›à¸´à¸”ใช้งาน ไทมไลน์บทบาทนี้à¹à¸¥à¸°à¸ªà¸¡à¸²à¸Šà¸´à¸à¸—ี่มีบทบาทนี้จะเปิดเผยเป็นสาธารณะ" - displayOrder: "ตำà¹à¸«à¸™à¹ˆà¸‡" - descriptionOfDisplayOrder: "ยิ่งตัวเลขสูง ตำà¹à¸«à¸™à¹ˆà¸‡ UI à¸à¹‡à¸¢à¸´à¹ˆà¸‡à¸ªà¸¹à¸‡à¸‚ึ้นนะ" + displayOrder: "ลำดับà¸à¸²à¸£à¹à¸ªà¸”งผล" + descriptionOfDisplayOrder: "เลขที่สูงà¸à¸§à¹ˆà¸²à¸ˆà¸°à¹à¸ªà¸”งบน UI à¸à¹ˆà¸à¸™" canEditMembersByModerator: "à¸à¸™à¸¸à¸à¸²à¸•ให้ผู้ควบคุมà¹à¸à¹‰à¹„ขสมาชิà¸" descriptionOfCanEditMembersByModerator: "เมื่à¸à¹€à¸›à¸´à¸”ใช้ นà¸à¸à¹€à¸«à¸™à¸·à¸à¸ˆà¸²à¸à¸œà¸¹à¹‰à¸„วบคุมà¹à¸¥à¸°à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¸£à¸°à¸šà¸šà¹à¸¥à¹‰à¸§ จะสามารถเพิ่มถà¸à¸™à¸šà¸—บาทนี้à¹à¸à¹ˆà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¹„ด้ à¹à¸•่เมื่à¸à¸›à¸´à¸”ใช้ จะมีเฉพาะผู้ดูà¹à¸¥à¸£à¸°à¸šà¸šà¹€à¸—่านั้นที่จะสามารถดำเนินà¸à¸²à¸£à¹„ด้" priority: "ลำดับความสำคัà¸" @@ -1633,6 +1655,7 @@ _role: gtlAvailable: "à¸à¸²à¸£à¸”ูไทม์ไลน์ทั่วโลà¸" ltlAvailable: "à¸à¸²à¸£à¸”ูไทม์ไลน์ในท้à¸à¸‡à¸–ิ่น" canPublicNote: "สามารถโพสต์à¹à¸šà¸šà¸ªà¸²à¸˜à¸²à¸£à¸“ะ" + mentionMax: "จำนวนà¸à¸²à¸£à¸à¸¥à¹ˆà¸²à¸§à¸–ึงสูงสุดต่à¸à¹‚น้ต" canInvite: "สร้างรหัสเชิà¸à¸à¸´à¸™à¸ªà¹à¸•นซ์" inviteLimit: "จำà¸à¸±à¸”à¸à¸²à¸£à¹€à¸Šà¸´à¸" inviteLimitCycle: "คูลดาวน์ในà¸à¸²à¸£à¹€à¸Šà¸´à¸" @@ -1656,6 +1679,7 @@ _role: canUseTranslator: "à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¹à¸›à¸¥" avatarDecorationLimit: "จำนวนà¸à¸²à¸£à¸•à¸à¹à¸•่งไà¸à¸„à¸à¸™à¸ªà¸¹à¸‡à¸ªà¸¸à¸”ที่สามารถติดตั้งได้" _condition: + roleAssignedTo: "มà¸à¸šà¸«à¸¡à¸²à¸¢à¹ƒà¸«à¹‰à¸¡à¸µà¸šà¸—บาทà¹à¸šà¸šà¸—ำมืà¸" isLocal: "ผู้ใช้ในพื้นที่" isRemote: "ผู้ใช้ระยะไà¸à¸¥" createdLessThan: "สร้างน้à¸à¸¢à¸à¸§à¹ˆà¸²" @@ -1685,13 +1709,13 @@ _emailUnavailable: smtp: "เซิร์ฟเวà¸à¸£à¹Œà¸à¸µà¹€à¸¡à¸¥à¸™à¸µà¹‰à¹„ม่มีà¸à¸²à¸£à¸•à¸à¸šà¸ªà¸™à¸à¸‡" banned: "คุณไม่สามารถลงทะเบียนด้วยที่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸™à¸µà¹‰à¹„ด้" _ffVisibility: - public: "เผยà¹à¸žà¸£à¹ˆ" + public: "สาธารณะ" followers: "ปราà¸à¸à¹ƒà¸«à¹‰à¹à¸à¹ˆà¸œà¸¹à¹‰à¸•ิดตามเท่านั้น" private: "ส่วนตัว" _signup: almostThere: "เà¸à¸·à¸à¸šà¸ˆà¸°à¹€à¸ªà¸£à¹‡à¸ˆà¹à¸¥à¹‰à¸§" emailAddressInfo: "à¸à¸£à¸¸à¸“าà¸à¸£à¸à¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸—ี่คุณใช้ ที่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸‚à¸à¸‡à¸„ุณจะไม่ถูà¸à¹€à¸œà¸¢à¹à¸žà¸£à¹ˆà¸ªà¸¹à¹ˆà¸ªà¸²à¸˜à¸²à¸£à¸“ชน" - emailSent: "เราได้ส่งà¸à¸µà¹€à¸¡à¸¥à¸¢à¸·à¸™à¸¢à¸±à¸™à¹„ปยังที่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸‚à¸à¸‡à¸„ุณà¹à¸¥à¹‰à¸§à¸™à¸° ({email}) โปรดคลิà¸à¸¥à¸´à¸‡à¸à¹Œà¸—ี่รวมไว้เพื่à¸à¸ªà¸£à¹‰à¸²à¸‡à¸šà¸±à¸à¸Šà¸µà¹ƒà¸«à¹‰à¹€à¸ªà¸£à¹‡à¸ˆà¸ªà¸´à¹‰à¸™" + emailSent: "à¸à¸µà¹€à¸¡à¸¥à¸¢à¸·à¸™à¸¢à¸±à¸™à¹„ด้ถูà¸à¸ªà¹ˆà¸‡à¹„ปยังที่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸—ี่คุณป้à¸à¸™ ({email}) à¹à¸¥à¹‰à¸§ à¸à¸£à¸¸à¸“าติดตามลิงà¸à¹Œà¹ƒà¸™à¸à¸µà¹€à¸¡à¸¥à¹€à¸žà¸·à¹ˆà¸à¸ªà¸£à¹‰à¸²à¸‡à¸šà¸±à¸à¸Šà¸µà¹ƒà¸«à¹‰à¹€à¸ªà¸£à¹‡à¸ˆà¸ªà¸¡à¸šà¸¹à¸£à¸“์ ลิงà¸à¹Œà¸—ี่ให้ไว้จะหมดà¸à¸²à¸¢à¸¸à¹ƒà¸™ 30 นาที" _accountDelete: accountDelete: "ลบบัà¸à¸Šà¸µà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" mayTakeTime: "เนื่à¸à¸‡à¸ˆà¸²à¸à¸à¸²à¸£à¸¥à¸šà¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¸ˆà¸°à¹€à¸›à¹‡à¸™à¸à¸£à¸°à¸šà¸§à¸™à¸à¸²à¸£à¸—ี่ต้à¸à¸‡à¹ƒà¸Šà¹‰à¸—รัพยาà¸à¸£à¸¡à¸²à¸ จึงà¸à¸²à¸ˆà¸ˆà¸°à¸•้à¸à¸‡à¹ƒà¸Šà¹‰à¹€à¸§à¸¥à¸²à¸ªà¸±à¸à¸„รู่ถึงจะเสร็จสมบูรณ์ ทั้งนี้ขึ้นà¸à¸¢à¸¹à¹ˆà¸à¸±à¸šà¸ˆà¸³à¸™à¸§à¸™à¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸—ี่คุณสร้างà¹à¸¥à¸°à¸ˆà¸³à¸™à¸§à¸™à¹„ฟล์ที่คุณà¸à¸±à¸›à¹‚หลดนะ" @@ -1729,7 +1753,7 @@ _plugin: viewSource: "ดูต้นฉบับ" _preferencesBackups: list: "สร้างà¸à¸²à¸£à¸ªà¸³à¸£à¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥" - saveNew: "บันทึà¸à¹ƒà¸«à¸¡à¹ˆ" + saveNew: "บันทึà¸à¸‚้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡à¹ƒà¸«à¸¡à¹ˆ" loadFile: "โหลดจาà¸à¹„ฟล์" apply: "นำไปใช้à¸à¸±à¸šà¸à¸¸à¸›à¸à¸£à¸“์นี้" save: "บันทึà¸" @@ -1739,8 +1763,8 @@ _preferencesBackups: applyConfirm: "คุณต้à¸à¸‡à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‚้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡ \"{name}\" à¸à¸±à¸šà¸à¸¸à¸›à¸à¸£à¸“์นี้à¸à¸¢à¹ˆà¸²à¸‡à¸‡à¸±à¹‰à¸™à¸ˆà¸£à¸´à¸‡à¸«à¸£à¸ à¸à¸²à¸£à¸•ั้งค่าที่มีà¸à¸¢à¸¹à¹ˆà¸‚à¸à¸‡à¸à¸¸à¸›à¸à¸£à¸“์นี้จะถูà¸à¹€à¸‚ียนทับนะ" saveConfirm: "บันทึà¸à¸‚้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡à¹€à¸›à¹‡à¸™ {name} มั้ย?" deleteConfirm: "ลบข้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡ {name} มั้ย?" - renameConfirm: "เปลี่ยนชื่à¸à¸‚้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡à¸™à¸µà¹‰à¸ˆà¸²à¸ \"{old}\" เป็น \"{new}\" หรืà¸à¹„ม่?" - noBackups: "ไม่มีข้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡à¸™à¸° คุณสามารถสำรà¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•ั้งค่าไคลเà¸à¸™à¸•์ขà¸à¸‡à¸„ุณบนเซิร์ฟเวà¸à¸£à¹Œà¸™à¸µà¹‰à¹‚ดยใช้ \"สร้างà¸à¸²à¸£à¸ªà¸³à¸£à¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥à¹ƒà¸«à¸¡à¹ˆ\"ได้นะ" + renameConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸Šà¸·à¹ˆà¸à¸‚้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡à¸ˆà¸²à¸ “{old}†เป็น “{new}†ใช่ไหม?" + noBackups: "ไม่มีข้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡ สามารถบันทึà¸à¸à¸²à¸£à¸•ั้งค่าไคลเà¸à¸™à¸•์ปัจจุบันไปยังเซิร์ฟเวà¸à¸£à¹Œà¸”้วย “บันทึà¸à¸‚้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡à¹ƒà¸«à¸¡à¹ˆâ€" createdAt: "สร้างเมื่à¸: {date} {time}" updatedAt: "à¸à¸±à¸›à¹€à¸”ตเมื่à¸: {date} {time}" cannotLoad: "à¸à¸²à¸£à¹‚หลดล้มเหลว" @@ -1756,14 +1780,16 @@ _aboutMisskey: contributors: "ผู้สนับสนุนหลัà¸" allContributors: "ผู้มีส่วนร่วมทั้งหมด" source: "ซà¸à¸£à¹Œà¸ªà¹‚ค้ด" + original: "ต้นฉบับ" + thisIsModifiedVersion: "{name} ใช้ Misskey เวà¸à¸£à¹Œà¸Šà¸±à¸™à¸”ัดà¹à¸›à¸¥à¸‡" translation: "à¹à¸›à¸¥à¸ าษา Misskey" donate: "บริจาคให้à¸à¸±à¸š Misskey" - morePatrons: " ขà¸à¸šà¸„ุณทุà¸à¸—่านที่ร่วมà¸à¸±à¸™à¸Šà¹ˆà¸§à¸¢à¹€à¸«à¸¥à¸·à¸à¸•ลà¸à¸”มานะคะ 🥰" - patrons: "สมาชิà¸à¸žà¸±à¸™à¸˜à¸¡à¸´à¸•ร" + morePatrons: "à¹à¸¥à¸°à¸à¸µà¸à¸«à¸¥à¸²à¸¢à¸—่านที่ไม่ได้เà¸à¹ˆà¸¢à¸™à¸²à¸¡ ขà¸à¸šà¸„ุณที่ร่วมช่วยเหลืà¸à¸•ลà¸à¸”มานะคะ 🥰" + patrons: "ผู้à¸à¸¸à¸›à¸–ัมภ์" projectMembers: "สมาชิà¸à¹ƒà¸™à¹‚ครงà¸à¸²à¸£" _displayOfSensitiveMedia: - respect: "ซ่à¸à¸™à¸ªà¸·à¹ˆà¸à¸—ี่ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" - ignore: "à¹à¸ªà¸”งสื่à¸à¸—ี่ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" + respect: "ซ่à¸à¸™à¸ªà¸·à¹ˆà¸à¸—ี่มีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" + ignore: "à¹à¸ªà¸”งสื่à¸à¸—ี่มีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" force: "ซ่à¸à¸™à¸ªà¸·à¹ˆà¸à¸—ั้งหมด" _instanceTicker: none: "ไม่ต้à¸à¸‡à¹à¸ªà¸”ง" @@ -1831,8 +1857,8 @@ _theme: importInfo: "ถ้าหาà¸à¸•้à¸à¸‡à¸à¸²à¸£à¸›à¹‰à¸à¸™à¹‚ค้ดที่นี่ คุณยังสามารถนำเข้าไปยังโปรà¹à¸à¸£à¸¡à¹à¸à¹‰à¹„ขธีมได้" deleteConstantConfirm: "คุณต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¸„่าคงที่ {const} หรืà¸à¸›à¹ˆà¸²à¸§?" keys: - accent: "เน้น" - bg: "ภาพพื้นหลัง" + accent: "สีหลัà¸" + bg: "พื้นหลัง" fg: "ข้à¸à¸„วาม" focus: "โฟà¸à¸±à¸ª" indicator: "ตัวบ่งชี้" @@ -1868,11 +1894,11 @@ _theme: wallpaperOverlay: "วà¸à¸¥à¸¥à¹Œà¹€à¸›à¹€à¸›à¸à¸£à¹Œà¸‹à¹‰à¸à¸™à¸—ับ" badge: "ตรา" messageBg: "พื้นหลังà¹à¸Šà¸—" - accentDarken: "เน้น (มืด)" - accentLighten: "เน้น (สว่าง)" + accentDarken: "สีหลัภ(มืด)" + accentLighten: "สีหลัภ(สว่าง)" fgHighlighted: "ข้à¸à¸„วามที่ไฮไลต์" _sfx: - note: "หมายเหตุ" + note: "โน้ต" noteMy: "โน้ตขà¸à¸‡à¸•ัวเà¸à¸‡" notification: "à¸à¸²à¸£à¹€à¹€à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™" antenna: "เสาà¸à¸²à¸à¸²à¸¨" @@ -1959,7 +1985,7 @@ _permissions: "read:reactions": "ดูรีà¹à¸à¸„ชั่นขà¸à¸‡à¸„ุณ" "write:reactions": "à¹à¸à¹‰à¹„ขรีà¹à¸à¸„ชั่นขà¸à¸‡à¸„ุณ" "write:votes": "โหวตบนสำรวจความคิดเห็น" - "read:pages": "ดหน้าเพจ" + "read:pages": "ดูหน้าเพจ" "write:pages": "à¹à¸à¹‰à¹„ขหรืà¸à¸¥à¸šà¹€à¸žà¸ˆà¸‚à¸à¸‡à¸„ุณ" "read:page-likes": "ดูรายà¸à¸²à¸£à¹€à¸žà¸ˆà¸—ี่ถูà¸à¹ƒà¸ˆà¹„ว้" "write:page-likes": "à¹à¸à¹‰à¹„ขรายà¸à¸²à¸£à¹€à¸žà¸ˆà¸—ี่ถูà¸à¹ƒà¸ˆ" @@ -1971,8 +1997,8 @@ _permissions: "write:gallery": "à¹à¸à¹‰à¹„ขà¹à¸à¸¥à¹€à¸¥à¸à¸£à¸µà¹ˆà¸‚à¸à¸‡à¸„ุณ" "read:gallery-likes": "ดูรายà¸à¸²à¸£à¹‚พสต์à¹à¸à¸¥à¹€à¸¥à¸à¸£à¸µà¸—ี่ถูà¸à¹ƒà¸ˆà¹„ว้" "write:gallery-likes": "à¹à¸à¹‰à¹„ขรายà¸à¸²à¸£à¹‚พสต์à¹à¸à¸¥à¹€à¸¥à¸à¸£à¸µà¸—ี่ถูà¸à¹ƒà¸ˆà¹„ว้" - "read:flash": "วิว เพลย์" - "write:flash": "à¹à¸à¹‰à¹„ขเพลย์" + "read:flash": "ดู Play" + "write:flash": "à¹à¸à¹‰à¹„ข Play" "read:flash-likes": "ดูรายà¸à¸²à¸£ play ที่ถูà¸à¹ƒà¸ˆà¹„ว้" "write:flash-likes": "à¹à¸à¹‰à¹„ขรายà¸à¸²à¸£ play ที่ถูà¸à¹ƒà¸ˆà¹„ว้" "read:admin:abuse-user-reports": "ดูรายงานจาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" @@ -1999,8 +2025,8 @@ _permissions: "read:admin:roles": "ดูบทบาท" "write:admin:relays": "จัดà¸à¸²à¸£à¸£à¸µà¹€à¸¥à¸¢à¹Œ" "read:admin:relays": "ดูรีเลย์" - "write:admin:invite-codes": "จัดà¸à¸²à¸£à¸„ำเชิà¸" - "read:admin:invite-codes": "ดูรหัสคำเชิà¸" + "write:admin:invite-codes": "จัดà¸à¸²à¸£à¸£à¸«à¸±à¸ªà¹€à¸Šà¸´à¸" + "read:admin:invite-codes": "ดูรหัสเชิà¸" "write:admin:announcements": "จัดà¸à¸²à¸£à¸›à¸£à¸°à¸à¸²à¸¨" "read:admin:announcements": "ดูประà¸à¸²à¸¨" "write:admin:avatar-decorations": "จัดà¸à¸²à¸£à¸à¸²à¸£à¸•à¸à¹à¸•่งà¸à¸§à¸•าร" @@ -2018,7 +2044,7 @@ _permissions: "read:admin:stream": "ใช้ Websocket API สำหรับผู้ดูà¹à¸¥à¸£à¸°à¸šà¸š" "write:admin:ad": "จัดà¸à¸²à¸£à¹‚ฆษณา" "read:admin:ad": "ดูโฆษณา" - "write:invite-codes": "สร้างรหัสคำเชิà¸" + "write:invite-codes": "สร้างรหัสเชิà¸" "read:invite-codes": "รับรหัสเชิà¸" "write:clip-favorite": "ควบคุมà¸à¸²à¸£à¸–ูà¸à¹ƒà¸ˆà¸‚à¸à¸‡à¸„ลิป" "read:clip-favorite": "ดูà¸à¸²à¸£à¸–ูà¸à¹ƒà¸ˆà¸‚à¸à¸‡à¸„ลิป" @@ -2071,8 +2097,8 @@ _widgets: onlineUsers: "ผู้ใช้ที่à¸à¸à¸™à¹„ลน์" jobQueue: "คิวงาน" serverMetric: "ตัวชี้วัดเซิร์ฟเวà¸à¸£à¹Œ" - aiscript: "AiScript คà¸à¸™à¹‚ซล" - aiscriptApp: "AiScript à¹à¸à¸ž" + aiscript: " คà¸à¸™à¹‚ซล AiScript" + aiscriptApp: "à¹à¸à¸› AiScript" aichan: "ไà¸" userList: "รายชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" _userList: @@ -2086,15 +2112,15 @@ _cw: files: "{count} ไฟล์" _poll: noOnlyOneChoice: "จำเป็นต้à¸à¸‡à¸¡à¸µà¸à¸¢à¹ˆà¸²à¸‡à¸™à¹‰à¸à¸¢à¸ªà¸à¸‡à¸•ัวเลืà¸à¸" - choiceN: "ตัวเลืà¸à¸ {n}" - noMore: "คุณไม่สามารถเพิ่มตัวเลืà¸à¸à¸à¸·à¹ˆà¸™à¹„ด้" + choiceN: "ตัวเลืà¸à¸à¸—ี่ {n}" + noMore: "เพิ่มตัวเลืà¸à¸à¸à¸µà¸à¹„ม่ได้à¹à¸¥à¹‰à¸§" canMultipleVote: "สามารถตà¸à¸šà¹„ด้หลายคำตà¸à¸š" - expiration: "สิ้นสุดà¸à¸²à¸£à¸ªà¸³à¸£à¸§à¸ˆà¸„วามคิดเห็น" - infinite: "ไม่ต้à¸à¸‡à¹€à¸¥à¸¢" - at: "จบที่..." - after: "สิ้นสุดหลัง..." + expiration: "สิ้นสุดโพล" + infinite: "ไม่à¸à¸³à¸«à¸™à¸”ระยะเวลา" + at: "ระบุวันเวลา" + after: "ระบุระยะเวลา" deadlineDate: "วันสิ้นสุด" - deadlineTime: "ชั่วโมง" + deadlineTime: "เวลา" duration: "ระยะเวลา" votesCount: "{n} คะà¹à¸™à¸™à¹€à¸ªà¸µà¸¢à¸‡" totalVotes: "{n} คะà¹à¸™à¸™à¹€à¸ªà¸µà¸¢à¸‡à¸—ั้งหมด" @@ -2102,17 +2128,17 @@ _poll: showResult: "ดูผลลัพธ์" voted: "โหวตà¹à¸¥à¹‰à¸§" closed: "สิ้นสุดà¹à¸¥à¹‰à¸§" - remainingDays: "จะเสร็จสิ้นในà¸à¸µà¸ {d} วัน {h} ชั่วโมง" - remainingHours: "{h} ชั่วโมง(s) {m} นาที(s) ที่เหลืà¸à¸à¸¢à¸¹à¹ˆ" - remainingMinutes: "{m} นาที(s) {s} วินาที(s) ที่เหลืà¸à¸à¸¢à¸¹à¹ˆ" - remainingSeconds: "{s} นาที(s) ที่เหลืà¸à¸à¸¢à¸¹à¹ˆ" + remainingDays: "เหลืà¸à¸à¸µà¸ {d} วัน {h} ชั่วโมง" + remainingHours: "เหลืà¸à¸à¸µà¸ {h} ชั่วโมง {m} นาที" + remainingMinutes: "เหลืà¸à¸à¸µà¸ {m} นาที {s} วินาที" + remainingSeconds: "เหลืà¸à¸à¸µà¸ {s} วินาที" _visibility: public: "สาธารณะ" publicDescription: "โน้ตขà¸à¸‡à¸„ุณจะปราà¸à¸à¹à¸à¹ˆà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ุà¸à¸„น" home: "หน้าà¹à¸£à¸" homeDescription: "โพสลงไทม์ไลน์ที่บ้านเท่านั้น" followers: "ผู้ติดตาม" - followersDescription: "ทำให้ผู้ติดตามนั้นมà¸à¸‡à¹€à¸«à¹‡à¸™à¹à¸„่คุณเท่านั้น" + followersDescription: "เฉพาะผู้ติดตามเท่านั้นที่มà¸à¸‡à¹€à¸«à¹‡à¸™à¹„ด้" specified: "ไดเร็ค" specifiedDescription: "ทำให้มà¸à¸‡à¹€à¸«à¹‡à¸™à¹„ด้เฉพาะผู้ใช้ที่ระบุเท่านั้น" disableFederation: "ไม่มีสหพันธ์" @@ -2122,11 +2148,11 @@ _postForm: quotePlaceholder: "à¸à¹‰à¸²à¸‡à¹‚น้ตนี้..." channelPlaceholder: "โพสต์ลงช่à¸à¸‡..." _placeholders: - a: "คุณเป็นà¸à¸°à¹„รไปหรà¸?" - b: "เà¸à¸´à¸”à¸à¸°à¹„รขึ้นรà¸à¸šà¸•ัวคุณ?" - c: "คุณà¸à¸³à¸¥à¸±à¸‡à¸„ิดà¸à¸°à¹„รà¸à¸¢à¸¹à¹ˆ?" - d: "คุณต้à¸à¸‡à¸à¸²à¸£à¸ˆà¸°à¸žà¸¹à¸”à¸à¸°à¹„ร?" - e: "เริ่มเขียน..." + a: "ตà¸à¸™à¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¸¢à¸±à¸‡à¹„งบ้าง?" + b: "มีà¸à¸°à¹„รเà¸à¸´à¸”ขึ้นหรืà¸à¹€à¸›à¸¥à¹ˆà¸²?" + c: "à¸à¸³à¸¥à¸±à¸‡à¸„ิดà¸à¸°à¹„รà¸à¸¢à¸¹à¹ˆ?" + d: "ต้à¸à¸‡à¸à¸²à¸£à¸ˆà¸°à¸žà¸¹à¸”à¸à¸°à¹„รไหม?" + e: "มาเขียนà¸à¸±à¸™à¹€à¸–à¸à¸°" f: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸à¹ƒà¸«à¹‰à¸„ุณเขียน..." _profile: name: "ชื่à¸" @@ -2140,11 +2166,11 @@ _profile: metadataContent: "เนื้à¸à¸«à¸²" changeAvatar: "เปลี่ยนà¸à¸§à¸²à¸•าร์" changeBanner: "เปลี่ยนà¹à¸šà¸™à¹€à¸™à¸à¸£à¹Œ" - verifiedLinkDescription: "โดยà¸à¸²à¸£à¸›à¹‰à¸à¸™ URL ที่มีลิงà¸à¹Œà¹„ปยังโปรไฟล์ขà¸à¸‡à¸„ุณตรงนี้ ส่วนไà¸à¸„à¸à¸™à¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸„วามเป็นเจ้าขà¸à¸‡à¸™à¸±à¹‰à¸™à¸à¹‡à¸ªà¸²à¸¡à¸²à¸£à¸–à¹à¸ªà¸”งถัดจาà¸à¸Ÿà¸´à¸¥à¸”์ได้นะ" + verifiedLinkDescription: "หาà¸à¸›à¹‰à¸à¸™ URL ที่มีลิงà¸à¹Œà¹„ปยังโปรไฟล์ขà¸à¸‡à¸„ุณ ไà¸à¸„à¸à¸™à¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸„วามเป็นเจ้าขà¸à¸‡à¸ˆà¸°à¹à¸ªà¸”งถัดจาà¸à¸Ÿà¸´à¸¥à¸”์นั้น ๆ" avatarDecorationMax: "คุณสามารถเพิ่มà¸à¸²à¸£à¸•à¸à¹à¸•่งได้สูงสุด {max}" _exportOrImport: allNotes: "โน้ตทั้งหมด" - favoritedNotes: "บันทึà¸à¸—ี่ชื่นชà¸à¸š" + favoritedNotes: "โน้ตที่ถูà¸à¹ƒà¸ˆà¹„ว้" clips: "คลิป" followingList: "à¸à¸³à¸¥à¸±à¸‡à¸•ิดตาม" muteList: "ปิดเสียง" @@ -2227,7 +2253,7 @@ _pages: summary: "สรุปเพจ" alignCenter: "เซ็นเตà¸à¸£à¹Œ" hideTitleWhenPinned: "ซ่à¸à¸™à¸Šà¸·à¹ˆà¸à¸«à¸™à¹‰à¸²à¹€à¸žà¸ˆà¹€à¸¡à¸·à¹ˆà¸à¸›à¸±à¸à¸«à¸¡à¸¸à¸”ไว้ที่โปรไฟล์" - font: "ตัวà¸à¸±à¸à¸©à¸£" + font: "à¹à¸šà¸šà¸à¸±à¸à¸©à¸£" fontSerif: "Serif" fontSansSerif: "Sans Serif" eyeCatchingImageSet: "ตั้งค่าภาพขนาดย่à¸" @@ -2253,27 +2279,28 @@ _relayStatus: accepted: "ได้รับà¸à¸²à¸£à¸à¸™à¸¸à¸¡à¸±à¸•ิ" rejected: "ถูà¸à¸›à¸à¸´à¹€à¸ªà¸˜" _notification: - fileUploaded: "ไฟล์ถูà¸à¸à¸±à¸žà¹‚หลดà¹à¸¥à¹‰à¸§à¸™à¹ˆà¸°" + fileUploaded: "ไฟล์ถูà¸à¸à¸±à¸›à¹‚หลดà¹à¸¥à¹‰à¸§" youGotMention: "{name} à¸à¸¥à¹ˆà¸²à¸§à¸–ึงคุณ" youGotReply: "{name} ตà¸à¸šà¸à¸¥à¸±à¸šà¸–ึงคุณ" - youGotQuote: "{name} à¸à¹‰à¸²à¸‡à¸–ึงคุณ" + youGotQuote: "{name} à¸à¹‰à¸²à¸‡à¸à¸´à¸‡à¸„ุณ" youRenoted: "รีโน้ตจาภ{name}" youWereFollowed: "ได้ติดตามคุณ" - youReceivedFollowRequest: "คุณมีคำขà¸à¸•ิดตามใหม่น่ะ" - yourFollowRequestAccepted: "คำขà¸à¸•ิดตามขà¸à¸‡à¸„ุณได้รับà¸à¸²à¸£à¸¢à¸à¸¡à¸£à¸±à¸šà¹à¸¥à¹‰à¸§à¸™à¹ˆà¸°" - pollEnded: "โพลสำรวจความคิดเห็นผลลัพธ์มีพร้à¸à¸¡à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" + youReceivedFollowRequest: "ได้รับคำขà¸à¸•ิดตาม" + yourFollowRequestAccepted: "คำขà¸à¸•ิดตามได้รับà¸à¸²à¸£à¸à¸™à¸¸à¸¡à¸±à¸•ิà¹à¸¥à¹‰à¸§" + pollEnded: "ผลโพลà¸à¸à¸à¸¡à¸²à¹à¸¥à¹‰à¸§" newNote: "โพสต์ใหม่" unreadAntennaNote: "เสาà¸à¸²à¸à¸²à¸¨ {name}" roleAssigned: "ได้รับบทบาท" - emptyPushNotificationMessage: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¹à¸šà¸šà¸žà¸¸à¸Šà¹„ด้รับà¸à¸²à¸£à¸à¸±à¸žà¹€à¸”ทà¹à¸¥à¹‰à¸§" + emptyPushNotificationMessage: "à¸à¸±à¸›à¹€à¸”ตà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¹à¸šà¸šà¸žà¸¸à¸Šà¹à¸¥à¹‰à¸§" achievementEarned: "รับความสำเร็จ" testNotification: "ทดสà¸à¸šà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™" checkNotificationBehavior: "à¸à¸”เพื่à¸à¸”ูลัà¸à¸©à¸“ะà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™" sendTestNotification: "ส่งทดสà¸à¸šà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™" notificationWillBeDisplayedLikeThis: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸¡à¸µà¸¥à¸±à¸à¸©à¸“ะà¹à¸šà¸šà¸™à¸µà¹‰" reactedBySomeUsers: "ถูà¸à¸£à¸µà¹à¸à¸„ชั่นโดยผู้ใช้ {n} ราย" - renotedBySomeUsers: "Renote จาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸ˆà¸³à¸™à¸§à¸™ {n} ราย" + renotedBySomeUsers: "รีโน้ตจาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰ {n} ราย" followedBySomeUsers: "มีผู้ติดตาม {n} ราย" + flushNotification: "ล้างประวัติà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™" _types: all: "ทั้งหมด" note: "โน้ตใหม่" @@ -2283,9 +2310,9 @@ _notification: renote: "รีโน้ต" quote: "à¸à¹‰à¸²à¸‡à¸„ำพูด" reaction: "รีà¹à¸à¸„ชั่น" - pollEnded: "โพลนี้สิ้นสุดลงà¹à¸¥à¹‰à¸§" - receiveFollowRequest: "ได้รับคำขà¸à¸•ิดตาม\n" - followRequestAccepted: "ยà¸à¸¡à¸£à¸±à¸šà¸„ำขà¸à¸•ิดตาม" + pollEnded: "โพลสิ้นสุดà¹à¸¥à¹‰à¸§" + receiveFollowRequest: "ได้รับคำร้à¸à¸‡à¸‚à¸à¸•ิดตาม" + followRequestAccepted: "à¸à¸™à¸¸à¸¡à¸±à¸•ิให้ติดตามà¹à¸¥à¹‰à¸§" roleAssigned: "ให้บทบาท" achievementEarned: "ปลดล็à¸à¸à¸„วามสำเร็จà¹à¸¥à¹‰à¸§" app: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸ˆà¸²à¸à¹à¸à¸›à¸—ี่มีลิงà¸à¹Œ" @@ -2322,7 +2349,7 @@ _deck: list: "รายà¸à¸²à¸£" channel: "ช่à¸à¸‡" mentions: "พูดถึง" - direct: "ไดเร็ค" + direct: "ไดเร็à¸à¸•์" roleTimeline: "บทบาทไทม์ไลน์" _dialog: charactersExceeded: "คุณà¸à¸³à¸¥à¸±à¸‡à¸¡à¸µà¸•ัวà¸à¸±à¸à¸‚ระเà¸à¸´à¸™à¸‚ีดจำà¸à¸±à¸”สูงสุดà¹à¸¥à¹‰à¸§à¸™à¸°! ปัจจุบันà¸à¸¢à¸¹à¹ˆà¸—ี่ {current} จาภ{max}" @@ -2353,8 +2380,8 @@ _moderationLogTypes: updateRole: "à¸à¸±à¸›à¹€à¸”ตบทบาทà¹à¸¥à¹‰à¸§" assignRole: "ได้รับมà¸à¸šà¸«à¸¡à¸²à¸¢à¸šà¸—บาท" unassignRole: "ถà¸à¸”à¸à¸à¸à¸ˆà¸²à¸à¸šà¸—บาทà¹à¸¥à¹‰à¸§" - suspend: "ถูà¸à¸£à¸°à¸‡à¸±à¸š" - unsuspend: "เลิà¸à¸–ูà¸à¸£à¸°à¸‡à¸±à¸š" + suspend: "ระงับ" + unsuspend: "เลิà¸à¸£à¸°à¸‡à¸±à¸š" addCustomEmoji: "เพิ่มเà¸à¹‚มจิที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡à¹à¸¥à¹‰à¸§" updateCustomEmoji: "à¸à¸±à¸›à¹€à¸”ตเà¸à¹‚มจิที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡à¹à¸¥à¹‰à¸§" deleteCustomEmoji: "ลบเà¸à¹‚มจิที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡à¸à¸à¸à¹à¸¥à¹‰à¸§" @@ -2369,12 +2396,13 @@ _moderationLogTypes: deleteGlobalAnnouncement: "ลบประà¸à¸²à¸¨à¸—ั่วโลà¸à¸à¸à¸à¹à¸¥à¹‰à¸§" deleteUserAnnouncement: "ลบประà¸à¸²à¸¨à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸à¸à¸à¹à¸¥à¹‰à¸§" resetPassword: "รีเซ็ตรหัสผ่าน" - suspendRemoteInstance: "à¸à¸´à¸™à¸ªà¹à¸•นซ์ระยะไà¸à¸¥à¸–ูà¸à¸£à¸°à¸‡à¸±à¸š" - unsuspendRemoteInstance: "à¸à¸´à¸™à¸ªà¹à¸•นซ์ระยะไà¸à¸¥à¹€à¸¥à¸´à¸à¸à¸²à¸£à¸£à¸°à¸‡à¸±à¸š" + suspendRemoteInstance: "ระงับà¸à¸´à¸™à¸ªà¹à¸•นซ์ระยะไà¸à¸¥" + unsuspendRemoteInstance: "เลิà¸à¸£à¸°à¸‡à¸±à¸šà¸à¸´à¸™à¸ªà¹à¸•นซ์ระยะไà¸à¸¥" + updateRemoteInstanceNote: "à¸à¸±à¸›à¹€à¸”ตโน้ตà¸à¸²à¸£à¸à¸¥à¸±à¹ˆà¸™à¸à¸£à¸à¸‡à¸‚à¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•นซ์ระยะไà¸à¸¥à¹à¸¥à¹‰à¸§" markSensitiveDriveFile: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹„ฟล์ว่ามีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" unmarkSensitiveDriveFile: "ยà¸à¹€à¸¥à¸´à¸à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹„ฟล์ว่ามีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" resolveAbuseReport: "รายงานได้รับà¸à¸²à¸£à¹à¸à¹‰à¹„ขà¹à¸¥à¹‰à¸§" - createInvitation: "สร้างคำเชิà¸" + createInvitation: "สร้างรหัสเชิà¸" createAd: "สร้างโฆษณาà¹à¸¥à¹‰à¸§" deleteAd: "ลบโฆษณาà¸à¸à¸à¹à¸¥à¹‰à¸§" updateAd: "à¸à¸±à¸›à¹€à¸”ตโฆษณาà¹à¸¥à¹‰à¸§" @@ -2491,6 +2519,8 @@ _reversi: opponentHasSettingsChanged: "à¸à¸µà¸à¸à¹ˆà¸²à¸¢à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸à¸²à¸£à¸•ั้งค่า" allowIrregularRules: "à¸à¸™à¸¸à¸à¸²à¸•à¸à¸Žà¸—ี่ไม่ปรà¸à¸•ิ (โหมดฟรีทุà¸à¸à¸¢à¹ˆà¸²à¸‡)" disallowIrregularRules: "ไม่à¸à¸™à¸¸à¸à¸²à¸•à¸à¸Žà¸—ี่ไม่ปรà¸à¸•ิ" + showBoardLabels: "à¹à¸ªà¸”งหมายเลขà¹à¸–ว/คà¸à¸¥à¸±à¸¡à¸™à¹Œà¸šà¸™à¸à¸£à¸°à¸”าน" + useAvatarAsStone: "ใช้รูปà¸à¸§à¸•ารเป็นหมาà¸" _offlineScreen: title: "à¸à¸à¸Ÿà¹„ลน์ - ไม่สามารถเชื่à¸à¸¡à¸•่à¸à¸à¸±à¸šà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¹„ด้" header: "ไม่สามารถเชื่à¸à¸¡à¸•่à¸à¸à¸±à¸šà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¹„ด้" diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index 7cfdde3204..59883f4a6c 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -1048,6 +1048,7 @@ verifiedLink: "Chúng tôi đã xác nháºn bạn là chá»§ sở hữu cá»§a Ä‘Æ sourceCode: "Mã nguồn" flip: "Láºt" lastNDays: "{n} ngà y trước" +surrender: "Từ chối" _announcement: forExistingUsers: "Chỉ những ngưá»i dùng đã tồn tại" forExistingUsersDescription: "Nếu được báºt, thông báo nà y sẽ chỉ hiển thị vá»›i những ngưá»i dùng đã tồn tại và o lúc thông báo được tạo. Nếu tắt Ä‘i, những tà i khoản má»›i đăng ký sau khi thông báo được đăng lên cÅ©ng sẽ thấy nó." diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index d0891f0678..17ad6e7150 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -336,7 +336,7 @@ displayOfSensitiveMedia: "æ˜¾ç¤ºæ•æ„Ÿåª’体" whenServerDisconnected: "与æœåŠ¡å™¨è¿žæŽ¥ä¸æ–æ—¶" disconnectedFromServer: "已和æœåС噍æ–开连接" reload: "釿–°åŠ è½½" -doNothing: "å…³é—弹窗" +doNothing: "å…³é—" reloadConfirm: "确定è¦é‡æ–°åŠ è½½å—?" watch: "关注" unwatch: "å–æ¶ˆå…³æ³¨" @@ -991,6 +991,7 @@ neverShow: "ä¸å†æ˜¾ç¤º" remindMeLater: "ç¨åŽæé†’æˆ‘" didYouLikeMisskey: "您喜欢 Misskey å—?" pleaseDonate: "Misskey 是 {host} 所使用的å…费软件。为了今åŽä¹Ÿèƒ½å¤Ÿç»´æŒ Misskey 的开å‘,请在有余力的情况下进行æåŠ©ï¼" +correspondingSourceIsAvailable: "对应的æºä»£ç å¯åœ¨{anchor}找到" roles: "角色" role: "角色" noRole: "角色ä¸å˜åœ¨" @@ -1042,6 +1043,7 @@ sensitiveWords: "æ•æ„Ÿè¯" sensitiveWordsDescription: "将包å«è®¾ç½®è¯çš„帖åçš„å¯è§èŒƒå›´è®¾ç½®ä¸ºé¦–页。å¯ä»¥é€šè¿‡ç”¨æ¢è¡Œç¬¦åˆ†éš”æ¥è®¾ç½®å¤šä¸ªã€‚" sensitiveWordsDescription2: "AND æ¡ä»¶ç”¨ç©ºæ ¼åˆ†éš”,æ£åˆ™è¡¨è¾¾å¼ç”¨æ–œçº¿åŒ…裹。" prohibitedWords: "ç¦ç”¨è¯" +prohibitedWordsDescription: "å‘布包å«è®¾å®šè¯æ±‡çš„取志¶å°†å‡ºé”™ã€‚å¯ç”¨æ¢è¡Œè®¾å®šå¤šä¸ªå…³é”®å—" prohibitedWordsDescription2: "AND æ¡ä»¶ç”¨ç©ºæ ¼åˆ†éš”,æ£åˆ™è¡¨è¾¾å¼ç”¨æ–œçº¿åŒ…裹。" hiddenTags: "éšè—æ ‡ç¾" hiddenTagsDescription: "è®¾å®šçš„æ ‡ç¾å°†ä¸ä¼šåœ¨æ—¶é—´çº¿ä¸Šæ˜¾ç¤ºã€‚å¯ä½¿ç”¨æ¢è¡Œæ¥è®¾ç½®å¤šä¸ªæ ‡ç¾ã€‚" @@ -1115,7 +1117,7 @@ branding: "å“牌" enableServerMachineStats: "公开æœåŠ¡å™¨ç¡¬ä»¶ç»Ÿè®¡ä¿¡æ¯" enableIdenticonGeneration: "å¯ç”¨ç”Ÿæˆç”¨æˆ· Identicon" turnOffToImprovePerformance: "å…³é—该选项å¯ä»¥æé«˜æ€§èƒ½ã€‚" -createInviteCode: "å‘行邀请ç " +createInviteCode: "生æˆé‚€è¯·ç " createWithOptions: "使用选项æ¥åˆ›å»º" createCount: "å‘行数" inviteCodeCreated: "已创建邀请ç " @@ -1127,7 +1129,7 @@ noExpirationDate: "ä¸è®¾ç½®æœ‰æ•ˆæ—¥æœŸ" inviteCodeUsedAt: "邀请ç 被使用的日期和时间" registeredUserUsingInviteCode: "使用了邀请ç 的用户" waitingForMailAuth: "ç‰å¾…验è¯ç”µå邮件" -inviteCodeCreator: "å‘行邀请ç 的用户" +inviteCodeCreator: "生æˆé‚€è¯·ç 的用户" usedAt: "使用时间" unused: "未使用" used: "已使用" @@ -1158,6 +1160,7 @@ showRenotes: "显示转帖" edited: "已编辑" notificationRecieveConfig: "通知接收设置" mutualFollow: "互相关注" +followingOrFollower: "å…³æ³¨ä¸æˆ–关注者" fileAttachedOnly: "ä»…é™åª’体" showRepliesToOthersInTimeline: "在时间线ä¸åŒ…å«ç»™åˆ«äººçš„回å¤" hideRepliesToOthersInTimeline: "在时间线ä¸éšè—给别人的回å¤" @@ -1167,6 +1170,12 @@ confirmShowRepliesAll: "æ¤æ“作ä¸å¯æ’¤é”€ã€‚确认è¦åœ¨æ—¶é—´çº¿ä¸åŒ…å«çŽ confirmHideRepliesAll: "æ¤æ“作ä¸å¯æ’¤é”€ã€‚确认è¦åœ¨æ—¶é—´çº¿ä¸éšè—现在关注的所有人的回å¤å—?" externalServices: "外部æœåŠ¡" sourceCode: "æºä»£ç " +sourceCodeIsNotYetProvided: "还未æä¾›æºä»£ç 。è¦è§£å†³æ¤é—®é¢˜è¯·è”系管ç†å‘˜ã€‚" +repositoryUrl: "仓库地å€" +repositoryUrlDescription: "è‹¥æºä»£ç 所在的仓库是公开的,请填入对应的 URLã€‚è‹¥æ˜¯æŒ‰åŽŸæ ·ä½¿ç”¨ Misskeyï¼ˆå¹¶æœªè¿½åŠ æˆ–è€…ä¿®æ”¹ä»£ç )的情况请填入 https://github.com/misskey-dev/misskey。" +repositoryUrlOrTarballRequired: "è‹¥ä»“åº“å¹¶æœªå…¬å¼€ï¼Œåˆ™éœ€è¦æä¾› tarball 作为替代。详情请看 .config/example.yml。" +feedback: "å馈" +feedbackUrl: "å馈地å€" impressum: "è¿è¥å•†ä¿¡æ¯" impressumUrl: "è¿è¥å•†ä¿¡æ¯åœ°å€" impressumDescription: "德国ç‰å›½å®¶å’Œåœ°åŒºæœ‰ä¹‰åŠ¡å±•ç¤ºæ¤ç±»ä¿¡æ¯ï¼ˆImpressum)。" @@ -1196,11 +1205,14 @@ seasonalScreenEffect: "åº”æ™¯çš„ç”»é¢æ•ˆæžœ" decorate: "装饰" addMfmFunction: "æ·»åŠ è£…é¥°" enableQuickAddMfmFunction: "显示高级 MFM 选择器" +bubbleGame: "泡泡游æˆ" sfx: "音效" soundWillBePlayed: "å£°éŸ³å°†ä¼šæ’æ”¾" -showReplay: "æŸ¥çœ‹é‡æ’" +showReplay: "观看回放" replay: "釿’" replaying: "釿’ä¸" +endReplay: "结æŸå›žæ”¾" +copyReplayData: "å¤åˆ¶å›žæ”¾æ•°æ®" ranking: "排行榜" lastNDays: "最近 {n} 天" backToTitle: "è¿”å›žæ ‡é¢˜" @@ -1208,8 +1220,19 @@ hemisphere: "å±…ä½åœ°åŒº" withSensitive: "æ˜¾ç¤ºåŒ…å«æ•感媒体的帖å" userSaysSomethingSensitive: "å« {name} æ•æ„Ÿæ–‡ä»¶çš„帖å" enableHorizontalSwipe: "æ»‘åŠ¨åˆ‡æ¢æ ‡ç¾é¡µ" +loading: "读å–ä¸" +surrender: "å–æ¶ˆ" +gameRetry: "é‡è¯•" _bubbleGame: howToPlay: "游æˆè¯´æ˜Ž" + hold: "抓ä½" + _score: + score: "得分" + scoreYen: "赚到的钱" + highScore: "最高分" + maxChain: "最高连击数" + yen: "{yen} 日元" + estimatedQty: "约 {qty} 个" _howToPlay: section1: "对准ä½ç½®å°†Emoji投入盒å。" section2: "相åŒçš„Emojiç›¸äº’æŽ¥è§¦åˆæˆåŽä¼šå¾—到新的Emoji,以æ¤èŽ·å¾—åˆ†æ•°ã€‚" @@ -1298,8 +1321,8 @@ _initialTutorial: description: "对于æœåŠ¡å™¨æ–¹é’ˆæ‰€è¦æ±‚è¦æ±‚çš„ï¼Œåˆæˆ–者ä¸é€‚åˆç›´æŽ¥å±•ç¤ºçš„é™„ä»¶ï¼Œè¯·æ·»åŠ ã€Œæ•æ„Ÿã€æ ‡è®°ã€‚\n" tryThisFile: "è¯•è¯•çœ‹ï¼Œå°†é™„åŠ åˆ°æ¤çª—å£çš„å›¾åƒæ ‡æ³¨ä¸ºæ•感ï¼" _exampleNote: - note: "ä¸è¯¥æ‰“开纳豆的盖å的……" - method: "è¦æ ‡æ³¨é™„ä»¶ä¸ºæ•æ„Ÿå†…容,请å•击该文件以打开èœå•,然åŽå•å‡»â€œè®¾ç½®ä¸ºæ•æ„Ÿâ€ã€‚" + note: "拆纳豆包装时出错了…" + method: "è¦æ ‡æ³¨é™„ä»¶ä¸ºæ•æ„Ÿå†…容,请å•击该文件以打开èœå•,然åŽå•å‡»â€œæ ‡è®°ä¸ºæ•æ„Ÿå†…容â€ã€‚" sensitiveSucceeded: "é™„åŠ æ–‡ä»¶æ—¶ï¼Œè¯·éµå¾ªæœåŠ¡å™¨çš„æ¡æ¬¾æ¥è®¾ç½®æ£ç¡®æ•感设定。\n" doItToContinue: "å°†å›¾åƒæ ‡è®°ä¸ºæ•æ„ŸåŽæ‰èƒ½å¤Ÿç»§ç»" _done: @@ -1630,8 +1653,9 @@ _role: gtlAvailable: "查看全局时间线" ltlAvailable: "查看本地时间线" canPublicNote: "å…许公开å‘帖" + mentionMax: "帖å内最多æåŠæ•°" canInvite: "呿”¾æœåŠ¡å™¨é‚€è¯·ç " - inviteLimit: "å¯å‘行邀请ç 的数é‡" + inviteLimit: "å¯ç”Ÿæˆé‚€è¯·ç 的数é‡" inviteLimitCycle: "邀请ç çš„å‘行间隔" inviteExpirationTime: "邀请ç 的有效日期" canManageCustomEmojis: "管ç†è‡ªå®šä¹‰è¡¨æƒ…符å·" @@ -1653,6 +1677,7 @@ _role: canUseTranslator: "使用翻译功能" avatarDecorationLimit: "坿·»åŠ å¤´åƒæŒ‚件的最大个数" _condition: + roleAssignedTo: "已分é…给手动角色" isLocal: "是本地用户" isRemote: "是远程用户" createdLessThan: "账户创建时间少于" @@ -1753,6 +1778,8 @@ _aboutMisskey: contributors: "主è¦è´¡çŒ®è€…" allContributors: "全体贡献者" source: "æºä»£ç " + original: "原版" + thisIsModifiedVersion: "{name}æ£åœ¨ä½¿ç”¨ä¿®æ”¹åŽçš„ Misskey。" translation: "翻译 Misskey" donate: "赞助 Misskey" morePatrons: "è¿˜æœ‰å¾ˆå¤šå…¶å®ƒçš„äººä¹Ÿåœ¨æ”¯æŒæˆ‘们,éžå¸¸æ„Ÿè°¢ðŸ¥°" @@ -2015,7 +2042,7 @@ _permissions: "read:admin:stream": "使用管ç†å‘˜ç”¨çš„ Websocket API" "write:admin:ad": "编辑广告" "read:admin:ad": "查看广告" - "write:invite-codes": "å‘行邀请ç " + "write:invite-codes": "生æˆé‚€è¯·ç " "read:invite-codes": "获å–å·²å‘行的邀请ç " "write:clip-favorite": "编辑便ç¾çš„点赞" "read:clip-favorite": "查看便ç¾çš„点赞" @@ -2271,6 +2298,7 @@ _notification: reactedBySomeUsers: "{n} 人回应了" renotedBySomeUsers: "{n} 人转å‘了" followedBySomeUsers: "被 {n} 人关注" + flushNotification: "é‡ç½®é€šçŸ¥åކå²" _types: all: "全部" note: "用户的新帖å" @@ -2368,10 +2396,11 @@ _moderationLogTypes: resetPassword: "é‡ç½®å¯†ç " suspendRemoteInstance: "åœæ¢è¿œç¨‹æœåС噍" unsuspendRemoteInstance: "æ¢å¤è¿œç¨‹æœåС噍" + updateRemoteInstanceNote: "更新远程æœåŠ¡å™¨çš„ç®¡ç†ç¬”è®°" markSensitiveDriveFile: "æ ‡è®°ç½‘ç›˜æ–‡ä»¶ä¸ºæ•æ„Ÿåª’体" unmarkSensitiveDriveFile: "å–æ¶ˆæ ‡è®°ç½‘ç›˜æ–‡ä»¶ä¸ºæ•æ„Ÿåª’体" resolveAbuseReport: "处ç†ä¸¾æŠ¥" - createInvitation: "å‘行邀请ç " + createInvitation: "生æˆé‚€è¯·ç " createAd: "创建了广告" deleteAd: "åˆ é™¤äº†å¹¿å‘Š" updateAd: "更新了广告" @@ -2462,6 +2491,8 @@ _reversi: myTurn: "ä½ çš„å›žåˆ" turnOf: "{name}的回åˆ" pastTurnOf: "{name}的回åˆ" + surrender: "认输" + surrendered: "已认输" timeout: "è¶…æ—¶" drawn: "平局" won: "{name}获胜" @@ -2483,6 +2514,8 @@ _reversi: opponentHasSettingsChanged: "对手更改了设定" allowIrregularRules: "å…许éžå¸¸è§„规则(完全自由)" disallowIrregularRules: "ç¦æ¢éžå¸¸è§„规则" + showBoardLabels: "显示行å·å’Œåˆ—å·" + useAvatarAsStone: "用头åƒä½œä¸ºæ£‹å" _offlineScreen: title: "ç¦»çº¿â€”â€”æ— æ³•è¿žæŽ¥åˆ°æœåС噍" header: "æ— æ³•è¿žæŽ¥åˆ°æœåС噍" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 2762a612f5..5cdecc10ac 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -991,6 +991,7 @@ neverShow: "ä¸å†é¡¯ç¤º" remindMeLater: "以後å†èªª" didYouLikeMisskey: "æ‚¨å–œæ¡ Misskey 嗎?" pleaseDonate: "Misskey 是由 {host} 使用的å…費軟體。請贊助我們,讓開發得以æŒçºŒï¼" +correspondingSourceIsAvailable: "å°æ‡‰çš„原始碼å¯ä»¥åœ¨ {anchor} 處找到。" roles: "角色" role: "角色" noRole: "沒有角色" @@ -1159,6 +1160,7 @@ showRenotes: "顯示其他人的轉發貼文" edited: "已編輯" notificationRecieveConfig: "接å—通知的è¨å®š" mutualFollow: "互相追隨" +followingOrFollower: "è¿½éš¨ä¸æˆ–追隨者" fileAttachedOnly: "顯示包å«é™„件的貼文" showRepliesToOthersInTimeline: "顯示給其他人的回覆" hideRepliesToOthersInTimeline: "在時間軸上隱è—給其他人的回覆" @@ -1168,6 +1170,12 @@ confirmShowRepliesAll: "é€²è¡Œæ¤æ“ä½œå¾Œç„¡æ³•å¾©åŽŸã€‚æ‚¨çœŸçš„å¸Œæœ›æ™‚é–“è» confirmHideRepliesAll: "é€²è¡Œæ¤æ“作後無法復原。您真的希望時間軸「ä¸åŒ…å«ã€æ‚¨ç›®å‰è¿½éš¨çš„æ‰€æœ‰äººçš„回覆嗎?" externalServices: "外部æœå‹™" sourceCode: "原始碼" +sourceCodeIsNotYetProvided: "尚未æä¾›åŽŸå§‹ç¢¼ï¼Œè«‹æ´½è©¢ç®¡ç†å“¡è§£æ±ºé€™å€‹å•題。" +repositoryUrl: "儲å˜åº« URL" +repositoryUrlDescription: "如果å˜åœ¨å¯å…¬é–‹å–得原始碼的儲å˜åº«ï¼Œè«‹è¼¸å…¥å…¶ URL。 如果您按原樣使用 Misskey(ä¸å°åŽŸå§‹ç¢¼é€²è¡Œä»»ä½•æ›´æ”¹ï¼‰ï¼Œè«‹è¼¸å…¥ https://github.com/misskey-dev/misskey。" +repositoryUrlOrTarballRequired: "如果儲å˜åº«ä¸æ˜¯å…¬é–‹çš„ï¼Œå‰‡å¿…é ˆæä¾› tarball。 詳細資訊請åƒé–± .config/example.yml。" +feedback: "æ„見回饋" +feedbackUrl: "æ„見回饋 URL" impressum: "營é‹è€…資訊" impressumUrl: "營é‹è€…資訊網å€" impressumDescription: "在德國與部份地å€å¿…é ˆè¦æ˜Žç¢ºé¡¯ç¤ºç‡Ÿé‹è€…資訊。" @@ -1203,6 +1211,8 @@ soundWillBePlayed: "å°‡æ’æ”¾éŸ³æ•ˆ" showReplay: "è§€çœ‹é‡æ’" replay: "釿’" replaying: "釿’ä¸" +endReplay: "é€€å‡ºé‡æ’" +copyReplayData: "è¤‡è£½é‡æ’資料" ranking: "排行榜" lastNDays: "éŽåŽ» {n} 天" backToTitle: "å›žåˆ°éŠæˆ²æ¨™é¡Œé " @@ -1210,8 +1220,20 @@ hemisphere: "您居ä½çš„地å€" withSensitive: "é¡¯ç¤ºåŒ…å«æ•感檔案的貼文" userSaysSomethingSensitive: "åŒ…å« {name} æ•æ„Ÿæª”案的貼文" enableHorizontalSwipe: "æ»‘å‹•åˆ‡æ›æ™‚間軸" +loading: "載入ä¸" +surrender: "退出" +gameRetry: "å†è©¦ä¸€æ¬¡" _bubbleGame: howToPlay: "玩法說明" + hold: "ä¿ç•™" + _score: + score: "分數" + scoreYen: "賺å–的金é¡" + highScore: "最高分" + maxChain: "最大çµåˆæ•¸" + yen: "{yen} 日圓" + estimatedQty: "{qty}個" + scoreSweets: "飯糰 {onigiriQtyWithUnit}" _howToPlay: section1: "調整ä½ç½®ä¸¦å°‡ç‰©é«”放入盒åä¸ã€‚" section2: "當相åŒé¡žåž‹çš„物體é»åœ¨ä¸€èµ·æ™‚,它們會變æˆä¸åŒçš„物體,您就會得到分數。" @@ -1615,7 +1637,7 @@ _role: baseRole: "基本角色" useBaseValue: "使用基本角色的值" chooseRoleToAssign: "鏿“‡è¦æŒ‡æ´¾çš„角色" - iconUrl: "圖示的URL" + iconUrl: "圖示的 URL" asBadge: "é¡¯ç¤ºç‚ºå¾½ç« " descriptionOfAsBadge: "開啟的話,角色圖示會顯示在使用者å稱æ—邊。" isExplorable: "讓使用者更容易找到您" @@ -1633,6 +1655,7 @@ _role: gtlAvailable: "ç€è¦½å…¨åŸŸæ™‚間軸" ltlAvailable: "ç€è¦½æœ¬åœ°æ™‚間軸" canPublicNote: "å…許公開貼文" + mentionMax: "貼文內的最大æåŠæ•¸" canInvite: "發行伺æœå™¨é‚€è«‹ç¢¼" inviteLimit: "å¯å»ºç«‹é‚€è«‹ç¢¼çš„æ•¸é‡" inviteLimitCycle: "邀請碼的發放間隔" @@ -1656,6 +1679,7 @@ _role: canUseTranslator: "使用翻è¯åŠŸèƒ½" avatarDecorationLimit: "é åƒè£é£¾çš„æœ€å¤§è¨ç½®é‡" _condition: + roleAssignedTo: "手動指派角色完æˆ" isLocal: "本地使用者" isRemote: "é 端使用者" createdLessThan: "å¸³æˆ¶åŠ å…¥æ™‚é–“ä¸è¶…éŽ" @@ -1756,6 +1780,8 @@ _aboutMisskey: contributors: "主è¦è²¢ç»è€…" allContributors: "全體貢ç»äººå“¡" source: "原始碼" + original: "原始" + thisIsModifiedVersion: "{name} 使用原始 Misskey 的修改版本。" translation: "ç¿»è¯ Misskey" donate: "贊助 Misskey" morePatrons: "還有許許多多幫助我們的其他人,éžå¸¸æ„Ÿè¬ä½ 們。 🥰" @@ -2274,6 +2300,7 @@ _notification: reactedBySomeUsers: "{n}人åšå‡ºäº†å應" renotedBySomeUsers: "{n}人åšäº†è½‰ç™¼" followedBySomeUsers: "被{n}人追隨了" + flushNotification: "é‡ç½®é€šçŸ¥æ·å²ç´€éŒ„" _types: all: "全部 " note: "使用者的最新貼文" @@ -2359,7 +2386,7 @@ _moderationLogTypes: updateCustomEmoji: "更新自訂表情符號" deleteCustomEmoji: "刪除自訂表情符號" updateServerSettings: "更新伺æœå™¨è¨å®š" - updateUserNote: "更新管ç†ç†è¨˜" + updateUserNote: "更新了使用者的管ç†ç†è¨˜" deleteDriveFile: "刪除檔案" deleteNote: "刪除貼文" createGlobalAnnouncement: "建立全網通知" @@ -2371,6 +2398,7 @@ _moderationLogTypes: resetPassword: "é‡è¨å¯†ç¢¼" suspendRemoteInstance: "å°éŽ–é 端伺æœå™¨" unsuspendRemoteInstance: "解除å°éŽ–é 端伺æœå™¨" + updateRemoteInstanceNote: "更新了é 端伺æœå™¨çš„管ç†ç†è¨˜" markSensitiveDriveFile: "æ¨™è¨˜ç‚ºæ•æ„Ÿæª”案" unmarkSensitiveDriveFile: "æ’¤éŠ·æ¨™è¨˜ç‚ºæ•æ„Ÿæª”案" resolveAbuseReport: "解決檢舉" @@ -2491,6 +2519,8 @@ _reversi: opponentHasSettingsChanged: "å°æ‰‹æ›´æ”¹äº†è¨å®š" allowIrregularRules: "å…許異常è¦å‰‡ï¼ˆå®Œå…¨è‡ªç”±ï¼‰" disallowIrregularRules: "ä¸å…許異常è¦å‰‡" + showBoardLabels: "在棋盤上顯示行ã€åˆ—號" + useAvatarAsStone: "用大é 貼當作棋å" _offlineScreen: title: "離線ï¼ç„¡æ³•連接伺æœå™¨" header: "無法連接伺æœå™¨" diff --git a/package.json b/package.json index 3f94448db7..dee4645ee3 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "misskey", - "version": "2024.2.0", + "version": "2024.3.0", "codename": "nasubi", "repository": { "type": "git", "url": "https://github.com/misskey-dev/misskey.git" }, - "packageManager": "pnpm@8.15.1", + "packageManager": "pnpm@8.15.4", "workspaces": [ "packages/frontend", "packages/backend", @@ -48,22 +48,22 @@ "lodash": "4.17.21" }, "dependencies": { - "cssnano": "6.0.3", + "cssnano": "6.0.5", "execa": "8.0.1", "fast-glob": "3.3.2", "ignore-walk": "6.0.4", "js-yaml": "4.1.0", - "postcss": "8.4.33", + "postcss": "8.4.35", "tar": "6.2.0", - "terser": "5.27.0", + "terser": "5.28.1", "typescript": "5.3.3" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "6.18.1", - "@typescript-eslint/parser": "6.18.1", + "@typescript-eslint/eslint-plugin": "7.1.0", + "@typescript-eslint/parser": "7.1.0", "cross-env": "7.0.3", - "cypress": "13.6.3", - "eslint": "8.56.0", + "cypress": "13.6.6", + "eslint": "8.57.0", "ncp": "2.0.0", "start-server-and-test": "2.0.3" }, diff --git a/packages/backend/migration/1708266695091-repositoryUrl-from-syuilo-to-misskey-dev.js b/packages/backend/migration/1708266695091-repositoryUrl-from-syuilo-to-misskey-dev.js new file mode 100644 index 0000000000..e4dbaa16d0 --- /dev/null +++ b/packages/backend/migration/1708266695091-repositoryUrl-from-syuilo-to-misskey-dev.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class RepositoryUrlFromSyuiloToMisskeyDev1708266695091 { + name = 'RepositoryUrlFromSyuiloToMisskeyDev1708266695091' + + async up(queryRunner) { + await queryRunner.query(`UPDATE "meta" SET "repositoryUrl" = 'https://github.com/misskey-dev/misskey' WHERE "repositoryUrl" = 'https://github.com/syuilo/misskey'`); + } + + async down(queryRunner) { + // no valid down migration + } +} diff --git a/packages/backend/migration/1708399372194-per-instance-mod-note.js b/packages/backend/migration/1708399372194-per-instance-mod-note.js new file mode 100644 index 0000000000..339a4d7af9 --- /dev/null +++ b/packages/backend/migration/1708399372194-per-instance-mod-note.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class PerInstanceModNote1708399372194 { + name = 'PerInstanceModNote1708399372194' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "instance" ADD "moderationNote" character varying(16384) NOT NULL DEFAULT ''`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "moderationNote"`); + } +} diff --git a/packages/backend/package.json b/packages/backend/package.json index 86a52faa05..8680610441 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -67,9 +67,9 @@ "dependencies": { "@aws-sdk/client-s3": "3.412.0", "@aws-sdk/lib-storage": "3.412.0", - "@bull-board/api": "5.14.0", - "@bull-board/fastify": "5.14.0", - "@bull-board/ui": "5.14.0", + "@bull-board/api": "5.14.2", + "@bull-board/fastify": "5.14.2", + "@bull-board/ui": "5.14.2", "@discordapp/twemoji": "15.0.2", "@fastify/accepts": "4.3.0", "@fastify/cookie": "9.3.1", @@ -79,13 +79,13 @@ "@fastify/multipart": "8.1.0", "@fastify/static": "6.12.0", "@fastify/view": "8.2.0", - "@misskey-dev/sharp-read-bmp": "^1.1.1", - "@misskey-dev/summaly": "^5.0.3", - "@nestjs/common": "10.2.10", - "@nestjs/core": "10.2.10", - "@nestjs/testing": "10.2.10", + "@misskey-dev/sharp-read-bmp": "1.2.0", + "@misskey-dev/summaly": "5.0.3", + "@nestjs/common": "10.3.3", + "@nestjs/core": "10.3.3", + "@nestjs/testing": "10.3.3", "@peertube/http-signature": "1.7.0", - "@simplewebauthn/server": "9.0.2", + "@simplewebauthn/server": "9.0.3", "@sinonjs/fake-timers": "11.2.2", "@smithy/node-http-handler": "2.1.10", "@swc/cli": "0.1.63", @@ -98,7 +98,7 @@ "bcryptjs": "2.4.3", "blurhash": "2.0.5", "body-parser": "1.20.2", - "bullmq": "5.1.9", + "bullmq": "5.4.0", "cacheable-lookup": "7.0.0", "cbor": "9.0.2", "chalk": "5.3.0", @@ -115,10 +115,11 @@ "file-type": "19.0.0", "fluent-ffmpeg": "2.1.2", "form-data": "4.0.0", - "got": "14.1.0", + "got": "14.2.0", "happy-dom": "10.0.3", "hpagent": "1.2.0", - "http-link-header": "1.1.1", + "htmlescape": "1.1.1", + "http-link-header": "1.1.2", "ioredis": "5.3.2", "ip-cidr": "3.1.0", "ipaddr.js": "2.1.0", @@ -127,7 +128,7 @@ "jsdom": "23.2.0", "json5": "2.2.3", "jsonld": "8.3.2", - "jsrsasign": "11.0.0", + "jsrsasign": "11.1.0", "meilisearch": "0.37.0", "mfm-js": "0.24.0", "microformats-parser": "2.0.2", @@ -135,10 +136,10 @@ "misskey-js": "workspace:*", "misskey-reversi": "workspace:*", "ms": "3.0.0-canary.1", - "nanoid": "5.0.4", + "nanoid": "5.0.6", "nested-property": "4.0.0", "node-fetch": "3.3.2", - "nodemailer": "6.9.8", + "nodemailer": "6.9.10", "nsfwjs": "2.4.2", "oauth": "0.10.0", "oauth2orize": "1.12.0", @@ -158,19 +159,19 @@ "ratelimiter": "3.4.1", "re2": "1.20.9", "redis-lock": "0.1.4", - "reflect-metadata": "0.1.14", + "reflect-metadata": "0.2.1", "rename": "1.0.4", "rss-parser": "3.13.0", "rxjs": "7.8.1", - "sanitize-html": "2.11.0", + "sanitize-html": "2.12.1", "secure-json-parse": "2.7.0", - "sharp": "0.32.6", + "sharp": "0.33.2", "slacc": "0.0.10", "strict-event-emitter-types": "2.0.0", "stringz": "2.1.0", - "systeminformation": "5.21.24", + "systeminformation": "5.22.0", "tinycolor2": "1.6.0", - "tmp": "0.2.1", + "tmp": "0.2.2", "tsc-alias": "1.8.8", "tsconfig-paths": "4.2.0", "typeorm": "0.3.20", @@ -184,7 +185,7 @@ "devDependencies": { "@jest/globals": "29.7.0", "@misskey-dev/eslint-plugin": "1.0.0", - "@nestjs/platform-express": "10.3.1", + "@nestjs/platform-express": "10.3.3", "@simplewebauthn/types": "9.0.1", "@swc/jest": "0.2.31", "@types/accepts": "1.3.7", @@ -194,6 +195,7 @@ "@types/color-convert": "2.0.3", "@types/content-disposition": "0.5.8", "@types/fluent-ffmpeg": "2.1.24", + "@types/htmlescape": "^1.1.3", "@types/http-link-header": "1.0.5", "@types/jest": "29.5.11", "@types/js-yaml": "4.0.9", @@ -202,21 +204,21 @@ "@types/jsrsasign": "10.5.12", "@types/mime-types": "2.1.4", "@types/ms": "0.7.34", - "@types/node": "20.11.17", + "@types/node": "20.11.22", "@types/node-fetch": "3.0.3", "@types/nodemailer": "6.4.14", "@types/oauth": "0.9.4", "@types/oauth2orize": "1.11.3", "@types/oauth2orize-pkce": "0.1.2", - "@types/pg": "8.11.0", + "@types/pg": "8.11.2", "@types/pug": "2.0.10", - "@types/punycode": "2.1.3", + "@types/punycode": "2.1.4", "@types/qrcode": "1.5.5", "@types/random-seed": "0.3.5", "@types/ratelimiter": "3.4.6", "@types/rename": "1.0.7", - "@types/sanitize-html": "2.9.5", - "@types/semver": "7.5.6", + "@types/sanitize-html": "2.11.0", + "@types/semver": "7.5.8", "@types/simple-oauth2": "5.0.7", "@types/sinonjs__fake-timers": "8.1.5", "@types/tinycolor2": "1.4.6", @@ -224,17 +226,17 @@ "@types/vary": "1.1.3", "@types/web-push": "3.6.3", "@types/ws": "8.5.10", - "@typescript-eslint/eslint-plugin": "6.18.1", - "@typescript-eslint/parser": "6.18.1", + "@typescript-eslint/eslint-plugin": "7.1.0", + "@typescript-eslint/parser": "7.1.0", "aws-sdk-client-mock": "3.0.1", "cross-env": "7.0.3", - "eslint": "8.56.0", + "eslint": "8.57.0", "eslint-plugin-import": "2.29.1", "execa": "8.0.1", "fkill": "^9.0.0", "jest": "29.7.0", "jest-mock": "29.7.0", - "nodemon": "3.0.3", + "nodemon": "3.1.0", "pid-port": "1.0.0", "simple-oauth2": "5.0.0" } diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts index b7796a5183..5bd885df40 100644 --- a/packages/backend/src/core/AccountMoveService.ts +++ b/packages/backend/src/core/AccountMoveService.ts @@ -20,7 +20,6 @@ import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { CacheService } from '@/core/CacheService.js'; import { ProxyAccountService } from '@/core/ProxyAccountService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { MetaService } from '@/core/MetaService.js'; @@ -60,7 +59,6 @@ export class AccountMoveService { private instanceChart: InstanceChart, private metaService: MetaService, private relayService: RelayService, - private cacheService: CacheService, private queueService: QueueService, ) { } @@ -84,7 +82,7 @@ export class AccountMoveService { Object.assign(src, update); // Update cache - this.cacheService.uriPersonCache.set(srcUri, src); + this.globalEventService.publishInternalEvent('localUserUpdated', src); const srcPerson = await this.apRendererService.renderPerson(src); const updateAct = this.apRendererService.addContext(this.apRendererService.renderUpdate(srcPerson, src)); diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts index 263df56476..d008e7ec52 100644 --- a/packages/backend/src/core/CacheService.ts +++ b/packages/backend/src/core/CacheService.ts @@ -128,10 +128,13 @@ export class CacheService implements OnApplicationShutdown { const { type, body } = obj.message as GlobalEvents['internal']['payload']; switch (type) { case 'userChangeSuspendedState': - case 'remoteUserUpdated': { + case 'userChangeDeletedState': + case 'remoteUserUpdated': + case 'localUserUpdated': { const user = await this.usersRepository.findOneBy({ id: body.id }); if (user == null) { this.userByIdCache.delete(body.id); + this.localUserByIdCache.delete(body.id); for (const [k, v] of this.uriPersonCache.cache.entries()) { if (v.value?.id === body.id) { this.uriPersonCache.delete(k); diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index c31cef36e8..2c27d33c06 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -116,6 +116,7 @@ import { FlashEntityService } from './entities/FlashEntityService.js'; import { FlashLikeEntityService } from './entities/FlashLikeEntityService.js'; import { RoleEntityService } from './entities/RoleEntityService.js'; import { ReversiGameEntityService } from './entities/ReversiGameEntityService.js'; +import { MetaEntityService } from './entities/MetaEntityService.js'; import { ApAudienceService } from './activitypub/ApAudienceService.js'; import { ApDbResolverService } from './activitypub/ApDbResolverService.js'; @@ -254,6 +255,7 @@ const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisti const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService }; const $RoleEntityService: Provider = { provide: 'RoleEntityService', useExisting: RoleEntityService }; const $ReversiGameEntityService: Provider = { provide: 'ReversiGameEntityService', useExisting: ReversiGameEntityService }; +const $MetaEntityService: Provider = { provide: 'MetaEntityService', useExisting: MetaEntityService }; const $ApAudienceService: Provider = { provide: 'ApAudienceService', useExisting: ApAudienceService }; const $ApDbResolverService: Provider = { provide: 'ApDbResolverService', useExisting: ApDbResolverService }; @@ -393,6 +395,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting FlashLikeEntityService, RoleEntityService, ReversiGameEntityService, + MetaEntityService, ApAudienceService, ApDbResolverService, @@ -528,6 +531,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $FlashLikeEntityService, $RoleEntityService, $ReversiGameEntityService, + $MetaEntityService, $ApAudienceService, $ApDbResolverService, @@ -663,6 +667,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting FlashLikeEntityService, RoleEntityService, ReversiGameEntityService, + MetaEntityService, ApAudienceService, ApDbResolverService, @@ -797,6 +802,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $FlashLikeEntityService, $RoleEntityService, $ReversiGameEntityService, + $MetaEntityService, $ApAudienceService, $ApDbResolverService, diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 64a8c1acdf..edb9335b6e 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -394,6 +394,11 @@ export class CustomEmojiService implements OnApplicationShutdown { } @bindThis + public getEmojiByName(name: string): Promise<MiEmoji | null> { + return this.emojisRepository.findOneBy({ name, host: IsNull() }); + } + + @bindThis public dispose(): void { this.cache.dispose(); } diff --git a/packages/backend/src/core/DeleteAccountService.ts b/packages/backend/src/core/DeleteAccountService.ts index fc5d217ae0..79b614edba 100644 --- a/packages/backend/src/core/DeleteAccountService.ts +++ b/packages/backend/src/core/DeleteAccountService.ts @@ -9,6 +9,7 @@ import { QueueService } from '@/core/QueueService.js'; import { UserSuspendService } from '@/core/UserSuspendService.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; @Injectable() export class DeleteAccountService { @@ -18,6 +19,7 @@ export class DeleteAccountService { private userSuspendService: UserSuspendService, private queueService: QueueService, + private globalEventService: GlobalEventService, ) { } @@ -39,5 +41,7 @@ export class DeleteAccountService { await this.usersRepository.update(user.id, { isDeleted: true, }); + + this.globalEventService.publishInternalEvent('userChangeDeletedState', { id: user.id, isDeleted: true }); } } diff --git a/packages/backend/src/core/FileInfoService.ts b/packages/backend/src/core/FileInfoService.ts index b177367a16..b8babcb3a7 100644 --- a/packages/backend/src/core/FileInfoService.ts +++ b/packages/backend/src/core/FileInfoService.ts @@ -15,6 +15,7 @@ import isSvg from 'is-svg'; import probeImageSize from 'probe-image-size'; import { type predictionType } from 'nsfwjs'; import sharp from 'sharp'; +import { sharpBmp } from '@misskey-dev/sharp-read-bmp'; import { encode } from 'blurhash'; import { createTempDir } from '@/misc/create-temp.js'; import { AiService } from '@/core/AiService.js'; @@ -122,7 +123,7 @@ export class FileInfoService { 'image/avif', 'image/svg+xml', ].includes(type.mime)) { - blurhash = await this.getBlurhash(path).catch(e => { + blurhash = await this.getBlurhash(path, type.mime).catch(e => { warnings.push(`getBlurhash failed: ${e}`); return undefined; }); @@ -407,9 +408,9 @@ export class FileInfoService { * Calculate average color of image */ @bindThis - private getBlurhash(path: string): Promise<string> { - return new Promise((resolve, reject) => { - sharp(path) + private getBlurhash(path: string, type: string): Promise<string> { + return new Promise(async (resolve, reject) => { + (await sharpBmp(path, type)) .raw() .ensureAlpha() .resize(64, 64, { fit: 'inside' }) diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 01dd133ead..90efd63f3a 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -69,6 +69,7 @@ export interface MainEventTypes { file: Packed<'DriveFile'>; }; readAllNotifications: undefined; + notificationFlushed: undefined; unreadNotification: Packed<'Notification'>; unreadMention: MiNote['id']; readAllUnreadMentions: undefined; @@ -209,8 +210,10 @@ type SerializedAll<T> = { export interface InternalEventTypes { userChangeSuspendedState: { id: MiUser['id']; isSuspended: MiUser['isSuspended']; }; + userChangeDeletedState: { id: MiUser['id']; isDeleted: MiUser['isDeleted']; }; userTokenRegenerated: { id: MiUser['id']; oldToken: string; newToken: string; }; remoteUserUpdated: { id: MiUser['id']; }; + localUserUpdated: { id: MiUser['id']; }; follow: { followerId: MiUser['id']; followeeId: MiUser['id']; }; unfollow: { followerId: MiUser['id']; followeeId: MiUser['id']; }; blockingCreated: { blockerId: MiUser['id']; blockeeId: MiUser['id']; }; diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 9cec614d5c..81ae2908d3 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -59,6 +59,8 @@ import { UtilityService } from '@/core/UtilityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { isReply } from '@/misc/is-reply.js'; import { trackPromise } from '@/misc/promise-tracker.js'; +import { isNotNull } from '@/misc/is-not-null.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; @@ -151,8 +153,6 @@ type Option = { export class NoteCreateService implements OnApplicationShutdown { #shutdownController = new AbortController(); - public static ContainsProhibitedWordsError = class extends Error {}; - constructor( @Inject(DI.config) private config: Config, @@ -263,8 +263,14 @@ export class NoteCreateService implements OnApplicationShutdown { } } - if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', meta.prohibitedWords)) { - throw new NoteCreateService.ContainsProhibitedWordsError(); + const hasProhibitedWords = await this.checkProhibitedWordsContain({ + cw: data.cw, + text: data.text, + pollChoices: data.poll?.choices, + }, meta.prohibitedWords); + + if (hasProhibitedWords) { + throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words'); } const inSilencedInstance = this.utilityService.isSilencedHost(meta.silencedHosts, user.host); @@ -379,6 +385,10 @@ export class NoteCreateService implements OnApplicationShutdown { } } + if (mentionedUsers.length > 0 && mentionedUsers.length > (await this.roleService.getUserPolicies(user.id)).mentionLimit) { + throw new IdentifiableError('9f466dab-c856-48cd-9e65-ff90ff750580', 'Note contains too many mentions'); + } + const note = await this.insertNote(user, data, tags, emojis, mentionedUsers); setImmediate('post created', { signal: this.#shutdownController.signal }).then( @@ -817,7 +827,7 @@ export class NoteCreateService implements OnApplicationShutdown { const mentions = extractMentions(tokens); let mentionedUsers = (await Promise.all(mentions.map(m => this.remoteUserResolveService.resolveUser(m.username, m.host ?? user.host).catch(() => null), - ))).filter(x => x != null) as MiUser[]; + ))).filter(isNotNull); // Drop duplicate users mentionedUsers = mentionedUsers.filter((u, i, self) => @@ -991,6 +1001,23 @@ export class NoteCreateService implements OnApplicationShutdown { } } + public async checkProhibitedWordsContain(content: Parameters<UtilityService['concatNoteContentsForKeyWordCheck']>[0], prohibitedWords?: string[]) { + if (prohibitedWords == null) { + prohibitedWords = (await this.metaService.fetch()).prohibitedWords; + } + + if ( + this.utilityService.isKeyWordIncluded( + this.utilityService.concatNoteContentsForKeyWordCheck(content), + prohibitedWords, + ) + ) { + return true; + } + + return false; + } + @bindThis public dispose(): void { this.#shutdownController.abort(); diff --git a/packages/backend/src/core/NoteReadService.ts b/packages/backend/src/core/NoteReadService.ts index feef024602..181c9f7649 100644 --- a/packages/backend/src/core/NoteReadService.ts +++ b/packages/backend/src/core/NoteReadService.ts @@ -88,46 +88,47 @@ export class NoteReadService implements OnApplicationShutdown { userId: MiUser['id'], notes: (MiNote | Packed<'Note'>)[], ): Promise<void> { - const readMentions: (MiNote | Packed<'Note'>)[] = []; - const readSpecifiedNotes: (MiNote | Packed<'Note'>)[] = []; + if (notes.length === 0) return; + + const noteIds = new Set<MiNote['id']>(); for (const note of notes) { if (note.mentions && note.mentions.includes(userId)) { - readMentions.push(note); + noteIds.add(note.id); } else if (note.visibleUserIds && note.visibleUserIds.includes(userId)) { - readSpecifiedNotes.push(note); + noteIds.add(note.id); } } - if ((readMentions.length > 0) || (readSpecifiedNotes.length > 0)) { - // Remove the record - await this.noteUnreadsRepository.delete({ - userId: userId, - noteId: In([...readMentions.map(n => n.id), ...readSpecifiedNotes.map(n => n.id)]), - }); + if (noteIds.size === 0) return; - // TODO: ↓ã¾ã¨ã‚ã¦ã‚¯ã‚¨ãƒªã—ãŸã„ + // Remove the record + await this.noteUnreadsRepository.delete({ + userId: userId, + noteId: In(Array.from(noteIds)), + }); - trackPromise(this.noteUnreadsRepository.countBy({ - userId: userId, - isMentioned: true, - }).then(mentionsCount => { - if (mentionsCount === 0) { - // å…¨ã¦æ—¢èªã«ãªã£ãŸã‚¤ãƒ™ãƒ³ãƒˆã‚’発行 - this.globalEventService.publishMainStream(userId, 'readAllUnreadMentions'); - } - })); + // TODO: ↓ã¾ã¨ã‚ã¦ã‚¯ã‚¨ãƒªã—ãŸã„ - trackPromise(this.noteUnreadsRepository.countBy({ - userId: userId, - isSpecified: true, - }).then(specifiedCount => { - if (specifiedCount === 0) { - // å…¨ã¦æ—¢èªã«ãªã£ãŸã‚¤ãƒ™ãƒ³ãƒˆã‚’発行 - this.globalEventService.publishMainStream(userId, 'readAllUnreadSpecifiedNotes'); - } - })); - } + trackPromise(this.noteUnreadsRepository.countBy({ + userId: userId, + isMentioned: true, + }).then(mentionsCount => { + if (mentionsCount === 0) { + // å…¨ã¦æ—¢èªã«ãªã£ãŸã‚¤ãƒ™ãƒ³ãƒˆã‚’発行 + this.globalEventService.publishMainStream(userId, 'readAllUnreadMentions'); + } + })); + + trackPromise(this.noteUnreadsRepository.countBy({ + userId: userId, + isSpecified: true, + }).then(specifiedCount => { + if (specifiedCount === 0) { + // å…¨ã¦æ—¢èªã«ãªã£ãŸã‚¤ãƒ™ãƒ³ãƒˆã‚’発行 + this.globalEventService.publishMainStream(userId, 'readAllUnreadSpecifiedNotes'); + } + })); } @bindThis diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index ee16193579..68ad92f396 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -126,6 +126,14 @@ export class NotificationService implements OnApplicationShutdown { this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => Object.hasOwn(followings, notifierId)), this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => Object.hasOwn(followings, notifieeId)), ]); + if (!(isFollowing && isFollower)) { + return null; + } + } else if (recieveConfig?.type === 'followingOrFollower') { + const [isFollowing, isFollower] = await Promise.all([ + this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => Object.hasOwn(followings, notifierId)), + this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => Object.hasOwn(followings, notifieeId)), + ]); if (!isFollowing && !isFollower) { return null; } @@ -155,6 +163,8 @@ export class NotificationService implements OnApplicationShutdown { const packed = await this.notificationEntityService.pack(notification, notifieeId, {}); + if (packed == null) return null; + // Publish notification event this.globalEventService.publishMainStream(notifieeId, 'notification', packed); @@ -205,6 +215,15 @@ export class NotificationService implements OnApplicationShutdown { } @bindThis + public async flushAllNotifications(userId: MiUser['id']) { + await Promise.all([ + this.redisClient.del(`notificationTimeline:${userId}`), + this.redisClient.del(`latestReadNotification:${userId}`), + ]); + this.globalEventService.publishMainStream(userId, 'notificationFlushed'); + } + + @bindThis public dispose(): void { this.#shutdownController.abort(); } diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts index e630539fbc..3b706d9854 100644 --- a/packages/backend/src/core/PushNotificationService.ts +++ b/packages/backend/src/core/PushNotificationService.ts @@ -115,6 +115,8 @@ export class PushNotificationService implements OnApplicationShutdown { endpoint: subscription.endpoint, auth: subscription.auth, publickey: subscription.publickey, + }).then(() => { + this.refreshCache(userId); }); } }); @@ -122,6 +124,11 @@ export class PushNotificationService implements OnApplicationShutdown { } @bindThis + public refreshCache(userId: string): void { + this.subscriptionsCache.refresh(userId); + } + + @bindThis public dispose(): void { this.subscriptionsCache.dispose(); } diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index 5014156a5c..cb0b079df0 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -322,35 +322,36 @@ export class ReactionService { //#endregion } + /** + * æ–‡å—列タイプã®ãƒ¬ã‚¬ã‚·ãƒ¼ãªå½¢å¼ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’ç¾åœ¨ã®å½¢å¼ã«å¤‰æ›ã—ã¤ã¤ã€ + * データベース上ã«ã¯å˜åœ¨ã™ã‚‹ã€Œ0個ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ãŒã¤ã„ã¦ã„ã‚‹ã€ã¨ã„ã†æƒ…å ±ã‚’å‰Šé™¤ã™ã‚‹ã€‚ + */ @bindThis - public convertLegacyReactions(reactions: Record<string, number>) { - const _reactions = {} as Record<string, number>; + public convertLegacyReactions(reactions: MiNote['reactions']): MiNote['reactions'] { + return Object.entries(reactions) + .filter(([, count]) => { + // `ReactionService.prototype.delete`ã§ã¯ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³å‰Šé™¤æ™‚ã«ã€ + // `MiNote['reactions']`ã®ã‚¨ãƒ³ãƒˆãƒªã®å€¤ã‚’デクリメントã—ã¦ã„ã‚‹ãŒã€ + // デクリメントã—ã¦ã„ã‚‹ã ã‘ãªã®ã§ã‚¨ãƒ³ãƒˆãƒªè‡ªä½“ã¯0を値ã¨ã—ã¦æŒã¤å½¢ã§æ®‹ã‚Šç¶šã‘る。 + // ãã®ãŸã‚ã€ã“ã®å‡¦ç†ãŒãªã‘れã°ã€ã€Œ0個ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ãŒã¤ã„ã¦ã„ã‚‹ã€ã¨ã„ã†ã“ã¨ã«ãªã£ã¦ã—ã¾ã†ã€‚ + return count > 0; + }) + .map(([reaction, count]) => { + // unchecked indexed access + const convertedReaction = legacies[reaction] as string | undefined; - for (const reaction of Object.keys(reactions)) { - if (reactions[reaction] <= 0) continue; + const key = this.decodeReaction(convertedReaction ?? reaction).reaction; - if (Object.keys(legacies).includes(reaction)) { - if (_reactions[legacies[reaction]]) { - _reactions[legacies[reaction]] += reactions[reaction]; - } else { - _reactions[legacies[reaction]] = reactions[reaction]; - } - } else { - if (_reactions[reaction]) { - _reactions[reaction] += reactions[reaction]; - } else { - _reactions[reaction] = reactions[reaction]; - } - } - } - - const _reactions2 = {} as Record<string, number>; + return [key, count] as const; + }) + .reduce<MiNote['reactions']>((acc, [key, count]) => { + // unchecked indexed access + const prevCount = acc[key] as number | undefined; - for (const reaction of Object.keys(_reactions)) { - _reactions2[this.decodeReaction(reaction).reaction] = _reactions[reaction]; - } + acc[key] = (prevCount ?? 0) + count; - return _reactions2; + return acc; + }, {}); } @bindThis diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index c5baaf3fff..09f3097114 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -35,6 +35,7 @@ export type RolePolicies = { gtlAvailable: boolean; ltlAvailable: boolean; canPublicNote: boolean; + mentionLimit: number; canInvite: boolean; inviteLimit: number; inviteLimitCycle: number; @@ -62,6 +63,7 @@ export const DEFAULT_POLICIES: RolePolicies = { gtlAvailable: true, ltlAvailable: true, canPublicNote: true, + mentionLimit: 20, canInvite: false, inviteLimit: 0, inviteLimitCycle: 60 * 24 * 7, @@ -200,17 +202,20 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { } @bindThis - private evalCond(user: MiUser, value: RoleCondFormulaValue): boolean { + private evalCond(user: MiUser, roles: MiRole[], value: RoleCondFormulaValue): boolean { try { switch (value.type) { case 'and': { - return value.values.every(v => this.evalCond(user, v)); + return value.values.every(v => this.evalCond(user, roles, v)); } case 'or': { - return value.values.some(v => this.evalCond(user, v)); + return value.values.some(v => this.evalCond(user, roles, v)); } case 'not': { - return !this.evalCond(user, value.value); + return !this.evalCond(user, roles, value.value); + } + case 'roleAssignedTo': { + return roles.some(r => r.id === value.roleId); } case 'isLocal': { return this.userEntityService.isLocalUser(user); @@ -272,7 +277,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { const assigns = await this.getUserAssigns(userId); const assignedRoles = roles.filter(r => assigns.map(x => x.roleId).includes(r.id)); const user = roles.some(r => r.target === 'conditional') ? await this.cacheService.findUserById(userId) : null; - const matchedCondRoles = roles.filter(r => r.target === 'conditional' && this.evalCond(user!, r.condFormula)); + const matchedCondRoles = roles.filter(r => r.target === 'conditional' && this.evalCond(user!, assignedRoles, r.condFormula)); return [...assignedRoles, ...matchedCondRoles]; } @@ -285,13 +290,13 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId })); // 期é™åˆ‡ã‚Œã®ãƒãƒ¼ãƒ«ã‚’除外 assigns = assigns.filter(a => a.expiresAt == null || (a.expiresAt.getTime() > now)); - const assignedRoleIds = assigns.map(x => x.roleId); const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({})); - const assignedBadgeRoles = roles.filter(r => r.asBadge && assignedRoleIds.includes(r.id)); + const assignedRoles = roles.filter(r => assigns.map(x => x.roleId).includes(r.id)); + const assignedBadgeRoles = assignedRoles.filter(r => r.asBadge); const badgeCondRoles = roles.filter(r => r.asBadge && (r.target === 'conditional')); if (badgeCondRoles.length > 0) { const user = roles.some(r => r.target === 'conditional') ? await this.cacheService.findUserById(userId) : null; - const matchedBadgeCondRoles = badgeCondRoles.filter(r => this.evalCond(user!, r.condFormula)); + const matchedBadgeCondRoles = badgeCondRoles.filter(r => this.evalCond(user!, assignedRoles, r.condFormula)); return [...assignedBadgeRoles, ...matchedBadgeCondRoles]; } else { return assignedBadgeRoles; @@ -325,6 +330,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { gtlAvailable: calc('gtlAvailable', vs => vs.some(v => v === true)), ltlAvailable: calc('ltlAvailable', vs => vs.some(v => v === true)), canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)), + mentionLimit: calc('mentionLimit', vs => Math.max(...vs)), canInvite: calc('canInvite', vs => vs.some(v => v === true)), inviteLimit: calc('inviteLimit', vs => Math.max(...vs)), inviteLimitCycle: calc('inviteLimitCycle', vs => Math.max(...vs)), diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index 8ad85391c6..0a492c06e4 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -30,6 +30,7 @@ import type { Config } from '@/config.js'; import { AccountMoveService } from '@/core/AccountMoveService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; +import type { ThinUser } from '@/queue/types.js'; import Logger from '../logger.js'; const logger = new Logger('following/create'); @@ -95,20 +96,34 @@ export class UserFollowingService implements OnModuleInit { } @bindThis + public async deliverAccept(follower: MiRemoteUser, followee: MiPartialLocalUser, requestId?: string) { + const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee)); + this.queueService.deliver(followee, content, follower.inbox, false); + } + + @bindThis public async follow( - _follower: { id: MiUser['id'] }, - _followee: { id: MiUser['id'] }, + _follower: ThinUser, + _followee: ThinUser, { requestId, silent = false, withReplies }: { requestId?: string, silent?: boolean, withReplies?: boolean, } = {}, ): Promise<void> { + /** + * å¿…ãšæœ€æ–°ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼æƒ…å ±ã‚’å–å¾—ã™ã‚‹ + */ const [follower, followee] = await Promise.all([ this.usersRepository.findOneByOrFail({ id: _follower.id }), this.usersRepository.findOneByOrFail({ id: _followee.id }), ]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser]; + if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isRemoteUser(followee)) { + // What? + throw new Error('Remote user cannot follow remote user.'); + } + // check blocking const [blocking, blocked] = await Promise.all([ this.userBlockingService.checkBlocked(follower.id, followee.id), @@ -129,6 +144,24 @@ export class UserFollowingService implements OnModuleInit { if (blocked) throw new IdentifiableError('3338392a-f764-498d-8855-db939dcf8c48', 'blocked'); } + if (await this.followingsRepository.exists({ + where: { + followerId: follower.id, + followeeId: followee.id, + }, + })) { + // ã™ã§ã«ãƒ•ã‚©ãƒãƒ¼é–¢ä¿‚ãŒå˜åœ¨ã—ã¦ã„ã‚‹å ´åˆ + if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { + // リモート → ãƒãƒ¼ã‚«ãƒ«: acceptã‚’é€ã‚Šè¿”ã—ã¦ãŠã—ã¾ã„ + this.deliverAccept(follower, followee, requestId); + return; + } + if (this.userEntityService.isLocalUser(follower)) { + // ãƒãƒ¼ã‚«ãƒ« → リモート/ãƒãƒ¼ã‚«ãƒ«: 例外 + throw new IdentifiableError('ec3f65c0-a9d1-47d9-8791-b2e7b9dcdced', 'already following'); + } + } + const followeeProfile = await this.userProfilesRepository.findOneByOrFail({ userId: followee.id }); // フォãƒãƒ¼å¯¾è±¡ãŒéµã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã‚ã‚‹ or // フォãƒãƒ¯ãƒ¼ãŒBotã§ã‚りã€ãƒ•ã‚©ãƒãƒ¼å¯¾è±¡ãŒBotã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒ¼ã«æ…Žé‡ã§ã‚ã‚‹ or @@ -189,8 +222,7 @@ export class UserFollowingService implements OnModuleInit { await this.insertFollowingDoc(followee, follower, silent, withReplies); if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { - const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee)); - this.queueService.deliver(followee, content, follower.inbox, false); + this.deliverAccept(follower, followee, requestId); } } @@ -571,8 +603,7 @@ export class UserFollowingService implements OnModuleInit { await this.insertFollowingDoc(followee, follower, false, request.withReplies); if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { - const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee as MiPartialLocalUser, request.requestId!), followee)); - this.queueService.deliver(followee, content, follower.inbox, false); + this.deliverAccept(follower, followee as MiPartialLocalUser, request.requestId ?? undefined); } this.userEntityService.pack(followee.id, followee, { diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts index 638a0c019e..652e8f7449 100644 --- a/packages/backend/src/core/UtilityService.ts +++ b/packages/backend/src/core/UtilityService.ts @@ -43,6 +43,20 @@ export class UtilityService { } @bindThis + public concatNoteContentsForKeyWordCheck(content: { + cw?: string | null; + text?: string | null; + pollChoices?: string[] | null; + others?: string[] | null; + }): string { + /** + * ノートã®å†…容をçµåˆã—ã¦ã‚ーワードãƒã‚§ãƒƒã‚¯ç”¨ã®æ–‡å—列を生æˆã™ã‚‹ + * cwã¨textã¯å†…容ãŒç¹‹ãŒã£ã¦ã„ã‚‹ã‹ã‚‚ã—れãªã„ã®ã§é–“ã«ä½•も入れãšã«ãƒã‚§ãƒƒã‚¯ã™ã‚‹ + */ + return `${content.cw ?? ''}${content.text ?? ''}\n${(content.pollChoices ?? []).join('\n')}\n${(content.others ?? []).join('\n')}`; + } + + @bindThis public isKeyWordIncluded(text: string, keyWords: string[]): boolean { if (keyWords.length === 0) return false; if (text === '') return false; diff --git a/packages/backend/src/core/WebAuthnService.ts b/packages/backend/src/core/WebAuthnService.ts index 4d11865906..42fbed2110 100644 --- a/packages/backend/src/core/WebAuthnService.ts +++ b/packages/backend/src/core/WebAuthnService.ts @@ -191,7 +191,7 @@ export class WebAuthnService { if (cert[0] === 0x04) { // å‰ã®å®Ÿè£…ã§ã¯ã„ã¤ã‚‚ 0x04 ã§å§‹ã¾ã£ã¦ã„㟠const halfLength = (cert.length - 1) / 2; - const cborMap = new Map<number, number | ArrayBufferLike>(); + const cborMap = new Map<number, number | Uint8Array>(); cborMap.set(1, 2); // kty, EC2 cborMap.set(3, -7); // alg, ES256 cborMap.set(-1, 1); // crv, P256 diff --git a/packages/backend/src/core/activitypub/ApAudienceService.ts b/packages/backend/src/core/activitypub/ApAudienceService.ts index d47be79441..0fccc7b950 100644 --- a/packages/backend/src/core/activitypub/ApAudienceService.ts +++ b/packages/backend/src/core/activitypub/ApAudienceService.ts @@ -8,6 +8,7 @@ import promiseLimit from 'promise-limit'; import type { MiRemoteUser, MiUser } from '@/models/User.js'; import { concat, unique } from '@/misc/prelude/array.js'; import { bindThis } from '@/decorators.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { getApIds } from './type.js'; import { ApPersonService } from './models/ApPersonService.js'; import type { ApObject } from './type.js'; @@ -40,7 +41,7 @@ export class ApAudienceService { const limit = promiseLimit<MiUser | null>(2); const mentionedUsers = (await Promise.all( others.map(id => limit(() => this.apPersonService.resolvePerson(id, resolver).catch(() => null))), - )).filter((x): x is MiUser => x != null); + )).filter(isNotNull); if (toGroups.public.length > 0) { return { diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts index 1cc54b6ff6..1621c41bcc 100644 --- a/packages/backend/src/core/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -27,6 +27,7 @@ import { QueueService } from '@/core/QueueService.js'; import type { UsersRepository, NotesRepository, FollowingsRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import type { MiRemoteUser } from '@/models/User.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { getApHrefNullable, getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; import { ApNoteService } from './models/ApNoteService.js'; import { ApLoggerService } from './ApLoggerService.js'; @@ -35,7 +36,6 @@ import { ApResolverService } from './ApResolverService.js'; import { ApAudienceService } from './ApAudienceService.js'; import { ApPersonService } from './models/ApPersonService.js'; import { ApQuestionService } from './models/ApQuestionService.js'; -import { CacheService } from '@/core/CacheService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { Resolver } from './ApResolverService.js'; import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IReject, IRemove, IUndo, IUpdate, IMove } from './type.js'; @@ -84,7 +84,6 @@ export class ApInboxService { private apPersonService: ApPersonService, private apQuestionService: ApQuestionService, private queueService: QueueService, - private cacheService: CacheService, private globalEventService: GlobalEventService, ) { this.logger = this.apLoggerService.logger; @@ -521,7 +520,7 @@ export class ApInboxService { const userIds = uris .filter(uri => uri.startsWith(this.config.url + '/users/')) .map(uri => uri.split('/').at(-1)) - .filter((userId): userId is string => userId !== undefined); + .filter(isNotNull); const users = await this.usersRepository.findBy({ id: In(userIds), }); diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index 494622909a..d7fb977a99 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -315,7 +315,7 @@ export class ApRendererService { const getPromisedFiles = async (ids: string[]): Promise<MiDriveFile[]> => { if (ids.length === 0) return []; const items = await this.driveFilesRepository.findBy({ id: In(ids) }); - return ids.map(id => items.find(item => item.id === id)).filter((item): item is MiDriveFile => item != null); + return ids.map(id => items.find(item => item.id === id)).filter(isNotNull); }; let inReplyTo; diff --git a/packages/backend/src/core/activitypub/models/ApMentionService.ts b/packages/backend/src/core/activitypub/models/ApMentionService.ts index 73eea1edf0..0ced7e88af 100644 --- a/packages/backend/src/core/activitypub/models/ApMentionService.ts +++ b/packages/backend/src/core/activitypub/models/ApMentionService.ts @@ -8,6 +8,7 @@ import promiseLimit from 'promise-limit'; import type { MiUser } from '@/models/_.js'; import { toArray, unique } from '@/misc/prelude/array.js'; import { bindThis } from '@/decorators.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { isMention } from '../type.js'; import { Resolver } from '../ApResolverService.js'; import { ApPersonService } from './ApPersonService.js'; @@ -27,7 +28,7 @@ export class ApMentionService { const limit = promiseLimit<MiUser | null>(2); const mentionedUsers = (await Promise.all( hrefs.map(x => limit(() => this.apPersonService.resolvePerson(x, resolver).catch(() => null))), - )).filter((x): x is MiUser => x != null); + )).filter(isNotNull); return mentionedUsers; } diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 8da9407216..b2fd435f93 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -24,6 +24,8 @@ import { StatusError } from '@/misc/status-error.js'; import { UtilityService } from '@/core/UtilityService.js'; import { bindThis } from '@/decorators.js'; import { checkHttps } from '@/misc/check-https.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js'; import { ApLoggerService } from '../ApLoggerService.js'; import { ApMfmService } from '../ApMfmService.js'; @@ -151,11 +153,47 @@ export class ApNoteService { throw new Error('invalid note.attributedTo: ' + note.attributedTo); } - const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as MiRemoteUser; + const uri = getOneApId(note.attributedTo); - // 投稿者ãŒå‡çµã•れã¦ã„ãŸã‚‰ã‚¹ã‚ップ + // ãƒãƒ¼ã‚«ãƒ«ã§æŠ•稿者を検索ã—ã€ã‚‚ã—å‡çµã•れã¦ã„ãŸã‚‰ã‚¹ã‚ップ + const cachedActor = await this.apPersonService.fetchPerson(uri) as MiRemoteUser; + if (cachedActor && cachedActor.isSuspended) { + throw new IdentifiableError('85ab9bd7-3a41-4530-959d-f07073900109', 'actor has been suspended'); + } + + const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver); + const apHashtags = extractApHashtags(note.tag); + + const cw = note.summary === '' ? null : note.summary; + + // テã‚ストã®ãƒ‘ース + let text: string | null = null; + if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') { + text = note.source.content; + } else if (typeof note._misskey_content !== 'undefined') { + text = note._misskey_content; + } else if (typeof note.content === 'string') { + text = this.apMfmService.htmlToMfm(note.content, note.tag); + } + + const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined); + + //#region Contents Check + // 添付ファイルã¨ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’ã“ã®ã‚µãƒ¼ãƒãƒ¼ã§ç™»éŒ²ã™ã‚‹å‰ã«å†…容をãƒã‚§ãƒƒã‚¯ã™ã‚‹ + /** + * ç¦æ¢ãƒ¯ãƒ¼ãƒ‰ãƒã‚§ãƒƒã‚¯ + */ + const hasProhibitedWords = await this.noteCreateService.checkProhibitedWordsContain({ cw, text, pollChoices: poll?.choices }); + if (hasProhibitedWords) { + throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words'); + } + //#endregion + + const actor = cachedActor ?? await this.apPersonService.resolvePerson(uri, resolver) as MiRemoteUser; + + // 解決ã—ãŸæŠ•ç¨¿è€…ãŒå‡çµã•れã¦ã„ãŸã‚‰ã‚¹ã‚ップ if (actor.isSuspended) { - throw new Error('actor has been suspended'); + throw new IdentifiableError('85ab9bd7-3a41-4530-959d-f07073900109', 'actor has been suspended'); } const noteAudience = await this.apAudienceService.parseAudience(actor, note.to, note.cc, resolver); @@ -170,9 +208,6 @@ export class ApNoteService { } } - const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver); - const apHashtags = extractApHashtags(note.tag); - // 添付ファイル // TODO: attachmentã¯å¿…ãšã—ã‚‚Imageã§ã¯ãªã„ // TODO: attachmentã¯å¿…ãšã—ã‚‚é…列ã§ã¯ãªã„ @@ -221,7 +256,7 @@ export class ApNoteService { } }; - const uris = unique([note._misskey_quote, note.quoteUrl].filter((x): x is string => typeof x === 'string')); + const uris = unique([note._misskey_quote, note.quoteUrl].filter(isNotNull)); const results = await Promise.all(uris.map(tryResolveNote)); quote = results.filter((x): x is { status: 'ok', res: MiNote } => x.status === 'ok').map(x => x.res).at(0); @@ -232,18 +267,6 @@ export class ApNoteService { } } - const cw = note.summary === '' ? null : note.summary; - - // テã‚ストã®ãƒ‘ース - let text: string | null = null; - if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') { - text = note.source.content; - } else if (typeof note._misskey_content !== 'undefined') { - text = note._misskey_content; - } else if (typeof note.content === 'string') { - text = this.apMfmService.htmlToMfm(note.content, note.tag); - } - // vote if (reply && reply.hasPoll) { const poll = await this.pollsRepository.findOneByOrFail({ noteId: reply.id }); @@ -273,8 +296,6 @@ export class ApNoteService { const apEmojis = emojis.map(emoji => emoji.name); - const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined); - try { return await this.noteCreateService.create(actor, { createdAt: note.published ? new Date(note.published) : null, diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index e80cd34a56..744b1ea683 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -38,6 +38,7 @@ import { MetaService } from '@/core/MetaService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import type { AccountMoveService } from '@/core/AccountMoveService.js'; import { checkHttps } from '@/misc/check-https.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js'; import { extractApHashtags } from './tag.js'; import type { OnModuleInit } from '@nestjs/common'; @@ -636,7 +637,7 @@ export class ApPersonService implements OnModuleInit { // ã¨ã‚Šã‚ãˆãšidã‚’åˆ¥ã®æ™‚é–“ã§ç”Ÿæˆã—ã¦é †ç•ªã‚’ç¶æŒ let td = 0; - for (const note of featuredNotes.filter((note): note is MiNote => note != null)) { + for (const note of featuredNotes.filter(isNotNull)) { td -= 1000; transactionalEntityManager.insert(MiUserNotePining, { id: this.idService.gen(Date.now() + td), diff --git a/packages/backend/src/core/activitypub/models/ApQuestionService.ts b/packages/backend/src/core/activitypub/models/ApQuestionService.ts index e78b3a3599..d1936cfe1d 100644 --- a/packages/backend/src/core/activitypub/models/ApQuestionService.ts +++ b/packages/backend/src/core/activitypub/models/ApQuestionService.ts @@ -10,6 +10,7 @@ import type { Config } from '@/config.js'; import type { IPoll } from '@/models/Poll.js'; import type Logger from '@/logger.js'; import { bindThis } from '@/decorators.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { isQuestion } from '../type.js'; import { ApLoggerService } from '../ApLoggerService.js'; import { ApResolverService } from '../ApResolverService.js'; @@ -51,7 +52,7 @@ export class ApQuestionService { const choices = question[multiple ? 'anyOf' : 'oneOf'] ?.map((x) => x.name) - .filter((x): x is string => typeof x === 'string') + .filter(isNotNull) ?? []; const votes = question[multiple ? 'anyOf' : 'oneOf']?.map((x) => x.replies?.totalItems ?? x._misskey_votes ?? 0); diff --git a/packages/backend/src/core/activitypub/models/tag.ts b/packages/backend/src/core/activitypub/models/tag.ts index ced101b764..e7ceec3262 100644 --- a/packages/backend/src/core/activitypub/models/tag.ts +++ b/packages/backend/src/core/activitypub/models/tag.ts @@ -4,6 +4,7 @@ */ import { toArray } from '@/misc/prelude/array.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { isHashtag } from '../type.js'; import type { IObject, IApHashtag } from '../type.js'; @@ -15,7 +16,7 @@ export function extractApHashtags(tags: IObject | IObject[] | null | undefined): return hashtags.map(tag => { const m = tag.name.match(/^#(.+)/); return m ? m[1] : null; - }).filter((x): x is string => x != null); + }).filter(isNotNull); } export function extractApHashtagObjects(tags: IObject | IObject[] | null | undefined): IApHashtag[] { diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index 50f1c49b48..8affe2b3bf 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -259,7 +259,7 @@ export class DriveFileEntityService { options?: PackOptions, ): Promise<Packed<'DriveFile'>[]> { const items = await Promise.all(files.map(f => this.packNullable(f, options))); - return items.filter((x): x is Packed<'DriveFile'> => x != null); + return items.filter(isNotNull); } @bindThis diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts index 9287c98003..e46bd8b963 100644 --- a/packages/backend/src/core/entities/InstanceEntityService.ts +++ b/packages/backend/src/core/entities/InstanceEntityService.ts @@ -8,12 +8,15 @@ import type { Packed } from '@/misc/json-schema.js'; import type { MiInstance } from '@/models/Instance.js'; import { MetaService } from '@/core/MetaService.js'; import { bindThis } from '@/decorators.js'; -import { UtilityService } from '../UtilityService.js'; +import { UtilityService } from '@/core/UtilityService.js'; +import { RoleService } from '@/core/RoleService.js'; +import { MiUser } from '@/models/User.js'; @Injectable() export class InstanceEntityService { constructor( private metaService: MetaService, + private roleService: RoleService, private utilityService: UtilityService, ) { @@ -22,8 +25,11 @@ export class InstanceEntityService { @bindThis public async pack( instance: MiInstance, + me?: { id: MiUser['id']; } | null | undefined, ): Promise<Packed<'FederationInstance'>> { const meta = await this.metaService.fetch(); + const iAmModerator = me ? await this.roleService.isModerator(me as MiUser) : false; + return { id: instance.id, firstRetrievedAt: instance.firstRetrievedAt.toISOString(), @@ -48,6 +54,7 @@ export class InstanceEntityService { themeColor: instance.themeColor, infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null, latestRequestReceivedAt: instance.latestRequestReceivedAt ? instance.latestRequestReceivedAt.toISOString() : null, + moderationNote: iAmModerator ? instance.moderationNote : null, }; } diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts new file mode 100644 index 0000000000..b50d76288f --- /dev/null +++ b/packages/backend/src/core/entities/MetaEntityService.ts @@ -0,0 +1,154 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Brackets } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; +import JSON5 from 'json5'; +import type { Packed } from '@/misc/json-schema.js'; +import type { MiMeta } from '@/models/Meta.js'; +import type { AdsRepository } from '@/models/_.js'; +import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; +import { MetaService } from '@/core/MetaService.js'; +import { bindThis } from '@/decorators.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { InstanceActorService } from '@/core/InstanceActorService.js'; +import type { Config } from '@/config.js'; +import { DI } from '@/di-symbols.js'; +import { DEFAULT_POLICIES } from '@/core/RoleService.js'; + +@Injectable() +export class MetaEntityService { + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.adsRepository) + private adsRepository: AdsRepository, + + private userEntityService: UserEntityService, + private metaService: MetaService, + private instanceActorService: InstanceActorService, + ) { } + + @bindThis + public async pack(meta?: MiMeta): Promise<Packed<'MetaLite'>> { + let instance = meta; + + if (!instance) { + instance = await this.metaService.fetch(); + } + + const ads = await this.adsRepository.createQueryBuilder('ads') + .where('ads.expiresAt > :now', { now: new Date() }) + .andWhere('ads.startsAt <= :now', { now: new Date() }) + .andWhere(new Brackets(qb => { + // 曜日ã®ãƒ“ットフラグを確èªã™ã‚‹ + qb.where('ads.dayOfWeek & :dayOfWeek > 0', { dayOfWeek: 1 << new Date().getDay() }) + .orWhere('ads.dayOfWeek = 0'); + })) + .getMany(); + + const packed: Packed<'MetaLite'> = { + maintainerName: instance.maintainerName, + maintainerEmail: instance.maintainerEmail, + + version: this.config.version, + providesTarball: this.config.publishTarballInsteadOfProvideRepositoryUrl, + + name: instance.name, + shortName: instance.shortName, + uri: this.config.url, + description: instance.description, + langs: instance.langs, + tosUrl: instance.termsOfServiceUrl, + repositoryUrl: instance.repositoryUrl, + feedbackUrl: instance.feedbackUrl, + impressumUrl: instance.impressumUrl, + privacyPolicyUrl: instance.privacyPolicyUrl, + disableRegistration: instance.disableRegistration, + emailRequiredForSignup: instance.emailRequiredForSignup, + enableHcaptcha: instance.enableHcaptcha, + hcaptchaSiteKey: instance.hcaptchaSiteKey, + enableMcaptcha: instance.enableMcaptcha, + mcaptchaSiteKey: instance.mcaptchaSitekey, + mcaptchaInstanceUrl: instance.mcaptchaInstanceUrl, + enableRecaptcha: instance.enableRecaptcha, + recaptchaSiteKey: instance.recaptchaSiteKey, + enableTurnstile: instance.enableTurnstile, + turnstileSiteKey: instance.turnstileSiteKey, + swPublickey: instance.swPublicKey, + themeColor: instance.themeColor, + mascotImageUrl: instance.mascotImageUrl ?? '/assets/ai.png', + bannerUrl: instance.bannerUrl, + infoImageUrl: instance.infoImageUrl, + serverErrorImageUrl: instance.serverErrorImageUrl, + notFoundImageUrl: instance.notFoundImageUrl, + iconUrl: instance.iconUrl, + backgroundImageUrl: instance.backgroundImageUrl, + logoImageUrl: instance.logoImageUrl, + maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, + // ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆã®æ‰‹é–“を減らã™ãŸã‚ã‚らã‹ã˜ã‚JSONã«å¤‰æ›ã—ã¦ãŠã + defaultLightTheme: instance.defaultLightTheme ? JSON.stringify(JSON5.parse(instance.defaultLightTheme)) : null, + defaultDarkTheme: instance.defaultDarkTheme ? JSON.stringify(JSON5.parse(instance.defaultDarkTheme)) : null, + ads: ads.map(ad => ({ + id: ad.id, + url: ad.url, + place: ad.place, + ratio: ad.ratio, + imageUrl: ad.imageUrl, + dayOfWeek: ad.dayOfWeek, + })), + notesPerOneAd: instance.notesPerOneAd, + enableEmail: instance.enableEmail, + enableServiceWorker: instance.enableServiceWorker, + + translatorAvailable: instance.deeplAuthKey != null, + + serverRules: instance.serverRules, + + policies: { ...DEFAULT_POLICIES, ...instance.policies }, + + mediaProxy: this.config.mediaProxy, + }; + + return packed; + } + + @bindThis + public async packDetailed(meta?: MiMeta): Promise<Packed<'MetaDetailed'>> { + let instance = meta; + + if (!instance) { + instance = await this.metaService.fetch(); + } + + const packed = await this.pack(instance); + + const proxyAccount = instance.proxyAccountId ? await this.userEntityService.pack(instance.proxyAccountId).catch(() => null) : null; + + const packDetailed: Packed<'MetaDetailed'> = { + ...packed, + cacheRemoteFiles: instance.cacheRemoteFiles, + cacheRemoteSensitiveFiles: instance.cacheRemoteSensitiveFiles, + requireSetup: !await this.instanceActorService.realLocalUsersPresent(), + proxyAccountName: proxyAccount ? proxyAccount.username : null, + features: { + localTimeline: instance.policies.ltlAvailable, + globalTimeline: instance.policies.gtlAvailable, + registration: !instance.disableRegistration, + emailRequiredForSignup: instance.emailRequiredForSignup, + hcaptcha: instance.enableHcaptcha, + recaptcha: instance.enableRecaptcha, + turnstile: instance.enableTurnstile, + objectStorage: instance.useObjectStorage, + serviceWorker: instance.enableServiceWorker, + miauth: true, + }, + }; + + return packDetailed; + } +} + diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts index 2799f58992..3f4fa3cf96 100644 --- a/packages/backend/src/core/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts @@ -69,4 +69,19 @@ export class NoteReactionEntityService implements OnModuleInit { } : {}), }; } + + @bindThis + public async packMany( + reactions: MiNoteReaction[], + me?: { id: MiUser['id'] } | null | undefined, + options?: { + withNote: boolean; + }, + ): Promise<Packed<'NoteReaction'>[]> { + const opts = Object.assign({ + withNote: false, + }, options); + + return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts))); + } } diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index 0663898edb..94d56c883b 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -14,14 +14,14 @@ import type { MiNote } from '@/models/Note.js'; import type { Packed } from '@/misc/json-schema.js'; import { bindThis } from '@/decorators.js'; import { isNotNull } from '@/misc/is-not-null.js'; -import { FilterUnionByProperty, notificationTypes } from '@/types.js'; +import { FilterUnionByProperty, groupedNotificationTypes } from '@/types.js'; +import { CacheService } from '@/core/CacheService.js'; import { RoleEntityService } from './RoleEntityService.js'; import type { OnModuleInit } from '@nestjs/common'; import type { UserEntityService } from './UserEntityService.js'; import type { NoteEntityService } from './NoteEntityService.js'; -const NOTE_REQUIRED_NOTIFICATION_TYPES = new Set(['note', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded'] as (typeof notificationTypes[number])[]); -const NOTE_REQUIRED_GROUPED_NOTIFICATION_TYPES = new Set(['note', 'mention', 'reply', 'renote', 'renote:grouped', 'quote', 'reaction', 'reaction:grouped', 'pollEnded']); +const NOTE_REQUIRED_NOTIFICATION_TYPES = new Set(['note', 'mention', 'reply', 'renote', 'renote:grouped', 'quote', 'reaction', 'reaction:grouped', 'pollEnded'] as (typeof groupedNotificationTypes[number])[]); @Injectable() export class NotificationEntityService implements OnModuleInit { @@ -41,6 +41,8 @@ export class NotificationEntityService implements OnModuleInit { @Inject(DI.followRequestsRepository) private followRequestsRepository: FollowRequestsRepository, + private cacheService: CacheService, + //private userEntityService: UserEntityService, //private noteEntityService: NoteEntityService, ) { @@ -52,130 +54,48 @@ export class NotificationEntityService implements OnModuleInit { this.roleEntityService = this.moduleRef.get('RoleEntityService'); } - @bindThis - public async pack( - src: MiNotification, + /** + * 通知をパックã™ã‚‹å…±é€šå‡¦ç† + */ + async #packInternal <T extends MiNotification | MiGroupedNotification> ( + src: T, meId: MiUser['id'], // eslint-disable-next-line @typescript-eslint/ban-types options: { - + checkValidNotifier?: boolean; }, hint?: { packedNotes: Map<MiNote['id'], Packed<'Note'>>; packedUsers: Map<MiUser['id'], Packed<'UserLite'>>; }, - ): Promise<Packed<'Notification'>> { + ): Promise<Packed<'Notification'> | null> { const notification = src; - const noteIfNeed = NOTE_REQUIRED_NOTIFICATION_TYPES.has(notification.type) && 'noteId' in notification ? ( - hint?.packedNotes != null - ? hint.packedNotes.get(notification.noteId) - : this.noteEntityService.pack(notification.noteId, { id: meId }, { - detail: true, - }) - ) : undefined; - const userIfNeed = 'notifierId' in notification ? ( - hint?.packedUsers != null - ? hint.packedUsers.get(notification.notifierId) - : this.userEntityService.pack(notification.notifierId, { id: meId }) - ) : undefined; - const role = notification.type === 'roleAssigned' ? await this.roleEntityService.pack(notification.roleId) : undefined; - - return await awaitAll({ - id: notification.id, - createdAt: new Date(notification.createdAt).toISOString(), - type: notification.type, - userId: 'notifierId' in notification ? notification.notifierId : undefined, - ...(userIfNeed != null ? { user: userIfNeed } : {}), - ...(noteIfNeed != null ? { note: noteIfNeed } : {}), - ...(notification.type === 'reaction' ? { - reaction: notification.reaction, - } : {}), - ...(notification.type === 'roleAssigned' ? { - role: role, - } : {}), - ...(notification.type === 'achievementEarned' ? { - achievement: notification.achievement, - } : {}), - ...(notification.type === 'app' ? { - body: notification.customBody, - header: notification.customHeader, - icon: notification.customIcon, - } : {}), - }); - } - - @bindThis - public async packMany( - notifications: MiNotification[], - meId: MiUser['id'], - ) { - if (notifications.length === 0) return []; - - let validNotifications = notifications; - - const noteIds = validNotifications.map(x => 'noteId' in x ? x.noteId : null).filter(isNotNull); - const notes = noteIds.length > 0 ? await this.notesRepository.find({ - where: { id: In(noteIds) }, - relations: ['user', 'reply', 'reply.user', 'renote', 'renote.user'], - }) : []; - const packedNotesArray = await this.noteEntityService.packMany(notes, { id: meId }, { - detail: true, - }); - const packedNotes = new Map(packedNotesArray.map(p => [p.id, p])); - - validNotifications = validNotifications.filter(x => !('noteId' in x) || packedNotes.has(x.noteId)); - - const userIds = validNotifications.map(x => 'notifierId' in x ? x.notifierId : null).filter(isNotNull); - const users = userIds.length > 0 ? await this.usersRepository.find({ - where: { id: In(userIds) }, - }) : []; - const packedUsersArray = await this.userEntityService.packMany(users, { id: meId }); - const packedUsers = new Map(packedUsersArray.map(p => [p.id, p])); - - // æ—¢ã«è§£æ±ºã•れãŸãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã®é€šçŸ¥ã‚’除外 - const followRequestNotifications = validNotifications.filter((x): x is FilterUnionByProperty<MiGroupedNotification, 'type', 'receiveFollowRequest'> => x.type === 'receiveFollowRequest'); - if (followRequestNotifications.length > 0) { - const reqs = await this.followRequestsRepository.find({ - where: { followerId: In(followRequestNotifications.map(x => x.notifierId)) }, - }); - validNotifications = validNotifications.filter(x => (x.type !== 'receiveFollowRequest') || reqs.some(r => r.followerId === x.notifierId)); - } - return await Promise.all(validNotifications.map(x => this.pack(x, meId, {}, { - packedNotes, - packedUsers, - }))); - } + if (options.checkValidNotifier !== false && !(await this.#isValidNotifier(notification, meId))) return null; - @bindThis - public async packGrouped( - src: MiGroupedNotification, - meId: MiUser['id'], - // eslint-disable-next-line @typescript-eslint/ban-types - options: { - - }, - hint?: { - packedNotes: Map<MiNote['id'], Packed<'Note'>>; - packedUsers: Map<MiUser['id'], Packed<'UserLite'>>; - }, - ): Promise<Packed<'Notification'>> { - const notification = src; - const noteIfNeed = NOTE_REQUIRED_GROUPED_NOTIFICATION_TYPES.has(notification.type) && 'noteId' in notification ? ( + const needsNote = NOTE_REQUIRED_NOTIFICATION_TYPES.has(notification.type) && 'noteId' in notification; + const noteIfNeed = needsNote ? ( hint?.packedNotes != null ? hint.packedNotes.get(notification.noteId) : this.noteEntityService.pack(notification.noteId, { id: meId }, { detail: true, }) ) : undefined; - const userIfNeed = 'notifierId' in notification ? ( + // if the note has been deleted, don't show this notification + if (needsNote && !noteIfNeed) return null; + + const needsUser = 'notifierId' in notification; + const userIfNeed = needsUser ? ( hint?.packedUsers != null ? hint.packedUsers.get(notification.notifierId) : this.userEntityService.pack(notification.notifierId, { id: meId }) ) : undefined; + // if the user has been deleted, don't show this notification + if (needsUser && !userIfNeed) return null; + // #region Grouped notifications if (notification.type === 'reaction:grouped') { - const reactions = await Promise.all(notification.reactions.map(async reaction => { + const reactions = (await Promise.all(notification.reactions.map(async reaction => { const user = hint?.packedUsers != null ? hint.packedUsers.get(reaction.userId)! : await this.userEntityService.pack(reaction.userId, { id: meId }); @@ -183,7 +103,12 @@ export class NotificationEntityService implements OnModuleInit { user, reaction: reaction.reaction, }; - })); + }))).filter(r => isNotNull(r.user)); + // if all users have been deleted, don't show this notification + if (reactions.length === 0) { + return null; + } + return await awaitAll({ id: notification.id, createdAt: new Date(notification.createdAt).toISOString(), @@ -192,14 +117,19 @@ export class NotificationEntityService implements OnModuleInit { reactions, }); } else if (notification.type === 'renote:grouped') { - const users = await Promise.all(notification.userIds.map(userId => { + const users = (await Promise.all(notification.userIds.map(userId => { const packedUser = hint?.packedUsers != null ? hint.packedUsers.get(userId) : null; if (packedUser) { return packedUser; } return this.userEntityService.pack(userId, { id: meId }); - })); + }))).filter(isNotNull); + // if all users have been deleted, don't show this notification + if (users.length === 0) { + return null; + } + return await awaitAll({ id: notification.id, createdAt: new Date(notification.createdAt).toISOString(), @@ -208,8 +138,14 @@ export class NotificationEntityService implements OnModuleInit { users, }); } + // #endregion - const role = notification.type === 'roleAssigned' ? await this.roleEntityService.pack(notification.roleId) : undefined; + const needsRole = notification.type === 'roleAssigned'; + const role = needsRole ? await this.roleEntityService.pack(notification.roleId) : undefined; + // if the role has been deleted, don't show this notification + if (needsRole && !role) { + return null; + } return await awaitAll({ id: notification.id, @@ -235,15 +171,16 @@ export class NotificationEntityService implements OnModuleInit { }); } - @bindThis - public async packGroupedMany( - notifications: MiGroupedNotification[], + async #packManyInternal <T extends MiNotification | MiGroupedNotification> ( + notifications: T[], meId: MiUser['id'], - ) { + ): Promise<T[]> { if (notifications.length === 0) return []; let validNotifications = notifications; + validNotifications = await this.#filterValidNotifier(validNotifications, meId); + const noteIds = validNotifications.map(x => 'noteId' in x ? x.noteId : null).filter(isNotNull); const notes = noteIds.length > 0 ? await this.notesRepository.find({ where: { id: In(noteIds) }, @@ -269,7 +206,7 @@ export class NotificationEntityService implements OnModuleInit { const packedUsers = new Map(packedUsersArray.map(p => [p.id, p])); // æ—¢ã«è§£æ±ºã•れãŸãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã®é€šçŸ¥ã‚’除外 - const followRequestNotifications = validNotifications.filter((x): x is FilterUnionByProperty<MiGroupedNotification, 'type', 'receiveFollowRequest'> => x.type === 'receiveFollowRequest'); + const followRequestNotifications = validNotifications.filter((x): x is FilterUnionByProperty<T, 'type', 'receiveFollowRequest'> => x.type === 'receiveFollowRequest'); if (followRequestNotifications.length > 0) { const reqs = await this.followRequestsRepository.find({ where: { followerId: In(followRequestNotifications.map(x => x.notifierId)) }, @@ -277,9 +214,107 @@ export class NotificationEntityService implements OnModuleInit { validNotifications = validNotifications.filter(x => (x.type !== 'receiveFollowRequest') || reqs.some(r => r.followerId === x.notifierId)); } - return await Promise.all(validNotifications.map(x => this.packGrouped(x, meId, {}, { - packedNotes, - packedUsers, - }))); + const packPromises = validNotifications.map(x => { + return this.pack( + x, + meId, + { checkValidNotifier: false }, + { packedNotes, packedUsers }, + ); + }); + + return (await Promise.all(packPromises)).filter(isNotNull); + } + + @bindThis + public async pack( + src: MiNotification | MiGroupedNotification, + meId: MiUser['id'], + // eslint-disable-next-line @typescript-eslint/ban-types + options: { + checkValidNotifier?: boolean; + }, + hint?: { + packedNotes: Map<MiNote['id'], Packed<'Note'>>; + packedUsers: Map<MiUser['id'], Packed<'UserLite'>>; + }, + ): Promise<Packed<'Notification'> | null> { + return await this.#packInternal(src, meId, options, hint); + } + + @bindThis + public async packMany( + notifications: MiNotification[], + meId: MiUser['id'], + ): Promise<MiNotification[]> { + return await this.#packManyInternal(notifications, meId); + } + + @bindThis + public async packGroupedMany( + notifications: MiGroupedNotification[], + meId: MiUser['id'], + ): Promise<MiGroupedNotification[]> { + return await this.#packManyInternal(notifications, meId); + } + + /** + * notifierãŒå˜åœ¨ã™ã‚‹ã‹ã€ãƒŸãƒ¥ãƒ¼ãƒˆã•れã¦ã„ãªã„ã‹ã€ã‚µã‚¹ãƒšãƒ³ãƒ‰ã•れã¦ã„ãªã„ã‹ã‚’確èªã™ã‚‹validator + */ + #validateNotifier <T extends MiNotification | MiGroupedNotification> ( + notification: T, + userIdsWhoMeMuting: Set<MiUser['id']>, + userMutedInstances: Set<string>, + notifiers: MiUser[], + ): boolean { + if (!('notifierId' in notification)) return true; + if (userIdsWhoMeMuting.has(notification.notifierId)) return false; + + const notifier = notifiers.find(x => x.id === notification.notifierId) ?? null; + + if (notifier == null) return false; + if (notifier.host && userMutedInstances.has(notifier.host)) return false; + + if (notifier.isSuspended) return false; + + return true; + } + + /** + * notifierãŒå˜åœ¨ã™ã‚‹ã‹ã€ãƒŸãƒ¥ãƒ¼ãƒˆã•れã¦ã„ãªã„ã‹ã€ã‚µã‚¹ãƒšãƒ³ãƒ‰ã•れã¦ã„ãªã„ã‹ã‚’実際ã«ç¢ºèªã™ã‚‹ + */ + async #isValidNotifier( + notification: MiNotification | MiGroupedNotification, + meId: MiUser['id'], + ): Promise<boolean> { + return (await this.#filterValidNotifier([notification], meId)).length === 1; + } + + /** + * notifierãŒå˜åœ¨ã™ã‚‹ã‹ã€ãƒŸãƒ¥ãƒ¼ãƒˆã•れã¦ã„ãªã„ã‹ã€ã‚µã‚¹ãƒšãƒ³ãƒ‰ã•れã¦ã„ãªã„ã‹ã‚’実際ã«è¤‡æ•°ç¢ºèªã™ã‚‹ + */ + async #filterValidNotifier <T extends MiNotification | MiGroupedNotification> ( + notifications: T[], + meId: MiUser['id'], + ): Promise<T[]> { + const [ + userIdsWhoMeMuting, + userMutedInstances, + ] = await Promise.all([ + this.cacheService.userMutingsCache.fetch(meId), + this.cacheService.userProfileCache.fetch(meId).then(p => new Set(p.mutedInstances)), + ]); + + const notifierIds = notifications.map(notification => 'notifierId' in notification ? notification.notifierId : null).filter(isNotNull); + const notifiers = notifierIds.length > 0 ? await this.usersRepository.find({ + where: { id: In(notifierIds) }, + }) : []; + + const filteredNotifications = ((await Promise.all(notifications.map(async (notification) => { + const isValid = this.#validateNotifier(notification, userIdsWhoMeMuting, userMutedInstances, notifiers); + return isValid ? notification : null; + }))) as [T | null] ).filter(isNotNull); + + return filteredNotifications; } } diff --git a/packages/backend/src/core/entities/PageEntityService.ts b/packages/backend/src/core/entities/PageEntityService.ts index fe7b137bd2..65c69a49a7 100644 --- a/packages/backend/src/core/entities/PageEntityService.ts +++ b/packages/backend/src/core/entities/PageEntityService.ts @@ -14,6 +14,7 @@ import type { MiPage } from '@/models/Page.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; @@ -102,7 +103,7 @@ export class PageEntityService { script: page.script, eyeCatchingImageId: page.eyeCatchingImageId, eyeCatchingImage: page.eyeCatchingImageId ? await this.driveFileEntityService.pack(page.eyeCatchingImageId) : null, - attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter((x): x is MiDriveFile => x != null)), + attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter(isNotNull)), likedCount: page.likedCount, isLiked: meId ? await this.pageLikesRepository.exists({ where: { pageId: page.id, userId: meId } }) : undefined, }); diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 53df32f210..14761357a5 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -25,6 +25,7 @@ import { IdService } from '@/core/IdService.js'; import type { AnnouncementService } from '@/core/AnnouncementService.js'; import type { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { AvatarDecorationService } from '@/core/AvatarDecorationService.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import type { OnModuleInit } from '@nestjs/common'; import type { NoteEntityService } from './NoteEntityService.js'; import type { DriveFileEntityService } from './DriveFileEntityService.js'; @@ -384,7 +385,7 @@ export class UserEntityService implements OnModuleInit { movedTo: user.movedToUri ? this.apPersonService.resolvePerson(user.movedToUri).then(user => user.id).catch(() => null) : null, alsoKnownAs: user.alsoKnownAs ? Promise.all(user.alsoKnownAs.map(uri => this.apPersonService.fetchPerson(uri).then(user => user?.id).catch(() => null))) - .then(xs => xs.length === 0 ? null : xs.filter(x => x != null) as string[]) + .then(xs => xs.length === 0 ? null : xs.filter(isNotNull)) : null, createdAt: this.idService.parse(user.id).date.toISOString(), updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null, diff --git a/packages/backend/src/misc/FileWriterStream.ts b/packages/backend/src/misc/FileWriterStream.ts new file mode 100644 index 0000000000..367a8eb560 --- /dev/null +++ b/packages/backend/src/misc/FileWriterStream.ts @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as fs from 'node:fs/promises'; +import type { PathLike } from 'node:fs'; + +/** + * `fs.createWriteStream()`相当ã®ã“ã¨ã‚’行ã†`WritableStream` (Web標準) + */ +export class FileWriterStream extends WritableStream<Uint8Array> { + constructor(path: PathLike) { + let file: fs.FileHandle | null = null; + + super({ + start: async () => { + file = await fs.open(path, 'a'); + }, + write: async (chunk, controller) => { + if (file === null) { + controller.error(); + throw new Error(); + } + + await file.write(chunk); + }, + close: async () => { + await file?.close(); + }, + abort: async () => { + await file?.close(); + }, + }); + } +} diff --git a/packages/backend/src/misc/JsonArrayStream.ts b/packages/backend/src/misc/JsonArrayStream.ts new file mode 100644 index 0000000000..754938989d --- /dev/null +++ b/packages/backend/src/misc/JsonArrayStream.ts @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { TransformStream } from 'node:stream/web'; + +/** + * ã‚¹ãƒˆãƒªãƒ¼ãƒ ã«æµã‚Œã¦ããŸå„データã«ã¤ã„ã¦`JSON.stringify()`ã—ãŸä¸Šã§ã€ãれらを一ã¤ã®é…列ã«ã¾ã¨ã‚ã‚‹ + */ +export class JsonArrayStream extends TransformStream<unknown, string> { + constructor() { + /** 最åˆã®è¦ç´ ã‹ã©ã†ã‹ã‚’変数ã«è¨˜éŒ² */ + let isFirst = true; + + super({ + start(controller) { + controller.enqueue('['); + }, + flush(controller) { + controller.enqueue(']'); + }, + transform(chunk, controller) { + if (isFirst) { + isFirst = false; + } else { + // 妥当ãªJSONé…列ã«ã™ã‚‹ãŸã‚ã«ã¯æœ€åˆä»¥å¤–ã®è¦ç´ ã®å‰ã«`,`を挿入ã—ãªã‘れã°ãªã‚‰ãªã„ + controller.enqueue(',\n'); + } + + controller.enqueue(JSON.stringify(chunk)); + }, + }); + } +} diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index 7f4d1521b5..bba64a06ef 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -187,6 +187,10 @@ export class RedisSingleCache<T> { // TODO: メモリ節約ã®ãŸã‚ã‚ã¾ã‚Šå‚ç…§ã•れãªã„ã‚ーを定期的ã«å‰Šé™¤ã§ãるよã†ã«ã™ã‚‹ï¼Ÿ export class MemoryKVCache<T> { + /** + * データをæŒã¤ãƒžãƒƒãƒ— + * @deprecated ã“れを直接æ“作ã™ã‚‹ã¹ãã§ã¯ãªã„ + */ public cache: Map<string, { date: number; value: T; }>; private lifetime: number; private gcIntervalHandle: NodeJS.Timeout; @@ -201,6 +205,10 @@ export class MemoryKVCache<T> { } @bindThis + /** + * Mapã«ã‚ャッシュをセットã—ã¾ã™ + * @deprecated ã“れを直接呼ã³å‡ºã™ã¹ãã§ã¯ãªã„。InternalEventãªã©ã§å¤‰æ›´ã‚’å…¨ã¦ã®ãƒ—ãƒã‚»ã‚¹/マシンã«é€šçŸ¥ã™ã‚‹ã¹ã + */ public set(key: string, value: T): void { this.cache.set(key, { date: Date.now(), diff --git a/packages/backend/src/misc/is-not-null.ts b/packages/backend/src/misc/is-not-null.ts index 584a09d35a..8d9dc8bb39 100644 --- a/packages/backend/src/misc/is-not-null.ts +++ b/packages/backend/src/misc/is-not-null.ts @@ -3,8 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -// we are using {} as "any non-nullish value" as expected -// eslint-disable-next-line @typescript-eslint/ban-types -export function isNotNull<T extends {}>(input: T | undefined | null): input is T { +export function isNotNull<T extends NonNullable<unknown>>(input: T | undefined | null): input is T { return input != null; } diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index de38f145b2..46b0bb2fab 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -44,12 +44,18 @@ import { packedRoleCondFormulaLogicsSchema, packedRoleCondFormulaValueNot, packedRoleCondFormulaValueIsLocalOrRemoteSchema, + packedRoleCondFormulaValueAssignedRoleSchema, packedRoleCondFormulaValueCreatedSchema, packedRoleCondFormulaFollowersOrFollowingOrNotesSchema, packedRoleCondFormulaValueSchema, } from '@/models/json-schema/role.js'; import { packedAdSchema } from '@/models/json-schema/ad.js'; import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js'; +import { + packedMetaLiteSchema, + packedMetaDetailedOnlySchema, + packedMetaDetailedSchema, +} from '@/models/json-schema/meta.js'; export const refs = { UserLite: packedUserLiteSchema, @@ -91,6 +97,7 @@ export const refs = { RoleCondFormulaLogics: packedRoleCondFormulaLogicsSchema, RoleCondFormulaValueNot: packedRoleCondFormulaValueNot, RoleCondFormulaValueIsLocalOrRemote: packedRoleCondFormulaValueIsLocalOrRemoteSchema, + RoleCondFormulaValueAssignedRole: packedRoleCondFormulaValueAssignedRoleSchema, RoleCondFormulaValueCreated: packedRoleCondFormulaValueCreatedSchema, RoleCondFormulaFollowersOrFollowingOrNotes: packedRoleCondFormulaFollowersOrFollowingOrNotesSchema, RoleCondFormulaValue: packedRoleCondFormulaValueSchema, @@ -99,6 +106,9 @@ export const refs = { RolePolicies: packedRolePoliciesSchema, ReversiGameLite: packedReversiGameLiteSchema, ReversiGameDetailed: packedReversiGameDetailedSchema, + MetaLite: packedMetaLiteSchema, + MetaDetailedOnly: packedMetaDetailedOnlySchema, + MetaDetailed: packedMetaDetailedSchema, }; export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>; diff --git a/packages/backend/src/models/Instance.ts b/packages/backend/src/models/Instance.ts index 0632ef525b..9863c9d75d 100644 --- a/packages/backend/src/models/Instance.ts +++ b/packages/backend/src/models/Instance.ts @@ -144,4 +144,9 @@ export class MiInstance { nullable: true, }) public infoUpdatedAt: Date | null; + + @Column('varchar', { + length: 16384, default: '', + }) + public moderationNote: string; } diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 6ed0ec6ce5..66f19ce197 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -253,6 +253,8 @@ export class MiMeta { }) public turnstileSecretKey: string | null; + // chaptchaç³»ã‚’è¿½åŠ ã—ãŸéš›ã«ã¯nodeinfoã®ãƒ¬ã‚¹ãƒãƒ³ã‚¹ã«è¿½åŠ ã™ã‚‹ã®ã‚’忘れãªã„よã†ã«ã™ã‚‹ã“㨠+ @Column('enum', { enum: ['none', 'all', 'local', 'remote'], default: 'none', diff --git a/packages/backend/src/models/Role.ts b/packages/backend/src/models/Role.ts index fa05ea8637..058abe3118 100644 --- a/packages/backend/src/models/Role.ts +++ b/packages/backend/src/models/Role.ts @@ -29,6 +29,11 @@ type CondFormulaValueIsRemote = { type: 'isRemote'; }; +type CondFormulaValueRoleAssignedTo = { + type: 'roleAssignedTo'; + roleId: string; +}; + type CondFormulaValueCreatedLessThan = { type: 'createdLessThan'; sec: number; @@ -75,6 +80,7 @@ export type RoleCondFormulaValue = { id: string } & ( CondFormulaValueNot | CondFormulaValueIsLocal | CondFormulaValueIsRemote | + CondFormulaValueRoleAssignedTo | CondFormulaValueCreatedLessThan | CondFormulaValueCreatedMoreThan | CondFormulaValueFollowersLessThanOrEq | diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts index 1ca2f55850..7dbe0b3717 100644 --- a/packages/backend/src/models/UserProfile.ts +++ b/packages/backend/src/models/UserProfile.ts @@ -250,6 +250,8 @@ export class MiUserProfile { } | { type: 'mutualFollow'; } | { + type: 'followingOrFollower'; + } | { type: 'list'; userListId: MiUserList['id']; }; diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts index 087a0e6967..42d98fe523 100644 --- a/packages/backend/src/models/json-schema/federation-instance.ts +++ b/packages/backend/src/models/json-schema/federation-instance.ts @@ -107,5 +107,9 @@ export const packedFederationInstanceSchema = { optional: false, nullable: true, format: 'date-time', }, + moderationNote: { + type: 'string', + optional: true, nullable: true, + }, }, } as const; diff --git a/packages/backend/src/models/json-schema/meta.ts b/packages/backend/src/models/json-schema/meta.ts new file mode 100644 index 0000000000..17789f3b46 --- /dev/null +++ b/packages/backend/src/models/json-schema/meta.ts @@ -0,0 +1,328 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export const packedMetaLiteSchema = { + type: 'object', + optional: false, nullable: false, + properties: { + maintainerName: { + type: 'string', + optional: false, nullable: true, + }, + maintainerEmail: { + type: 'string', + optional: false, nullable: true, + }, + version: { + type: 'string', + optional: false, nullable: false, + }, + providesTarball: { + type: 'boolean', + optional: false, nullable: false, + }, + name: { + type: 'string', + optional: false, nullable: true, + }, + shortName: { + type: 'string', + optional: false, nullable: true, + }, + uri: { + type: 'string', + optional: false, nullable: false, + format: 'url', + example: 'https://misskey.example.com', + }, + description: { + type: 'string', + optional: false, nullable: true, + }, + langs: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + }, + }, + tosUrl: { + type: 'string', + optional: false, nullable: true, + }, + repositoryUrl: { + type: 'string', + optional: false, nullable: true, + default: 'https://github.com/misskey-dev/misskey', + }, + feedbackUrl: { + type: 'string', + optional: false, nullable: true, + default: 'https://github.com/misskey-dev/misskey/issues/new', + }, + defaultDarkTheme: { + type: 'string', + optional: false, nullable: true, + }, + defaultLightTheme: { + type: 'string', + optional: false, nullable: true, + }, + disableRegistration: { + type: 'boolean', + optional: false, nullable: false, + }, + emailRequiredForSignup: { + type: 'boolean', + optional: false, nullable: false, + }, + enableHcaptcha: { + type: 'boolean', + optional: false, nullable: false, + }, + hcaptchaSiteKey: { + type: 'string', + optional: false, nullable: true, + }, + enableMcaptcha: { + type: 'boolean', + optional: false, nullable: false, + }, + mcaptchaSiteKey: { + type: 'string', + optional: false, nullable: true, + }, + mcaptchaInstanceUrl: { + type: 'string', + optional: false, nullable: true, + }, + enableRecaptcha: { + type: 'boolean', + optional: false, nullable: false, + }, + recaptchaSiteKey: { + type: 'string', + optional: false, nullable: true, + }, + enableTurnstile: { + type: 'boolean', + optional: false, nullable: false, + }, + turnstileSiteKey: { + type: 'string', + optional: false, nullable: true, + }, + swPublickey: { + type: 'string', + optional: false, nullable: true, + }, + mascotImageUrl: { + type: 'string', + optional: false, nullable: false, + default: '/assets/ai.png', + }, + bannerUrl: { + type: 'string', + optional: false, nullable: true, + }, + serverErrorImageUrl: { + type: 'string', + optional: false, nullable: true, + }, + infoImageUrl: { + type: 'string', + optional: false, nullable: true, + }, + notFoundImageUrl: { + type: 'string', + optional: false, nullable: true, + }, + iconUrl: { + type: 'string', + optional: false, nullable: true, + }, + maxNoteTextLength: { + type: 'number', + optional: false, nullable: false, + }, + ads: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + example: 'xxxxxxxxxx', + }, + url: { + type: 'string', + optional: false, nullable: false, + format: 'url', + }, + place: { + type: 'string', + optional: false, nullable: false, + }, + ratio: { + type: 'number', + optional: false, nullable: false, + }, + imageUrl: { + type: 'string', + optional: false, nullable: false, + format: 'url', + }, + dayOfWeek: { + type: 'integer', + optional: false, nullable: false, + }, + }, + }, + }, + notesPerOneAd: { + type: 'number', + optional: false, nullable: false, + default: 0, + }, + enableEmail: { + type: 'boolean', + optional: false, nullable: false, + }, + enableServiceWorker: { + type: 'boolean', + optional: false, nullable: false, + }, + translatorAvailable: { + type: 'boolean', + optional: false, nullable: false, + }, + mediaProxy: { + type: 'string', + optional: false, nullable: false, + }, + backgroundImageUrl: { + type: 'string', + optional: false, nullable: true, + }, + impressumUrl: { + type: 'string', + optional: false, nullable: true, + }, + logoImageUrl: { + type: 'string', + optional: false, nullable: true, + }, + privacyPolicyUrl: { + type: 'string', + optional: false, nullable: true, + }, + serverRules: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + }, + }, + themeColor: { + type: 'string', + optional: false, nullable: true, + }, + policies: { + type: 'object', + optional: false, nullable: false, + ref: 'RolePolicies', + }, + }, +} as const; + +export const packedMetaDetailedOnlySchema = { + type: 'object', + optional: false, nullable: false, + properties: { + features: { + type: 'object', + optional: true, nullable: false, + properties: { + registration: { + type: 'boolean', + optional: false, nullable: false, + }, + emailRequiredForSignup: { + type: 'boolean', + optional: false, nullable: false, + }, + localTimeline: { + type: 'boolean', + optional: false, nullable: false, + }, + globalTimeline: { + type: 'boolean', + optional: false, nullable: false, + }, + hcaptcha: { + type: 'boolean', + optional: false, nullable: false, + }, + turnstile: { + type: 'boolean', + optional: false, nullable: false, + }, + recaptcha: { + type: 'boolean', + optional: false, nullable: false, + }, + objectStorage: { + type: 'boolean', + optional: false, nullable: false, + }, + serviceWorker: { + type: 'boolean', + optional: false, nullable: false, + }, + miauth: { + type: 'boolean', + optional: true, nullable: false, + default: true, + }, + }, + }, + proxyAccountName: { + type: 'string', + optional: false, nullable: true, + }, + requireSetup: { + type: 'boolean', + optional: false, nullable: false, + example: false, + }, + cacheRemoteFiles: { + type: 'boolean', + optional: false, nullable: false, + }, + cacheRemoteSensitiveFiles: { + type: 'boolean', + optional: false, nullable: false, + }, + }, +} as const; + +export const packedMetaDetailedSchema = { + type: 'object', + allOf: [ + { + type: 'object', + ref: 'MetaLite', + }, + { + type: 'object', + ref: 'MetaDetailedOnly', + }, + ], +} as const; diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts index ef6b279bee..c770250503 100644 --- a/packages/backend/src/models/json-schema/role.ts +++ b/packages/backend/src/models/json-schema/role.ts @@ -57,6 +57,26 @@ export const packedRoleCondFormulaValueIsLocalOrRemoteSchema = { }, } as const; +export const packedRoleCondFormulaValueAssignedRoleSchema = { + type: 'object', + properties: { + id: { + type: 'string', optional: false, + }, + type: { + type: 'string', + nullable: false, optional: false, + enum: ['roleAssignedTo'], + }, + roleId: { + type: 'string', + nullable: false, optional: false, + format: 'id', + example: 'xxxxxxxxxx', + }, + }, +} as const; + export const packedRoleCondFormulaValueCreatedSchema = { type: 'object', properties: { @@ -116,6 +136,9 @@ export const packedRoleCondFormulaValueSchema = { ref: 'RoleCondFormulaValueIsLocalOrRemote', }, { + ref: 'RoleCondFormulaValueAssignedRole', + }, + { ref: 'RoleCondFormulaValueCreated', }, { @@ -140,6 +163,10 @@ export const packedRolePoliciesSchema = { type: 'boolean', optional: false, nullable: false, }, + mentionLimit: { + type: 'integer', + optional: false, nullable: false, + }, canInvite: { type: 'boolean', optional: false, nullable: false, diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index c7f86635da..947a9317d7 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -13,7 +13,7 @@ export const notificationRecieveConfig = { type: { type: 'string', nullable: false, - enum: ['all', 'following', 'follower', 'mutualFollow', 'never'], + enum: ['all', 'following', 'follower', 'mutualFollow', 'followingOrFollower', 'never'], }, }, required: ['type'], @@ -148,6 +148,9 @@ export const packedUserLiteSchema = { emojis: { type: 'object', nullable: false, optional: false, + additionalProperties: { + type: 'string', + }, }, onlineStatus: { type: 'string', diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index f2ae0ce4b4..c7611012d7 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import * as fs from 'node:fs'; +import { ReadableStream, TextEncoderStream } from 'node:stream/web'; import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; @@ -18,10 +18,82 @@ import { bindThis } from '@/decorators.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { Packed } from '@/misc/json-schema.js'; import { IdService } from '@/core/IdService.js'; +import { JsonArrayStream } from '@/misc/JsonArrayStream.js'; +import { FileWriterStream } from '@/misc/FileWriterStream.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type * as Bull from 'bullmq'; import type { DbJobDataWithUser } from '../types.js'; +class NoteStream extends ReadableStream<Record<string, unknown>> { + constructor( + job: Bull.Job, + notesRepository: NotesRepository, + pollsRepository: PollsRepository, + driveFileEntityService: DriveFileEntityService, + idService: IdService, + userId: string, + ) { + let exportedNotesCount = 0; + let cursor: MiNote['id'] | null = null; + + const serialize = ( + note: MiNote, + poll: MiPoll | null, + files: Packed<'DriveFile'>[], + ): Record<string, unknown> => { + return { + id: note.id, + text: note.text, + createdAt: idService.parse(note.id).date.toISOString(), + fileIds: note.fileIds, + files: files, + replyId: note.replyId, + renoteId: note.renoteId, + poll: poll, + cw: note.cw, + visibility: note.visibility, + visibleUserIds: note.visibleUserIds, + localOnly: note.localOnly, + reactionAcceptance: note.reactionAcceptance, + }; + }; + + super({ + async pull(controller): Promise<void> { + const notes = await notesRepository.find({ + where: { + userId, + ...(cursor !== null ? { id: MoreThan(cursor) } : {}), + }, + take: 100, // 100ä»¶ãšã¤å–å¾— + order: { id: 1 }, + }); + + if (notes.length === 0) { + job.updateProgress(100); + controller.close(); + } + + cursor = notes.at(-1)?.id ?? null; + + for (const note of notes) { + const poll = note.hasPoll + ? await pollsRepository.findOneByOrFail({ noteId: note.id }) // N+1 + : null; + const files = await driveFileEntityService.packManyByIds(note.fileIds); // N+1 + const content = serialize(note, poll, files); + + controller.enqueue(content); + exportedNotesCount++; + } + + const total = await notesRepository.countBy({ userId }); + job.updateProgress(exportedNotesCount / total); + }, + }); + } +} + @Injectable() export class ExportNotesProcessorService { private logger: Logger; @@ -59,67 +131,19 @@ export class ExportNotesProcessorService { this.logger.info(`Temp file is ${path}`); try { - const stream = fs.createWriteStream(path, { flags: 'a' }); - - const write = (text: string): Promise<void> => { - return new Promise<void>((res, rej) => { - stream.write(text, err => { - if (err) { - this.logger.error(err); - rej(err); - } else { - res(); - } - }); - }); - }; - - await write('['); - - let exportedNotesCount = 0; - let cursor: MiNote['id'] | null = null; - - while (true) { - const notes = await this.notesRepository.find({ - where: { - userId: user.id, - ...(cursor ? { id: MoreThan(cursor) } : {}), - }, - take: 100, - order: { - id: 1, - }, - }) as MiNote[]; - - if (notes.length === 0) { - job.updateProgress(100); - break; - } - - cursor = notes.at(-1)?.id ?? null; - - for (const note of notes) { - let poll: MiPoll | undefined; - if (note.hasPoll) { - poll = await this.pollsRepository.findOneByOrFail({ noteId: note.id }); - } - const files = await this.driveFileEntityService.packManyByIds(note.fileIds); - const content = JSON.stringify(this.serialize(note, poll, files)); - const isFirst = exportedNotesCount === 0; - await write(isFirst ? content : ',\n' + content); - exportedNotesCount++; - } - - const total = await this.notesRepository.countBy({ - userId: user.id, - }); + // メモリãŒè¶³ã‚Šãªããªã‚‰ãªã„よã†ã«ã‚¹ãƒˆãƒªãƒ¼ãƒ ã§å‡¦ç†ã™ã‚‹ + await new NoteStream( + job, + this.notesRepository, + this.pollsRepository, + this.driveFileEntityService, + this.idService, + user.id, + ) + .pipeThrough(new JsonArrayStream()) + .pipeThrough(new TextEncoderStream()) + .pipeTo(new FileWriterStream(path)); - job.updateProgress(exportedNotesCount / total); - } - - await write(']'); - - stream.end(); this.logger.succ(`Exported to: ${path}`); const fileName = 'notes-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json'; @@ -130,22 +154,4 @@ export class ExportNotesProcessorService { cleanup(); } } - - private serialize(note: MiNote, poll: MiPoll | null = null, files: Packed<'DriveFile'>[]): Record<string, unknown> { - return { - id: note.id, - text: note.text, - createdAt: this.idService.parse(note.id).date.toISOString(), - fileIds: note.fileIds, - files: files, - replyId: note.replyId, - renoteId: note.renoteId, - poll: poll, - cw: note.cw, - visibility: note.visibility, - visibleUserIds: note.visibleUserIds, - localOnly: note.localOnly, - reactionAcceptance: note.reactionAcceptance, - }; - } } diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index 7adadd799b..3addead058 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -24,6 +24,7 @@ import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; import { LdSignatureService } from '@/core/activitypub/LdSignatureService.js'; import { ApInboxService } from '@/core/activitypub/ApInboxService.js'; import { bindThis } from '@/decorators.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type { InboxJobData } from '../types.js'; @@ -180,7 +181,17 @@ export class InboxProcessorService { }); // ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ãƒ†ã‚£ã‚’å‡¦ç† - await this.apInboxService.performActivity(authUser.user, activity); + try { + await this.apInboxService.performActivity(authUser.user, activity); + } catch (e) { + if (e instanceof IdentifiableError) { + if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') { + return 'blocked notes with prohibited words'; + } + if (e.id === '85ab9bd7-3a41-4530-959d-f07073900109') return 'actor has been suspended'; + } + throw e; + } return 'ok'; } } diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 81318ab5ac..c1e5af08c9 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -117,6 +117,8 @@ export class NodeinfoServerService { emailRequiredForSignup: meta.emailRequiredForSignup, enableHcaptcha: meta.enableHcaptcha, enableRecaptcha: meta.enableRecaptcha, + enableMcaptcha: meta.enableMcaptcha, + enableTurnstile: meta.enableTurnstile, maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, enableEmail: meta.enableEmail, enableServiceWorker: meta.enableServiceWorker, diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 8a003725cd..88d3999eb0 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -293,6 +293,7 @@ import * as ep___notes_translate from './endpoints/notes/translate.js'; import * as ep___notes_unrenote from './endpoints/notes/unrenote.js'; import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js'; import * as ep___notifications_create from './endpoints/notifications/create.js'; +import * as ep___notifications_flush from './endpoints/notifications/flush.js'; import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js'; import * as ep___notifications_testNotification from './endpoints/notifications/test-notification.js'; import * as ep___pagePush from './endpoints/page-push.js'; @@ -664,6 +665,7 @@ const $notes_translate: Provider = { provide: 'ep:notes/translate', useClass: ep const $notes_unrenote: Provider = { provide: 'ep:notes/unrenote', useClass: ep___notes_unrenote.default }; const $notes_userListTimeline: Provider = { provide: 'ep:notes/user-list-timeline', useClass: ep___notes_userListTimeline.default }; const $notifications_create: Provider = { provide: 'ep:notifications/create', useClass: ep___notifications_create.default }; +const $notifications_flush: Provider = { provide: 'ep:notifications/flush', useClass: ep___notifications_flush.default }; const $notifications_markAllAsRead: Provider = { provide: 'ep:notifications/mark-all-as-read', useClass: ep___notifications_markAllAsRead.default }; const $notifications_testNotification: Provider = { provide: 'ep:notifications/test-notification', useClass: ep___notifications_testNotification.default }; const $pagePush: Provider = { provide: 'ep:page-push', useClass: ep___pagePush.default }; @@ -1039,6 +1041,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $notes_unrenote, $notes_userListTimeline, $notifications_create, + $notifications_flush, $notifications_markAllAsRead, $notifications_testNotification, $pagePush, @@ -1408,7 +1411,9 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $notes_unrenote, $notes_userListTimeline, $notifications_create, + $notifications_flush, $notifications_markAllAsRead, + $notifications_testNotification, $pagePush, $pages_create, $pages_delete, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index e1c8be727e..f7e64a7356 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -293,6 +293,7 @@ import * as ep___notes_translate from './endpoints/notes/translate.js'; import * as ep___notes_unrenote from './endpoints/notes/unrenote.js'; import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js'; import * as ep___notifications_create from './endpoints/notifications/create.js'; +import * as ep___notifications_flush from './endpoints/notifications/flush.js'; import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js'; import * as ep___notifications_testNotification from './endpoints/notifications/test-notification.js'; import * as ep___pagePush from './endpoints/page-push.js'; @@ -662,6 +663,7 @@ const eps = [ ['notes/unrenote', ep___notes_unrenote], ['notes/user-list-timeline', ep___notes_userListTimeline], ['notifications/create', ep___notifications_create], + ['notifications/flush', ep___notifications_flush], ['notifications/mark-all-as-read', ep___notifications_markAllAsRead], ['notifications/test-notification', ep___notifications_testNotification], ['page-push', ep___pagePush], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index e32a19120d..796f273330 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -31,7 +31,10 @@ export const meta = { }, }, - ref: 'EmojiDetailed', + res: { + type: 'object', + ref: 'EmojiDetailed', + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index a9ff4236d2..22609a16a3 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -57,7 +57,10 @@ export const paramDef = { type: 'string', } }, }, - required: ['id', 'name', 'aliases'], + anyOf: [ + { required: ['id'] }, + { required: ['name'] }, + ], } as const; @Injectable() @@ -70,27 +73,33 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- ) { super(meta, paramDef, async (ps, me) => { let driveFile; - if (ps.fileId) { driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); } - const emoji = await this.customEmojiService.getEmojiById(ps.id); - if (emoji != null) { - if (ps.name !== emoji.name) { + + let emojiId; + if (ps.id) { + emojiId = ps.id; + const emoji = await this.customEmojiService.getEmojiById(ps.id); + if (!emoji) throw new ApiError(meta.errors.noSuchEmoji); + if (ps.name && (ps.name !== emoji.name)) { const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name); if (isDuplicate) throw new ApiError(meta.errors.sameNameEmojiExists); } } else { - throw new ApiError(meta.errors.noSuchEmoji); + if (!ps.name) throw new Error('Invalid Params unexpectedly passed. This is a BUG. Please report it to the development team.'); + const emoji = await this.customEmojiService.getEmojiByName(ps.name); + if (!emoji) throw new ApiError(meta.errors.noSuchEmoji); + emojiId = emoji.id; } - await this.customEmojiService.update(ps.id, { + await this.customEmojiService.update(emojiId, { driveFile, name: ps.name, - category: ps.category ?? null, + category: ps.category, aliases: ps.aliases, - license: ps.license ?? null, + license: ps.license, isSensitive: ps.isSensitive, localOnly: ps.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction, diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts index b989b99e47..0bcdc2a4b8 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts @@ -24,8 +24,9 @@ export const paramDef = { properties: { host: { type: 'string' }, isSuspended: { type: 'boolean' }, + moderationNote: { type: 'string' }, }, - required: ['host', 'isSuspended'], + required: ['host'], } as const; @Injectable() @@ -47,9 +48,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- await this.federatedInstanceService.update(instance.id, { isSuspended: ps.isSuspended, + moderationNote: ps.moderationNote, }); - if (instance.isSuspended !== ps.isSuspended) { + if (ps.isSuspended != null && instance.isSuspended !== ps.isSuspended) { if (ps.isSuspended) { this.moderationLogService.log(me, 'suspendRemoteInstance', { id: instance.id, @@ -62,6 +64,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }); } } + + if (ps.moderationNote != null && instance.moderationNote !== ps.moderationNote) { + this.moderationLogService.log(me, 'updateRemoteInstanceNote', { + id: instance.id, + host: instance.host, + before: instance.moderationNote, + after: ps.moderationNote, + }); + } }); } } diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 39f3fab21e..f4dfe1ecc4 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -124,9 +124,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- notes.sort((a, b) => a.id > b.id ? -1 : 1); } - if (notes.length > 0) { - this.noteReadService.read(me.id, notes); - } + this.noteReadService.read(me.id, notes); return await this.noteEntityService.packMany(notes, me); }); diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts index e3c598d110..2972861a4b 100644 --- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts +++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts @@ -43,7 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const instance = await this.instancesRepository .findOneBy({ host: this.utilityService.toPuny(ps.host) }); - return instance ? await this.instanceEntityService.pack(instance) : null; + return instance ? await this.instanceEntityService.pack(instance, me) : null; }); } } diff --git a/packages/backend/src/server/api/endpoints/flash/update.ts b/packages/backend/src/server/api/endpoints/flash/update.ts index 7d7633daa5..e378669f0a 100644 --- a/packages/backend/src/server/api/endpoints/flash/update.ts +++ b/packages/backend/src/server/api/endpoints/flash/update.ts @@ -51,7 +51,7 @@ export const paramDef = { } }, visibility: { type: 'string', enum: ['public', 'private'] }, }, - required: ['flashId', 'title', 'summary', 'script', 'permissions'], + required: ['flashId'], } as const; @Injectable() @@ -71,11 +71,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- await this.flashsRepository.update(flash.id, { updatedAt: new Date(), - title: ps.title, - summary: ps.summary, - script: ps.script, - permissions: ps.permissions, - visibility: ps.visibility, + ...Object.fromEntries( + Object.entries(ps).filter( + ([key, val]) => (key !== 'flashId') && Object.hasOwn(paramDef.properties, key) + ) + ), }); }); } diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index ceaf32ccb2..db320e7129 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -71,7 +71,7 @@ export const paramDef = { type: 'object', properties: { userId: { type: 'string', format: 'misskey:id' }, - withReplies: { type: 'boolean' } + withReplies: { type: 'boolean' }, }, required: ['userId'], } as const; @@ -100,22 +100,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw err; }); - // Check if already following - const exist = await this.followingsRepository.exists({ - where: { - followerId: follower.id, - followeeId: followee.id, - }, - }); - - if (exist) { - throw new ApiError(meta.errors.alreadyFollowing); - } - try { await this.userFollowingService.follow(follower, followee, { withReplies: ps.withReplies }); } catch (e) { if (e instanceof IdentifiableError) { + if (e.id === 'ec3f65c0-a9d1-47d9-8791-b2e7b9dcdced') throw new ApiError(meta.errors.alreadyFollowing); if (e.id === '710e8fb0-b8c3-4922-be49-d5d93d8e6a6e') throw new ApiError(meta.errors.blocking); if (e.id === '3338392a-f764-498d-8855-db939dcf8c48') throw new ApiError(meta.errors.blocked); } diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts index 784ae5088f..b07cdf1ed9 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts @@ -12,6 +12,7 @@ import type { MiDriveFile } from '@/models/DriveFile.js'; import { IdService } from '@/core/IdService.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; +import { isNotNull } from '@/misc/is-not-null.js'; export const meta = { tags: ['gallery'], @@ -69,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- id: fileId, userId: me.id, }), - ))).filter((file): file is MiDriveFile => file != null); + ))).filter(isNotNull); if (files.length === 0) { throw new Error(); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts index 8872b261dd..8bd83ff5ba 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts @@ -10,6 +10,7 @@ import type { DriveFilesRepository, GalleryPostsRepository } from '@/models/_.js import type { MiDriveFile } from '@/models/DriveFile.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; +import { isNotNull } from '@/misc/is-not-null.js'; export const meta = { tags: ['gallery'], @@ -67,7 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- id: fileId, userId: me.id, }), - ))).filter((file): file is MiDriveFile => file != null); + ))).filter(isNotNull); if (files.length === 0) { throw new Error(); diff --git a/packages/backend/src/server/api/endpoints/hashtags/search.ts b/packages/backend/src/server/api/endpoints/hashtags/search.ts index 12d47fa512..d4eb851054 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/search.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/search.ts @@ -43,7 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- super(meta, paramDef, async (ps, me) => { const hashtags = await this.hashtagsRepository.createQueryBuilder('tag') .where('tag.name like :q', { q: sqlLikeEscape(ps.query.toLowerCase()) + '%' }) - .orderBy('tag.count', 'DESC') + .orderBy('tag.mentionedLocalUsersCount', 'DESC') .groupBy('tag.id') .limit(ps.limit) .offset(ps.offset) diff --git a/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts b/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts index 703808d279..dc6ffd3e02 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts @@ -3,11 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Brackets, In } from 'typeorm'; +import { In } from 'typeorm'; import * as Redis from 'ioredis'; import { Inject, Injectable } from '@nestjs/common'; import type { NotesRepository } from '@/models/_.js'; -import { obsoleteNotificationTypes, notificationTypes, FilterUnionByProperty } from '@/types.js'; +import { obsoleteNotificationTypes, groupedNotificationTypes, FilterUnionByProperty } from '@/types.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteReadService } from '@/core/NoteReadService.js'; import { NotificationEntityService } from '@/core/entities/NotificationEntityService.js'; @@ -48,10 +48,10 @@ export const paramDef = { markAsRead: { type: 'boolean', default: true }, // 後方互æ›ã®ãŸã‚ã€å»ƒæ¢ã•れãŸé€šçŸ¥ã‚¿ã‚¤ãƒ—ã‚‚å—ã‘付ã‘ã‚‹ includeTypes: { type: 'array', items: { - type: 'string', enum: [...notificationTypes, ...obsoleteNotificationTypes], + type: 'string', enum: [...groupedNotificationTypes, ...obsoleteNotificationTypes], } }, excludeTypes: { type: 'array', items: { - type: 'string', enum: [...notificationTypes, ...obsoleteNotificationTypes], + type: 'string', enum: [...groupedNotificationTypes, ...obsoleteNotificationTypes], } }, }, required: [], @@ -79,12 +79,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- return []; } // excludeTypes ã«å…¨æŒ‡å®šã•れã¦ã„ã‚‹å ´åˆã¯ã‚¯ã‚¨ãƒªã—ãªã„ - if (notificationTypes.every(type => ps.excludeTypes?.includes(type))) { + if (groupedNotificationTypes.every(type => ps.excludeTypes?.includes(type))) { return []; } - const includeTypes = ps.includeTypes && ps.includeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof notificationTypes[number][]; - const excludeTypes = ps.excludeTypes && ps.excludeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof notificationTypes[number][]; + const includeTypes = ps.includeTypes && ps.includeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof groupedNotificationTypes[number][]; + const excludeTypes = ps.excludeTypes && ps.excludeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof groupedNotificationTypes[number][]; const limit = (ps.limit + EXTRA_LIMIT) + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdã«æŒ‡å®šã—ãŸã‚‚ã®ã‚‚å«ã¾ã‚Œã‚‹ãŸã‚+1 const notificationsRes = await this.redisClient.xrevrange( @@ -162,7 +162,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } groupedNotifications = groupedNotifications.slice(0, ps.limit); - const noteIds = groupedNotifications .filter((notification): notification is FilterUnionByProperty<MiNotification, 'type', 'mention' | 'reply' | 'quote'> => ['mention', 'reply', 'quote'].includes(notification.type)) .map(notification => notification.noteId!); diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 52b6749e3f..320d9fdb00 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Brackets, In } from 'typeorm'; +import { In } from 'typeorm'; import * as Redis from 'ioredis'; import { Inject, Injectable } from '@nestjs/common'; import type { NotesRepository } from '@/models/_.js'; diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index bf6c53d8eb..84a1931a3d 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -456,9 +456,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- this.hashtagService.updateUsertags(user, tags); //#endregion - if (Object.keys(updates).length > 0) await this.usersRepository.update(user.id, updates); - if (Object.keys(updates).includes('alsoKnownAs')) { - this.cacheService.uriPersonCache.set(this.userEntityService.genLocalUserUri(user.id), { ...user, ...updates }); + if (Object.keys(updates).length > 0) { + await this.usersRepository.update(user.id, updates); + this.globalEventService.publishInternalEvent('localUserUpdated', { id: user.id }); } await this.userProfilesRepository.update(user.id, { diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 834158baf4..5460635e1d 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -3,18 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { IsNull, LessThanOrEqual, MoreThan, Brackets } from 'typeorm'; -import { Inject, Injectable } from '@nestjs/common'; -import JSON5 from 'json5'; -import type { AdsRepository } from '@/models/_.js'; -import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { MetaService } from '@/core/MetaService.js'; -import { InstanceActorService } from '@/core/InstanceActorService.js'; -import type { Config } from '@/config.js'; -import { DI } from '@/di-symbols.js'; -import { DEFAULT_POLICIES } from '@/core/RoleService.js'; +import { MetaEntityService } from '@/core/entities/MetaEntityService.js'; export const meta = { tags: ['meta'], @@ -23,297 +14,10 @@ export const meta = { res: { type: 'object', - optional: false, nullable: false, - properties: { - maintainerName: { - type: 'string', - optional: false, nullable: true, - }, - maintainerEmail: { - type: 'string', - optional: false, nullable: true, - }, - version: { - type: 'string', - optional: false, nullable: false, - }, - providesTarball: { - type: 'boolean', - optional: false, nullable: false, - }, - name: { - type: 'string', - optional: false, nullable: false, - }, - shortName: { - type: 'string', - optional: false, nullable: true, - }, - uri: { - type: 'string', - optional: false, nullable: false, - format: 'url', - example: 'https://misskey.example.com', - }, - description: { - type: 'string', - optional: false, nullable: true, - }, - langs: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'string', - optional: false, nullable: false, - }, - }, - tosUrl: { - type: 'string', - optional: false, nullable: true, - }, - repositoryUrl: { - type: 'string', - optional: false, nullable: true, - default: 'https://github.com/misskey-dev/misskey', - }, - feedbackUrl: { - type: 'string', - optional: false, nullable: true, - default: 'https://github.com/misskey-dev/misskey/issues/new', - }, - defaultDarkTheme: { - type: 'string', - optional: false, nullable: true, - }, - defaultLightTheme: { - type: 'string', - optional: false, nullable: true, - }, - disableRegistration: { - type: 'boolean', - optional: false, nullable: false, - }, - cacheRemoteFiles: { - type: 'boolean', - optional: false, nullable: false, - }, - cacheRemoteSensitiveFiles: { - type: 'boolean', - optional: false, nullable: false, - }, - emailRequiredForSignup: { - type: 'boolean', - optional: false, nullable: false, - }, - enableHcaptcha: { - type: 'boolean', - optional: false, nullable: false, - }, - hcaptchaSiteKey: { - type: 'string', - optional: false, nullable: true, - }, - enableMcaptcha: { - type: 'boolean', - optional: false, nullable: false, - }, - mcaptchaSiteKey: { - type: 'string', - optional: false, nullable: true, - }, - mcaptchaInstanceUrl: { - type: 'string', - optional: false, nullable: true, - }, - enableRecaptcha: { - type: 'boolean', - optional: false, nullable: false, - }, - recaptchaSiteKey: { - type: 'string', - optional: false, nullable: true, - }, - enableTurnstile: { - type: 'boolean', - optional: false, nullable: false, - }, - turnstileSiteKey: { - type: 'string', - optional: false, nullable: true, - }, - swPublickey: { - type: 'string', - optional: false, nullable: true, - }, - mascotImageUrl: { - type: 'string', - optional: false, nullable: false, - default: '/assets/ai.png', - }, - bannerUrl: { - type: 'string', - optional: false, nullable: false, - }, - serverErrorImageUrl: { - type: 'string', - optional: false, nullable: true, - }, - infoImageUrl: { - type: 'string', - optional: false, nullable: true, - }, - notFoundImageUrl: { - type: 'string', - optional: false, nullable: true, - }, - iconUrl: { - type: 'string', - optional: false, nullable: true, - }, - maxNoteTextLength: { - type: 'number', - optional: false, nullable: false, - }, - ads: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - properties: { - id: { - type: 'string', - optional: false, nullable: false, - format: 'id', - example: 'xxxxxxxxxx', - }, - url: { - type: 'string', - optional: false, nullable: false, - format: 'url', - }, - place: { - type: 'string', - optional: false, nullable: false, - }, - ratio: { - type: 'number', - optional: false, nullable: false, - }, - imageUrl: { - type: 'string', - optional: false, nullable: false, - format: 'url', - }, - dayOfWeek: { - type: 'integer', - optional: false, nullable: false, - }, - }, - }, - }, - notesPerOneAd: { - type: 'number', - optional: false, nullable: false, - default: 0, - }, - requireSetup: { - type: 'boolean', - optional: false, nullable: false, - example: false, - }, - enableEmail: { - type: 'boolean', - optional: false, nullable: false, - }, - enableServiceWorker: { - type: 'boolean', - optional: false, nullable: false, - }, - translatorAvailable: { - type: 'boolean', - optional: false, nullable: false, - }, - proxyAccountName: { - type: 'string', - optional: false, nullable: true, - }, - mediaProxy: { - type: 'string', - optional: false, nullable: false, - }, - features: { - type: 'object', - optional: true, nullable: false, - properties: { - registration: { - type: 'boolean', - optional: false, nullable: false, - }, - localTimeline: { - type: 'boolean', - optional: false, nullable: false, - }, - globalTimeline: { - type: 'boolean', - optional: false, nullable: false, - }, - hcaptcha: { - type: 'boolean', - optional: false, nullable: false, - }, - recaptcha: { - type: 'boolean', - optional: false, nullable: false, - }, - objectStorage: { - type: 'boolean', - optional: false, nullable: false, - }, - serviceWorker: { - type: 'boolean', - optional: false, nullable: false, - }, - miauth: { - type: 'boolean', - optional: true, nullable: false, - default: true, - }, - }, - }, - backgroundImageUrl: { - type: 'string', - optional: false, nullable: true, - }, - impressumUrl: { - type: 'string', - optional: false, nullable: true, - }, - logoImageUrl: { - type: 'string', - optional: false, nullable: true, - }, - privacyPolicyUrl: { - type: 'string', - optional: false, nullable: true, - }, - serverRules: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'string', - }, - }, - themeColor: { - type: 'string', - optional: false, nullable: true, - }, - policies: { - type: 'object', - optional: false, nullable: false, - ref: 'RolePolicies', - }, - }, + oneOf: [ + { type: 'object', ref: 'MetaLite' }, + { type: 'object', ref: 'MetaDetailed' }, + ], }, } as const; @@ -328,115 +32,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.adsRepository) - private adsRepository: AdsRepository, - - private userEntityService: UserEntityService, - private metaService: MetaService, - private instanceActorService: InstanceActorService, + private metaEntityService: MetaEntityService, ) { super(meta, paramDef, async (ps, me) => { - const instance = await this.metaService.fetch(true); - - const ads = await this.adsRepository.createQueryBuilder('ads') - .where('ads.expiresAt > :now', { now: new Date() }) - .andWhere('ads.startsAt <= :now', { now: new Date() }) - .andWhere(new Brackets(qb => { - // 曜日ã®ãƒ“ットフラグを確èªã™ã‚‹ - qb.where('ads.dayOfWeek & :dayOfWeek > 0', { dayOfWeek: 1 << new Date().getDay() }) - .orWhere('ads.dayOfWeek = 0'); - })) - .getMany(); - - const response: any = { - maintainerName: instance.maintainerName, - maintainerEmail: instance.maintainerEmail, - - version: this.config.version, - providesTarball: this.config.publishTarballInsteadOfProvideRepositoryUrl, - - name: instance.name, - shortName: instance.shortName, - uri: this.config.url, - description: instance.description, - langs: instance.langs, - tosUrl: instance.termsOfServiceUrl, - repositoryUrl: instance.repositoryUrl, - feedbackUrl: instance.feedbackUrl, - impressumUrl: instance.impressumUrl, - privacyPolicyUrl: instance.privacyPolicyUrl, - disableRegistration: instance.disableRegistration, - emailRequiredForSignup: instance.emailRequiredForSignup, - enableHcaptcha: instance.enableHcaptcha, - hcaptchaSiteKey: instance.hcaptchaSiteKey, - enableMcaptcha: instance.enableMcaptcha, - mcaptchaSiteKey: instance.mcaptchaSitekey, - mcaptchaInstanceUrl: instance.mcaptchaInstanceUrl, - enableRecaptcha: instance.enableRecaptcha, - recaptchaSiteKey: instance.recaptchaSiteKey, - enableTurnstile: instance.enableTurnstile, - turnstileSiteKey: instance.turnstileSiteKey, - swPublickey: instance.swPublicKey, - themeColor: instance.themeColor, - mascotImageUrl: instance.mascotImageUrl, - bannerUrl: instance.bannerUrl, - infoImageUrl: instance.infoImageUrl, - serverErrorImageUrl: instance.serverErrorImageUrl, - notFoundImageUrl: instance.notFoundImageUrl, - iconUrl: instance.iconUrl, - backgroundImageUrl: instance.backgroundImageUrl, - logoImageUrl: instance.logoImageUrl, - maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, - // ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆã®æ‰‹é–“を減らã™ãŸã‚ã‚らã‹ã˜ã‚JSONã«å¤‰æ›ã—ã¦ãŠã - defaultLightTheme: instance.defaultLightTheme ? JSON.stringify(JSON5.parse(instance.defaultLightTheme)) : null, - defaultDarkTheme: instance.defaultDarkTheme ? JSON.stringify(JSON5.parse(instance.defaultDarkTheme)) : null, - ads: ads.map(ad => ({ - id: ad.id, - url: ad.url, - place: ad.place, - ratio: ad.ratio, - imageUrl: ad.imageUrl, - dayOfWeek: ad.dayOfWeek, - })), - notesPerOneAd: instance.notesPerOneAd, - enableEmail: instance.enableEmail, - enableServiceWorker: instance.enableServiceWorker, - - translatorAvailable: instance.deeplAuthKey != null, - - serverRules: instance.serverRules, - - policies: { ...DEFAULT_POLICIES, ...instance.policies }, - - mediaProxy: this.config.mediaProxy, - - ...(ps.detail ? { - cacheRemoteFiles: instance.cacheRemoteFiles, - cacheRemoteSensitiveFiles: instance.cacheRemoteSensitiveFiles, - requireSetup: !await this.instanceActorService.realLocalUsersPresent(), - } : {}), - }; - - if (ps.detail) { - const proxyAccount = instance.proxyAccountId ? await this.userEntityService.pack(instance.proxyAccountId).catch(() => null) : null; - - response.proxyAccountName = proxyAccount ? proxyAccount.username : null; - response.features = { - registration: !instance.disableRegistration, - emailRequiredForSignup: instance.emailRequiredForSignup, - hcaptcha: instance.enableHcaptcha, - recaptcha: instance.enableRecaptcha, - turnstile: instance.enableTurnstile, - objectStorage: instance.useObjectStorage, - serviceWorker: instance.enableServiceWorker, - miauth: true, - }; - } - - return response; + return ps.detail ? await this.metaEntityService.packDetailed() : await this.metaEntityService.pack(); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index e6e4fcc745..bfb9214439 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -19,6 +19,7 @@ import { DI } from '@/di-symbols.js'; import { isPureRenote } from '@/misc/is-pure-renote.js'; import { MetaService } from '@/core/MetaService.js'; import { UtilityService } from '@/core/UtilityService.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -84,6 +85,12 @@ export const meta = { id: '3ac74a84-8fd5-4bb0-870f-01804f82ce15', }, + cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility: { + message: 'You cannot reply to a specified visibility note with extended visibility.', + code: 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY', + id: 'ed940410-535c-4d5e-bfa3-af798671e93c', + }, + cannotCreateAlreadyExpiredPoll: { message: 'Poll is already expired.', code: 'CANNOT_CREATE_ALREADY_EXPIRED_POLL', @@ -119,6 +126,12 @@ export const meta = { code: 'CONTAINS_PROHIBITED_WORDS', id: 'aa6e01d3-a85c-669d-758a-76aab43af334', }, + + containsTooManyMentions: { + message: 'Cannot post because it exceeds the allowed number of mentions.', + code: 'CONTAINS_TOO_MANY_MENTIONS', + id: '4de0363a-3046-481b-9b0f-feff3e211025', + }, }, } as const; @@ -312,6 +325,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw new ApiError(meta.errors.cannotReplyToPureRenote); } else if (!await this.noteEntityService.isVisibleForMe(reply, me.id)) { throw new ApiError(meta.errors.cannotReplyToInvisibleNote); + } else if (reply.visibility === 'specified' && ps.visibility !== 'specified') { + throw new ApiError(meta.errors.cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility); } // Check blocking @@ -376,10 +391,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }; } catch (e) { // TODO: ä»–ã®Errorã‚‚ã“ã“ã§ã‚ャッãƒã—ã¦ã‚¨ãƒ©ãƒ¼ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’当ã¦ã‚‹ã‚ˆã†ã«ã—ãŸã„ - if (e instanceof NoteCreateService.ContainsProhibitedWordsError) { - throw new ApiError(meta.errors.containsProhibitedWords); + if (e instanceof IdentifiableError) { + if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') { + throw new ApiError(meta.errors.containsProhibitedWords); + } else if (e.id === '9f466dab-c856-48cd-9e65-ff90ff750580') { + throw new ApiError(meta.errors.containsTooManyMentions); + } } - throw e; } }); diff --git a/packages/backend/src/server/api/endpoints/notifications/flush.ts b/packages/backend/src/server/api/endpoints/notifications/flush.ts new file mode 100644 index 0000000000..47c0642fd1 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notifications/flush.ts @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { NotificationService } from '@/core/NotificationService.js'; + +export const meta = { + tags: ['notifications', 'account'], + + requireCredential: true, + + kind: 'write:notifications', +} as const; + +export const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private notificationService: NotificationService, + ) { + super(meta, paramDef, async (ps, me) => { + this.notificationService.flushAllNotifications(me.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts index 1f4509764f..784766bcb5 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -12,6 +12,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/core/MetaService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; +import { isNotNull } from '@/misc/is-not-null.js'; export const meta = { tags: ['users'], @@ -52,7 +53,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- host: acct.host ?? IsNull(), }))); - return await this.userEntityService.packMany(users.filter(x => x !== null) as MiUser[], me, { schema: 'UserDetailed' }); + return await this.userEntityService.packMany(users.filter(isNotNull), me, { schema: 'UserDetailed' }); }); } } diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index 06c04b3f9a..a9a33149f9 100644 --- a/packages/backend/src/server/api/endpoints/sw/register.ts +++ b/packages/backend/src/server/api/endpoints/sw/register.ts @@ -9,6 +9,7 @@ import type { SwSubscriptionsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/core/MetaService.js'; import { DI } from '@/di-symbols.js'; +import { PushNotificationService } from '@/core/PushNotificationService.js'; export const meta = { tags: ['account'], @@ -66,6 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private idService: IdService, private metaService: MetaService, + private pushNotificationService: PushNotificationService, ) { super(meta, paramDef, async (ps, me) => { // if already subscribed @@ -97,6 +99,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- sendReadMessage: ps.sendReadMessage, }); + this.pushNotificationService.refreshCache(me.id); + return { state: 'subscribed' as const, key: instance.swPublicKey, diff --git a/packages/backend/src/server/api/endpoints/sw/unregister.ts b/packages/backend/src/server/api/endpoints/sw/unregister.ts index 2bc91c7278..2edf7fab1b 100644 --- a/packages/backend/src/server/api/endpoints/sw/unregister.ts +++ b/packages/backend/src/server/api/endpoints/sw/unregister.ts @@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { SwSubscriptionsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; +import { PushNotificationService } from '@/core/PushNotificationService.js'; export const meta = { tags: ['account'], @@ -29,12 +30,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject(DI.swSubscriptionsRepository) private swSubscriptionsRepository: SwSubscriptionsRepository, + + private pushNotificationService: PushNotificationService, ) { super(meta, paramDef, async (ps, me) => { await this.swSubscriptionsRepository.delete({ ...(me ? { userId: me.id } : {}), endpoint: ps.endpoint, }); + + if (me) { + this.pushNotificationService.refreshCache(me.id); + } }); } } diff --git a/packages/backend/src/server/api/endpoints/sw/update-registration.ts b/packages/backend/src/server/api/endpoints/sw/update-registration.ts index b56b07fd00..839a07c770 100644 --- a/packages/backend/src/server/api/endpoints/sw/update-registration.ts +++ b/packages/backend/src/server/api/endpoints/sw/update-registration.ts @@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { SwSubscriptionsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; +import { PushNotificationService } from '@/core/PushNotificationService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -58,6 +59,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject(DI.swSubscriptionsRepository) private swSubscriptionsRepository: SwSubscriptionsRepository, + + private pushNotificationService: PushNotificationService, ) { super(meta, paramDef, async (ps, me) => { const swSubscription = await this.swSubscriptionsRepository.findOneBy({ @@ -77,6 +80,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- sendReadMessage: swSubscription.sendReadMessage, }); + this.pushNotificationService.refreshCache(me.id); + return { userId: swSubscription.userId, endpoint: swSubscription.endpoint, diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index e20d896248..aca883a052 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -98,7 +98,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .limit(ps.limit) .getMany(); - return await Promise.all(reactions.map(reaction => this.noteReactionEntityService.pack(reaction, me, { withNote: true }))); + return await this.noteReactionEntityService.packMany(reactions, me, { withNote: true }); }); } } diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index ce9d7f5647..f45bf8622e 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -71,7 +71,15 @@ class HomeTimelineChannel extends Channel { } } - if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; + // 純粋ãªãƒªãƒŽãƒ¼ãƒˆï¼ˆå¼•用リノートã§ãªã„リノート)ã®å ´åˆ + if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && note.poll == null) { + if (!this.withRenotes) return; + if (note.renote.reply) { + const reply = note.renote.reply; + // 自分ã®ãƒ•ã‚©ãƒãƒ¼ã—ã¦ã„ãªã„ユーザー㮠visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ã®ãƒªãƒŽãƒ¼ãƒˆã¯å¼¾ã + if (reply.visibility === 'followers' && !Object.hasOwn(this.following, reply.userId)) return; + } + } // æµã‚Œã¦ããŸNoteãŒãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーãŒé–¢ã‚ã‚‹ã‚‚ã®ã ã£ãŸã‚‰ç„¡è¦–ã™ã‚‹ if (isUserRelated(note, this.userIdsWhoMeMuting)) return; diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index f255e28fc2..b1af0c3df6 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -19,6 +19,7 @@ import fastifyView from '@fastify/view'; import fastifyCookie from '@fastify/cookie'; import fastifyProxy from '@fastify/http-proxy'; import vary from 'vary'; +import htmlSafeJsonStringify from 'htmlescape'; import type { Config } from '@/config.js'; import { getNoteSummary } from '@/misc/get-note-summary.js'; import { DI } from '@/di-symbols.js'; @@ -28,12 +29,12 @@ import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, Obj import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { PageEntityService } from '@/core/entities/PageEntityService.js'; +import { MetaEntityService } from '@/core/entities/MetaEntityService.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, MiMeta, NotesRepository, PagesRepository, ReversiGamesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; -import { deepClone } from '@/misc/clone.js'; import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js'; import { bindThis } from '@/decorators.js'; import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; @@ -93,6 +94,7 @@ export class ClientServerService { private userEntityService: UserEntityService, private noteEntityService: NoteEntityService, private pageEntityService: PageEntityService, + private metaEntityService: MetaEntityService, private galleryPostEntityService: GalleryPostEntityService, private clipEntityService: ClipEntityService, private channelEntityService: ChannelEntityService, @@ -173,7 +175,7 @@ export class ClientServerService { } @bindThis - private generateCommonPugData(meta: MiMeta) { + private async generateCommonPugData(meta: MiMeta) { return { instanceName: meta.name ?? 'Misskey', icon: meta.iconUrl, @@ -183,6 +185,8 @@ export class ClientServerService { infoImageUrl: meta.infoImageUrl ?? 'https://xn--931a.moe/assets/info.jpg', notFoundImageUrl: meta.notFoundImageUrl ?? 'https://xn--931a.moe/assets/not-found.jpg', instanceUrl: this.config.url, + metaJson: htmlSafeJsonStringify(await this.metaEntityService.packDetailed(meta)), + now: Date.now(), }; } @@ -433,7 +437,7 @@ export class ClientServerService { url: this.config.url, title: meta.name ?? 'Misskey', desc: meta.description, - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), }); }; @@ -520,7 +524,7 @@ export class ClientServerService { user, profile, me, avatarUrl: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user), sub: request.params.sub, - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), }); } else { // リモートユーザーãªã®ã§ @@ -570,7 +574,7 @@ export class ClientServerService { avatarUrl: _note.user.avatarUrl, // TODO: Let locale changeable by instance setting summary: getNoteSummary(_note), - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), }); } else { return await renderBase(reply); @@ -609,7 +613,7 @@ export class ClientServerService { page: _page, profile, avatarUrl: _page.user.avatarUrl, - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), }); } else { return await renderBase(reply); @@ -635,7 +639,7 @@ export class ClientServerService { flash: _flash, profile, avatarUrl: _flash.user.avatarUrl, - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), }); } else { return await renderBase(reply); @@ -661,7 +665,7 @@ export class ClientServerService { clip: _clip, profile, avatarUrl: _clip.user.avatarUrl, - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), }); } else { return await renderBase(reply); @@ -685,7 +689,7 @@ export class ClientServerService { post: _post, profile, avatarUrl: _post.user.avatarUrl, - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), }); } else { return await renderBase(reply); @@ -704,7 +708,7 @@ export class ClientServerService { reply.header('Cache-Control', 'public, max-age=15'); return await reply.view('channel', { channel: _channel, - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), }); } else { return await renderBase(reply); @@ -723,7 +727,7 @@ export class ClientServerService { reply.header('Cache-Control', 'public, max-age=3600'); return await reply.view('reversi-game', { game: _game, - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), }); } else { return await renderBase(reply); diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index d167afe1e8..123336809b 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -68,6 +68,9 @@ html var VERSION = "#{version}"; var CLIENT_ENTRY = "#{clientEntry.file}"; + script(type='application/json' id='misskey_meta' data-generated-at=now) + != metaJson + script include ../boot.js diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index fdcd2c0629..929070d0d2 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -33,7 +33,15 @@ export const notificationTypes = [ 'roleAssigned', 'achievementEarned', 'app', - 'test'] as const; + 'test', +] as const; + +export const groupedNotificationTypes = [ + ...notificationTypes, + 'reaction:grouped', + 'renote:grouped', +] as const; + export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; @@ -69,6 +77,7 @@ export const moderationLogTypes = [ 'resetPassword', 'suspendRemoteInstance', 'unsuspendRemoteInstance', + 'updateRemoteInstanceNote', 'markSensitiveDriveFile', 'unmarkSensitiveDriveFile', 'resolveAbuseReport', @@ -209,6 +218,12 @@ export type ModerationLogPayloads = { id: string; host: string; }; + updateRemoteInstanceNote: { + id: string; + host: string; + before: string | null; + after: string | null; + }; markSensitiveDriveFile: { fileId: string; fileUserId: string | null; diff --git a/packages/backend/test/e2e/mute.ts b/packages/backend/test/e2e/mute.ts index e63067cd62..1d28e07b7d 100644 --- a/packages/backend/test/e2e/mute.ts +++ b/packages/backend/test/e2e/mute.ts @@ -117,5 +117,185 @@ describe('Mute', () => { assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒªãƒ—ライãŒå«ã¾ã‚Œãªã„', async () => { + const aliceNote = await post(alice, { text: 'hi' }); + await post(bob, { text: '@alice hi', replyId: aliceNote.id }); + await post(carol, { text: '@alice hi', replyId: aliceNote.id }); + + const res = await api('/i/notifications', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒªãƒ—ライãŒå«ã¾ã‚Œãªã„', async () => { + await post(alice, { text: 'hi' }); + await post(bob, { text: '@alice hi' }); + await post(carol, { text: '@alice hi' }); + + const res = await api('/i/notifications', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®å¼•用リノートãŒå«ã¾ã‚Œãªã„', async () => { + const aliceNote = await post(alice, { text: 'hi' }); + await post(bob, { text: 'hi', renoteId: aliceNote.id }); + await post(carol, { text: 'hi', renoteId: aliceNote.id }); + + const res = await api('/i/notifications', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => { + const aliceNote = await post(alice, { text: 'hi' }); + await post(bob, { renoteId: aliceNote.id }); + await post(carol, { renoteId: aliceNote.id }); + + const res = await api('/i/notifications', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒ¼é€šçŸ¥ãŒå«ã¾ã‚Œãªã„', async () => { + await api('/i/follow', { userId: alice.id }, bob); + await api('/i/follow', { userId: alice.id }, carol); + + const res = await api('/i/notifications', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒå«ã¾ã‚Œãªã„', async () => { + await api('/i/update/', { isLocked: true }, alice); + await api('/following/create', { userId: alice.id }, bob); + await api('/following/create', { userId: alice.id }, carol); + + const res = await api('/i/notifications', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + }); + + describe('Notification (Grouped)', () => { + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã®é€šçŸ¥ãŒå«ã¾ã‚Œãªã„(リアクション)', async () => { + const aliceNote = await post(alice, { text: 'hi' }); + await react(bob, aliceNote, 'like'); + await react(carol, aliceNote, 'like'); + + const res = await api('/i/notifications-grouped', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒªãƒ—ライãŒå«ã¾ã‚Œãªã„', async () => { + const aliceNote = await post(alice, { text: 'hi' }); + await post(bob, { text: '@alice hi', replyId: aliceNote.id }); + await post(carol, { text: '@alice hi', replyId: aliceNote.id }); + + const res = await api('/i/notifications-grouped', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒªãƒ—ライãŒå«ã¾ã‚Œãªã„', async () => { + await post(alice, { text: 'hi' }); + await post(bob, { text: '@alice hi' }); + await post(carol, { text: '@alice hi' }); + + const res = await api('/i/notifications-grouped', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®å¼•用リノートãŒå«ã¾ã‚Œãªã„', async () => { + const aliceNote = await post(alice, { text: 'hi' }); + await post(bob, { text: 'hi', renoteId: aliceNote.id }); + await post(carol, { text: 'hi', renoteId: aliceNote.id }); + + const res = await api('/i/notifications-grouped', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => { + const aliceNote = await post(alice, { text: 'hi' }); + await post(bob, { renoteId: aliceNote.id }); + await post(carol, { renoteId: aliceNote.id }); + + const res = await api('/i/notifications-grouped', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒ¼é€šçŸ¥ãŒå«ã¾ã‚Œãªã„', async () => { + await api('/i/follow', { userId: alice.id }, bob); + await api('/i/follow', { userId: alice.id }, carol); + + const res = await api('/i/notifications-grouped', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒå«ã¾ã‚Œãªã„', async () => { + await api('/i/update/', { isLocked: true }, alice); + await api('/following/create', { userId: alice.id }, bob); + await api('/following/create', { userId: alice.id }, carol); + + const res = await api('/i/notifications-grouped', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); }); }); diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts index a5742d6e77..2406204f41 100644 --- a/packages/backend/test/e2e/note.ts +++ b/packages/backend/test/e2e/note.ts @@ -176,6 +176,87 @@ describe('Note', () => { assert.strictEqual(deleteRes.status, 204); }); + test('visibility: followersãªãƒŽãƒ¼ãƒˆã«å¯¾ã—ã¦ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã¯ãƒªãƒ—ライã§ãã‚‹', async () => { + await api('/following/create', { + userId: alice.id, + }, bob); + + const aliceNote = await api('/notes/create', { + text: 'direct note to bob', + visibility: 'followers', + }, alice); + + assert.strictEqual(aliceNote.status, 200); + + const replyId = aliceNote.body.createdNote.id; + const bobReply = await api('/notes/create', { + text: 'reply to alice note', + replyId, + }, bob); + + assert.strictEqual(bobReply.status, 200); + assert.strictEqual(bobReply.body.createdNote.replyId, replyId); + + await api('/following/delete', { + userId: alice.id, + }, bob); + }); + + test('visibility: followersãªãƒŽãƒ¼ãƒˆã«å¯¾ã—ã¦ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã§ãªã„ユーザーãŒãƒªãƒ—ライã—よã†ã¨ã™ã‚‹ã¨æ€’られる', async () => { + const aliceNote = await api('/notes/create', { + text: 'direct note to bob', + visibility: 'followers', + }, alice); + + assert.strictEqual(aliceNote.status, 200); + + const bobReply = await api('/notes/create', { + text: 'reply to alice note', + replyId: aliceNote.body.createdNote.id, + }, bob); + + assert.strictEqual(bobReply.status, 400); + assert.strictEqual(bobReply.body.error.code, 'CANNOT_REPLY_TO_AN_INVISIBLE_NOTE'); + }); + + test('visibility: specifiedãªãƒŽãƒ¼ãƒˆã«å¯¾ã—ã¦visibility: specifiedã§è¿”ä¿¡ã§ãã‚‹', async () => { + const aliceNote = await api('/notes/create', { + text: 'direct note to bob', + visibility: 'specified', + visibleUserIds: [bob.id], + }, alice); + + assert.strictEqual(aliceNote.status, 200); + + const bobReply = await api('/notes/create', { + text: 'reply to alice note', + replyId: aliceNote.body.createdNote.id, + visibility: 'specified', + visibleUserIds: [alice.id], + }, bob); + + assert.strictEqual(bobReply.status, 200); + }); + + test('visibility: specifiedãªãƒŽãƒ¼ãƒˆã«å¯¾ã—ã¦visibility: follwersã§è¿”ä¿¡ã—よã†ã¨ã™ã‚‹ã¨æ€’られる', async () => { + const aliceNote = await api('/notes/create', { + text: 'direct note to bob', + visibility: 'specified', + visibleUserIds: [bob.id], + }, alice); + + assert.strictEqual(aliceNote.status, 200); + + const bobReply = await api('/notes/create', { + text: 'reply to alice note with visibility: followers', + replyId: aliceNote.body.createdNote.id, + visibility: 'followers', + }, bob); + + assert.strictEqual(bobReply.status, 400); + assert.strictEqual(bobReply.body.error.code, 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY'); + }); + test('æ–‡å—æ•°ãŽã‚ŠãŽã‚Šã§æ€’られãªã„', async () => { const post = { text: '!'.repeat(MAX_NOTE_TEXT_LENGTH), // 3000æ–‡å— @@ -680,6 +761,171 @@ describe('Note', () => { assert.strictEqual(note1.status, 400); }); + + test('ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ã®æ•°ãŒä¸Šé™ã‚’è¶…ãˆã‚‹ã¨ã‚¨ãƒ©ãƒ¼ã«ãªã‚‹', async () => { + const res = await api('admin/roles/create', { + name: 'test', + description: '', + color: null, + iconUrl: null, + displayOrder: 0, + target: 'manual', + condFormula: {}, + isAdministrator: false, + isModerator: false, + isPublic: false, + isExplorable: false, + asBadge: false, + canEditMembersByModerator: false, + policies: { + mentionLimit: { + useDefault: false, + priority: 1, + value: 0, + }, + }, + }, alice); + + assert.strictEqual(res.status, 200); + + await new Promise(x => setTimeout(x, 2)); + + const assign = await api('admin/roles/assign', { + userId: alice.id, + roleId: res.body.id, + }, alice); + + assert.strictEqual(assign.status, 204); + + await new Promise(x => setTimeout(x, 2)); + + const note = await api('/notes/create', { + text: '@bob potentially annoying text', + }, alice); + + assert.strictEqual(note.status, 400); + assert.strictEqual(note.body.error.code, 'CONTAINS_TOO_MANY_MENTIONS'); + + await api('admin/roles/unassign', { + userId: alice.id, + roleId: res.body.id, + }); + + await api('admin/roles/delete', { + roleId: res.body.id, + }, alice); + }); + + test('ダイレクト投稿もエラーã«ãªã‚‹', async () => { + const res = await api('admin/roles/create', { + name: 'test', + description: '', + color: null, + iconUrl: null, + displayOrder: 0, + target: 'manual', + condFormula: {}, + isAdministrator: false, + isModerator: false, + isPublic: false, + isExplorable: false, + asBadge: false, + canEditMembersByModerator: false, + policies: { + mentionLimit: { + useDefault: false, + priority: 1, + value: 0, + }, + }, + }, alice); + + assert.strictEqual(res.status, 200); + + await new Promise(x => setTimeout(x, 2)); + + const assign = await api('admin/roles/assign', { + userId: alice.id, + roleId: res.body.id, + }, alice); + + assert.strictEqual(assign.status, 204); + + await new Promise(x => setTimeout(x, 2)); + + const note = await api('/notes/create', { + text: 'potentially annoying text', + visibility: 'specified', + visibleUserIds: [ bob.id ], + }, alice); + + assert.strictEqual(note.status, 400); + assert.strictEqual(note.body.error.code, 'CONTAINS_TOO_MANY_MENTIONS'); + + await api('admin/roles/unassign', { + userId: alice.id, + roleId: res.body.id, + }); + + await api('admin/roles/delete', { + roleId: res.body.id, + }, alice); + }); + + test('ダイレクトã®å®›å…ˆã¨ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ãŒåŒã˜å ´åˆã¯é‡è¤‡ã—ã¦ã‚«ã‚¦ãƒ³ãƒˆã—ãªã„', async () => { + const res = await api('admin/roles/create', { + name: 'test', + description: '', + color: null, + iconUrl: null, + displayOrder: 0, + target: 'manual', + condFormula: {}, + isAdministrator: false, + isModerator: false, + isPublic: false, + isExplorable: false, + asBadge: false, + canEditMembersByModerator: false, + policies: { + mentionLimit: { + useDefault: false, + priority: 1, + value: 1, + }, + }, + }, alice); + + assert.strictEqual(res.status, 200); + + await new Promise(x => setTimeout(x, 2)); + + const assign = await api('admin/roles/assign', { + userId: alice.id, + roleId: res.body.id, + }, alice); + + assert.strictEqual(assign.status, 204); + + await new Promise(x => setTimeout(x, 2)); + + const note = await api('/notes/create', { + text: '@bob potentially annoying text', + visibility: 'specified', + visibleUserIds: [ bob.id ], + }, alice); + + assert.strictEqual(note.status, 200); + + await api('admin/roles/unassign', { + userId: alice.id, + roleId: res.body.id, + }); + + await api('admin/roles/delete', { + roleId: res.body.id, + }, alice); + }); }); describe('notes/delete', () => { diff --git a/packages/backend/test/e2e/streaming.ts b/packages/backend/test/e2e/streaming.ts index 071daa275f..57ce73ba60 100644 --- a/packages/backend/test/e2e/streaming.ts +++ b/packages/backend/test/e2e/streaming.ts @@ -40,9 +40,9 @@ describe('Streaming', () => { let chinatsu: misskey.entities.SignupResponse; let takumi: misskey.entities.SignupResponse; - let kyokoNote: any; - let kanakoNote: any; - let takumiNote: any; + let kyokoNote: misskey.entities.Note; + let kanakoNote: misskey.entities.Note; + let takumiNote: misskey.entities.Note; let list: any; beforeAll(async () => { @@ -68,6 +68,9 @@ describe('Streaming', () => { // Follow: ayano => akari await follow(ayano, akari); + // Follow: kyoko => chitose + await api('following/create', { userId: chitose.id }, kyoko); + // Mute: chitose => kanako await api('mute/create', { userId: kanako.id }, chitose); @@ -170,7 +173,28 @@ describe('Streaming', () => { */ test('フォãƒãƒ¼ã—ã¦ã„るユーザーã®ãƒ•ã‚©ãƒãƒ¼ã—ã¦ã„ãªã„ユーザー㮠visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒæµã‚Œãªã„', async () => { - // TODO + const chitoseNote = await post(chitose, { text: 'followers-only post', visibility: 'followers' }); + + const fired = await waitFire( + ayano, 'homeTimeline', // ayano:home + () => api('notes/create', { text: 'reply to chitose\'s followers-only post', replyId: chitoseNote.id }, kyoko), // kyoko's reply to chitose's followers-only post + msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko + ); + + assert.strictEqual(fired, false); + }); + + test('フォãƒãƒ¼ã—ã¦ã„るユーザーã®ãƒ•ã‚©ãƒãƒ¼ã—ã¦ã„ãªã„ユーザー㮠visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ã®ãƒªãƒŽãƒ¼ãƒˆãŒæµã‚Œãªã„', async () => { + const chitoseNote = await post(chitose, { text: 'followers-only post', visibility: 'followers' }); + const kyokoReply = await post(kyoko, { text: 'reply to followers-only post', replyId: chitoseNote.id }); + + const fired = await waitFire( + ayano, 'homeTimeline', // ayano:home + () => api('notes/create', { renoteId: kyokoReply.id }, kyoko), // kyoko's renote of kyoko's reply to chitose's followers-only post + msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko + ); + + assert.strictEqual(fired, false); }); test('フォãƒãƒ¼ã—ã¦ã„ãªã„ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æŠ•ç¨¿ã¯æµã‚Œãªã„', async () => { @@ -202,6 +226,79 @@ describe('Streaming', () => { assert.strictEqual(fired, false); }); + + /** + * TODO: è½ã¡ã‚‹ + * @see https://github.com/misskey-dev/misskey/issues/13474 + test('visibility: specified ãªãƒŽãƒ¼ãƒˆã§ visibleUserIds ã«è‡ªåˆ†ãŒå«ã¾ã‚Œã¦ã„ã‚‹ã¨ããã®ãƒŽãƒ¼ãƒˆã¸ã®ãƒªãƒ—ãƒ©ã‚¤ãŒæµã‚Œã¦ãã‚‹', async () => { + const chitoseToKyokoAndAyano = await post(chitose, { text: 'direct note from chitose to kyoko and ayano', visibility: 'specified', visibleUserIds: [kyoko.id, ayano.id] }); + + const fired = await waitFire( + ayano, 'homeTimeline', // ayano:home + () => api('notes/create', { text: 'direct reply from kyoko to chitose and ayano', replyId: chitoseToKyokoAndAyano.id, visibility: 'specified', visibleUserIds: [chitose.id, ayano.id] }, kyoko), + msg => msg.type === 'note' && msg.body.userId === kyoko.id, + ); + + assert.strictEqual(fired, true); + }); + */ + + test('visibility: specified ãªæŠ•ç¨¿ã«å¯¾ã™ã‚‹ãƒªãƒ—ライ㧠visibleUserIds ãŒæ‹¡å¼µã•れãŸã¨ãã€ãã®æ‹¡å¼µã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã® HTL ã«ã¯ãã®ãƒªãƒ—ãƒ©ã‚¤ãŒæµã‚Œãªã„', async () => { + const chitoseToKyoko = await post(chitose, { text: 'direct note from chitose to kyoko', visibility: 'specified', visibleUserIds: [kyoko.id] }); + + const fired = await waitFire( + ayano, 'homeTimeline', // ayano:home + () => api('notes/create', { text: 'direct reply from kyoko to chitose and ayano', replyId: chitoseToKyoko.id, visibility: 'specified', visibleUserIds: [chitose.id, ayano.id] }, kyoko), + msg => msg.type === 'note' && msg.body.userId === kyoko.id, + ); + + assert.strictEqual(fired, false); + }); + + test('visibility: specified ãªæŠ•ç¨¿ã«å¯¾ã™ã‚‹ãƒªãƒ—ライ㧠visibleUserIds ãŒåŽç¸®ã•れãŸã¨ãã€ãã®åŽç¸®ã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã® HTL ã«ã¯ãã®ãƒªãƒ—ãƒ©ã‚¤ãŒæµã‚Œãªã„', async () => { + const chitoseToKyokoAndAyano = await post(chitose, { text: 'direct note from chitose to kyoko and ayano', visibility: 'specified', visibleUserIds: [kyoko.id, ayano.id] }); + + const fired = await waitFire( + ayano, 'homeTimeline', // ayano:home + () => api('notes/create', { text: 'direct reply from kyoko to chitose', replyId: chitoseToKyokoAndAyano.id, visibility: 'specified', visibleUserIds: [chitose.id] }, kyoko), + msg => msg.type === 'note' && msg.body.userId === kyoko.id, + ); + + assert.strictEqual(fired, false); + }); + + test('withRenotes: false ã®ã¨ããƒªãƒŽãƒ¼ãƒˆãŒæµã‚Œãªã„', async () => { + const fired = await waitFire( + ayano, 'homeTimeline', // ayano:home + () => api('notes/create', { renoteId: kyokoNote.id }, kyoko), // kyoko renote + msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko + { withRenotes: false }, + ); + + assert.strictEqual(fired, false); + }); + + test('withRenotes: false ã®ã¨ãå¼•ç”¨ãƒªãƒŽãƒ¼ãƒˆãŒæµã‚Œã‚‹', async () => { + const fired = await waitFire( + ayano, 'homeTimeline', // ayano:home + () => api('notes/create', { text: 'quote', renoteId: kyokoNote.id }, kyoko), // kyoko quote + msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko + { withRenotes: false }, + ); + + assert.strictEqual(fired, true); + }); + + test('withRenotes: false ã®ã¨ã投票ã®ã¿ã®ãƒªãƒŽãƒ¼ãƒˆãŒæµã‚Œã‚‹', async () => { + const fired = await waitFire( + ayano, 'homeTimeline', // ayano:home + () => api('notes/create', { poll: { choices: ['kinoko', 'takenoko'] }, renoteId: kyokoNote.id }, kyoko), // kyoko renote with poll + msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko + { withRenotes: false }, + ); + + assert.strictEqual(fired, true); + }); }); // Home describe('Local Timeline', () => { diff --git a/packages/backend/test/unit/ReactionService.ts b/packages/backend/test/unit/ReactionService.ts index d1c31cac3a..1957f4544c 100644 --- a/packages/backend/test/unit/ReactionService.ts +++ b/packages/backend/test/unit/ReactionService.ts @@ -90,4 +90,45 @@ describe('ReactionService', () => { assert.strictEqual(await reactionService.normalize('unknown'), 'â¤'); }); }); + + describe('convertLegacyReactions', () => { + test('空ã®å…¥åŠ›ã«å¯¾ã—ã¦ã¯ä½•ã‚‚ã—ãªã„', () => { + const input = {}; + assert.deepStrictEqual(reactionService.convertLegacyReactions(input), input); + }); + + test('Unicode絵文å—リアクションを変æ›ã—ã¦ã—ã¾ã‚ãªã„', () => { + const input = { 'ðŸ‘': 1, 'ðŸ®': 2 }; + assert.deepStrictEqual(reactionService.convertLegacyReactions(input), input); + }); + + test('カスタム絵文å—リアクションを変æ›ã—ã¦ã—ã¾ã‚ãªã„', () => { + const input = { ':like@.:': 1, ':pudding@example.tld:': 2 }; + assert.deepStrictEqual(reactionService.convertLegacyReactions(input), input); + }); + + test('æ–‡å—列ã«ã‚ˆã‚‹ãƒ¬ã‚¬ã‚·ãƒ¼ãªãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’変æ›ã™ã‚‹', () => { + const input = { 'like': 1, 'pudding': 2 }; + const output = { 'ðŸ‘': 1, 'ðŸ®': 2 }; + assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output); + }); + + test('host部分ãŒçœç•¥ã•れãŸãƒ¬ã‚¬ã‚·ãƒ¼ãªã‚«ã‚¹ã‚¿ãƒ 絵文å—リアクションを変æ›ã™ã‚‹', () => { + const input = { ':custom_emoji:': 1 }; + const output = { ':custom_emoji@.:': 1 }; + assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output); + }); + + test('「0個ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã€æƒ…å ±ã‚’å‰Šé™¤ã™ã‚‹', () => { + const input = { 'angry': 0 }; + const output = {}; + assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output); + }); + + test('hostéƒ¨åˆ†ã®æœ‰ç„¡ã«ã‚ˆã‚Šãƒ‡ã‚³ãƒ¼ãƒ‰ã™ã‚‹ã¨åŒã˜è¡¨è¨˜ã«ãªã‚‹ã‚«ã‚¹ã‚¿ãƒ 絵文å—リアクションã®å€‹æ•°æƒ…å ±ã‚’æ£ã—ãè¶³ã—åˆã‚ã›ã‚‹', () => { + const input = { ':custom_emoji:': 1, ':custom_emoji@.:': 2 }; + const output = { ':custom_emoji@.:': 3 }; + assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output); + }); + }); }); diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts index 5222745b7f..fe5ad31597 100644 --- a/packages/backend/test/unit/RoleService.ts +++ b/packages/backend/test/unit/RoleService.ts @@ -251,6 +251,34 @@ describe('RoleService', () => { expect(user2Policies.canManageCustomEmojis).toBe(true); }); + test('コンディショナルãƒãƒ¼ãƒ«: マニュアルãƒãƒ¼ãƒ«ã«ã‚¢ã‚µã‚¤ãƒ³æ¸ˆã¿', async () => { + const [user1, user2, role1] = await Promise.all([ + createUser(), + createUser(), + createRole({ + name: 'manual role', + }), + ]); + const role2 = await createRole({ + name: 'conditional role', + target: 'conditional', + condFormula: { + // idã¯ãƒãƒƒã‚¯ã‚¨ãƒ³ãƒ‰ã®ãƒã‚¸ãƒƒã‚¯ã«å¿…è¦ãªã„? + id: 'bdc612bd-9d54-4675-ae83-0499c82ea670', + type: 'roleAssignedTo', + roleId: role1.id, + }, + }); + await roleService.assign(user2.id, role1.id); + + const [u1role, u2role] = await Promise.all([ + roleService.getUserRoles(user1.id), + roleService.getUserRoles(user2.id), + ]); + expect(u1role.some(r => r.id === role2.id)).toBe(false); + expect(u2role.some(r => r.id === role2.id)).toBe(true); + }); + test('expired role', async () => { const user = await createUser(); const role = await createRole({ diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index a2220ffae6..cd5dddd68d 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -355,7 +355,7 @@ export const uploadUrl = async (user: UserToken, url: string): Promise<Packed<'D return catcher; }; -export function connectStream(user: UserToken, channel: string, listener: (message: Record<string, any>) => any, params?: any): Promise<WebSocket> { +export function connectStream<C extends keyof misskey.Channels>(user: UserToken, channel: C, listener: (message: Record<string, any>) => any, params?: misskey.Channels[C]['params']): Promise<WebSocket> { return new Promise((res, rej) => { const url = new URL(`ws://127.0.0.1:${port}/streaming`); const options: ClientOptions = {}; @@ -390,7 +390,7 @@ export function connectStream(user: UserToken, channel: string, listener: (messa }); } -export const waitFire = async (user: UserToken, channel: string, trgr: () => any, cond: (msg: Record<string, any>) => boolean, params?: any) => { +export const waitFire = async <C extends keyof misskey.Channels>(user: UserToken, channel: C, trgr: () => any, cond: (msg: Record<string, any>) => boolean, params?: misskey.Channels[C]['params']) => { return new Promise<boolean>(async (res, rej) => { let timer: NodeJS.Timeout | null = null; @@ -435,7 +435,7 @@ export const waitFire = async (user: UserToken, channel: string, trgr: () => any */ export function makeStreamCatcher<T>( user: UserToken, - channel: string, + channel: keyof misskey.Channels, cond: (message: Record<string, any>) => boolean, extractor: (message: Record<string, any>) => T, timeout = 60 * 1000): Promise<T> { diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx index 76c5b6be4b..1e925aede6 100644 --- a/packages/frontend/.storybook/generate.tsx +++ b/packages/frontend/.storybook/generate.tsx @@ -401,7 +401,8 @@ function toStories(component: string): Promise<string> { // glob('src/{components,pages,ui,widgets}/**/*.vue') (async () => { const globs = await Promise.all([ - glob('src/components/global/*.vue'), + glob('src/components/global/Mk*.vue'), + glob('src/components/global/RouterView.vue'), glob('src/components/Mk{A,B}*.vue'), glob('src/components/MkDigitalClock.vue'), glob('src/components/MkGalleryPostPreview.vue'), diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 91a391ac08..682def8e8d 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -27,19 +27,19 @@ "@syuilo/aiscript": "0.17.0", "@tabler/icons-webfont": "2.44.0", "@twemoji/parser": "15.0.0", - "@vitejs/plugin-vue": "5.0.3", - "@vue/compiler-sfc": "3.4.18", + "@vitejs/plugin-vue": "5.0.4", + "@vue/compiler-sfc": "3.4.21", "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2", "astring": "1.8.6", "broadcast-channel": "7.0.0", "buraha": "0.0.1", - "canvas-confetti": "1.6.1", - "chart.js": "4.4.1", + "canvas-confetti": "1.9.2", + "chart.js": "4.4.2", "chartjs-adapter-date-fns": "3.0.0", "chartjs-chart-matrix": "2.0.1", "chartjs-plugin-gradient": "0.6.1", "chartjs-plugin-zoom": "2.0.1", - "chromatic": "10.6.1", + "chromatic": "11.0.0", "compare-versions": "6.1.0", "cropperjs": "2.0.0-beta.4", "date-fns": "2.30.0", @@ -57,79 +57,79 @@ "misskey-reversi": "workspace:*", "photoswipe": "5.4.3", "punycode": "2.3.1", - "rollup": "4.9.6", - "sanitize-html": "2.11.0", - "sass": "1.70.0", - "shiki": "1.0.0-beta.3", + "rollup": "4.12.0", + "sanitize-html": "2.12.1", + "sass": "1.71.1", + "shiki": "1.1.7", "strict-event-emitter-types": "2.0.0", "textarea-caret": "3.1.0", - "three": "0.160.1", + "three": "0.162.0", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tsc-alias": "1.8.8", "tsconfig-paths": "4.2.0", "typescript": "5.3.3", "uuid": "9.0.1", - "v-code-diff": "1.7.2", - "vite": "5.1.0", - "vue": "3.4.18", + "v-code-diff": "1.9.0", + "vite": "5.1.4", + "vue": "3.4.21", "vuedraggable": "next" }, "devDependencies": { "@misskey-dev/eslint-plugin": "1.0.0", "@misskey-dev/summaly": "5.0.3", - "@storybook/addon-actions": "8.0.0-beta.2", - "@storybook/addon-essentials": "8.0.0-beta.2", - "@storybook/addon-interactions": "8.0.0-beta.2", - "@storybook/addon-links": "8.0.0-beta.2", - "@storybook/addon-mdx-gfm": "8.0.0-beta.2", - "@storybook/addon-storysource": "8.0.0-beta.2", - "@storybook/blocks": "8.0.0-beta.2", - "@storybook/components": "8.0.0-beta.2", - "@storybook/core-events": "8.0.0-beta.2", - "@storybook/manager-api": "8.0.0-beta.2", - "@storybook/preview-api": "8.0.0-beta.2", - "@storybook/react": "8.0.0-beta.2", - "@storybook/react-vite": "8.0.0-beta.2", - "@storybook/test": "8.0.0-beta.2", - "@storybook/theming": "8.0.0-beta.2", - "@storybook/types": "8.0.0-beta.2", - "@storybook/vue3": "8.0.0-beta.2", - "@storybook/vue3-vite": "8.0.0-beta.2", + "@storybook/addon-actions": "8.0.0-beta.6", + "@storybook/addon-essentials": "8.0.0-beta.6", + "@storybook/addon-interactions": "8.0.0-beta.6", + "@storybook/addon-links": "8.0.0-beta.6", + "@storybook/addon-mdx-gfm": "8.0.0-beta.6", + "@storybook/addon-storysource": "8.0.0-beta.6", + "@storybook/blocks": "8.0.0-beta.6", + "@storybook/components": "8.0.0-beta.6", + "@storybook/core-events": "8.0.0-beta.6", + "@storybook/manager-api": "8.0.0-beta.6", + "@storybook/preview-api": "8.0.0-beta.6", + "@storybook/react": "8.0.0-beta.6", + "@storybook/react-vite": "8.0.0-beta.6", + "@storybook/test": "8.0.0-beta.6", + "@storybook/theming": "8.0.0-beta.6", + "@storybook/types": "8.0.0-beta.6", + "@storybook/vue3": "8.0.0-beta.6", + "@storybook/vue3-vite": "8.0.0-beta.6", "@testing-library/vue": "8.0.2", "@types/escape-regexp": "0.0.3", "@types/estree": "1.0.5", "@types/matter-js": "0.19.6", "@types/micromatch": "4.0.6", - "@types/node": "20.11.17", - "@types/punycode": "2.1.3", - "@types/sanitize-html": "2.9.5", + "@types/node": "20.11.22", + "@types/punycode": "2.1.4", + "@types/sanitize-html": "2.11.0", "@types/throttle-debounce": "5.0.2", "@types/tinycolor2": "1.4.6", "@types/uuid": "9.0.8", "@types/ws": "8.5.10", - "@typescript-eslint/eslint-plugin": "6.18.1", - "@typescript-eslint/parser": "6.18.1", + "@typescript-eslint/eslint-plugin": "7.1.0", + "@typescript-eslint/parser": "7.1.0", "@vitest/coverage-v8": "0.34.6", - "@vue/runtime-core": "3.4.18", + "@vue/runtime-core": "3.4.21", "acorn": "8.11.3", "cross-env": "7.0.3", - "cypress": "13.6.4", - "eslint": "8.56.0", + "cypress": "13.6.6", + "eslint": "8.57.0", "eslint-plugin-import": "2.29.1", - "eslint-plugin-vue": "9.20.1", + "eslint-plugin-vue": "9.22.0", "fast-glob": "3.3.2", - "happy-dom": "10.0.3", + "happy-dom": "13.6.2", "intersection-observer": "0.12.2", "micromatch": "4.0.5", "msw": "2.1.7", "msw-storybook-addon": "2.0.0-beta.1", - "nodemon": "3.0.3", + "nodemon": "3.1.0", "prettier": "3.2.5", "react": "18.2.0", "react-dom": "18.2.0", "start-server-and-test": "2.0.3", - "storybook": "8.0.0-beta.2", + "storybook": "8.0.0-beta.6", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "vite-plugin-turbosnap": "1.0.3", "vitest": "0.34.6", diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts index e606fe368c..7f20e0b1a2 100644 --- a/packages/frontend/src/account.ts +++ b/packages/frontend/src/account.ts @@ -290,7 +290,7 @@ export async function openAccountMenu(opts: { text: i18n.ts.profile, to: `/@${ $i.username }`, avatar: $i, - }, { type: 'divider' }, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, { + }, { type: 'divider' as const }, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, { type: 'parent' as const, icon: 'ti ti-plus', text: i18n.ts.addAccount, diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index b19d45a35e..61f04678bf 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -11,7 +11,7 @@ import { alert, confirm, popup, post, toast } from '@/os.js'; import { useStream } from '@/stream.js'; import * as sound from '@/scripts/sound.js'; import { $i, signout, updateAccount } from '@/account.js'; -import { fetchInstance, instance } from '@/instance.js'; +import { instance } from '@/instance.js'; import { ColdDeviceStorage, defaultStore } from '@/store.js'; import { makeHotkey } from '@/scripts/hotkey.js'; import { reactionPicker } from '@/scripts/reaction-picker.js'; @@ -235,12 +235,10 @@ export async function mainBoot() { } } - fetchInstance().then(() => { - const modifiedVersionMustProminentlyOfferInAgplV3Section13Read = miLocalStorage.getItem('modifiedVersionMustProminentlyOfferInAgplV3Section13Read'); - if (modifiedVersionMustProminentlyOfferInAgplV3Section13Read !== 'true' && instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey') { - popup(defineAsyncComponent(() => import('@/components/MkSourceCodeAvailablePopup.vue')), {}, {}, 'closed'); - } - }); + const modifiedVersionMustProminentlyOfferInAgplV3Section13Read = miLocalStorage.getItem('modifiedVersionMustProminentlyOfferInAgplV3Section13Read'); + if (modifiedVersionMustProminentlyOfferInAgplV3Section13Read !== 'true' && instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey') { + popup(defineAsyncComponent(() => import('@/components/MkSourceCodeAvailablePopup.vue')), {}, {}, 'closed'); + } if ('Notification' in window) { // 許å¯ã‚’å¾—ã¦ã„ãªã‹ã£ãŸã‚‰ãƒªã‚¯ã‚¨ã‚¹ãƒˆ diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 412325bfee..cae6bc7111 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -57,18 +57,7 @@ import { i18n } from '@/i18n.js'; import { miLocalStorage } from '@/local-storage.js'; import { customEmojis } from '@/custom-emojis.js'; import { MFM_TAGS, MFM_PARAMS } from '@/const.js'; - -type EmojiDef = { - emoji: string; - name: string; - url: string; - aliasOf?: string; -} | { - emoji: string; - name: string; - aliasOf?: string; - isCustomEmoji?: true; -}; +import { searchEmoji, EmojiDef } from '@/scripts/search-emoji.js'; const lib = emojilist.filter(x => x.category !== 'flags'); @@ -249,7 +238,7 @@ function exec() { return; } - emojis.value = emojiAutoComplete(props.q, emojiDb.value); + emojis.value = searchEmoji(props.q, emojiDb.value); } else if (props.type === 'mfmTag') { if (!props.q || props.q === '') { mfmTags.value = MFM_TAGS; @@ -267,87 +256,6 @@ function exec() { } } -type EmojiScore = { emoji: EmojiDef, score: number }; - -function emojiAutoComplete(query: string | null, emojiDb: EmojiDef[], max = 30): EmojiDef[] { - if (!query) { - return []; - } - - const matched = new Map<string, EmojiScore>(); - // 完全一致(エイリアス込ã¿ï¼‰ - emojiDb.some(x => { - if (x.name === query && !matched.has(x.aliasOf ?? x.name)) { - matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length + 2 }); - } - return matched.size === max; - }); - - // 剿–¹ä¸€è‡´ï¼ˆã‚¨ã‚¤ãƒªã‚¢ã‚¹ãªã—) - if (matched.size < max) { - emojiDb.some(x => { - if (x.name.startsWith(query) && !x.aliasOf) { - matched.set(x.name, { emoji: x, score: query.length + 1 }); - } - return matched.size === max; - }); - } - - // 剿–¹ä¸€è‡´ï¼ˆã‚¨ã‚¤ãƒªã‚¢ã‚¹è¾¼ã¿ï¼‰ - if (matched.size < max) { - emojiDb.some(x => { - if (x.name.startsWith(query) && !matched.has(x.aliasOf ?? x.name)) { - matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length }); - } - return matched.size === max; - }); - } - - // 部分一致(エイリアス込ã¿ï¼‰ - if (matched.size < max) { - emojiDb.some(x => { - if (x.name.includes(query) && !matched.has(x.aliasOf ?? x.name)) { - matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length - 1 }); - } - return matched.size === max; - }); - } - - // 簡易ã‚ã„ã¾ã„検索(3æ–‡å—以上) - if (matched.size < max && query.length > 3) { - const queryChars = [...query]; - const hitEmojis = new Map<string, EmojiScore>(); - - for (const x of emojiDb) { - // æ–‡å—列ã®ä½ç½®ã‚’進ã‚ãªãŒã‚‰ã€ã‚¯ã‚¨ãƒªã®æ–‡å—ã‚’é †ç•ªã«æŽ¢ã™ - - let pos = 0; - let hit = 0; - for (const c of queryChars) { - pos = x.name.indexOf(c, pos); - if (pos <= -1) break; - hit++; - } - - // åŠåˆ†ä»¥ä¸Šã®æ–‡å—ãŒå«ã¾ã‚Œã¦ã„れã°ãƒ’ットã¨ã™ã‚‹ - if (hit > Math.ceil(queryChars.length / 2) && hit - 2 > (matched.get(x.aliasOf ?? x.name)?.score ?? 0)) { - hitEmojis.set(x.aliasOf ?? x.name, { emoji: x, score: hit - 2 }); - } - } - - // ヒットã—ãŸã‚‚ã®ã‚’å…¨éƒ¨è¿½åŠ ã™ã‚‹ã¨é›‘多ã«ãªã‚‹ã®ã§ã€å…ˆé ã®6件程度ã ã‘ã«ã—ã¦ãŠã(6ä»¶ï¼ã‚ªãƒ¼ãƒˆã‚³ãƒ³ãƒ—リートã®ãƒãƒƒãƒ—アップã®ã‚µã‚¤ã‚ºåˆ†ï¼‰ - [...hitEmojis.values()] - .sort((x, y) => y.score - x.score) - .slice(0, 6) - .forEach(it => matched.set(it.emoji.name, it)); - } - - return [...matched.values()] - .sort((x, y) => y.score - x.score) - .slice(0, max) - .map(it => it.emoji); -} - function onMousedown(event: Event) { if (!contains(rootEl.value, event.target) && (rootEl.value !== event.target)) props.close(); } diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue index dd745c2140..04b6d2f29c 100644 --- a/packages/frontend/src/components/MkChart.vue +++ b/packages/frontend/src/components/MkChart.vue @@ -240,7 +240,7 @@ const render = () => { }, external: externalTooltipHandler, callbacks: { - label: (item) => chartData?.bytes ? bytes(item.parsed.y * 1000, 1) : item.parsed.y.toString(), + label: (item) => `${item.dataset.label}: ${chartData?.bytes ? bytes(item.parsed.y * 1000, 1) : item.parsed.y.toString()}`, }, }, zoom: props.detailed ? { diff --git a/packages/frontend/src/components/MkCode.core.vue b/packages/frontend/src/components/MkCode.core.vue index f993e983e4..872517b6aa 100644 --- a/packages/frontend/src/components/MkCode.core.vue +++ b/packages/frontend/src/components/MkCode.core.vue @@ -52,7 +52,7 @@ async function fetchLanguage(to: string): Promise<void> { return bundle.id === language || bundle.aliases?.includes(language); }); if (bundles.length > 0) { - console.log(`Loading language: ${language}`); + if (_DEV_) console.log(`Loading language: ${language}`); await highlighter.loadLanguage(bundles[0].import); codeLang.value = language; } else { diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue index 4b7584faaa..4577d37c08 100644 --- a/packages/frontend/src/components/MkDialog.vue +++ b/packages/frontend/src/components/MkDialog.vue @@ -38,11 +38,6 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-if="select.items"> <option v-for="item in select.items" :value="item.value">{{ item.text }}</option> </template> - <template v-else> - <optgroup v-for="groupedItem in select.groupedItems" :label="groupedItem.label"> - <option v-for="item in groupedItem.items" :value="item.value">{{ item.text }}</option> - </optgroup> - </template> </MkSelect> <div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons"> <MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabledReason" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton> @@ -64,7 +59,7 @@ import MkSelect from '@/components/MkSelect.vue'; import { i18n } from '@/i18n.js'; type Input = { - type: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local'; + type?: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local'; placeholder?: string | null; autocomplete?: string; default: string | number | null; @@ -74,22 +69,17 @@ type Input = { type Select = { items: { - value: string; + value: any; text: string; }[]; - groupedItems: { - label: string; - items: { - value: string; - text: string; - }[]; - }[]; default: string | null; }; +type Result = string | number | true | null; + const props = withDefaults(defineProps<{ type?: 'success' | 'error' | 'warning' | 'info' | 'question' | 'waiting'; - title: string; + title?: string; text?: string; input?: Input; select?: Select; @@ -113,7 +103,7 @@ const props = withDefaults(defineProps<{ }); const emit = defineEmits<{ - (ev: 'done', v: { canceled: boolean; result: any }): void; + (ev: 'done', v: { canceled: true } | { canceled: false, result: Result }): void; (ev: 'closed'): void; }>(); @@ -139,8 +129,11 @@ const okButtonDisabledReason = computed<null | 'charactersExceeded' | 'character return null; }); -function done(canceled: boolean, result?) { - emit('done', { canceled, result }); +// overload function を使ã„ãŸã„ã®ã§ lint エラーを無視ã™ã‚‹ +function done(canceled: true): void; +function done(canceled: false, result: Result): void; // eslint-disable-line no-redeclare +function done(canceled: boolean, result?: Result): void { // eslint-disable-line no-redeclare + emit('done', { canceled, result } as { canceled: true } | { canceled: false, result: Result }); modal.value?.close(); } diff --git a/packages/frontend/src/components/MkDriveSelectDialog.vue b/packages/frontend/src/components/MkDriveSelectDialog.vue index 77b5532f79..f1ecc27123 100644 --- a/packages/frontend/src/components/MkDriveSelectDialog.vue +++ b/packages/frontend/src/components/MkDriveSelectDialog.vue @@ -39,13 +39,13 @@ withDefaults(defineProps<{ }); const emit = defineEmits<{ - (ev: 'done', r?: Misskey.entities.DriveFile[]): void; + (ev: 'done', r?: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]): void; (ev: 'closed'): void; }>(); const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); -const selected = ref<Misskey.entities.DriveFile[]>([]); +const selected = ref<Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]>([]); function ok() { emit('done', selected.value); @@ -57,7 +57,7 @@ function cancel() { dialog.value?.close(); } -function onChangeSelection(files: Misskey.entities.DriveFile[]) { - selected.value = files; +function onChangeSelection(v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]) { + selected.value = v; } </script> diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue index 30ad2bcbbf..c295ab6bb7 100644 --- a/packages/frontend/src/components/MkEmojiPicker.section.vue +++ b/packages/frontend/src/components/MkEmojiPicker.section.vue @@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only :key="emoji" :data-emoji="emoji" class="_button item" + :disabled="disabledEmojis?.value.includes(emoji)" @pointerenter="computeButtonTitle" @click="emit('chosen', emoji, $event)" > @@ -48,6 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only :key="emoji" :data-emoji="emoji" class="_button item" + :disabled="disabledEmojis?.value.includes(emoji)" @pointerenter="computeButtonTitle" @click="emit('chosen', emoji, $event)" > @@ -67,6 +69,7 @@ import MkEmojiPickerSection from '@/components/MkEmojiPicker.section.vue'; const props = defineProps<{ emojis: string[] | Ref<string[]>; + disabledEmojis?: Ref<string[]>; initialShown?: boolean; hasChildSection?: boolean; customEmojiTree?: CustomEmojiFolderTree[]; diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 366273118b..06243e5b04 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -14,6 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only v-for="emoji in searchResultCustom" :key="emoji.name" class="_button item" + :disabled="!canReact(emoji)" :title="emoji.name" tabindex="0" @click="chosen(emoji, $event)" @@ -39,16 +40,17 @@ SPDX-License-Identifier: AGPL-3.0-only <section v-if="showPinned && (pinned && pinned.length > 0)"> <div class="body"> <button - v-for="emoji in pinned" - :key="emoji" - :data-emoji="emoji" + v-for="emoji in pinnedEmojisDef" + :key="getKey(emoji)" + :data-emoji="getKey(emoji)" class="_button item" + :disabled="!canReact(emoji)" tabindex="0" @pointerenter="computeButtonTitle" @click="chosen(emoji, $event)" > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + <MkCustomEmoji v-if="!emoji.hasOwnProperty('char')" class="emoji" :name="getKey(emoji)" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="getKey(emoji)" :normal="true"/> </button> </div> </section> @@ -57,15 +59,16 @@ SPDX-License-Identifier: AGPL-3.0-only <header class="_acrylic"><i class="ti ti-clock ti-fw"></i> {{ i18n.ts.recentUsed }}</header> <div class="body"> <button - v-for="emoji in recentlyUsedEmojis" - :key="emoji" + v-for="emoji in recentlyUsedEmojisDef" + :key="getKey(emoji)" class="_button item" - :data-emoji="emoji" + :disabled="!canReact(emoji)" + :data-emoji="getKey(emoji)" @pointerenter="computeButtonTitle" @click="chosen(emoji, $event)" > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + <MkCustomEmoji v-if="!emoji.hasOwnProperty('char')" class="emoji" :name="getKey(emoji)" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="getKey(emoji)" :normal="true"/> </button> </div> </section> @@ -76,7 +79,8 @@ SPDX-License-Identifier: AGPL-3.0-only v-for="child in customEmojiFolderRoot.children" :key="`custom:${child.value}`" :initialShown="false" - :emojis="computed(() => customEmojis.filter(e => child.value === '' ? (e.category === 'null' || !e.category) : e.category === child.value).filter(filterAvailable).map(e => `:${e.name}:`))" + :emojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value)).map(e => `:${e.name}:`))" + :disabledEmojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value)).filter(e => !canReact(e)).map(e => `:${e.name}:`))" :hasChildSection="child.children.length !== 0" :customEmojiTree="child.children" @chosen="chosen" @@ -109,6 +113,7 @@ import { unicodeEmojiCategories as categories, getEmojiName, CustomEmojiFolderTree, + getUnicodeEmoji, } from '@/scripts/emojilist.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import * as os from '@/os.js'; @@ -146,6 +151,13 @@ const { recentlyUsedEmojis, } = defaultStore.reactiveState; +const recentlyUsedEmojisDef = computed(() => { + return recentlyUsedEmojis.value.map(getDef); +}); +const pinnedEmojisDef = computed(() => { + return pinned.value?.map(getDef); +}); + const pinned = computed(() => props.pinnedEmojis); const size = computed(() => emojiPickerScale.value); const width = computed(() => emojiPickerWidth.value); @@ -337,14 +349,18 @@ watch(q, () => { return matches; }; - searchResultCustom.value = Array.from(searchCustom()).filter(filterAvailable); + searchResultCustom.value = Array.from(searchCustom()); searchResultUnicode.value = Array.from(searchUnicode()); }); -function filterAvailable(emoji: Misskey.entities.EmojiSimple): boolean { +function canReact(emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef): boolean { return !props.targetNote || checkReactionPermissions($i!, props.targetNote, emoji); } +function filterCategory(emoji: Misskey.entities.EmojiSimple, category: string): boolean { + return category === '' ? (emoji.category === 'null' || !emoji.category) : emoji.category === category; +} + function focus() { if (!['smartphone', 'tablet'].includes(deviceKind) && !isTouchUsing) { searchEl.value?.focus({ @@ -362,6 +378,14 @@ function getKey(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef): return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`; } +function getDef(emoji: string) { + if (emoji.includes(':')) { + return customEmojisMap.get(emoji.replace(/:/g, ''))!; + } else { + return getUnicodeEmoji(emoji)!; + } +} + /** @see MkEmojiPicker.section.vue */ function computeButtonTitle(ev: MouseEvent): void { const elm = ev.target as HTMLElement; @@ -526,6 +550,18 @@ defineExpose({ width: auto; height: auto; min-width: 0; + + &:disabled { + cursor: not-allowed; + background: linear-gradient(-45deg, transparent 0% 48%, var(--X6) 48% 52%, transparent 52% 100%); + opacity: 1; + + > .emoji { + filter: grayscale(1); + mix-blend-mode: exclusion; + opacity: 0.8; + } + } } } } @@ -548,6 +584,18 @@ defineExpose({ width: auto; height: auto; min-width: 0; + + &:disabled { + cursor: not-allowed; + background: linear-gradient(-45deg, transparent 0% 48%, var(--X6) 48% 52%, transparent 52% 100%); + opacity: 1; + + > .emoji { + filter: grayscale(1); + mix-blend-mode: exclusion; + opacity: 0.8; + } + } } } } @@ -663,6 +711,18 @@ defineExpose({ box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15); } + &:disabled { + cursor: not-allowed; + background: linear-gradient(-45deg, transparent 0% 48%, var(--X6) 48% 52%, transparent 52% 100%); + opacity: 1; + + > .emoji { + filter: grayscale(1); + mix-blend-mode: exclusion; + opacity: 0.8; + } + } + > .emoji { height: 1.25em; vertical-align: -.25em; diff --git a/packages/frontend/src/components/MkEmojiPickerDialog.vue b/packages/frontend/src/components/MkEmojiPickerDialog.vue index 59f4b51522..adcea839ee 100644 --- a/packages/frontend/src/components/MkEmojiPickerDialog.vue +++ b/packages/frontend/src/components/MkEmojiPickerDialog.vue @@ -56,7 +56,7 @@ const props = withDefaults(defineProps<{ }); const emit = defineEmits<{ - (ev: 'done', v: any): void; + (ev: 'done', v: string): void; (ev: 'close'): void; (ev: 'closed'): void; }>(); @@ -64,7 +64,7 @@ const emit = defineEmits<{ const modal = shallowRef<InstanceType<typeof MkModal>>(); const picker = shallowRef<InstanceType<typeof MkEmojiPicker>>(); -function chosen(emoji: any) { +function chosen(emoji: string) { emit('done', emoji); if (props.choseAndClose) { modal.value?.close(); diff --git a/packages/frontend/src/components/MkEmojiPickerWindow.vue b/packages/frontend/src/components/MkEmojiPickerWindow.vue deleted file mode 100644 index 6952943345..0000000000 --- a/packages/frontend/src/components/MkEmojiPickerWindow.vue +++ /dev/null @@ -1,49 +0,0 @@ -<!-- -SPDX-FileCopyrightText: syuilo and misskey-project -SPDX-License-Identifier: AGPL-3.0-only ---> - -<template> -<MkWindow - ref="window" - :initialWidth="300" - :initialHeight="290" - :canResize="true" - :mini="true" - :front="true" - @closed="emit('closed')" -> - <MkEmojiPicker :showPinned="showPinned" :asReactionPicker="asReactionPicker" :targetNote="targetNote" asWindow :class="$style.picker" @chosen="chosen"/> -</MkWindow> -</template> - -<script lang="ts" setup> -import { } from 'vue'; -import * as Misskey from 'misskey-js'; -import MkWindow from '@/components/MkWindow.vue'; -import MkEmojiPicker from '@/components/MkEmojiPicker.vue'; - -withDefaults(defineProps<{ - src?: HTMLElement; - showPinned?: boolean; - asReactionPicker?: boolean; - targetNote?: Misskey.entities.Note -}>(), { - showPinned: true, -}); - -const emit = defineEmits<{ - (ev: 'chosen', v: any): void; - (ev: 'closed'): void; -}>(); - -function chosen(emoji: any) { - emit('chosen', emoji); -} -</script> - -<style lang="scss" module> -.picker { - height: 100%; -} -</style> diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue index 0d8734799c..deedc5badb 100644 --- a/packages/frontend/src/components/MkFormDialog.vue +++ b/packages/frontend/src/components/MkFormDialog.vue @@ -21,37 +21,37 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSpacer :marginMin="20" :marginMax="32"> <div v-if="Object.keys(form).filter(item => !form[item].hidden).length > 0" class="_gaps_m"> - <template v-for="item in Object.keys(form).filter(item => !form[item].hidden)"> - <MkInput v-if="form[item].type === 'number'" v-model="values[item]" type="number" :step="form[item].step || 1"> - <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template> - <template v-if="form[item].description" #caption>{{ form[item].description }}</template> + <template v-for="(v, k) in Object.fromEntries(Object.entries(form).filter(([_, v]) => !('hidden' in v) || 'hidden' in v && !v.hidden))"> + <MkInput v-if="v.type === 'number'" v-model="values[k]" type="number" :step="v.step || 1"> + <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template> + <template v-if="v.description" #caption>{{ v.description }}</template> </MkInput> - <MkInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model="values[item]" type="text" :mfmAutocomplete="form[item].treatAsMfm"> - <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template> - <template v-if="form[item].description" #caption>{{ form[item].description }}</template> + <MkInput v-else-if="v.type === 'string' && !v.multiline" v-model="values[k]" type="text" :mfmAutocomplete="v.treatAsMfm"> + <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template> + <template v-if="v.description" #caption>{{ v.description }}</template> </MkInput> - <MkTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model="values[item]" :mfmAutocomplete="form[item].treatAsMfm" :mfmPreview="form[item].treatAsMfm"> - <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template> - <template v-if="form[item].description" #caption>{{ form[item].description }}</template> + <MkTextarea v-else-if="v.type === 'string' && v.multiline" v-model="values[k]" :mfmAutocomplete="v.treatAsMfm" :mfmPreview="v.treatAsMfm"> + <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template> + <template v-if="v.description" #caption>{{ v.description }}</template> </MkTextarea> - <MkSwitch v-else-if="form[item].type === 'boolean'" v-model="values[item]"> - <span v-text="form[item].label || item"></span> - <template v-if="form[item].description" #caption>{{ form[item].description }}</template> + <MkSwitch v-else-if="v.type === 'boolean'" v-model="values[k]"> + <span v-text="v.label || k"></span> + <template v-if="v.description" #caption>{{ v.description }}</template> </MkSwitch> - <MkSelect v-else-if="form[item].type === 'enum'" v-model="values[item]"> - <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template> - <option v-for="option in form[item].enum" :key="option.value" :value="option.value">{{ option.label }}</option> + <MkSelect v-else-if="v.type === 'enum'" v-model="values[k]"> + <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template> + <option v-for="option in v.enum" :key="option.value" :value="option.value">{{ option.label }}</option> </MkSelect> - <MkRadios v-else-if="form[item].type === 'radio'" v-model="values[item]"> - <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template> - <option v-for="option in form[item].options" :key="option.value" :value="option.value">{{ option.label }}</option> + <MkRadios v-else-if="v.type === 'radio'" v-model="values[k]"> + <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template> + <option v-for="option in v.options" :key="option.value" :value="option.value">{{ option.label }}</option> </MkRadios> - <MkRange v-else-if="form[item].type === 'range'" v-model="values[item]" :min="form[item].min" :max="form[item].max" :step="form[item].step" :textConverter="form[item].textConverter"> - <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template> - <template v-if="form[item].description" #caption>{{ form[item].description }}</template> + <MkRange v-else-if="v.type === 'range'" v-model="values[k]" :min="v.min" :max="v.max" :step="v.step" :textConverter="v.textConverter"> + <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template> + <template v-if="v.description" #caption>{{ v.description }}</template> </MkRange> - <MkButton v-else-if="form[item].type === 'button'" @click="form[item].action($event, values)"> - <span v-text="form[item].content || item"></span> + <MkButton v-else-if="v.type === 'button'" @click="v.action($event, values)"> + <span v-text="v.content || k"></span> </MkButton> </template> </div> @@ -72,19 +72,21 @@ import MkSelect from './MkSelect.vue'; import MkRange from './MkRange.vue'; import MkButton from './MkButton.vue'; import MkRadios from './MkRadios.vue'; +import type { Form } from '@/scripts/form.js'; import MkModalWindow from '@/components/MkModalWindow.vue'; import { i18n } from '@/i18n.js'; import { infoImageUrl } from '@/instance.js'; const props = defineProps<{ title: string; - form: any; + form: Form; }>(); const emit = defineEmits<{ (ev: 'done', v: { - canceled?: boolean; - result?: any; + canceled: true; + } | { + result: Record<string, any>; }): void; (ev: 'closed'): void; }>(); diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue index a9f019dd9c..389987338d 100644 --- a/packages/frontend/src/components/MkNotifications.vue +++ b/packages/frontend/src/components/MkNotifications.vue @@ -35,6 +35,7 @@ import { notificationTypes } from '@/const.js'; import { infoImageUrl } from '@/instance.js'; import { defaultStore } from '@/store.js'; import MkPullToRefresh from '@/components/MkPullToRefresh.vue'; +import * as Misskey from 'misskey-js'; const props = defineProps<{ excludeTypes?: typeof notificationTypes[number][]; @@ -75,17 +76,19 @@ function reload() { }); } -let connection; +let connection: Misskey.ChannelConnection<Misskey.Channels['main']>; onMounted(() => { connection = useStream().useChannel('main'); connection.on('notification', onNotification); + connection.on('notificationFlushed', reload); }); onActivated(() => { pagingComponent.value?.reload(); connection = useStream().useChannel('main'); connection.on('notification', onNotification); + connection.on('notificationFlushed', reload); }); onUnmounted(() => { diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 819f0f692c..e03faeaf55 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -172,7 +172,7 @@ const emit = defineEmits<{ const textareaEl = shallowRef<HTMLTextAreaElement | null>(null); const cwInputEl = shallowRef<HTMLInputElement | null>(null); const hashtagsInputEl = shallowRef<HTMLInputElement | null>(null); -const visibilityButton = shallowRef<HTMLElement | null>(null); +const visibilityButton = shallowRef<HTMLElement>(); const posting = ref(false); const posted = ref(false); @@ -461,6 +461,7 @@ function setVisibility() { isSilenced: $i.isSilenced, localOnly: localOnly.value, src: visibilityButton.value, + ...(props.reply ? { isReplyVisibilitySpecified: props.reply.visibility === 'specified' } : {}), }, { changeVisibility: v => { visibility.value = v; diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue index 3f775bc6e2..95eb367318 100644 --- a/packages/frontend/src/components/MkPostFormAttaches.vue +++ b/packages/frontend/src/components/MkPostFormAttaches.vue @@ -152,12 +152,12 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent): void { icon: 'ti ti-crop', action: () : void => { crop(file); }, }] : [], { + type: 'divider', + }, { text: i18n.ts.attachCancel, icon: 'ti ti-circle-x', action: () => { detachMedia(file.id); }, }, { - type: 'divider', - }, { text: i18n.ts.deleteFile, icon: 'ti ti-trash', danger: true, diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index 0dcd8b0ea2..c41811febe 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -33,7 +33,8 @@ import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import * as sound from '@/scripts/sound.js'; import { checkReactionPermissions } from '@/scripts/check-reaction-permissions.js'; -import { customEmojis } from '@/custom-emojis.js'; +import { customEmojisMap } from '@/custom-emojis.js'; +import { getUnicodeEmoji } from '@/scripts/emojilist.js'; const props = defineProps<{ reaction: string; @@ -50,13 +51,11 @@ const emit = defineEmits<{ const buttonEl = shallowRef<HTMLElement>(); -const isCustomEmoji = computed(() => props.reaction.includes(':')); -const emoji = computed(() => isCustomEmoji.value ? customEmojis.value.find(emoji => emoji.name === props.reaction.replace(/:/g, '').replace(/@\./, '')) : null); +const emojiName = computed(() => props.reaction.replace(/:/g, '').replace(/@\./, '')); +const emoji = computed(() => customEmojisMap.get(emojiName.value) ?? getUnicodeEmoji(props.reaction)); const canToggle = computed(() => { - return !props.reaction.match(/@\w/) && $i - && (emoji.value && checkReactionPermissions($i, props.note, emoji.value)) - || !isCustomEmoji.value; + return !props.reaction.match(/@\w/) && $i && emoji.value && checkReactionPermissions($i, props.note, emoji.value); }); const canGetInfo = computed(() => !props.reaction.match(/@\w/) && props.reaction.includes(':')); diff --git a/packages/frontend/src/components/MkVisibilityPicker.vue b/packages/frontend/src/components/MkVisibilityPicker.vue index 3439a751a0..5ecd41bfdf 100644 --- a/packages/frontend/src/components/MkVisibilityPicker.vue +++ b/packages/frontend/src/components/MkVisibilityPicker.vue @@ -9,21 +9,21 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="[$style.label, $style.item]"> {{ i18n.ts.visibility }} </div> - <button key="public" :disabled="isSilenced" class="_button" :class="[$style.item, { [$style.active]: v === 'public' }]" data-index="1" @click="choose('public')"> + <button key="public" :disabled="isSilenced || isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'public' }]" data-index="1" @click="choose('public')"> <div :class="$style.icon"><i class="ti ti-world"></i></div> <div :class="$style.body"> <span :class="$style.itemTitle">{{ i18n.ts._visibility.public }}</span> <span :class="$style.itemDescription">{{ i18n.ts._visibility.publicDescription }}</span> </div> </button> - <button key="home" class="_button" :class="[$style.item, { [$style.active]: v === 'home' }]" data-index="2" @click="choose('home')"> + <button key="home" :disabled="isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'home' }]" data-index="2" @click="choose('home')"> <div :class="$style.icon"><i class="ti ti-home"></i></div> <div :class="$style.body"> <span :class="$style.itemTitle">{{ i18n.ts._visibility.home }}</span> <span :class="$style.itemDescription">{{ i18n.ts._visibility.homeDescription }}</span> </div> </button> - <button key="followers" class="_button" :class="[$style.item, { [$style.active]: v === 'followers' }]" data-index="3" @click="choose('followers')"> + <button key="followers" :disabled="isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'followers' }]" data-index="3" @click="choose('followers')"> <div :class="$style.icon"><i class="ti ti-lock"></i></div> <div :class="$style.body"> <span :class="$style.itemTitle">{{ i18n.ts._visibility.followers }}</span> @@ -54,6 +54,7 @@ const props = withDefaults(defineProps<{ isSilenced: boolean; localOnly: boolean; src?: HTMLElement; + isReplyVisibilitySpecified?: boolean; }>(), { }); diff --git a/packages/frontend/src/components/global/MkA.stories.impl.ts b/packages/frontend/src/components/global/MkA.stories.impl.ts index 9d57841f04..c1d8cf0ca6 100644 --- a/packages/frontend/src/components/global/MkA.stories.impl.ts +++ b/packages/frontend/src/components/global/MkA.stories.impl.ts @@ -32,7 +32,8 @@ export const Default = { async play({ canvasElement }) { const canvas = within(canvasElement); const a = canvas.getByRole<HTMLAnchorElement>('link'); - await expect(a.href).toMatch(/^https?:\/\/.*#test$/); + // FIXME: 通るã‘ã©ãã®å¾Œè½ã¡ã‚‹ã®ã§ã‚³ãƒ¡ãƒ³ãƒˆã‚¢ã‚¦ãƒˆ + // await expect(a.href).toMatch(/^https?:\/\/.*#test$/); await userEvent.pointer({ keys: '[MouseRight]', target: a }); await tick(); const menu = canvas.getByRole('menu'); @@ -44,6 +45,7 @@ export const Default = { }, args: { to: '#test', + behavior: 'browser', }, parameters: { layout: 'centered', diff --git a/packages/frontend/src/components/global/MkTime.stories.impl.ts b/packages/frontend/src/components/global/MkTime.stories.impl.ts index 2b4b1485fd..355c839113 100644 --- a/packages/frontend/src/components/global/MkTime.stories.impl.ts +++ b/packages/frontend/src/components/global/MkTime.stories.impl.ts @@ -10,7 +10,7 @@ import MkTime from './MkTime.vue'; import { i18n } from '@/i18n.js'; import { dateTimeFormat } from '@/scripts/intl-const.js'; const now = new Date('2023-04-01T00:00:00.000Z'); -const future = new Date(8640000000000000); +const future = new Date('2024-04-01T00:00:00.000Z'); const oneHourAgo = new Date(now.getTime() - 3600000); const oneDayAgo = new Date(now.getTime() - 86400000); const oneWeekAgo = new Date(now.getTime() - 604800000); @@ -49,11 +49,12 @@ export const Empty = { export const RelativeFuture = { ...Empty, async play({ canvasElement }) { - await expect(canvasElement).toHaveTextContent(i18n.ts._ago.future); + await expect(canvasElement).toHaveTextContent(i18n.tsx._timeIn.years({ n: 1 })); // n (1) = future (2024) - now (2023) }, args: { ...Empty.args, time: future, + origin: now, }, } satisfies StoryObj<typeof MkTime>; export const AbsoluteFuture = { diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index 0bac4d0b7c..9e41926a97 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -75,6 +75,7 @@ export const ROLE_POLICIES = [ 'gtlAvailable', 'ltlAvailable', 'canPublicNote', + 'mentionLimit', 'canInvite', 'inviteLimit', 'inviteLimitCycle', diff --git a/packages/frontend/src/directives/user-preview.ts b/packages/frontend/src/directives/user-preview.ts index 0d6c330da1..7a008a4486 100644 --- a/packages/frontend/src/directives/user-preview.ts +++ b/packages/frontend/src/directives/user-preview.ts @@ -99,7 +99,6 @@ export class UserPreview { this.el.removeEventListener('mouseover', this.onMouseover); this.el.removeEventListener('mouseleave', this.onMouseleave); this.el.removeEventListener('click', this.onClick); - window.clearInterval(this.checkTimer); } } diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts index 2056023692..4232cbcd78 100644 --- a/packages/frontend/src/instance.ts +++ b/packages/frontend/src/instance.ts @@ -11,13 +11,24 @@ import { DEFAULT_INFO_IMAGE_URL, DEFAULT_NOT_FOUND_IMAGE_URL, DEFAULT_SERVER_ERR // TODO: ä»–ã®ã‚¿ãƒ–ã¨æ°¸ç¶šåŒ–ã•れãŸstateã‚’åŒæœŸ -const cached = miLocalStorage.getItem('instance'); +//#region loader +const providedMetaEl = document.getElementById('misskey_meta'); + +let cachedMeta = miLocalStorage.getItem('instance') ? JSON.parse(miLocalStorage.getItem('instance')!) : null; +let cachedAt = miLocalStorage.getItem('instanceCachedAt') ? parseInt(miLocalStorage.getItem('instanceCachedAt')!) : 0; +const providedMeta = providedMetaEl && providedMetaEl.textContent ? JSON.parse(providedMetaEl.textContent) : null; +const providedAt = providedMetaEl && providedMetaEl.dataset.generatedAt ? parseInt(providedMetaEl.dataset.generatedAt) : 0; +if (providedAt > cachedAt) { + miLocalStorage.setItem('instance', JSON.stringify(providedMeta)); + miLocalStorage.setItem('instanceCachedAt', providedAt.toString()); + cachedMeta = providedMeta; + cachedAt = providedAt; +} +//#endregion // TODO: instanceをリアクティブã«ã™ã‚‹ã‹ã¯å†è€ƒã®ä½™åœ°ã‚り -export const instance: Misskey.entities.MetaResponse = reactive(cached ? JSON.parse(cached) : { - // TODO: set default values -}); +export const instance: Misskey.entities.MetaResponse = reactive(cachedMeta ?? {}); export const serverErrorImageUrl = computed(() => instance.serverErrorImageUrl ?? DEFAULT_SERVER_ERROR_IMAGE_URL); @@ -25,7 +36,15 @@ export const infoImageUrl = computed(() => instance.infoImageUrl ?? DEFAULT_INFO export const notFoundImageUrl = computed(() => instance.notFoundImageUrl ?? DEFAULT_NOT_FOUND_IMAGE_URL); -export async function fetchInstance() { +export async function fetchInstance(force = false): Promise<void> { + if (!force) { + const cachedAt = miLocalStorage.getItem('instanceCachedAt') ? parseInt(miLocalStorage.getItem('instanceCachedAt')!) : 0; + + if (Date.now() - cachedAt < 1000 * 60 * 60) { + return; + } + } + const meta = await misskeyApi('meta', { detail: false, }); @@ -35,4 +54,5 @@ export async function fetchInstance() { } miLocalStorage.setItem('instance', JSON.stringify(instance)); + miLocalStorage.setItem('instanceCachedAt', Date.now().toString()); } diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts index 3de81c9bb9..8029bca68d 100644 --- a/packages/frontend/src/local-storage.ts +++ b/packages/frontend/src/local-storage.ts @@ -7,6 +7,7 @@ type Keys = 'v' | 'lastVersion' | 'instance' | + 'instanceCachedAt' | 'account' | 'accounts' | 'latestDonationInfoShownAt' | diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index a4fde6b701..c561e84a23 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -7,9 +7,9 @@ import { Component, markRaw, Ref, ref, defineAsyncComponent } from 'vue'; import { EventEmitter } from 'eventemitter3'; -import insertTextAtCursor from 'insert-text-at-cursor'; import * as Misskey from 'misskey-js'; -import type { ComponentProps } from 'vue-component-type-helpers'; +import type { ComponentProps as CP } from 'vue-component-type-helpers'; +import type { Form, GetFormResultType } from '@/scripts/form.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import MkPostFormDialog from '@/components/MkPostFormDialog.vue'; @@ -19,7 +19,6 @@ import MkToast from '@/components/MkToast.vue'; import MkDialog from '@/components/MkDialog.vue'; import MkPasswordDialog from '@/components/MkPasswordDialog.vue'; import MkEmojiPickerDialog from '@/components/MkEmojiPickerDialog.vue'; -import MkEmojiPickerWindow from '@/components/MkEmojiPickerWindow.vue'; import MkPopupMenu from '@/components/MkPopupMenu.vue'; import MkContextMenu from '@/components/MkContextMenu.vue'; import { MenuItem } from '@/types/menu.js'; @@ -28,15 +27,15 @@ import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; export const openingWindowsCount = ref(0); -export const apiWithDialog = (( - endpoint: string, - data: Record<string, any> = {}, +export const apiWithDialog = (<E extends keyof Misskey.Endpoints = keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req'] = Misskey.Endpoints[E]['req']>( + endpoint: E, + data: P = {} as any, token?: string | null | undefined, ) => { const promise = misskeyApi(endpoint, data, token); promiseDialog(promise, null, async (err) => { - let title = null; - let text = err.message + '\n' + (err as any).id; + let title: string | undefined; + let text = err.message + '\n' + err.id; if (err.code === 'INTERNAL_ERROR') { title = i18n.ts.internalServerError; text = i18n.ts.internalServerErrorDescription; @@ -88,7 +87,7 @@ export const apiWithDialog = (( export function promiseDialog<T extends Promise<any>>( promise: T, onSuccess?: ((res: any) => void) | null, - onFailure?: ((err: Error) => void) | null, + onFailure?: ((err: Misskey.api.APIError) => void) | null, text?: string, ): T { const showing = ref(true); @@ -149,14 +148,30 @@ export function claimZIndex(priority: keyof typeof zIndexes = 'low'): number { // 使ã„物ã«ãªã‚‰ãªã„ã®ã§ã€ä»£ã‚り㫠['$props'] ã‹ã‚‰è‰²ã€…çœãã“ã¨ã§ emit ã®åž‹ã‚’生æˆã™ã‚‹ // FIXME: 何故㋠*.ts ファイルã‹ã‚‰ã ã¨åž‹ãŒã†ã¾ãå–れãªã„?ã“ã¨ãŒã‚ã‚‹ã®ã‚’ãªã‚“ã¨ã‹ã—ãŸã„ type ComponentEmit<T> = T extends new () => { $props: infer Props } - ? EmitsExtractor<Props> - : never; + ? [keyof Pick<T, Extract<keyof T, `on${string}`>>] extends [never] + ? Record<string, unknown> // *.ts ファイルã‹ã‚‰åž‹ãŒã†ã¾ãå–れãªã„ã¨ã用(ã“れãŒãªã„㨠{} ã«ãªã£ã¦åž‹ã‚¨ãƒ©ãƒ¼ãŒã†ã‚‹ã•ã„) + : EmitsExtractor<Props> + : T extends (...args: any) => any + ? ReturnType<T> extends { [x: string]: any; __ctx?: { [x: string]: any; props: infer Props } } + ? [keyof Pick<T, Extract<keyof T, `on${string}`>>] extends [never] + ? Record<string, unknown> + : EmitsExtractor<Props> + : never + : never; + +// props ã« ref を許å¯ã™ã‚‹ã‚ˆã†ã«ã™ã‚‹ +type ComponentProps<T extends Component> = { [K in keyof CP<T>]: CP<T>[K] | Ref<CP<T>[K]> }; type EmitsExtractor<T> = { [K in keyof T as K extends `onVnode${string}` ? never : K extends `on${infer E}` ? Uncapitalize<E> : K extends string ? never : K]: T[K]; }; -export async function popup<T extends Component>(component: T, props: ComponentProps<T>, events: ComponentEmit<T> = {} as ComponentEmit<T>, disposeEvent?: keyof ComponentEmit<T>) { +export async function popup<T extends Component>( + component: T, + props: ComponentProps<T>, + events: ComponentEmit<T> = {} as ComponentEmit<T>, + disposeEvent?: keyof ComponentEmit<T>, +): Promise<{ dispose: () => void }> { markRaw(component); const id = ++popupIdCount; @@ -197,12 +212,12 @@ export function toast(message: string) { export function alert(props: { type?: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question'; - title?: string | null; - text?: string | null; + title?: string; + text?: string; }): Promise<void> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(MkDialog, props, { - done: result => { + done: () => { resolve(); }, }, 'closed'); @@ -211,12 +226,12 @@ export function alert(props: { export function confirm(props: { type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question'; - title?: string | null; - text?: string | null; + title?: string; + text?: string; okText?: string; cancelText?: string; }): Promise<{ canceled: boolean }> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(MkDialog, { ...props, showCancelButton: true, @@ -237,13 +252,15 @@ export function actions<T extends { danger?: boolean, }[]>(props: { type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question'; - title?: string | null; - text?: string | null; + title?: string; + text?: string; actions: T; -}): Promise<{ canceled: true; result: undefined; } | { +}): Promise<{ + canceled: true; result: undefined; +} | { canceled: false; result: T[number]['value']; }> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(MkDialog, { ...props, actions: props.actions.map(a => ({ @@ -262,19 +279,50 @@ export function actions<T extends { }); } +// default ãŒæŒ‡å®šã•れã¦ã„ãŸã‚‰ result 㯠null ã«ãªã‚Šå¾—ãªã„ã“ã¨ã‚’ä¿è¨¼ã™ã‚‹ overload function export function inputText(props: { type?: 'text' | 'email' | 'password' | 'url'; - title?: string | null; - text?: string | null; + title?: string; + text?: string; placeholder?: string | null; autocomplete?: string; - default?: string | null; + default: string; minLength?: number; maxLength?: number; -}): Promise<{ canceled: true; result: undefined; } | { +}): Promise<{ + canceled: true; result: undefined; +} | { canceled: false; result: string; +}>; +export function inputText(props: { + type?: 'text' | 'email' | 'password' | 'url'; + title?: string; + text?: string; + placeholder?: string | null; + autocomplete?: string; + default?: string | null; + minLength?: number; + maxLength?: number; +}): Promise<{ + canceled: true; result: undefined; +} | { + canceled: false; result: string | null; +}>; +export function inputText(props: { + type?: 'text' | 'email' | 'password' | 'url'; + title?: string; + text?: string; + placeholder?: string | null; + autocomplete?: string; + default?: string | null; + minLength?: number; + maxLength?: number; +}): Promise<{ + canceled: true; result: undefined; +} | { + canceled: false; result: string | null; }> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(MkDialog, { title: props.title, text: props.text, @@ -282,7 +330,7 @@ export function inputText(props: { type: props.type, placeholder: props.placeholder, autocomplete: props.autocomplete, - default: props.default, + default: props.default ?? null, minLength: props.minLength, maxLength: props.maxLength, }, @@ -294,16 +342,41 @@ export function inputText(props: { }); } +// default ãŒæŒ‡å®šã•れã¦ã„ãŸã‚‰ result 㯠null ã«ãªã‚Šå¾—ãªã„ã“ã¨ã‚’ä¿è¨¼ã™ã‚‹ overload function export function inputNumber(props: { - title?: string | null; - text?: string | null; + title?: string; + text?: string; placeholder?: string | null; autocomplete?: string; - default?: number | null; -}): Promise<{ canceled: true; result: undefined; } | { + default: number; +}): Promise<{ + canceled: true; result: undefined; +} | { canceled: false; result: number; +}>; +export function inputNumber(props: { + title?: string; + text?: string; + placeholder?: string | null; + autocomplete?: string; + default?: number | null; +}): Promise<{ + canceled: true; result: undefined; +} | { + canceled: false; result: number | null; +}>; +export function inputNumber(props: { + title?: string; + text?: string; + placeholder?: string | null; + autocomplete?: string; + default?: number | null; +}): Promise<{ + canceled: true; result: undefined; +} | { + canceled: false; result: number | null; }> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(MkDialog, { title: props.title, text: props.text, @@ -311,7 +384,7 @@ export function inputNumber(props: { type: 'number', placeholder: props.placeholder, autocomplete: props.autocomplete, - default: props.default, + default: props.default ?? null, }, }, { done: result => { @@ -322,34 +395,38 @@ export function inputNumber(props: { } export function inputDate(props: { - title?: string | null; - text?: string | null; + title?: string; + text?: string; placeholder?: string | null; - default?: Date | null; -}): Promise<{ canceled: true; result: undefined; } | { + default?: string | null; +}): Promise<{ + canceled: true; result: undefined; +} | { canceled: false; result: Date; }> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(MkDialog, { title: props.title, text: props.text, input: { type: 'date', placeholder: props.placeholder, - default: props.default, + default: props.default ?? null, }, }, { done: result => { - resolve(result ? { result: new Date(result.result), canceled: false } : { canceled: true }); + resolve(result ? { result: new Date(result.result), canceled: false } : { result: undefined, canceled: true }); }, }, 'closed'); }); } -export function authenticateDialog(): Promise<{ canceled: true; result: undefined; } | { +export function authenticateDialog(): Promise<{ + canceled: true; result: undefined; +} | { canceled: false; result: { password: string; token: string | null; }; }> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(MkPasswordDialog, {}, { done: result => { resolve(result ? { canceled: false, result } : { canceled: true, result: undefined }); @@ -358,34 +435,53 @@ export function authenticateDialog(): Promise<{ canceled: true; result: undefine }); } +// default ãŒæŒ‡å®šã•れã¦ã„ãŸã‚‰ result 㯠null ã«ãªã‚Šå¾—ãªã„ã“ã¨ã‚’ä¿è¨¼ã™ã‚‹ overload function export function select<C = any>(props: { - title?: string | null; - text?: string | null; + title?: string; + text?: string; + default: string; + items: { + value: C; + text: string; + }[]; +}): Promise<{ + canceled: true; result: undefined; +} | { + canceled: false; result: C; +}>; +export function select<C = any>(props: { + title?: string; + text?: string; default?: string | null; -} & ({ items: { value: C; text: string; }[]; +}): Promise<{ + canceled: true; result: undefined; } | { - groupedItems: { - label: string; - items: { - value: C; - text: string; - }[]; + canceled: false; result: C | null; +}>; +export function select<C = any>(props: { + title?: string; + text?: string; + default?: string | null; + items: { + value: C; + text: string; }[]; -})): Promise<{ canceled: true; result: undefined; } | { - canceled: false; result: C; +}): Promise<{ + canceled: true; result: undefined; +} | { + canceled: false; result: C | null; }> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(MkDialog, { title: props.title, text: props.text, select: { items: props.items, - groupedItems: props.groupedItems, - default: props.default, + default: props.default ?? null, }, }, { done: result => { @@ -396,7 +492,7 @@ export function select<C = any>(props: { } export function success(): Promise<void> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { const showing = ref(true); window.setTimeout(() => { showing.value = false; @@ -411,7 +507,7 @@ export function success(): Promise<void> { } export function waiting(): Promise<void> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { const showing = ref(true); popup(MkWaitingDialog, { success: false, @@ -422,9 +518,9 @@ export function waiting(): Promise<void> { }); } -export function form(title, form) { - return new Promise((resolve, reject) => { - popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form }, { +export function form<F extends Form>(title: string, f: F): Promise<{ canceled: true } | { result: GetFormResultType<F> }> { + return new Promise(resolve => { + popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form: f }, { done: result => { resolve(result); }, @@ -433,7 +529,7 @@ export function form(title, form) { } export async function selectUser(opts: { includeSelf?: boolean; localOnly?: boolean; } = {}): Promise<Misskey.entities.UserDetailed> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(defineAsyncComponent(() => import('@/components/MkUserSelectDialog.vue')), { includeSelf: opts.includeSelf, localOnly: opts.localOnly, @@ -446,7 +542,7 @@ export async function selectUser(opts: { includeSelf?: boolean; localOnly?: bool } export async function selectDriveFile(multiple: boolean): Promise<Misskey.entities.DriveFile[]> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), { type: 'file', multiple, @@ -460,23 +556,23 @@ export async function selectDriveFile(multiple: boolean): Promise<Misskey.entiti }); } -export async function selectDriveFolder(multiple: boolean) { - return new Promise((resolve, reject) => { +export async function selectDriveFolder(multiple: boolean): Promise<Misskey.entities.DriveFolder[]> { + return new Promise(resolve => { popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), { type: 'folder', multiple, }, { done: folders => { if (folders) { - resolve(multiple ? folders : folders[0]); + resolve(folders); } }, }, 'closed'); }); } -export async function pickEmoji(src: HTMLElement | null, opts) { - return new Promise((resolve, reject) => { +export async function pickEmoji(src: HTMLElement, opts: ComponentProps<typeof MkEmojiPickerDialog>): Promise<string> { + return new Promise(resolve => { popup(MkEmojiPickerDialog, { src, ...opts, @@ -492,7 +588,7 @@ export async function cropImage(image: Misskey.entities.DriveFile, options: { aspectRatio: number; uploadFolder?: string | null; }): Promise<Misskey.entities.DriveFile> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(defineAsyncComponent(() => import('@/components/MkCropperDialog.vue')), { file: image, aspectRatio: options.aspectRatio, @@ -505,67 +601,13 @@ export async function cropImage(image: Misskey.entities.DriveFile, options: { }); } -type AwaitType<T> = - T extends Promise<infer U> ? U : - T extends (...args: any[]) => Promise<infer V> ? V : - T; -let openingEmojiPicker: AwaitType<ReturnType<typeof popup>> | null = null; -let activeTextarea: HTMLTextAreaElement | HTMLInputElement | null = null; -export async function openEmojiPicker(src?: HTMLElement, opts, initialTextarea: typeof activeTextarea) { - if (openingEmojiPicker) return; - - activeTextarea = initialTextarea; - - const textareas = document.querySelectorAll('textarea, input'); - for (const textarea of Array.from(textareas)) { - textarea.addEventListener('focus', () => { - activeTextarea = textarea; - }); - } - - const observer = new MutationObserver(records => { - for (const record of records) { - for (const node of Array.from(record.addedNodes).filter(node => node instanceof HTMLElement) as HTMLElement[]) { - const textareas = node.querySelectorAll('textarea, input') as NodeListOf<NonNullable<typeof activeTextarea>>; - for (const textarea of Array.from(textareas).filter(textarea => textarea.dataset.preventEmojiInsert == null)) { - if (document.activeElement === textarea) activeTextarea = textarea; - textarea.addEventListener('focus', () => { - activeTextarea = textarea; - }); - } - } - } - }); - - observer.observe(document.body, { - childList: true, - subtree: true, - attributes: false, - characterData: false, - }); - - openingEmojiPicker = await popup(MkEmojiPickerWindow, { - src, - ...opts, - }, { - chosen: emoji => { - insertTextAtCursor(activeTextarea, emoji); - }, - closed: () => { - openingEmojiPicker!.dispose(); - openingEmojiPicker = null; - observer.disconnect(); - }, - }); -} - -export function popupMenu(items: MenuItem[] | Ref<MenuItem[]>, src?: HTMLElement | EventTarget | null, options?: { +export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | null, options?: { align?: string; width?: number; viaKeyboard?: boolean; onClosing?: () => void; }): Promise<void> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { let dispose; popup(MkPopupMenu, { items, @@ -587,9 +629,9 @@ export function popupMenu(items: MenuItem[] | Ref<MenuItem[]>, src?: HTMLElement }); } -export function contextMenu(items: MenuItem[] | Ref<MenuItem[]>, ev: MouseEvent): Promise<void> { +export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> { ev.preventDefault(); - return new Promise((resolve, reject) => { + return new Promise(resolve => { let dispose; popup(MkContextMenu, { items, @@ -608,7 +650,7 @@ export function contextMenu(items: MenuItem[] | Ref<MenuItem[]>, ev: MouseEvent) export function post(props: Record<string, any> = {}): Promise<void> { showMovedDialog(); - return new Promise((resolve, reject) => { + return new Promise(resolve => { // NOTE: MkPostFormDialogã‚’dynamic importã™ã‚‹ã¨iOSã§ãƒ†ã‚ストエリアã«è‡ªå‹•フォーカスã§ããªã„ // NOTE: ãŸã ã€dynamic importã—ãªã„å ´åˆã€MkPostFormDialogインスタンスãŒä½¿ã„ã¾ã‚ã•れ〠// VueãŒæ¸¡ã•れãŸã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆã«å†…部的ã«__propsã¨ã„ã†ãƒ—ãƒãƒ‘ティを生やã™å½±éŸ¿ã§ã€ diff --git a/packages/frontend/src/pages/admin/RolesEditorFormula.vue b/packages/frontend/src/pages/admin/RolesEditorFormula.vue index f4a8f44955..2f5b4c47d8 100644 --- a/packages/frontend/src/pages/admin/RolesEditorFormula.vue +++ b/packages/frontend/src/pages/admin/RolesEditorFormula.vue @@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSelect v-model="type" :class="$style.typeSelect"> <option value="isLocal">{{ i18n.ts._role._condition.isLocal }}</option> <option value="isRemote">{{ i18n.ts._role._condition.isRemote }}</option> + <option value="roleAssignedTo">{{ i18n.ts._role._condition.roleAssignedTo }}</option> <option value="createdLessThan">{{ i18n.ts._role._condition.createdLessThan }}</option> <option value="createdMoreThan">{{ i18n.ts._role._condition.createdMoreThan }}</option> <option value="followersLessThanOrEq">{{ i18n.ts._role._condition.followersLessThanOrEq }}</option> @@ -51,6 +52,10 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInput v-else-if="['followersLessThanOrEq', 'followersMoreThanOrEq', 'followingLessThanOrEq', 'followingMoreThanOrEq', 'notesLessThanOrEq', 'notesMoreThanOrEq'].includes(type)" v-model="v.value" type="number"> </MkInput> + + <MkSelect v-else-if="type === 'roleAssignedTo'" v-model="v.roleId"> + <option v-for="role in roles.filter(r => r.target === 'manual')" :key="role.id" :value="role.id">{{ role.name }}</option> + </MkSelect> </div> </template> @@ -62,6 +67,7 @@ import MkSelect from '@/components/MkSelect.vue'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; import { deepClone } from '@/scripts/clone.js'; +import { rolesCache } from '@/cache.js'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); @@ -77,6 +83,8 @@ const props = defineProps<{ const v = ref(deepClone(props.modelValue)); +const roles = await rolesCache.fetch(); + watch(() => props.modelValue, () => { if (JSON.stringify(props.modelValue) === JSON.stringify(v.value)) return; v.value = deepClone(props.modelValue); @@ -92,6 +100,7 @@ const type = computed({ if (t === 'and') v.value.values = []; if (t === 'or') v.value.values = []; if (t === 'not') v.value.value = { id: uuid(), type: 'isRemote' }; + if (t === 'roleAssignedTo') v.value.roleId = ''; if (t === 'createdLessThan') v.value.sec = 86400; if (t === 'createdMoreThan') v.value.sec = 86400; if (t === 'followersLessThanOrEq') v.value.value = 10; diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue index e5e04fdeb8..73c5e1919f 100644 --- a/packages/frontend/src/pages/admin/bot-protection.vue +++ b/packages/frontend/src/pages/admin/bot-protection.vue @@ -142,7 +142,7 @@ function save() { turnstileSiteKey: turnstileSiteKey.value, turnstileSecretKey: turnstileSecretKey.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } </script> diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue index 2b559f92c9..fe1b7c561d 100644 --- a/packages/frontend/src/pages/admin/branding.vue +++ b/packages/frontend/src/pages/admin/branding.vue @@ -169,7 +169,7 @@ function save() { feedbackUrl: feedbackUrl.value === '' ? null : feedbackUrl.value, manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)), }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue index 839b9bee16..4a858887f3 100644 --- a/packages/frontend/src/pages/admin/email-settings.vue +++ b/packages/frontend/src/pages/admin/email-settings.vue @@ -124,7 +124,7 @@ function save() { smtpUser: smtpUser.value, smtpPass: smtpPass.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue index ba3eb05e72..e0b82eb02e 100644 --- a/packages/frontend/src/pages/admin/external-services.vue +++ b/packages/frontend/src/pages/admin/external-services.vue @@ -61,7 +61,7 @@ function save() { deeplAuthKey: deeplAuthKey.value, deeplIsPro: deeplIsPro.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue index 5167b2e6b2..6b14bd42c2 100644 --- a/packages/frontend/src/pages/admin/instance-block.vue +++ b/packages/frontend/src/pages/admin/instance-block.vue @@ -50,7 +50,7 @@ function save() { silencedHosts: silencedHosts.value.split('\n') || [], }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue index d6cb1e39a7..9efb34ac9a 100644 --- a/packages/frontend/src/pages/admin/moderation.vue +++ b/packages/frontend/src/pages/admin/moderation.vue @@ -110,7 +110,7 @@ function save() { hiddenTags: hiddenTags.value.split('\n'), preservedUsernames: preservedUsernames.value.split('\n'), }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue index 21d68331cb..e33c882721 100644 --- a/packages/frontend/src/pages/admin/modlog.ModLog.vue +++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue @@ -110,6 +110,12 @@ SPDX-License-Identifier: AGPL-3.0-only <CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/> </div> </template> + <template v-else-if="log.type === 'updateRemoteInstanceNote'"> + <div>{{ i18n.ts.user }}: {{ log.info.userId }}</div> + <div :class="$style.diff"> + <CodeDiff :context="5" :hideHeader="true" :oldString="log.info.before ?? ''" :newString="log.info.after ?? ''" maxHeight="300px"/> + </div> + </template> <details> <summary>raw</summary> diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue index 5e251b8a6f..8590ee1651 100644 --- a/packages/frontend/src/pages/admin/modlog.vue +++ b/packages/frontend/src/pages/admin/modlog.vue @@ -54,8 +54,6 @@ const pagination = { })), }; -console.log(Misskey); - const headerActions = computed(() => []); const headerTabs = computed(() => []); diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue index 4ff5ab09ca..5fddb715cd 100644 --- a/packages/frontend/src/pages/admin/object-storage.vue +++ b/packages/frontend/src/pages/admin/object-storage.vue @@ -143,7 +143,7 @@ function save() { objectStorageSetPublicRead: objectStorageSetPublicRead.value, objectStorageS3ForcePathStyle: objectStorageS3ForcePathStyle.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/other-settings.vue b/packages/frontend/src/pages/admin/other-settings.vue index 651f0ef936..345cf333b5 100644 --- a/packages/frontend/src/pages/admin/other-settings.vue +++ b/packages/frontend/src/pages/admin/other-settings.vue @@ -73,7 +73,7 @@ function save() { enableChartsForRemoteUser: enableChartsForRemoteUser.value, enableChartsForFederatedInstances: enableChartsForFederatedInstances.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/proxy-account.vue b/packages/frontend/src/pages/admin/proxy-account.vue index 02b506d13d..81db9f1da9 100644 --- a/packages/frontend/src/pages/admin/proxy-account.vue +++ b/packages/frontend/src/pages/admin/proxy-account.vue @@ -56,7 +56,7 @@ function save() { os.apiWithDialog('admin/update-meta', { proxyAccountId: proxyAccountId.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index ad9df35dbf..eb8a59b34f 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -160,6 +160,25 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])"> + <template #label>{{ i18n.ts._role._options.mentionMax }}</template> + <template #suffix> + <span v-if="role.policies.mentionLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.mentionLimit.value }}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.mentionLimit)"></i></span> + </template> + <div class="_gaps"> + <MkSwitch v-model="role.policies.mentionLimit.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts._role.useBaseValue }}</template> + </MkSwitch> + <MkInput v-model="role.policies.mentionLimit.value" :disabled="role.policies.mentionLimit.useDefault" type="number" :readonly="readonly"> + </MkInput> + <MkRange v-model="role.policies.mentionLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <template #label>{{ i18n.ts._role.priority }}</template> + </MkRange> + </div> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])"> <template #label>{{ i18n.ts._role._options.canInvite }}</template> <template #suffix> diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 496cb09664..9753d9f6cb 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -48,6 +48,13 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSwitch> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])"> + <template #label>{{ i18n.ts._role._options.mentionMax }}</template> + <template #suffix>{{ policies.mentionLimit }}</template> + <MkInput v-model="policies.mentionLimit" type="number"> + </MkInput> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])"> <template #label>{{ i18n.ts._role._options.canInvite }}</template> <template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template> diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue index cadcf5a8cc..c4745978df 100644 --- a/packages/frontend/src/pages/admin/security.vue +++ b/packages/frontend/src/pages/admin/security.vue @@ -196,7 +196,7 @@ async function init() { enableTruemailApi.value = meta.enableTruemailApi; truemailInstance.value = meta.truemailInstance; truemailAuthKey.value = meta.truemailAuthKey; - bannedEmailDomains.value = meta.bannedEmailDomains?.join('\n') || ""; + bannedEmailDomains.value = meta.bannedEmailDomains?.join('\n') || ''; } function save() { @@ -221,7 +221,7 @@ function save() { truemailAuthKey: truemailAuthKey.value, bannedEmailDomains: bannedEmailDomains.value.split('\n'), }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue index 87318bccce..ff9b8d6299 100644 --- a/packages/frontend/src/pages/admin/server-rules.vue +++ b/packages/frontend/src/pages/admin/server-rules.vue @@ -58,7 +58,7 @@ const save = async () => { await os.apiWithDialog('admin/update-meta', { serverRules: serverRules.value, }); - fetchInstance(); + fetchInstance(true); }; const remove = (index: number): void => { diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue index c505d70aa9..9a198ee8a3 100644 --- a/packages/frontend/src/pages/admin/settings.vue +++ b/packages/frontend/src/pages/admin/settings.vue @@ -243,7 +243,7 @@ async function save(): void { notesPerOneAd: notesPerOneAd.value, }); - fetchInstance(); + fetchInstance(true); } const headerTabs = computed(() => []); diff --git a/packages/frontend/src/pages/drop-and-fusion.game.vue b/packages/frontend/src/pages/drop-and-fusion.game.vue index d9881cebbf..eba5b92154 100644 --- a/packages/frontend/src/pages/drop-and-fusion.game.vue +++ b/packages/frontend/src/pages/drop-and-fusion.game.vue @@ -7,9 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSpacer :contentMax="800"> <div :class="$style.root"> <div v-if="!gameLoaded" :class="$style.loadingScreen"> - <div> - Loading... - </div> + <div>{{ i18n.ts.loading }}<MkEllipsis/></div> </div> <!-- ↓ã«å¯¾ã—ã¦Transitionコンãƒãƒ¼ãƒãƒ³ãƒˆã‚’使ã†ã¨ä½•æ•…ã‹keyを指定ã—ã¦ã„ã¦ã‚‚ã‚ャッシュãŒåйã‹ãšæ§˜ã€…ãªã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆãŒéƒ½åº¦å†è©•価ã•れã¦ãƒ‘フォーマンスãŒä½Žä¸‹ã™ã‚‹ --> <div v-show="gameLoaded" class="_gaps_s"> @@ -32,18 +30,18 @@ SPDX-License-Identifier: AGPL-3.0-only </Transition> <div :class="$style.header"> - <div :class="[$style.frame, $style.headerTitle]"> - <div :class="$style.frameInner"> - <b>BUBBLE GAME</b> - <div>- {{ gameMode }} -</div> + <div class="_woodenFrame" :class="[$style.headerTitle]"> + <div class="_woodenFrameInner"> + <b>{{ i18n.ts.bubbleGame }}</b> + <div>- {{ gameMode.toUpperCase() }} -</div> </div> </div> - <div :class="[$style.frame, $style.frameH]"> - <div :class="$style.frameInner"> - <MkButton inline small @click="hold">HOLD</MkButton> + <div class="_woodenFrame _woodenFrameH"> + <div class="_woodenFrameInner"> + <MkButton inline small @click="hold">{{ i18n.ts._bubbleGame.hold }}</MkButton> <img v-if="holdingStock" :src="getTextureImageUrl(holdingStock.mono)" style="width: 32px; margin-left: 8px; vertical-align: bottom;"/> </div> - <div :class="[$style.frameInner, $style.stock]" style="text-align: center;"> + <div class="_woodenFrameInner" :class="$style.stock" style="text-align: center;"> <TransitionGroup :enterActiveClass="$style.transition_stock_enterActive" :leaveActiveClass="$style.transition_stock_leaveActive" @@ -90,58 +88,74 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="isGameOver && !replaying" :class="$style.gameOverLabel"> <div class="_gaps_s"> <img src="/client-assets/drop-and-fusion/gameover.png" style="width: 200px; max-width: 100%; display: block; margin: auto; margin-bottom: -5px;"/> - <div>SCORE: <MkNumber :value="score"/>{{ getScoreUnit(gameMode) }}</div> - <div>MAX CHAIN: <MkNumber :value="maxCombo"/></div> - <div v-if="gameMode === 'yen'">TOTAL EARNINGS: <b><MkNumber :value="yenTotal ?? score"/>円</b></div> - <div v-if="gameMode === 'sweets'"><b>ãŠã«ãŽã‚Š<MkNumber :value="score / 130"/>個分</b></div> + <div>{{ i18n.ts._bubbleGame._score.score }}: <MkNumber :value="score"/>{{ getScoreUnit(gameMode) }}</div> + <div>{{ i18n.ts._bubbleGame._score.maxChain }}: <MkNumber :value="maxCombo"/></div> + <div v-if="gameMode === 'yen'"> + {{ i18n.ts._bubbleGame._score.scoreYen }}: + <I18n :src="i18n.ts._bubbleGame._score.yen" tag="b"> + <template #yen><MkNumber :value="yenTotal ?? score"/></template> + </I18n> + </div> + <I18n v-if="gameMode === 'sweets'" :src="i18n.ts._bubbleGame._score.scoreSweets" tag="div"> + <template #onigiriQtyWithUnit> + <I18n :src="i18n.ts._bubbleGame._score.estimatedQty" tag="b"> + <template #qty><MkNumber :value="score / 130"/></template> + </I18n> + </template> + </I18n> </div> </div> <div v-if="replaying" :class="$style.replayIndicator"><span :class="$style.replayIndicatorText"><i class="ti ti-player-play"></i> {{ i18n.ts.replaying }}</span></div> </div> - <div v-if="replaying" :class="$style.frame"> - <div :class="$style.frameInner"> + <div v-if="replaying" class="_woodenFrame"> + <div class="_woodenFrameInner"> <div style="background: #0004;"> <div style="height: 10px; background: var(--accent); will-change: width;" :style="{ width: `${(currentFrame / endedAtFrame) * 100}%` }"></div> </div> </div> - <div :class="$style.frameInner"> + <div class="_woodenFrameInner"> <div class="_buttonsCenter"> - <MkButton @click="endReplay"><i class="ti ti-player-stop"></i> END</MkButton> + <MkButton @click="endReplay"><i class="ti ti-player-stop"></i> {{ i18n.ts.endReplay }}</MkButton> <MkButton :primary="replayPlaybackRate === 4" @click="replayPlaybackRate = replayPlaybackRate === 4 ? 1 : 4"><i class="ti ti-player-track-next"></i> x4</MkButton> <MkButton :primary="replayPlaybackRate === 16" @click="replayPlaybackRate = replayPlaybackRate === 16 ? 1 : 16"><i class="ti ti-player-track-next"></i> x16</MkButton> </div> </div> </div> - <div v-if="isGameOver" :class="$style.frame"> - <div :class="$style.frameInner"> + <div v-if="isGameOver" class="_woodenFrame"> + <div class="_woodenFrameInner"> <div class="_buttonsCenter"> <MkButton primary rounded @click="backToTitle">{{ i18n.ts.backToTitle }}</MkButton> <MkButton primary rounded @click="replay">{{ i18n.ts.showReplay }}</MkButton> <MkButton primary rounded @click="share">{{ i18n.ts.share }}</MkButton> - <MkButton rounded @click="exportLog">Copy replay data</MkButton> + <MkButton rounded @click="exportLog">{{ i18n.ts.copyReplayData }}</MkButton> </div> </div> </div> <div style="display: flex;"> - <div :class="$style.frame" style="flex: 1; margin-right: 10px;"> - <div :class="$style.frameInner"> - <div>SCORE: <b><MkNumber :value="score"/>{{ getScoreUnit(gameMode) }}</b></div> - <div>HIGH SCORE: <b v-if="highScore"><MkNumber :value="highScore"/>{{ getScoreUnit(gameMode) }}</b><b v-else>-</b></div> - <div v-if="gameMode === 'yen'">TOTAL EARNINGS: <b v-if="yenTotal"><MkNumber :value="yenTotal"/>円</b><b v-else>-</b></div> + <div class="_woodenFrame" style="flex: 1; margin-right: 10px;"> + <div class="_woodenFrameInner"> + <div>{{ i18n.ts._bubbleGame._score.score }}: <MkNumber :value="score"/>{{ getScoreUnit(gameMode) }}</div> + <div>{{ i18n.ts._bubbleGame._score.highScore }}: <b v-if="highScore"><MkNumber :value="highScore"/>{{ getScoreUnit(gameMode) }}</b><b v-else>-</b></div> + <div v-if="gameMode === 'yen'"> + {{ i18n.ts._bubbleGame._score.scoreYen }}: + <I18n :src="i18n.ts._bubbleGame._score.yen" tag="b"> + <template #yen><MkNumber :value="yenTotal ?? score"/></template> + </I18n> + </div> </div> </div> - <div :class="[$style.frame]" style="margin-left: auto;"> - <div :class="$style.frameInner" style="text-align: center;"> + <div class="_woodenFrame" style="margin-left: auto;"> + <div class="_woodenFrameInner" style="text-align: center;"> <div @click="showConfig = !showConfig"><i class="ti ti-settings"></i></div> </div> </div> </div> - <div v-if="showConfig" :class="$style.frame"> - <div :class="$style.frameInner"> + <div v-if="showConfig" class="_woodenFrame"> + <div class="_woodenFrameInner"> <div class="_gaps"> <MkRange v-model="bgmVolume" :min="0" :max="1" :step="0.01" :textConverter="(v) => `${Math.floor(v * 100)}%`" :continuousUpdate="true" @dragEnded="(v) => updateSettings('bgmVolume', v)"> <template #label>BGM {{ i18n.ts.volume }}</template> @@ -153,8 +167,8 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> - <div :class="$style.frame"> - <div :class="$style.frameInner"> + <div class="_woodenFrame"> + <div class="_woodenFrameInner"> <div>FUSION RECIPE</div> <div> <div v-for="(mono, i) in game.monoDefinitions.sort((a, b) => a.level - b.level)" :key="mono.id" style="display: inline-block;"> @@ -165,10 +179,10 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> - <div :class="$style.frame"> - <div :class="$style.frameInner"> - <MkButton v-if="!isGameOver && !replaying" full danger @click="surrender">Surrender</MkButton> - <MkButton v-else full @click="restart">Retry</MkButton> + <div class="_woodenFrame"> + <div class="_woodenFrameInner"> + <MkButton v-if="!isGameOver && !replaying" full danger @click="surrender">{{ i18n.ts.surrender }}</MkButton> + <MkButton v-else full @click="restart">{{ i18n.ts.gameRetry }}</MkButton> </div> </div> </div> @@ -1313,38 +1327,6 @@ definePageMetadata(() => ({ max-width: 100%; } -.frame { - padding: 7px; - background: #8C4F26; - box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c; - border-radius: 10px; -} - -.frameH { - display: flex; - gap: 6px; -} - -.frameInner { - padding: 8px; - margin-top: 8px; - background: #F1E8DC; - box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410; - border-radius: 6px; - color: #693410; - - &:first-child { - margin-top: 0; - } -} - -.frameDivider { - height: 0; - border: none; - border-top: 1px solid #693410; - border-bottom: 1px solid #ce8a5c; -} - .header { position: relative; z-index: 10; diff --git a/packages/frontend/src/pages/drop-and-fusion.vue b/packages/frontend/src/pages/drop-and-fusion.vue index 1b11457988..54352c9b0d 100644 --- a/packages/frontend/src/pages/drop-and-fusion.vue +++ b/packages/frontend/src/pages/drop-and-fusion.vue @@ -15,13 +15,13 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSpacer v-if="!gameStarted" :contentMax="800"> <div :class="$style.root"> <div class="_gaps"> - <div :class="$style.frame" style="text-align: center;"> - <div :class="$style.frameInner"> + <div class="_woodenFrame" style="text-align: center;"> + <div class="_woodenFrameInner"> <img src="/client-assets/drop-and-fusion/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/> </div> </div> - <div :class="$style.frame" style="text-align: center;"> - <div :class="$style.frameInner"> + <div class="_woodenFrame" style="text-align: center;"> + <div class="_woodenFrameInner"> <div class="_gaps" style="padding: 16px;"> <MkSelect v-model="gameMode"> <option value="normal">NORMAL</option> @@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton primary gradate large rounded inline @click="start">{{ i18n.ts.start }}</MkButton> </div> </div> - <div :class="$style.frameInner"> + <div class="_woodenFrameInner"> <div class="_gaps" style="padding: 16px;"> <div style="font-size: 90%;"><i class="ti ti-music"></i> {{ i18n.ts.soundWillBePlayed }}</div> <MkSwitch v-model="mute"> @@ -42,10 +42,10 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </div> - <div :class="$style.frame"> - <div :class="$style.frameInner"> + <div class="_woodenFrame"> + <div class="_woodenFrameInner"> <div class="_gaps_s" style="padding: 16px;"> - <div><b>{{ i18n.tsx.lastNDays({ n: 7 }) }} {{ i18n.ts.ranking }}</b> ({{ gameMode }})</div> + <div><b>{{ i18n.tsx.lastNDays({ n: 7 }) }} {{ i18n.ts.ranking }}</b> ({{ gameMode.toUpperCase() }})</div> <div v-if="ranking" class="_gaps_s"> <div v-for="r in ranking" :key="r.id" :class="$style.rankingRecord"> <MkAvatar :link="true" style="width: 24px; height: 24px; margin-right: 4px;" :user="r.user"/> @@ -57,8 +57,8 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </div> - <div :class="$style.frame"> - <div :class="$style.frameInner" style="padding: 16px;"> + <div class="_woodenFrame"> + <div class="_woodenFrameInner" style="padding: 16px;"> <div style="font-weight: bold;">{{ i18n.ts._bubbleGame.howToPlay }}</div> <ol> <li>{{ i18n.ts._bubbleGame._howToPlay.section1 }}</li> @@ -67,8 +67,8 @@ SPDX-License-Identifier: AGPL-3.0-only </ol> </div> </div> - <div :class="$style.frame"> - <div :class="$style.frameInner"> + <div class="_woodenFrame"> + <div class="_woodenFrameInner"> <div class="_gaps_s" style="padding: 16px;"> <div><b>Credit</b></div> <div> @@ -149,38 +149,6 @@ definePageMetadata(() => ({ } } -.frame { - padding: 7px; - background: #8C4F26; - box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c; - border-radius: 10px; -} - -.frameH { - display: flex; - gap: 6px; -} - -.frameInner { - padding: 8px; - margin-top: 8px; - background: #F1E8DC; - box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410; - border-radius: 6px; - color: #693410; - - &:first-child { - margin-top: 0; - } -} - -.frameDivider { - height: 0; - border: none; - border-top: 1px solid #693410; - border-bottom: 1px solid #ce8a5c; -} - .rankingRecord { display: flex; line-height: 24px; diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue index 12e9416f72..16769ef360 100644 --- a/packages/frontend/src/pages/emoji-edit-dialog.vue +++ b/packages/frontend/src/pages/emoji-edit-dialog.vue @@ -135,7 +135,7 @@ async function addRole() { const { canceled, result: role } = await os.select({ items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ text: r.name, value: r })), }); - if (canceled) return; + if (canceled || role == null) return; rolesThatCanBeUsedThisEmojiAsReaction.value.push(role); } diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index 2f1557182a..cb7fe2866c 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -39,6 +39,9 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="isBlocked" :disabled="!meta || !instance" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch> <MkSwitch v-model="isSilenced" :disabled="!meta || !instance" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch> <MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton> + <MkTextarea v-model="moderationNote" manualSave> + <template #label>{{ i18n.ts.moderationNote }}</template> + </MkTextarea> </div> </FormSection> @@ -119,7 +122,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref, computed } from 'vue'; +import { ref, computed, watch } from 'vue'; import * as Misskey from 'misskey-js'; import MkChart from '@/components/MkChart.vue'; import MkObjectView from '@/components/MkObjectView.vue'; @@ -141,6 +144,7 @@ import MkPagination from '@/components/MkPagination.vue'; import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; import { dateString } from '@/filters/date.js'; +import MkTextarea from '@/components/MkTextarea.vue'; const props = defineProps<{ host: string; @@ -155,6 +159,7 @@ const suspended = ref(false); const isBlocked = ref(false); const isSilenced = ref(false); const faviconUrl = ref<string | null>(null); +const moderationNote = ref(''); const usersPagination = { endpoint: iAmModerator ? 'admin/show-users' : 'users' as const, @@ -167,6 +172,10 @@ const usersPagination = { offsetMode: true, }; +watch(moderationNote, async () => { + await misskeyApi('admin/federation/update-instance', { host: instance.value.host, moderationNote: moderationNote.value }); +}); + async function fetch(): Promise<void> { if (iAmAdmin) { meta.value = await misskeyApi('admin/meta'); @@ -178,6 +187,7 @@ async function fetch(): Promise<void> { isBlocked.value = instance.value?.isBlocked ?? false; isSilenced.value = instance.value?.isSilenced ?? false; faviconUrl.value = getProxiedImageUrlNullable(instance.value?.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.value?.iconUrl, 'preview'); + moderationNote.value = instance.value?.moderationNote; } async function toggleBlock(): Promise<void> { diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue index 7db6fa5395..28f5838296 100644 --- a/packages/frontend/src/pages/notifications.vue +++ b/packages/frontend/src/pages/notifications.vue @@ -52,7 +52,7 @@ const directNotesPagination = { function setFilter(ev) { const typeItems = notificationTypes.map(t => ({ text: i18n.ts._notification._types[t], - active: includeTypes.value && includeTypes.value.includes(t), + active: (includeTypes.value && includeTypes.value.includes(t)) ?? false, action: () => { includeTypes.value = [t]; }, @@ -63,7 +63,7 @@ function setFilter(ev) { action: () => { includeTypes.value = null; }, - }, { type: 'divider' }, ...typeItems] : typeItems; + }, { type: 'divider' as const }, ...typeItems] : typeItems; os.popupMenu(items, ev.currentTarget ?? ev.target); } diff --git a/packages/frontend/src/pages/reversi/game.board.vue b/packages/frontend/src/pages/reversi/game.board.vue index cf7cec6b5e..5259dfa29a 100644 --- a/packages/frontend/src/pages/reversi/game.board.vue +++ b/packages/frontend/src/pages/reversi/game.board.vue @@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> - <div :class="$style.board"> + <div class="_woodenFrame"> <div :class="$style.boardInner"> <div v-if="showBoardLabels" :class="$style.labelsX"> <span v-for="i in game.map[0].length" :key="i" :class="$style.labelsXLabel">{{ String.fromCharCode(64 + i) }}</span> @@ -124,8 +124,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkFolder> <template #label>{{ i18n.ts.options }}</template> <div class="_gaps_s" style="text-align: left;"> - <MkSwitch v-model="showBoardLabels">Show labels</MkSwitch> - <MkSwitch v-model="useAvatarAsStone">useAvatarAsStone</MkSwitch> + <MkSwitch v-model="showBoardLabels">{{ i18n.ts._reversi.showBoardLabels }}</MkSwitch> + <MkSwitch v-model="useAvatarAsStone">{{ i18n.ts._reversi.useAvatarAsStone }}</MkSwitch> </div> </MkFolder> @@ -248,7 +248,7 @@ if (game.value.isStarted && !game.value.isEnded) { crc32: crc32.toString(), }).then((res) => { if (res.desynced) { - console.log('resynced'); + if (_DEV_) console.log('resynced'); restoreGame(res.game!); } }); @@ -500,17 +500,6 @@ $gap: 4px; text-align: center; } -.board { - width: 100%; - box-sizing: border-box; - margin: 0 auto; - - padding: 7px; - background: #8C4F26; - box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c; - border-radius: 12px; -} - .boardInner { padding: 32px; diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue index cd38f9850f..1919f80864 100644 --- a/packages/frontend/src/pages/settings/drive.vue +++ b/packages/frontend/src/pages/settings/drive.vue @@ -113,7 +113,7 @@ if (defaultStore.state.uploadFolder) { function chooseUploadFolder() { os.selectDriveFolder(false).then(async folder => { - defaultStore.set('uploadFolder', folder ? folder.id : null); + defaultStore.set('uploadFolder', folder[0] ? folder[0].id : null); os.success(); if (defaultStore.state.uploadFolder) { uploadFolder.value = await misskeyApi('drive/folders/show', { diff --git a/packages/frontend/src/pages/settings/emoji-picker.vue b/packages/frontend/src/pages/settings/emoji-picker.vue index 79969427ec..ce296ec183 100644 --- a/packages/frontend/src/pages/settings/emoji-picker.vue +++ b/packages/frontend/src/pages/settings/emoji-picker.vue @@ -213,7 +213,7 @@ async function pickEmoji(itemsRef: Ref<string[]>, ev: MouseEvent) { os.pickEmoji(getHTMLElement(ev), { showPinned: false, }).then(it => { - const emoji = it as string; + const emoji = it; if (!itemsRef.value.includes(emoji)) { itemsRef.value.push(emoji); } diff --git a/packages/frontend/src/pages/settings/notifications.notification-config.vue b/packages/frontend/src/pages/settings/notifications.notification-config.vue index d6aac63674..a36f036303 100644 --- a/packages/frontend/src/pages/settings/notifications.notification-config.vue +++ b/packages/frontend/src/pages/settings/notifications.notification-config.vue @@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <option value="following">{{ i18n.ts.following }}</option> <option value="follower">{{ i18n.ts.followers }}</option> <option value="mutualFollow">{{ i18n.ts.mutualFollow }}</option> + <option value="followingOrFollower">{{ i18n.ts.followingOrFollower }}</option> <option value="list">{{ i18n.ts.userList }}</option> <option value="never">{{ i18n.ts.none }}</option> </MkSelect> diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index febcfa32ed..70db6a5109 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only $i.notificationRecieveConfig[type]?.type === 'following' ? i18n.ts.following : $i.notificationRecieveConfig[type]?.type === 'follower' ? i18n.ts.followers : $i.notificationRecieveConfig[type]?.type === 'mutualFollow' ? i18n.ts.mutualFollow : + $i.notificationRecieveConfig[type]?.type === 'followingOrFollower' ? i18n.ts.followingOrFollower : $i.notificationRecieveConfig[type]?.type === 'list' ? i18n.ts.userList : i18n.ts.all }} @@ -34,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only <FormSection> <div class="_gaps_m"> <FormLink @click="testNotification">{{ i18n.ts._notification.sendTestNotification }}</FormLink> + <FormLink @click="flushNotification">{{ i18n.ts._notification.flushNotification }}</FormLink> </div> </FormSection> <FormSection> @@ -113,6 +115,17 @@ function testNotification(): void { misskeyApi('notifications/test-notification'); } +async function flushNotification() { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.ts.resetAreYouSure, + }); + + if (canceled) return; + + os.apiWithDialog('notifications/flush'); +} + const headerActions = computed(() => []); const headerTabs = computed(() => []); diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue index 676159d1b5..942de19d82 100644 --- a/packages/frontend/src/pages/settings/preferences-backups.vue +++ b/packages/frontend/src/pages/settings/preferences-backups.vue @@ -203,6 +203,7 @@ async function saveNew(): Promise<void> { const { canceled, result: name } = await os.inputText({ title: ts._preferencesBackups.inputName, + default: '', }); if (canceled) return; @@ -371,6 +372,7 @@ async function rename(id: string): Promise<void> { const { canceled: cancel1, result: name } = await os.inputText({ title: ts._preferencesBackups.inputName, + default: '', }); if (cancel1 || profiles.value[id].name === name) return; diff --git a/packages/frontend/src/scripts/autocomplete.ts b/packages/frontend/src/scripts/autocomplete.ts index fe515d81a1..9fc8f7843e 100644 --- a/packages/frontend/src/scripts/autocomplete.ts +++ b/packages/frontend/src/scripts/autocomplete.ts @@ -93,9 +93,11 @@ export class Autocomplete { return; } + const afterLastMfmParam = text.split(/\$\[[a-zA-Z]+/).pop(); + const isMention = mentionIndex !== -1; const isHashtag = hashtagIndex !== -1; - const isMfmParam = mfmParamIndex !== -1 && text.split(/\$\[[a-zA-Z]+/).pop()?.includes('.'); + const isMfmParam = mfmParamIndex !== -1 && afterLastMfmParam?.includes('.') && !afterLastMfmParam?.includes(' '); const isMfmTag = mfmTagIndex !== -1 && !isMfmParam; const isEmoji = emojiIndex !== -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':'); diff --git a/packages/frontend/src/scripts/check-reaction-permissions.ts b/packages/frontend/src/scripts/check-reaction-permissions.ts index c9d2a5bfc6..da704717c1 100644 --- a/packages/frontend/src/scripts/check-reaction-permissions.ts +++ b/packages/frontend/src/scripts/check-reaction-permissions.ts @@ -1,6 +1,10 @@ import * as Misskey from 'misskey-js'; +import { UnicodeEmojiDef } from './emojilist.js'; -export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple): boolean { +export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef): boolean { + if ('char' in emoji) return true; // UnicodeEmojiDefãªã‚‰å¸¸ã«ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³å¯èƒ½ + + emoji = emoji as Misskey.entities.EmojiSimple; const roleIdsThatCanBeUsedThisEmojiAsReaction = emoji.roleIdsThatCanBeUsedThisEmojiAsReaction ?? []; return !(emoji.localOnly && note.user.host !== me.host) && !(emoji.isSensitive && (note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote')) diff --git a/packages/frontend/src/scripts/clear-cache.ts b/packages/frontend/src/scripts/clear-cache.ts index f2db87c4fb..b20109ec72 100644 --- a/packages/frontend/src/scripts/clear-cache.ts +++ b/packages/frontend/src/scripts/clear-cache.ts @@ -2,14 +2,18 @@ import { unisonReload } from '@/scripts/unison-reload.js'; import * as os from '@/os.js'; import { miLocalStorage } from '@/local-storage.js'; import { fetchCustomEmojis } from '@/custom-emojis.js'; +import { fetchInstance } from '@/instance.js'; export async function clearCache() { os.waiting(); + miLocalStorage.removeItem('instance'); + miLocalStorage.removeItem('instanceCachedAt'); miLocalStorage.removeItem('locale'); miLocalStorage.removeItem('localeVersion'); miLocalStorage.removeItem('theme'); miLocalStorage.removeItem('emojis'); miLocalStorage.removeItem('lastEmojisFetchedAt'); + await fetchInstance(true); await fetchCustomEmojis(true); unisonReload(); } diff --git a/packages/frontend/src/scripts/emojilist.ts b/packages/frontend/src/scripts/emojilist.ts index 54d45e025f..f2fbeae0ed 100644 --- a/packages/frontend/src/scripts/emojilist.ts +++ b/packages/frontend/src/scripts/emojilist.ts @@ -20,6 +20,10 @@ export const emojilist: UnicodeEmojiDef[] = _emojilist.map(x => ({ category: unicodeEmojiCategories[x[2]], })); +const unicodeEmojisMap = new Map<string, UnicodeEmojiDef>( + emojilist.map(x => [x.char, x]), +); + const _indexByChar = new Map<string, number>(); const _charGroupByCategory = new Map<string, string[]>(); for (let i = 0; i < emojilist.length; i++) { @@ -35,6 +39,11 @@ for (let i = 0; i < emojilist.length; i++) { export const emojiCharByCategory = _charGroupByCategory; +export function getUnicodeEmoji(char: string): UnicodeEmojiDef | null { + // Colorize it because emojilist.json assumes that + return unicodeEmojisMap.get(colorizeEmoji(char)) ?? null; +} + export function getEmojiName(char: string): string | null { // Colorize it because emojilist.json assumes that const idx = _indexByChar.get(colorizeEmoji(char)); diff --git a/packages/frontend/src/scripts/form.ts b/packages/frontend/src/scripts/form.ts index 26a027f461..b0db404f28 100644 --- a/packages/frontend/src/scripts/form.ts +++ b/packages/frontend/src/scripts/form.ts @@ -12,29 +12,37 @@ export type FormItem = { label?: string; type: 'string'; default: string | null; + description?: string; + required?: boolean; hidden?: boolean; multiline?: boolean; + treatAsMfm?: boolean; } | { label?: string; type: 'number'; default: number | null; + description?: string; + required?: boolean; hidden?: boolean; step?: number; } | { label?: string; type: 'boolean'; default: boolean | null; + description?: string; hidden?: boolean; } | { label?: string; type: 'enum'; default: string | null; + required?: boolean; hidden?: boolean; enum: EnumItem[]; } | { label?: string; type: 'radio'; default: unknown | null; + required?: boolean; hidden?: boolean; options: { label: string; @@ -44,9 +52,12 @@ export type FormItem = { label?: string; type: 'range'; default: number | null; - step: number; + description?: string; + required?: boolean; + step?: number; min: number; max: number; + textConverter?: (value: number) => string; } | { label?: string; type: 'object'; @@ -57,6 +68,10 @@ export type FormItem = { type: 'array'; default: unknown[] | null; hidden: boolean; +} | { + type: 'button'; + content?: string; + action: (ev: MouseEvent, v: any) => void; }; export type Form = Record<string, FormItem>; diff --git a/packages/frontend/src/scripts/search-emoji.ts b/packages/frontend/src/scripts/search-emoji.ts new file mode 100644 index 0000000000..371f69b9a7 --- /dev/null +++ b/packages/frontend/src/scripts/search-emoji.ts @@ -0,0 +1,106 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export type EmojiDef = { + emoji: string; + name: string; + url: string; + aliasOf?: string; +} | { + emoji: string; + name: string; + aliasOf?: string; + isCustomEmoji?: true; +}; +type EmojiScore = { emoji: EmojiDef, score: number }; + +export function searchEmoji(query: string | null, emojiDb: EmojiDef[], max = 30): EmojiDef[] { + if (!query) { + return []; + } + + const matched = new Map<string, EmojiScore>(); + // 完全一致(エイリアスãªã—) + emojiDb.some(x => { + if (x.name === query && !x.aliasOf) { + matched.set(x.name, { emoji: x, score: query.length + 3 }); + } + return matched.size === max; + }); + + // 完全一致(エイリアス込ã¿ï¼‰ + if (matched.size < max) { + emojiDb.some(x => { + if (x.name === query && !matched.has(x.aliasOf ?? x.name)) { + matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length + 2 }); + } + return matched.size === max; + }); + } + + // 剿–¹ä¸€è‡´ï¼ˆã‚¨ã‚¤ãƒªã‚¢ã‚¹ãªã—) + if (matched.size < max) { + emojiDb.some(x => { + if (x.name.startsWith(query) && !x.aliasOf && !matched.has(x.name)) { + matched.set(x.name, { emoji: x, score: query.length + 1 }); + } + return matched.size === max; + }); + } + + // 剿–¹ä¸€è‡´ï¼ˆã‚¨ã‚¤ãƒªã‚¢ã‚¹è¾¼ã¿ï¼‰ + if (matched.size < max) { + emojiDb.some(x => { + if (x.name.startsWith(query) && !matched.has(x.aliasOf ?? x.name)) { + matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length }); + } + return matched.size === max; + }); + } + + // 部分一致(エイリアス込ã¿ï¼‰ + if (matched.size < max) { + emojiDb.some(x => { + if (x.name.includes(query) && !matched.has(x.aliasOf ?? x.name)) { + matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length - 1 }); + } + return matched.size === max; + }); + } + + // 簡易ã‚ã„ã¾ã„検索(3æ–‡å—以上) + if (matched.size < max && query.length > 3) { + const queryChars = [...query]; + const hitEmojis = new Map<string, EmojiScore>(); + + for (const x of emojiDb) { + // æ–‡å—列ã®ä½ç½®ã‚’進ã‚ãªãŒã‚‰ã€ã‚¯ã‚¨ãƒªã®æ–‡å—ã‚’é †ç•ªã«æŽ¢ã™ + + let pos = 0; + let hit = 0; + for (const c of queryChars) { + pos = x.name.indexOf(c, pos); + if (pos <= -1) break; + hit++; + } + + // åŠåˆ†ä»¥ä¸Šã®æ–‡å—ãŒå«ã¾ã‚Œã¦ã„れã°ãƒ’ットã¨ã™ã‚‹ + if (hit > Math.ceil(queryChars.length / 2) && hit - 2 > (matched.get(x.aliasOf ?? x.name)?.score ?? 0)) { + hitEmojis.set(x.aliasOf ?? x.name, { emoji: x, score: hit - 2 }); + } + } + + // ヒットã—ãŸã‚‚ã®ã‚’å…¨éƒ¨è¿½åŠ ã™ã‚‹ã¨é›‘多ã«ãªã‚‹ã®ã§ã€å…ˆé ã®6件程度ã ã‘ã«ã—ã¦ãŠã(6ä»¶ï¼ã‚ªãƒ¼ãƒˆã‚³ãƒ³ãƒ—リートã®ãƒãƒƒãƒ—アップã®ã‚µã‚¤ã‚ºåˆ†ï¼‰ + [...hitEmojis.values()] + .sort((x, y) => y.score - x.score) + .slice(0, 6) + .forEach(it => matched.set(it.emoji.name, it)); + } + + return [...matched.values()] + .sort((x, y) => y.score - x.score) + .slice(0, max) + .map(it => it.emoji); +} diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts index 9555579e0d..fcd59510df 100644 --- a/packages/frontend/src/scripts/sound.ts +++ b/packages/frontend/src/scripts/sound.ts @@ -126,7 +126,7 @@ export async function loadAudio(url: string, options?: { useCache?: boolean; }) */ export function playMisskeySfx(operationType: OperationType) { const sound = defaultStore.state[`sound_${operationType}`]; - if (sound.type == null || !canPlay) return; + if (sound.type == null || !canPlay || ('userActivation' in navigator && !navigator.userActivation.hasBeenActive)) return; canPlay = false; playMisskeySfxFile(sound).finally(() => { diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index cbec377277..0951a7d98d 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -417,6 +417,39 @@ rt { transition-timing-function: cubic-bezier(0,.5,.5,1); } +._woodenFrame { + padding: 7px; + background: #8C4F26; + box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c; + border-radius: 10px; + + --bg: #F1E8DC; + --panel: #fff; + --fg: #693410; + --switchOffBg: rgba(0, 0, 0, 0.1); + --switchOffFg: rgb(255, 255, 255); + --switchOnBg: var(--accent); + --switchOnFg: rgb(255, 255, 255); +} + +._woodenFrameH { + display: flex; + gap: 6px; +} + +._woodenFrameInner { + padding: 8px; + margin-top: 8px; + background: var(--bg); + box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410; + border-radius: 6px; + color: var(--fg); + + &:first-child { + margin-top: 0; + } +} + ._transition_zoom-enter-active, ._transition_zoom-leave-active { transition: opacity 0.5s, transform 0.5s !important; } diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue index 92d2e23d9b..bdb62dca15 100644 --- a/packages/frontend/src/ui/deck.vue +++ b/packages/frontend/src/ui/deck.vue @@ -117,6 +117,7 @@ import XMentionsColumn from '@/ui/deck/mentions-column.vue'; import XDirectColumn from '@/ui/deck/direct-column.vue'; import XRoleTimelineColumn from '@/ui/deck/role-timeline-column.vue'; import { mainRouter } from '@/router/main.js'; +import { MenuItem } from '@/types/menu.js'; const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue')); const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announcements.vue')); @@ -221,21 +222,19 @@ document.documentElement.style.scrollBehavior = 'auto'; loadDeck(); function changeProfile(ev: MouseEvent) { - const items = ref([{ + let items: MenuItem[] = [{ text: deckStore.state.profile, - active: true.valueOf, - }]); + active: true, + action: () => {}, + }]; getProfiles().then(profiles => { - items.value = [{ - text: deckStore.state.profile, - active: true.valueOf, - }, ...(profiles.filter(k => k !== deckStore.state.profile).map(k => ({ + items.push(...(profiles.filter(k => k !== deckStore.state.profile).map(k => ({ text: k, action: () => { deckStore.set('profile', k); unisonReload(); }, - }))), { type: 'divider' }, { + }))), { type: 'divider' as const }, { text: i18n.ts._deck.newProfile, icon: 'ti ti-plus', action: async () => { @@ -248,9 +247,10 @@ function changeProfile(ev: MouseEvent) { deckStore.set('profile', name); unisonReload(); }, - }]; + }); + }).then(() => { + os.popupMenu(items, ev.currentTarget ?? ev.target); }); - os.popupMenu(items, ev.currentTarget ?? ev.target); } async function deleteProfile() { diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue index 125c85130e..bd3b059497 100644 --- a/packages/frontend/src/ui/deck/channel-column.vue +++ b/packages/frontend/src/ui/deck/channel-column.vue @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-if="column.channelId"> <div style="padding: 8px; text-align: center;"> - <MkButton primary gradate rounded inline @click="post"><i class="ti ti-pencil"></i></MkButton> + <MkButton primary gradate rounded inline small @click="post"><i class="ti ti-pencil"></i></MkButton> </div> <MkTimeline ref="timeline" src="channel" :channel="column.channelId"/> </template> diff --git a/packages/frontend/src/widgets/WidgetSlideshow.vue b/packages/frontend/src/widgets/WidgetSlideshow.vue index 7a3671a240..b8efd3bda9 100644 --- a/packages/frontend/src/widgets/WidgetSlideshow.vue +++ b/packages/frontend/src/widgets/WidgetSlideshow.vue @@ -93,10 +93,10 @@ const fetch = () => { const choose = () => { os.selectDriveFolder(false).then(folder => { - if (folder == null) { + if (folder[0] == null) { return; } - widgetProps.folderId = folder.id; + widgetProps.folderId = folder[0].id; save(); fetch(); }); diff --git a/packages/frontend/test/autocomplete.test.ts b/packages/frontend/test/autocomplete.test.ts new file mode 100644 index 0000000000..394ac3a821 --- /dev/null +++ b/packages/frontend/test/autocomplete.test.ts @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { assert, describe, test } from 'vitest'; +import { searchEmoji } from '@/scripts/search-emoji.js'; + +describe('emoji autocomplete', () => { + test('åå‰ã®å®Œå…¨ä¸€è‡´ã¯åå‰ã®å‰æ–¹ä¸€è‡´ã‚ˆã‚Šå„ªå…ˆã•れる', async () => { + const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]); + assert.equal(result[0].emoji, ':foooo:'); + }); + + test('åå‰ã®å‰æ–¹ä¸€è‡´ã¯åå‰ã®éƒ¨åˆ†ä¸€è‡´ã‚ˆã‚Šå„ªå…ˆã•れる', async () => { + const result = searchEmoji('baaa', [{ emoji: ':baaar:', name: 'baaar' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]); + assert.equal(result[0].emoji, ':baaar:'); + }); + + test('åå‰ã®å®Œå…¨ä¸€è‡´ã¯ã‚¿ã‚°ã®å®Œå…¨ä¸€è‡´ã‚ˆã‚Šå„ªå…ˆã•れる', async () => { + const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]); + assert.equal(result[0].emoji, ':foooo:'); + }); + + test('åå‰ã®å‰æ–¹ä¸€è‡´ã¯ã‚¿ã‚°ã®å‰æ–¹ä¸€è‡´ã‚ˆã‚Šå„ªå…ˆã•れる', async () => { + const result = searchEmoji('foo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]); + assert.equal(result[0].emoji, ':foooo:'); + }); + + test('åå‰ã®éƒ¨åˆ†ä¸€è‡´ã¯ã‚¿ã‚°ã®éƒ¨åˆ†ä¸€è‡´ã‚ˆã‚Šå„ªå…ˆã•れる', async () => { + const result = searchEmoji('oooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]); + assert.equal(result[0].emoji, ':foooo:'); + }); +}); diff --git a/packages/frontend/test/scroll.test.ts b/packages/frontend/test/scroll.test.ts index e49ec270d5..a0b56b7221 100644 --- a/packages/frontend/test/scroll.test.ts +++ b/packages/frontend/test/scroll.test.ts @@ -9,6 +9,7 @@ import { onScrollBottom, onScrollTop } from '@/scripts/scroll.js'; describe('Scroll', () => { describe('onScrollTop', () => { + /* 動作ã—ãªã„(happy-domã®ãƒã‚°ï¼Ÿ) test('Initial onScrollTop callback for connected elements', () => { const { document } = new Window(); const div = document.createElement('div'); @@ -21,6 +22,7 @@ describe('Scroll', () => { assert.ok(called); }); + */ test('No onScrollTop callback for disconnected elements', () => { const { document } = new Window(); @@ -35,11 +37,11 @@ describe('Scroll', () => { }); describe('onScrollBottom', () => { + /* 動作ã—ãªã„(happy-domã®ãƒã‚°ï¼Ÿ) test('Initial onScrollBottom callback for connected elements', () => { const { document } = new Window(); const div = document.createElement('div'); assert.strictEqual(div.scrollTop, 0); - (div as any).scrollHeight = 100; // happy-dom has no scrollHeight document.body.append(div); @@ -48,12 +50,12 @@ describe('Scroll', () => { assert.ok(called); }); + */ test('No onScrollBottom callback for disconnected elements', () => { const { document } = new Window(); const div = document.createElement('div'); assert.strictEqual(div.scrollTop, 0); - (div as any).scrollHeight = 100; // happy-dom has no scrollHeight let called = false; onScrollBottom(div as any as HTMLElement, () => called = true); diff --git a/packages/misskey-bubble-game/package.json b/packages/misskey-bubble-game/package.json index 9de7ba005f..ddc4c2134b 100644 --- a/packages/misskey-bubble-game/package.json +++ b/packages/misskey-bubble-game/package.json @@ -29,9 +29,9 @@ "@types/matter-js": "0.19.6", "@types/node": "20.11.5", "@types/seedrandom": "3.0.8", - "@typescript-eslint/eslint-plugin": "6.18.1", - "@typescript-eslint/parser": "6.18.1", - "eslint": "8.56.0", + "@typescript-eslint/eslint-plugin": "7.1.0", + "@typescript-eslint/parser": "7.1.0", + "eslint": "8.57.0", "nodemon": "3.0.2", "typescript": "5.3.3" }, diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index f362f0b34e..2237d278f4 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -125,6 +125,9 @@ type AdminEmojiAddAliasesBulkRequest = operations['admin/emoji/add-aliases-bulk' type AdminEmojiAddRequest = operations['admin/emoji/add']['requestBody']['content']['application/json']; // @public (undocumented) +type AdminEmojiAddResponse = operations['admin/emoji/add']['responses']['200']['content']['application/json']; + +// @public (undocumented) type AdminEmojiCopyRequest = operations['admin/emoji/copy']['requestBody']['content']['application/json']; // @public (undocumented) @@ -527,6 +530,7 @@ export type Channels = { unreadNotification: (payload: Notification_2) => void; unreadMention: (payload: Note['id']) => void; readAllUnreadMentions: () => void; + notificationFlushed: () => void; unreadSpecifiedNote: (payload: Note['id']) => void; readAllUnreadSpecifiedNotes: () => void; readAllAntennas: () => void; @@ -1154,6 +1158,7 @@ declare namespace entities { AdminDriveShowFileResponse, AdminEmojiAddAliasesBulkRequest, AdminEmojiAddRequest, + AdminEmojiAddResponse, AdminEmojiCopyRequest, AdminEmojiCopyResponse, AdminEmojiDeleteBulkRequest, @@ -1708,6 +1713,7 @@ declare namespace entities { RoleCondFormulaLogics, RoleCondFormulaValueNot, RoleCondFormulaValueIsLocalOrRemote, + RoleCondFormulaValueAssignedRole, RoleCondFormulaValueCreated, RoleCondFormulaFollowersOrFollowingOrNotes, RoleCondFormulaValue, @@ -1715,7 +1721,10 @@ declare namespace entities { Role, RolePolicies, ReversiGameLite, - ReversiGameDetailed + ReversiGameDetailed, + MetaLite, + MetaDetailedOnly, + MetaDetailed } } export { entities } @@ -2224,6 +2233,15 @@ type MeDetailed = components['schemas']['MeDetailed']; type MeDetailedOnly = components['schemas']['MeDetailedOnly']; // @public (undocumented) +type MetaDetailed = components['schemas']['MetaDetailed']; + +// @public (undocumented) +type MetaDetailedOnly = components['schemas']['MetaDetailedOnly']; + +// @public (undocumented) +type MetaLite = components['schemas']['MetaLite']; + +// @public (undocumented) type MetaRequest = operations['meta']['requestBody']['content']['application/json']; // @public (undocumented) @@ -2317,6 +2335,9 @@ type ModerationLog = { type: 'unsuspendRemoteInstance'; info: ModerationLogPayloads['unsuspendRemoteInstance']; } | { + type: 'updateRemoteInstanceNote'; + info: ModerationLogPayloads['updateRemoteInstanceNote']; +} | { type: 'markSensitiveDriveFile'; info: ModerationLogPayloads['markSensitiveDriveFile']; } | { @@ -2355,7 +2376,7 @@ type ModerationLog = { }); // @public (undocumented) -export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"]; +export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "updateRemoteInstanceNote", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"]; // @public (undocumented) type MuteCreateRequest = operations['mute/create']['requestBody']['content']['application/json']; @@ -2713,6 +2734,9 @@ type RoleCondFormulaLogics = components['schemas']['RoleCondFormulaLogics']; type RoleCondFormulaValue = components['schemas']['RoleCondFormulaValue']; // @public (undocumented) +type RoleCondFormulaValueAssignedRole = components['schemas']['RoleCondFormulaValueAssignedRole']; + +// @public (undocumented) type RoleCondFormulaValueCreated = components['schemas']['RoleCondFormulaValueCreated']; // @public (undocumented) diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 051c63cbe1..a7c629119c 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2024.2.0", + "version": "2024.3.0", "description": "Misskey SDK for JavaScript", "types": "./built/dts/index.d.ts", "exports": { @@ -38,18 +38,18 @@ "@microsoft/api-extractor": "7.39.1", "@misskey-dev/eslint-plugin": "1.0.0", "@swc/jest": "0.2.31", - "@types/jest": "29.5.11", - "@types/node": "20.11.17", - "@typescript-eslint/eslint-plugin": "6.18.1", - "@typescript-eslint/parser": "6.18.1", - "eslint": "8.56.0", + "@types/jest": "29.5.12", + "@types/node": "20.11.22", + "@typescript-eslint/eslint-plugin": "7.1.0", + "@typescript-eslint/parser": "7.1.0", + "eslint": "8.57.0", "jest": "29.7.0", "jest-fetch-mock": "3.0.3", "jest-websocket-mock": "2.5.0", "mock-socket": "9.3.1", "ncp": "2.0.0", - "nodemon": "3.0.3", - "tsd": "0.30.4", + "nodemon": "3.1.0", + "tsd": "0.30.7", "typescript": "5.3.3" }, "files": [ diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts index d27413810c..5309350100 100644 --- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts +++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts @@ -3200,6 +3200,17 @@ declare module '../api.js' { * * **Credential required**: *Yes* / **Permission**: *write:notifications* */ + request<E extends 'notifications/flush', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:notifications* + */ request<E extends 'notifications/mark-all-as-read', P extends Endpoints[E]['req']>( endpoint: E, params: P, diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index 595d0d66c0..b0982e1e55 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -35,6 +35,7 @@ import type { AdminDriveShowFileResponse, AdminEmojiAddAliasesBulkRequest, AdminEmojiAddRequest, + AdminEmojiAddResponse, AdminEmojiCopyRequest, AdminEmojiCopyResponse, AdminEmojiDeleteBulkRequest, @@ -578,7 +579,7 @@ export type Endpoints = { 'admin/drive/files': { req: AdminDriveFilesRequest; res: AdminDriveFilesResponse }; 'admin/drive/show-file': { req: AdminDriveShowFileRequest; res: AdminDriveShowFileResponse }; 'admin/emoji/add-aliases-bulk': { req: AdminEmojiAddAliasesBulkRequest; res: EmptyResponse }; - 'admin/emoji/add': { req: AdminEmojiAddRequest; res: EmptyResponse }; + 'admin/emoji/add': { req: AdminEmojiAddRequest; res: AdminEmojiAddResponse }; 'admin/emoji/copy': { req: AdminEmojiCopyRequest; res: AdminEmojiCopyResponse }; 'admin/emoji/delete-bulk': { req: AdminEmojiDeleteBulkRequest; res: EmptyResponse }; 'admin/emoji/delete': { req: AdminEmojiDeleteRequest; res: EmptyResponse }; @@ -840,6 +841,7 @@ export type Endpoints = { 'notes/unrenote': { req: NotesUnrenoteRequest; res: EmptyResponse }; 'notes/user-list-timeline': { req: NotesUserListTimelineRequest; res: NotesUserListTimelineResponse }; 'notifications/create': { req: NotificationsCreateRequest; res: EmptyResponse }; + 'notifications/flush': { req: EmptyRequest; res: EmptyResponse }; 'notifications/mark-all-as-read': { req: EmptyRequest; res: EmptyResponse }; 'notifications/test-notification': { req: EmptyRequest; res: EmptyResponse }; 'page-push': { req: PagePushRequest; res: EmptyResponse }; diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index e7ed146c43..a936931e99 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -37,6 +37,7 @@ export type AdminDriveShowFileRequest = operations['admin/drive/show-file']['req export type AdminDriveShowFileResponse = operations['admin/drive/show-file']['responses']['200']['content']['application/json']; export type AdminEmojiAddAliasesBulkRequest = operations['admin/emoji/add-aliases-bulk']['requestBody']['content']['application/json']; export type AdminEmojiAddRequest = operations['admin/emoji/add']['requestBody']['content']['application/json']; +export type AdminEmojiAddResponse = operations['admin/emoji/add']['responses']['200']['content']['application/json']; export type AdminEmojiCopyRequest = operations['admin/emoji/copy']['requestBody']['content']['application/json']; export type AdminEmojiCopyResponse = operations['admin/emoji/copy']['responses']['200']['content']['application/json']; export type AdminEmojiDeleteBulkRequest = operations['admin/emoji/delete-bulk']['requestBody']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts index 6400567a2d..6f61458600 100644 --- a/packages/misskey-js/src/autogen/models.ts +++ b/packages/misskey-js/src/autogen/models.ts @@ -38,6 +38,7 @@ export type Signin = components['schemas']['Signin']; export type RoleCondFormulaLogics = components['schemas']['RoleCondFormulaLogics']; export type RoleCondFormulaValueNot = components['schemas']['RoleCondFormulaValueNot']; export type RoleCondFormulaValueIsLocalOrRemote = components['schemas']['RoleCondFormulaValueIsLocalOrRemote']; +export type RoleCondFormulaValueAssignedRole = components['schemas']['RoleCondFormulaValueAssignedRole']; export type RoleCondFormulaValueCreated = components['schemas']['RoleCondFormulaValueCreated']; export type RoleCondFormulaFollowersOrFollowingOrNotes = components['schemas']['RoleCondFormulaFollowersOrFollowingOrNotes']; export type RoleCondFormulaValue = components['schemas']['RoleCondFormulaValue']; @@ -46,3 +47,6 @@ export type Role = components['schemas']['Role']; export type RolePolicies = components['schemas']['RolePolicies']; export type ReversiGameLite = components['schemas']['ReversiGameLite']; export type ReversiGameDetailed = components['schemas']['ReversiGameDetailed']; +export type MetaLite = components['schemas']['MetaLite']; +export type MetaDetailedOnly = components['schemas']['MetaDetailedOnly']; +export type MetaDetailed = components['schemas']['MetaDetailed']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index d0d8573b40..b1e6a194f9 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -2770,6 +2770,15 @@ export type paths = { */ post: operations['notifications/create']; }; + '/notifications/flush': { + /** + * notifications/flush + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:notifications* + */ + post: operations['notifications/flush']; + }; '/notifications/mark-all-as-read': { /** * notifications/mark-all-as-read @@ -3588,7 +3597,9 @@ export type components = { faviconUrl: string | null; themeColor: string | null; }; - emojis: Record<string, never>; + emojis: { + [key: string]: string; + }; /** @enum {string} */ onlineStatus: 'unknown' | 'online' | 'active' | 'offline'; badgeRoles?: ({ @@ -3698,7 +3709,7 @@ export type components = { notificationRecieveConfig: { note?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3707,7 +3718,7 @@ export type components = { }]>; follow?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3716,7 +3727,7 @@ export type components = { }]>; mention?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3725,7 +3736,7 @@ export type components = { }]>; reply?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3734,7 +3745,7 @@ export type components = { }]>; renote?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3743,7 +3754,7 @@ export type components = { }]>; quote?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3752,7 +3763,7 @@ export type components = { }]>; reaction?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3761,7 +3772,7 @@ export type components = { }]>; pollEnded?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3770,7 +3781,7 @@ export type components = { }]>; receiveFollowRequest?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3779,7 +3790,7 @@ export type components = { }]>; followRequestAccepted?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3788,7 +3799,7 @@ export type components = { }]>; roleAssigned?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3797,7 +3808,7 @@ export type components = { }]>; achievementEarned?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3806,7 +3817,7 @@ export type components = { }]>; app?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3815,7 +3826,7 @@ export type components = { }]>; test?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -4480,6 +4491,7 @@ export type components = { infoUpdatedAt: string | null; /** Format: date-time */ latestRequestReceivedAt: string | null; + moderationNote?: string | null; }; GalleryPost: { /** @@ -4570,6 +4582,16 @@ export type components = { /** @enum {string} */ type: 'isLocal' | 'isRemote'; }; + RoleCondFormulaValueAssignedRole: { + id: string; + /** @enum {string} */ + type: 'roleAssignedTo'; + /** + * Format: id + * @example xxxxxxxxxx + */ + roleId: string; + }; RoleCondFormulaValueCreated: { id: string; /** @enum {string} */ @@ -4582,7 +4604,7 @@ export type components = { type: 'followersLessThanOrEq' | 'followersMoreThanOrEq' | 'followingLessThanOrEq' | 'followingMoreThanOrEq' | 'notesLessThanOrEq' | 'notesMoreThanOrEq'; value: number; }; - RoleCondFormulaValue: components['schemas']['RoleCondFormulaLogics'] | components['schemas']['RoleCondFormulaValueNot'] | components['schemas']['RoleCondFormulaValueIsLocalOrRemote'] | components['schemas']['RoleCondFormulaValueCreated'] | components['schemas']['RoleCondFormulaFollowersOrFollowingOrNotes']; + RoleCondFormulaValue: components['schemas']['RoleCondFormulaLogics'] | components['schemas']['RoleCondFormulaValueNot'] | components['schemas']['RoleCondFormulaValueIsLocalOrRemote'] | components['schemas']['RoleCondFormulaValueAssignedRole'] | components['schemas']['RoleCondFormulaValueCreated'] | components['schemas']['RoleCondFormulaFollowersOrFollowingOrNotes']; RoleLite: { /** * Format: id @@ -4631,6 +4653,7 @@ export type components = { gtlAvailable: boolean; ltlAvailable: boolean; canPublicNote: boolean; + mentionLimit: number; canInvite: boolean; inviteLimit: number; inviteLimitCycle: number; @@ -4723,6 +4746,96 @@ export type components = { logs: number[][]; map: string[]; }; + MetaLite: { + maintainerName: string | null; + maintainerEmail: string | null; + version: string; + providesTarball: boolean; + name: string | null; + shortName: string | null; + /** + * Format: url + * @example https://misskey.example.com + */ + uri: string; + description: string | null; + langs: string[]; + tosUrl: string | null; + /** @default https://github.com/misskey-dev/misskey */ + repositoryUrl: string | null; + /** @default https://github.com/misskey-dev/misskey/issues/new */ + feedbackUrl: string | null; + defaultDarkTheme: string | null; + defaultLightTheme: string | null; + disableRegistration: boolean; + emailRequiredForSignup: boolean; + enableHcaptcha: boolean; + hcaptchaSiteKey: string | null; + enableMcaptcha: boolean; + mcaptchaSiteKey: string | null; + mcaptchaInstanceUrl: string | null; + enableRecaptcha: boolean; + recaptchaSiteKey: string | null; + enableTurnstile: boolean; + turnstileSiteKey: string | null; + swPublickey: string | null; + /** @default /assets/ai.png */ + mascotImageUrl: string; + bannerUrl: string | null; + serverErrorImageUrl: string | null; + infoImageUrl: string | null; + notFoundImageUrl: string | null; + iconUrl: string | null; + maxNoteTextLength: number; + ads: { + /** + * Format: id + * @example xxxxxxxxxx + */ + id: string; + /** Format: url */ + url: string; + place: string; + ratio: number; + /** Format: url */ + imageUrl: string; + dayOfWeek: number; + }[]; + /** @default 0 */ + notesPerOneAd: number; + enableEmail: boolean; + enableServiceWorker: boolean; + translatorAvailable: boolean; + mediaProxy: string; + backgroundImageUrl: string | null; + impressumUrl: string | null; + logoImageUrl: string | null; + privacyPolicyUrl: string | null; + serverRules: string[]; + themeColor: string | null; + policies: components['schemas']['RolePolicies']; + }; + MetaDetailedOnly: { + features?: { + registration: boolean; + emailRequiredForSignup: boolean; + localTimeline: boolean; + globalTimeline: boolean; + hcaptcha: boolean; + turnstile: boolean; + recaptcha: boolean; + objectStorage: boolean; + serviceWorker: boolean; + /** @default true */ + miauth?: boolean; + }; + proxyAccountName: string | null; + /** @example false */ + requireSetup: boolean; + cacheRemoteFiles: boolean; + cacheRemoteSensitiveFiles: boolean; + }; + MetaDetailed: components['schemas']['MetaLite'] & components['schemas']['MetaDetailedOnly']; }; responses: never; parameters: never; @@ -6385,9 +6498,11 @@ export type operations = { }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['EmojiDetailed']; + }; }; /** @description Client error */ 400: { @@ -6998,13 +7113,13 @@ export type operations = { content: { 'application/json': { /** Format: misskey:id */ - id: string; - name: string; + id?: string; + name?: string; /** Format: misskey:id */ fileId?: string; /** @description Use `null` to reset the category. */ category?: string | null; - aliases: string[]; + aliases?: string[]; license?: string | null; isSensitive?: boolean; localOnly?: boolean; @@ -7213,7 +7328,8 @@ export type operations = { content: { 'application/json': { host: string; - isSuspended: boolean; + isSuspended?: boolean; + moderationNote?: string; }; }; }; @@ -8331,7 +8447,7 @@ export type operations = { notificationRecieveConfig: { note?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8340,7 +8456,7 @@ export type operations = { }]>; follow?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8349,7 +8465,7 @@ export type operations = { }]>; mention?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8358,7 +8474,7 @@ export type operations = { }]>; reply?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8367,7 +8483,7 @@ export type operations = { }]>; renote?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8376,7 +8492,7 @@ export type operations = { }]>; quote?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8385,7 +8501,7 @@ export type operations = { }]>; reaction?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8394,7 +8510,7 @@ export type operations = { }]>; pollEnded?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8403,7 +8519,7 @@ export type operations = { }]>; receiveFollowRequest?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8412,7 +8528,7 @@ export type operations = { }]>; followRequestAccepted?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8421,7 +8537,7 @@ export type operations = { }]>; roleAssigned?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8430,7 +8546,7 @@ export type operations = { }]>; achievementEarned?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8439,7 +8555,7 @@ export type operations = { }]>; app?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8448,7 +8564,7 @@ export type operations = { }]>; test?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -17582,8 +17698,8 @@ export type operations = { untilId?: string; /** @default true */ markAsRead?: boolean; - includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[]; - excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[]; + includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[]; + excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[]; }; }; }; @@ -18682,7 +18798,7 @@ export type operations = { notificationRecieveConfig?: { note?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18691,7 +18807,7 @@ export type operations = { }]>; follow?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18700,7 +18816,7 @@ export type operations = { }]>; mention?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18709,7 +18825,7 @@ export type operations = { }]>; reply?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18718,7 +18834,7 @@ export type operations = { }]>; renote?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18727,7 +18843,7 @@ export type operations = { }]>; quote?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18736,7 +18852,7 @@ export type operations = { }]>; reaction?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18745,7 +18861,7 @@ export type operations = { }]>; pollEnded?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18754,7 +18870,7 @@ export type operations = { }]>; receiveFollowRequest?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18763,7 +18879,7 @@ export type operations = { }]>; followRequestAccepted?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18772,7 +18888,7 @@ export type operations = { }]>; roleAssigned?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18781,7 +18897,7 @@ export type operations = { }]>; achievementEarned?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18790,7 +18906,7 @@ export type operations = { }]>; app?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18799,7 +18915,7 @@ export type operations = { }]>; test?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -19446,91 +19562,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - maintainerName: string | null; - maintainerEmail: string | null; - version: string; - providesTarball: boolean; - name: string; - shortName: string | null; - /** - * Format: url - * @example https://misskey.example.com - */ - uri: string; - description: string | null; - langs: string[]; - tosUrl: string | null; - /** @default https://github.com/misskey-dev/misskey */ - repositoryUrl: string | null; - /** @default https://github.com/misskey-dev/misskey/issues/new */ - feedbackUrl: string | null; - defaultDarkTheme: string | null; - defaultLightTheme: string | null; - disableRegistration: boolean; - cacheRemoteFiles: boolean; - cacheRemoteSensitiveFiles: boolean; - emailRequiredForSignup: boolean; - enableHcaptcha: boolean; - hcaptchaSiteKey: string | null; - enableMcaptcha: boolean; - mcaptchaSiteKey: string | null; - mcaptchaInstanceUrl: string | null; - enableRecaptcha: boolean; - recaptchaSiteKey: string | null; - enableTurnstile: boolean; - turnstileSiteKey: string | null; - swPublickey: string | null; - /** @default /assets/ai.png */ - mascotImageUrl: string; - bannerUrl: string; - serverErrorImageUrl: string | null; - infoImageUrl: string | null; - notFoundImageUrl: string | null; - iconUrl: string | null; - maxNoteTextLength: number; - ads: { - /** - * Format: id - * @example xxxxxxxxxx - */ - id: string; - /** Format: url */ - url: string; - place: string; - ratio: number; - /** Format: url */ - imageUrl: string; - dayOfWeek: number; - }[]; - /** @default 0 */ - notesPerOneAd: number; - /** @example false */ - requireSetup: boolean; - enableEmail: boolean; - enableServiceWorker: boolean; - translatorAvailable: boolean; - proxyAccountName: string | null; - mediaProxy: string; - features?: { - registration: boolean; - localTimeline: boolean; - globalTimeline: boolean; - hcaptcha: boolean; - recaptcha: boolean; - objectStorage: boolean; - serviceWorker: boolean; - /** @default true */ - miauth?: boolean; - }; - backgroundImageUrl: string | null; - impressumUrl: string | null; - logoImageUrl: string | null; - privacyPolicyUrl: string | null; - serverRules: string[]; - themeColor: string | null; - policies: components['schemas']['RolePolicies']; - }; + 'application/json': components['schemas']['MetaLite'] | components['schemas']['MetaDetailed']; }; }; /** @description Client error */ @@ -22036,6 +22068,50 @@ export type operations = { }; }; /** + * notifications/flush + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:notifications* + */ + 'notifications/flush': { + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** * notifications/mark-all-as-read * @description No description provided. * @@ -22925,10 +23001,10 @@ export type operations = { 'application/json': { /** Format: misskey:id */ flashId: string; - title: string; - summary: string; - script: string; - permissions: string[]; + title?: string; + summary?: string; + script?: string; + permissions?: string[]; /** @enum {string} */ visibility?: 'public' | 'private'; }; diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts index 0e446c1215..b690621e98 100644 --- a/packages/misskey-js/src/consts.ts +++ b/packages/misskey-js/src/consts.ts @@ -121,6 +121,7 @@ export const moderationLogTypes = [ 'resetPassword', 'suspendRemoteInstance', 'unsuspendRemoteInstance', + 'updateRemoteInstanceNote', 'markSensitiveDriveFile', 'unmarkSensitiveDriveFile', 'resolveAbuseReport', @@ -261,6 +262,12 @@ export type ModerationLogPayloads = { id: string; host: string; }; + updateRemoteInstanceNote: { + id: string; + host: string; + before: string | null; + after: string | null; + }; markSensitiveDriveFile: { fileId: string; fileUserId: string | null; diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index 772d2bbfa1..35503d6d6f 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -96,6 +96,9 @@ export type ModerationLog = { type: 'unsuspendRemoteInstance'; info: ModerationLogPayloads['unsuspendRemoteInstance']; } | { + type: 'updateRemoteInstanceNote'; + info: ModerationLogPayloads['updateRemoteInstanceNote']; +} | { type: 'markSensitiveDriveFile'; info: ModerationLogPayloads['markSensitiveDriveFile']; } | { diff --git a/packages/misskey-js/src/streaming.types.ts b/packages/misskey-js/src/streaming.types.ts index 0ba5715d68..9a86e03d69 100644 --- a/packages/misskey-js/src/streaming.types.ts +++ b/packages/misskey-js/src/streaming.types.ts @@ -40,6 +40,7 @@ export type Channels = { unreadNotification: (payload: Notification) => void; unreadMention: (payload: Note['id']) => void; readAllUnreadMentions: () => void; + notificationFlushed: () => void; unreadSpecifiedNote: (payload: Note['id']) => void; readAllUnreadSpecifiedNotes: () => void; readAllAntennas: () => void; diff --git a/packages/misskey-reversi/package.json b/packages/misskey-reversi/package.json index 52d497d3f8..7bfc890fef 100644 --- a/packages/misskey-reversi/package.json +++ b/packages/misskey-reversi/package.json @@ -27,9 +27,9 @@ "devDependencies": { "@misskey-dev/eslint-plugin": "1.0.0", "@types/node": "20.11.5", - "@typescript-eslint/eslint-plugin": "6.18.1", - "@typescript-eslint/parser": "6.18.1", - "eslint": "8.56.0", + "@typescript-eslint/eslint-plugin": "7.1.0", + "@typescript-eslint/parser": "7.1.0", + "eslint": "8.57.0", "nodemon": "3.0.2", "typescript": "5.3.3" }, diff --git a/packages/sw/package.json b/packages/sw/package.json index 244a676e86..bac0cc1ff5 100644 --- a/packages/sw/package.json +++ b/packages/sw/package.json @@ -15,11 +15,11 @@ }, "devDependencies": { "@misskey-dev/eslint-plugin": "1.0.0", - "@typescript-eslint/parser": "6.18.1", + "@typescript-eslint/parser": "7.1.0", "@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67", - "eslint": "8.56.0", + "eslint": "8.57.0", "eslint-plugin-import": "2.29.1", - "nodemon": "3.0.3", + "nodemon": "3.1.0", "typescript": "5.3.3" }, "type": "module" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f89f8a417d..5e29c1162b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,8 +13,8 @@ importers: .: dependencies: cssnano: - specifier: 6.0.3 - version: 6.0.3(postcss@8.4.33) + specifier: 6.0.5 + version: 6.0.5(postcss@8.4.35) execa: specifier: 8.0.1 version: 8.0.1 @@ -28,14 +28,14 @@ importers: specifier: 4.1.0 version: 4.1.0 postcss: - specifier: 8.4.33 - version: 8.4.33 + specifier: 8.4.35 + version: 8.4.35 tar: specifier: 6.2.0 version: 6.2.0 terser: - specifier: 5.27.0 - version: 5.27.0 + specifier: 5.28.1 + version: 5.28.1 typescript: specifier: 5.3.3 version: 5.3.3 @@ -45,20 +45,20 @@ importers: version: 4.4.0 devDependencies: '@typescript-eslint/eslint-plugin': - specifier: 6.18.1 - version: 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/parser': - specifier: 6.18.1 - version: 6.18.1(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(eslint@8.57.0)(typescript@5.3.3) cross-env: specifier: 7.0.3 version: 7.0.3 cypress: - specifier: 13.6.3 - version: 13.6.3 + specifier: 13.6.6 + version: 13.6.6 eslint: - specifier: 8.56.0 - version: 8.56.0 + specifier: 8.57.0 + version: 8.57.0 ncp: specifier: 2.0.0 version: 2.0.0 @@ -75,14 +75,14 @@ importers: specifier: 3.412.0 version: 3.412.0(@aws-sdk/client-s3@3.412.0) '@bull-board/api': - specifier: 5.14.0 - version: 5.14.0(@bull-board/ui@5.14.0) + specifier: 5.14.2 + version: 5.14.2(@bull-board/ui@5.14.2) '@bull-board/fastify': - specifier: 5.14.0 - version: 5.14.0 + specifier: 5.14.2 + version: 5.14.2 '@bull-board/ui': - specifier: 5.14.0 - version: 5.14.0 + specifier: 5.14.2 + version: 5.14.2 '@discordapp/twemoji': specifier: 15.0.2 version: 15.0.2 @@ -111,26 +111,26 @@ importers: specifier: 8.2.0 version: 8.2.0 '@misskey-dev/sharp-read-bmp': - specifier: ^1.1.1 - version: 1.1.1 + specifier: 1.2.0 + version: 1.2.0 '@misskey-dev/summaly': - specifier: ^5.0.3 + specifier: 5.0.3 version: 5.0.3 '@nestjs/common': - specifier: 10.2.10 - version: 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1) + specifier: 10.3.3 + version: 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1) '@nestjs/core': - specifier: 10.2.10 - version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + specifier: 10.3.3 + version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) '@nestjs/testing': - specifier: 10.2.10 - version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)(@nestjs/platform-express@10.3.1) + specifier: 10.3.3 + version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(@nestjs/platform-express@10.3.3) '@peertube/http-signature': specifier: 1.7.0 version: 1.7.0 '@simplewebauthn/server': - specifier: 9.0.2 - version: 9.0.2 + specifier: 9.0.3 + version: 9.0.3 '@sinonjs/fake-timers': specifier: 11.2.2 version: 11.2.2 @@ -168,8 +168,8 @@ importers: specifier: 1.20.2 version: 1.20.2 bullmq: - specifier: 5.1.9 - version: 5.1.9 + specifier: 5.4.0 + version: 5.4.0 cacheable-lookup: specifier: 7.0.0 version: 7.0.0 @@ -219,17 +219,20 @@ importers: specifier: 4.0.0 version: 4.0.0 got: - specifier: 14.1.0 - version: 14.1.0 + specifier: 14.2.0 + version: 14.2.0 happy-dom: specifier: 10.0.3 version: 10.0.3 hpagent: specifier: 1.2.0 version: 1.2.0 - http-link-header: + htmlescape: specifier: 1.1.1 version: 1.1.1 + http-link-header: + specifier: 1.1.2 + version: 1.1.2 ioredis: specifier: 5.3.2 version: 5.3.2 @@ -255,8 +258,8 @@ importers: specifier: 8.3.2 version: 8.3.2 jsrsasign: - specifier: 11.0.0 - version: 11.0.0 + specifier: 11.1.0 + version: 11.1.0 meilisearch: specifier: 0.37.0 version: 0.37.0 @@ -279,8 +282,8 @@ importers: specifier: 3.0.0-canary.1 version: 3.0.0-canary.1 nanoid: - specifier: 5.0.4 - version: 5.0.4 + specifier: 5.0.6 + version: 5.0.6 nested-property: specifier: 4.0.0 version: 4.0.0 @@ -288,8 +291,8 @@ importers: specifier: 3.3.2 version: 3.3.2 nodemailer: - specifier: 6.9.8 - version: 6.9.8 + specifier: 6.9.10 + version: 6.9.10 nsfwjs: specifier: 2.4.2 version: 2.4.2(@tensorflow/tfjs@4.4.0) @@ -348,8 +351,8 @@ importers: specifier: 0.1.4 version: 0.1.4 reflect-metadata: - specifier: 0.1.14 - version: 0.1.14 + specifier: 0.2.1 + version: 0.2.1 rename: specifier: 1.0.4 version: 1.0.4 @@ -360,14 +363,14 @@ importers: specifier: 7.8.1 version: 7.8.1 sanitize-html: - specifier: 2.11.0 - version: 2.11.0 + specifier: 2.12.1 + version: 2.12.1 secure-json-parse: specifier: 2.7.0 version: 2.7.0 sharp: - specifier: 0.32.6 - version: 0.32.6 + specifier: 0.33.2 + version: 0.33.2 slacc: specifier: 0.0.10 version: 0.0.10 @@ -378,14 +381,14 @@ importers: specifier: 2.1.0 version: 2.1.0 systeminformation: - specifier: 5.21.24 - version: 5.21.24 + specifier: 5.22.0 + version: 5.22.0 tinycolor2: specifier: 1.6.0 version: 1.6.0 tmp: - specifier: 0.2.1 - version: 0.2.1 + specifier: 0.2.2 + version: 0.2.2 tsc-alias: specifier: 1.8.8 version: 1.8.8 @@ -507,10 +510,10 @@ importers: version: 29.7.0 '@misskey-dev/eslint-plugin': specifier: 1.0.0 - version: 1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0) + version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) '@nestjs/platform-express': - specifier: 10.3.1 - version: 10.3.1(@nestjs/common@10.2.10)(@nestjs/core@10.2.10) + specifier: 10.3.3 + version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3) '@simplewebauthn/types': specifier: 9.0.1 version: 9.0.1 @@ -538,6 +541,9 @@ importers: '@types/fluent-ffmpeg': specifier: 2.1.24 version: 2.1.24 + '@types/htmlescape': + specifier: ^1.1.3 + version: 1.1.3 '@types/http-link-header': specifier: 1.0.5 version: 1.0.5 @@ -563,8 +569,8 @@ importers: specifier: 0.7.34 version: 0.7.34 '@types/node': - specifier: 20.11.17 - version: 20.11.17 + specifier: 20.11.22 + version: 20.11.22 '@types/node-fetch': specifier: 3.0.3 version: 3.0.3 @@ -581,14 +587,14 @@ importers: specifier: 0.1.2 version: 0.1.2 '@types/pg': - specifier: 8.11.0 - version: 8.11.0 + specifier: 8.11.2 + version: 8.11.2 '@types/pug': specifier: 2.0.10 version: 2.0.10 '@types/punycode': - specifier: 2.1.3 - version: 2.1.3 + specifier: 2.1.4 + version: 2.1.4 '@types/qrcode': specifier: 1.5.5 version: 1.5.5 @@ -602,11 +608,11 @@ importers: specifier: 1.0.7 version: 1.0.7 '@types/sanitize-html': - specifier: 2.9.5 - version: 2.9.5 + specifier: 2.11.0 + version: 2.11.0 '@types/semver': - specifier: 7.5.6 - version: 7.5.6 + specifier: 7.5.8 + version: 7.5.8 '@types/simple-oauth2': specifier: 5.0.7 version: 5.0.7 @@ -629,11 +635,11 @@ importers: specifier: 8.5.10 version: 8.5.10 '@typescript-eslint/eslint-plugin': - specifier: 6.18.1 - version: 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/parser': - specifier: 6.18.1 - version: 6.18.1(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(eslint@8.57.0)(typescript@5.3.3) aws-sdk-client-mock: specifier: 3.0.1 version: 3.0.1 @@ -641,11 +647,11 @@ importers: specifier: 7.0.3 version: 7.0.3 eslint: - specifier: 8.56.0 - version: 8.56.0 + specifier: 8.57.0 + version: 8.57.0 eslint-plugin-import: specifier: 2.29.1 - version: 2.29.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0) + version: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0) execa: specifier: 8.0.1 version: 8.0.1 @@ -654,13 +660,13 @@ importers: version: 9.0.0 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.11.17) + version: 29.7.0(@types/node@20.11.22) jest-mock: specifier: 29.7.0 version: 29.7.0 nodemon: - specifier: 3.0.3 - version: 3.0.3 + specifier: 3.1.0 + version: 3.1.0 pid-port: specifier: 1.0.0 version: 1.0.0 @@ -684,13 +690,13 @@ importers: version: 2024.1.0 '@rollup/plugin-json': specifier: 6.1.0 - version: 6.1.0(rollup@4.9.6) + version: 6.1.0(rollup@4.12.0) '@rollup/plugin-replace': specifier: 5.0.5 - version: 5.0.5(rollup@4.9.6) + version: 5.0.5(rollup@4.12.0) '@rollup/pluginutils': specifier: 5.1.0 - version: 5.1.0(rollup@4.9.6) + version: 5.1.0(rollup@4.12.0) '@syuilo/aiscript': specifier: 0.17.0 version: 0.17.0 @@ -701,11 +707,11 @@ importers: specifier: 15.0.0 version: 15.0.0 '@vitejs/plugin-vue': - specifier: 5.0.3 - version: 5.0.3(vite@5.1.0)(vue@3.4.18) + specifier: 5.0.4 + version: 5.0.4(vite@5.1.4)(vue@3.4.21) '@vue/compiler-sfc': - specifier: 3.4.18 - version: 3.4.18 + specifier: 3.4.21 + version: 3.4.21 aiscript-vscode: specifier: github:aiscript-dev/aiscript-vscode#v0.1.2 version: github.com/aiscript-dev/aiscript-vscode/793211d40243c8775f6b85f015c221c82cbffb07 @@ -719,26 +725,26 @@ importers: specifier: 0.0.1 version: 0.0.1 canvas-confetti: - specifier: 1.6.1 - version: 1.6.1 + specifier: 1.9.2 + version: 1.9.2 chart.js: - specifier: 4.4.1 - version: 4.4.1 + specifier: 4.4.2 + version: 4.4.2 chartjs-adapter-date-fns: specifier: 3.0.0 - version: 3.0.0(chart.js@4.4.1)(date-fns@2.30.0) + version: 3.0.0(chart.js@4.4.2)(date-fns@2.30.0) chartjs-chart-matrix: specifier: 2.0.1 - version: 2.0.1(chart.js@4.4.1) + version: 2.0.1(chart.js@4.4.2) chartjs-plugin-gradient: specifier: 0.6.1 - version: 0.6.1(chart.js@4.4.1) + version: 0.6.1(chart.js@4.4.2) chartjs-plugin-zoom: specifier: 2.0.1 - version: 2.0.1(chart.js@4.4.1) + version: 2.0.1(chart.js@4.4.2) chromatic: - specifier: 10.6.1 - version: 10.6.1 + specifier: 11.0.0 + version: 11.0.0 compare-versions: specifier: 6.1.0 version: 6.1.0 @@ -791,17 +797,17 @@ importers: specifier: 2.3.1 version: 2.3.1 rollup: - specifier: 4.9.6 - version: 4.9.6 + specifier: 4.12.0 + version: 4.12.0 sanitize-html: - specifier: 2.11.0 - version: 2.11.0 + specifier: 2.12.1 + version: 2.12.1 sass: - specifier: 1.70.0 - version: 1.70.0 + specifier: 1.71.1 + version: 1.71.1 shiki: - specifier: 1.0.0-beta.3 - version: 1.0.0-beta.3 + specifier: 1.1.7 + version: 1.1.7 strict-event-emitter-types: specifier: 2.0.0 version: 2.0.0 @@ -809,8 +815,8 @@ importers: specifier: 3.1.0 version: 3.1.0 three: - specifier: 0.160.1 - version: 0.160.1 + specifier: 0.162.0 + version: 0.162.0 throttle-debounce: specifier: 5.0.0 version: 5.0.0 @@ -830,81 +836,81 @@ importers: specifier: 9.0.1 version: 9.0.1 v-code-diff: - specifier: 1.7.2 - version: 1.7.2(vue@3.4.18) + specifier: 1.9.0 + version: 1.9.0(vue@3.4.21) vite: - specifier: 5.1.0 - version: 5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0) + specifier: 5.1.4 + version: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) vue: - specifier: 3.4.18 - version: 3.4.18(typescript@5.3.3) + specifier: 3.4.21 + version: 3.4.21(typescript@5.3.3) vuedraggable: specifier: next - version: 4.1.0(vue@3.4.18) + version: 4.1.0(vue@3.4.21) devDependencies: '@misskey-dev/eslint-plugin': specifier: 1.0.0 - version: 1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0) + version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) '@misskey-dev/summaly': specifier: 5.0.3 version: 5.0.3 '@storybook/addon-actions': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2 + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6 '@storybook/addon-essentials': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) '@storybook/addon-interactions': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2 + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6 '@storybook/addon-links': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2(react@18.2.0) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(react@18.2.0) '@storybook/addon-mdx-gfm': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2 + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6 '@storybook/addon-storysource': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2 + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6 '@storybook/blocks': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) '@storybook/components': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) '@storybook/core-events': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2 + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6 '@storybook/manager-api': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) '@storybook/preview-api': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2 + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6 '@storybook/react': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3) '@storybook/react-vite': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.6)(typescript@5.3.3)(vite@5.1.0) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(rollup@4.12.0)(typescript@5.3.3)(vite@5.1.4) '@storybook/test': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2(vitest@0.34.6) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(vitest@0.34.6) '@storybook/theming': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) '@storybook/types': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2 + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6 '@storybook/vue3': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2(vue@3.4.18) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(vue@3.4.21) '@storybook/vue3-vite': - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)(vite@5.1.0)(vue@3.4.18) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(vite@5.1.4)(vue@3.4.21) '@testing-library/vue': specifier: 8.0.2 - version: 8.0.2(@vue/compiler-sfc@3.4.18)(vue@3.4.18) + version: 8.0.2(@vue/compiler-sfc@3.4.21)(vue@3.4.21) '@types/escape-regexp': specifier: 0.0.3 version: 0.0.3 @@ -918,14 +924,14 @@ importers: specifier: 4.0.6 version: 4.0.6 '@types/node': - specifier: 20.11.17 - version: 20.11.17 + specifier: 20.11.22 + version: 20.11.22 '@types/punycode': - specifier: 2.1.3 - version: 2.1.3 + specifier: 2.1.4 + version: 2.1.4 '@types/sanitize-html': - specifier: 2.9.5 - version: 2.9.5 + specifier: 2.11.0 + version: 2.11.0 '@types/throttle-debounce': specifier: 5.0.2 version: 5.0.2 @@ -939,17 +945,17 @@ importers: specifier: 8.5.10 version: 8.5.10 '@typescript-eslint/eslint-plugin': - specifier: 6.18.1 - version: 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/parser': - specifier: 6.18.1 - version: 6.18.1(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(eslint@8.57.0)(typescript@5.3.3) '@vitest/coverage-v8': specifier: 0.34.6 version: 0.34.6(vitest@0.34.6) '@vue/runtime-core': - specifier: 3.4.18 - version: 3.4.18 + specifier: 3.4.21 + version: 3.4.21 acorn: specifier: 8.11.3 version: 8.11.3 @@ -957,23 +963,23 @@ importers: specifier: 7.0.3 version: 7.0.3 cypress: - specifier: 13.6.4 - version: 13.6.4 + specifier: 13.6.6 + version: 13.6.6 eslint: - specifier: 8.56.0 - version: 8.56.0 + specifier: 8.57.0 + version: 8.57.0 eslint-plugin-import: specifier: 2.29.1 - version: 2.29.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0) + version: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0) eslint-plugin-vue: - specifier: 9.20.1 - version: 9.20.1(eslint@8.56.0) + specifier: 9.22.0 + version: 9.22.0(eslint@8.57.0) fast-glob: specifier: 3.3.2 version: 3.3.2 happy-dom: - specifier: 10.0.3 - version: 10.0.3 + specifier: 13.6.2 + version: 13.6.2 intersection-observer: specifier: 0.12.2 version: 0.12.2 @@ -987,8 +993,8 @@ importers: specifier: 2.0.0-beta.1 version: 2.0.0-beta.1(msw@2.1.7) nodemon: - specifier: 3.0.3 - version: 3.0.3 + specifier: 3.1.0 + version: 3.1.0 prettier: specifier: 3.2.5 version: 3.2.5 @@ -1002,17 +1008,17 @@ importers: specifier: 2.0.3 version: 2.0.3 storybook: - specifier: 8.0.0-beta.2 - version: 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) storybook-addon-misskey-theme: specifier: github:misskey-dev/storybook-addon-misskey-theme - version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.0-beta.2)(@storybook/components@8.0.0-beta.2)(@storybook/core-events@8.0.0-beta.2)(@storybook/manager-api@8.0.0-beta.2)(@storybook/preview-api@8.0.0-beta.2)(@storybook/theming@8.0.0-beta.2)(@storybook/types@8.0.0-beta.2)(react-dom@18.2.0)(react@18.2.0) + version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.0-beta.6)(@storybook/components@8.0.0-beta.6)(@storybook/core-events@8.0.0-beta.6)(@storybook/manager-api@8.0.0-beta.6)(@storybook/preview-api@8.0.0-beta.6)(@storybook/theming@8.0.0-beta.6)(@storybook/types@8.0.0-beta.6)(react-dom@18.2.0)(react@18.2.0) vite-plugin-turbosnap: specifier: 1.0.3 version: 1.0.3 vitest: specifier: 0.34.6 - version: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0) + version: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1) vitest-fetch-mock: specifier: 0.2.2 version: 0.2.2(vitest@0.34.6) @@ -1021,7 +1027,7 @@ importers: version: 1.8.27 vue-eslint-parser: specifier: 9.4.2 - version: 9.4.2(eslint@8.56.0) + version: 9.4.2(eslint@8.57.0) vue-tsc: specifier: 1.8.27 version: 1.8.27(typescript@5.3.3) @@ -1046,7 +1052,7 @@ importers: devDependencies: '@misskey-dev/eslint-plugin': specifier: 1.0.0 - version: 1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0) + version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) '@types/matter-js': specifier: 0.19.6 version: 0.19.6 @@ -1057,14 +1063,14 @@ importers: specifier: 3.0.8 version: 3.0.8 '@typescript-eslint/eslint-plugin': - specifier: 6.18.1 - version: 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/parser': - specifier: 6.18.1 - version: 6.18.1(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(eslint@8.57.0)(typescript@5.3.3) eslint: - specifier: 8.56.0 - version: 8.56.0 + specifier: 8.57.0 + version: 8.57.0 nodemon: specifier: 3.0.2 version: 3.0.2 @@ -1089,31 +1095,31 @@ importers: devDependencies: '@microsoft/api-extractor': specifier: 7.39.1 - version: 7.39.1(@types/node@20.11.17) + version: 7.39.1(@types/node@20.11.22) '@misskey-dev/eslint-plugin': specifier: 1.0.0 - version: 1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0) + version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) '@swc/jest': specifier: 0.2.31 version: 0.2.31(@swc/core@1.3.105) '@types/jest': - specifier: 29.5.11 - version: 29.5.11 + specifier: 29.5.12 + version: 29.5.12 '@types/node': - specifier: 20.11.17 - version: 20.11.17 + specifier: 20.11.22 + version: 20.11.22 '@typescript-eslint/eslint-plugin': - specifier: 6.18.1 - version: 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/parser': - specifier: 6.18.1 - version: 6.18.1(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(eslint@8.57.0)(typescript@5.3.3) eslint: - specifier: 8.56.0 - version: 8.56.0 + specifier: 8.57.0 + version: 8.57.0 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.11.17) + version: 29.7.0(@types/node@20.11.22) jest-fetch-mock: specifier: 3.0.3 version: 3.0.3 @@ -1127,11 +1133,11 @@ importers: specifier: 2.0.0 version: 2.0.0 nodemon: - specifier: 3.0.3 - version: 3.0.3 + specifier: 3.1.0 + version: 3.1.0 tsd: - specifier: 0.30.4 - version: 0.30.4 + specifier: 0.30.7 + version: 0.30.7 typescript: specifier: 5.3.3 version: 5.3.3 @@ -1186,19 +1192,19 @@ importers: devDependencies: '@misskey-dev/eslint-plugin': specifier: 1.0.0 - version: 1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0) + version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) '@types/node': specifier: 20.11.5 version: 20.11.5 '@typescript-eslint/eslint-plugin': - specifier: 6.18.1 - version: 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/parser': - specifier: 6.18.1 - version: 6.18.1(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(eslint@8.57.0)(typescript@5.3.3) eslint: - specifier: 8.56.0 - version: 8.56.0 + specifier: 8.57.0 + version: 8.57.0 nodemon: specifier: 3.0.2 version: 3.0.2 @@ -1220,22 +1226,22 @@ importers: devDependencies: '@misskey-dev/eslint-plugin': specifier: 1.0.0 - version: 1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0) + version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) '@typescript-eslint/parser': - specifier: 6.18.1 - version: 6.18.1(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(eslint@8.57.0)(typescript@5.3.3) '@typescript/lib-webworker': specifier: npm:@types/serviceworker@0.0.67 version: /@types/serviceworker@0.0.67 eslint: - specifier: 8.56.0 - version: 8.56.0 + specifier: 8.57.0 + version: 8.57.0 eslint-plugin-import: specifier: 2.29.1 - version: 2.29.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0) + version: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0) nodemon: - specifier: 3.0.3 - version: 3.0.3 + specifier: 3.1.0 + version: 3.1.0 typescript: specifier: 5.3.3 version: 5.3.3 @@ -1893,6 +1899,29 @@ packages: - supports-color dev: true + /@babel/core@7.24.0: + resolution: {integrity: sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.6 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) + '@babel/helpers': 7.24.0 + '@babel/parser': 7.24.0 + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.0 + '@babel/types': 7.24.0 + convert-source-map: 2.0.0 + debug: 4.3.4(supports-color@8.1.1) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/generator@7.23.5: resolution: {integrity: sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==} engines: {node: '>=6.9.0'} @@ -1903,6 +1932,16 @@ packages: jsesc: 2.5.2 dev: true + /@babel/generator@7.23.6: + resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + '@jridgewell/gen-mapping': 0.3.2 + '@jridgewell/trace-mapping': 0.3.18 + jsesc: 2.5.2 + dev: true + /@babel/helper-annotate-as-pure@7.22.5: resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} engines: {node: '>=6.9.0'} @@ -1928,6 +1967,17 @@ packages: semver: 6.3.1 dev: true + /@babel/helper-compilation-targets@7.23.6: + resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.23.5 + '@babel/helper-validator-option': 7.23.5 + browserslist: 4.22.2 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: true + /@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.23.5): resolution: {integrity: sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==} engines: {node: '>=6.9.0'} @@ -2021,6 +2071,20 @@ packages: '@babel/helper-validator-identifier': 7.22.20 dev: true + /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.0): + resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + dev: true + /@babel/helper-optimise-call-expression@7.22.5: resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} engines: {node: '>=6.9.0'} @@ -2111,6 +2175,17 @@ packages: - supports-color dev: true + /@babel/helpers@7.24.0: + resolution: {integrity: sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.0 + '@babel/types': 7.24.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/highlight@7.23.4: resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} engines: {node: '>=6.9.0'} @@ -2127,6 +2202,14 @@ packages: dependencies: '@babel/types': 7.23.5 + /@babel/parser@7.24.0: + resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.24.0 + dev: true + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.23.5): resolution: {integrity: sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==} engines: {node: '>=6.9.0'} @@ -3094,6 +3177,15 @@ packages: '@babel/types': 7.23.5 dev: true + /@babel/template@7.24.0: + resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/parser': 7.24.0 + '@babel/types': 7.24.0 + dev: true + /@babel/traverse@7.23.5: resolution: {integrity: sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==} engines: {node: '>=6.9.0'} @@ -3112,6 +3204,24 @@ packages: - supports-color dev: true + /@babel/traverse@7.24.0: + resolution: {integrity: sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.6 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.24.0 + '@babel/types': 7.24.0 + debug: 4.3.4(supports-color@8.1.1) + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/types@7.23.5: resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==} engines: {node: '>=6.9.0'} @@ -3120,6 +3230,15 @@ packages: '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 + /@babel/types@7.24.0: + resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.23.4 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: true + /@base2/pretty-print-object@1.0.1: resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==} dev: true @@ -3128,29 +3247,29 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true - /@bull-board/api@5.14.0(@bull-board/ui@5.14.0): - resolution: {integrity: sha512-ppN9GeCH8QmCzs47CpDFwVb4Q5W2nK2QvcnbxKpjktCTonZ+5PnoWyXQvLStbcKU9SbMKAM0/OXhj4xOcSRllQ==} + /@bull-board/api@5.14.2(@bull-board/ui@5.14.2): + resolution: {integrity: sha512-0wppAGPU7ZMwWMpzkmtrlmm7ySI5immymyaRS1cVNJ54rUiGOZP5tnm+Sj7MwPdf63rxqIM843un8+PvQyARGg==} peerDependencies: - '@bull-board/ui': 5.14.0 + '@bull-board/ui': 5.14.2 dependencies: - '@bull-board/ui': 5.14.0 + '@bull-board/ui': 5.14.2 redis-info: 3.1.0 dev: false - /@bull-board/fastify@5.14.0: - resolution: {integrity: sha512-MEZbfUY74wL2dc9OJZGgYABZADlohp62MP1ZMOlC+6ZF4i7X95yxTQ9DmtIV6kkva7+abJgFGNUhtKi7Mq15Fg==} + /@bull-board/fastify@5.14.2: + resolution: {integrity: sha512-GQMK70tKOu2gjBi2pjWXMXcftzWRvQNSm+deLmGlJUgqUUbNlzIGRyvaTk7giT4CFzgKcP+hT+lphcAsGTKBQw==} dependencies: - '@bull-board/api': 5.14.0(@bull-board/ui@5.14.0) - '@bull-board/ui': 5.14.0 + '@bull-board/api': 5.14.2(@bull-board/ui@5.14.2) + '@bull-board/ui': 5.14.2 '@fastify/static': 6.12.0 '@fastify/view': 8.2.0 ejs: 3.1.9 dev: false - /@bull-board/ui@5.14.0: - resolution: {integrity: sha512-quustWmLsLbqdbCQd4Mud9Eo/2BQzfJSNSiyJt9OrtYT4AXHMgGtbFUy2Ycyda7iQjC4ScKl8f+WdFs4y+KUJA==} + /@bull-board/ui@5.14.2: + resolution: {integrity: sha512-NiyKWLjKjy29I6ySCnSYbzGX4ZJyPE4xlS5/Z5dVsF2bJLoAV+yD1obflxteJMt60FiEgLV7tfs6tMSVa+Htew==} dependencies: - '@bull-board/api': 5.14.0(@bull-board/ui@5.14.0) + '@bull-board/api': 5.14.2(@bull-board/ui@5.14.2) dev: false /@bundled-es-modules/cookie@2.0.0: @@ -3169,54 +3288,6 @@ packages: resolution: {integrity: sha512-BxOqI5LgsIQP1odU5KMwV9yoijleOPzHL18/YvNqF9KFSGF2K/DLlYAbDQsWqd/1nbaFuSkYD/191dpMtNh4vw==} dev: false - /@cbor-extract/cbor-extract-darwin-arm64@2.1.1: - resolution: {integrity: sha512-blVBy5MXz6m36Vx0DfLd7PChOQKEs8lK2bD1WJn/vVgG4FXZiZmZb2GECHFvVPA5T7OnODd9xZiL3nMCv6QUhA==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - - /@cbor-extract/cbor-extract-darwin-x64@2.1.1: - resolution: {integrity: sha512-h6KFOzqk8jXTvkOftyRIWGrd7sKQzQv2jVdTL9nKSf3D2drCvQB/LHUxAOpPXo3pv2clDtKs3xnHalpEh3rDsw==} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - - /@cbor-extract/cbor-extract-linux-arm64@2.1.1: - resolution: {integrity: sha512-SxAaRcYf8S0QHaMc7gvRSiTSr7nUYMqbUdErBEu+HYA4Q6UNydx1VwFE68hGcp1qvxcy9yT5U7gA+a5XikfwSQ==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@cbor-extract/cbor-extract-linux-arm@2.1.1: - resolution: {integrity: sha512-ds0uikdcIGUjPyraV4oJqyVE5gl/qYBpa/Wnh6l6xLE2lj/hwnjT2XcZCChdXwW/YFZ1LUHs6waoYN8PmK0nKQ==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@cbor-extract/cbor-extract-linux-x64@2.1.1: - resolution: {integrity: sha512-GVK+8fNIE9lJQHAlhOROYiI0Yd4bAZ4u++C2ZjlkS3YmO6hi+FUxe6Dqm+OKWTcMpL/l71N6CQAmaRcb4zyJuA==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@cbor-extract/cbor-extract-win32-x64@2.1.1: - resolution: {integrity: sha512-2Niq1C41dCRIDeD8LddiH+mxGlO7HJ612Ll3D/E73ZWBmycued+8ghTr/Ho3CMOWPUEr08XtyBMVXAjqF+TcKw==} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: false - optional: true - /@colors/colors@1.5.0: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -3370,6 +3441,14 @@ packages: engines: {node: '>=10.0.0'} dev: true + /@emnapi/runtime@0.45.0: + resolution: {integrity: sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==} + requiresBuild: true + dependencies: + tslib: 2.6.2 + dev: false + optional: true + /@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.2.0): resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==} peerDependencies: @@ -3770,13 +3849,13 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.56.0 + eslint: 8.57.0 eslint-visitor-keys: 3.4.3 dev: true @@ -3807,8 +3886,8 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@eslint/js@8.56.0: - resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==} + /@eslint/js@8.57.0: + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true @@ -4003,6 +4082,17 @@ packages: - supports-color dev: true + /@humanwhocodes/config-array@0.11.14: + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 2.0.2 + debug: 4.3.4(supports-color@8.1.1) + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + /@humanwhocodes/module-importer@1.0.1: resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} @@ -4017,6 +4107,198 @@ packages: resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} dev: true + /@humanwhocodes/object-schema@2.0.2: + resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} + dev: true + + /@img/sharp-darwin-arm64@0.33.2: + resolution: {integrity: sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.1 + dev: false + optional: true + + /@img/sharp-darwin-x64@0.33.2: + resolution: {integrity: sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [darwin] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.1 + dev: false + optional: true + + /@img/sharp-libvips-darwin-arm64@1.0.1: + resolution: {integrity: sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==} + engines: {macos: '>=11', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-darwin-x64@1.0.1: + resolution: {integrity: sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==} + engines: {macos: '>=10.13', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-arm64@1.0.1: + resolution: {integrity: sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==} + engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-arm@1.0.1: + resolution: {integrity: sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==} + engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-s390x@1.0.1: + resolution: {integrity: sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==} + engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-x64@1.0.1: + resolution: {integrity: sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==} + engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linuxmusl-arm64@1.0.1: + resolution: {integrity: sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==} + engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linuxmusl-x64@1.0.1: + resolution: {integrity: sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==} + engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-linux-arm64@0.33.2: + resolution: {integrity: sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.1 + dev: false + optional: true + + /@img/sharp-linux-arm@0.33.2: + resolution: {integrity: sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==} + engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.1 + dev: false + optional: true + + /@img/sharp-linux-s390x@0.33.2: + resolution: {integrity: sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==} + engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [s390x] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.1 + dev: false + optional: true + + /@img/sharp-linux-x64@0.33.2: + resolution: {integrity: sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.1 + dev: false + optional: true + + /@img/sharp-linuxmusl-arm64@0.33.2: + resolution: {integrity: sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==} + engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.1 + dev: false + optional: true + + /@img/sharp-linuxmusl-x64@0.33.2: + resolution: {integrity: sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==} + engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.1 + dev: false + optional: true + + /@img/sharp-wasm32@0.33.2: + resolution: {integrity: sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [wasm32] + requiresBuild: true + dependencies: + '@emnapi/runtime': 0.45.0 + dev: false + optional: true + + /@img/sharp-win32-ia32@0.33.2: + resolution: {integrity: sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-win32-x64@0.33.2: + resolution: {integrity: sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@ioredis/commands@1.2.0: resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} dev: false @@ -4053,7 +4335,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.11.17 + '@types/node': 20.11.22 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -4074,14 +4356,14 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.17 + '@types/node': 20.11.22 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.7.1 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.11.17) + jest-config: 29.7.0(@types/node@20.11.22) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -4116,7 +4398,7 @@ packages: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.17 + '@types/node': 20.11.22 jest-mock: 29.7.0 dev: true @@ -4143,7 +4425,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.11.17 + '@types/node': 20.11.22 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -4176,7 +4458,7 @@ packages: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.18 - '@types/node': 20.11.17 + '@types/node': 20.11.22 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -4263,7 +4545,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 20.11.17 + '@types/node': 20.11.22 '@types/yargs': 16.0.5 chalk: 4.1.2 dev: true @@ -4275,12 +4557,12 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 20.11.17 + '@types/node': 20.11.22 '@types/yargs': 17.0.19 chalk: 4.1.2 dev: true - /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.3)(vite@5.1.0): + /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.3)(vite@5.1.4): resolution: {integrity: sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==} peerDependencies: typescript: '>= 4.3.x' @@ -4294,7 +4576,7 @@ packages: magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.3.3) typescript: 5.3.3 - vite: 5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0) + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) dev: true /@jridgewell/gen-mapping@0.3.2: @@ -4339,6 +4621,10 @@ packages: resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==} dev: false + /@levischuck/tiny-cbor@0.2.2: + resolution: {integrity: sha512-f5CnPw997Y2GQ8FAvtuVVC19FX8mwNNC+1XJcIi16n/LTJifKO6QBgGLgN3YEmqtGMk17SKSuoWES3imJVxAVw==} + dev: false + /@lukeed/csprng@1.0.1: resolution: {integrity: sha512-uSvJdwQU5nK+Vdf6zxcWAY2A8r7uqe+gePwLWzJ+fsQehq18pc0I2hJKwypZ2aLM90+Er9u1xn4iLJPZ+xlL4g==} engines: {node: '>=8'} @@ -4389,24 +4675,24 @@ packages: react: 18.2.0 dev: true - /@microsoft/api-extractor-model@7.28.4(@types/node@20.11.17): + /@microsoft/api-extractor-model@7.28.4(@types/node@20.11.22): resolution: {integrity: sha512-vucgyPmgHrJ/D4/xQywAmjTmSfxAx2/aDmD6TkIoLu51FdsAfuWRbijWA48AePy60OO+l+mmy9p2P/CEeBZqig==} dependencies: '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.63.0(@types/node@20.11.17) + '@rushstack/node-core-library': 3.63.0(@types/node@20.11.22) transitivePeerDependencies: - '@types/node' dev: true - /@microsoft/api-extractor@7.39.1(@types/node@20.11.17): + /@microsoft/api-extractor@7.39.1(@types/node@20.11.22): resolution: {integrity: sha512-V0HtCufWa8hZZvSmlEzQZfINcJkHAU/bmpyJQj6w+zpI87EkR8DuBOW6RWrO9c7mUYFZoDaNgUTyKo83ytv+QQ==} hasBin: true dependencies: - '@microsoft/api-extractor-model': 7.28.4(@types/node@20.11.17) + '@microsoft/api-extractor-model': 7.28.4(@types/node@20.11.22) '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.63.0(@types/node@20.11.17) + '@rushstack/node-core-library': 3.63.0(@types/node@20.11.22) '@rushstack/rig-package': 0.5.1 '@rushstack/ts-command-line': 4.17.1 colors: 1.2.5 @@ -4450,7 +4736,7 @@ packages: eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.11.0)(eslint@8.53.0) dev: true - /@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0): + /@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0): resolution: {integrity: sha512-dh6UbcrNDVg5DD8k8Qh4ab30OPpuEYIlJCqaBV/lkIV8wNN/AfCJ2V7iTP8V8KjryM4t+sf5IqzQLQnT0mWI4A==} peerDependencies: '@typescript-eslint/eslint-plugin': '>= 6' @@ -4458,18 +4744,18 @@ packages: eslint: '>= 3' eslint-plugin-import: '>= 2' dependencies: - '@typescript-eslint/eslint-plugin': 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/parser': 6.18.1(eslint@8.56.0)(typescript@5.3.3) - eslint: 8.56.0 - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0) + '@typescript-eslint/eslint-plugin': 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3) + eslint: 8.57.0 + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0) dev: true - /@misskey-dev/sharp-read-bmp@1.1.1: - resolution: {integrity: sha512-X52BQYL/I9mafypQ+wBhst+BUlYiPWnHhKGcF6ybcYSLl+zhcV0q5mezIXHozhM0Sv0A7xCdrWmR7TCNxHLrtQ==} + /@misskey-dev/sharp-read-bmp@1.2.0: + resolution: {integrity: sha512-er4pRakXzHYfEgOFAFfQagqDouG+wLm+kwNq1I30oSdIHDa0wM3KjFpfIGQ25Fks4GcmOl1s7Zh6xoQu5dNjTw==} dependencies: decode-bmp: 0.2.1 decode-ico: 0.4.1 - sharp: 0.32.6 + sharp: 0.33.2 dev: false /@misskey-dev/summaly@5.0.3: @@ -4571,12 +4857,12 @@ packages: tar-fs: 2.1.1 dev: true - /@nestjs/common@10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1): - resolution: {integrity: sha512-fwAk931rjW8CNH2Mgwawq/7HWHH1dxkOLdcgs7U52ddLk8CtHXjejm1cbNahewlSbNhvlOl7y1STLHutE6sUqw==} + /@nestjs/common@10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1): + resolution: {integrity: sha512-LAkTe8/CF0uNWM0ecuDwUNTHCi1lVSITmmR4FQ6Ftz1E7ujQCnJ5pMRzd8JRN14vdBkxZZ8VbVF0BDUKoKNxMQ==} peerDependencies: class-transformer: '*' class-validator: '*' - reflect-metadata: ^0.1.12 + reflect-metadata: ^0.1.12 || ^0.2.0 rxjs: ^7.1.0 peerDependenciesMeta: class-transformer: @@ -4585,20 +4871,20 @@ packages: optional: true dependencies: iterare: 1.2.1 - reflect-metadata: 0.1.14 + reflect-metadata: 0.2.1 rxjs: 7.8.1 tslib: 2.6.2 uid: 2.0.2 - /@nestjs/core@10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1): - resolution: {integrity: sha512-+ckOI6BPi2ZMHikT9MCG4ctHDc4OnjhoIytrn7f2AYMMXI4bnutJhqyQKc30VDka5x3Wq6QAD57pgSP7y+JjJg==} + /@nestjs/core@10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1): + resolution: {integrity: sha512-kxJWggQAPX3RuZx9JVec69eSLaYLNIox2emkZJpfBJ5Qq7cAq7edQIt1r4LGjTKq6kFubNTPsqhWf5y7yFRBPw==} requiresBuild: true peerDependencies: '@nestjs/common': ^10.0.0 '@nestjs/microservices': ^10.0.0 '@nestjs/platform-express': ^10.0.0 '@nestjs/websockets': ^10.0.0 - reflect-metadata: ^0.1.12 + reflect-metadata: ^0.1.12 || ^0.2.0 rxjs: ^7.1.0 peerDependenciesMeta: '@nestjs/microservices': @@ -4608,27 +4894,27 @@ packages: '@nestjs/websockets': optional: true dependencies: - '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/platform-express': 10.3.1(@nestjs/common@10.2.10)(@nestjs/core@10.2.10) + '@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/platform-express': 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3) '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 iterare: 1.2.1 path-to-regexp: 3.2.0 - reflect-metadata: 0.1.14 + reflect-metadata: 0.2.1 rxjs: 7.8.1 tslib: 2.6.2 uid: 2.0.2 transitivePeerDependencies: - encoding - /@nestjs/platform-express@10.3.1(@nestjs/common@10.2.10)(@nestjs/core@10.2.10): - resolution: {integrity: sha512-Rj21quI5h4Lry7q9an+nO4ADQiQUy9A6XK74o5aTUHo3Ysm25ujqh2NgU4XbT3M2oXU9qzhE59OfhkQ7ZUvTAg==} + /@nestjs/platform-express@10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3): + resolution: {integrity: sha512-GGKSEU48Os7nYFIsUM0nutuFUGn5AbeP8gzFBiBCAtiuJWrXZXpZ58pMBYxAbMf7IrcOZFInHEukjHGAQU0OZw==} peerDependencies: '@nestjs/common': ^10.0.0 '@nestjs/core': ^10.0.0 dependencies: - '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) body-parser: 1.20.2 cors: 2.8.5 express: 4.18.2 @@ -4637,8 +4923,8 @@ packages: transitivePeerDependencies: - supports-color - /@nestjs/testing@10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)(@nestjs/platform-express@10.3.1): - resolution: {integrity: sha512-IVLUnPz/+fkBtPATYfqTIP+phN9yjkXejmj+JyhmcfPJZpxBmD1i9VSMqa4u54l37j0xkGPscQ0IXpbhqMYUKw==} + /@nestjs/testing@10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(@nestjs/platform-express@10.3.3): + resolution: {integrity: sha512-kX20GfjAImL5grd/i69uD/x7sc00BaqGcP2dRG3ilqshQUuy5DOmspLCr3a2C8xmVU7kzK4spT0oTxhe6WcCAA==} peerDependencies: '@nestjs/common': ^10.0.0 '@nestjs/core': ^10.0.0 @@ -4650,9 +4936,9 @@ packages: '@nestjs/platform-express': optional: true dependencies: - '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/platform-express': 10.3.1(@nestjs/common@10.2.10)(@nestjs/core@10.2.10) + '@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/platform-express': 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3) tslib: 2.6.2 dev: false @@ -4872,7 +5158,7 @@ packages: openapi-types: 12.1.3 dev: true - /@rollup/plugin-json@6.1.0(rollup@4.9.6): + /@rollup/plugin-json@6.1.0(rollup@4.12.0): resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -4881,11 +5167,11 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.9.6) - rollup: 4.9.6 + '@rollup/pluginutils': 5.1.0(rollup@4.12.0) + rollup: 4.12.0 dev: false - /@rollup/plugin-replace@5.0.5(rollup@4.9.6): + /@rollup/plugin-replace@5.0.5(rollup@4.12.0): resolution: {integrity: sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==} engines: {node: '>=14.0.0'} peerDependencies: @@ -4894,12 +5180,12 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.9.6) + '@rollup/pluginutils': 5.1.0(rollup@4.12.0) magic-string: 0.30.7 - rollup: 4.9.6 + rollup: 4.12.0 dev: false - /@rollup/pluginutils@5.1.0(rollup@4.9.6): + /@rollup/pluginutils@5.1.0(rollup@4.12.0): resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} engines: {node: '>=14.0.0'} peerDependencies: @@ -4911,100 +5197,100 @@ packages: '@types/estree': 1.0.5 estree-walker: 2.0.2 picomatch: 2.3.1 - rollup: 4.9.6 + rollup: 4.12.0 - /@rollup/rollup-android-arm-eabi@4.9.6: - resolution: {integrity: sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==} + /@rollup/rollup-android-arm-eabi@4.12.0: + resolution: {integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==} cpu: [arm] os: [android] requiresBuild: true optional: true - /@rollup/rollup-android-arm64@4.9.6: - resolution: {integrity: sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw==} + /@rollup/rollup-android-arm64@4.12.0: + resolution: {integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==} cpu: [arm64] os: [android] requiresBuild: true optional: true - /@rollup/rollup-darwin-arm64@4.9.6: - resolution: {integrity: sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw==} + /@rollup/rollup-darwin-arm64@4.12.0: + resolution: {integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==} cpu: [arm64] os: [darwin] requiresBuild: true optional: true - /@rollup/rollup-darwin-x64@4.9.6: - resolution: {integrity: sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog==} + /@rollup/rollup-darwin-x64@4.12.0: + resolution: {integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==} cpu: [x64] os: [darwin] requiresBuild: true optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.9.6: - resolution: {integrity: sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ==} + /@rollup/rollup-linux-arm-gnueabihf@4.12.0: + resolution: {integrity: sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==} cpu: [arm] os: [linux] requiresBuild: true optional: true - /@rollup/rollup-linux-arm64-gnu@4.9.6: - resolution: {integrity: sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ==} + /@rollup/rollup-linux-arm64-gnu@4.12.0: + resolution: {integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@rollup/rollup-linux-arm64-musl@4.9.6: - resolution: {integrity: sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ==} + /@rollup/rollup-linux-arm64-musl@4.12.0: + resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@rollup/rollup-linux-riscv64-gnu@4.9.6: - resolution: {integrity: sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA==} + /@rollup/rollup-linux-riscv64-gnu@4.12.0: + resolution: {integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==} cpu: [riscv64] os: [linux] requiresBuild: true optional: true - /@rollup/rollup-linux-x64-gnu@4.9.6: - resolution: {integrity: sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw==} + /@rollup/rollup-linux-x64-gnu@4.12.0: + resolution: {integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@rollup/rollup-linux-x64-musl@4.9.6: - resolution: {integrity: sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ==} + /@rollup/rollup-linux-x64-musl@4.12.0: + resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@rollup/rollup-win32-arm64-msvc@4.9.6: - resolution: {integrity: sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA==} + /@rollup/rollup-win32-arm64-msvc@4.12.0: + resolution: {integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==} cpu: [arm64] os: [win32] requiresBuild: true optional: true - /@rollup/rollup-win32-ia32-msvc@4.9.6: - resolution: {integrity: sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ==} + /@rollup/rollup-win32-ia32-msvc@4.12.0: + resolution: {integrity: sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==} cpu: [ia32] os: [win32] requiresBuild: true optional: true - /@rollup/rollup-win32-x64-msvc@4.9.6: - resolution: {integrity: sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ==} + /@rollup/rollup-win32-x64-msvc@4.12.0: + resolution: {integrity: sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==} cpu: [x64] os: [win32] requiresBuild: true optional: true - /@rushstack/node-core-library@3.63.0(@types/node@20.11.17): + /@rushstack/node-core-library@3.63.0(@types/node@20.11.22): resolution: {integrity: sha512-Q7B3dVpBQF1v+mUfxNcNZh5uHVR8ntcnkN5GYjbBLrxUYHBGKbnCM+OdcN+hzCpFlLBH6Ob0dEHhZ0spQwf24A==} peerDependencies: '@types/node': '*' @@ -5012,7 +5298,7 @@ packages: '@types/node': optional: true dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 colors: 1.2.5 fs-extra: 7.0.1 import-lazy: 4.0.0 @@ -5038,8 +5324,8 @@ packages: string-argv: 0.3.1 dev: true - /@shikijs/core@1.0.0-beta.3: - resolution: {integrity: sha512-SCwPom2Wn8XxNlEeqdzycU93SKgzYeVsedjqDsgZaz4XiiPpZUzlHt2NAEQTwTnPcHNZapZ6vbkwJ8P11ggL3Q==} + /@shikijs/core@1.1.7: + resolution: {integrity: sha512-gTYLUIuD1UbZp/11qozD3fWpUTuMqPSf3svDMMrL0UmlGU7D9dPw/V1FonwAorCUJBltaaESxq90jrSjQyGixg==} dev: false /@sideway/address@4.1.4: @@ -5056,18 +5342,18 @@ packages: resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} dev: true - /@simplewebauthn/server@9.0.2: - resolution: {integrity: sha512-aaWA+qVOU4byk5IDb/l+M1+7dmrAJhTb4ISJHucpsgRQcMMEes76tbGIqO2JQuA7N50tc/OBrnGKBjoKYG1kSw==} + /@simplewebauthn/server@9.0.3: + resolution: {integrity: sha512-FMZieoBosrVLFxCnxPFD9Enhd1U7D8nidVDT4MsHc6l4fdVcjoeHjDueeXCloO1k5O/fZg1fsSXXPKbY2XTzDA==} engines: {node: '>=16.0.0'} dependencies: '@hexagon/base64': 1.1.27 + '@levischuck/tiny-cbor': 0.2.2 '@peculiar/asn1-android': 2.3.10 '@peculiar/asn1-ecc': 2.3.8 '@peculiar/asn1-rsa': 2.3.8 '@peculiar/asn1-schema': 2.3.8 '@peculiar/asn1-x509': 2.3.8 '@simplewebauthn/types': 9.0.1 - cbor-x: 1.5.4 cross-fetch: 4.0.0 transitivePeerDependencies: - encoding @@ -5076,10 +5362,6 @@ packages: /@simplewebauthn/types@9.0.1: resolution: {integrity: sha512-tGSRP1QvsAvsJmnOlRQyw/mvK9gnPtjEc5fg2+m8n+QUa+D7rvrKkOYyfpy42GTs90X3RDOnqJgfHt+qO67/+w==} - /@sinclair/typebox@0.24.51: - resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} - dev: true - /@sinclair/typebox@0.27.8: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} dev: true @@ -5564,10 +5846,10 @@ packages: resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} dev: false - /@storybook/addon-actions@8.0.0-beta.2: - resolution: {integrity: sha512-sw51iot8E4aZP+z96fWLG7idrpCj/LqTV5lOcE06MU3T6/mW0OqoS7nFF+ncOtjcDsOjValmLiVQCL8m759mTQ==} + /@storybook/addon-actions@8.0.0-beta.6: + resolution: {integrity: sha512-g+X2M6Awg21vkXzRP7hWBYCdbXnxJ3BJWsP7BblYmPo2J7eJDzhQascNyTmSr0pb1/7nv+tworGviXThgvlUgw==} dependencies: - '@storybook/core-events': 8.0.0-beta.2 + '@storybook/core-events': 8.0.0-beta.6 '@storybook/global': 5.0.0 '@types/uuid': 9.0.8 dequal: 2.0.3 @@ -5575,18 +5857,18 @@ packages: uuid: 9.0.1 dev: true - /@storybook/addon-backgrounds@8.0.0-beta.2: - resolution: {integrity: sha512-cyDbV7srhuh/qaEMCvfz4dTLwnJV0VjHMivLtqSZgzhU24kekc7145KnLOOpDKzEQiAl1mVXb/7HBrykQcbKtg==} + /@storybook/addon-backgrounds@8.0.0-beta.6: + resolution: {integrity: sha512-C8MS635knAOSat5JbkpZXOiAqkDm1bKWvuVqiQfbX2into45/aAuyN3mYxveGIRTRjPJCv/UpostkLSNvfH/NQ==} dependencies: '@storybook/global': 5.0.0 memoizerific: 1.11.3 ts-dedent: 2.2.0 dev: true - /@storybook/addon-controls@8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-9rvjv4Er7WJkSeXPvCJ78GnKeUqbc7NFGZVlWl2gS3gFeLrXRgtrA5raOR+XneI51UtvAPZX89Mdeg/bQueUvQ==} + /@storybook/addon-controls@8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-G96MH7yU/KShq3lTrkgtU1IbNQXLVc3BG7miaLqzQgWFN8SSAivlu3vk1Vffui3+3Dv52WZhMKi3hueNfnM1Xw==} dependencies: - '@storybook/blocks': 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + '@storybook/blocks': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) lodash: 4.17.21 ts-dedent: 2.2.0 transitivePeerDependencies: @@ -5597,22 +5879,22 @@ packages: - supports-color dev: true - /@storybook/addon-docs@8.0.0-beta.2: - resolution: {integrity: sha512-ax9Nto8pXGmNh13IfYalBoQ/6YLYjlQkhURM5eGDqhz6lZdMLQZF/GMz3gMwSXTD8edcfamXtmMOfzWc8qR1kw==} + /@storybook/addon-docs@8.0.0-beta.6: + resolution: {integrity: sha512-VLys4EuL8XVhmu1QxUiUG5keID8v/FsC5L71Y0Wcf5D+ll6ZD8vCqEtbMY3TiJJ9NqqNIcmcG3bG6JVXOYcD8g==} dependencies: '@babel/core': 7.23.5 '@mdx-js/react': 3.0.1(@types/react@18.0.28)(react@18.2.0) - '@storybook/blocks': 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) - '@storybook/client-logger': 8.0.0-beta.2 - '@storybook/components': 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) - '@storybook/csf-plugin': 8.0.0-beta.2 - '@storybook/csf-tools': 8.0.0-beta.2 + '@storybook/blocks': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/components': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + '@storybook/csf-plugin': 8.0.0-beta.6 + '@storybook/csf-tools': 8.0.0-beta.6 '@storybook/global': 5.0.0 - '@storybook/node-logger': 8.0.0-beta.2 - '@storybook/preview-api': 8.0.0-beta.2 - '@storybook/react-dom-shim': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) - '@storybook/theming': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 8.0.0-beta.2 + '@storybook/node-logger': 8.0.0-beta.6 + '@storybook/preview-api': 8.0.0-beta.6 + '@storybook/react-dom-shim': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 8.0.0-beta.6 '@types/react': 18.0.28 fs-extra: 11.1.1 react: 18.2.0 @@ -5625,22 +5907,22 @@ packages: - supports-color dev: true - /@storybook/addon-essentials@8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-zB1sRf/ynxddBcWkzxZ55YVN5trbh2sMh9iPA+MLmKwz/tWK+f8/EoV8jfevu1ou2MS/2Jkjyk90jyZEXloVjg==} + /@storybook/addon-essentials@8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-6Vjf03c0oIavXqOK9DIN0UeH0iJFmBoVrFt1mTwydMxchyJBSP785MSd9DuFhLdYZPQTMHaR4/JhOIjdDV8mbA==} dependencies: - '@storybook/addon-actions': 8.0.0-beta.2 - '@storybook/addon-backgrounds': 8.0.0-beta.2 - '@storybook/addon-controls': 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) - '@storybook/addon-docs': 8.0.0-beta.2 - '@storybook/addon-highlight': 8.0.0-beta.2 - '@storybook/addon-measure': 8.0.0-beta.2 - '@storybook/addon-outline': 8.0.0-beta.2 - '@storybook/addon-toolbars': 8.0.0-beta.2 - '@storybook/addon-viewport': 8.0.0-beta.2 - '@storybook/core-common': 8.0.0-beta.2 - '@storybook/manager-api': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) - '@storybook/node-logger': 8.0.0-beta.2 - '@storybook/preview-api': 8.0.0-beta.2 + '@storybook/addon-actions': 8.0.0-beta.6 + '@storybook/addon-backgrounds': 8.0.0-beta.6 + '@storybook/addon-controls': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-docs': 8.0.0-beta.6 + '@storybook/addon-highlight': 8.0.0-beta.6 + '@storybook/addon-measure': 8.0.0-beta.6 + '@storybook/addon-outline': 8.0.0-beta.6 + '@storybook/addon-toolbars': 8.0.0-beta.6 + '@storybook/addon-viewport': 8.0.0-beta.6 + '@storybook/core-common': 8.0.0-beta.6 + '@storybook/manager-api': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/node-logger': 8.0.0-beta.6 + '@storybook/preview-api': 8.0.0-beta.6 ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' @@ -5650,24 +5932,24 @@ packages: - supports-color dev: true - /@storybook/addon-highlight@8.0.0-beta.2: - resolution: {integrity: sha512-Y5/I4WkhcwiE6/p3kaWz+wN1IMr6GNK8ytxsVnIQHOCUfpu1lArGuHzU4E6nN7/bmXahDO+Hz3dWGdnS5YeLXw==} + /@storybook/addon-highlight@8.0.0-beta.6: + resolution: {integrity: sha512-U+qz4TNLrw24t1eZ2Zmhl2FZKZKiwHbibq4qR5ruAFe9W5/aMHqPuBB0POroaGu3P+tyDP2G46dckMNXVraiWA==} dependencies: '@storybook/global': 5.0.0 dev: true - /@storybook/addon-interactions@8.0.0-beta.2: - resolution: {integrity: sha512-L4XLTkF8z3f6V9Z61N+t/8i1d0tECyHkaeexsRjWgXaiJst+9iSdDFCApalxemLzI6mA8tIiOkRH0+DqewvpNQ==} + /@storybook/addon-interactions@8.0.0-beta.6: + resolution: {integrity: sha512-KSigq+7vCA1tnj31MjhM7xaqickR1guZdjyXVRx7gi7qbdhSuCQv52gAkVpDapwlEuvGFCCYxzt7tmcn6dkLZQ==} dependencies: '@storybook/global': 5.0.0 - '@storybook/types': 8.0.0-beta.2 + '@storybook/types': 8.0.0-beta.6 jest-mock: 27.5.1 polished: 4.2.2 ts-dedent: 2.2.0 dev: true - /@storybook/addon-links@8.0.0-beta.2(react@18.2.0): - resolution: {integrity: sha512-hP1sBcG7/yVz6s81xW3mMS39G4rGcBiw4PWLKmILCqpBhAyog9EGXJrKrYMTdDlX9EcPd11fHbdLgRNw+UPIDg==} + /@storybook/addon-links@8.0.0-beta.6(react@18.2.0): + resolution: {integrity: sha512-+5knw5CHEb23n6Bm9Xp9nmoLRqWZ3QVGb1gNI3mGwmkpLwesohFR4fW7OrdRmzYHpS0PyYToZyfTCMYrmjBDvg==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 peerDependenciesMeta: @@ -5680,50 +5962,50 @@ packages: ts-dedent: 2.2.0 dev: true - /@storybook/addon-mdx-gfm@8.0.0-beta.2: - resolution: {integrity: sha512-yFRBEoJzeGsLcXKQmDfiT+tr1EjLJ1ktsFDjS3ymVK9DzxSbnZa1u+wIA8spAn6F5qC9uSSAng64UrVyU9JbWQ==} + /@storybook/addon-mdx-gfm@8.0.0-beta.6: + resolution: {integrity: sha512-b4pb59rrX+C/oYFeEiHb8jJn0h9WZSkHVkLIgaj0G64Nd9OpyKZXMbGpDxwMq4LTi1w65Wddi1UUQbUVVDNHRw==} dependencies: - '@storybook/node-logger': 8.0.0-beta.2 + '@storybook/node-logger': 8.0.0-beta.6 remark-gfm: 4.0.0 ts-dedent: 2.2.0 transitivePeerDependencies: - supports-color dev: true - /@storybook/addon-measure@8.0.0-beta.2: - resolution: {integrity: sha512-V0kVtV9EihgsBHZ698QtH0tPr6bwFpwjLvK/Oz/PYh97jBfjzYI1A0qfAV1ixFAr12W/Aco1BBsw+ascI+0AjA==} + /@storybook/addon-measure@8.0.0-beta.6: + resolution: {integrity: sha512-D+KzWRULcbwR8/ysD7Qbw4uWBn9gwNm9s3IeVuhupawUb3u+H4XfVCOW2rA5qry/x8aroKOhAmyKd9v4i+l3pg==} dependencies: '@storybook/global': 5.0.0 tiny-invariant: 1.3.1 dev: true - /@storybook/addon-outline@8.0.0-beta.2: - resolution: {integrity: sha512-0FNcGgUvftiML5c5j9nRbKlaYcsXKISAdHxvku/dFBd16HctX/krf4neHVcSBpP1VfU2wT/782s3BXQcRwC/4Q==} + /@storybook/addon-outline@8.0.0-beta.6: + resolution: {integrity: sha512-U+5TFTj+gtkIiIJCk6h7zbrP588CUipzVVsiDTSLl4pc+H3ylGTGncq3ZGtOyl+DCoBsQCgKxy2YWQtKHrESOw==} dependencies: '@storybook/global': 5.0.0 ts-dedent: 2.2.0 dev: true - /@storybook/addon-storysource@8.0.0-beta.2: - resolution: {integrity: sha512-t3Nsr8MvcWlS+OONimYQ01CI3pPM5CKb+spS3BI7g89gnt7nz/OdrvbTZjOVLp6AqUo0lYnmVgEcjsOL09Zdfg==} + /@storybook/addon-storysource@8.0.0-beta.6: + resolution: {integrity: sha512-J9sCZ5/KQW2hbfKsom8LmgSWJxw+Kp/7LjIHGevFfov/i9DR8i9xbh5htUwC9fx+vWGR87tez03b+oUJbyHPog==} dependencies: - '@storybook/source-loader': 8.0.0-beta.2 + '@storybook/source-loader': 8.0.0-beta.6 estraverse: 5.3.0 tiny-invariant: 1.3.1 dev: true - /@storybook/addon-toolbars@8.0.0-beta.2: - resolution: {integrity: sha512-VoTZeLZo156QE4ZkymIH2OFHaZvfDWNBG2YdG/2vcBz3XG5xqlBtM+8IIAwIQik4vHIGVqFVDwPpjzWayQFr2A==} + /@storybook/addon-toolbars@8.0.0-beta.6: + resolution: {integrity: sha512-ClT5spwh6S1rUvyFEIFQndE3VK6tpwI2cyIW4E20LajtfUmj3dOfJQX/ZbnhEH3sDBsCm97ysZ/mNR0mbBHZrg==} dev: true - /@storybook/addon-viewport@8.0.0-beta.2: - resolution: {integrity: sha512-OZzMtkOSIvLGXbODGd5UZb3KXvJNAuXfqkcrrtkSnC+8baJi+3xscVDTU5Tn8gfLz7wsGInrWchxNnXX+DKfmg==} + /@storybook/addon-viewport@8.0.0-beta.6: + resolution: {integrity: sha512-KNYGM6nVrz/Ej25W3lcpaxxJDYVXBYeGl60FWN/WlqRnjo4c4Fyufl6Xev2plQ3eI8jIvWEdGNC/Z/NQnDx1+Q==} dependencies: memoizerific: 1.11.3 dev: true - /@storybook/blocks@8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-IH8hEfPtR5N81PGydrPQdpBWGqOf6l1mXFjRjWwp1BkWvrvWv4lLk4bQ9JqpMF0zH2soKl5BUa5aP0yiufFtlg==} + /@storybook/blocks@8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-QkrWT0BELNv3UGv/dtNuB/ROZn0f9VpERbadhXLE/oNXMJLalyjEbRGM635l0lDeoqjYnWHl+tuM6DTe1Xpk2w==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -5733,18 +6015,18 @@ packages: react-dom: optional: true dependencies: - '@storybook/channels': 8.0.0-beta.2 - '@storybook/client-logger': 8.0.0-beta.2 - '@storybook/components': 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-events': 8.0.0-beta.2 + '@storybook/channels': 8.0.0-beta.6 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/components': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 8.0.0-beta.6 '@storybook/csf': 0.1.2 - '@storybook/docs-tools': 8.0.0-beta.2 + '@storybook/docs-tools': 8.0.0-beta.6 '@storybook/global': 5.0.0 '@storybook/icons': 1.2.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/manager-api': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 8.0.0-beta.2 - '@storybook/theming': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 8.0.0-beta.2 + '@storybook/manager-api': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 8.0.0-beta.6 + '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 8.0.0-beta.6 '@types/lodash': 4.14.191 color-convert: 2.0.1 dequal: 2.0.3 @@ -5765,13 +6047,13 @@ packages: - supports-color dev: true - /@storybook/builder-manager@8.0.0-beta.2: - resolution: {integrity: sha512-YC9UFESllCLmo69R8xktieWcesCbJiDxeAhMdn9mosQLSOvPlZ/ElivTx423Ombrs3saXAxtLVY5rQJQKSUHEw==} + /@storybook/builder-manager@8.0.0-beta.6: + resolution: {integrity: sha512-bB/gSsPIpU22Tc6YTjPZdw1RM6nrsuJJ9aYXGqEJTqA4l4lBUN7fwIZQ1x/pS+5LbeUO0J9lAhGXurS+m8rI2A==} dependencies: '@fal-works/esbuild-plugin-global-externals': 2.1.2 - '@storybook/core-common': 8.0.0-beta.2 - '@storybook/manager': 8.0.0-beta.2 - '@storybook/node-logger': 8.0.0-beta.2 + '@storybook/core-common': 8.0.0-beta.6 + '@storybook/manager': 8.0.0-beta.6 + '@storybook/node-logger': 8.0.0-beta.6 '@types/ejs': 3.1.2 '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.18.20) browser-assert: 1.2.1 @@ -5787,8 +6069,8 @@ packages: - supports-color dev: true - /@storybook/builder-vite@8.0.0-beta.2(typescript@5.3.3)(vite@5.1.0): - resolution: {integrity: sha512-dtkEef/pZMRkv3f+byj6rNlotXK3L+93q1kZRPkICq3V46F4D8EhPZmN/KYi8LHoyKHP/8zE9aI3Mi7GBjQZiA==} + /@storybook/builder-vite@8.0.0-beta.6(typescript@5.3.3)(vite@5.1.4): + resolution: {integrity: sha512-3P5uTZqwwcUW64Hep/VtJXpQYi5vTkmqAjwZvr8gmzr37NYq3YT/PiSGn4CaZswSx5Z/lSYq3In8oIwmj/a1/g==} peerDependencies: '@preact/preset-vite': '*' typescript: '>= 4.3.x' @@ -5802,14 +6084,15 @@ packages: vite-plugin-glimmerx: optional: true dependencies: - '@storybook/channels': 8.0.0-beta.2 - '@storybook/client-logger': 8.0.0-beta.2 - '@storybook/core-common': 8.0.0-beta.2 - '@storybook/csf-plugin': 8.0.0-beta.2 - '@storybook/node-logger': 8.0.0-beta.2 - '@storybook/preview': 8.0.0-beta.2 - '@storybook/preview-api': 8.0.0-beta.2 - '@storybook/types': 8.0.0-beta.2 + '@storybook/channels': 8.0.0-beta.6 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/core-common': 8.0.0-beta.6 + '@storybook/core-events': 8.0.0-beta.6 + '@storybook/csf-plugin': 8.0.0-beta.6 + '@storybook/node-logger': 8.0.0-beta.6 + '@storybook/preview': 8.0.0-beta.6 + '@storybook/preview-api': 8.0.0-beta.6 + '@storybook/types': 8.0.0-beta.6 '@types/find-cache-dir': 3.2.1 browser-assert: 1.2.1 es-module-lexer: 0.9.3 @@ -5819,38 +6102,39 @@ packages: magic-string: 0.30.7 ts-dedent: 2.2.0 typescript: 5.3.3 - vite: 5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0) + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) transitivePeerDependencies: - encoding - supports-color dev: true - /@storybook/channels@8.0.0-beta.2: - resolution: {integrity: sha512-6PoOkce/T3g5pf5wA/tE9JRo9ZoyhdjzZqS2gVsxKza1Ie3gICVKWA+Cu3IM7s05+fX5syHmTvzOLykwfMh9QQ==} + /@storybook/channels@8.0.0-beta.6: + resolution: {integrity: sha512-DjwJhty45gQifo+TvGqddLX+NX1iGTmZyGLxlqPMpdp+x/yq8WwVZ316Q7tLt6z6fyAmsroc3ma5p1iLhqpV7g==} dependencies: - '@storybook/client-logger': 8.0.0-beta.2 - '@storybook/core-events': 8.0.0-beta.2 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/core-events': 8.0.0-beta.6 '@storybook/global': 5.0.0 qs: 6.11.1 telejson: 7.2.0 tiny-invariant: 1.3.1 dev: true - /@storybook/cli@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-IfCYCpkOZvMQnf3i+AIdTZ4x45lfuEYNRWZYAZT8Nmnuz2gc0AKui3So4IgNB276Zmbru+OCudf05xHgoxxu3A==} + /@storybook/cli@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-sREQYnPds2bwQS7FLbRy7oaxGvOmYhPEYVf93pWKyo/qwSWyXEXbqGCGT6bNhSl/xzqXX7VryLDmuOoHmVTh1g==} hasBin: true dependencies: + '@babel/core': 7.23.5 '@babel/types': 7.23.5 '@ndelangen/get-tarball': 3.0.7 - '@storybook/codemod': 8.0.0-beta.2 - '@storybook/core-common': 8.0.0-beta.2 - '@storybook/core-events': 8.0.0-beta.2 - '@storybook/core-server': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) - '@storybook/csf-tools': 8.0.0-beta.2 - '@storybook/node-logger': 8.0.0-beta.2 - '@storybook/telemetry': 8.0.0-beta.2 - '@storybook/types': 8.0.0-beta.2 - '@types/semver': 7.5.6 + '@storybook/codemod': 8.0.0-beta.6 + '@storybook/core-common': 8.0.0-beta.6 + '@storybook/core-events': 8.0.0-beta.6 + '@storybook/core-server': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/csf-tools': 8.0.0-beta.6 + '@storybook/node-logger': 8.0.0-beta.6 + '@storybook/telemetry': 8.0.0-beta.6 + '@storybook/types': 8.0.0-beta.6 + '@types/semver': 7.5.8 '@yarnpkg/fslib': 2.10.3 '@yarnpkg/libzip': 2.3.0 chalk: 4.1.2 @@ -5885,22 +6169,22 @@ packages: - utf-8-validate dev: true - /@storybook/client-logger@8.0.0-beta.2: - resolution: {integrity: sha512-Sp2tRQO7NmwUjFgN7WTptzJhcyT75rJ+PV9TeSi5BxJXSPTKvA/e6VKFA5k83MS5AI3VBzKV//rFsqyd5+EVkg==} + /@storybook/client-logger@8.0.0-beta.6: + resolution: {integrity: sha512-XX9CSWt9NDO/1K8tTYV+yuj0ur4HznM1Vc5mY5AwT5xh0RP5HtWZ+VoJfrWYXlBoRXaj0gf8si+FO+lSW82DcQ==} dependencies: '@storybook/global': 5.0.0 dev: true - /@storybook/codemod@8.0.0-beta.2: - resolution: {integrity: sha512-s0QcLCdFsMjQmMYRfLQwPaVaYwBmT+CYp0p43xLJ9EVMydSj+So9zs2L0Tp4BN+w9yMz+QvjSq0UZvexuFmC9Q==} + /@storybook/codemod@8.0.0-beta.6: + resolution: {integrity: sha512-ttQYDkhKmtU6Qbg+Kgn4K2XXf8XMpa2euuC6PmYffBD7/qLiGfABfBc4FHKRv4yScnvKK7Ehy7K0lvipfg6tXw==} dependencies: '@babel/core': 7.23.5 '@babel/preset-env': 7.23.5(@babel/core@7.23.5) '@babel/types': 7.23.5 '@storybook/csf': 0.1.2 - '@storybook/csf-tools': 8.0.0-beta.2 - '@storybook/node-logger': 8.0.0-beta.2 - '@storybook/types': 8.0.0-beta.2 + '@storybook/csf-tools': 8.0.0-beta.6 + '@storybook/node-logger': 8.0.0-beta.6 + '@storybook/types': 8.0.0-beta.6 '@types/cross-spawn': 6.0.2 cross-spawn: 7.0.3 globby: 11.1.0 @@ -5913,19 +6197,19 @@ packages: - supports-color dev: true - /@storybook/components@8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-FsY+Sk6i/62RSPRTupUkBJEBb02Ry5Cg9XEfAa7eH5MpaxxLLIBBDxJ8y1FPepvr0Hkzqo0sBa8w3KMbTfo2ow==} + /@storybook/components@8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-J3aJtPgaSco0sefvRMBLFsWbslhKMhaS3U+5baRqlV5bjPLZN+d4P18gP1RMaw/coh6DiKEQJZuHRoPIOdt4CA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: '@radix-ui/react-slot': 1.0.2(@types/react@18.0.28)(react@18.2.0) - '@storybook/client-logger': 8.0.0-beta.2 + '@storybook/client-logger': 8.0.0-beta.6 '@storybook/csf': 0.1.2 '@storybook/global': 5.0.0 '@storybook/icons': 1.2.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/theming': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 8.0.0-beta.2 + '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 8.0.0-beta.6 memoizerific: 1.11.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -5934,13 +6218,13 @@ packages: - '@types/react' dev: true - /@storybook/core-common@8.0.0-beta.2: - resolution: {integrity: sha512-il2D+GpFg0MdVrQ04f2g5dopynleY9SbkDIfd28RCwTuMefy2exe9DEQoGFiEgBx9inJPS7L3WR0h0p6OMO9KA==} + /@storybook/core-common@8.0.0-beta.6: + resolution: {integrity: sha512-Mah4Kx/VBNhHaX6neYHTiVwfD93yf3LVVfLTS9WcJFOpek74EAAqbARV3vzOn/utOI75N7yu2PCVoKi5KkDoVw==} dependencies: - '@storybook/core-events': 8.0.0-beta.2 - '@storybook/csf-tools': 8.0.0-beta.2 - '@storybook/node-logger': 8.0.0-beta.2 - '@storybook/types': 8.0.0-beta.2 + '@storybook/core-events': 8.0.0-beta.6 + '@storybook/csf-tools': 8.0.0-beta.6 + '@storybook/node-logger': 8.0.0-beta.6 + '@storybook/types': 8.0.0-beta.6 '@yarnpkg/fslib': 2.10.3 '@yarnpkg/libzip': 2.3.0 chalk: 4.1.2 @@ -5970,35 +6254,36 @@ packages: - supports-color dev: true - /@storybook/core-events@8.0.0-beta.2: - resolution: {integrity: sha512-C2o0ShpfIFvSDyqaNaXwEfvJaaFlR0rRfvD1a65FMEFM6YAttA/es6z2yjUySUR2vfJ/vwnEtJxs7eGmuQuBmA==} + /@storybook/core-events@8.0.0-beta.6: + resolution: {integrity: sha512-ZyEVkOJ5gGGTfHjyasyeZgNGoeVJwVkLFRpV6cUl8hzOT29R5iDsf5PbJdrpF1x2pm1oLumeRckYQ7sYhr+R/w==} dependencies: ts-dedent: 2.2.0 dev: true - /@storybook/core-server@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-J96aic++0180m6KrIerWxNAbDxjUnUor7smbVWFWcvvZAM2cW77Th2tIXxs5gcyJ6LEEAea/jYV0P+/I+afdoA==} + /@storybook/core-server@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-0ciJTWZs+mCnQOUzB3WuSfkhwXKpO033M5iYK92PKu9A6KSrwdc/WCwIJHeBNnIpmxC0GEh9j6/CgIsWehwJvg==} dependencies: '@aw-web-design/x-default-browser': 1.4.126 + '@babel/core': 7.24.0 '@discoveryjs/json-ext': 0.5.7 - '@storybook/builder-manager': 8.0.0-beta.2 - '@storybook/channels': 8.0.0-beta.2 - '@storybook/core-common': 8.0.0-beta.2 - '@storybook/core-events': 8.0.0-beta.2 + '@storybook/builder-manager': 8.0.0-beta.6 + '@storybook/channels': 8.0.0-beta.6 + '@storybook/core-common': 8.0.0-beta.6 + '@storybook/core-events': 8.0.0-beta.6 '@storybook/csf': 0.1.2 - '@storybook/csf-tools': 8.0.0-beta.2 + '@storybook/csf-tools': 8.0.0-beta.6 '@storybook/docs-mdx': 3.0.0 '@storybook/global': 5.0.0 - '@storybook/manager': 8.0.0-beta.2 - '@storybook/manager-api': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) - '@storybook/node-logger': 8.0.0-beta.2 - '@storybook/preview-api': 8.0.0-beta.2 - '@storybook/telemetry': 8.0.0-beta.2 - '@storybook/types': 8.0.0-beta.2 + '@storybook/manager': 8.0.0-beta.6 + '@storybook/manager-api': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/node-logger': 8.0.0-beta.6 + '@storybook/preview-api': 8.0.0-beta.6 + '@storybook/telemetry': 8.0.0-beta.6 + '@storybook/types': 8.0.0-beta.6 '@types/detect-port': 1.3.2 '@types/node': 18.17.15 '@types/pretty-hrtime': 1.0.1 - '@types/semver': 7.5.6 + '@types/semver': 7.5.8 better-opn: 3.0.2 chalk: 4.1.2 cli-table3: 0.6.3 @@ -6007,7 +6292,7 @@ packages: express: 4.18.2 fs-extra: 11.1.1 globby: 11.1.0 - ip: 2.0.0 + ip: 2.0.1 lodash: 4.17.21 open: 8.4.2 pretty-hrtime: 1.0.3 @@ -6030,24 +6315,24 @@ packages: - utf-8-validate dev: true - /@storybook/csf-plugin@8.0.0-beta.2: - resolution: {integrity: sha512-gdOiI57mkMwgPXnONE1bY4myX2dkol2UdzHYB12QEp9rxE+DHFudCYhxHIj4uyTybTXCjffXAgjlFyT8vBfYUA==} + /@storybook/csf-plugin@8.0.0-beta.6: + resolution: {integrity: sha512-cYI/4OndODf0utV0DxJs8AOKbmjCG+pEgxQGcmPtGnkSmEuieUwpQpN7v+fEIN7IPUQLYvs0wspR0njZQAIzyA==} dependencies: - '@storybook/csf-tools': 8.0.0-beta.2 + '@storybook/csf-tools': 8.0.0-beta.6 unplugin: 1.4.0 transitivePeerDependencies: - supports-color dev: true - /@storybook/csf-tools@8.0.0-beta.2: - resolution: {integrity: sha512-vujr640EkjkCj8h9r579wugSuKdc3Hbd8GWWiWnCQCRMYW6j9Axj79W8lNOz+u3yWSy6FhqWXqUxr0eMcAv1NQ==} + /@storybook/csf-tools@8.0.0-beta.6: + resolution: {integrity: sha512-wwzbE6f8ykrvIeZlXYTba0IA8D5GPSyZ4L0+PqRAYHm3ozu0DXqtm4USDHKrjYAzuD+W+fG/6qIOQmsWYbNmpA==} dependencies: '@babel/generator': 7.23.5 '@babel/parser': 7.23.9 '@babel/traverse': 7.23.5 '@babel/types': 7.23.5 '@storybook/csf': 0.1.2 - '@storybook/types': 8.0.0-beta.2 + '@storybook/types': 8.0.0-beta.6 fs-extra: 11.1.1 recast: 0.23.4 ts-dedent: 2.2.0 @@ -6065,12 +6350,12 @@ packages: resolution: {integrity: sha512-NmiGXl2HU33zpwTv1XORe9XG9H+dRUC1Jl11u92L4xr062pZtrShLmD4VKIsOQujxhhOrbxpwhNOt+6TdhyIdQ==} dev: true - /@storybook/docs-tools@8.0.0-beta.2: - resolution: {integrity: sha512-uw2F9bhbotZ/v6+FFFv2jj+Oflfd+7gVj5vQttAVQ4o+f6hSsOQkvLeRc5pbs9/ANhB4OVKp23CZBcuySfDtTg==} + /@storybook/docs-tools@8.0.0-beta.6: + resolution: {integrity: sha512-fSKXEu0vegzqC2HT1RaOKqi0+W/vIn+qa5D+dZHkj2BnceYxWAGYsX9ZZPHW6DUvvwp0WZp1vz57nPUhsLvcQg==} dependencies: - '@storybook/core-common': 8.0.0-beta.2 - '@storybook/preview-api': 8.0.0-beta.2 - '@storybook/types': 8.0.0-beta.2 + '@storybook/core-common': 8.0.0-beta.6 + '@storybook/preview-api': 8.0.0-beta.6 + '@storybook/types': 8.0.0-beta.6 '@types/doctrine': 0.0.3 assert: 2.1.0 doctrine: 3.0.0 @@ -6095,29 +6380,29 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/instrumenter@8.0.0-beta.2: - resolution: {integrity: sha512-44W0krseJHhJ4u8auD2QB6civNjBWdAuc7pxJ/IYgIO7Hd3yGnJpDrcOUpaUpPT3WhLFpbbASjQIlauED0DiXw==} + /@storybook/instrumenter@8.0.0-beta.6: + resolution: {integrity: sha512-xJ3qkvj8dce7nJEa6hmp4PDDZJMBuP5UlSKPidiMAfEsB0MeUbDulTFNDb0t1DwcH9ywinDl8TilSzG4+r1kDA==} dependencies: - '@storybook/channels': 8.0.0-beta.2 - '@storybook/client-logger': 8.0.0-beta.2 - '@storybook/core-events': 8.0.0-beta.2 + '@storybook/channels': 8.0.0-beta.6 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/core-events': 8.0.0-beta.6 '@storybook/global': 5.0.0 - '@storybook/preview-api': 8.0.0-beta.2 + '@storybook/preview-api': 8.0.0-beta.6 '@vitest/utils': 0.34.6 util: 0.12.5 dev: true - /@storybook/manager-api@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-LMgxSXqpB8Zdtmvs0rthityGE77rVgQ82gq+LBMaBEEFlwdSfYhoLLnlNLHrc3m8A2mTXJ4/aDhvTTjh9PPG5w==} + /@storybook/manager-api@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-kOGOT/yGFgKzld9IL1HREouFwZ0LpFuXZZOHBih5ydK8XT+bkWF6e3SiqthB3qtqpd0eVLAbNiPfY9R8t3qfWg==} dependencies: - '@storybook/channels': 8.0.0-beta.2 - '@storybook/client-logger': 8.0.0-beta.2 - '@storybook/core-events': 8.0.0-beta.2 + '@storybook/channels': 8.0.0-beta.6 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/core-events': 8.0.0-beta.6 '@storybook/csf': 0.1.2 '@storybook/global': 5.0.0 - '@storybook/router': 8.0.0-beta.2 - '@storybook/theming': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 8.0.0-beta.2 + '@storybook/router': 8.0.0-beta.6 + '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 8.0.0-beta.6 dequal: 2.0.3 lodash: 4.17.21 memoizerific: 1.11.3 @@ -6129,23 +6414,23 @@ packages: - react-dom dev: true - /@storybook/manager@8.0.0-beta.2: - resolution: {integrity: sha512-eho+n+gUjuNlX5HQYoOKKvWjTcL96kwp9KdAmrhUn3KMNLkG8kax9iEUSFKG2p+tR+YAxsJJzDlC9gV4XzbBGA==} + /@storybook/manager@8.0.0-beta.6: + resolution: {integrity: sha512-FeQ2/CIasSOgcTMEE3QYMFa92KeMnfEMyUVO4hHEmPh3SqPsz6OOv8p0bQvN0SWWBgZarbhFR0dKC3W10yYrXg==} dev: true - /@storybook/node-logger@8.0.0-beta.2: - resolution: {integrity: sha512-bBTayxV0B87FPL+suMGxpMfPzUhAwu/yO8c6glLJ4xVHJlUNn+tVQpLDehU6NeqgYTdAg9oh0fi9ufZoROVfMw==} + /@storybook/node-logger@8.0.0-beta.6: + resolution: {integrity: sha512-nmBlmZ8wzJiU1/ubhUmFeWQaJPBv6l6s0Cndk04omPSjROa+O1whoPhDTVGvWC28zm17tmAYVcQRujkdoi+YBA==} dev: true - /@storybook/preview-api@8.0.0-beta.2: - resolution: {integrity: sha512-eekdhIwSOI3RnLDHJViLBBoTuSmQUo7Oa1FGU/gDx7ZEofNF+k2N4FdFPRc72Dkv4SI7hGmoJJWbPA6BR2ZHww==} + /@storybook/preview-api@8.0.0-beta.6: + resolution: {integrity: sha512-V07MF1ArjBGi2EPSjrEW8pjCoW/TIwxNDilcO9cD12LHrDQGXuo/iKyR47TGUYmcJ/u1I2Eu9cjyVj9DVyppag==} dependencies: - '@storybook/channels': 8.0.0-beta.2 - '@storybook/client-logger': 8.0.0-beta.2 - '@storybook/core-events': 8.0.0-beta.2 + '@storybook/channels': 8.0.0-beta.6 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/core-events': 8.0.0-beta.6 '@storybook/csf': 0.1.2 '@storybook/global': 5.0.0 - '@storybook/types': 8.0.0-beta.2 + '@storybook/types': 8.0.0-beta.6 '@types/qs': 6.9.7 dequal: 2.0.3 lodash: 4.17.21 @@ -6156,12 +6441,12 @@ packages: util-deprecate: 1.0.2 dev: true - /@storybook/preview@8.0.0-beta.2: - resolution: {integrity: sha512-n9OqS5KRdUGD3oImCG5NzUIZabcV/A3LifD2YOYCyJHS4U9yg4Wse2o6Px8niklAstAdFFOP2iyMQxjt0iQ0DA==} + /@storybook/preview@8.0.0-beta.6: + resolution: {integrity: sha512-tp3Wyvjsbf5r5RhbCQSafArQWJAir1bmIJWGG2S4o2E3YT6TlHFpR078tNJtgXqsPyG0yhF9vhRRkDczrPX/Gw==} dev: true - /@storybook/react-dom-shim@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-z372LCk+5WbSX/nWpnYaxP4oWoeKTtmq7CHqK7pWrdtZJRwZJbsIKZdZragzO4yyfZLEuAybw7kR9qgCqz1ToA==} + /@storybook/react-dom-shim@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-l14oDKAW2jyrXynHKP6SoNGal78gXcWCgj0zLwSDWpKgAFWC7SuIneuxLv6weU1D4+f9Y9FBrz+K3CCaMgMtOA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -6170,23 +6455,23 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/react-vite@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.6)(typescript@5.3.3)(vite@5.1.0): - resolution: {integrity: sha512-PKbOW0JP03e0x9cndFkCXoCUUQ+P7C7JzUnURRz/uRGXl/DUiR1IcjxL0aNT7uv9Gm+whpnGCP977RHFrvMlqA==} + /@storybook/react-vite@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(rollup@4.12.0)(typescript@5.3.3)(vite@5.1.4): + resolution: {integrity: sha512-Tvz25pTXmhncDxprjIYsnXc68Lfa9idDybpRTRRbtvjsJyVpZogUdgz2/kddGNTuX3mqz6vmTMWiLiIVh+ytQA==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 vite: ^4.0.0 || ^5.0.0 dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.3)(vite@5.1.0) - '@rollup/pluginutils': 5.1.0(rollup@4.9.6) - '@storybook/builder-vite': 8.0.0-beta.2(typescript@5.3.3)(vite@5.1.0) - '@storybook/react': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.3)(vite@5.1.4) + '@rollup/pluginutils': 5.1.0(rollup@4.12.0) + '@storybook/builder-vite': 8.0.0-beta.6(typescript@5.3.3)(vite@5.1.4) + '@storybook/react': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3) magic-string: 0.30.7 react: 18.2.0 react-docgen: 7.0.1 react-dom: 18.2.0(react@18.2.0) - vite: 5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0) + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) transitivePeerDependencies: - '@preact/preset-vite' - encoding @@ -6196,8 +6481,8 @@ packages: - vite-plugin-glimmerx dev: true - /@storybook/react@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3): - resolution: {integrity: sha512-6Snd+u9UQHrzYkmEYi/BxJMl1FNnJI+3aXdopiwdslze9bosZg0glK4TKBvhqGwCBYLnKgzKHXhttMECWQApFA==} + /@storybook/react@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3): + resolution: {integrity: sha512-69B0c08HDYHEgZRRnkB+3z4dY/HO/GMSiRzRCNpzI0SBQzk1YwDzG9MOtkNgGqzdLK3e3DveSXb5Uyy1cB0ZiQ==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -6207,12 +6492,12 @@ packages: typescript: optional: true dependencies: - '@storybook/client-logger': 8.0.0-beta.2 - '@storybook/docs-tools': 8.0.0-beta.2 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/docs-tools': 8.0.0-beta.6 '@storybook/global': 5.0.0 - '@storybook/preview-api': 8.0.0-beta.2 - '@storybook/react-dom-shim': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 8.0.0-beta.2 + '@storybook/preview-api': 8.0.0-beta.6 + '@storybook/react-dom-shim': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 8.0.0-beta.6 '@types/escodegen': 0.0.6 '@types/estree': 0.0.51 '@types/node': 18.17.15 @@ -6236,30 +6521,30 @@ packages: - supports-color dev: true - /@storybook/router@8.0.0-beta.2: - resolution: {integrity: sha512-bnNEayh3g4KZUVmH7zlJZ0tdSE6lJZZhUDE61bjPHjweLx3xYLlev0A8EwzAjz1BBpr4H8UtyYa32C7+G1Urxw==} + /@storybook/router@8.0.0-beta.6: + resolution: {integrity: sha512-JjLyDaVzCH3kmNsOkuJ8/U2bPIoReZZ/QsgHJdfvm22T2wKNjQ+lfNrQptBgNybfi1o/Tmn9VbCdRqurSlh9Dw==} dependencies: - '@storybook/client-logger': 8.0.0-beta.2 + '@storybook/client-logger': 8.0.0-beta.6 memoizerific: 1.11.3 qs: 6.11.1 dev: true - /@storybook/source-loader@8.0.0-beta.2: - resolution: {integrity: sha512-nSGtn7y/o4aaHVI2mw2Xi/GIElfqMUah9uHFtEzl+vT0JIOs7jImR9dblpR5jGFRZM/2Waod8aXJ+hYkQ7MaQA==} + /@storybook/source-loader@8.0.0-beta.6: + resolution: {integrity: sha512-cYtjnuJZgm8MS9SsNsbuhuFz2d7j6BKRLZByBUqELrK+ftup0qqOWM+78w26qn3nPgA8myZXWxGa+V/Pjxio5w==} dependencies: '@storybook/csf': 0.1.2 - '@storybook/types': 8.0.0-beta.2 + '@storybook/types': 8.0.0-beta.6 estraverse: 5.3.0 lodash: 4.17.21 prettier: 3.2.5 dev: true - /@storybook/telemetry@8.0.0-beta.2: - resolution: {integrity: sha512-jc2w//1ZYn0EuDOjtBx27uTpThjFj2+0fNf0G83+BmZR8yJciBj+YXrzQB6FWdiKyT4WLaH4la1ZlaiJP+ZdYQ==} + /@storybook/telemetry@8.0.0-beta.6: + resolution: {integrity: sha512-3CU5Sdj8eVm0tb35GriMkDrxJyTpdGcfU/hgUnsuw+I4eHYdZsc4Boh9uXWTVNsaBaoqbD/MP1aqbfxkElqPxQ==} dependencies: - '@storybook/client-logger': 8.0.0-beta.2 - '@storybook/core-common': 8.0.0-beta.2 - '@storybook/csf-tools': 8.0.0-beta.2 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/core-common': 8.0.0-beta.6 + '@storybook/csf-tools': 8.0.0-beta.6 chalk: 4.1.2 detect-package-manager: 2.0.1 fetch-retry: 5.0.4 @@ -6270,16 +6555,16 @@ packages: - supports-color dev: true - /@storybook/test@8.0.0-beta.2(vitest@0.34.6): - resolution: {integrity: sha512-sMo5mUKMOLoPMOWvAvK++V9Db6qO5bgGr1JLmnX+1YTh3mzZzOlsKe5nEKdEBjT1iI1OstypDg+oKHAEfg5Oag==} + /@storybook/test@8.0.0-beta.6(vitest@0.34.6): + resolution: {integrity: sha512-GcV76EX3U77G+k8+0V+jAa/sJQZEuNb/4W+g/RaqGLRCEG73UADzkgRuFm60UQUBGtltvvRZU9sIPVbFTJFxuA==} dependencies: - '@storybook/client-logger': 8.0.0-beta.2 - '@storybook/core-events': 8.0.0-beta.2 - '@storybook/instrumenter': 8.0.0-beta.2 - '@storybook/preview-api': 8.0.0-beta.2 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/core-events': 8.0.0-beta.6 + '@storybook/instrumenter': 8.0.0-beta.6 + '@storybook/preview-api': 8.0.0-beta.6 '@testing-library/dom': 9.3.3 '@testing-library/jest-dom': 6.4.2(vitest@0.34.6) - '@testing-library/user-event': 14.3.0(@testing-library/dom@9.3.3) + '@testing-library/user-event': 14.5.2(@testing-library/dom@9.3.3) '@vitest/expect': 1.1.3 '@vitest/spy': 1.2.2 chai: 4.3.10 @@ -6292,8 +6577,8 @@ packages: - vitest dev: true - /@storybook/theming@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Nl3eCdsBVjh98sghb7YF/v+65bUWEyJfvhU7aQHxRcYoBw+UKJIX5FKSd+PFnC/BOkH0So2ngDU2XFoOdA6BPg==} + /@storybook/theming@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-WXvDbV257fKbHM5jHd7hOHefRSBnyZec08NGpcVOG6muJjLu8nPjazcYgISqFc97MkFmxvEDPFfX8CvBEeefzA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -6304,33 +6589,36 @@ packages: optional: true dependencies: '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) - '@storybook/client-logger': 8.0.0-beta.2 + '@storybook/client-logger': 8.0.0-beta.6 '@storybook/global': 5.0.0 memoizerific: 1.11.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/types@8.0.0-beta.2: - resolution: {integrity: sha512-MK6QFpMGWxu+sLCw8VrXmL0gOJ3g6XPpV85T5s+CEMsfMhSH5wCMdhtWkCRbHGfVEPKa5fEtA0SaGLqJhnSpQw==} + /@storybook/types@8.0.0-beta.6: + resolution: {integrity: sha512-w3jq8mBcxir4P0RK3gQePeUJ0rXbnUbCKg91YBOKeitmU0+4jSr4e1EwTWOYgsyz7KtikzSNr8JXtMQn2TJD5A==} dependencies: - '@storybook/channels': 8.0.0-beta.2 + '@storybook/channels': 8.0.0-beta.6 '@types/express': 4.17.17 file-system-cache: 2.3.0 dev: true - /@storybook/vue3-vite@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)(vite@5.1.0)(vue@3.4.18): - resolution: {integrity: sha512-Pzth2PEEmLyI2hW827x+Cd4nYO6xayAsWk46JdXjfHOVnHDgp6CZPSi1zr79J+bbjvQtHHb+9BV4TljdiM0zxw==} + /@storybook/vue3-vite@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(vite@5.1.4)(vue@3.4.21): + resolution: {integrity: sha512-Pf9W7hcHjx1FE3JmhY1iSxGq9k/Tp5n/obOCd4FJGUdIttPYFclG9km49DrCJtNfhK7M6+d2QTZ6Uds4ORWZPg==} engines: {node: '>=18.0.0'} peerDependencies: vite: ^4.0.0 || ^5.0.0 dependencies: - '@storybook/builder-vite': 8.0.0-beta.2(typescript@5.3.3)(vite@5.1.0) - '@storybook/core-server': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) - '@storybook/vue3': 8.0.0-beta.2(vue@3.4.18) + '@storybook/builder-vite': 8.0.0-beta.6(typescript@5.3.3)(vite@5.1.4) + '@storybook/core-server': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/vue3': 8.0.0-beta.6(vue@3.4.21) + find-package-json: 1.2.0 magic-string: 0.30.7 - vite: 5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0) - vue-docgen-api: 4.64.1(vue@3.4.18) + typescript: 5.3.3 + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) + vue-component-meta: 1.8.27(typescript@5.3.3) + vue-docgen-api: 4.75.1(vue@3.4.21) transitivePeerDependencies: - '@preact/preset-vite' - bufferutil @@ -6338,27 +6626,26 @@ packages: - react - react-dom - supports-color - - typescript - utf-8-validate - vite-plugin-glimmerx - vue dev: true - /@storybook/vue3@8.0.0-beta.2(vue@3.4.18): - resolution: {integrity: sha512-lGupXqWl+/gx5in8jJEzxCHvtTfFHCemYFVMXUqNhJ+Chudwx7LRyy3frQ+0AE4FzGIIYWM/tJjkaKvtBQvEDg==} + /@storybook/vue3@8.0.0-beta.6(vue@3.4.21): + resolution: {integrity: sha512-027KDM1f6y0XzMK1yE5W4JKY/VsbGpr1kj0mvEKxaPUYgBJV9wTHADWgmluiJS/e/MWrCCZql5mE+D9lVJUjoA==} engines: {node: '>=18.0.0'} peerDependencies: vue: ^3.0.0 dependencies: - '@storybook/docs-tools': 8.0.0-beta.2 + '@storybook/docs-tools': 8.0.0-beta.6 '@storybook/global': 5.0.0 - '@storybook/preview-api': 8.0.0-beta.2 - '@storybook/types': 8.0.0-beta.2 + '@storybook/preview-api': 8.0.0-beta.6 + '@storybook/types': 8.0.0-beta.6 '@vue/compiler-core': 3.4.18 lodash: 4.17.21 ts-dedent: 2.2.0 type-fest: 2.19.0 - vue: 3.4.18(typescript@5.3.3) + vue: 3.4.21(typescript@5.3.3) vue-component-type-helpers: 1.8.27 transitivePeerDependencies: - encoding @@ -6953,11 +7240,11 @@ packages: dom-accessibility-api: 0.6.3 lodash: 4.17.21 redent: 3.0.0 - vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0) + vitest: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1) dev: true - /@testing-library/user-event@14.3.0(@testing-library/dom@9.3.3): - resolution: {integrity: sha512-P02xtBBa8yMaLhK8CzJCIns8rqwnF6FxhR9zs810flHOBXUYCFjLd8Io1rQrAkQRWEmW2PGdZIEdMxf/KLsqFA==} + /@testing-library/user-event@14.5.2(@testing-library/dom@9.3.3): + resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==} engines: {node: '>=12', npm: '>=6'} peerDependencies: '@testing-library/dom': '>=7.21.4' @@ -6965,7 +7252,7 @@ packages: '@testing-library/dom': 9.3.3 dev: true - /@testing-library/vue@8.0.2(@vue/compiler-sfc@3.4.18)(vue@3.4.18): + /@testing-library/vue@8.0.2(@vue/compiler-sfc@3.4.21)(vue@3.4.21): resolution: {integrity: sha512-A8wWX+qQn0o0izpQWnGCpwQt8wAdpsVP8vPP2h5Q/jcGhZ5yKXz9PPUqhQv+45LTFaWlyRf8bArTVaB/KFFd5A==} engines: {node: '>=14'} peerDependencies: @@ -6977,9 +7264,9 @@ packages: dependencies: '@babel/runtime': 7.23.4 '@testing-library/dom': 9.3.3 - '@vue/compiler-sfc': 3.4.18 - '@vue/test-utils': 2.4.1(vue@3.4.18) - vue: 3.4.18(typescript@5.3.3) + '@vue/compiler-sfc': 3.4.21 + '@vue/test-utils': 2.4.1(vue@3.4.21) + vue: 3.4.21(typescript@5.3.3) transitivePeerDependencies: - '@vue/server-renderer' dev: true @@ -7005,7 +7292,7 @@ packages: /@types/accepts@1.3.7: resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/archiver@6.0.2: @@ -7059,7 +7346,7 @@ packages: resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: '@types/connect': 3.4.35 - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/braces@3.0.1: @@ -7071,7 +7358,7 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.4 '@types/keyv': 3.1.4 - '@types/node': 20.11.17 + '@types/node': 20.11.22 '@types/responselike': 1.0.0 dev: false @@ -7098,7 +7385,7 @@ packages: /@types/connect@3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/content-disposition@0.5.8: @@ -7112,7 +7399,7 @@ packages: /@types/cross-spawn@6.0.2: resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/debug@4.1.12: @@ -7170,7 +7457,7 @@ packages: /@types/express-serve-static-core@4.17.33: resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 dev: true @@ -7191,20 +7478,20 @@ packages: /@types/fluent-ffmpeg@2.1.24: resolution: {integrity: sha512-g5oQO8Jgi2kFS3tTub7wLvfLztr1s8tdXmRd8PiL/hLMLzTIAyMR2sANkTggM/rdEDAg3d63nYRRVepwBiCw5A==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/glob@7.2.0: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/graceful-fs@4.1.6: resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/hast@3.0.4: @@ -7213,13 +7500,17 @@ packages: '@types/unist': 3.0.2 dev: true + /@types/htmlescape@1.1.3: + resolution: {integrity: sha512-tuC81YJXGUe0q8WRtBNW+uyx79rkkzWK651ALIXXYq5/u/IxjX4iHneGF2uUqzsNp+F+9J2mFZOv9jiLTtIq0w==} + dev: true + /@types/http-cache-semantics@4.0.4: resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} /@types/http-link-header@1.0.5: resolution: {integrity: sha512-AxhIKR8UbyoqCTNp9rRepkktHuUOw3DjfOfDCaO9kwI8AYzjhxyrvZq4+mRw/2daD3hYDknrtSeV6SsPwmc71w==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/istanbul-lib-coverage@2.0.4: @@ -7245,6 +7536,13 @@ packages: pretty-format: 29.7.0 dev: true + /@types/jest@29.5.12: + resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==} + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + dev: true + /@types/js-yaml@4.0.9: resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} dev: true @@ -7252,7 +7550,7 @@ packages: /@types/jsdom@21.1.6: resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 '@types/tough-cookie': 4.0.2 parse5: 7.1.2 dev: true @@ -7276,7 +7574,7 @@ packages: /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: false /@types/lodash@4.14.191: @@ -7332,7 +7630,7 @@ packages: resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==} requiresBuild: true dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 form-data: 3.0.1 dev: false @@ -7347,8 +7645,8 @@ packages: resolution: {integrity: sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==} dev: true - /@types/node@20.11.17: - resolution: {integrity: sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==} + /@types/node@20.11.22: + resolution: {integrity: sha512-/G+IxWxma6V3E+pqK1tSl2Fo1kl41pK1yeCyDsgkF9WlVAme4j5ISYM2zR11bgLFJGLN5sVK40T4RJNuiZbEjA==} dependencies: undici-types: 5.26.5 @@ -7367,7 +7665,7 @@ packages: /@types/nodemailer@6.4.14: resolution: {integrity: sha512-fUWthHO9k9DSdPCSPRqcu6TWhYyxTBg382vlNIttSe9M7XfsT06y0f24KHXtbnijPGGRIcVvdKHTNikOI6qiHA==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/normalize-package-data@2.4.1: @@ -7384,13 +7682,13 @@ packages: resolution: {integrity: sha512-Ali0fUUn+zgr4Yy/pCTFbuiaiJpq7l7OQwFnxYVchNbNGIx0c4Wkcdje6WO89I91RAaYF+gVc1pOaizA4YKZmA==} dependencies: '@types/express': 4.17.17 - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/oauth@0.9.4: resolution: {integrity: sha512-qk9orhti499fq5XxKCCEbd0OzdPZuancneyse3KtR+vgMiHRbh+mn8M4G6t64ob/Fg+GZGpa565MF/2dKWY32A==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/offscreencanvas@2019.3.0: @@ -7403,10 +7701,10 @@ packages: requiresBuild: true dev: false - /@types/pg@8.11.0: - resolution: {integrity: sha512-sDAlRiBNthGjNFfvt0k6mtotoVYVQ63pA8R4EMWka7crawSR60waVYR0HAgmPRs/e2YaeJTD/43OoZ3PFw80pw==} + /@types/pg@8.11.2: + resolution: {integrity: sha512-G2Mjygf2jFMU/9hCaTYxJrwdObdcnuQde1gndooZSOHsNSaCehAuwc7EIuSA34Do8Jx2yZ19KtvW8P0j4EuUXw==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 pg-protocol: 1.6.0 pg-types: 4.0.1 dev: true @@ -7423,14 +7721,14 @@ packages: resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==} dev: true - /@types/punycode@2.1.3: - resolution: {integrity: sha512-dFkH9Mz0yY5UfQVSrpj1grQyqRwe4TohTLlHFx4Gli8/fsaNyoOVUAsiEBZk5JBwbEJVZ49W6st8D5g6dRJb/w==} + /@types/punycode@2.1.4: + resolution: {integrity: sha512-trzh6NzBnq8yw5e35f8xe8VTYjqM3NE7bohBtvDVf/dtUer3zYTLK1Ka3DG3p7bdtoaOHZucma6FfVKlQ134pQ==} dev: true /@types/qrcode@1.5.5: resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/qs@6.9.7: @@ -7460,7 +7758,7 @@ packages: /@types/readdir-glob@1.1.1: resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/rename@1.0.7: @@ -7474,11 +7772,11 @@ packages: /@types/responselike@1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: false - /@types/sanitize-html@2.9.5: - resolution: {integrity: sha512-2Sr1vd8Dw+ypsg/oDDfZ57OMSG2Befs+l2CMyCC5bVSK3CpE7lTB2aNlbbWzazgVA+Qqfuholwom6x/mWd1qmw==} + /@types/sanitize-html@2.11.0: + resolution: {integrity: sha512-7oxPGNQHXLHE48r/r/qjn7q0hlrs3kL7oZnGj0Wf/h9tj/6ibFyRkNbsDxaBBZ4XUZ0Dx5LGCyDJ04ytSofacQ==} dependencies: htmlparser2: 8.0.1 dev: true @@ -7496,15 +7794,15 @@ packages: resolution: {integrity: sha512-TY1eezMU2zH2ozQoAFAQFOPpvP15g+ZgSfTZt31AUUH/Rxtnz3H+A/Sv1Snw2/amp//omibc+AEkTaA8KUeOLQ==} dev: true - /@types/semver@7.5.6: - resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} + /@types/semver@7.5.8: + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} dev: true /@types/serve-static@1.15.1: resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==} dependencies: '@types/mime': 3.0.1 - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/serviceworker@0.0.67: @@ -7568,13 +7866,13 @@ packages: /@types/vary@1.1.3: resolution: {integrity: sha512-XJT8/ZQCL7NUut9QDLf6l24JfAEl7bnNdgxfj50cHIpEPRJLHHDDFOAq6i+GsEmeFfH7NamhBE4c4Thtb2egWg==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/web-push@3.6.3: resolution: {integrity: sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/webgl-ext@0.0.30: @@ -7585,7 +7883,7 @@ packages: /@types/ws@8.5.10: resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /@types/yargs-parser@21.0.0: @@ -7608,7 +7906,7 @@ packages: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true optional: true @@ -7641,29 +7939,29 @@ packages: - supports-color dev: true - /@typescript-eslint/eslint-plugin@6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-nISDRYnnIpk7VCFrGcu1rnZfM1Dh9LRHnfgdkjcbi/l7g16VYRri3TjXi9Ir4lOZSw5N/gnV/3H7jIPQ8Q4daA==} + /@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-j6vT/kCulhG5wBmGtstKeiVr1rdXE4nk+DT1k6trYkwlrvW9eOF5ZbgKnd/YR6PcM4uTEXa0h6Fcvf6X7Dxl0w==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: - '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha - eslint: ^7.0.0 || ^8.0.0 + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: '@eslint-community/regexpp': 4.6.2 - '@typescript-eslint/parser': 6.18.1(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/scope-manager': 6.18.1 - '@typescript-eslint/type-utils': 6.18.1(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/utils': 6.18.1(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.18.1 + '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 7.1.0 + '@typescript-eslint/type-utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 7.1.0 debug: 4.3.4(supports-color@8.1.1) - eslint: 8.56.0 + eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.2.4 natural-compare: 1.4.0 - semver: 7.5.4 + semver: 7.6.0 ts-api-utils: 1.0.1(typescript@5.3.3) typescript: 5.3.3 transitivePeerDependencies: @@ -7691,22 +7989,22 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@6.18.1(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-zct/MdJnVaRRNy9e84XnVtRv9Vf91/qqe+hZJtKanjojud4wAVy/7lXxJmMyX6X6J+xc6c//YEWvpeif8cAhWA==} + /@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-V1EknKUubZ1gWFjiOZhDSNToOjs63/9O0puCgGS8aDOgpZY326fzFu15QAUjwaXzRZjf/qdsdBrckYdv9YxB8w==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.18.1 - '@typescript-eslint/types': 6.18.1 - '@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.18.1 + '@typescript-eslint/scope-manager': 7.1.0 + '@typescript-eslint/types': 7.1.0 + '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 7.1.0 debug: 4.3.4(supports-color@8.1.1) - eslint: 8.56.0 + eslint: 8.57.0 typescript: 5.3.3 transitivePeerDependencies: - supports-color @@ -7720,12 +8018,12 @@ packages: '@typescript-eslint/visitor-keys': 6.11.0 dev: true - /@typescript-eslint/scope-manager@6.18.1: - resolution: {integrity: sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==} + /@typescript-eslint/scope-manager@7.1.0: + resolution: {integrity: sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.18.1 - '@typescript-eslint/visitor-keys': 6.18.1 + '@typescript-eslint/types': 7.1.0 + '@typescript-eslint/visitor-keys': 7.1.0 dev: true /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.3.3): @@ -7748,20 +8046,20 @@ packages: - supports-color dev: true - /@typescript-eslint/type-utils@6.18.1(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-wyOSKhuzHeU/5pcRDP2G2Ndci+4g653V43gXTpt4nbyoIOAASkGDA9JIAgbQCdCkcr1MvpSYWzxTz0olCn8+/Q==} + /@typescript-eslint/type-utils@7.1.0(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-UZIhv8G+5b5skkcuhgvxYWHjk7FW7/JP5lPASMEUoliAPwIH/rxoUSQPia2cuOj9AmDZmwUl1usKm85t5VUMew==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3) - '@typescript-eslint/utils': 6.18.1(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3) + '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3) debug: 4.3.4(supports-color@8.1.1) - eslint: 8.56.0 + eslint: 8.57.0 ts-api-utils: 1.0.1(typescript@5.3.3) typescript: 5.3.3 transitivePeerDependencies: @@ -7773,8 +8071,8 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/types@6.18.1: - resolution: {integrity: sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==} + /@typescript-eslint/types@7.1.0: + resolution: {integrity: sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==} engines: {node: ^16.0.0 || >=18.0.0} dev: true @@ -7799,8 +8097,8 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree@6.18.1(typescript@5.3.3): - resolution: {integrity: sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==} + /@typescript-eslint/typescript-estree@7.1.0(typescript@5.3.3): + resolution: {integrity: sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -7808,13 +8106,13 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.18.1 - '@typescript-eslint/visitor-keys': 6.18.1 + '@typescript-eslint/types': 7.1.0 + '@typescript-eslint/visitor-keys': 7.1.0 debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 - semver: 7.5.4 + semver: 7.6.0 ts-api-utils: 1.0.1(typescript@5.3.3) typescript: 5.3.3 transitivePeerDependencies: @@ -7829,7 +8127,7 @@ packages: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) '@types/json-schema': 7.0.12 - '@types/semver': 7.5.6 + '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 6.11.0 '@typescript-eslint/types': 6.11.0 '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3) @@ -7840,20 +8138,20 @@ packages: - typescript dev: true - /@typescript-eslint/utils@6.18.1(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ==} + /@typescript-eslint/utils@7.1.0(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-WUFba6PZC5OCGEmbweGpnNJytJiLG7ZvDBJJoUcX4qZYf1mGZ97mO2Mps6O2efxJcJdRNpqweCistDbZMwIVHw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: ^8.56.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.12 - '@types/semver': 7.5.6 - '@typescript-eslint/scope-manager': 6.18.1 - '@typescript-eslint/types': 6.18.1 - '@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3) - eslint: 8.56.0 - semver: 7.5.4 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 7.1.0 + '@typescript-eslint/types': 7.1.0 + '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3) + eslint: 8.57.0 + semver: 7.6.0 transitivePeerDependencies: - supports-color - typescript @@ -7867,11 +8165,11 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@typescript-eslint/visitor-keys@6.18.1: - resolution: {integrity: sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==} + /@typescript-eslint/visitor-keys@7.1.0: + resolution: {integrity: sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.18.1 + '@typescript-eslint/types': 7.1.0 eslint-visitor-keys: 3.4.3 dev: true @@ -7879,15 +8177,15 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true - /@vitejs/plugin-vue@5.0.3(vite@5.1.0)(vue@3.4.18): - resolution: {integrity: sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA==} + /@vitejs/plugin-vue@5.0.4(vite@5.1.4)(vue@3.4.21): + resolution: {integrity: sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==} engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: vite: ^5.0.0 vue: ^3.2.25 dependencies: - vite: 5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0) - vue: 3.4.18(typescript@5.3.3) + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) + vue: 3.4.21(typescript@5.3.3) dev: false /@vitest/coverage-v8@0.34.6(vitest@0.34.6): @@ -7906,7 +8204,7 @@ packages: std-env: 3.7.0 test-exclude: 6.0.0 v8-to-istanbul: 9.2.0 - vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0) + vitest: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1) transitivePeerDependencies: - supports-color dev: true @@ -8005,31 +8303,48 @@ packages: entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.0.2 + dev: true + + /@vue/compiler-core@3.4.21: + resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==} + dependencies: + '@babel/parser': 7.23.9 + '@vue/shared': 3.4.21 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.0.2 /@vue/compiler-dom@3.4.18: resolution: {integrity: sha512-24Eb8lcMfInefvQ6YlEVS18w5Q66f4+uXWVA+yb7praKbyjHRNuKVWGuinfSSjM0ZIiPi++QWukhkgznBaqpEA==} dependencies: '@vue/compiler-core': 3.4.18 '@vue/shared': 3.4.18 + dev: true - /@vue/compiler-sfc@3.4.18: - resolution: {integrity: sha512-rG5tqtnzwrVpMqAQ7FHtvHaV70G6LLfJIWLYZB/jZ9m/hrnZmIQh+H3ewnC5onwe/ibljm9+ZupxeElzqCkTAw==} + /@vue/compiler-dom@3.4.21: + resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==} + dependencies: + '@vue/compiler-core': 3.4.21 + '@vue/shared': 3.4.21 + + /@vue/compiler-sfc@3.4.21: + resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==} dependencies: '@babel/parser': 7.23.9 - '@vue/compiler-core': 3.4.18 - '@vue/compiler-dom': 3.4.18 - '@vue/compiler-ssr': 3.4.18 - '@vue/shared': 3.4.18 + '@vue/compiler-core': 3.4.21 + '@vue/compiler-dom': 3.4.21 + '@vue/compiler-ssr': 3.4.21 + '@vue/shared': 3.4.21 estree-walker: 2.0.2 magic-string: 0.30.7 - postcss: 8.4.33 + postcss: 8.4.35 source-map-js: 1.0.2 - /@vue/compiler-ssr@3.4.18: - resolution: {integrity: sha512-hSlv20oUhPxo2UYUacHgGaxtqP0tvFo6ixxxD6JlXIkwzwoZ9eKK6PFQN4hNK/R13JlNyldwWt/fqGBKgWJ6nQ==} + /@vue/compiler-ssr@3.4.21: + resolution: {integrity: sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==} dependencies: - '@vue/compiler-dom': 3.4.18 - '@vue/shared': 3.4.18 + '@vue/compiler-dom': 3.4.21 + '@vue/shared': 3.4.21 /@vue/language-core@1.8.27(typescript@5.3.3): resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==} @@ -8051,32 +8366,32 @@ packages: vue-template-compiler: 2.7.14 dev: true - /@vue/reactivity@3.4.18: - resolution: {integrity: sha512-7uda2/I0jpLiRygprDo5Jxs2HJkOVXcOMlyVlY54yRLxoycBpwGJRwJT9EdGB4adnoqJDXVT2BilUAYwI7qvmg==} + /@vue/reactivity@3.4.21: + resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==} dependencies: - '@vue/shared': 3.4.18 + '@vue/shared': 3.4.21 - /@vue/runtime-core@3.4.18: - resolution: {integrity: sha512-7mU9diCa+4e+8/wZ7Udw5pwTH10A11sZ1nldmHOUKJnzCwvZxfJqAtw31mIf4T5H2FsLCSBQT3xgioA9vIjyDQ==} + /@vue/runtime-core@3.4.21: + resolution: {integrity: sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==} dependencies: - '@vue/reactivity': 3.4.18 - '@vue/shared': 3.4.18 + '@vue/reactivity': 3.4.21 + '@vue/shared': 3.4.21 - /@vue/runtime-dom@3.4.18: - resolution: {integrity: sha512-2y1Mkzcw1niSfG7z3Qx+2ir9Gb4hdTkZe5p/I8x1aTIKQE0vY0tPAEUPhZm5tx6183gG3D/KwHG728UR0sIufA==} + /@vue/runtime-dom@3.4.21: + resolution: {integrity: sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==} dependencies: - '@vue/runtime-core': 3.4.18 - '@vue/shared': 3.4.18 + '@vue/runtime-core': 3.4.21 + '@vue/shared': 3.4.21 csstype: 3.1.3 - /@vue/server-renderer@3.4.18(vue@3.4.18): - resolution: {integrity: sha512-YJd1wa7mzUN3NRqLEsrwEYWyO+PUBSROIGlCc3J/cvn7Zu6CxhNLgXa8Z4zZ5ja5/nviYO79J1InoPeXgwBTZA==} + /@vue/server-renderer@3.4.21(vue@3.4.21): + resolution: {integrity: sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==} peerDependencies: - vue: 3.4.18 + vue: 3.4.21 dependencies: - '@vue/compiler-ssr': 3.4.18 - '@vue/shared': 3.4.18 - vue: 3.4.18(typescript@5.3.3) + '@vue/compiler-ssr': 3.4.21 + '@vue/shared': 3.4.21 + vue: 3.4.21(typescript@5.3.3) /@vue/shared@3.3.12: resolution: {integrity: sha512-6p0Yin0pclvnER7BLNOQuod9Z+cxSYh8pSh7CzHnWNjAIP6zrTlCdHRvSCb1aYEx6i3Q3kvfuWU7nG16CgG1ag==} @@ -8084,8 +8399,12 @@ packages: /@vue/shared@3.4.18: resolution: {integrity: sha512-CxouGFxxaW5r1WbrSmWwck3No58rApXgRSBxrqgnY1K+jk20F6DrXJkHdH9n4HVT+/B6G2CAn213Uq3npWiy8Q==} + dev: true - /@vue/test-utils@2.4.1(vue@3.4.18): + /@vue/shared@3.4.21: + resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==} + + /@vue/test-utils@2.4.1(vue@3.4.21): resolution: {integrity: sha512-VO8nragneNzUZUah6kOjiFmD/gwRjUauG9DROh6oaOeFwX1cZRUNHhdeogE8635cISigXFTtGLUQWx5KCb0xeg==} peerDependencies: '@vue/server-renderer': ^3.0.1 @@ -8095,7 +8414,7 @@ packages: optional: true dependencies: js-beautify: 1.14.9 - vue: 3.4.18(typescript@5.3.3) + vue: 3.4.21(typescript@5.3.3) vue-component-type-helpers: 1.8.4 dev: true @@ -8535,20 +8854,6 @@ packages: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} dev: true - /ast-types@0.14.2: - resolution: {integrity: sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==} - engines: {node: '>=4'} - dependencies: - tslib: 2.6.2 - dev: true - - /ast-types@0.15.2: - resolution: {integrity: sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==} - engines: {node: '>=4'} - dependencies: - tslib: 2.6.2 - dev: true - /ast-types@0.16.1: resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} engines: {node: '>=4'} @@ -8833,6 +9138,7 @@ packages: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.0 + dev: true /blob-util@2.0.2: resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==} @@ -8947,6 +9253,18 @@ packages: electron-to-chromium: 1.4.601 node-releases: 2.0.14 update-browserslist-db: 1.0.13(browserslist@4.22.2) + dev: true + + /browserslist@4.23.0: + resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001591 + electron-to-chromium: 1.4.686 + node-releases: 2.0.14 + update-browserslist-db: 1.0.13(browserslist@4.23.0) + dev: false /bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} @@ -8981,6 +9299,7 @@ packages: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 + dev: true /buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -8996,13 +9315,14 @@ packages: dependencies: node-gyp-build: 4.6.0 - /bullmq@5.1.9: - resolution: {integrity: sha512-9MfcQxYyfkG8kxpIxRsRXWYlTRQ1o8xWqgdoFR5pLClVTjtMI8qeDO5basRQLZPfp/uiPtv+gpzJ3OTNrm2ZNg==} + /bullmq@5.4.0: + resolution: {integrity: sha512-QNOT+Vp/OFctwKa1/LYvrfIMXqb6Hu8a1VwXxQpa/JXoFAQ9E4ZcqW4fyEjx9iYrXakpV6cAGPbmdgWaKTGXOQ==} dependencies: cron-parser: 4.8.1 - glob: 8.1.0 + fast-glob: 3.3.2 ioredis: 5.3.2 lodash: 4.17.21 + minimatch: 9.0.3 msgpackr: 1.10.1 node-abort-controller: 3.1.1 semver: 7.5.4 @@ -9129,7 +9449,7 @@ packages: /caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: - browserslist: 4.22.2 + browserslist: 4.23.0 caniuse-lite: 1.0.30001566 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 @@ -9138,39 +9458,21 @@ packages: /caniuse-lite@1.0.30001566: resolution: {integrity: sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==} + /caniuse-lite@1.0.30001591: + resolution: {integrity: sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==} + dev: false + /canonicalize@1.0.8: resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==} dev: false - /canvas-confetti@1.6.1: - resolution: {integrity: sha512-CgGR5DL9+dkne4AwcpvWQc0LIQq43yDIxlwdZcyrq3yklricNfuPHoOSoM6Ya7yCQ+sXmZ2iNV2feiKjVG8C1g==} + /canvas-confetti@1.9.2: + resolution: {integrity: sha512-6Xi7aHHzKwxZsem4mCKoqP6YwUG3HamaHHAlz1hTNQPCqXhARFpSXnkC9TWlahHY5CG6hSL5XexNjxK8irVErg==} dev: false /caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} - /cbor-extract@2.1.1: - resolution: {integrity: sha512-1UX977+L+zOJHsp0mWFG13GLwO6ucKgSmSW6JTl8B9GUvACvHeIVpFqhU92299Z6PfD09aTXDell5p+lp1rUFA==} - hasBin: true - requiresBuild: true - dependencies: - node-gyp-build-optional-packages: 5.0.3 - optionalDependencies: - '@cbor-extract/cbor-extract-darwin-arm64': 2.1.1 - '@cbor-extract/cbor-extract-darwin-x64': 2.1.1 - '@cbor-extract/cbor-extract-linux-arm': 2.1.1 - '@cbor-extract/cbor-extract-linux-arm64': 2.1.1 - '@cbor-extract/cbor-extract-linux-x64': 2.1.1 - '@cbor-extract/cbor-extract-win32-x64': 2.1.1 - dev: false - optional: true - - /cbor-x@1.5.4: - resolution: {integrity: sha512-PVKILDn+Rf6MRhhcyzGXi5eizn1i0i3F8Fe6UMMxXBnWkalq9+C5+VTmlIjAYM4iF2IYF2N+zToqAfYOp+3rfw==} - optionalDependencies: - cbor-extract: 2.1.1 - dev: false - /cbor@9.0.2: resolution: {integrity: sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==} engines: {node: '>=16'} @@ -9248,45 +9550,45 @@ packages: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: true - /chart.js@4.4.1: - resolution: {integrity: sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==} - engines: {pnpm: '>=7'} + /chart.js@4.4.2: + resolution: {integrity: sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==} + engines: {pnpm: '>=8'} dependencies: '@kurkle/color': 0.3.2 dev: false - /chartjs-adapter-date-fns@3.0.0(chart.js@4.4.1)(date-fns@2.30.0): + /chartjs-adapter-date-fns@3.0.0(chart.js@4.4.2)(date-fns@2.30.0): resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==} peerDependencies: chart.js: '>=2.8.0' date-fns: '>=2.0.0' dependencies: - chart.js: 4.4.1 + chart.js: 4.4.2 date-fns: 2.30.0 dev: false - /chartjs-chart-matrix@2.0.1(chart.js@4.4.1): + /chartjs-chart-matrix@2.0.1(chart.js@4.4.2): resolution: {integrity: sha512-BGfeY+/PHnITyDlc7WfnKJ1RyOfgOzIqWp/gxzzl7pUjyoGzHDcw51qd2xJF9gdT9Def7ZwOnOMm8GJUXDxI0w==} peerDependencies: chart.js: '>=3.0.0' dependencies: - chart.js: 4.4.1 + chart.js: 4.4.2 dev: false - /chartjs-plugin-gradient@0.6.1(chart.js@4.4.1): + /chartjs-plugin-gradient@0.6.1(chart.js@4.4.2): resolution: {integrity: sha512-TGHNIh8KqQMLdb+UfY80cBHYRyOC47eeokmgkeajRdKGbFt462lJiyiq4ZJ25fiM7BGsmzoBLhmVyEw4B3gQxw==} peerDependencies: chart.js: '>=2.6.0' dependencies: - chart.js: 4.4.1 + chart.js: 4.4.2 dev: false - /chartjs-plugin-zoom@2.0.1(chart.js@4.4.1): + /chartjs-plugin-zoom@2.0.1(chart.js@4.4.2): resolution: {integrity: sha512-ogOmLu6e+Q7E1XWOCOz9YwybMslz9qNfGV2a+qjfmqJYpsw5ZMoRHZBUyW+NGhkpQ5PwwPA/+rikHpBZb7PZuA==} peerDependencies: chart.js: '>=3.2.0' dependencies: - chart.js: 4.4.1 + chart.js: 4.4.2 hammerjs: 2.0.8 dev: false @@ -9345,16 +9647,16 @@ packages: engines: {node: '>=10'} requiresBuild: true - /chromatic@10.6.1: - resolution: {integrity: sha512-bd4C5sEEtN83uUmbc4Fu+x7+lJIPdMUdu4D6HRDQEIDl/Tatc8+By4bZluH1pzg/MbP9vllkL6Ua9vF4EEA7VA==} + /chromatic@11.0.0: + resolution: {integrity: sha512-utzRVqdMrpzYwZNf7dHWU0z0/rx6SH/FUVNozQxYHDtQfYUdEj+Ro4OSch5+Wsk2FoUmznJyLkaC2J839z1N7A==} hasBin: true peerDependencies: - chromatic-cypress: ^0.4.0 || ^1.0.0 - chromatic-playwright: ^0.4.0 || ^1.0.0 + '@chromatic-com/cypress': ^0.5.2 || ^1.0.0 + '@chromatic-com/playwright': ^0.5.2 || ^1.0.0 peerDependenciesMeta: - chromatic-cypress: + '@chromatic-com/cypress': optional: true - chromatic-playwright: + '@chromatic-com/playwright': optional: true dev: false @@ -9717,7 +10019,7 @@ packages: readable-stream: 3.6.0 dev: false - /create-jest@29.7.0(@types/node@20.11.17): + /create-jest@29.7.0(@types/node@20.11.22): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -9726,7 +10028,7 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.11.17) + jest-config: 29.7.0(@types/node@20.11.22) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -9794,13 +10096,13 @@ packages: engines: {node: '>=8'} dev: true - /css-declaration-sorter@7.1.1(postcss@8.4.33): + /css-declaration-sorter@7.1.1(postcss@8.4.35): resolution: {integrity: sha512-dZ3bVTEEc1vxr3Bek9vGwfB5Z6ESPULhcRvO472mfjVnj8jRcTnKO8/JTczlvxM10Myb+wBM++1MtdO76eWcaQ==} engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.0.9 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 dev: false /css-select@5.1.0: @@ -9840,62 +10142,62 @@ packages: engines: {node: '>=4'} hasBin: true - /cssnano-preset-default@6.0.3(postcss@8.4.33): - resolution: {integrity: sha512-4y3H370aZCkT9Ev8P4SO4bZbt+AExeKhh8wTbms/X7OLDo5E7AYUUy6YPxa/uF5Grf+AJwNcCnxKhZynJ6luBA==} + /cssnano-preset-default@6.0.5(postcss@8.4.35): + resolution: {integrity: sha512-M+qRDEr5QZrfNl0B2ySdbTLGyNb8kBcSjuwR7WBamYBOEREH9t2efnB/nblekqhdGLZdkf4oZNetykG2JWRdZQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - css-declaration-sorter: 7.1.1(postcss@8.4.33) - cssnano-utils: 4.0.1(postcss@8.4.33) - postcss: 8.4.33 - postcss-calc: 9.0.1(postcss@8.4.33) - postcss-colormin: 6.0.2(postcss@8.4.33) - postcss-convert-values: 6.0.2(postcss@8.4.33) - postcss-discard-comments: 6.0.1(postcss@8.4.33) - postcss-discard-duplicates: 6.0.1(postcss@8.4.33) - postcss-discard-empty: 6.0.1(postcss@8.4.33) - postcss-discard-overridden: 6.0.1(postcss@8.4.33) - postcss-merge-longhand: 6.0.2(postcss@8.4.33) - postcss-merge-rules: 6.0.3(postcss@8.4.33) - postcss-minify-font-values: 6.0.1(postcss@8.4.33) - postcss-minify-gradients: 6.0.1(postcss@8.4.33) - postcss-minify-params: 6.0.2(postcss@8.4.33) - postcss-minify-selectors: 6.0.2(postcss@8.4.33) - postcss-normalize-charset: 6.0.1(postcss@8.4.33) - postcss-normalize-display-values: 6.0.1(postcss@8.4.33) - postcss-normalize-positions: 6.0.1(postcss@8.4.33) - postcss-normalize-repeat-style: 6.0.1(postcss@8.4.33) - postcss-normalize-string: 6.0.1(postcss@8.4.33) - postcss-normalize-timing-functions: 6.0.1(postcss@8.4.33) - postcss-normalize-unicode: 6.0.2(postcss@8.4.33) - postcss-normalize-url: 6.0.1(postcss@8.4.33) - postcss-normalize-whitespace: 6.0.1(postcss@8.4.33) - postcss-ordered-values: 6.0.1(postcss@8.4.33) - postcss-reduce-initial: 6.0.2(postcss@8.4.33) - postcss-reduce-transforms: 6.0.1(postcss@8.4.33) - postcss-svgo: 6.0.2(postcss@8.4.33) - postcss-unique-selectors: 6.0.2(postcss@8.4.33) + css-declaration-sorter: 7.1.1(postcss@8.4.35) + cssnano-utils: 4.0.1(postcss@8.4.35) + postcss: 8.4.35 + postcss-calc: 9.0.1(postcss@8.4.35) + postcss-colormin: 6.0.3(postcss@8.4.35) + postcss-convert-values: 6.0.4(postcss@8.4.35) + postcss-discard-comments: 6.0.1(postcss@8.4.35) + postcss-discard-duplicates: 6.0.2(postcss@8.4.35) + postcss-discard-empty: 6.0.2(postcss@8.4.35) + postcss-discard-overridden: 6.0.1(postcss@8.4.35) + postcss-merge-longhand: 6.0.3(postcss@8.4.35) + postcss-merge-rules: 6.0.4(postcss@8.4.35) + postcss-minify-font-values: 6.0.2(postcss@8.4.35) + postcss-minify-gradients: 6.0.2(postcss@8.4.35) + postcss-minify-params: 6.0.3(postcss@8.4.35) + postcss-minify-selectors: 6.0.2(postcss@8.4.35) + postcss-normalize-charset: 6.0.1(postcss@8.4.35) + postcss-normalize-display-values: 6.0.1(postcss@8.4.35) + postcss-normalize-positions: 6.0.1(postcss@8.4.35) + postcss-normalize-repeat-style: 6.0.1(postcss@8.4.35) + postcss-normalize-string: 6.0.1(postcss@8.4.35) + postcss-normalize-timing-functions: 6.0.1(postcss@8.4.35) + postcss-normalize-unicode: 6.0.3(postcss@8.4.35) + postcss-normalize-url: 6.0.1(postcss@8.4.35) + postcss-normalize-whitespace: 6.0.1(postcss@8.4.35) + postcss-ordered-values: 6.0.1(postcss@8.4.35) + postcss-reduce-initial: 6.0.3(postcss@8.4.35) + postcss-reduce-transforms: 6.0.1(postcss@8.4.35) + postcss-svgo: 6.0.2(postcss@8.4.35) + postcss-unique-selectors: 6.0.2(postcss@8.4.35) dev: false - /cssnano-utils@4.0.1(postcss@8.4.33): + /cssnano-utils@4.0.1(postcss@8.4.35): resolution: {integrity: sha512-6qQuYDqsGoiXssZ3zct6dcMxiqfT6epy7x4R0TQJadd4LWO3sPR6JH6ZByOvVLoZ6EdwPGgd7+DR1EmX3tiXQQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 dev: false - /cssnano@6.0.3(postcss@8.4.33): - resolution: {integrity: sha512-MRq4CIj8pnyZpcI2qs6wswoYoDD1t0aL28n+41c1Ukcpm56m1h6mCexIHBGjfZfnTqtGSSCP4/fB1ovxgjBOiw==} + /cssnano@6.0.5(postcss@8.4.35): + resolution: {integrity: sha512-tpTp/ukgrElwu3ESFY4IvWnGn8eTt8cJhC2aAbtA3lvUlxp6t6UPv8YCLjNnEGiFreT1O0LiOM1U3QyTBVFl2A==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - cssnano-preset-default: 6.0.3(postcss@8.4.33) - lilconfig: 3.0.0 - postcss: 8.4.33 + cssnano-preset-default: 6.0.5(postcss@8.4.35) + lilconfig: 3.1.1 + postcss: 8.4.35 dev: false /csso@5.0.5: @@ -9921,8 +10223,8 @@ packages: uniq: 1.0.1 dev: false - /cypress@13.6.3: - resolution: {integrity: sha512-d/pZvgwjAyZsoyJ3FOsJT5lDsqnxQ/clMqnNc++rkHjbkkiF2h9s0JsZSyyH4QXhVFW3zPFg82jD25roFLOdZA==} + /cypress@13.6.6: + resolution: {integrity: sha512-S+2S9S94611hXimH9a3EAYt81QM913ZVA03pUmGDfLTFa5gyp85NJ8dJGSlEAEmyRsYkioS1TtnWtbv/Fzt11A==} engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} hasBin: true requiresBuild: true @@ -9966,57 +10268,7 @@ packages: request-progress: 3.0.0 semver: 7.5.4 supports-color: 8.1.1 - tmp: 0.2.1 - untildify: 4.0.0 - yauzl: 2.10.0 - dev: true - - /cypress@13.6.4: - resolution: {integrity: sha512-pYJjCfDYB+hoOoZuhysbbYhEmNW7DEDsqn+ToCLwuVowxUXppIWRr7qk4TVRIU471ksfzyZcH+mkoF0CQUKnpw==} - engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} - hasBin: true - requiresBuild: true - dependencies: - '@cypress/request': 3.0.0 - '@cypress/xvfb': 1.2.4(supports-color@8.1.1) - '@types/sinonjs__fake-timers': 8.1.1 - '@types/sizzle': 2.3.3 - arch: 2.2.0 - blob-util: 2.0.2 - bluebird: 3.7.2 - buffer: 5.7.1 - cachedir: 2.3.0 - chalk: 4.1.2 - check-more-types: 2.24.0 - cli-cursor: 3.1.0 - cli-table3: 0.6.3 - commander: 6.2.1 - common-tags: 1.8.2 - dayjs: 1.11.10 - debug: 4.3.4(supports-color@8.1.1) - enquirer: 2.3.6 - eventemitter2: 6.4.7 - execa: 4.1.0 - executable: 4.1.1 - extract-zip: 2.0.1(supports-color@8.1.1) - figures: 3.2.0 - fs-extra: 9.1.0 - getos: 3.2.1 - is-ci: 3.0.1 - is-installed-globally: 0.4.0 - lazy-ass: 1.6.0 - listr2: 3.14.0(enquirer@2.3.6) - lodash: 4.17.21 - log-symbols: 4.1.0 - minimist: 1.2.8 - ospath: 1.2.2 - pretty-bytes: 5.6.0 - process: 0.11.10 - proxy-from-env: 1.0.0 - request-progress: 3.0.0 - semver: 7.5.4 - supports-color: 8.1.1 - tmp: 0.2.1 + tmp: 0.2.2 untildify: 4.0.0 yauzl: 2.10.0 dev: true @@ -10197,11 +10449,6 @@ packages: which-typed-array: 1.1.11 dev: true - /deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - dev: false - /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true @@ -10460,6 +10707,11 @@ packages: /electron-to-chromium@1.4.601: resolution: {integrity: sha512-SpwUMDWe9tQu8JX5QCO1+p/hChAi9AE9UpoC3rcHVc+gdCGlbT3SGb5I1klgb952HRIyvt9wZhSz9bNBYz9swA==} + dev: true + + /electron-to-chromium@1.4.686: + resolution: {integrity: sha512-3avY1B+vUzNxEgkBDpKOP8WarvUAEwpRaiCL0He5OKWEFxzaOFiq4WoZEZe7qh0ReS7DiWoHMnYoQCKxNZNzSg==} + dev: false /emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -10798,7 +11050,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.18.1)(eslint-import-resolver-node@0.3.9)(eslint@8.56.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -10819,9 +11071,9 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.18.1(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3) debug: 3.2.7(supports-color@8.1.1) - eslint: 8.56.0 + eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color @@ -10862,7 +11114,7 @@ packages: - supports-color dev: true - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0): + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0): resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} engines: {node: '>=4'} peerDependencies: @@ -10872,16 +11124,16 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.18.1(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3) array-includes: 3.1.7 array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 debug: 3.2.7(supports-color@8.1.1) doctrine: 2.1.0 - eslint: 8.56.0 + eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.18.1)(eslint-import-resolver-node@0.3.9)(eslint@8.56.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) hasown: 2.0.0 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -10897,19 +11149,19 @@ packages: - supports-color dev: true - /eslint-plugin-vue@9.20.1(eslint@8.56.0): - resolution: {integrity: sha512-GyCs8K3lkEvoyC1VV97GJhP1SvqsKCiWGHnbn0gVUYiUhaH2+nB+Dv1uekv1THFMPbBfYxukrzQdltw950k+LQ==} + /eslint-plugin-vue@9.22.0(eslint@8.57.0): + resolution: {integrity: sha512-7wCXv5zuVnBtZE/74z4yZ0CM8AjH6bk4MQGm7hZjUC2DBppKU5ioeOk5LGSg/s9a1ZJnIsdPLJpXnu1Rc+cVHg==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) - eslint: 8.56.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + eslint: 8.57.0 natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 6.0.15 - semver: 7.5.4 - vue-eslint-parser: 9.4.2(eslint@8.56.0) + semver: 7.6.0 + vue-eslint-parser: 9.4.2(eslint@8.57.0) xml-name-validator: 4.0.0 transitivePeerDependencies: - supports-color @@ -10979,16 +11231,16 @@ packages: - supports-color dev: true - /eslint@8.56.0: - resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==} + /eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@eslint-community/regexpp': 4.6.2 '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.56.0 - '@humanwhocodes/config-array': 0.11.13 + '@eslint/js': 8.57.0 + '@humanwhocodes/config-array': 0.11.14 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 '@ungap/structured-clone': 1.2.0 @@ -11193,11 +11445,6 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /expand-template@2.0.3: - resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} - engines: {node: '>=6'} - dev: false - /expect@29.7.0: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -11540,6 +11787,10 @@ packages: safe-regex2: 2.0.0 dev: false + /find-package-json@1.2.0: + resolution: {integrity: sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw==} + dev: true + /find-up@3.0.0: resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} engines: {node: '>=6'} @@ -11688,6 +11939,7 @@ packages: /fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + dev: true /fs-extra@11.1.1: resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} @@ -11905,10 +12157,6 @@ packages: - supports-color dev: true - /github-from-package@0.0.0: - resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} - dev: false - /github-slugger@2.0.0: resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} dev: true @@ -12053,8 +12301,8 @@ packages: p-cancelable: 3.0.0 responselike: 3.0.0 - /got@14.1.0: - resolution: {integrity: sha512-jGmSBfxa7jOGg464azcsf/cUlJBZldU8edFpiVebIJrVBE4vqVx0t3Z2f1kz1WrcMvLgQREoC/l2ttDmSHwyRg==} + /got@14.2.0: + resolution: {integrity: sha512-dBq2KkHcQl3AwPoIWsLsQScCPpUgRulz1qZVthjPYKYOPmYfBnekR3vxecjZbm91Vc3JUGnV9mqFX7B+Fe2quw==} engines: {node: '>=20'} dependencies: '@sindresorhus/is': 6.1.0 @@ -12125,6 +12373,16 @@ packages: webidl-conversions: 7.0.0 whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 + dev: false + + /happy-dom@13.6.2: + resolution: {integrity: sha512-Ku+wDqcF/KwFA0dI+xIMZd9Jn020RXjuSil/Vz7gu2yhDC3FsDYZ55qqV9k+SGC4opwb4acisXqVSRxUJMlPbQ==} + engines: {node: '>=16.0.0'} + dependencies: + entities: 4.5.0 + webidl-conversions: 7.0.0 + whatwg-mimetype: 3.0.0 + dev: true /har-schema@2.0.0: resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} @@ -12235,8 +12493,8 @@ packages: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} dev: false - /highlight.js@11.8.0: - resolution: {integrity: sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==} + /highlight.js@11.9.0: + resolution: {integrity: sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==} engines: {node: '>=12.0.0'} dev: false @@ -12275,6 +12533,11 @@ packages: engines: {node: '>=8'} dev: true + /htmlescape@1.1.1: + resolution: {integrity: sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg==} + engines: {node: '>=0.10'} + dev: false + /htmlparser2@8.0.1: resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==} dependencies: @@ -12296,8 +12559,8 @@ packages: statuses: 2.0.1 toidentifier: 1.0.1 - /http-link-header@1.1.1: - resolution: {integrity: sha512-mW3N/rTYpCn99s1do0zx6nzFZSwLH9HGfUM4ZqLWJ16ylmYaC2v5eYGqrNTQlByx8AzUgGI+V/32gXPugs1+Sw==} + /http-link-header@1.1.2: + resolution: {integrity: sha512-6qz1XhMq/ryde52SZGzVhzi3jcG2KqO16KITkupyQxvW6u7iylm0Fq7r3OpCYsc0S0ELlCiFpuxDcccUwjbEqA==} engines: {node: '>=6.0.0'} dev: false @@ -12481,6 +12744,7 @@ packages: /ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: true /ini@2.0.0: resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} @@ -12573,6 +12837,11 @@ packages: /ip@2.0.0: resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} + dev: false + + /ip@2.0.1: + resolution: {integrity: sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==} + dev: true /ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} @@ -13025,7 +13294,7 @@ packages: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.17 + '@types/node': 20.11.22 chalk: 4.1.2 co: 4.6.0 dedent: 1.3.0 @@ -13046,7 +13315,7 @@ packages: - supports-color dev: true - /jest-cli@29.7.0(@types/node@20.11.17): + /jest-cli@29.7.0(@types/node@20.11.22): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -13060,10 +13329,10 @@ packages: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.11.17) + create-jest: 29.7.0(@types/node@20.11.22) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.11.17) + jest-config: 29.7.0(@types/node@20.11.22) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -13074,7 +13343,7 @@ packages: - ts-node dev: true - /jest-config@29.7.0(@types/node@20.11.17): + /jest-config@29.7.0(@types/node@20.11.22): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -13089,7 +13358,7 @@ packages: '@babel/core': 7.23.5 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.17 + '@types/node': 20.11.22 babel-jest: 29.7.0(@babel/core@7.23.5) chalk: 4.1.2 ci-info: 3.7.1 @@ -13149,7 +13418,7 @@ packages: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.17 + '@types/node': 20.11.22 jest-mock: 29.7.0 jest-util: 29.7.0 dev: true @@ -13174,7 +13443,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.6 - '@types/node': 20.11.17 + '@types/node': 20.11.22 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -13225,7 +13494,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 20.11.17 + '@types/node': 20.11.22 dev: true /jest-mock@29.7.0: @@ -13233,7 +13502,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.11.17 + '@types/node': 20.11.22 jest-util: 29.7.0 dev: true @@ -13288,7 +13557,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.17 + '@types/node': 20.11.22 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -13319,7 +13588,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.17 + '@types/node': 20.11.22 chalk: 4.1.2 cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 @@ -13371,7 +13640,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.11.17 + '@types/node': 20.11.22 chalk: 4.1.2 ci-info: 3.7.1 graceful-fs: 4.2.11 @@ -13396,7 +13665,7 @@ packages: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.17 + '@types/node': 20.11.22 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -13415,13 +13684,13 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest@29.7.0(@types/node@20.11.17): + /jest@29.7.0(@types/node@20.11.22): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -13434,7 +13703,7 @@ packages: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.11.17) + jest-cli: 29.7.0(@types/node@20.11.22) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -13693,8 +13962,8 @@ packages: verror: 1.10.0 dev: true - /jsrsasign@11.0.0: - resolution: {integrity: sha512-BtRwVKS+5dsgPpAtzJcpo5OoWjSs1/zllSBG0+8o8/aV0Ki76m6iZwHnwnsqoTdhfFZDN1XIdcaZr5ZkP+H2gg==} + /jsrsasign@11.1.0: + resolution: {integrity: sha512-Ov74K9GihaK9/9WncTe1mPmvrO7Py665TUfUKvraXBpu+xcTWitrtuOwcjf4KMU9maPaYn0OuaWy0HOzy/GBXg==} dev: false /jssha@3.3.1: @@ -13803,8 +14072,8 @@ packages: set-cookie-parser: 2.6.0 dev: false - /lilconfig@3.0.0: - resolution: {integrity: sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==} + /lilconfig@3.1.1: + resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==} engines: {node: '>=14'} dev: false @@ -14697,6 +14966,7 @@ packages: /mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + dev: true /mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} @@ -14864,16 +15134,12 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - /nanoid@5.0.4: - resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==} + /nanoid@5.0.6: + resolution: {integrity: sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==} engines: {node: ^18 || >=20} hasBin: true dev: false - /napi-build-utils@1.0.2: - resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} - dev: false - /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true @@ -14948,21 +15214,10 @@ packages: path-to-regexp: 1.8.0 dev: true - /node-abi@3.31.0: - resolution: {integrity: sha512-eSKV6s+APenqVh8ubJyiu/YhZgxQpGP66ntzUb3lY1xB9ukSRaGnx0AIxI+IM+1+IVYC1oWobgG5L3Lt9ARykQ==} - engines: {node: '>=10'} - dependencies: - semver: 7.5.4 - dev: false - /node-abort-controller@3.1.1: resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} dev: false - /node-addon-api@6.1.0: - resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} - dev: false - /node-bitmap@0.0.1: resolution: {integrity: sha512-Jx5lPaaLdIaOsj2mVLWMWulXF6GQVdyLvNSxmiYCvZ8Ma2hfKX0POoR2kgKOqz+oFsRreq0yYZjQ2wjE9VNzCA==} engines: {node: '>=v0.6.5'} @@ -15015,13 +15270,6 @@ packages: fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 - /node-gyp-build-optional-packages@5.0.3: - resolution: {integrity: sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==} - hasBin: true - requiresBuild: true - dev: false - optional: true - /node-gyp-build-optional-packages@5.0.7: resolution: {integrity: sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==} hasBin: true @@ -15060,8 +15308,8 @@ packages: /node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} - /nodemailer@6.9.8: - resolution: {integrity: sha512-cfrYUk16e67Ks051i4CntM9kshRYei1/o/Gi8K1d+R34OIs21xdFnW7Pt7EucmVKA0LKtqUGNcjMZ7ehjl49mQ==} + /nodemailer@6.9.10: + resolution: {integrity: sha512-qtoKfGFhvIFW5kLfrkw2R6Nm6Ur4LNUMykyqu6n9BRKJuyQrqEGwdXXUAbwWEKt33dlWUGXb7rzmJP/p4+O+CA==} engines: {node: '>=6.0.0'} dev: false @@ -15082,8 +15330,8 @@ packages: undefsafe: 2.0.5 dev: true - /nodemon@3.0.3: - resolution: {integrity: sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==} + /nodemon@3.1.0: + resolution: {integrity: sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==} engines: {node: '>=10'} hasBin: true dependencies: @@ -15880,264 +16128,264 @@ packages: '@babel/runtime': 7.23.4 dev: true - /postcss-calc@9.0.1(postcss@8.4.33): + /postcss-calc@9.0.1(postcss@8.4.35): resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.2.2 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 postcss-selector-parser: 6.0.15 postcss-value-parser: 4.2.0 dev: false - /postcss-colormin@6.0.2(postcss@8.4.33): - resolution: {integrity: sha512-TXKOxs9LWcdYo5cgmcSHPkyrLAh86hX1ijmyy6J8SbOhyv6ua053M3ZAM/0j44UsnQNIWdl8gb5L7xX2htKeLw==} + /postcss-colormin@6.0.3(postcss@8.4.35): + resolution: {integrity: sha512-ECpkS+UZRyAtu/kjive2/1mihP+GNtgC8kcdU8ueWZi1ZVxMNnRziCLdhrWECJhEtSWijfX2Cl9XTTCK/hjGaA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - browserslist: 4.22.2 + browserslist: 4.23.0 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.4.33 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-convert-values@6.0.2(postcss@8.4.33): - resolution: {integrity: sha512-aeBmaTnGQ+NUSVQT8aY0sKyAD/BaLJenEKZ03YK0JnDE1w1Rr8XShoxdal2V2H26xTJKr3v5haByOhJuyT4UYw==} + /postcss-convert-values@6.0.4(postcss@8.4.35): + resolution: {integrity: sha512-YT2yrGzPXoQD3YeA2kBo/696qNwn7vI+15AOS2puXWEvSWqdCqlOyDWRy5GNnOc9ACRGOkuQ4ESQEqPJBWt/GA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - browserslist: 4.22.2 - postcss: 8.4.33 + browserslist: 4.23.0 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-discard-comments@6.0.1(postcss@8.4.33): + /postcss-discard-comments@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-f1KYNPtqYLUeZGCHQPKzzFtsHaRuECe6jLakf/RjSRqvF5XHLZnM2+fXLhb8Qh/HBFHs3M4cSLb1k3B899RYIg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 dev: false - /postcss-discard-duplicates@6.0.1(postcss@8.4.33): - resolution: {integrity: sha512-1hvUs76HLYR8zkScbwyJ8oJEugfPV+WchpnA+26fpJ7Smzs51CzGBHC32RS03psuX/2l0l0UKh2StzNxOrKCYg==} + /postcss-discard-duplicates@6.0.2(postcss@8.4.35): + resolution: {integrity: sha512-U2rsj4w6pAGROCCcD13LP2eBIi1whUsXs4kgE6xkIuGfkbxCBSKhkCTWyowFd66WdVlLv0uM1euJKIgmdmZObg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 dev: false - /postcss-discard-empty@6.0.1(postcss@8.4.33): - resolution: {integrity: sha512-yitcmKwmVWtNsrrRqGJ7/C0YRy53i0mjexBDQ9zYxDwTWVBgbU4+C9jIZLmQlTDT9zhml+u0OMFJh8+31krmOg==} + /postcss-discard-empty@6.0.2(postcss@8.4.35): + resolution: {integrity: sha512-rj6pVC2dVCJrP0Y2RkYTQEbYaCf4HEm+R/2StQgJqGHxAa3+KcYslNQhcRqjLHtl/4wpzipJluaJLqBj6d5eDQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 dev: false - /postcss-discard-overridden@6.0.1(postcss@8.4.33): + /postcss-discard-overridden@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-qs0ehZMMZpSESbRkw1+inkf51kak6OOzNRaoLd/U7Fatp0aN2HQ1rxGOrJvYcRAN9VpX8kUF13R2ofn8OlvFVA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 dev: false - /postcss-merge-longhand@6.0.2(postcss@8.4.33): - resolution: {integrity: sha512-+yfVB7gEM8SrCo9w2lCApKIEzrTKl5yS1F4yGhV3kSim6JzbfLGJyhR1B6X+6vOT0U33Mgx7iv4X9MVWuaSAfw==} + /postcss-merge-longhand@6.0.3(postcss@8.4.35): + resolution: {integrity: sha512-kF/y3DU8CRt+SX3tP/aG+2gkZI2Z7OXDsPU7FgxIJmuyhQQ1EHceIYcsp/alvzCm2P4c37Sfdu8nNrHc+YeyLg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 postcss-value-parser: 4.2.0 - stylehacks: 6.0.2(postcss@8.4.33) + stylehacks: 6.0.3(postcss@8.4.35) dev: false - /postcss-merge-rules@6.0.3(postcss@8.4.33): - resolution: {integrity: sha512-yfkDqSHGohy8sGYIJwBmIGDv4K4/WrJPX355XrxQb/CSsT4Kc/RxDi6akqn5s9bap85AWgv21ArcUWwWdGNSHA==} + /postcss-merge-rules@6.0.4(postcss@8.4.35): + resolution: {integrity: sha512-97iF3UJ5v8N1BWy38y+0l+Z8o5/9uGlEgtWic2PJPzoRrLB6Gxg8TVG93O0EK52jcLeMsywre26AUlX1YAYeHA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - browserslist: 4.22.2 + browserslist: 4.23.0 caniuse-api: 3.0.0 - cssnano-utils: 4.0.1(postcss@8.4.33) - postcss: 8.4.33 + cssnano-utils: 4.0.1(postcss@8.4.35) + postcss: 8.4.35 postcss-selector-parser: 6.0.15 dev: false - /postcss-minify-font-values@6.0.1(postcss@8.4.33): - resolution: {integrity: sha512-tIwmF1zUPoN6xOtA/2FgVk1ZKrLcCvE0dpZLtzyyte0j9zUeB8RTbCqrHZGjJlxOvNWKMYtunLrrl7HPOiR46w==} + /postcss-minify-font-values@6.0.2(postcss@8.4.35): + resolution: {integrity: sha512-IedzbVMoX0a7VZWjSYr5qJ6C37rws8kl8diPBeMZLJfWKkgXuMFY5R/OxPegn/q9tK9ztd0XRH3aR0u2t+A7uQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-minify-gradients@6.0.1(postcss@8.4.33): - resolution: {integrity: sha512-M1RJWVjd6IOLPl1hYiOd5HQHgpp6cvJVLrieQYS9y07Yo8itAr6jaekzJphaJFR0tcg4kRewCk3kna9uHBxn/w==} + /postcss-minify-gradients@6.0.2(postcss@8.4.35): + resolution: {integrity: sha512-vP5mF7iI6/5fcpv+rSfwWQekOE+8I1i7/7RjZPGuIjj6eUaZVeG4XZYZrroFuw1WQd51u2V32wyQFZ+oYdE7CA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: colord: 2.9.3 - cssnano-utils: 4.0.1(postcss@8.4.33) - postcss: 8.4.33 + cssnano-utils: 4.0.1(postcss@8.4.35) + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-minify-params@6.0.2(postcss@8.4.33): - resolution: {integrity: sha512-zwQtbrPEBDj+ApELZ6QylLf2/c5zmASoOuA4DzolyVGdV38iR2I5QRMsZcHkcdkZzxpN8RS4cN7LPskOkTwTZw==} + /postcss-minify-params@6.0.3(postcss@8.4.35): + resolution: {integrity: sha512-j4S74d3AAeCK5eGdQndXSrkxusV2ekOxbXGnlnZthMyZBBvSDiU34CihTASbJxuVB3bugudmwolS7+Dgs5OyOQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - browserslist: 4.22.2 - cssnano-utils: 4.0.1(postcss@8.4.33) - postcss: 8.4.33 + browserslist: 4.23.0 + cssnano-utils: 4.0.1(postcss@8.4.35) + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-minify-selectors@6.0.2(postcss@8.4.33): + /postcss-minify-selectors@6.0.2(postcss@8.4.35): resolution: {integrity: sha512-0b+m+w7OAvZejPQdN2GjsXLv5o0jqYHX3aoV0e7RBKPCsB7TYG5KKWBFhGnB/iP3213Ts8c5H4wLPLMm7z28Sg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 postcss-selector-parser: 6.0.15 dev: false - /postcss-normalize-charset@6.0.1(postcss@8.4.33): + /postcss-normalize-charset@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-aW5LbMNRZ+oDV57PF9K+WI1Z8MPnF+A8qbajg/T8PP126YrGX1f9IQx21GI2OlGz7XFJi/fNi0GTbY948XJtXg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 dev: false - /postcss-normalize-display-values@6.0.1(postcss@8.4.33): + /postcss-normalize-display-values@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-mc3vxp2bEuCb4LgCcmG1y6lKJu1Co8T+rKHrcbShJwUmKJiEl761qb/QQCfFwlrvSeET3jksolCR/RZuMURudw==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-positions@6.0.1(postcss@8.4.33): + /postcss-normalize-positions@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-HRsq8u/0unKNvm0cvwxcOUEcakFXqZ41fv3FOdPn916XFUrympjr+03oaLkuZENz3HE9RrQE9yU0Xv43ThWjQg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-repeat-style@6.0.1(postcss@8.4.33): + /postcss-normalize-repeat-style@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-Gbb2nmCy6tTiA7Sh2MBs3fj9W8swonk6lw+dFFeQT68B0Pzwp1kvisJQkdV6rbbMSd9brMlS8I8ts52tAGWmGQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-string@6.0.1(postcss@8.4.33): + /postcss-normalize-string@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-5Fhx/+xzALJD9EI26Aq23hXwmv97Zfy2VFrt5PLT8lAhnBIZvmaT5pQk+NuJ/GWj/QWaKSKbnoKDGLbV6qnhXg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-timing-functions@6.0.1(postcss@8.4.33): + /postcss-normalize-timing-functions@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-4zcczzHqmCU7L5dqTB9rzeqPWRMc0K2HoR+Bfl+FSMbqGBUcP5LRfgcH4BdRtLuzVQK1/FHdFoGT3F7rkEnY+g==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-unicode@6.0.2(postcss@8.4.33): - resolution: {integrity: sha512-Ff2VdAYCTGyMUwpevTZPZ4w0+mPjbZzLLyoLh/RMpqUqeQKZ+xMm31hkxBavDcGKcxm6ACzGk0nBfZ8LZkStKA==} + /postcss-normalize-unicode@6.0.3(postcss@8.4.35): + resolution: {integrity: sha512-T2Bb3gXz0ASgc3ori2dzjv6j/P2IantreaC6fT8tWjqYUiqMAh5jGIkdPwEV2FaucjQlCLeFJDJh2BeSugE1ig==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - browserslist: 4.22.2 - postcss: 8.4.33 + browserslist: 4.23.0 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-url@6.0.1(postcss@8.4.33): + /postcss-normalize-url@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-jEXL15tXSvbjm0yzUV7FBiEXwhIa9H88JOXDGQzmcWoB4mSjZIsmtto066s2iW9FYuIrIF4k04HA2BKAOpbsaQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-whitespace@6.0.1(postcss@8.4.33): + /postcss-normalize-whitespace@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-76i3NpWf6bB8UHlVuLRxG4zW2YykF9CTEcq/9LGAiz2qBuX5cBStadkk0jSkg9a9TCIXbMQz7yzrygKoCW9JuA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-ordered-values@6.0.1(postcss@8.4.33): + /postcss-ordered-values@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-XXbb1O/MW9HdEhnBxitZpPFbIvDgbo9NK4c/5bOfiKpnIGZDoL2xd7/e6jW5DYLsWxBbs+1nZEnVgnjnlFViaA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - cssnano-utils: 4.0.1(postcss@8.4.33) - postcss: 8.4.33 + cssnano-utils: 4.0.1(postcss@8.4.35) + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-reduce-initial@6.0.2(postcss@8.4.33): - resolution: {integrity: sha512-YGKalhNlCLcjcLvjU5nF8FyeCTkCO5UtvJEt0hrPZVCTtRLSOH4z00T1UntQPj4dUmIYZgMj8qK77JbSX95hSw==} + /postcss-reduce-initial@6.0.3(postcss@8.4.35): + resolution: {integrity: sha512-w4QIR9pEa1N4xMx3k30T1vLZl6udVK2RmNqrDXhBXX9L0mBj2a8ADs8zkbaEH7eUy1m30Wyr5EBgHN31Yq1JvA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - browserslist: 4.22.2 + browserslist: 4.23.0 caniuse-api: 3.0.0 - postcss: 8.4.33 + postcss: 8.4.35 dev: false - /postcss-reduce-transforms@6.0.1(postcss@8.4.33): + /postcss-reduce-transforms@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-fUbV81OkUe75JM+VYO1gr/IoA2b/dRiH6HvMwhrIBSUrxq3jNZQZitSnugcTLDi1KkQh1eR/zi+iyxviUNBkcQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false @@ -16148,24 +16396,24 @@ packages: cssesc: 3.0.0 util-deprecate: 1.0.2 - /postcss-svgo@6.0.2(postcss@8.4.33): + /postcss-svgo@6.0.2(postcss@8.4.35): resolution: {integrity: sha512-IH5R9SjkTkh0kfFOQDImyy1+mTCb+E830+9SV1O+AaDcoHTvfsvt6WwJeo7KwcHbFnevZVCsXhDmjFiGVuwqFQ==} engines: {node: ^14 || ^16 || >= 18} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 postcss-value-parser: 4.2.0 svgo: 3.2.0 dev: false - /postcss-unique-selectors@6.0.2(postcss@8.4.33): + /postcss-unique-selectors@6.0.2(postcss@8.4.35): resolution: {integrity: sha512-8IZGQ94nechdG7Y9Sh9FlIY2b4uS8/k8kdKRX040XHsS3B6d1HrJAkXrBSsSu4SuARruSsUjW3nlSw8BHkaAYQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.33 + postcss: 8.4.35 postcss-selector-parser: 6.0.15 dev: false @@ -16173,14 +16421,6 @@ packages: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} dev: false - /postcss@8.4.33: - resolution: {integrity: sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==} - engines: {node: ^10 || ^12 || >=14} - dependencies: - nanoid: 3.3.7 - picocolors: 1.0.0 - source-map-js: 1.0.2 - /postcss@8.4.35: resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==} engines: {node: ^10 || ^12 || >=14} @@ -16237,25 +16477,6 @@ packages: resolution: {integrity: sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==} dev: true - /prebuild-install@7.1.1: - resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} - engines: {node: '>=10'} - hasBin: true - dependencies: - detect-libc: 2.0.2 - expand-template: 2.0.3 - github-from-package: 0.0.0 - minimist: 1.2.8 - mkdirp-classic: 0.5.3 - napi-build-utils: 1.0.2 - node-abi: 3.31.0 - pump: 3.0.0 - rc: 1.2.8 - simple-get: 4.0.1 - tar-fs: 2.1.1 - tunnel-agent: 0.6.0 - dev: false - /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -16655,16 +16876,6 @@ packages: iconv-lite: 0.4.24 unpipe: 1.0.0 - /rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true - dependencies: - deep-extend: 0.6.0 - ini: 1.3.8 - minimist: 1.2.8 - strip-json-comments: 2.0.1 - dev: false - /rdf-canonize@3.4.0: resolution: {integrity: sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA==} engines: {node: '>=12'} @@ -16846,17 +17057,6 @@ packages: engines: {node: '>= 12.13.0'} dev: false - /recast@0.22.0: - resolution: {integrity: sha512-5AAx+mujtXijsEavc5lWXBPQqrM4+Dl5qNH96N2aNeuJFUzpiiToKPsxQD/zAIJHspz7zz0maX0PCtCTFVlixQ==} - engines: {node: '>= 4'} - dependencies: - assert: 2.1.0 - ast-types: 0.15.2 - esprima: 4.0.1 - source-map: 0.6.1 - tslib: 2.6.2 - dev: true - /recast@0.23.4: resolution: {integrity: sha512-qtEDqIZGVcSZCHniWwZWbRy79Dc6Wp3kT/UmDA2RJKBPg7+7k51aQBZirHmUGn5uvHf2rg8DkjizrN26k61ATw==} engines: {node: '>= 4'} @@ -16903,12 +17103,8 @@ packages: redis-errors: 1.2.0 dev: false - /reflect-metadata@0.1.14: - resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==} - /reflect-metadata@0.2.1: resolution: {integrity: sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==} - dev: false /regenerate-unicode-properties@10.1.0: resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==} @@ -17175,26 +17371,33 @@ packages: dependencies: glob: 7.2.3 - /rollup@4.9.6: - resolution: {integrity: sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg==} + /rimraf@5.0.5: + resolution: {integrity: sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==} + engines: {node: '>=14'} + hasBin: true + dependencies: + glob: 10.3.10 + + /rollup@4.12.0: + resolution: {integrity: sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.9.6 - '@rollup/rollup-android-arm64': 4.9.6 - '@rollup/rollup-darwin-arm64': 4.9.6 - '@rollup/rollup-darwin-x64': 4.9.6 - '@rollup/rollup-linux-arm-gnueabihf': 4.9.6 - '@rollup/rollup-linux-arm64-gnu': 4.9.6 - '@rollup/rollup-linux-arm64-musl': 4.9.6 - '@rollup/rollup-linux-riscv64-gnu': 4.9.6 - '@rollup/rollup-linux-x64-gnu': 4.9.6 - '@rollup/rollup-linux-x64-musl': 4.9.6 - '@rollup/rollup-win32-arm64-msvc': 4.9.6 - '@rollup/rollup-win32-ia32-msvc': 4.9.6 - '@rollup/rollup-win32-x64-msvc': 4.9.6 + '@rollup/rollup-android-arm-eabi': 4.12.0 + '@rollup/rollup-android-arm64': 4.12.0 + '@rollup/rollup-darwin-arm64': 4.12.0 + '@rollup/rollup-darwin-x64': 4.12.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.12.0 + '@rollup/rollup-linux-arm64-gnu': 4.12.0 + '@rollup/rollup-linux-arm64-musl': 4.12.0 + '@rollup/rollup-linux-riscv64-gnu': 4.12.0 + '@rollup/rollup-linux-x64-gnu': 4.12.0 + '@rollup/rollup-linux-x64-musl': 4.12.0 + '@rollup/rollup-win32-arm64-msvc': 4.12.0 + '@rollup/rollup-win32-ia32-msvc': 4.12.0 + '@rollup/rollup-win32-x64-msvc': 4.12.0 fsevents: 2.3.3 /rrweb-cssom@0.6.0: @@ -17261,19 +17464,19 @@ packages: /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - /sanitize-html@2.11.0: - resolution: {integrity: sha512-BG68EDHRaGKqlsNjJ2xUB7gpInPA8gVx/mvjO743hZaeMCZ2DwzW7xvsqZ+KNU4QKwj86HJ3uu2liISf2qBBUA==} + /sanitize-html@2.12.1: + resolution: {integrity: sha512-Plh+JAn0UVDpBRP/xEjsk+xDCoOvMBwQUf/K+/cBAVuTbtX8bj2VB7S1sL1dssVpykqp0/KPSesHrqXtokVBpA==} dependencies: deepmerge: 4.2.2 escape-string-regexp: 4.0.0 htmlparser2: 8.0.1 is-plain-object: 5.0.0 parse-srcset: 1.0.2 - postcss: 8.4.33 + postcss: 8.4.35 dev: false - /sass@1.70.0: - resolution: {integrity: sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==} + /sass@1.71.1: + resolution: {integrity: sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==} engines: {node: '>=14.0.0'} hasBin: true dependencies: @@ -17334,6 +17537,14 @@ packages: dependencies: lru-cache: 6.0.0 + /semver@7.6.0: + resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + /send@0.18.0: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} engines: {node: '>= 0.8.0'} @@ -17395,19 +17606,34 @@ packages: kind-of: 6.0.3 dev: true - /sharp@0.32.6: - resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==} - engines: {node: '>=14.15.0'} + /sharp@0.33.2: + resolution: {integrity: sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==} + engines: {libvips: '>=8.15.1', node: ^18.17.0 || ^20.3.0 || >=21.0.0} requiresBuild: true dependencies: color: 4.2.3 detect-libc: 2.0.2 - node-addon-api: 6.1.0 - prebuild-install: 7.1.1 semver: 7.5.4 - simple-get: 4.0.1 - tar-fs: 3.0.4 - tunnel-agent: 0.6.0 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.2 + '@img/sharp-darwin-x64': 0.33.2 + '@img/sharp-libvips-darwin-arm64': 1.0.1 + '@img/sharp-libvips-darwin-x64': 1.0.1 + '@img/sharp-libvips-linux-arm': 1.0.1 + '@img/sharp-libvips-linux-arm64': 1.0.1 + '@img/sharp-libvips-linux-s390x': 1.0.1 + '@img/sharp-libvips-linux-x64': 1.0.1 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.1 + '@img/sharp-libvips-linuxmusl-x64': 1.0.1 + '@img/sharp-linux-arm': 0.33.2 + '@img/sharp-linux-arm64': 0.33.2 + '@img/sharp-linux-s390x': 0.33.2 + '@img/sharp-linux-x64': 0.33.2 + '@img/sharp-linuxmusl-arm64': 0.33.2 + '@img/sharp-linuxmusl-x64': 0.33.2 + '@img/sharp-wasm32': 0.33.2 + '@img/sharp-win32-ia32': 0.33.2 + '@img/sharp-win32-x64': 0.33.2 dev: false /shebang-command@1.2.0: @@ -17432,10 +17658,10 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - /shiki@1.0.0-beta.3: - resolution: {integrity: sha512-z7cHTNSSvwGx2DfeLwjSNLo+HcVxifgNIzLm6Ye52eXcIwNHXT0wHbhy7FDOKSKveuEHBwt9opfj3Hoc8LE1Yg==} + /shiki@1.1.7: + resolution: {integrity: sha512-9kUTMjZtcPH3i7vHunA6EraTPpPOITYTdA5uMrvsJRexktqP0s7P3s9HVK80b4pP42FRVe03D7fT3NmJv2yYhw==} dependencies: - '@shikijs/core': 1.0.0-beta.3 + '@shikijs/core': 1.1.7 dev: false /side-channel@1.0.4: @@ -17456,18 +17682,6 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - /simple-concat@1.0.1: - resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} - dev: false - - /simple-get@4.0.1: - resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} - dependencies: - decompress-response: 6.0.0 - once: 1.4.0 - simple-concat: 1.0.1 - dev: false - /simple-oauth2@5.0.0: resolution: {integrity: sha512-8291lo/z5ZdpmiOFzOs1kF3cxn22bMj5FFH+DNUppLJrpoIlM1QnFiE7KpshHu3J3i21TVcx4yW+gXYjdCKDLQ==} dependencies: @@ -17856,11 +18070,11 @@ packages: resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==} dev: true - /storybook@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-iXPJck+USEAp9JIBgPvkHNOGzgbfcRoyrk18JMtypwoEXeMZgf6gPw9uKqH2rAoQ0opEYHKbU8FsJ2v+GX01yQ==} + /storybook@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-8d9gpKPDY9Ix64f0560rXIifmnuoswDdvSdTz4NXHGvPt7WrKNmaDTvWGyt1/fbTbv2dvvVp7bsWPgq1KGbrcg==} hasBin: true dependencies: - '@storybook/cli': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) + '@storybook/cli': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) transitivePeerDependencies: - '@babel/preset-env' - bufferutil @@ -18042,11 +18256,6 @@ packages: min-indent: 1.0.1 dev: true - /strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - dev: false - /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -18075,14 +18284,14 @@ packages: peek-readable: 5.0.0 dev: false - /stylehacks@6.0.2(postcss@8.4.33): - resolution: {integrity: sha512-00zvJGnCu64EpMjX8b5iCZ3us2Ptyw8+toEkb92VdmkEaRaSGBNKAoK6aWZckhXxmQP8zWiTaFaiMGIU8Ve8sg==} + /stylehacks@6.0.3(postcss@8.4.35): + resolution: {integrity: sha512-KzBqjnqktc8/I0ERCb+lGq06giF/JxDbw2r9kEVhen9noHeIDRtMWUp9r62sOk+/2bbX6sFG1GhsS7ToXG0PEg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - browserslist: 4.22.2 - postcss: 8.4.33 + browserslist: 4.23.0 + postcss: 8.4.35 postcss-selector-parser: 6.0.15 dev: false @@ -18140,8 +18349,8 @@ packages: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} dev: false - /systeminformation@5.21.24: - resolution: {integrity: sha512-xQada8ByGGFoRXJaUptGgddn3i7IjtSdqNdCKzB8xkzsM7pHnfLYBWxkPdGzhZ0Z/l+W1yo+aZQZ74d2isj8kw==} + /systeminformation@5.22.0: + resolution: {integrity: sha512-oAP80ymt8ssrAzjX8k3frbL7ys6AotqC35oikG6/SG15wBw+tG9nCk4oPaXIhEaAOAZ8XngxUv3ORq2IuR3r4Q==} engines: {node: '>=8.0.0'} os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] hasBin: true @@ -18154,14 +18363,7 @@ packages: mkdirp-classic: 0.5.3 pump: 3.0.0 tar-stream: 2.2.0 - - /tar-fs@3.0.4: - resolution: {integrity: sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==} - dependencies: - mkdirp-classic: 0.5.3 - pump: 3.0.0 - tar-stream: 3.1.6 - dev: false + dev: true /tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} @@ -18172,6 +18374,7 @@ packages: fs-constants: 1.0.0 inherits: 2.0.4 readable-stream: 3.6.0 + dev: true /tar-stream@3.1.6: resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==} @@ -18243,8 +18446,8 @@ packages: unique-string: 2.0.0 dev: true - /terser@5.27.0: - resolution: {integrity: sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==} + /terser@5.28.1: + resolution: {integrity: sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==} engines: {node: '>=10'} hasBin: true dependencies: @@ -18293,8 +18496,8 @@ packages: real-require: 0.2.0 dev: false - /three@0.160.1: - resolution: {integrity: sha512-Bgl2wPJypDOZ1stAxwfWAcJ0WQf7QzlptsxkjYiURPz+n5k4RBDLsq+6f9Y75TYxn6aHLcWz+JNmwTOXWrQTBQ==} + /three@0.162.0: + resolution: {integrity: sha512-xfCYj4RnlozReCmUd+XQzj6/5OjDNHBy5nT6rVwrOKGENAvpXe2z1jL+DZYaMu4/9pNsjH/4Os/VvS9IrH7IOQ==} dev: false /throttle-debounce@5.0.0: @@ -18354,11 +18557,11 @@ packages: os-tmpdir: 1.0.2 dev: true - /tmp@0.2.1: - resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==} - engines: {node: '>=8.17.0'} + /tmp@0.2.2: + resolution: {integrity: sha512-ETcvHhaIc9J2MDEAH6N67j9bvBvu/3Gb764qaGhwtFvjtvhegqoqSpofgeyq1Sc24mW5pdyUDs9HP5j3ehkxRw==} + engines: {node: '>=14'} dependencies: - rimraf: 3.0.2 + rimraf: 5.0.5 /tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} @@ -18509,8 +18712,8 @@ packages: strip-bom: 3.0.0 dev: false - /tsd@0.30.4: - resolution: {integrity: sha512-ncC4SwAeUk0OTcXt5h8l0/gOLHJSp9ogosvOADT6QYzrl0ITm398B3wkz8YESqefIsEEwvYAU8bvo7/rcN/M0Q==} + /tsd@0.30.7: + resolution: {integrity: sha512-oTiJ28D6B/KXoU3ww/Eji+xqHJojiuPVMwA12g4KYX1O72N93Nb6P3P3h2OAhhf92Xl8NIhb/xFmBZd5zw/xUw==} engines: {node: '>=14.16'} hasBin: true dependencies: @@ -18911,6 +19114,18 @@ packages: browserslist: 4.22.2 escalade: 3.1.1 picocolors: 1.0.0 + dev: true + + /update-browserslist-db@1.0.13(browserslist@4.23.0): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.23.0 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: false /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -18961,8 +19176,8 @@ packages: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true - /v-code-diff@1.7.2(vue@3.4.18): - resolution: {integrity: sha512-y+q8ZHf8GfphYLhcZbjAKcId/h6vZujS71Ryq5u+dI6Jg4ZLTdLrBNVSzYpHywHSSFFfBMdilm6XvVryEaH4+A==} + /v-code-diff@1.9.0(vue@3.4.21): + resolution: {integrity: sha512-alg6krCxFvwTob/rJq+3LzjdIbLb/ni8tS8YmBbI0wckOkbJuN1cShFJ6XEkm82tMgpv5NYEeWLEWhggeV7BDg==} requiresBuild: true peerDependencies: '@vue/composition-api': ^1.4.9 @@ -18973,9 +19188,9 @@ packages: dependencies: diff: 5.1.0 diff-match-patch: 1.0.5 - highlight.js: 11.8.0 - vue: 3.4.18(typescript@5.3.3) - vue-demi: 0.13.11(vue@3.4.18) + highlight.js: 11.9.0 + vue: 3.4.21(typescript@5.3.3) + vue-demi: 0.14.7(vue@3.4.21) dev: false /v8-to-istanbul@9.2.0: @@ -19026,7 +19241,7 @@ packages: vfile-message: 4.0.2 dev: true - /vite-node@0.34.6(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0): + /vite-node@0.34.6(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1): resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} engines: {node: '>=v14.18.0'} hasBin: true @@ -19036,7 +19251,7 @@ packages: mlly: 1.5.0 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0) + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) transitivePeerDependencies: - '@types/node' - less @@ -19052,8 +19267,8 @@ packages: resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==} dev: true - /vite@5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0): - resolution: {integrity: sha512-STmSFzhY4ljuhz14bg9LkMTk3d98IO6DIArnTY6MeBwiD1Za2StcQtz7fzOUnRCqrHSD5+OS2reg4HOz1eoLnw==} + /vite@5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1): + resolution: {integrity: sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -19080,12 +19295,12 @@ packages: terser: optional: true dependencies: - '@types/node': 20.11.17 + '@types/node': 20.11.22 esbuild: 0.19.11 postcss: 8.4.35 - rollup: 4.9.6 - sass: 1.70.0 - terser: 5.27.0 + rollup: 4.12.0 + sass: 1.71.1 + terser: 5.28.1 optionalDependencies: fsevents: 2.3.3 @@ -19096,12 +19311,12 @@ packages: vitest: '>=0.16.0' dependencies: cross-fetch: 3.1.6 - vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0) + vitest: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1) transitivePeerDependencies: - encoding dev: true - /vitest@0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0): + /vitest@0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1): resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} engines: {node: '>=v14.18.0'} hasBin: true @@ -19134,7 +19349,7 @@ packages: dependencies: '@types/chai': 4.3.11 '@types/chai-subset': 1.3.5 - '@types/node': 20.11.17 + '@types/node': 20.11.22 '@vitest/expect': 0.34.6 '@vitest/runner': 0.34.6 '@vitest/snapshot': 0.34.6 @@ -19145,7 +19360,7 @@ packages: cac: 6.7.14 chai: 4.3.10 debug: 4.3.4(supports-color@8.1.1) - happy-dom: 10.0.3 + happy-dom: 13.6.2 local-pkg: 0.4.3 magic-string: 0.30.7 pathe: 1.1.2 @@ -19154,8 +19369,8 @@ packages: strip-literal: 1.3.0 tinybench: 2.6.0 tinypool: 0.7.0 - vite: 5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0) - vite-node: 0.34.6(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0) + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) + vite-node: 0.34.6(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) why-is-node-running: 2.2.2 transitivePeerDependencies: - less @@ -19207,6 +19422,21 @@ packages: vscode-languageserver-protocol: 3.17.5 dev: false + /vue-component-meta@1.8.27(typescript@5.3.3): + resolution: {integrity: sha512-j3WJsyQHP4TDlvnjHc/eseo0/eVkf0FaCpkqGwez5zD+Tj31onBzWZEXTnWKs8xRj0n3dMNYdy3SpiS6NubSvg==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@volar/typescript': 1.11.1 + '@vue/language-core': 1.8.27(typescript@5.3.3) + path-browserify: 1.0.1 + typescript: 5.3.3 + vue-component-type-helpers: 1.8.27 + dev: true + /vue-component-type-helpers@1.8.27: resolution: {integrity: sha512-0vOfAtI67UjeO1G6UiX5Kd76CqaQ67wrRZiOe7UAb9Jm6GzlUr/fC7CV90XfwapJRjpCMaZFhv1V0ajWRmE9Dg==} dev: true @@ -19215,8 +19445,8 @@ packages: resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==} dev: true - /vue-demi@0.13.11(vue@3.4.18): - resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==} + /vue-demi@0.14.7(vue@3.4.21): + resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} engines: {node: '>=12'} hasBin: true requiresBuild: true @@ -19227,35 +19457,36 @@ packages: '@vue/composition-api': optional: true dependencies: - vue: 3.4.18(typescript@5.3.3) + vue: 3.4.21(typescript@5.3.3) dev: false - /vue-docgen-api@4.64.1(vue@3.4.18): - resolution: {integrity: sha512-jbOf7ByE3Zvtuk+429Jorl+eIeh2aB2Fx1GUo3xJd1aByJWE8KDlSEa6b11PB1ze8f0sRUBraRDinICCk0KY7g==} + /vue-docgen-api@4.75.1(vue@3.4.21): + resolution: {integrity: sha512-MECZ3uExz+ssmhD/2XrFoQQs93y17IVO1KDYTp8nr6i9GNrk67AAto6QAtilW1H/pTDPMkQxJ7w/25ZIqVtfAA==} + peerDependencies: + vue: '>=2' dependencies: '@babel/parser': 7.23.9 '@babel/types': 7.23.5 '@vue/compiler-dom': 3.4.18 - '@vue/compiler-sfc': 3.4.18 - ast-types: 0.14.2 + '@vue/compiler-sfc': 3.4.21 + ast-types: 0.16.1 hash-sum: 2.0.0 lru-cache: 8.0.4 pug: 3.0.2 - recast: 0.22.0 + recast: 0.23.4 ts-map: 1.0.3 - vue-inbrowser-compiler-independent-utils: 4.64.1(vue@3.4.18) - transitivePeerDependencies: - - vue + vue: 3.4.21(typescript@5.3.3) + vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.4.21) dev: true - /vue-eslint-parser@9.4.2(eslint@8.56.0): + /vue-eslint-parser@9.4.2(eslint@8.57.0): resolution: {integrity: sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: '>=6.0.0' dependencies: debug: 4.3.4(supports-color@8.1.1) - eslint: 8.56.0 + eslint: 8.57.0 eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 @@ -19266,12 +19497,12 @@ packages: - supports-color dev: true - /vue-inbrowser-compiler-independent-utils@4.64.1(vue@3.4.18): - resolution: {integrity: sha512-Hn32n07XZ8j9W8+fmOXPQL+i+W2e/8i6mkH4Ju3H6nR0+cfvmWM95GhczYi5B27+Y8JlCKgAo04IUiYce4mKAw==} + /vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.4.21): + resolution: {integrity: sha512-K3wt3iVmNGaFEOUR4JIThQRWfqokxLfnPslD41FDZB2ajXp789+wCqJyGYlIFsvEQ2P61PInw6/ph5iiqg51gg==} peerDependencies: vue: '>=2' dependencies: - vue: 3.4.18(typescript@5.3.3) + vue: 3.4.21(typescript@5.3.3) dev: true /vue-template-compiler@2.7.14: @@ -19293,28 +19524,28 @@ packages: typescript: 5.3.3 dev: true - /vue@3.4.18(typescript@5.3.3): - resolution: {integrity: sha512-0zLRYamFRe0wF4q2L3O24KQzLyLpL64ye1RUToOgOxuWZsb/FhaNRdGmeozdtVYLz6tl94OXLaK7/WQIrVCw1A==} + /vue@3.4.21(typescript@5.3.3): + resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@vue/compiler-dom': 3.4.18 - '@vue/compiler-sfc': 3.4.18 - '@vue/runtime-dom': 3.4.18 - '@vue/server-renderer': 3.4.18(vue@3.4.18) - '@vue/shared': 3.4.18 + '@vue/compiler-dom': 3.4.21 + '@vue/compiler-sfc': 3.4.21 + '@vue/runtime-dom': 3.4.21 + '@vue/server-renderer': 3.4.21(vue@3.4.21) + '@vue/shared': 3.4.21 typescript: 5.3.3 - /vuedraggable@4.1.0(vue@3.4.18): + /vuedraggable@4.1.0(vue@3.4.21): resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==} peerDependencies: vue: ^3.0.1 dependencies: sortablejs: 1.14.0 - vue: 3.4.18(typescript@5.3.3) + vue: 3.4.21(typescript@5.3.3) dev: false /w3c-xmlserializer@5.0.0: @@ -19398,6 +19629,7 @@ packages: engines: {node: '>=12'} dependencies: iconv-lite: 0.6.3 + dev: false /whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} @@ -19755,7 +19987,7 @@ packages: vscode-languageclient: 9.0.1 dev: false - github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.0-beta.2)(@storybook/components@8.0.0-beta.2)(@storybook/core-events@8.0.0-beta.2)(@storybook/manager-api@8.0.0-beta.2)(@storybook/preview-api@8.0.0-beta.2)(@storybook/theming@8.0.0-beta.2)(@storybook/types@8.0.0-beta.2)(react-dom@18.2.0)(react@18.2.0): + github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.0-beta.6)(@storybook/components@8.0.0-beta.6)(@storybook/core-events@8.0.0-beta.6)(@storybook/manager-api@8.0.0-beta.6)(@storybook/preview-api@8.0.0-beta.6)(@storybook/theming@8.0.0-beta.6)(@storybook/types@8.0.0-beta.6)(react-dom@18.2.0)(react@18.2.0): resolution: {tarball: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640} id: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640 name: storybook-addon-misskey-theme @@ -19776,13 +20008,13 @@ packages: react-dom: optional: true dependencies: - '@storybook/blocks': 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) - '@storybook/components': 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-events': 8.0.0-beta.2 - '@storybook/manager-api': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 8.0.0-beta.2 - '@storybook/theming': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 8.0.0-beta.2 + '@storybook/blocks': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + '@storybook/components': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 8.0.0-beta.6 + '@storybook/manager-api': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 8.0.0-beta.6 + '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 8.0.0-beta.6 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true |