diff options
| author | Hazelnoot <acomputerdog@gmail.com> | 2025-04-24 14:23:45 -0400 |
|---|---|---|
| committer | Hazelnoot <acomputerdog@gmail.com> | 2025-04-24 14:23:45 -0400 |
| commit | a4dd19fdd427a5adc8fa80871d1c742aa9708730 (patch) | |
| tree | 3983a0e772922042043026b4af168e8dd3525fb2 | |
| parent | Merge branch 'develop' into merge/2025-03-24 (diff) | |
| parent | enhance(backend): DB note (userId) インデクス -> (userId, id) 複合イ... (diff) | |
| download | sharkey-a4dd19fdd427a5adc8fa80871d1c742aa9708730.tar.gz sharkey-a4dd19fdd427a5adc8fa80871d1c742aa9708730.tar.bz2 sharkey-a4dd19fdd427a5adc8fa80871d1c742aa9708730.zip | |
merge upstream again
167 files changed, 6715 insertions, 3888 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 7edf9c156d..ef02c8431b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +## 2025.4.1 + +### General +- Feat: bull-boardに代わるジョブキューの管理ツールが実装されました +- Enhance: チャットの新規メッセージをプッシュ通知するように + +### Client +- Feat: チャットウィジェットを追加 +- Feat: デッキにチャットカラムを追加 +- Enhance: Unicode絵文字をslugから入力する際に`:ok:`のように最後の`:`を入力したあとにUnicode絵文字に変換できるように +- Enhance: コントロールパネルでジョブキューをクリアできるように +- Enhance: テーマでページヘッダーの色を変更できるように +- Enhance: デザインのブラッシュアップ +- Fix: ログアウトした際に処理が終了しない問題を修正 +- Fix: 自動バックアップが設定されている環境でログアウト直前に設定をバックアップするように +- Fix: フォルダを開いた状態でメニューからアップロードしてもルートフォルダにアップロードされる問題を修正 #15836 +- Fix: タイムラインのスクロール位置を記憶するように修正 +- Fix: ノートの直後のノートを表示する機能で表示が逆順になっていた問題を修正 #15841 +- Fix: アカウントの移行時にアンテナのフィルターのユーザが更新されない問題を修正 #15843 + +### Server +- Enhance: ジョブキューの成功/失敗したジョブも一定数・一定期間保存するようにし、後から問題を調査することを容易に +- Enhance: フォローしているユーザーならフォロワー限定投稿のノートでもアンテナで検知できるように + (Cherry-picked from https://github.com/yojo-art/cherrypick/pull/568 and https://github.com/team-shahu/misskey/pull/38) +- Enhance: ユーザーごとにノートの表示が高速化するように +- Fix: システムアカウントの名前がサーバー名と同期されない問題を修正 +- Fix: 大文字を含むユーザの URL で紹介された場合に 404 エラーを返す問題 #15813 +- Fix: リードレプリカ設定時にレコードの追加・更新・削除を伴うクエリを発行した際はmasterノードで実行されるように調整( #10897 ) + ## 2025.4.0 ### General @@ -30,7 +59,7 @@ - プラグイン、テーマ、クライアントに追加されたすべてのアカウント情報も含まれるようになりました - 自動で設定データをサーバーにバックアップできるように - 設定→設定のプロファイル→自動バックアップ で有効にできます - - 新しいデバイスからログインしたり、ブラウザから設定データが消えてしまったときに自動で復元されます(復元をスキップすることも可能) + - ログインしたとき、ブラウザから設定データが消えてしまったときに自動で復元されます(復元をスキップすることも可能) - 任意の設定項目をデバイス間で同期できるように - 設定項目の「...」メニュー→「デバイス間で同期」 - 同期をオンにした際にサーバーに保存された値とローカルの値が競合する場合はどちらを優先するか選択できます @@ -39,7 +68,7 @@ - アカウントごとに設定値が分離される設定とそうでないクライアント設定が混在していた(かつ分離するかどうかを設定不可だった)のを、基本的に一律でクライアント全体に適用されるようにし、個別でアカウントごとに異なる設定を行えるように - 設定項目の「...」メニュー→「アカウントで上書き」をオンにすることで、設定値をそのアカウントでだけ適用するようにできます - ログアウトすると設定データもブラウザから消去されるようになりプライバシーが向上しました - - 再度ログインすればサーバーのバックアップから設定データを復元可能です + - バックアップを有効にしている場合、ログインした後にバックアップから設定データを復元可能です - エクスポートした設定データを他のサーバーでインポートして適用すること(設定の持ち運び)が可能になりました - 設定情報の移行は自動で行われますが、何らかの理由で失敗した場合、設定→その他→旧設定情報を移行 で再試行可能です - 過去に作成されたバックアップデータとは現在互換性がありませんのでご注意ください diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index 8f83a33356..129c3d24a5 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -356,7 +356,7 @@ banner: "Bàner" displayOfSensitiveMedia: "Visualització de contingut sensible" whenServerDisconnected: "Quan es perdi la connexió al servidor" disconnectedFromServer: "Desconnectat pel servidor" -reload: "Actualitza" +reload: "Actualitzar" doNothing: "Ignora" reloadConfirm: "Vols recarregar?" watch: "Veure" @@ -708,7 +708,7 @@ notificationSetting: "Paràmetres de notificacions" notificationSettingDesc: "Selecciona els tipus de notificacions que es mostraran" useGlobalSetting: "Fer servir la configuració global" useGlobalSettingDesc: "Si s'activa, es farà servir la configuració de notificacions del teu comte. Si no s'activa es poden fer configuracions individuals." -other: "Altre" +other: "Altres" regenerateLoginToken: "Regenerar clau de seguretat d'inici de sessió" regenerateLoginTokenDescription: "Regenera la clau de seguretat que es fa servir internament durant l'inici de sessió. Normalment aquesta acció no és necessària. Si es regenera es tancarà la sessió a tots els dispositius amb una sessió activa." theKeywordWhenSearchingForCustomEmoji: "Cercar un emoji personalitzat " @@ -979,6 +979,7 @@ document: "Documentació" numberOfPageCache: "Nombre de pàgines a la memòria cau" numberOfPageCacheDescription: "Incrementant aquest nombre farà que millori l'experiència de l'usuari, però es farà servir més memòria al dispositiu de l'usuari." logoutConfirm: "Vols sortir?" +logoutWillClearClientData: "En tancar la sessió, la informació del client al navegador s'esborrarà. Per garantir que la informació de configuració es pugui restaurar en tornar a iniciar sessió activa la còpia de seguretat automàtica de la configuració." lastActiveDate: "Fet servir per última vegada" statusbar: "Barra d'estat" pleaseSelect: "Selecciona una opció" @@ -1334,14 +1335,14 @@ postForm: "Formulari de publicació" textCount: "Nombre de caràcters " information: "Informació" chat: "Xat" -migrateOldSettings: "Migració de la configuració antiga " +migrateOldSettings: "Migrar la configuració anterior" migrateOldSettings_description: "Normalment això es fa automàticament, però si la transició no es fa, el procés es pot iniciar manualment. S'esborrarà la configuració actual." compress: "Comprimir " right: "Dreta" bottom: "A baix " top: "A dalt " embed: "Incrustar" -settingsMigrating: "Estem fent la migració de la teva configuració. Si us plau espera un moment... (També pots fer la migració més tard i manualment anant a Preferències → Altres configuracions → Migrar configuració antiga)" +settingsMigrating: "Estem migrant la teva configuració. Si us plau espera un moment... (També pots fer la migració més tard, manualment, anant a Preferències → Altres → Migrar configuració antiga)" readonly: "Només lectura" goToDeck: "Tornar al tauler" _chat: @@ -1359,7 +1360,7 @@ _chat: noInvitations: "No tens cap invitació " history: "Historial de converses " noHistory: "No hi ha un registre previ" - noRooms: "No hi ha habitacions" + noRooms: "No hi ha cap sala" inviteUser: "Invitar usuaris" sentInvitations: "Enviar invitacions" join: "Afegir-se " @@ -1417,8 +1418,8 @@ _settings: makeEveryTextElementsSelectable_description: "L'activació pot reduir la usabilitat en determinades ocasions." useStickyIcons: "Utilitza icones fixes" showNavbarSubButtons: "Mostrar sub botons a la barra de navegació " - ifOn: "Quan s'encén " - ifOff: "Quan s'apaga " + ifOn: "Quan s'activa" + ifOff: "Quan es desactiva" enableSyncThemesBetweenDevices: "Sincronitzar els temes instal·lats entre dispositius" _chat: showSenderName: "Mostrar el nom del remitent" @@ -1604,7 +1605,7 @@ _accountMigration: moveTo: "Migrar aquest compte a un altre" moveToLabel: "Compte al qual es vol migrar:" moveCannotBeUndone: "Les migracions dels comptes no es poden desfer." - moveAccountDescription: "Això migrarà la teva compte a un altre diferent.\n ・Els seguidors d'aquest compte és passaran al compte nou de forma automàtica\n ・Es deixaran de seguir a tots els usuaris que es segueixen actualment en aquest compte\n ・No es poden crear notes noves, etc. en aquest compte\n\nSi bé la migració de seguidors es automàtica, has de preparar alguns pasos manualment per migrar la llista d'usuaris que segueixes. Per fer això has d'exportar els seguidors que després importaraes al compte nou mitjançant el menú de configuració. El mateix procediment s'ha de seguir per less teves llistes i els teus usuaris silenciats i bloquejats.\n\n(Aquesta explicació s'aplica a Misskey v13.12.0 i posteriors. Altres aplicacions, com Mastodon, poden funcionar diferent.)" + moveAccountDescription: "Això migrarà el teu compte a un altre diferent.\n ・Els seguidors d'aquest compte és passaran al compte nou de forma automàtica\n ・Es deixaran de seguir a tots els usuaris que es segueixen actualment en aquest compte\n ・No es poden crear notes noves, etc. en aquest compte\n\nSi bé la migració de seguidors es automàtica, has de preparar alguns pasos manualment per migrar la llista d'usuaris que segueixes. Per fer això has d'exportar els seguidors que després importaraes al compte nou mitjançant el menú de configuració. El mateix procediment s'ha de seguir per less teves llistes i els teus usuaris silenciats i bloquejats.\n\n(Aquesta explicació s'aplica a Misskey v13.12.0 i posteriors. Altres aplicacions, com Mastodon, poden funcionar diferent.)" moveAccountHowTo: "Per fer la migració, primer has de crear un àlies per aquest compte al compte al qual vols migrar.\nDesprés de crear l'àlies, introdueix el compte al qual vols migrar amb el format següent: @nomusuari@servidor.exemple.com" startMigration: "Migrar" migrationConfirm: "Vols migrar aquest compte a {account}? Una vegada comenci la migració no es podrà parar O fer marxa enrere i no podràs tornar a fer servir aquest compte mai més." @@ -2368,6 +2369,7 @@ _widgets: chooseList: "Tria una llista" clicker: "Clicker" birthdayFollowings: "Usuaris que fan l'aniversari avui" + chat: "Xat" _cw: hide: "Amagar" show: "Carregar més" @@ -2634,6 +2636,7 @@ _deck: mentions: "Mencions" direct: "Publicacions directes" roleTimeline: "Línia de temps dels rols" + chat: "Xat" _dialog: charactersExceeded: "Has arribat al màxim de caràcters! Actualment és {current} de {max}" charactersBelow: "Ets per sota del mínim de caràcters! Actualment és {current} de {min}" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index b953a5717e..237603299c 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -2368,6 +2368,7 @@ _widgets: chooseList: "Liste auswählen" clicker: "Klickzähler" birthdayFollowings: "Nutzer, die heute Geburtstag haben" + chat: "Chat" _cw: hide: "Inhalt verbergen" show: "Inhalt anzeigen" @@ -2634,6 +2635,7 @@ _deck: mentions: "Erwähnungen" direct: "Direktnachrichten" roleTimeline: "Rollenchronik" + chat: "Chat" _dialog: charactersExceeded: "Maximallänge überschritten! Momentan {current} von {max}" charactersBelow: "Minimallänge unterschritten! Momentan {current} von {min}" diff --git a/locales/en-US.yml b/locales/en-US.yml index 06a536bcf6..b695a9bc5c 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -2368,6 +2368,7 @@ _widgets: chooseList: "Select a list" clicker: "Clicker" birthdayFollowings: "Today's Birthdays" + chat: "Chat" _cw: hide: "Hide" show: "Show content" @@ -2634,6 +2635,7 @@ _deck: mentions: "Mentions" direct: "Direct notes" roleTimeline: "Role Timeline" + chat: "Chat" _dialog: charactersExceeded: "You've exceeded the maximum character limit! Currently at {current} of {max}." charactersBelow: "You're below the minimum character limit! Currently at {current} of {min}." diff --git a/locales/es-ES.yml b/locales/es-ES.yml index f60e9beabe..713478b67e 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -301,6 +301,7 @@ uploadFromUrlMayTakeTime: "Subir el fichero puede tardar un tiempo." explore: "Explorar" messageRead: "Ya leído" noMoreHistory: "El historial se ha acabado" +startChat: "Nuevo Chat" nUsersRead: "Leído por {n} personas" agreeTo: "De acuerdo con {0}" agree: "De acuerdo." @@ -694,6 +695,7 @@ userSaysSomethingAbout: "{name} dijo algo sobre {word}" makeActive: "Activar" display: "Apariencia" copy: "Copiar" +copiedToClipboard: "Texto copiado al portapapeles" metrics: "Métricas" overview: "Resumen" logs: "Registros" @@ -1293,18 +1295,55 @@ passkeyVerificationFailed: "La verificación de la clave de acceso ha fallado." passkeyVerificationSucceededButPasswordlessLoginDisabled: "La verificación de la clave de acceso ha sido satisfactoria pero se ha deshabilitado el inicio de sesión sin contraseña." messageToFollower: "Mensaje a seguidores" target: "Para" +prohibitedWordsForNameOfUser: "Palabras prohibidas para nombres de usuario" +prohibitedWordsForNameOfUserDescription: "Si alguna de las cadenas de esta lista está incluida en el nombre del usuario, el nombre será denegado. Los usuarios con privilegios de moderador no se ven afectados por esta restricción." +yourNameContainsProhibitedWords: "Tu nombre contiene palabras prohibidas" +yourNameContainsProhibitedWordsDescription: "Si deseas usar este nombre, por favor contacta con tu administrador/a de tu servidor" +lockdown: "Bloqueo" +pleaseSelectAccount: "Seleccione una cuenta, por favor." +availableRoles: "Roles disponibles " +acknowledgeNotesAndEnable: "Activar después de comprender las precauciones" federationSpecified: "Este servidor opera en una federación de listas blancas. No puede interactuar con otros servidores que no sean los especificados por el administrador." federationDisabled: "La federación está desactivada en este servidor. No puede interactuar con usuarios de otros servidores" preferences: "Preferencias" postForm: "Formulario" information: "Información" +right: "Derecha" +bottom: "Abajo" +top: "Arriba" +embed: "Insertar" +settingsMigrating: "La configuración está siendo migrada, por favor espera un momento... (También puedes migrar manualmente más tarde yendo a Ajustes otros migrar configuración antigua" +readonly: "Solo Lectura" _chat: + noMessagesYet: "Aún no hay mensajes" + newMessage: "Mensajes nuevos" + individualChat: "Chat individual" + individualChat_description: "Mantén una conversación privada con otra persona." invitations: "Invitar" noHistory: "No hay datos en el historial" members: "Miembros" home: "Inicio" send: "Enviar" + chatNotAvailableInOtherAccount: "La función de chat está desactivada para el otro usuario." + cannotChatWithTheUser: "No se puede iniciar un chat con este usuario" + cannotChatWithTheUser_description: "El chat no está disponible o la otra parte no ha habilitado el chat." + chatWithThisUser: "Chatear" + thisUserAllowsChatOnlyFromFollowers: "Este usuario sólo acepta chats de seguidores." + thisUserAllowsChatOnlyFromFollowing: "Este usuario sólo acepta chats de los usuarios a los que sigue." + thisUserAllowsChatOnlyFromMutualFollowing: "Este usuario sólo acepta chats de usuarios que son seguidores mutuos." + thisUserNotAllowedChatAnyone: "Este usuario no acepta chats de nadie." + chatAllowedUsers: "A quién permitir chatear." + chatAllowedUsers_note: "Puedes chatear con cualquier persona a la que hayas enviado un mensaje de chat, independientemente de esta configuración." + _chatAllowedUsers: + everyone: "Todos" + followers: "Sólo sus propios seguidores." + following: "Solo usuarios que sigues" + mutual: "Solo seguidores mutuos" + none: "Nadie" +_emojiPalette: + palettes: "Paleta\n" _settings: + api: "API" webhook: "Webhook" _accountSettings: requireSigninToViewContents: "Se requiere iniciar sesión para ver el contenido" diff --git a/locales/index.d.ts b/locales/index.d.ts index d77c4137c3..dae5d912fc 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -3936,6 +3936,10 @@ export interface Locale extends ILocale { */ "logoutConfirm": string; /** + * ログアウトするとクライアントの設定情報がブラウザから消去されます。再ログイン時に設定情報を復元できるようにするためには、設定の自動バックアップを有効にしてください。 + */ + "logoutWillClearClientData": string; + /** * 最終利用日時 */ "lastActiveDate": string; @@ -5395,6 +5399,10 @@ export interface Locale extends ILocale { * デッキへ戻る */ "goToDeck": string; + /** + * 連合ジョブ + */ + "federationJobs": string; "_chat": { /** * まだメッセージはありません @@ -9316,6 +9324,10 @@ export interface Locale extends ILocale { */ "birthdayFollowings": string; /** + * チャット + */ + "chat": string; + /** * Search */ "search": string; @@ -10407,6 +10419,10 @@ export interface Locale extends ILocale { */ "roleTimeline": string; /** + * チャット + */ + "chat": string; + /** * Following */ "following": string; diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 44b0858fe8..75691d817f 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -424,6 +424,7 @@ antennaExcludeBots: "Escludere i Bot" antennaKeywordsDescription: "Sparando con uno spazio indichi la condizione E (and). Separando con un a capo, indichi la condizione O (or)." notifyAntenna: "Invia notifiche delle nuove note" withFileAntenna: "Solo note con file in allegato" +excludeNotesInSensitiveChannel: "Escludere le Note dai canali espliciti" enableServiceworker: "Abilita ServiceWorker" antennaUsersDescription: "Elenca un nome utente per riga" caseSensitive: "Sensibile alla distinzione tra maiuscole e minuscole" @@ -727,7 +728,7 @@ reporterOrigin: "Segnalazione da" send: "Inviare" openInNewTab: "Apri in una nuova scheda" openInSideView: "Apri in vista laterale" -defaultNavigationBehaviour: "Navigazione preimpostata" +defaultNavigationBehaviour: "Tipo di navigazione predefinita" editTheseSettingsMayBreakAccount: "Modificare queste impostazioni può danneggiare il profilo" instanceTicker: "Informazioni sull'istanza da cui vengono le note" waitingFor: "Aspettando {x}" @@ -866,7 +867,7 @@ noBotProtectionWarning: "Non è stata impostata alcuna protezione dai Bot" configure: "Imposta" postToGallery: "Pubblicare nella galleria" postToHashtag: "Pubblica a questo hashtag" -gallery: "Galleria" +gallery: "Gallerie" recentPosts: "Pubblicazioni recenti" popularPosts: "Le più visualizzate" shareWithNote: "Condividere in nota" @@ -978,6 +979,7 @@ document: "Documentazione" numberOfPageCache: "Numero di pagine cache" numberOfPageCacheDescription: "Aumenta l'usabilità, ma aumenta anche il carico e l'utilizzo della memoria." logoutConfirm: "Vuoi davvero uscire da Misskey? " +logoutWillClearClientData: "All'uscita, la configurazione del client viene rimossa dal browser. Per ripristinarla quando si effettua nuovamente l'accesso, abilitare il backup automatico." lastActiveDate: "Data dell'ultimo utilizzo" statusbar: "Barra di stato" pleaseSelect: "Scegli un'opzione" @@ -1340,6 +1342,9 @@ right: "Destra" bottom: "Sotto" top: "Sopra" embed: "Incorporare" +settingsMigrating: "Migrazione delle impostazioni. Attendere prego ... (Puoi anche migrare manualmente in un secondo momento, nel menu: Impostazioni → Altro → Migrazione delle impostazioni)" +readonly: "Sola lettura" +goToDeck: "Torna al Deck" _chat: noMessagesYet: "Ancora nessun messaggio" newMessage: "Nuovo messaggio" @@ -1369,6 +1374,7 @@ _chat: muteThisRoom: "Silenzia stanza" deleteRoom: "Elimina stanza" chatNotAvailableForThisAccountOrServer: "Questo server, o questo profilo ha disabilitato la chat." + chatIsReadOnlyForThisAccountOrServer: "Le chat, su questo server o su questo profilo, sono di sola lettura. Impossibile scrivere in chat o creare e partecipare a stanze." chatNotAvailableInOtherAccount: "La chat non è disponibile nel profilo dell'altra persona." cannotChatWithTheUser: "Impossibile chattare con questa persona" cannotChatWithTheUser_description: "La chat potrebbe non essere disponibile, oppure l'altra persona potrebbe non esserlo." @@ -1929,6 +1935,7 @@ _role: canImportFollowing: "Può importare Following" canImportMuting: "Può importare Silenziati" canImportUserLists: "Può importare liste di Profili" + chatAvailability: "Chat consentita" _condition: roleAssignedTo: "Assegnato a ruoli manualmente" isLocal: "Profilo locale" @@ -2362,6 +2369,7 @@ _widgets: chooseList: "Seleziona una lista" clicker: "Cliccheria" birthdayFollowings: "Compleanni del giorno" + chat: "Chat" _cw: hide: "Nascondere" show: "Continua la lettura..." @@ -2594,8 +2602,8 @@ _notification: renote: "Rinota" _deck: alwaysShowMainColumn: "Mostra sempre la colonna principale" - columnAlign: "Allineare colonne" - columnGap: "Margine tra le colonne" + columnAlign: "Allineamento delle colonne" + columnGap: "Spessore del margine tra colonne" deckMenuPosition: "Posizione del menu Deck" navbarPosition: "Posizione barra di navigazione" addColumn: "Aggiungi colonna" @@ -2624,10 +2632,11 @@ _deck: tl: "Timeline" antenna: "Antenne" list: "Liste" - channel: "Canale" + channel: "Canali" mentions: "Menzioni" direct: "Note Dirette" roleTimeline: "Timeline Ruolo" + chat: "Chat" _dialog: charactersExceeded: "Hai superato il limite di {max} caratteri! ({current})" charactersBelow: "Sei al di sotto del minimo di {min} caratteri! ({current})" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index c7a9fc8792..fb2590d8fd 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -979,6 +979,7 @@ document: "ドキュメント" numberOfPageCache: "ページキャッシュ数" numberOfPageCacheDescription: "多くすると利便性が向上しますが、負荷とメモリ使用量が増えます。" logoutConfirm: "ログアウトしますか?" +logoutWillClearClientData: "ログアウトするとクライアントの設定情報がブラウザから消去されます。再ログイン時に設定情報を復元できるようにするためには、設定の自動バックアップを有効にしてください。" lastActiveDate: "最終利用日時" statusbar: "ステータスバー" pleaseSelect: "選択してください" @@ -1344,6 +1345,7 @@ embed: "埋め込み" settingsMigrating: "設定を移行しています。しばらくお待ちください... (後ほど、設定→その他→旧設定情報を移行 で手動で移行することもできます)" readonly: "読み取り専用" goToDeck: "デッキへ戻る" +federationJobs: "連合ジョブ" _chat: noMessagesYet: "まだメッセージはありません" @@ -2420,6 +2422,7 @@ _widgets: chooseList: "リストを選択" clicker: "クリッカー" birthdayFollowings: "今日誕生日のユーザー" + chat: "チャット" _cw: hide: "隠す" @@ -2704,6 +2707,7 @@ _deck: mentions: "あなた宛て" direct: "ダイレクト" roleTimeline: "ロールタイムライン" + chat: "チャット" _dialog: charactersExceeded: "最大文字数を超えています! 現在 {current} / 制限 {max}" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index fc578758a2..fb7db80186 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -489,7 +489,7 @@ next: "다음" retype: "다시 입력" noteOf: "{user}의 노트" quoteAttached: "인용함" -quoteQuestion: "인용해서 작성하시겠습니까?" +quoteQuestion: "인용해서 첨부하시겠습니까?" attachAsFileQuestion: "붙여넣으려는 글이 너무 깁니다. 텍스트 파일로 첨부하시겠습니까?" onlyOneFileCanBeAttached: "메시지에 첨부할 수 있는 파일은 하나까지입니다" signinRequired: "진행하기 전에 로그인을 해 주세요" @@ -571,8 +571,8 @@ objectStorageSetPublicRead: "업로드할 때 'public-read'를 설정하기" s3ForcePathStyleDesc: "s3ForcePathStyle을 활성화하면, 버킷 이름을 URL의 호스트명이 아닌 경로의 일부로써 취급합니다. 셀프 호스트 Minio와 같은 서비스를 사용할 경우 활성화해야 할 수 있습니다." serverLogs: "서버 로그" deleteAll: "모두 삭제" -showFixedPostForm: "타임라인 상단에 글 작성란을 표시" -showFixedPostFormInChannel: "채널 타임라인 상단에 글 작성란을 표시" +showFixedPostForm: "타임라인 상단에 글 입력란을 표시" +showFixedPostFormInChannel: "채널 타임라인 상단에 글 입력란을 표시" withRepliesByDefaultForNewlyFollowed: "팔로우 할 때 기본적으로 답글을 타임라인에 나오게 하기" newNoteRecived: "새 노트가 있습니다" sounds: "소리" @@ -720,7 +720,7 @@ abuseReports: "신고" reportAbuse: "신고" reportAbuseRenote: "리노트 신고하기" reportAbuseOf: "{name} 신고하기" -fillAbuseReportDescription: "신고하려는 이유를 자세히 알려주세요. 특정 게시물을 신고할 때에는 게시물의 URL도 포함해 주세요." +fillAbuseReportDescription: "신고 사유를 자세히 기재해 주세요. 대상 노트나 페이지 등이 있는 경우에는 해당 URL도 기재해 주세요." abuseReported: "신고를 보냈습니다. 신고해 주셔서 감사합니다." reporter: "신고자" reporteeOrigin: "피신고자" @@ -825,7 +825,7 @@ editCode: "코드 수정" apply: "적용" receiveAnnouncementFromInstance: "이 서버의 알림을 이메일로 수신할게요" emailNotification: "메일 알림" -publish: "게시" +publish: "공개" inChannelSearch: "채널에서 검색" useReactionPickerForContextMenu: "우클릭하여 리액션 선택기 열기" typingUsers: "{users}님이 입력 중" @@ -1081,7 +1081,7 @@ sensitiveWords: "민감한 단어" sensitiveWordsDescription: "설정한 단어가 포함된 노트의 공개 범위를 '홈'으로 강제합니다. 개행으로 구분하여 여러 개를 지정할 수 있습니다." sensitiveWordsDescription2: "공백으로 구분하면 AND 지정이 되며, 키워드를 슬래시로 둘러싸면 정규 표현식이 됩니다." prohibitedWords: "금지 단어" -prohibitedWordsDescription: "설정된 단어가 포함되는 노트를 작성하려고 하면, 오류가 발생하도록 합니다. 줄바꿈으로 구분지어 복수 설정할 수 있습니다." +prohibitedWordsDescription: "설정된 단어가 포함되는 노트를 게시하려고 하면, 오류가 발생하도록 합니다. 줄바꿈으로 구분지어 복수 설정할 수 있습니다." prohibitedWordsDescription2: "공백으로 구분하면 AND 지정이 되며, 키워드를 슬래시로 둘러싸면 정규 표현식이 됩니다." hiddenTags: "숨긴 해시태그" hiddenTagsDescription: "설정한 태그를 트렌드에 표시하지 않도록 합니다. 줄 바꿈으로 하나씩 나눠서 설정할 수 있습니다." @@ -1373,7 +1373,7 @@ _chat: muteThisRoom: "이 룸을 뮤트" deleteRoom: "룸을 삭제" chatNotAvailableForThisAccountOrServer: "이 서버 또는 이 계정에서 채팅이 활성화되어 있지 않습니다." - chatIsReadOnlyForThisAccountOrServer: "이 서버 또는 이 계정에서 채팅은 읽기 전용입니다. 새로 쓰거나 채팅방을 만들거나 참가할 수 없습니다." + chatIsReadOnlyForThisAccountOrServer: "이 서버 또는 이 계정에서 채팅은 읽기 전용입니다. 새로 쓰거나 채팅 룸을 만들거나 참가할 수 없습니다." chatNotAvailableInOtherAccount: "상대방 계정에서 채팅 기능을 사용할 수 없는 상태입니다." cannotChatWithTheUser: "이 유저와 채팅을 시작할 수 없습니다" cannotChatWithTheUser_description: "채팅을 사용할 수 없는 상태이거나 상대방이 채팅을 열지 않은 상태입니다." @@ -1542,7 +1542,7 @@ _initialTutorial: description3: "이 외에도, '리스트 타임라인'이나 '채널 타임라인' 등이 있습니다. 자세한 사항은 {link}에서 확인하실 수 있습니다." _postNote: title: "노트 게시 설정" - description1: "Misskey에 노트를 쓸 때에는 다양한 옵션을 설정할 수 있습니다. 노트를 작성하는 화면은 이렇게 생겼습니다." + description1: "Misskey에 노트를 게시할 때에는 다양한 옵션 설정이 가능합니다. 노트를 게시할 때 쓰이는 '글 입력란'은 이렇게 생겼습니다." _visibility: description: "노트를 볼 수 있는 사람을 제한할 수 있습니다." public: "모든 유저에게 공개합니다." @@ -1562,7 +1562,7 @@ _initialTutorial: _howToMakeAttachmentsSensitive: title: "첨부 파일을 열람주의로 설정하려면?" description: "서버의 가이드라인에 따라 필요한 이미지, 또는 그대로 노출되기에 부적절한 미디어는 '열람 주의'를 설정해 주세요." - tryThisFile: "이 작성 창에 첨부된 이미지를 열람 주의로 설정해 보세요!" + tryThisFile: "이 입력란에 첨부된 이미지를 열람 주의로 설정해 보세요!" _exampleNote: note: "낫또 뚜껑 뜯다가 실수했다…" method: "첨부 파일을 열람 주의로 설정하려면, 해당 파일을 클릭하여 메뉴를 열고, '열람주의로 설정'을 클릭합니다." @@ -1816,7 +1816,7 @@ _achievements: description: "드라이브 폴더에 스스로를 넣게 했다" _reactWithoutRead: title: "읽고 답하긴 하시는 건가요?" - description: "100자가 넘는 노트를 작성한 지 3초 안에 리액션했다" + description: "100자가 넘는 노트를 게시한 지 3초 안에 리액션했다" _clickedClickHere: title: "여길 눌러보세요" description: "여기를 눌렀다" @@ -1845,7 +1845,7 @@ _achievements: _cookieClicked: title: "쿠키를 클릭하는 게임" description: "쿠키를 클릭했다" - flavor: "소프트웨어 착각하지 않았어?" + flavor: "소프트웨어 착각하지 않으셨나요?" _brainDiver: title: "Brain Diver" description: "Brain Diver로의 링크를 첨부했다" @@ -2368,6 +2368,7 @@ _widgets: chooseList: "리스트 선택" clicker: "클리커" birthdayFollowings: "오늘이 생일인 유저" + chat: "채팅" _cw: hide: "숨기기" show: "더 보기" @@ -2634,6 +2635,7 @@ _deck: mentions: "받은 멘션" direct: "다이렉트" roleTimeline: "역할 타임라인" + chat: "채팅" _dialog: charactersExceeded: "최대 글자수를 초과하였습니다! 현재 {current} / 최대 {max}" charactersBelow: "최소 글자수 미만입니다! 현재 {current} / 최소 {min}" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index b313c4ce1f..4b78b0a362 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -979,6 +979,7 @@ document: "文档" numberOfPageCache: "缓存页数" numberOfPageCacheDescription: "设置较高的值会更方便用户,但设备的负载和内存使用量会增加。" logoutConfirm: "是否确认登出?" +logoutWillClearClientData: "登出时将会从浏览器中删除客户端的设置信息。如果想要在再次登入时恢复设置信息,请在设置里打开自动备份。" lastActiveDate: "最后活跃时间" statusbar: "状态栏" pleaseSelect: "请选择" @@ -2367,6 +2368,7 @@ _widgets: chooseList: "选择列表" clicker: "点击器" birthdayFollowings: "今天是他们的生日" + chat: "聊天" _cw: hide: "隐藏" show: "查看更多" @@ -2633,6 +2635,7 @@ _deck: mentions: "提及" direct: "指定用户" roleTimeline: "角色时间线" + chat: "聊天" _dialog: charactersExceeded: "已经超过了最大字符数! 当前字符数 {current} / 限制字符数 {max}" charactersBelow: "低于最小字符数!当前字符数 {current} / 限制字符数 {min}" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 57b7e84b8a..cfe3b729f0 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -424,6 +424,7 @@ antennaExcludeBots: "排除機器人帳戶" antennaKeywordsDescription: "空格代表「以及」(AND),換行代表「或者」(OR)" notifyAntenna: "通知有新貼文" withFileAntenna: "僅帶有附件的貼文" +excludeNotesInSensitiveChannel: "排除敏感頻道的貼文" enableServiceworker: "啟用瀏覽器的推播通知" antennaUsersDescription: "填寫使用者名稱,以換行分隔" caseSensitive: "區分大小寫" @@ -978,6 +979,7 @@ document: "文件" numberOfPageCache: "快取頁面數" numberOfPageCacheDescription: "增加數量會提高便利性,但也會增加負荷與記憶體使用量。" logoutConfirm: "確定要登出嗎?" +logoutWillClearClientData: "當您登出時,客戶端的設定資訊將從瀏覽器中清除。為了能夠在重新登入時恢復您的設定資訊,請啟用設定內的自動備份選項。" lastActiveDate: "上次使用日期及時間" statusbar: "狀態列" pleaseSelect: "請選擇" @@ -1307,7 +1309,7 @@ availableRoles: "可用角色" acknowledgeNotesAndEnable: "了解注意事項後再開啟。" federationSpecified: "此伺服器以白名單聯邦的方式運作。除了管理員指定的伺服器外,它無法與其他伺服器互動。" federationDisabled: "此伺服器未開啟站台聯邦。無法與其他伺服器上的使用者互動。" -confirmOnReact: "反應時確認" +confirmOnReact: "在做出反應前先確認" reactAreYouSure: "用「 {emoji} 」反應嗎?" markAsSensitiveConfirm: "要將這個媒體設定為敏感嗎?" unmarkAsSensitiveConfirm: "要解除這個媒體的敏感設定嗎?" @@ -1444,7 +1446,7 @@ _accountSettings: makeNotesHiddenBefore: "隱藏過去的貼文" makeNotesHiddenBeforeDescription: "啟用此功能後,超過設定的日期和時間或超過設定時間的貼文將僅對自己顯示(私密化)。 如果您再次停用它,貼文的公開狀態也會恢復原狀。" mayNotEffectForFederatedNotes: "聯邦發送至遠端伺服器的貼文可能會不受影響。" - mayNotEffectSomeSituations: "這些限制已經簡化。它們可能不適用於某些情況,例如在遠端伺服器上檢視或管理時。" + mayNotEffectSomeSituations: "這些限制僅是簡化版本。在某些情況下,例如在遠端伺服器上瀏覽或進行審核時,可能不會套用這些限制。" notesHavePassedSpecifiedPeriod: "早於指定時間的貼文" notesOlderThanSpecifiedDateAndTime: "指定時間和日期之前的貼文" _abuseUserReport: @@ -2366,6 +2368,7 @@ _widgets: chooseList: "選擇清單" clicker: "點擊器" birthdayFollowings: "今天生日的使用者" + chat: "聊天" _cw: hide: "隱藏" show: "顯示內容" @@ -2632,6 +2635,7 @@ _deck: mentions: "提及" direct: "指定使用者" roleTimeline: "角色時間軸" + chat: "聊天" _dialog: charactersExceeded: "您的貼文太長了!現時字數 {current}/限制字數 {max}" charactersBelow: "您的貼文太短了!現時字數 {current}/限制字數 {min}" diff --git a/package.json b/package.json index a5ae3374c5..76ada83b4c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sharkey", - "version": "2025.4.0", + "version": "2025.4.1-alpha.2", "codename": "shonk", "repository": { "type": "git", diff --git a/packages/backend/migration/1745378064470-composite-note-index.js b/packages/backend/migration/1745378064470-composite-note-index.js new file mode 100644 index 0000000000..49e835d38c --- /dev/null +++ b/packages/backend/migration/1745378064470-composite-note-index.js @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class CompositeNoteIndex1745378064470 { + name = 'CompositeNoteIndex1745378064470'; + + async up(queryRunner) { + await queryRunner.query(`CREATE INDEX "IDX_724b311e6f883751f261ebe378" ON "note" ("userId", "id" DESC)`); + await queryRunner.query(`DROP INDEX IF EXISTS "IDX_5b87d9d19127bd5d92026017a7"`); + // Flush all cached Linear Scan Plans and redo statistics for composite index + // this is important for Postgres to learn that even in highly complex queries, using this index first can reduce the result set significantly + await queryRunner.query(`ANALYZE "user", "note"`); + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX IF EXISTS "IDX_724b311e6f883751f261ebe378"`); + await queryRunner.query(`CREATE INDEX "IDX_5b87d9d19127bd5d92026017a7" ON "note" ("userId")`); + } +} diff --git a/packages/backend/package.json b/packages/backend/package.json index e67029ef7f..64a64650c6 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -37,17 +37,17 @@ }, "optionalDependencies": { "@swc/core-android-arm64": "1.3.11", - "@swc/core-darwin-arm64": "1.11.11", - "@swc/core-darwin-x64": "1.11.11", + "@swc/core-darwin-arm64": "1.11.18", + "@swc/core-darwin-x64": "1.11.18", "@swc/core-freebsd-x64": "1.3.11", - "@swc/core-linux-arm-gnueabihf": "1.11.11", - "@swc/core-linux-arm64-gnu": "1.11.11", - "@swc/core-linux-arm64-musl": "1.11.11", - "@swc/core-linux-x64-gnu": "1.11.11", - "@swc/core-linux-x64-musl": "1.11.11", - "@swc/core-win32-arm64-msvc": "1.11.11", - "@swc/core-win32-ia32-msvc": "1.11.11", - "@swc/core-win32-x64-msvc": "1.11.11", + "@swc/core-linux-arm-gnueabihf": "1.11.18", + "@swc/core-linux-arm64-gnu": "1.11.18", + "@swc/core-linux-arm64-musl": "1.11.18", + "@swc/core-linux-x64-gnu": "1.11.18", + "@swc/core-linux-x64-musl": "1.11.18", + "@swc/core-win32-arm64-msvc": "1.11.18", + "@swc/core-win32-ia32-msvc": "1.11.18", + "@swc/core-win32-x64-msvc": "1.11.18", "bufferutil": "4.0.9", "slacc-android-arm-eabi": "0.0.10", "slacc-android-arm64": "0.0.10", @@ -65,8 +65,8 @@ "utf-8-validate": "6.0.5" }, "dependencies": { - "@aws-sdk/client-s3": "3.772.0", - "@aws-sdk/lib-storage": "3.772.0", + "@aws-sdk/client-s3": "3.782.0", + "@aws-sdk/lib-storage": "3.782.0", "@discordapp/twemoji": "15.1.0", "@fastify/accepts": "5.0.2", "@fastify/cookie": "11.0.2", @@ -76,11 +76,11 @@ "@fastify/multipart": "9.0.3", "@fastify/static": "8.1.1", "@fastify/view": "10.0.2", - "@misskey-dev/sharp-read-bmp": "1.2.0", + "@misskey-dev/sharp-read-bmp": "1.3.0", "@misskey-dev/summaly": "5.2.0", - "@nestjs/common": "11.0.12", - "@nestjs/core": "11.0.12", - "@nestjs/testing": "11.0.12", + "@nestjs/common": "11.0.16", + "@nestjs/core": "11.0.15", + "@nestjs/testing": "11.0.15", "@peertube/http-signature": "1.7.0", "@sentry/node": "8.55.0", "@sentry/profiling-node": "8.55.0", @@ -88,9 +88,10 @@ "@sinonjs/fake-timers": "11.3.1", "@smithy/node-http-handler": "2.5.0", "@swc/cli": "0.6.0", - "@swc/core": "1.11.11", + "@swc/core": "1.11.18", "@transfem-org/sfm-js": "0.24.6", "@twemoji/parser": "15.1.1", + "@types/redis-info": "3.0.3", "@types/psl": "^1.1.3", "accepts": "1.3.8", "ajv": "8.17.1", @@ -100,7 +101,7 @@ "bcryptjs": "2.4.3", "blurhash": "2.0.5", "body-parser": "1.20.3", - "bullmq": "5.44.1", + "bullmq": "5.48.1", "cacheable-lookup": "7.0.0", "canvas": "^3.1.0", "cbor": "9.0.2", @@ -113,7 +114,7 @@ "date-fns": "2.30.0", "deep-email-validator": "0.1.21", "fast-xml-parser": "4.4.1", - "fastify": "5.2.1", + "fastify": "5.3.2", "fastify-multer": "^2.0.3", "fastify-raw-body": "5.0.0", "feed": "4.2.2", @@ -121,7 +122,7 @@ "fluent-ffmpeg": "2.1.3", "form-data": "4.0.2", "glob": "11.0.0", - "got": "14.4.6", + "got": "14.4.7", "happy-dom": "16.8.1", "hpagent": "1.2.0", "htmlescape": "1.1.1", @@ -152,7 +153,7 @@ "oauth2orize": "1.12.0", "oauth2orize-pkce": "0.1.2", "os-utils": "0.0.14", - "otpauth": "9.3.6", + "otpauth": "9.4.0", "parse5": "7.2.1", "pg": "8.14.1", "pkce-challenge": "4.1.0", @@ -165,6 +166,7 @@ "random-seed": "0.3.0", "ratelimiter": "3.4.1", "re2": "1.21.4", + "redis-info": "3.1.0", "redis-lock": "0.1.4", "reflect-metadata": "0.2.2", "rename": "1.0.4", @@ -172,17 +174,17 @@ "rxjs": "7.8.2", "sanitize-html": "2.15.0", "secure-json-parse": "3.0.2", - "sharp": "0.33.5", + "sharp": "0.34.1", "slacc": "0.0.10", "strict-event-emitter-types": "2.0.0", "stringz": "2.1.0", "systeminformation": "5.25.11", "tinycolor2": "1.6.0", "tmp": "0.2.3", - "tsc-alias": "1.8.11", + "tsc-alias": "1.8.15", "tsconfig-paths": "4.2.0", - "typeorm": "0.3.21", - "typescript": "5.8.2", + "typeorm": "0.3.22", + "typescript": "5.8.3", "ulid": "2.4.0", "uuid": "^9.0.1", "vary": "1.1.2", @@ -193,7 +195,7 @@ "devDependencies": { "@jest/globals": "29.7.0", "@nestjs/platform-express": "10.4.15", - "@sentry/vue": "9.8.0", + "@sentry/vue": "9.12.0", "@simplewebauthn/types": "12.0.0", "@swc/jest": "0.2.37", "@types/accepts": "1.3.7", @@ -212,7 +214,7 @@ "@types/jsrsasign": "10.5.15", "@types/mime-types": "2.1.4", "@types/ms": "0.7.34", - "@types/node": "22.13.10", + "@types/node": "22.14.0", "@types/nodemailer": "6.4.17", "@types/oauth": "0.9.6", "@types/oauth2orize": "1.11.5", @@ -224,8 +226,8 @@ "@types/random-seed": "0.3.5", "@types/ratelimiter": "3.4.6", "@types/rename": "1.0.7", - "@types/sanitize-html": "2.13.0", - "@types/semver": "7.5.8", + "@types/sanitize-html": "2.15.0", + "@types/semver": "7.7.0", "@types/simple-oauth2": "5.0.7", "@types/sinonjs__fake-timers": "8.1.5", "@types/tinycolor2": "1.4.6", @@ -233,9 +235,9 @@ "@types/uuid": "^9.0.4", "@types/vary": "1.1.3", "@types/web-push": "3.6.4", - "@types/ws": "8.18.0", - "@typescript-eslint/eslint-plugin": "8.27.0", - "@typescript-eslint/parser": "8.27.0", + "@types/ws": "8.18.1", + "@typescript-eslint/eslint-plugin": "8.29.1", + "@typescript-eslint/parser": "8.29.1", "aws-sdk-client-mock": "4.1.0", "cross-env": "7.0.3", "eslint-plugin-import": "2.31.0", diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts index 5128caff60..7bf33e13c5 100644 --- a/packages/backend/src/core/AccountMoveService.ts +++ b/packages/backend/src/core/AccountMoveService.ts @@ -25,6 +25,7 @@ import InstanceChart from '@/core/chart/charts/instance.js'; import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js'; import { SystemAccountService } from '@/core/SystemAccountService.js'; import { RoleService } from '@/core/RoleService.js'; +import { AntennaService } from '@/core/AntennaService.js'; @Injectable() export class AccountMoveService { @@ -66,6 +67,7 @@ export class AccountMoveService { private queueService: QueueService, private systemAccountService: SystemAccountService, private roleService: RoleService, + private antennaService: AntennaService, ) { } @@ -127,6 +129,7 @@ export class AccountMoveService { this.deleteScheduledNotes(src), this.copyRoles(src, dst), this.updateLists(src, dst), + this.antennaService.onMoveAccount(src, dst), ]); } catch { /* skip if any error happens */ diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index 13e3dcdbd8..cf696e3599 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -5,18 +5,20 @@ import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; -import type { MiAntenna } from '@/models/Antenna.js'; -import type { MiNote } from '@/models/Note.js'; -import type { MiUser } from '@/models/User.js'; +import { In } from 'typeorm'; +import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; +import type { GlobalEvents } from '@/core/GlobalEventService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { UtilityService } from '@/core/UtilityService.js'; +import { bindThis } from '@/decorators.js'; +import { DI } from '@/di-symbols.js'; import * as Acct from '@/misc/acct.js'; import type { Packed } from '@/misc/json-schema.js'; -import { DI } from '@/di-symbols.js'; import type { AntennasRepository, UserListMembershipsRepository } from '@/models/_.js'; -import { UtilityService } from '@/core/UtilityService.js'; -import { bindThis } from '@/decorators.js'; -import type { GlobalEvents } from '@/core/GlobalEventService.js'; -import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; +import type { MiAntenna } from '@/models/Antenna.js'; +import type { MiNote } from '@/models/Note.js'; +import type { MiUser } from '@/models/User.js'; +import { CacheService } from './CacheService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() @@ -37,6 +39,7 @@ export class AntennaService implements OnApplicationShutdown { @Inject(DI.userListMembershipsRepository) private userListMembershipsRepository: UserListMembershipsRepository, + private cacheService: CacheService, private utilityService: UtilityService, private globalEventService: GlobalEventService, private fanoutTimelineService: FanoutTimelineService, @@ -111,9 +114,6 @@ export class AntennaService implements OnApplicationShutdown { @bindThis public async checkHitAntenna(antenna: MiAntenna, note: (MiNote | Packed<'Note'>), noteUser: { id: MiUser['id']; username: string; host: string | null; isBot: boolean; }): Promise<boolean> { - if (note.visibility === 'specified') return false; - if (note.visibility === 'followers') return false; - if (antenna.excludeNotesInSensitiveChannel && note.channel?.isSensitive) return false; if (antenna.excludeBots && noteUser.isBot) return false; @@ -122,6 +122,18 @@ export class AntennaService implements OnApplicationShutdown { if (!antenna.withReplies && note.replyId != null) return false; + if (note.visibility === 'specified') { + if (note.userId !== antenna.userId) { + if (note.visibleUserIds == null) return false; + if (!note.visibleUserIds.includes(antenna.userId)) return false; + } + } + + if (note.visibility === 'followers') { + const isFollowing = Object.hasOwn(await this.cacheService.userFollowingsCache.fetch(antenna.userId), note.userId); + if (!isFollowing && antenna.userId !== note.userId) return false; + } + if (antenna.src === 'home') { // TODO } else if (antenna.src === 'list') { @@ -213,6 +225,41 @@ export class AntennaService implements OnApplicationShutdown { } @bindThis + public async onMoveAccount(src: MiUser, dst: MiUser): Promise<void> { + // There is a possibility for users to add the srcUser to their antennas, but it's low, so we don't check it. + + // Get MiAntenna[] from cache and filter to select antennas with the src user is in the users list + const srcUserAcct = this.utilityService.getFullApAccount(src.username, src.host).toLowerCase(); + const antennasToMigrate = (await this.getAntennas()).filter(antenna => { + return antenna.users.some(user => { + const { username, host } = Acct.parse(user); + return this.utilityService.getFullApAccount(username, host).toLowerCase() === srcUserAcct; + }); + }); + + if (antennasToMigrate.length === 0) return; + + const antennaIds = antennasToMigrate.map(x => x.id); + + // Update the antennas by appending dst users acct to the users list + const dstUserAcct = '@' + Acct.toString({ username: dst.username, host: dst.host }); + + await this.antennasRepository.createQueryBuilder('antenna') + .update() + .set({ + users: () => 'array_append(antenna.users, :dstUserAcct)', + }) + .where('antenna.id IN (:...antennaIds)', { antennaIds }) + .setParameters({ dstUserAcct }) + .execute(); + + // announce update to event + for (const newAntenna of await this.antennasRepository.findBy({ id: In(antennaIds) })) { + this.globalEventService.publishInternalEvent('antennaUpdated', newAntenna); + } + } + + @bindThis public dispose(): void { this.redisForSub.off('message', this.onRedisMessage); } diff --git a/packages/backend/src/core/ChatService.ts b/packages/backend/src/core/ChatService.ts index b0e8cfb61c..9d294a80cb 100644 --- a/packages/backend/src/core/ChatService.ts +++ b/packages/backend/src/core/ChatService.ts @@ -232,7 +232,7 @@ export class ChatService { const packedMessageForTo = await this.chatEntityService.packMessageDetailed(inserted, toUser); this.globalEventService.publishMainStream(toUser.id, 'newChatMessage', packedMessageForTo); - //this.pushNotificationService.pushNotification(toUser.id, 'newChatMessage', packedMessageForTo); + this.pushNotificationService.pushNotification(toUser.id, 'newChatMessage', packedMessageForTo); }, 3000); } @@ -302,7 +302,7 @@ export class ChatService { if (marker == null) continue; this.globalEventService.publishMainStream(membershipsOtherThanMe[i].userId, 'newChatMessage', packedMessageForTo); - //this.pushNotificationService.pushNotification(membershipsOtherThanMe[i].userId, 'newChatMessage', packedMessageForTo); + this.pushNotificationService.pushNotification(membershipsOtherThanMe[i].userId, 'newChatMessage', packedMessageForTo); } }, 3000); diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts index bd86a80cbd..84ca06ec1e 100644 --- a/packages/backend/src/core/FanoutTimelineEndpointService.ts +++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts @@ -55,7 +55,7 @@ export class FanoutTimelineEndpointService { } @bindThis - private async getMiNotes(ps: TimelineOptions): Promise<MiNote[]> { + async getMiNotes(ps: TimelineOptions): Promise<MiNote[]> { // 呼び出し元と以下の処理をシンプルにするためにdbFallbackを置き換える if (!ps.useDbFallback) ps.dbFallback = () => Promise.resolve([]); diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index 0ef52ee1a6..1ee3bd2275 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -6,7 +6,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import * as parse5 from 'parse5'; -import { Window } from 'happy-dom'; +import { type Document, type HTMLParagraphElement, Window } from 'happy-dom'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { intersperse } from '@/misc/prelude/array.js'; @@ -23,6 +23,8 @@ type ChildNode = DefaultTreeAdapterMap['childNode']; const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/; const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/; +export type Appender = (document: Document, body: HTMLParagraphElement) => void; + @Injectable() export class MfmService { constructor( @@ -343,7 +345,7 @@ export class MfmService { } @bindThis - public toHtml(nodes: mfm.MfmNode[] | null, mentionedRemoteUsers: IMentionedRemoteUsers = []) { + public toHtml(nodes: mfm.MfmNode[] | null, mentionedRemoteUsers: IMentionedRemoteUsers = [], additionalAppenders: Appender[] = []) { if (nodes == null) { return null; } @@ -576,6 +578,10 @@ export class MfmService { appendChildren(nodes, body); + for (const additionalAppender of additionalAppenders) { + additionalAppender(doc, body); + } + const serialized = body.outerHTML; happyDOM.close().catch(err => {}); diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 5543cc080d..fd6300483f 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -640,7 +640,14 @@ export class NoteCreateService implements OnApplicationShutdown { }, { jobId: `pollEnd:${note.id}`, delay, - removeOnComplete: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts index 1479bb00d9..9333c1ebc5 100644 --- a/packages/backend/src/core/PushNotificationService.ts +++ b/packages/backend/src/core/PushNotificationService.ts @@ -22,6 +22,7 @@ type PushNotificationsTypes = { note: Packed<'Note'>; }; 'readAllNotifications': undefined; + newChatMessage: Packed<'ChatMessage'>; }; // Reduce length because push message servers have character limits diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts index 039c47724b..fb0fa8f28d 100644 --- a/packages/backend/src/core/QueueService.ts +++ b/packages/backend/src/core/QueueService.ts @@ -5,6 +5,8 @@ import { randomUUID } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; +import { MetricsTime, type JobType } from 'bullmq'; +import { parse as parseRedisInfo } from 'redis-info'; import type { IActivity } from '@/core/activitypub/type.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiWebhook, WebhookEventTypes } from '@/models/Webhook.js'; @@ -40,6 +42,18 @@ import type httpSignature from '@peertube/http-signature'; import type * as Bull from 'bullmq'; import { MiNote } from '@/models/Note.js'; +export const QUEUE_TYPES = [ + 'system', + 'endedPollNotification', + 'deliver', + 'inbox', + 'db', + 'relationship', + 'objectStorage', + 'userWebhookDeliver', + 'systemWebhookDeliver', +] as const; + @Injectable() export class QueueService { constructor( @@ -60,50 +74,58 @@ export class QueueService { this.systemQueue.add('tickCharts', { }, { repeat: { pattern: '55 * * * *' }, - removeOnComplete: true, + removeOnComplete: 10, + removeOnFail: 30, }); this.systemQueue.add('resyncCharts', { }, { repeat: { pattern: '0 0 * * *' }, - removeOnComplete: true, + removeOnComplete: 10, + removeOnFail: 30, }); this.systemQueue.add('cleanCharts', { }, { repeat: { pattern: '0 0 * * *' }, - removeOnComplete: true, + removeOnComplete: 10, + removeOnFail: 30, }); this.systemQueue.add('aggregateRetention', { }, { repeat: { pattern: '0 0 * * *' }, - removeOnComplete: true, + removeOnComplete: 10, + removeOnFail: 30, }); this.systemQueue.add('clean', { }, { repeat: { pattern: '0 0 * * *' }, - removeOnComplete: true, + removeOnComplete: 10, + removeOnFail: 30, }); this.systemQueue.add('checkExpiredMutings', { }, { repeat: { pattern: '*/5 * * * *' }, - removeOnComplete: true, + removeOnComplete: 10, + removeOnFail: 30, }); this.systemQueue.add('bakeBufferedReactions', { }, { repeat: { pattern: '0 0 * * *' }, - removeOnComplete: true, + removeOnComplete: 10, + removeOnFail: 30, }); this.systemQueue.add('checkModeratorsActivity', { }, { // 毎時30分に起動 repeat: { pattern: '30 * * * *' }, - removeOnComplete: true, + removeOnComplete: 10, + removeOnFail: 30, }); } @@ -125,13 +147,21 @@ export class QueueService { isSharedInbox, }; - return this.deliverQueue.add(to, data, { + const label = to.replace('https://', '').replace('/inbox', ''); + + return this.deliverQueue.add(label, data, { attempts: this.config.deliverJobMaxAttempts ?? 12, backoff: { type: 'custom', }, - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -153,12 +183,18 @@ export class QueueService { backoff: { type: 'custom', }, - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }; await this.deliverQueue.addBulk(Array.from(inboxes.entries(), d => ({ - name: d[0], + name: d[0].replace('https://', '').replace('/inbox', ''), data: { user, content: contentBody, @@ -179,13 +215,21 @@ export class QueueService { signature, }; - return this.inboxQueue.add('', data, { + const label = (activity.id ?? '').replace('https://', '').replace('/activity', ''); + + return this.inboxQueue.add(label, data, { attempts: this.config.inboxJobMaxAttempts ?? 8, backoff: { type: 'custom', }, - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -194,8 +238,14 @@ export class QueueService { return this.dbQueue.add('deleteDriveFiles', { user: { id: user.id }, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -204,8 +254,14 @@ export class QueueService { return this.dbQueue.add('exportCustomEmojis', { user: { id: user.id }, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -224,8 +280,14 @@ export class QueueService { return this.dbQueue.add('exportNotes', { user: { id: user.id }, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -234,8 +296,14 @@ export class QueueService { return this.dbQueue.add('exportClips', { user: { id: user.id }, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -244,8 +312,14 @@ export class QueueService { return this.dbQueue.add('exportFavorites', { user: { id: user.id }, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -256,8 +330,14 @@ export class QueueService { excludeMuting, excludeInactive, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -266,8 +346,14 @@ export class QueueService { return this.dbQueue.add('exportMuting', { user: { id: user.id }, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -276,8 +362,14 @@ export class QueueService { return this.dbQueue.add('exportBlocking', { user: { id: user.id }, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -286,8 +378,14 @@ export class QueueService { return this.dbQueue.add('exportUserLists', { user: { id: user.id }, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -296,8 +394,14 @@ export class QueueService { return this.dbQueue.add('exportAntennas', { user: { id: user.id }, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -308,8 +412,14 @@ export class QueueService { fileId: fileId, withReplies, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -373,8 +483,14 @@ export class QueueService { user: { id: user.id }, fileId: fileId, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -384,8 +500,14 @@ export class QueueService { user: { id: user.id }, fileId: fileId, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -405,8 +527,14 @@ export class QueueService { name, data, opts: { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }, }; } @@ -417,8 +545,14 @@ export class QueueService { user: { id: user.id }, fileId: fileId, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -428,8 +562,14 @@ export class QueueService { user: { id: user.id }, fileId: fileId, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -439,8 +579,14 @@ export class QueueService { user: { id: user.id }, antenna, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -450,8 +596,14 @@ export class QueueService { user: { id: user.id }, soft: opts.soft, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -501,8 +653,14 @@ export class QueueService { withReplies: data.withReplies, }, opts: { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, ...opts, }, }; @@ -513,16 +671,28 @@ export class QueueService { return this.objectStorageQueue.add('deleteFile', { key: key, }, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @bindThis public createCleanRemoteFilesJob() { return this.objectStorageQueue.add('cleanRemoteFiles', {}, { - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -553,8 +723,14 @@ export class QueueService { backoff: { type: 'custom', }, - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @@ -584,21 +760,201 @@ export class QueueService { backoff: { type: 'custom', }, - removeOnComplete: true, - removeOnFail: true, + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 30, + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + count: 100, + }, }); } @bindThis - public destroy() { - this.deliverQueue.once('cleaned', (jobs, status) => { - //deliverLogger.succ(`Cleaned ${jobs.length} ${status} jobs`); - }); - this.deliverQueue.clean(0, 0, 'delayed'); + private getQueue(type: typeof QUEUE_TYPES[number]): Bull.Queue { + switch (type) { + case 'system': return this.systemQueue; + case 'endedPollNotification': return this.endedPollNotificationQueue; + case 'deliver': return this.deliverQueue; + case 'inbox': return this.inboxQueue; + case 'db': return this.dbQueue; + case 'relationship': return this.relationshipQueue; + case 'objectStorage': return this.objectStorageQueue; + case 'userWebhookDeliver': return this.userWebhookDeliverQueue; + case 'systemWebhookDeliver': return this.systemWebhookDeliverQueue; + default: throw new Error(`Unrecognized queue type: ${type}`); + } + } + + @bindThis + public async queueClear(queueType: typeof QUEUE_TYPES[number], state: '*' | 'completed' | 'wait' | 'active' | 'paused' | 'prioritized' | 'delayed' | 'failed') { + const queue = this.getQueue(queueType); + + if (state === '*') { + await Promise.all([ + queue.clean(0, 0, 'completed'), + queue.clean(0, 0, 'wait'), + queue.clean(0, 0, 'active'), + queue.clean(0, 0, 'paused'), + queue.clean(0, 0, 'prioritized'), + queue.clean(0, 0, 'delayed'), + queue.clean(0, 0, 'failed'), + ]); + } else { + await queue.clean(0, 0, state); + } + } - this.inboxQueue.once('cleaned', (jobs, status) => { - //inboxLogger.succ(`Cleaned ${jobs.length} ${status} jobs`); + @bindThis + public async queuePromoteJobs(queueType: typeof QUEUE_TYPES[number]) { + const queue = this.getQueue(queueType); + await queue.promoteJobs(); + } + + @bindThis + public async queueRetryJob(queueType: typeof QUEUE_TYPES[number], jobId: string) { + const queue = this.getQueue(queueType); + const job: Bull.Job | null = await queue.getJob(jobId); + if (job) { + if (job.finishedOn != null) { + await job.retry(); + } else { + await job.promote(); + } + } + } + + @bindThis + public async queueRemoveJob(queueType: typeof QUEUE_TYPES[number], jobId: string) { + const queue = this.getQueue(queueType); + const job: Bull.Job | null = await queue.getJob(jobId); + if (job) { + await job.remove(); + } + } + + @bindThis + private packJobData(job: Bull.Job) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const stacktrace = job.stacktrace ? job.stacktrace.filter(Boolean) : []; + stacktrace.reverse(); + + return { + id: job.id, + name: job.name, + data: job.data, + opts: job.opts, + timestamp: job.timestamp, + processedOn: job.processedOn, + processedBy: job.processedBy, + finishedOn: job.finishedOn, + progress: job.progress, + attempts: job.attemptsMade, + delay: job.delay, + failedReason: job.failedReason, + stacktrace: stacktrace, + returnValue: job.returnvalue, + isFailed: !!job.failedReason || (Array.isArray(stacktrace) && stacktrace.length > 0), + }; + } + + @bindThis + public async queueGetJob(queueType: typeof QUEUE_TYPES[number], jobId: string) { + const queue = this.getQueue(queueType); + const job: Bull.Job | null = await queue.getJob(jobId); + if (job) { + return this.packJobData(job); + } else { + throw new Error(`Job not found: ${jobId}`); + } + } + + @bindThis + public async queueGetJobs(queueType: typeof QUEUE_TYPES[number], jobTypes: JobType[], search?: string) { + const RETURN_LIMIT = 100; + const queue = this.getQueue(queueType); + let jobs: Bull.Job[]; + + if (search) { + jobs = await queue.getJobs(jobTypes, 0, 1000); + + jobs = jobs.filter(job => { + const jobString = JSON.stringify(job).toLowerCase(); + return search.toLowerCase().split(' ').every(term => { + return jobString.includes(term); + }); + }); + + jobs = jobs.slice(0, RETURN_LIMIT); + } else { + jobs = await queue.getJobs(jobTypes, 0, RETURN_LIMIT); + } + + return jobs.map(job => this.packJobData(job)); + } + + @bindThis + public async queueGetQueues() { + const fetchings = QUEUE_TYPES.map(async type => { + const queue = this.getQueue(type); + + const counts = await queue.getJobCounts(); + const isPaused = await queue.isPaused(); + const metrics_completed = await queue.getMetrics('completed', 0, MetricsTime.ONE_WEEK); + const metrics_failed = await queue.getMetrics('failed', 0, MetricsTime.ONE_WEEK); + + return { + name: type, + counts: counts, + isPaused, + metrics: { + completed: metrics_completed, + failed: metrics_failed, + }, + }; }); - this.inboxQueue.clean(0, 0, 'delayed'); + + return await Promise.all(fetchings); + } + + @bindThis + public async queueGetQueue(queueType: typeof QUEUE_TYPES[number]) { + const queue = this.getQueue(queueType); + const counts = await queue.getJobCounts(); + const isPaused = await queue.isPaused(); + const metrics_completed = await queue.getMetrics('completed', 0, MetricsTime.ONE_WEEK); + const metrics_failed = await queue.getMetrics('failed', 0, MetricsTime.ONE_WEEK); + const db = parseRedisInfo(await (await queue.client).info()); + + return { + name: queueType, + qualifiedName: queue.qualifiedName, + counts: counts, + isPaused, + metrics: { + completed: metrics_completed, + failed: metrics_failed, + }, + db: { + version: db.redis_version, + mode: db.redis_mode, + runId: db.run_id, + processId: db.process_id, + port: parseInt(db.tcp_port), + os: db.os, + uptime: parseInt(db.uptime_in_seconds), + memory: { + total: parseInt(db.total_system_memory) || parseInt(db.maxmemory), + used: parseInt(db.used_memory), + fragmentationRatio: parseInt(db.mem_fragmentation_ratio), + peak: parseInt(db.used_memory_peak), + }, + clients: { + connected: parseInt(db.connected_clients), + blocked: parseInt(db.blocked_clients), + }, + }, + }; } } diff --git a/packages/backend/src/core/SystemAccountService.ts b/packages/backend/src/core/SystemAccountService.ts index 3f3afe6561..1288dc6ffa 100644 --- a/packages/backend/src/core/SystemAccountService.ts +++ b/packages/backend/src/core/SystemAccountService.ts @@ -5,11 +5,14 @@ import { randomUUID } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; +import type { OnApplicationShutdown } from '@nestjs/common'; import { DataSource, IsNull } from 'typeorm'; +import * as Redis from 'ioredis'; import bcrypt from 'bcryptjs'; import { MiLocalUser, MiUser } from '@/models/User.js'; import { MiSystemAccount, MiUsedUsername, MiUserKeypair, MiUserProfile, type UsersRepository, type SystemAccountsRepository } from '@/models/_.js'; import type { MiMeta, UserProfilesRepository } from '@/models/_.js'; +import type { GlobalEvents } from '@/core/GlobalEventService.js'; import { MemoryKVCache } from '@/misc/cache.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; @@ -20,10 +23,13 @@ import { genRsaKeyPair } from '@/misc/gen-key-pair.js'; export const SYSTEM_ACCOUNT_TYPES = ['actor', 'relay', 'proxy'] as const; @Injectable() -export class SystemAccountService { +export class SystemAccountService implements OnApplicationShutdown { private cache: MemoryKVCache<MiLocalUser>; constructor( + @Inject(DI.redisForSub) + private redisForSub: Redis.Redis, + @Inject(DI.db) private db: DataSource, @@ -42,6 +48,31 @@ export class SystemAccountService { private idService: IdService, ) { this.cache = new MemoryKVCache<MiLocalUser>(1000 * 60 * 10); // 10m + + this.redisForSub.on('message', this.onMessage); + } + + @bindThis + private async onMessage(_: string, data: string): Promise<void> { + const obj = JSON.parse(data); + + if (obj.channel === 'internal') { + const { type, body } = obj.message as GlobalEvents['internal']['payload']; + switch (type) { + case 'metaUpdated': { + if (body.before != null && body.before.name !== body.after.name) { + for (const account of SYSTEM_ACCOUNT_TYPES) { + await this.updateCorrespondingUserProfile(account, { + name: body.after.name, + }); + } + } + break; + } + default: + break; + } + } } @bindThis @@ -151,7 +182,7 @@ export class SystemAccountService { @bindThis public async updateCorrespondingUserProfile(type: typeof SYSTEM_ACCOUNT_TYPES[number], extra: { - name?: string; + name?: string | null; description?: MiUserProfile['description']; }): Promise<MiLocalUser> { const user = await this.fetch(type); @@ -187,4 +218,15 @@ export class SystemAccountService { public async getProxyActor() { return await this.fetch('proxy'); } + + @bindThis + public dispose(): void { + this.redisForSub.off('message', this.onMessage); + this.cache.dispose(); + } + + @bindThis + public onApplicationShutdown(signal?: string): void { + this.dispose(); + } } diff --git a/packages/backend/src/core/WebhookTestService.ts b/packages/backend/src/core/WebhookTestService.ts index 469b396fb0..2f8cfea7f7 100644 --- a/packages/backend/src/core/WebhookTestService.ts +++ b/packages/backend/src/core/WebhookTestService.ts @@ -431,8 +431,8 @@ export class WebhookTestService { name: user.name, username: user.username, host: user.host, - avatarUrl: user.avatarUrl, - avatarBlurhash: user.avatarBlurhash, + avatarUrl: user.avatarId == null ? null : user.avatarUrl, + avatarBlurhash: user.avatarId == null ? null : user.avatarBlurhash, avatarDecorations: user.avatarDecorations.map(it => ({ id: it.id, angle: it.angle, @@ -464,10 +464,10 @@ export class WebhookTestService { createdAt: new Date().toISOString(), updatedAt: user.updatedAt?.toISOString() ?? null, lastFetchedAt: user.lastFetchedAt?.toISOString() ?? null, - bannerUrl: user.bannerUrl, - bannerBlurhash: user.bannerBlurhash, - backgroundUrl: user.backgroundUrl, - backgroundBlurhash: user.backgroundBlurhash, + bannerUrl: user.bannerId == null ? null : user.bannerUrl, + bannerBlurhash: user.bannerId == null ? null : user.bannerBlurhash, + backgroundUrl: user.backgroundId == null ? null : user.backgroundUrl, + backgroundBlurhash: user.backgroundId == null ? null : user.backgroundBlurhash, listenbrainz: null, isLocked: user.isLocked, isSilenced: false, diff --git a/packages/backend/src/core/activitypub/ApMfmService.ts b/packages/backend/src/core/activitypub/ApMfmService.ts index 318710fa93..c4a948429a 100644 --- a/packages/backend/src/core/activitypub/ApMfmService.ts +++ b/packages/backend/src/core/activitypub/ApMfmService.ts @@ -5,7 +5,7 @@ import { Injectable } from '@nestjs/common'; import * as mfm from '@transfem-org/sfm-js'; -import { MfmService } from '@/core/MfmService.js'; +import { MfmService, Appender } from '@/core/MfmService.js'; import type { MiNote } from '@/models/Note.js'; import { bindThis } from '@/decorators.js'; import { extractApHashtagObjects } from './models/tag.js'; @@ -25,17 +25,17 @@ export class ApMfmService { } @bindThis - public getNoteHtml(note: Pick<MiNote, 'text' | 'mentionedRemoteUsers'>, apAppend?: string) { + public getNoteHtml(note: Pick<MiNote, 'text' | 'mentionedRemoteUsers'>, additionalAppender: Appender[] = []) { let noMisskeyContent = false; - const srcMfm = (note.text ?? '') + (apAppend ?? ''); + const srcMfm = (note.text ?? ''); const parsed = mfm.parse(srcMfm); - if (!apAppend && parsed?.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) { + if (!additionalAppender.length && parsed.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) { noMisskeyContent = true; } - const content = this.mfmService.toHtml(parsed, note.mentionedRemoteUsers ? JSON.parse(note.mentionedRemoteUsers) : []); + const content = this.mfmService.toHtml(parsed, note.mentionedRemoteUsers ? JSON.parse(note.mentionedRemoteUsers) : [], additionalAppender); return { content, diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index e6eb777f1b..8251bc3b15 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -20,7 +20,7 @@ import type { MiEmoji } from '@/models/Emoji.js'; import type { MiPoll } from '@/models/Poll.js'; import type { MiPollVote } from '@/models/PollVote.js'; import { UserKeypairService } from '@/core/UserKeypairService.js'; -import { MfmService } from '@/core/MfmService.js'; +import { MfmService, type Appender } from '@/core/MfmService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import type { MiUserKeypair } from '@/models/UserKeypair.js'; @@ -468,10 +468,24 @@ export class ApRendererService { poll = await this.pollsRepository.findOneBy({ noteId: note.id }); } - let apAppend = ''; + const apAppend: Appender[] = []; if (quote) { - apAppend += `\n\nRE: ${quote}`; + // Append quote link as `<br><br><span class="quote-inline">RE: <a href="...">...</a></span>` + // the claas name `quote-inline` is used in non-misskey clients for styling quote notes. + // For compatibility, the span part should be kept as possible. + apAppend.push((doc, body) => { + body.appendChild(doc.createElement('br')); + body.appendChild(doc.createElement('br')); + const span = doc.createElement('span'); + span.className = 'quote-inline'; + span.appendChild(doc.createTextNode('RE: ')); + const link = doc.createElement('a'); + link.setAttribute('href', quote); + link.textContent = quote; + span.appendChild(link); + body.appendChild(span); + }); } let summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw; diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index d11042d73b..16425bb6ce 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -584,8 +584,8 @@ export class UserEntityService implements OnModuleInit { name: user.name, username: user.username, host: user.host, - avatarUrl: user.avatarUrl ?? this.getIdenticonUrl(user), - avatarBlurhash: user.avatarBlurhash, + avatarUrl: (user.avatarId == null ? null : user.avatarUrl) ?? this.getIdenticonUrl(user), + avatarBlurhash: (user.avatarId == null ? null : user.avatarBlurhash), description: mastoapi ? mastoapi.description : profile ? profile.description : '', createdAt: this.idService.parse(user.id).date.toISOString(), avatarDecorations: user.avatarDecorations.length > 0 ? this.avatarDecorationService.getAll().then(decorations => user.avatarDecorations.filter(ud => decorations.some(d => d.id === ud.id)).map(ud => ({ @@ -643,10 +643,10 @@ export class UserEntityService implements OnModuleInit { : null, updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null, lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null, - bannerUrl: user.bannerUrl, - bannerBlurhash: user.bannerBlurhash, - backgroundUrl: user.backgroundUrl, - backgroundBlurhash: user.backgroundBlurhash, + bannerUrl: user.bannerId == null ? null : user.bannerUrl, + bannerBlurhash: user.bannerId == null ? null : user.bannerBlurhash, + backgroundUrl: user.backgroundUrl == null ? null : user.backgroundUrl, + backgroundBlurhash: user.backgroundBlurhash == null ? null : user.backgroundBlurhash, isLocked: user.isLocked, isSuspended: user.isSuspended, location: profile!.location, diff --git a/packages/backend/src/models/Note.ts b/packages/backend/src/models/Note.ts index 2dabb75d83..9328e9ebae 100644 --- a/packages/backend/src/models/Note.ts +++ b/packages/backend/src/models/Note.ts @@ -10,6 +10,7 @@ import { MiUser } from './User.js'; import { MiChannel } from './Channel.js'; import type { MiDriveFile } from './DriveFile.js'; +@Index(['userId', 'id']) @Entity('note') export class MiNote { @PrimaryColumn(id()) @@ -71,7 +72,6 @@ export class MiNote { }) public cw: string | null; - @Index() @Column({ ...id(), comment: 'The ID of author.', diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts index 0678746e26..760ef52d2b 100644 --- a/packages/backend/src/models/User.ts +++ b/packages/backend/src/models/User.ts @@ -132,11 +132,13 @@ export class MiUser { @JoinColumn() public background: MiDriveFile | null; + // avatarId が null になったとしてもこれが null でない可能性があるため、このフィールドを使うときは avatarId の non-null チェックをすること @Column('varchar', { length: 512, nullable: true, }) public avatarUrl: string | null; + // bannerId が null になったとしてもこれが null でない可能性があるため、このフィールドを使うときは bannerId の non-null チェックをすること @Column('varchar', { length: 512, nullable: true, }) @@ -147,11 +149,13 @@ export class MiUser { }) public backgroundUrl: string | null; + // avatarId が null になったとしてもこれが null でない可能性があるため、このフィールドを使うときは avatarId の non-null チェックをすること @Column('varchar', { length: 128, nullable: true, }) public avatarBlurhash: string | null; + // bannerId が null になったとしてもこれが null でない可能性があるため、このフィールドを使うときは bannerId の non-null チェックをすること @Column('varchar', { length: 128, nullable: true, }) diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts index 0ec619f38d..225e8ac025 100644 --- a/packages/backend/src/models/_.ts +++ b/packages/backend/src/models/_.ts @@ -3,30 +3,48 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { FindOneOptions, InsertQueryBuilder, ObjectLiteral, Repository, SelectQueryBuilder } from 'typeorm'; +import { + FindOneOptions, + InsertQueryBuilder, + ObjectLiteral, + QueryRunner, + Repository, + SelectQueryBuilder, +} from 'typeorm'; +import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions.js'; import { RelationCountLoader } from 'typeorm/query-builder/relation-count/RelationCountLoader.js'; import { RelationIdLoader } from 'typeorm/query-builder/relation-id/RelationIdLoader.js'; -import { RawSqlResultsToEntityTransformer } from 'typeorm/query-builder/transformer/RawSqlResultsToEntityTransformer.js'; -import { SkLatestNote } from '@/models/LatestNote.js'; -import { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; +import { + RawSqlResultsToEntityTransformer, +} from 'typeorm/query-builder/transformer/RawSqlResultsToEntityTransformer.js'; import { MiAbuseReportNotificationRecipient } from '@/models/AbuseReportNotificationRecipient.js'; +import { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; import { MiAccessToken } from '@/models/AccessToken.js'; import { MiAd } from '@/models/Ad.js'; import { MiAnnouncement } from '@/models/Announcement.js'; import { MiAnnouncementRead } from '@/models/AnnouncementRead.js'; import { MiAntenna } from '@/models/Antenna.js'; import { MiApp } from '@/models/App.js'; -import { MiAvatarDecoration } from '@/models/AvatarDecoration.js'; import { MiAuthSession } from '@/models/AuthSession.js'; +import { MiAvatarDecoration } from '@/models/AvatarDecoration.js'; import { MiBlocking } from '@/models/Blocking.js'; -import { MiChannelFollowing } from '@/models/ChannelFollowing.js'; +import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js'; +import { MiChannel } from '@/models/Channel.js'; import { MiChannelFavorite } from '@/models/ChannelFavorite.js'; +import { MiChannelFollowing } from '@/models/ChannelFollowing.js'; +import { MiChatApproval } from '@/models/ChatApproval.js'; +import { MiChatMessage } from '@/models/ChatMessage.js'; +import { MiChatRoom } from '@/models/ChatRoom.js'; +import { MiChatRoomInvitation } from '@/models/ChatRoomInvitation.js'; +import { MiChatRoomMembership } from '@/models/ChatRoomMembership.js'; import { MiClip } from '@/models/Clip.js'; -import { MiClipNote } from '@/models/ClipNote.js'; import { MiClipFavorite } from '@/models/ClipFavorite.js'; +import { MiClipNote } from '@/models/ClipNote.js'; import { MiDriveFile } from '@/models/DriveFile.js'; import { MiDriveFolder } from '@/models/DriveFolder.js'; import { MiEmoji } from '@/models/Emoji.js'; +import { MiFlash } from '@/models/Flash.js'; +import { MiFlashLike } from '@/models/FlashLike.js'; import { MiFollowing } from '@/models/Following.js'; import { MiFollowRequest } from '@/models/FollowRequest.js'; import { MiGalleryLike } from '@/models/GalleryLike.js'; @@ -36,10 +54,10 @@ import { MiInstance } from '@/models/Instance.js'; import { MiMeta } from '@/models/Meta.js'; import { MiModerationLog } from '@/models/ModerationLog.js'; import { MiMuting } from '@/models/Muting.js'; -import { MiRenoteMuting } from '@/models/RenoteMuting.js'; import { MiNote } from '@/models/Note.js'; import { MiNoteFavorite } from '@/models/NoteFavorite.js'; import { MiNoteReaction } from '@/models/NoteReaction.js'; +import { MiNoteSchedule } from '@/models/NoteSchedule.js'; import { MiNoteThreadMuting } from '@/models/NoteThreadMuting.js'; import { MiPage } from '@/models/Page.js'; import { MiPageLike } from '@/models/PageLike.js'; @@ -51,47 +69,43 @@ import { MiPromoRead } from '@/models/PromoRead.js'; import { MiRegistrationTicket } from '@/models/RegistrationTicket.js'; import { MiRegistryItem } from '@/models/RegistryItem.js'; import { MiRelay } from '@/models/Relay.js'; +import { MiRenoteMuting } from '@/models/RenoteMuting.js'; +import { MiRetentionAggregation } from '@/models/RetentionAggregation.js'; +import { MiReversiGame } from '@/models/ReversiGame.js'; +import { MiRole } from '@/models/Role.js'; +import { MiRoleAssignment } from '@/models/RoleAssignment.js'; import { MiSignin } from '@/models/Signin.js'; import { MiSwSubscription } from '@/models/SwSubscription.js'; import { MiSystemAccount } from '@/models/SystemAccount.js'; +import { MiSystemWebhook } from '@/models/SystemWebhook.js'; import { MiUsedUsername } from '@/models/UsedUsername.js'; import { MiUser } from '@/models/User.js'; import { MiUserIp } from '@/models/UserIp.js'; import { MiUserKeypair } from '@/models/UserKeypair.js'; import { MiUserList } from '@/models/UserList.js'; +import { MiUserListFavorite } from '@/models/UserListFavorite.js'; import { MiUserListMembership } from '@/models/UserListMembership.js'; +import { MiUserMemo } from '@/models/UserMemo.js'; import { MiUserNotePining } from '@/models/UserNotePining.js'; import { MiUserPending } from '@/models/UserPending.js'; import { MiUserProfile } from '@/models/UserProfile.js'; import { MiUserPublickey } from '@/models/UserPublickey.js'; import { MiUserSecurityKey } from '@/models/UserSecurityKey.js'; -import { MiUserMemo } from '@/models/UserMemo.js'; import { MiWebhook } from '@/models/Webhook.js'; -import { MiSystemWebhook } from '@/models/SystemWebhook.js'; -import { MiChannel } from '@/models/Channel.js'; -import { MiRetentionAggregation } from '@/models/RetentionAggregation.js'; -import { MiRole } from '@/models/Role.js'; -import { MiRoleAssignment } from '@/models/RoleAssignment.js'; -import { MiFlash } from '@/models/Flash.js'; -import { MiFlashLike } from '@/models/FlashLike.js'; -import { MiUserListFavorite } from '@/models/UserListFavorite.js'; +import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js'; import { NoteEdit } from '@/models/NoteEdit.js'; -import { MiChatMessage } from '@/models/ChatMessage.js'; -import { MiChatRoom } from '@/models/ChatRoom.js'; -import { MiChatRoomMembership } from '@/models/ChatRoomMembership.js'; -import { MiChatRoomInvitation } from '@/models/ChatRoomInvitation.js'; -import { MiChatApproval } from '@/models/ChatApproval.js'; -import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js'; -import { MiReversiGame } from '@/models/ReversiGame.js'; -import { MiNoteSchedule } from '@/models/NoteSchedule.js'; import { SkApInboxLog } from '@/models/SkApInboxLog.js'; import { SkApFetchLog } from '@/models/SkApFetchLog.js'; import { SkApContext } from '@/models/SkApContext.js'; -import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js'; +import { SkLatestNote } from '@/models/LatestNote.js'; export interface MiRepository<T extends ObjectLiteral> { createTableColumnNames(this: Repository<T> & MiRepository<T>): string[]; + insertOne(this: Repository<T> & MiRepository<T>, entity: QueryDeepPartialEntity<T>, findOptions?: Pick<FindOneOptions<T>, 'relations'>): Promise<T>; + + insertOneImpl(this: Repository<T> & MiRepository<T>, entity: QueryDeepPartialEntity<T>, findOptions?: Pick<FindOneOptions<T>, 'relations'>, queryRunner?: QueryRunner): Promise<T>; + selectAliasColumnNames(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>, builder: SelectQueryBuilder<T>): void; } @@ -100,6 +114,21 @@ export const miRepository = { return this.metadata.columns.filter(column => column.isSelect && !column.isVirtual).map(column => column.databaseName); }, async insertOne(entity, findOptions?) { + const opt = this.manager.connection.options as PostgresConnectionOptions; + if (opt.replication) { + const queryRunner = this.manager.connection.createQueryRunner('master'); + try { + return this.insertOneImpl(entity, findOptions, queryRunner); + } finally { + await queryRunner.release(); + } + } else { + return this.insertOneImpl(entity, findOptions); + } + }, + async insertOneImpl(entity, findOptions?, queryRunner?) { + // ---- insert + returningの結果を共通テーブル式(CTE)に保持するクエリを生成 ---- + const queryBuilder = this.createQueryBuilder().insert().values(entity); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const mainAlias = queryBuilder.expressionMap.mainAlias!; @@ -107,7 +136,9 @@ export const miRepository = { mainAlias.name = 't'; const columnNames = this.createTableColumnNames(); queryBuilder.returning(columnNames.reduce((a, c) => `${a}, ${queryBuilder.escape(c)}`, '').slice(2)); - const builder = this.createQueryBuilder().addCommonTableExpression(queryBuilder, 'cte', { columnNames }); + + // ---- 共通テーブル式(CTE)から結果を取得 ---- + const builder = this.createQueryBuilder(undefined, queryRunner).addCommonTableExpression(queryBuilder, 'cte', { columnNames }); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion builder.expressionMap.mainAlias!.tablePath = 'cte'; this.selectAliasColumnNames(queryBuilder, builder); @@ -216,7 +247,9 @@ export { }; export type AbuseUserReportsRepository = Repository<MiAbuseUserReport> & MiRepository<MiAbuseUserReport>; -export type AbuseReportNotificationRecipientRepository = Repository<MiAbuseReportNotificationRecipient> & MiRepository<MiAbuseReportNotificationRecipient>; +export type AbuseReportNotificationRecipientRepository = + Repository<MiAbuseReportNotificationRecipient> + & MiRepository<MiAbuseReportNotificationRecipient>; export type AccessTokensRepository = Repository<MiAccessToken> & MiRepository<MiAccessToken>; export type AdsRepository = Repository<MiAd> & MiRepository<MiAd>; export type AnnouncementsRepository = Repository<MiAnnouncement> & MiRepository<MiAnnouncement>; diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts index ffc2daa62c..632fd58927 100644 --- a/packages/backend/src/postgres.ts +++ b/packages/backend/src/postgres.ts @@ -5,7 +5,7 @@ // https://github.com/typeorm/typeorm/issues/2400 import pg from 'pg'; -import { DataSource, Logger } from 'typeorm'; +import { DataSource, Logger, type QueryRunner } from 'typeorm'; import * as highlight from 'cli-highlight'; import { entities as charts } from '@/core/chart/entities.js'; import { Config } from '@/config.js'; @@ -102,6 +102,7 @@ const sqlLogger = dbLogger.createSubLogger('sql', 'gray'); export type LoggerProps = { disableQueryTruncation?: boolean; enableQueryParamLogging?: boolean; + printReplicationMode?: boolean, }; function highlightSql(sql: string) { @@ -127,8 +128,10 @@ class MyCustomLogger implements Logger { } @bindThis - private transformQueryLog(sql: string) { - let modded = sql; + private transformQueryLog(sql: string, opts?: { + prefix?: string; + }) { + let modded = opts?.prefix ? opts.prefix + sql : sql; if (!this.props.disableQueryTruncation) { modded = truncateSql(modded); } @@ -146,18 +149,27 @@ class MyCustomLogger implements Logger { } @bindThis - public logQuery(query: string, parameters?: any[]) { - sqlLogger.info(this.transformQueryLog(query), this.transformParameters(parameters)); + public logQuery(query: string, parameters?: any[], queryRunner?: QueryRunner) { + const prefix = (this.props.printReplicationMode && queryRunner) + ? `[${queryRunner.getReplicationMode()}] ` + : undefined; + sqlLogger.info(this.transformQueryLog(query, { prefix }), this.transformParameters(parameters)); } @bindThis - public logQueryError(error: string, query: string, parameters?: any[]) { - sqlLogger.error(this.transformQueryLog(query), this.transformParameters(parameters)); + public logQueryError(error: string, query: string, parameters?: any[], queryRunner?: QueryRunner) { + const prefix = (this.props.printReplicationMode && queryRunner) + ? `[${queryRunner.getReplicationMode()}] ` + : undefined; + sqlLogger.error(this.transformQueryLog(query, { prefix }), this.transformParameters(parameters)); } @bindThis - public logQuerySlow(time: number, query: string, parameters?: any[]) { - sqlLogger.warn(this.transformQueryLog(query), this.transformParameters(parameters)); + public logQuerySlow(time: number, query: string, parameters?: any[], queryRunner?: QueryRunner) { + const prefix = (this.props.printReplicationMode && queryRunner) + ? `[${queryRunner.getReplicationMode()}] ` + : undefined; + sqlLogger.warn(this.transformQueryLog(query, { prefix }), this.transformParameters(parameters)); } @bindThis @@ -306,6 +318,7 @@ export function createPostgresDataSource(config: Config) { ? new MyCustomLogger({ disableQueryTruncation: config.logging?.sql?.disableQueryTruncation, enableQueryParamLogging: config.logging?.sql?.enableQueryParamLogging, + printReplicationMode: !!config.dbReplications, }) : undefined, maxQueryExecutionTime: 300, diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index 297edfd545..90aaf67a84 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -47,7 +47,7 @@ import { CleanProcessorService } from './processors/CleanProcessorService.js'; import { AggregateRetentionProcessorService } from './processors/AggregateRetentionProcessorService.js'; import { ScheduleNotePostProcessorService } from './processors/ScheduleNotePostProcessorService.js'; import { QueueLoggerService } from './QueueLoggerService.js'; -import { QUEUE, baseQueueOptions } from './const.js'; +import { QUEUE, baseWorkerOptions } from './const.js'; import { ImportNotesProcessorService } from './processors/ImportNotesProcessorService.js'; // ref. https://github.com/misskey-dev/misskey/pull/7635#issue-971097019 @@ -186,7 +186,7 @@ export class QueueProcessorService implements OnApplicationShutdown { return processer(job); } }, { - ...baseQueueOptions(this.config, QUEUE.SYSTEM), + ...baseWorkerOptions(this.config, QUEUE.SYSTEM), autorun: false, }); @@ -251,7 +251,7 @@ export class QueueProcessorService implements OnApplicationShutdown { return processer(job); } }, { - ...baseQueueOptions(this.config, QUEUE.DB), + ...baseWorkerOptions(this.config, QUEUE.DB), autorun: false, }); @@ -283,7 +283,7 @@ export class QueueProcessorService implements OnApplicationShutdown { return this.deliverProcessorService.process(job); } }, { - ...baseQueueOptions(this.config, QUEUE.DELIVER), + ...baseWorkerOptions(this.config, QUEUE.DELIVER), autorun: false, concurrency: this.config.deliverJobConcurrency ?? 128, limiter: { @@ -323,7 +323,7 @@ export class QueueProcessorService implements OnApplicationShutdown { return this.inboxProcessorService.process(job); } }, { - ...baseQueueOptions(this.config, QUEUE.INBOX), + ...baseWorkerOptions(this.config, QUEUE.INBOX), autorun: false, concurrency: this.config.inboxJobConcurrency ?? 16, limiter: { @@ -363,7 +363,7 @@ export class QueueProcessorService implements OnApplicationShutdown { return this.userWebhookDeliverProcessorService.process(job); } }, { - ...baseQueueOptions(this.config, QUEUE.USER_WEBHOOK_DELIVER), + ...baseWorkerOptions(this.config, QUEUE.USER_WEBHOOK_DELIVER), autorun: false, concurrency: 64, limiter: { @@ -403,7 +403,7 @@ export class QueueProcessorService implements OnApplicationShutdown { return this.systemWebhookDeliverProcessorService.process(job); } }, { - ...baseQueueOptions(this.config, QUEUE.SYSTEM_WEBHOOK_DELIVER), + ...baseWorkerOptions(this.config, QUEUE.SYSTEM_WEBHOOK_DELIVER), autorun: false, concurrency: 16, limiter: { @@ -453,7 +453,7 @@ export class QueueProcessorService implements OnApplicationShutdown { return processer(job); } }, { - ...baseQueueOptions(this.config, QUEUE.RELATIONSHIP), + ...baseWorkerOptions(this.config, QUEUE.RELATIONSHIP), autorun: false, concurrency: this.config.relationshipJobConcurrency ?? 16, limiter: { @@ -498,7 +498,7 @@ export class QueueProcessorService implements OnApplicationShutdown { return processer(job); } }, { - ...baseQueueOptions(this.config, QUEUE.OBJECT_STORAGE), + ...baseWorkerOptions(this.config, QUEUE.OBJECT_STORAGE), autorun: false, concurrency: 16, }); @@ -531,7 +531,7 @@ export class QueueProcessorService implements OnApplicationShutdown { return this.endedPollNotificationProcessorService.process(job); } }, { - ...baseQueueOptions(this.config, QUEUE.ENDED_POLL_NOTIFICATION), + ...baseWorkerOptions(this.config, QUEUE.ENDED_POLL_NOTIFICATION), autorun: false, }); } diff --git a/packages/backend/src/queue/const.ts b/packages/backend/src/queue/const.ts index fdf012f149..17c6b81736 100644 --- a/packages/backend/src/queue/const.ts +++ b/packages/backend/src/queue/const.ts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { MetricsTime } from 'bullmq'; import { Config } from '@/config.js'; import type * as Bull from 'bullmq'; @@ -28,3 +29,12 @@ export function baseQueueOptions(config: Config, queueName: typeof QUEUE[keyof t prefix: config.redisForJobQueue.prefix ? `${config.redisForJobQueue.prefix}:queue:${queueName}` : `queue:${queueName}`, }; } + +export function baseWorkerOptions(config: Config, queueName: typeof QUEUE[keyof typeof QUEUE]): Bull.WorkerOptions { + return { + ...baseQueueOptions(config, queueName), + metrics: { + maxDataPoints: MetricsTime.ONE_WEEK, + }, + }; +} diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index c7aa694964..95ee78d660 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -38,6 +38,7 @@ import * as Acct from '@/misc/acct.js'; import { CacheService } from '@/core/CacheService.js'; import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions, FastifyBodyParser } from 'fastify'; import type { FindOptionsWhere } from 'typeorm'; +import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js'; const ACTIVITY_JSON = 'application/activity+json; charset=utf-8'; const LD_JSON = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"; charset=utf-8'; @@ -85,6 +86,7 @@ export class ActivityPubServerService { private queueService: QueueService, private userKeypairService: UserKeypairService, private queryService: QueryService, + private fanoutTimelineEndpointService: FanoutTimelineEndpointService, private loggerService: LoggerService, private readonly cacheService: CacheService, ) { @@ -613,16 +615,28 @@ export class ActivityPubServerService { const partOf = `${this.config.url}/users/${userId}/outbox`; if (page) { - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), sinceId, untilId) - .andWhere('note.userId = :userId', { userId: user.id }) - .andWhere(new Brackets(qb => { - qb - .where('note.visibility = \'public\'') - .orWhere('note.visibility = \'home\''); - })) - .andWhere('note.localOnly = FALSE'); - - const notes = await query.limit(limit).getMany(); + const notes = this.meta.enableFanoutTimeline ? await this.fanoutTimelineEndpointService.getMiNotes({ + sinceId: sinceId ?? null, + untilId: untilId ?? null, + limit: limit, + allowPartial: false, // Possibly true? IDK it's OK for ordered collection. + me: null, + redisTimelines: [ + `userTimeline:${user.id}`, + `userTimelineWithReplies:${user.id}`, + ], + useDbFallback: true, + ignoreAuthorFromMute: true, + excludePureRenotes: false, + noteFilter: (note) => { + if (note.visibility !== 'home' && note.visibility !== 'public') return false; + if (note.localOnly) return false; + return true; + }, + dbFallback: async (untilId, sinceId, limit) => { + return await this.getUserNotesFromDb(sinceId, untilId, limit, user.id); + }, + }) : await this.getUserNotesFromDb(sinceId ?? null, untilId ?? null, limit, user.id); if (sinceId) notes.reverse(); @@ -660,6 +674,20 @@ export class ActivityPubServerService { } @bindThis + private async getUserNotesFromDb(untilId: string | null, sinceId: string | null, limit: number, userId: MiUser['id']) { + return await this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), sinceId, untilId) + .andWhere('note.userId = :userId', { userId }) + .andWhere(new Brackets(qb => { + qb + .where('note.visibility = \'public\'') + .orWhere('note.visibility = \'home\''); + })) + .andWhere('note.localOnly = FALSE') + .limit(limit) + .getMany(); + } + + @bindThis private async userInfo(request: FastifyRequest, reply: FastifyReply, user: MiUser | null, redact = false) { if (this.meta.federation === 'none') { reply.code(403); diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index c90c206e94..dce47e2290 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -223,7 +223,7 @@ export class ServerService implements OnApplicationShutdown { reply.header('Cache-Control', 'public, max-age=86400'); if (user) { - reply.redirect(user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user)); + reply.redirect((user.avatarId == null ? null : user.avatarUrl) ?? this.userEntityService.getIdenticonUrl(user)); } else { reply.redirect('/static-assets/user-unknown.png'); } diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts index 25fa725320..f48310c50f 100644 --- a/packages/backend/src/server/WellKnownServerService.ts +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -138,7 +138,7 @@ fastify.get('/.well-known/change-password', async (request, reply) => { const fromAcct = (acct: Acct.Acct): FindOptionsWhere<MiUser> | number => !acct.host || acct.host === this.config.host.toLowerCase() ? { - usernameLower: acct.username, + usernameLower: acct.username.toLowerCase(), host: IsNull(), isSuspended: false, } : 422; diff --git a/packages/backend/src/server/api/endpoint-list.ts b/packages/backend/src/server/api/endpoint-list.ts index e48b3de8ee..1c5a781fd9 100644 --- a/packages/backend/src/server/api/endpoint-list.ts +++ b/packages/backend/src/server/api/endpoint-list.ts @@ -72,8 +72,14 @@ export * as 'admin/promo/create' from './endpoints/admin/promo/create.js'; export * as 'admin/queue/clear' from './endpoints/admin/queue/clear.js'; export * as 'admin/queue/deliver-delayed' from './endpoints/admin/queue/deliver-delayed.js'; export * as 'admin/queue/inbox-delayed' from './endpoints/admin/queue/inbox-delayed.js'; -export * as 'admin/queue/promote' from './endpoints/admin/queue/promote.js'; +export * as 'admin/queue/retry-job' from './endpoints/admin/queue/retry-job.js'; +export * as 'admin/queue/remove-job' from './endpoints/admin/queue/remove-job.js'; +export * as 'admin/queue/show-job' from './endpoints/admin/queue/show-job.js'; +export * as 'admin/queue/promote-jobs' from './endpoints/admin/queue/promote-jobs.js'; +export * as 'admin/queue/jobs' from './endpoints/admin/queue/jobs.js'; export * as 'admin/queue/stats' from './endpoints/admin/queue/stats.js'; +export * as 'admin/queue/queues' from './endpoints/admin/queue/queues.js'; +export * as 'admin/queue/queue-stats' from './endpoints/admin/queue/queue-stats.js'; export * as 'admin/reject-quotes' from './endpoints/admin/reject-quotes.js'; export * as 'admin/relays/add' from './endpoints/admin/relays/add.js'; export * as 'admin/relays/list' from './endpoints/admin/relays/list.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts index 3f7df0e63d..81cb4b8119 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts @@ -6,7 +6,7 @@ import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; -import { QueueService } from '@/core/QueueService.js'; +import { QUEUE_TYPES, QueueService } from '@/core/QueueService.js'; export const meta = { tags: ['admin'], @@ -18,8 +18,11 @@ export const meta = { export const paramDef = { type: 'object', - properties: {}, - required: [], + properties: { + queue: { type: 'string', enum: QUEUE_TYPES }, + state: { type: 'string', enum: ['*', 'completed', 'wait', 'active', 'paused', 'prioritized', 'delayed', 'failed'] }, + }, + required: ['queue', 'state'], } as const; @Injectable() @@ -29,7 +32,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private queueService: QueueService, ) { super(meta, paramDef, async (ps, me) => { - this.queueService.destroy(); + this.queueService.queueClear(ps.queue, ps.state); this.moderationLogService.log(me, 'clearQueue'); }); diff --git a/packages/backend/src/server/api/endpoints/admin/queue/jobs.ts b/packages/backend/src/server/api/endpoints/admin/queue/jobs.ts new file mode 100644 index 0000000000..79731c9786 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/queue/jobs.ts @@ -0,0 +1,38 @@ +/* + * 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 { ModerationLogService } from '@/core/ModerationLogService.js'; +import { QUEUE_TYPES, QueueService } from '@/core/QueueService.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireModerator: true, + kind: 'read:admin:queue', +} as const; + +export const paramDef = { + type: 'object', + properties: { + queue: { type: 'string', enum: QUEUE_TYPES }, + state: { type: 'array', items: { type: 'string', enum: ['active', 'wait', 'delayed', 'completed', 'failed'] } }, + search: { type: 'string' }, + }, + required: ['queue', 'state'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private queueService: QueueService, + ) { + super(meta, paramDef, async (ps, me) => { + return this.queueService.queueGetJobs(ps.queue, ps.state, ps.search); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/queue/promote-jobs.ts b/packages/backend/src/server/api/endpoints/admin/queue/promote-jobs.ts new file mode 100644 index 0000000000..d22385e261 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/queue/promote-jobs.ts @@ -0,0 +1,39 @@ +/* + * 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 { ModerationLogService } from '@/core/ModerationLogService.js'; +import { QUEUE_TYPES, QueueService } from '@/core/QueueService.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireModerator: true, + kind: 'write:admin:queue', +} as const; + +export const paramDef = { + type: 'object', + properties: { + queue: { type: 'string', enum: QUEUE_TYPES }, + }, + required: ['queue'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private moderationLogService: ModerationLogService, + private queueService: QueueService, + ) { + super(meta, paramDef, async (ps, me) => { + this.queueService.queuePromoteJobs(ps.queue); + + this.moderationLogService.log(me, 'promoteQueue'); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/queue/promote.ts b/packages/backend/src/server/api/endpoints/admin/queue/promote.ts deleted file mode 100644 index 7502d4e1f7..0000000000 --- a/packages/backend/src/server/api/endpoints/admin/queue/promote.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 { ModerationLogService } from '@/core/ModerationLogService.js'; -import { QueueService } from '@/core/QueueService.js'; - -export const meta = { - tags: ['admin'], - - requireCredential: true, - requireModerator: true, - kind: 'write:admin:queue', -} as const; - -export const paramDef = { - type: 'object', - properties: { - type: { type: 'string', enum: ['deliver', 'inbox'] }, - }, - required: ['type'], -} as const; - -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export - constructor( - private moderationLogService: ModerationLogService, - private queueService: QueueService, - ) { - super(meta, paramDef, async (ps, me) => { - let delayedQueues; - - switch (ps.type) { - case 'deliver': - delayedQueues = await this.queueService.deliverQueue.getDelayed(); - for (let queueIndex = 0; queueIndex < delayedQueues.length; queueIndex++) { - const queue = delayedQueues[queueIndex]; - try { - await queue.promote(); - } catch (e) { - if (e instanceof Error) { - if (e.message.indexOf('not in a delayed state') !== -1) { - throw e; - } - } else { - throw e; - } - } - } - break; - - case 'inbox': - delayedQueues = await this.queueService.inboxQueue.getDelayed(); - for (let queueIndex = 0; queueIndex < delayedQueues.length; queueIndex++) { - const queue = delayedQueues[queueIndex]; - try { - await queue.promote(); - } catch (e) { - if (e instanceof Error) { - if (e.message.indexOf('not in a delayed state') !== -1) { - throw e; - } - } else { - throw e; - } - } - } - break; - } - - this.moderationLogService.log(me, 'promoteQueue'); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/admin/queue/queue-stats.ts b/packages/backend/src/server/api/endpoints/admin/queue/queue-stats.ts new file mode 100644 index 0000000000..10ce48332a --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/queue/queue-stats.ts @@ -0,0 +1,36 @@ +/* + * 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 { ModerationLogService } from '@/core/ModerationLogService.js'; +import { QUEUE_TYPES, QueueService } from '@/core/QueueService.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireModerator: true, + kind: 'read:admin:queue', +} as const; + +export const paramDef = { + type: 'object', + properties: { + queue: { type: 'string', enum: QUEUE_TYPES }, + }, + required: ['queue'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private queueService: QueueService, + ) { + super(meta, paramDef, async (ps, me) => { + return this.queueService.queueGetQueue(ps.queue); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/queue/queues.ts b/packages/backend/src/server/api/endpoints/admin/queue/queues.ts new file mode 100644 index 0000000000..3a38275f60 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/queue/queues.ts @@ -0,0 +1,35 @@ +/* + * 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 { ModerationLogService } from '@/core/ModerationLogService.js'; +import { QUEUE_TYPES, QueueService } from '@/core/QueueService.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireModerator: true, + kind: 'read:admin:queue', +} 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 queueService: QueueService, + ) { + super(meta, paramDef, async (ps, me) => { + return this.queueService.queueGetQueues(); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/queue/remove-job.ts b/packages/backend/src/server/api/endpoints/admin/queue/remove-job.ts new file mode 100644 index 0000000000..2c73f689d0 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/queue/remove-job.ts @@ -0,0 +1,38 @@ +/* + * 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 { ModerationLogService } from '@/core/ModerationLogService.js'; +import { QUEUE_TYPES, QueueService } from '@/core/QueueService.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireModerator: true, + kind: 'write:admin:queue', +} as const; + +export const paramDef = { + type: 'object', + properties: { + queue: { type: 'string', enum: QUEUE_TYPES }, + jobId: { type: 'string' }, + }, + required: ['queue', 'jobId'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private moderationLogService: ModerationLogService, + private queueService: QueueService, + ) { + super(meta, paramDef, async (ps, me) => { + this.queueService.queueRemoveJob(ps.queue, ps.jobId); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/queue/retry-job.ts b/packages/backend/src/server/api/endpoints/admin/queue/retry-job.ts new file mode 100644 index 0000000000..b2603128f8 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/queue/retry-job.ts @@ -0,0 +1,38 @@ +/* + * 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 { ModerationLogService } from '@/core/ModerationLogService.js'; +import { QUEUE_TYPES, QueueService } from '@/core/QueueService.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireModerator: true, + kind: 'write:admin:queue', +} as const; + +export const paramDef = { + type: 'object', + properties: { + queue: { type: 'string', enum: QUEUE_TYPES }, + jobId: { type: 'string' }, + }, + required: ['queue', 'jobId'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private moderationLogService: ModerationLogService, + private queueService: QueueService, + ) { + super(meta, paramDef, async (ps, me) => { + this.queueService.queueRetryJob(ps.queue, ps.jobId); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/queue/show-job.ts b/packages/backend/src/server/api/endpoints/admin/queue/show-job.ts new file mode 100644 index 0000000000..63747b5540 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/queue/show-job.ts @@ -0,0 +1,38 @@ +/* + * 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 { ModerationLogService } from '@/core/ModerationLogService.js'; +import { QUEUE_TYPES, QueueService } from '@/core/QueueService.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireModerator: true, + kind: 'read:admin:queue', +} as const; + +export const paramDef = { + type: 'object', + properties: { + queue: { type: 'string', enum: QUEUE_TYPES }, + jobId: { type: 'string' }, + }, + required: ['queue', 'jobId'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private moderationLogService: ModerationLogService, + private queueService: QueueService, + ) { + super(meta, paramDef, async (ps, me) => { + return this.queueService.queueGetJob(ps.queue, ps.jobId); + }); + } +} diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 34b69fd7b2..1321cf6338 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -558,7 +558,7 @@ export class ClientServerService { return await reply.view('user', { user, profile, me, - avatarUrl: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user), + avatarUrl: _user.avatarUrl, sub: request.params.sub, ...await this.generateCommonPugData(this.meta), clientCtx: htmlSafeJsonStringify({ diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts index 664f21d308..dcd4d80303 100644 --- a/packages/backend/src/server/web/FeedService.ts +++ b/packages/backend/src/server/web/FeedService.ts @@ -65,7 +65,7 @@ export class FeedService { generator: 'Sharkey', description: `${user.notesCount} Notes, ${profile.followingVisibility === 'public' ? user.followingCount : '?'} Following, ${profile.followersVisibility === 'public' ? user.followersCount : '?'} Followers${profile.description ? ` · ${profile.description}` : ''}`, link: author.link, - image: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user), + image: (user.avatarId == null ? null : user.avatarUrl) ?? this.userEntityService.getIdenticonUrl(user), feedLinks: { json: `${author.link}.json`, atom: `${author.link}.atom`, diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index 83dcb8df44..ee69e857bc 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -381,7 +381,8 @@ describe('User', () => { await alice.client.request('i/delete-account', { password: alice.password }); // NOTE: user deletion query is slow - await sleep(4000); + // FIXME: ensure user is removed successfully + await sleep(10000); const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 0); // no following relation @@ -480,7 +481,8 @@ describe('User', () => { await aAdmin.client.request('admin/suspend-user', { userId: alice.id }); // NOTE: user deletion query is slow - await sleep(4000); + // FIXME: ensure user is removed successfully + await sleep(10000); const following = await bob.client.request('users/following', { userId: bob.id }); strictEqual(following.length, 0); // no following relation diff --git a/packages/backend/test/e2e/antennas.ts b/packages/backend/test/e2e/antennas.ts index cbb18fe4ed..1bbacd065b 100644 --- a/packages/backend/test/e2e/antennas.ts +++ b/packages/backend/test/e2e/antennas.ts @@ -6,7 +6,6 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { DEFAULT_POLICIES } from '@/core/RoleService.js'; import { api, failedApiCall, @@ -19,6 +18,7 @@ import { userList, } from '../utils.js'; import type * as misskey from 'misskey-js'; +import { DEFAULT_POLICIES } from '@/core/RoleService.js'; const compareBy = <T extends { id: string }>(selector: (s: T) => string = (s: T): string => s.id) => (a: T, b: T): number => { return selector(a).localeCompare(selector(b)); @@ -235,11 +235,11 @@ describe('アンテナ', () => { await failedApiCall({ endpoint: 'antennas/create', parameters: { ...defaultParam, keywords: [[]], excludeKeywords: [[]] }, - user: alice + user: alice, }, { status: 400, code: 'EMPTY_KEYWORD', - id: '53ee222e-1ddd-4f9a-92e5-9fb82ddb463a' + id: '53ee222e-1ddd-4f9a-92e5-9fb82ddb463a', }); }); //#endregion @@ -274,11 +274,11 @@ describe('アンテナ', () => { await failedApiCall({ endpoint: 'antennas/update', parameters: { ...defaultParam, antennaId: antenna.id, keywords: [[]], excludeKeywords: [[]] }, - user: alice + user: alice, }, { status: 400, code: 'EMPTY_KEYWORD', - id: '721aaff6-4e1b-4d88-8de6-877fae9f68c4' + id: '721aaff6-4e1b-4d88-8de6-877fae9f68c4', }); }); @@ -375,14 +375,23 @@ describe('アンテナ', () => { ], }, { - // https://github.com/misskey-dev/misskey/issues/9025 - label: 'ただし、フォロワー限定投稿とDM投稿を含まない。フォロワーであっても。', + label: 'フォロワー限定投稿とDM投稿を含む', parameters: () => ({}), posts: [ { note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'public' }), included: true }, { note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'home' }), included: true }, - { note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'followers' }) }, - { note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'specified', visibleUserIds: [alice.id] }) }, + { note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'followers' }), included: true }, + { note: (): Promise<Note> => post(bob, { text: `${keyword}`, visibility: 'specified', visibleUserIds: [alice.id] }), included: true }, + ], + }, + { + label: 'フォロワー限定投稿とDM投稿を含まない', + parameters: () => ({}), + posts: [ + { note: (): Promise<Note> => post(bob, { text: `${keyword}`, visibility: 'public' }), included: true }, + { note: (): Promise<Note> => post(bob, { text: `${keyword}`, visibility: 'home' }), included: true }, + { note: (): Promise<Note> => post(bob, { text: `${keyword}`, visibility: 'followers' }) }, + { note: (): Promise<Note> => post(bob, { text: `${keyword}`, visibility: 'specified', visibleUserIds: [carol.id] }) }, ], }, { diff --git a/packages/backend/test/unit/AnnouncementService.ts b/packages/backend/test/unit/AnnouncementService.ts index 81da0fac31..a79655c9aa 100644 --- a/packages/backend/test/unit/AnnouncementService.ts +++ b/packages/backend/test/unit/AnnouncementService.ts @@ -44,7 +44,7 @@ describe('AnnouncementService', () => { return usersRepository.insert({ id: genAidx(Date.now()), username: un, - usernameLower: un, + usernameLower: un.toLowerCase(), ...data, }) .then(x => usersRepository.findOneByOrFail(x.identifiers[0])); diff --git a/packages/backend/test/unit/SigninWithPasskeyApiService.ts b/packages/backend/test/unit/SigninWithPasskeyApiService.ts index 2b7afb02f9..5bc86f6616 100644 --- a/packages/backend/test/unit/SigninWithPasskeyApiService.ts +++ b/packages/backend/test/unit/SigninWithPasskeyApiService.ts @@ -123,8 +123,8 @@ describe('SigninWithPasskeyApiService', () => { jest.spyOn(webAuthnService, 'verifySignInWithPasskeyAuthentication').mockImplementation(FakeWebauthnVerify); const dummyUser = { - id: uid, username: uid, usernameLower: uid.toLocaleLowerCase(), uri: null, host: null, - }; + id: uid, username: uid, usernameLower: uid.toLowerCase(), uri: null, host: null, + }; const dummyProfile = { userId: uid, password: 'qwerty', diff --git a/packages/backend/test/unit/entities/UserEntityService.ts b/packages/backend/test/unit/entities/UserEntityService.ts index 6b7eedff55..ce3f931bb0 100644 --- a/packages/backend/test/unit/entities/UserEntityService.ts +++ b/packages/backend/test/unit/entities/UserEntityService.ts @@ -74,7 +74,7 @@ describe('UserEntityService', () => { ...userData, id: genAidx(Date.now()), username: un, - usernameLower: un, + usernameLower: un.toLowerCase(), }) .then(x => usersRepository.findOneByOrFail(x.identifiers[0])); diff --git a/packages/frontend-embed/package.json b/packages/frontend-embed/package.json index 297a06fd8a..472e4d7c94 100644 --- a/packages/frontend-embed/package.json +++ b/packages/frontend-embed/package.json @@ -22,33 +22,33 @@ "astring": "1.9.0", "buraha": "0.0.1", "estree-walker": "3.0.3", - "misskey-js": "workspace:*", "frontend-shared": "workspace:*", + "json5": "2.2.3", + "misskey-js": "workspace:*", "punycode.js": "2.3.1", - "rollup": "4.36.0", - "sass": "1.86.0", - "shiki": "3.2.1", + "rollup": "4.39.0", + "sass": "1.86.3", + "shiki": "3.2.2", "tinycolor2": "1.6.0", - "tsc-alias": "1.8.11", + "tsc-alias": "1.8.15", "tsconfig-paths": "4.2.0", - "typescript": "5.8.2", + "typescript": "5.8.3", "uuid": "11.1.0", - "json5": "2.2.3", - "vite": "6.2.4", + "vite": "6.3.1", "vue": "3.5.13" }, "devDependencies": { "@misskey-dev/summaly": "5.2.0", "@testing-library/vue": "8.1.0", - "@types/estree": "1.0.6", + "@types/estree": "1.0.7", "@types/micromatch": "4.0.9", - "@types/node": "22.13.11", + "@types/node": "22.14.0", "@types/punycode.js": "npm:@types/punycode@2.1.4", "@types/tinycolor2": "1.4.6", - "@types/ws": "8.18.0", - "@typescript-eslint/eslint-plugin": "8.27.0", - "@typescript-eslint/parser": "8.27.0", - "@vitest/coverage-v8": "3.0.9", + "@types/ws": "8.18.1", + "@typescript-eslint/eslint-plugin": "8.29.1", + "@typescript-eslint/parser": "8.29.1", + "@vitest/coverage-v8": "3.1.1", "@vue/runtime-core": "3.5.13", "acorn": "8.14.1", "cross-env": "7.0.3", @@ -64,7 +64,7 @@ "start-server-and-test": "2.0.11", "vite-plugin-turbosnap": "1.0.3", "vue-component-type-helpers": "2.2.8", - "vue-eslint-parser": "10.1.1", + "vue-eslint-parser": "10.1.3", "vue-tsc": "2.2.8" } } diff --git a/packages/frontend-shared/package.json b/packages/frontend-shared/package.json index b9769cc670..a4874b8f77 100644 --- a/packages/frontend-shared/package.json +++ b/packages/frontend-shared/package.json @@ -21,14 +21,14 @@ "lint": "pnpm typecheck && pnpm eslint" }, "devDependencies": { - "@types/node": "22.13.11", - "@typescript-eslint/eslint-plugin": "8.27.0", - "@typescript-eslint/parser": "8.27.0", - "esbuild": "0.25.1", + "@types/node": "22.14.0", + "@typescript-eslint/eslint-plugin": "8.29.1", + "@typescript-eslint/parser": "8.29.1", + "esbuild": "0.25.2", "eslint-plugin-vue": "10.0.0", "nodemon": "3.1.9", - "typescript": "5.8.2", - "vue-eslint-parser": "10.1.1" + "typescript": "5.8.3", + "vue-eslint-parser": "10.1.3" }, "files": [ "js-built" diff --git a/packages/frontend-shared/themes/_dark.json5 b/packages/frontend-shared/themes/_dark.json5 index 57ec7b98b1..1c52dac9d5 100644 --- a/packages/frontend-shared/themes/_dark.json5 +++ b/packages/frontend-shared/themes/_dark.json5 @@ -34,6 +34,8 @@ navFg: '@fg', navActive: '@accent', navIndicator: '@indicator', + pageHeaderBg: '@bg', + pageHeaderFg: '@fg', link: '#44a4c1', hashtag: '#ff9156', mention: '@accent', diff --git a/packages/frontend-shared/themes/_light.json5 b/packages/frontend-shared/themes/_light.json5 index 23c8f39af7..c12f5be9db 100644 --- a/packages/frontend-shared/themes/_light.json5 +++ b/packages/frontend-shared/themes/_light.json5 @@ -34,6 +34,8 @@ navFg: '@fg', navActive: '@accent', navIndicator: '@indicator', + pageHeaderBg: '@bg', + pageHeaderFg: '@fg', link: '#44a4c1', hashtag: '#ff9156', mention: '@accent', diff --git a/packages/frontend/.storybook/fakes.ts b/packages/frontend/.storybook/fakes.ts index 377d26d6a3..7288dabb60 100644 --- a/packages/frontend/.storybook/fakes.ts +++ b/packages/frontend/.storybook/fakes.ts @@ -43,6 +43,41 @@ export function channel(id = 'somechannelid', name = 'Some Channel', bannerUrl: }; } +export function chatMessage(room = false, id = 'somechatmessageid', text = 'Hello!'): entities.ChatMessage { + const fromUser = userLite(); + const toRoom = chatRoom(); + const toUser = userLite('touserid'); + return { + id, + createdAt: '2016-12-28T22:49:51.000Z', + fromUserId: fromUser.id, + fromUser, + text, + isRead: false, + reactions: [], + ...room ? { + toRoomId: toRoom.id, + toRoom, + } : { + toUserId: toUser.id, + toUser, + }, + }; +} + +export function chatRoom(id = 'somechatroomid', name = 'Some Chat Room'): entities.ChatRoom { + const owner = userLite('someownerid'); + return { + id, + createdAt: '2016-12-28T22:49:51.000Z', + ownerId: owner.id, + owner, + name, + description: 'A chat room for testing', + isMuted: false, + }; +} + export function clip(id = 'someclipid', name = 'Some Clip'): entities.Clip { return { id, diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 489dab733f..1718dd93a1 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -25,7 +25,7 @@ "@rollup/plugin-replace": "6.0.2", "@rollup/pluginutils": "5.1.4", "@ruffle-rs/ruffle": "0.1.0-nightly.2024.10.15", - "@sentry/vue": "9.8.0", + "@sentry/vue": "9.12.0", "@syuilo/aiscript": "0.19.0", "@transfem-org/sfm-js": "0.24.6", "@twemoji/parser": "15.1.1", @@ -33,7 +33,7 @@ "@vue/compiler-sfc": "3.5.13", "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15", "astring": "1.9.0", - "broadcast-channel": "7.0.0", + "broadcast-channel": "7.1.0", "buraha": "0.0.1", "canvas-confetti": "1.9.3", "chart.js": "4.4.8", @@ -41,7 +41,7 @@ "chartjs-chart-matrix": "2.1.1", "chartjs-plugin-gradient": "0.6.1", "chartjs-plugin-zoom": "2.2.0", - "chromatic": "11.27.0", + "chromatic": "11.28.0", "compare-versions": "6.1.1", "cropperjs": "2.0.0", "date-fns": "4.1.0", @@ -61,21 +61,21 @@ "moment": "^2.30.1", "photoswipe": "5.4.4", "punycode.js": "2.3.1", - "rollup": "4.36.0", + "rollup": "4.39.0", "sanitize-html": "2.15.0", - "sass": "1.86.0", - "shiki": "3.2.1", + "sass": "1.86.3", + "shiki": "3.2.2", "strict-event-emitter-types": "2.0.0", "textarea-caret": "3.1.0", - "three": "0.174.0", + "three": "0.175.0", "throttle-debounce": "5.0.2", "tinycolor2": "1.6.0", - "tsc-alias": "1.8.11", + "tsc-alias": "1.8.15", "tsconfig-paths": "4.2.0", - "typescript": "5.8.2", + "typescript": "5.8.3", "uuid": "11.1.0", "v-code-diff": "1.13.1", - "vite": "6.2.4", + "vite": "6.3.1", "vue": "3.5.13", "vuedraggable": "next", "wanakana": "5.3.1" @@ -85,40 +85,40 @@ }, "devDependencies": { "@misskey-dev/summaly": "5.2.0", - "@storybook/addon-actions": "8.6.7", - "@storybook/addon-essentials": "8.6.7", - "@storybook/addon-interactions": "8.6.7", - "@storybook/addon-links": "8.6.7", - "@storybook/addon-mdx-gfm": "8.6.7", - "@storybook/addon-storysource": "8.6.7", - "@storybook/blocks": "8.6.7", - "@storybook/components": "8.6.7", - "@storybook/core-events": "8.6.7", - "@storybook/manager-api": "8.6.7", - "@storybook/preview-api": "8.6.7", - "@storybook/react": "8.6.7", - "@storybook/react-vite": "8.6.7", - "@storybook/test": "8.6.7", - "@storybook/theming": "8.6.7", - "@storybook/types": "8.6.7", - "@storybook/vue3": "8.6.7", - "@storybook/vue3-vite": "8.6.7", + "@storybook/addon-actions": "8.6.12", + "@storybook/addon-essentials": "8.6.12", + "@storybook/addon-interactions": "8.6.12", + "@storybook/addon-links": "8.6.12", + "@storybook/addon-mdx-gfm": "8.6.12", + "@storybook/addon-storysource": "8.6.12", + "@storybook/blocks": "8.6.12", + "@storybook/components": "8.6.12", + "@storybook/core-events": "8.6.12", + "@storybook/manager-api": "8.6.12", + "@storybook/preview-api": "8.6.12", + "@storybook/react": "8.6.12", + "@storybook/react-vite": "8.6.12", + "@storybook/test": "8.6.12", + "@storybook/theming": "8.6.12", + "@storybook/types": "8.6.12", + "@storybook/vue3": "8.6.12", + "@storybook/vue3-vite": "8.6.12", "@testing-library/vue": "8.1.0", "@types/canvas-confetti": "1.9.0", - "@types/estree": "1.0.6", + "@types/estree": "1.0.7", "@types/katex": "^0.16.7", "@types/matter-js": "0.19.8", "@types/micromatch": "4.0.9", - "@types/node": "22.13.11", + "@types/node": "22.14.0", "@types/punycode.js": "npm:@types/punycode@2.1.4", - "@types/sanitize-html": "2.13.0", + "@types/sanitize-html": "2.15.0", "@types/seedrandom": "3.0.8", "@types/throttle-debounce": "5.0.2", "@types/tinycolor2": "1.4.6", - "@types/ws": "8.18.0", - "@typescript-eslint/eslint-plugin": "8.27.0", - "@typescript-eslint/parser": "8.27.0", - "@vitest/coverage-v8": "3.0.9", + "@types/ws": "8.18.1", + "@typescript-eslint/eslint-plugin": "8.29.1", + "@typescript-eslint/parser": "8.29.1", + "@vitest/coverage-v8": "3.1.1", "@vue/compiler-core": "3.5.13", "@vue/runtime-core": "3.5.13", "acorn": "8.14.1", @@ -134,18 +134,17 @@ "msw-storybook-addon": "2.0.4", "nodemon": "3.1.9", "prettier": "3.5.3", - "react": "19.0.0", - "react-dom": "19.0.0", + "react": "19.1.0", + "react-dom": "19.1.0", "seedrandom": "3.0.5", "start-server-and-test": "2.0.11", - "storybook": "8.6.7", + "storybook": "8.6.12", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", - "vite-node": "3.0.9", "vite-plugin-turbosnap": "1.0.3", - "vitest": "3.0.9", + "vitest": "3.1.1", "vitest-fetch-mock": "0.4.5", "vue-component-type-helpers": "2.2.8", - "vue-eslint-parser": "10.1.1", + "vue-eslint-parser": "10.1.3", "vue-tsc": "2.2.8" } } diff --git a/packages/frontend/src/accounts.ts b/packages/frontend/src/accounts.ts index 032455bd70..d535c4c313 100644 --- a/packages/frontend/src/accounts.ts +++ b/packages/frontend/src/accounts.ts @@ -21,14 +21,19 @@ type AccountWithToken = Misskey.entities.MeDetailed & { token: string }; export async function getAccounts(): Promise<{ host: string; - user: Misskey.entities.User; + id: Misskey.entities.User['id']; + username: Misskey.entities.User['username']; + user?: Misskey.entities.User | null; token: string | null; }[]> { const tokens = store.s.accountTokens; + const accountInfos = store.s.accountInfos; const accounts = prefer.s.accounts; return accounts.map(([host, user]) => ({ host, - user, + id: user.id, + username: user.username, + user: accountInfos[host + '/' + user.id], token: tokens[host + '/' + user.id] ?? null, })); } @@ -36,7 +41,8 @@ export async function getAccounts(): Promise<{ async function addAccount(host: string, user: Misskey.entities.User, token: AccountWithToken['token']) { if (!prefer.s.accounts.some(x => x[0] === host && x[1].id === user.id)) { store.set('accountTokens', { ...store.s.accountTokens, [host + '/' + user.id]: token }); - prefer.commit('accounts', [...prefer.s.accounts, [host, user]]); + store.set('accountInfos', { ...store.s.accountInfos, [host + '/' + user.id]: user }); + prefer.commit('accounts', [...prefer.s.accounts, [host, { id: user.id, username: user.username }]]); } } @@ -44,6 +50,10 @@ export async function removeAccount(host: string, id: AccountWithToken['id']) { const tokens = JSON.parse(JSON.stringify(store.s.accountTokens)); delete tokens[host + '/' + id]; store.set('accountTokens', tokens); + const accountInfos = JSON.parse(JSON.stringify(store.s.accountInfos)); + delete accountInfos[host + '/' + id]; + store.set('accountInfos', accountInfos); + prefer.commit('accounts', prefer.s.accounts.filter(x => x[0] !== host || x[1].id !== id)); } @@ -128,14 +138,7 @@ export function updateCurrentAccount(accountData: Misskey.entities.MeDetailed) { for (const [key, value] of Object.entries(accountData)) { $i[key] = value; } - prefer.commit('accounts', prefer.s.accounts.map(([host, user]) => { - // TODO: $iのホストも比較したいけど通常null - if (user.id === $i.id) { - return [host, $i]; - } else { - return [host, user]; - } - })); + store.set('accountInfos', { ...store.s.accountInfos, [host + '/' + $i.id]: $i }); $i.token = token; miLocalStorage.setItem('account', JSON.stringify($i)); } @@ -145,17 +148,9 @@ export function updateCurrentAccountPartial(accountData: Partial<Misskey.entitie for (const [key, value] of Object.entries(accountData)) { $i[key] = value; } - prefer.commit('accounts', prefer.s.accounts.map(([host, user]) => { - // TODO: $iのホストも比較したいけど通常null - if (user.id === $i.id) { - const newUser = JSON.parse(JSON.stringify($i)); - for (const [key, value] of Object.entries(accountData)) { - newUser[key] = value; - } - return [host, newUser]; - } - return [host, user]; - })); + + store.set('accountInfos', { ...store.s.accountInfos, [host + '/' + $i.id]: $i }); + miLocalStorage.setItem('account', JSON.stringify($i)); } @@ -230,25 +225,42 @@ export async function openAccountMenu(opts: { }, ev: MouseEvent) { if (!$i) return; - function createItem(host: string, account: Misskey.entities.User): MenuItem { - return { - type: 'user' as const, - user: account, - active: opts.active != null ? opts.active === account.id : false, - action: async () => { - if (opts.onChoose) { - opts.onChoose(account); - } else { - switchAccount(host, account.id); - } - }, - }; + function createItem(host: string, id: Misskey.entities.User['id'], username: Misskey.entities.User['username'], account: Misskey.entities.User | null | undefined, token: string): MenuItem { + if (account) { + return { + type: 'user' as const, + user: account, + active: opts.active != null ? opts.active === id : false, + action: async () => { + if (opts.onChoose) { + opts.onChoose(account); + } else { + switchAccount(host, id); + } + }, + }; + } else { + return { + type: 'button' as const, + text: username, + active: opts.active != null ? opts.active === id : false, + action: async () => { + if (opts.onChoose) { + fetchAccount(token, id).then(account => { + opts.onChoose(account); + }); + } else { + switchAccount(host, id); + } + }, + }; + } } const menuItems: MenuItem[] = []; // TODO: $iのホストも比較したいけど通常null - const accountItems = (await getAccounts().then(accounts => accounts.filter(x => x.user.id !== $i.id))).map(a => createItem(a.host, a.user)); + const accountItems = (await getAccounts().then(accounts => accounts.filter(x => x.id !== $i.id))).map(a => createItem(a.host, a.id, a.username, a.user, a.token)); if (opts.withExtraOperation) { menuItems.push({ @@ -261,7 +273,7 @@ export async function openAccountMenu(opts: { }); if (opts.includeCurrentAccount) { - menuItems.push(createItem(host, $i)); + menuItems.push(createItem(host, $i.id, $i.username, $i, $i.token)); } menuItems.push(...accountItems); @@ -302,7 +314,7 @@ export async function openAccountMenu(opts: { }); } else { if (opts.includeCurrentAccount) { - menuItems.push(createItem(host, $i)); + menuItems.push(createItem(host, $i.id, $i.username, $i, $i.token)); } menuItems.push(...accountItems); diff --git a/packages/frontend/src/components/MkAuthConfirm.vue b/packages/frontend/src/components/MkAuthConfirm.vue index 00bf8e68d9..b3331d742b 100644 --- a/packages/frontend/src/components/MkAuthConfirm.vue +++ b/packages/frontend/src/components/MkAuthConfirm.vue @@ -157,7 +157,7 @@ async function init() { const accounts = await getAccounts(); - const accountIdsToFetch = accounts.map(a => a.user.id).filter(id => !users.value.has(id)); + const accountIdsToFetch = accounts.map(a => a.id).filter(id => !users.value.has(id)); if (accountIdsToFetch.length > 0) { const usersRes = await misskeyApi('users/show', { @@ -169,7 +169,7 @@ async function init() { users.value.set(user.id, { ...user, - token: accounts.find(a => a.user.id === user.id)!.token, + token: accounts.find(a => a.id === user.id)!.token, }); } } diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 321b043d9e..6608eeaa47 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -15,12 +15,12 @@ SPDX-License-Identifier: AGPL-3.0-only </li> <li tabindex="-1" :class="$style.item" @click="chooseUser()" @keydown="onKeydown">{{ i18n.ts.selectUser }}</li> </ol> - <ol v-else-if="hashtags.length > 0" ref="suggests" :class="$style.list"> + <ol v-else-if="type === 'hashtag' && hashtags.length > 0" ref="suggests" :class="$style.list"> <li v-for="hashtag in hashtags" tabindex="-1" :class="$style.item" @click="complete(type, hashtag)" @keydown="onKeydown"> <span class="name">{{ hashtag }}</span> </li> </ol> - <ol v-else-if="emojis.length > 0" ref="suggests" :class="$style.list"> + <ol v-else-if="type === 'emoji' || type === 'emojiComplete' && emojis.length > 0" ref="suggests" :class="$style.list"> <li v-for="emoji in emojis" :key="emoji.emoji" :class="$style.item" tabindex="-1" @click="complete(type, emoji.emoji)" @keydown="onKeydown"> <MkCustomEmoji v-if="'isCustomEmoji' in emoji && emoji.isCustomEmoji" :name="emoji.emoji" :class="$style.emoji" :fallbackToImage="true"/> <MkEmoji v-else :emoji="emoji.emoji" :class="$style.emoji"/> @@ -30,12 +30,12 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-if="emoji.aliasOf" :class="$style.emojiAlias">({{ emoji.aliasOf }})</span> </li> </ol> - <ol v-else-if="mfmTags.length > 0" ref="suggests" :class="$style.list"> + <ol v-else-if="type === 'mfmTag' && mfmTags.length > 0" ref="suggests" :class="$style.list"> <li v-for="tag in mfmTags" tabindex="-1" :class="$style.item" @click="complete(type, tag)" @keydown="onKeydown"> <span>{{ tag }}</span> </li> </ol> - <ol v-else-if="mfmParams.length > 0" ref="suggests" :class="$style.list"> + <ol v-else-if="type === 'mfmParam' && mfmParams.length > 0" ref="suggests" :class="$style.list"> <li v-for="param in mfmParams" tabindex="-1" :class="$style.item" @click="complete(type, q.params.toSpliced(-1, 1, param).join(','))" @keydown="onKeydown"> <span>{{ param }}</span> </li> @@ -58,12 +58,44 @@ import { store } from '@/store.js'; import { i18n } from '@/i18n.js'; import { miLocalStorage } from '@/local-storage.js'; import { customEmojis } from '@/custom-emojis.js'; -import { searchEmoji } from '@/utility/search-emoji.js'; +import { searchEmoji, searchEmojiExact } from '@/utility/search-emoji.js'; import { prefer } from '@/preferences.js'; +export type CompleteInfo = { + user: { + payload: any; + query: string | null; + }, + hashtag: { + payload: string; + query: string; + }, + // `:emo` -> `:emoji:` or some unicode emoji + emoji: { + payload: string; + query: string; + }, + // like emoji but for `:emoji:` -> unicode emoji + emojiComplete: { + payload: string; + query: string; + }, + mfmTag: { + payload: string; + query: string; + }, + mfmParam: { + payload: string; + query: { + tag: string; + params: string[]; + }; + }, +}; + const lib = emojilist.filter(x => x.category !== 'flags'); -const emojiDb = computed(() => { +const unicodeEmojiDB = computed(() => { //#region Unicode Emoji const char2path = prefer.r.emojiStyle.value === 'twemoji' ? char2twemojiFilePath : prefer.r.emojiStyle.value === 'tossface' ? char2tossfaceFilePath : char2fluentEmojiFilePath; @@ -87,6 +119,12 @@ const emojiDb = computed(() => { } unicodeEmojiDB.sort((a, b) => a.name.length - b.name.length); + + return unicodeEmojiDB; +}); + +const emojiDb = computed(() => { + //#region Unicode Emoji //#endregion //#region Custom Emoji @@ -114,7 +152,7 @@ const emojiDb = computed(() => { customEmojiDB.sort((a, b) => a.name.length - b.name.length); //#endregion - return markRaw([...customEmojiDB, ...unicodeEmojiDB]); + return markRaw([...customEmojiDB, ...unicodeEmojiDB.value]); }); export default { @@ -123,18 +161,23 @@ export default { }; </script> -<script lang="ts" setup> -const props = defineProps<{ - type: string; - q: any; - textarea: HTMLTextAreaElement; +<script lang="ts" setup generic="T extends keyof CompleteInfo"> +type PropsType<T extends keyof CompleteInfo> = { + type: T; + q: CompleteInfo[T]['query']; + // なぜかわからないけど HTMLTextAreaElement | HTMLInputElement だと addEventListener/removeEventListenerがエラー + textarea: (HTMLTextAreaElement | HTMLInputElement) & HTMLElement; close: () => void; x: number; y: number; -}>(); +}; +//const props = defineProps<PropsType<keyof CompleteInfo>>(); +// ↑と同じだけど↓にしないとdiscriminated unionにならない。 +// https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions +const props = defineProps<PropsType<'user'> | PropsType<'hashtag'> | PropsType<'emoji'> | PropsType<'emojiComplete'> | PropsType<'mfmTag'> | PropsType<'mfmParam'>>(); const emit = defineEmits<{ - (event: 'done', value: { type: string; value: any }): void; + <T extends keyof CompleteInfo>(event: 'done', value: { type: T; value: CompleteInfo[T]['payload'] }): void; (event: 'closed'): void; }>(); @@ -151,10 +194,10 @@ const mfmParams = ref<string[]>([]); const select = ref(-1); const zIndex = os.claimZIndex('high'); -function complete(type: string, value: any) { +function complete<T extends keyof CompleteInfo>(type: T, value: CompleteInfo[T]['payload']) { emit('done', { type, value }); emit('closed'); - if (type === 'emoji') { + if (type === 'emoji' || type === 'emojiComplete') { let recents = store.s.recentlyUsedEmojis; recents = recents.filter((emoji: any) => emoji !== value); recents.unshift(value); @@ -243,6 +286,8 @@ function exec() { } emojis.value = searchEmoji(props.q.normalize('NFC').toLowerCase(), emojiDb.value); + } else if (props.type === 'emojiComplete') { + emojis.value = searchEmojiExact(props.q.normalize('NFC').toLowerCase(), unicodeEmojiDB.value); } else if (props.type === 'mfmTag') { if (!props.q || props.q === '') { mfmTags.value = MFM_TAGS; diff --git a/packages/frontend/src/components/MkChatHistories.stories.impl.ts b/packages/frontend/src/components/MkChatHistories.stories.impl.ts new file mode 100644 index 0000000000..8268adc36f --- /dev/null +++ b/packages/frontend/src/components/MkChatHistories.stories.impl.ts @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { http, HttpResponse } from 'msw'; +import { action } from '@storybook/addon-actions'; +import { chatMessage } from '../../.storybook/fakes'; +import MkChatHistories from './MkChatHistories.vue'; +import type { StoryObj } from '@storybook/vue3'; +import type * as Misskey from 'misskey-js'; +export const Default = { + render(args) { + return { + components: { + MkChatHistories, + }, + setup() { + return { + args, + }; + }, + computed: { + props() { + return { + ...this.args, + }; + }, + }, + template: '<MkChatHistories v-bind="props" />', + }; + }, + parameters: { + layout: 'centered', + msw: { + handlers: [ + http.post('/api/chat/history', async ({ request }) => { + const body = await request.json() as Misskey.entities.ChatHistoryRequest; + action('POST /api/chat/history')(body); + return HttpResponse.json([chatMessage(body.room)]); + }), + ], + }, + }, +} satisfies StoryObj<typeof MkChatHistories>; diff --git a/packages/frontend/src/components/MkChatHistories.vue b/packages/frontend/src/components/MkChatHistories.vue new file mode 100644 index 0000000000..c508ea8451 --- /dev/null +++ b/packages/frontend/src/components/MkChatHistories.vue @@ -0,0 +1,208 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div v-if="history.length > 0" class="_gaps_s"> + <MkA + v-for="item in history" + :key="item.id" + :class="[$style.message, { [$style.isMe]: item.isMe, [$style.isRead]: item.message.isRead }]" + class="_panel" + :to="item.message.toRoomId ? `/chat/room/${item.message.toRoomId}` : `/chat/user/${item.other!.id}`" + > + <MkAvatar v-if="item.message.toRoomId" :class="$style.messageAvatar" :user="item.message.fromUser" indicator :preview="false"/> + <MkAvatar v-else-if="item.other" :class="$style.messageAvatar" :user="item.other" indicator :preview="false"/> + <div :class="$style.messageBody"> + <header v-if="item.message.toRoom" :class="$style.messageHeader"> + <span :class="$style.messageHeaderName"><i class="ti ti-users"></i> {{ item.message.toRoom.name }}</span> + <MkTime :time="item.message.createdAt" :class="$style.messageHeaderTime"/> + </header> + <header v-else :class="$style.messageHeader"> + <MkUserName :class="$style.messageHeaderName" :user="item.other!"/> + <MkAcct :class="$style.messageHeaderUsername" :user="item.other!"/> + <MkTime :time="item.message.createdAt" :class="$style.messageHeaderTime"/> + </header> + <div :class="$style.messageBodyText"><span v-if="item.isMe" :class="$style.youSaid">{{ i18n.ts.you }}:</span>{{ item.message.text }}</div> + </div> + </MkA> +</div> +<div v-if="!initializing && history.length == 0" class="_fullinfo"> + <div>{{ i18n.ts._chat.noHistory }}</div> +</div> +<MkLoading v-if="initializing"/> +</template> + +<script lang="ts" setup> +import { onActivated, onDeactivated, onMounted, ref } from 'vue'; +import * as Misskey from 'misskey-js'; +import { useInterval } from '@@/js/use-interval.js'; +import { misskeyApi } from '@/utility/misskey-api.js'; +import { i18n } from '@/i18n.js'; +import { ensureSignin } from '@/i.js'; + +const $i = ensureSignin(); + +const history = ref<{ + id: string; + message: Misskey.entities.ChatMessage; + other: Misskey.entities.ChatMessage['fromUser'] | Misskey.entities.ChatMessage['toUser'] | null; + isMe: boolean; +}[]>([]); + +const initializing = ref(true); +const fetching = ref(false); + +async function fetchHistory() { + if (fetching.value) return; + + fetching.value = true; + + const [userMessages, roomMessages] = await Promise.all([ + misskeyApi('chat/history', { room: false }), + misskeyApi('chat/history', { room: true }), + ]); + + history.value = [...userMessages, ...roomMessages] + .toSorted((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) + .map(m => ({ + id: m.id, + message: m, + other: (!('room' in m) || m.room == null) ? (m.fromUserId === $i.id ? m.toUser : m.fromUser) : null, + isMe: m.fromUserId === $i.id, + })); + + fetching.value = false; + initializing.value = false; +} + +let isActivated = true; + +onActivated(() => { + isActivated = true; +}); + +onDeactivated(() => { + isActivated = false; +}); + +useInterval(() => { + // TODO: DOM的にバックグラウンドになっていないかどうかも考慮する + if (!window.document.hidden && isActivated) { + fetchHistory(); + } +}, 1000 * 10, { + immediate: false, + afterMounted: true, +}); + +onActivated(() => { + fetchHistory(); +}); + +onMounted(() => { + fetchHistory(); +}); +</script> + +<style lang="scss" module> +.message { + position: relative; + display: flex; + padding: 16px 24px; + + &.isRead, + &.isMe { + opacity: 0.8; + } + + &:not(.isMe):not(.isRead) { + &::before { + content: ''; + position: absolute; + top: 8px; + right: 8px; + width: 8px; + height: 8px; + border-radius: 100%; + background-color: var(--MI_THEME-accent); + } + } +} + +@container (max-width: 500px) { + .message { + font-size: 90%; + padding: 14px 20px; + } +} + +@container (max-width: 450px) { + .message { + font-size: 80%; + padding: 12px 16px; + } +} + +.messageAvatar { + width: 50px; + height: 50px; + margin: 0 16px 0 0; +} + +@container (max-width: 500px) { + .messageAvatar { + width: 45px; + height: 45px; + } +} + +@container (max-width: 450px) { + .messageAvatar { + width: 40px; + height: 40px; + } +} + +.messageBody { + flex: 1; + min-width: 0; +} + +.messageHeader { + display: flex; + align-items: center; + margin-bottom: 2px; + white-space: nowrap; + overflow: clip; +} + +.messageHeaderName { + margin: 0; + padding: 0; + overflow: hidden; + text-overflow: ellipsis; + font-size: 1em; + font-weight: bold; +} + +.messageHeaderUsername { + margin: 0 8px; +} + +.messageHeaderTime { + margin-left: auto; +} + +.messageBodyText { + overflow: hidden; + overflow-wrap: break-word; + font-size: 1.1em; +} + +.youSaid { + font-weight: bold; + margin-right: 0.5em; +} +</style> diff --git a/packages/frontend/src/components/MkCode.vue b/packages/frontend/src/components/MkCode.vue index a99ab3bd09..d79ecc7302 100644 --- a/packages/frontend/src/components/MkCode.vue +++ b/packages/frontend/src/components/MkCode.vue @@ -12,8 +12,8 @@ SPDX-License-Identifier: AGPL-3.0-only <template #fallback> <MkLoading/> </template> - <XCode v-if="show && lang" :code="code" :lang="lang"/> - <pre v-else-if="show" :class="$style.codeBlockFallbackRoot"><code :class="$style.codeBlockFallbackCode">{{ code }}</code></pre> + <XCode v-if="show && lang" class="_selectable" :code="code" :lang="lang"/> + <pre v-else-if="show" class="_selectable" :class="$style.codeBlockFallbackRoot"><code :class="$style.codeBlockFallbackCode">{{ code }}</code></pre> <button v-else :class="$style.codePlaceholderRoot" @click="show = true"> <div :class="$style.codePlaceholderContainer"> <div><i class="ti ti-code"></i> {{ i18n.ts.code }}</div> @@ -70,11 +70,9 @@ function copy() { .codeBlockFallbackRoot { display: block; overflow-wrap: anywhere; - background: var(--MI_THEME-bg); padding: 1em; - margin: .5em 0; + margin: 0; overflow: auto; - border-radius: var(--MI-radius-sm); } .codeBlockFallbackCode { diff --git a/packages/frontend/src/components/MkDisableSection.stories.impl.ts b/packages/frontend/src/components/MkDisableSection.stories.impl.ts new file mode 100644 index 0000000000..78e556c63e --- /dev/null +++ b/packages/frontend/src/components/MkDisableSection.stories.impl.ts @@ -0,0 +1,7 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import MkDisableSection from './MkDisableSection.vue'; +void MkDisableSection; diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index 218ce9d93a..cb7c270e91 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -640,13 +640,13 @@ function getMenu() { text: i18n.ts.upload + ' (' + i18n.ts.compress + ')', icon: 'ti ti-upload', action: () => { - chooseFileFromPc(true, { keepOriginal: false }); + chooseFileFromPc(true, { uploadFolder: folder.value?.id, keepOriginal: false }); }, }, { text: i18n.ts.upload, icon: 'ti ti-upload', action: () => { - chooseFileFromPc(true, { keepOriginal: true }); + chooseFileFromPc(true, { uploadFolder: folder.value?.id, keepOriginal: true }); }, }, { text: i18n.ts.fromUrl, diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue index 5231a577d7..c228853bea 100644 --- a/packages/frontend/src/components/MkFolder.vue +++ b/packages/frontend/src/components/MkFolder.vue @@ -38,15 +38,26 @@ SPDX-License-Identifier: AGPL-3.0-only > <KeepAlive> <div v-show="opened"> - <MkSpacer v-if="withSpacer" :marginMin="spacerMin" :marginMax="spacerMax"> - <slot></slot> - </MkSpacer> - <div v-else> - <slot></slot> - </div> - <div v-if="$slots.footer" :class="$style.footer"> - <slot name="footer"></slot> - </div> + <MkStickyContainer> + <template #header> + <div v-if="$slots.header" :class="$style.inBodyHeader"> + <slot name="header"></slot> + </div> + </template> + + <MkSpacer v-if="withSpacer" :marginMin="spacerMin" :marginMax="spacerMax"> + <slot></slot> + </MkSpacer> + <div v-else> + <slot></slot> + </div> + + <template #footer> + <div v-if="$slots.footer" :class="$style.inBodyFooter"> + <slot name="footer"></slot> + </div> + </template> + </MkStickyContainer> </div> </KeepAlive> </Transition> @@ -230,14 +241,21 @@ onMounted(() => { &.bgSame { background: var(--MI_THEME-bg); + + .inBodyHeader { + background: color(from var(--MI_THEME-bg) srgb r g b / 0.75); + } } } -.footer { - position: sticky !important; - z-index: 1; - bottom: var(--MI-stickyBottom, 0px); - left: 0; +.inBodyHeader { + background: color(from var(--MI_THEME-panel) srgb r g b / 0.75); + -webkit-backdrop-filter: var(--MI-blur, blur(15px)); + backdrop-filter: var(--MI-blur, blur(15px)); + border-bottom: solid 0.5px var(--MI_THEME-divider); +} + +.inBodyFooter { padding: 12px; background: color(from var(--MI_THEME-bg) srgb r g b / 0.5); -webkit-backdrop-filter: var(--MI-blur, blur(15px)); diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index 8be4792cf8..c2fc967e1d 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -31,9 +31,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-for="item in (items2 ?? [])"> <div v-if="item.type === 'divider'" role="separator" tabindex="-1" :class="$style.divider"></div> - <span v-else-if="item.type === 'label'" role="menuitem" tabindex="-1" :class="[$style.label, $style.item]"> - <span style="opacity: 0.7;">{{ item.text }}</span> - </span> + <div v-else-if="item.type === 'label'" role="menuitem" tabindex="-1" :class="[$style.label]"> + <span>{{ item.text }}</span> + </div> <span v-else-if="item.type === 'pending'" role="menuitem" tabindex="0" :class="[$style.pending, $style.item]"> <span><MkEllipsis/></span> @@ -619,12 +619,6 @@ onBeforeUnmount(() => { --menuActiveBg: var(--MI_THEME-accentedBg); } - &.label { - pointer-events: none; - font-size: 0.7em; - padding-bottom: 4px; - } - &.pending { pointer-events: none; opacity: 0.7; @@ -694,6 +688,19 @@ onBeforeUnmount(() => { font-size: 12px; } +.label { + position: relative; + padding: 6px 16px; + box-sizing: border-box; + white-space: nowrap; + font-size: 0.7em; + text-align: left; + overflow: hidden; + text-overflow: ellipsis; + opacity: 0.7; + pointer-events: none; +} + .divider { margin: 8px 0; border-top: solid 0.5px var(--MI_THEME-divider); diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotes.vue index 6c52db004b..5edae908b0 100644 --- a/packages/frontend/src/components/MkNotes.vue +++ b/packages/frontend/src/components/MkNotes.vue @@ -14,7 +14,8 @@ SPDX-License-Identifier: AGPL-3.0-only <template #default="{ items: notes }"> <component - :is="prefer.s.animation ? TransitionGroup : 'div'" :class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap }]" + :is="prefer.s.animation ? TransitionGroup : 'div'" + :class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap, [$style.reverse]: pagination.reversed }]" :enterActiveClass="$style.transition_x_enterActive" :leaveActiveClass="$style.transition_x_leaveActive" :enterFromClass="$style.transition_x_enterFrom" @@ -22,14 +23,14 @@ SPDX-License-Identifier: AGPL-3.0-only :moveClass=" $style.transition_x_move" tag="div" > - <template v-for="note in notes" :key="note.id"> - <div v-if="note._shouldInsertAd_" :class="[$style.noteWithAd, { '_gaps': !noGap }]"> + <template v-for="(note, i) in notes" :key="note.id"> + <div v-if="note._shouldInsertAd_" :class="[$style.noteWithAd, { '_gaps': !noGap }]" :data-scroll-anchor="note.id"> <DynamicNote :class="$style.note" :note="note as Misskey.entities.Note" :withHardMute="true"/> <div :class="$style.ad"> <MkAd :preferForms="['horizontal', 'horizontal-big']"/> </div> </div> - <DynamicNote v-else :class="$style.note" :note="note as Misskey.entities.Note" :withHardMute="true"/> + <DynamicNote v-else :class="$style.note" :note="note as Misskey.entities.Note" :withHardMute="true" :data-scroll-anchor="note.id"/> </template> </component> </template> @@ -74,6 +75,11 @@ defineExpose({ position: absolute; } +.reverse { + display: flex; + flex-direction: column-reverse; +} + .root { container-type: inline-size; diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index a4591a35f7..5b2a8dfc5a 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -346,6 +346,7 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification) right: -2px; width: 20px; height: 20px; + line-height: 20px; box-sizing: border-box; border-radius: var(--MI-radius-full); background: var(--MI_THEME-panel); @@ -360,73 +361,61 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification) } .t_follow, .t_followRequestAccepted, .t_receiveFollowRequest { - padding: 3px; background: var(--eventFollow); pointer-events: none; } .t_renote { - padding: 3px; background: var(--eventRenote); pointer-events: none; } .t_quote { - padding: 3px; background: var(--eventRenote); pointer-events: none; } .t_reply { - padding: 3px; background: var(--eventReply); pointer-events: none; } .t_mention { - padding: 3px; background: var(--eventOther); pointer-events: none; } .t_pollEnded { - padding: 3px; background: var(--eventOther); pointer-events: none; } .t_achievementEarned { - padding: 3px; background: var(--eventAchievement); pointer-events: none; } .t_exportCompleted { - padding: 3px; background: var(--eventOther); pointer-events: none; } .t_roleAssigned { - padding: 3px; background: var(--eventOther); pointer-events: none; } .t_login { - padding: 3px; background: var(--eventLogin); pointer-events: none; } .t_createToken { - padding: 3px; background: var(--eventOther); pointer-events: none; } .t_chatRoomInvitationReceived { - padding: 3px; background: var(--eventOther); pointer-events: none; } diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue index 453a6c83b3..55ee054285 100644 --- a/packages/frontend/src/components/MkNotifications.vue +++ b/packages/frontend/src/components/MkNotifications.vue @@ -24,8 +24,8 @@ SPDX-License-Identifier: AGPL-3.0-only tag="div" > <template v-for="(notification, i) in notifications" :key="notification.id"> - <DynamicNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :class="$style.item" :note="notification.note" :withHardMute="true"/> - <XNotification v-else :class="$style.item" :notification="notification" :withTime="true" :full="true"/> + <DynamicNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :class="$style.item" :note="notification.note" :withHardMute="true" :data-scroll-anchor="notification.id"/> + <XNotification v-else :class="$style.item" :notification="notification" :withTime="true" :full="true" :data-scroll-anchor="notification.id"/> </template> </component> </template> diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 01bc80f397..c530effe4a 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -956,7 +956,7 @@ async function post(ev?: MouseEvent) { if (postAccount.value) { const storedAccounts = await getAccounts(); - token = storedAccounts.find(x => x.user.id === postAccount.value?.id)?.token; + token = storedAccounts.find(x => x.id === postAccount.value?.id)?.token; } posting.value = true; diff --git a/packages/frontend/src/components/MkHorizontalSwipe.vue b/packages/frontend/src/components/MkSwiper.vue index 1d0ffaea11..1d0ffaea11 100644 --- a/packages/frontend/src/components/MkHorizontalSwipe.vue +++ b/packages/frontend/src/components/MkSwiper.vue diff --git a/packages/frontend/src/components/MkTabs.vue b/packages/frontend/src/components/MkTabs.vue new file mode 100644 index 0000000000..a1f30100d0 --- /dev/null +++ b/packages/frontend/src/components/MkTabs.vue @@ -0,0 +1,235 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div :class="$style.tabs"> + <div :class="$style.tabsInner"> + <button + v-for="t in tabs" :ref="(el) => tabRefs[t.key] = (el as HTMLElement)" v-tooltip.noDelay="t.title" + class="_button" :class="[$style.tab, { [$style.active]: t.key != null && t.key === props.tab, [$style.animate]: prefer.s.animation }]" + @mousedown="(ev) => onTabMousedown(t, ev)" @click="(ev) => onTabClick(t, ev)" + > + <div :class="$style.tabInner"> + <i v-if="t.icon" :class="[$style.tabIcon, t.icon]"></i> + <div + v-if="!t.iconOnly || (!prefer.s.animation && t.key === tab)" + :class="$style.tabTitle" + > + {{ t.title }} + </div> + <Transition + v-else mode="in-out" @enter="enter" @afterEnter="afterEnter" @leave="leave" + @afterLeave="afterLeave" + > + <div v-show="t.key === tab" :class="[$style.tabTitle, $style.animate]">{{ t.title }}</div> + </Transition> + </div> + </button> + </div> + <div + ref="tabHighlightEl" + :class="[$style.tabHighlight, { [$style.animate]: prefer.s.animation }]" + ></div> +</div> +</template> + +<script lang="ts"> +export type Tab = { + key: string; + onClick?: (ev: MouseEvent) => void; +} & ( + | { + iconOnly?: false; + title: string; + icon?: string; + } + | { + iconOnly: true; + icon: string; + } +); +</script> + +<script lang="ts" setup> +import { nextTick, onMounted, onUnmounted, useTemplateRef, watch } from 'vue'; +import { prefer } from '@/preferences.js'; + +const props = withDefaults(defineProps<{ + tabs?: Tab[]; + tab?: string; +}>(), { + tabs: () => ([] as Tab[]), +}); + +const emit = defineEmits<{ + (ev: 'update:tab', key: string); + (ev: 'tabClick', key: string); +}>(); + +const tabHighlightEl = useTemplateRef('tabHighlightEl'); +const tabRefs: Record<string, HTMLElement | null> = {}; + +function onTabMousedown(tab: Tab, ev: MouseEvent): void { + // ユーザビリティの観点からmousedown時にはonClickは呼ばない + if (tab.key) { + emit('update:tab', tab.key); + } +} + +function onTabClick(t: Tab, ev: MouseEvent): void { + emit('tabClick', t.key); + + if (t.onClick) { + ev.preventDefault(); + ev.stopPropagation(); + t.onClick(ev); + } + + if (t.key) { + emit('update:tab', t.key); + } +} + +function renderTab() { + const tabEl = props.tab ? tabRefs[props.tab] : undefined; + if (tabEl && tabHighlightEl.value && tabHighlightEl.value.parentElement) { + // offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある + // https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4 + const parentRect = tabHighlightEl.value.parentElement.getBoundingClientRect(); + const rect = tabEl.getBoundingClientRect(); + tabHighlightEl.value.style.width = rect.width + 'px'; + tabHighlightEl.value.style.left = (rect.left - parentRect.left + tabHighlightEl.value.parentElement.scrollLeft) + 'px'; + } +} + +let entering = false; + +async function enter(el: Element) { + if (!(el instanceof HTMLElement)) return; + entering = true; + const elementWidth = el.getBoundingClientRect().width; + el.style.width = '0'; + el.style.paddingLeft = '0'; + el.offsetWidth; // reflow + el.style.width = `${elementWidth}px`; + el.style.paddingLeft = ''; + nextTick(() => { + entering = false; + }); + + window.setTimeout(renderTab, 170); +} + +function afterEnter(el: Element) { + if (!(el instanceof HTMLElement)) return; + // element.style.width = ''; +} + +async function leave(el: Element) { + if (!(el instanceof HTMLElement)) return; + const elementWidth = el.getBoundingClientRect().width; + el.style.width = `${elementWidth}px`; + el.style.paddingLeft = ''; + el.offsetWidth; // reflow + el.style.width = '0'; + el.style.paddingLeft = '0'; +} + +function afterLeave(el: Element) { + if (!(el instanceof HTMLElement)) return; + el.style.width = ''; +} + +onMounted(() => { + watch([() => props.tab, () => props.tabs], () => { + nextTick(() => { + if (entering) return; + renderTab(); + }); + }, { + immediate: true, + }); +}); + +onUnmounted(() => { +}); +</script> + +<style lang="scss" module> +.tabs { + --height: 40px; + + display: block; + position: relative; + margin: 0; + height: var(--height); + font-size: 85%; + overflow-x: auto; + overflow-y: hidden; + scrollbar-width: none; + + &::-webkit-scrollbar { + display: none; + } +} + +.tabsInner { + display: inline-block; + height: var(--height); + white-space: nowrap; +} + +.tab { + display: inline-block; + position: relative; + padding: 0 10px; + height: 100%; + font-weight: normal; + opacity: 0.7; + + &:hover { + opacity: 1; + } + + &.active { + opacity: 1; + } + + &.animate { + transition: opacity 0.2s ease; + } +} + +.tabInner { + display: flex; + align-items: center; +} + +.tabIcon + .tabTitle { + padding-left: 4px; +} + +.tabTitle { + overflow: hidden; + + &.animate { + transition: width .15s linear, padding-left .15s linear; + } +} + +.tabHighlight { + position: absolute; + bottom: 0; + height: 3px; + background: var(--MI_THEME-accent); + border-radius: 999px; + transition: none; + pointer-events: none; + + &.animate { + transition: width 0.15s ease, left 0.15s ease; + } +} +</style> diff --git a/packages/frontend/src/components/MkThemePreview.vue b/packages/frontend/src/components/MkThemePreview.vue index 013ab9d6a4..cc4254a2f6 100644 --- a/packages/frontend/src/components/MkThemePreview.vue +++ b/packages/frontend/src/components/MkThemePreview.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <g fill-rule="evenodd"> <rect width="200" height="150" :fill="themeVariables.bg"/> <rect width="64" height="150" :fill="themeVariables.navBg"/> - <rect x="64" width="136" height="41" :fill="themeVariables.bg"/> + <rect x="64" width="136" height="41" :fill="themeVariables.pageHeaderBg"/> <path transform="scale(.26458)" d="m439.77 247.19c-43.673 0-78.832 35.157-78.832 78.83v249.98h407.06v-328.81z" :fill="themeVariables.panel"/> </g> <circle cx="32" cy="83" r="21" :fill="themeVariables.accentedBg"/> @@ -62,6 +62,7 @@ const themeVariables = ref<{ accent: string; accentedBg: string; navBg: string; + pageHeaderBg: string; success: string; warn: string; error: string; @@ -76,6 +77,7 @@ const themeVariables = ref<{ accent: 'var(--MI_THEME-accent)', accentedBg: 'var(--MI_THEME-accentedBg)', navBg: 'var(--MI_THEME-navBg)', + pageHeaderBg: 'var(--MI_THEME-pageHeaderBg)', success: 'var(--MI_THEME-success)', warn: 'var(--MI_THEME-warn)', error: 'var(--MI_THEME-error)', @@ -104,6 +106,7 @@ watch(() => props.theme, (theme) => { accent: compiled.accent ?? 'var(--MI_THEME-accent)', accentedBg: compiled.accentedBg ?? 'var(--MI_THEME-accentedBg)', navBg: compiled.navBg ?? 'var(--MI_THEME-navBg)', + pageHeaderBg: compiled.pageHeaderBg ?? 'var(--MI_THEME-pageHeaderBg)', success: compiled.success ?? 'var(--MI_THEME-success)', warn: compiled.warn ?? 'var(--MI_THEME-warn)', error: compiled.error ?? 'var(--MI_THEME-error)', diff --git a/packages/frontend/src/components/MkTl.vue b/packages/frontend/src/components/MkTl.vue new file mode 100644 index 0000000000..95cc4d2a2a --- /dev/null +++ b/packages/frontend/src/components/MkTl.vue @@ -0,0 +1,173 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div :class="$style.items"> + <template v-for="(item, i) in items" :key="item.id"> + <div :class="$style.left"> + <slot v-if="item.type === 'event'" name="left" :event="item.data" :timestamp="item.timestamp" :delta="item.delta"></slot> + </div> + <div :class="[$style.center, item.type === 'date' ? $style.date : '']"> + <div :class="$style.centerLine"></div> + <div :class="$style.centerPoint"></div> + </div> + <div :class="$style.right"> + <slot v-if="item.type === 'event'" name="right" :event="item.data" :timestamp="item.timestamp" :delta="item.delta"></slot> + <div v-else :class="$style.dateLabel"><i class="ti ti-chevron-up"></i> {{ item.prevText }}</div> + </div> + </template> +</div> +</template> + +<script lang="ts" setup> +import { computed } from 'vue'; + +const props = defineProps<{ + events: { + id: string; + timestamp: number; + data: any; + }[]; +}>(); + +const events = computed(() => { + return props.events.toSorted((a, b) => b.timestamp - a.timestamp); +}); + +function getDateText(dateInstance: Date) { + const year = dateInstance.getFullYear(); + const month = dateInstance.getMonth() + 1; + const date = dateInstance.getDate(); + const hour = dateInstance.getHours(); + return `${year.toString()}/${month.toString()}/${date.toString()} ${hour.toString().padStart(2, '0')}:00:00`; +} + +const items = computed<({ + id: string; + type: 'event'; + timestamp: number; + delta: number; + data: any; +} | { + id: string; + type: 'date'; + prev: Date; + prevText: string; + next: Date | null; + nextText: string; +})[]>(() => { + const results = []; + for (let i = 0; i < events.value.length; i++) { + const item = events.value[i]; + + const date = new Date(item.timestamp); + const nextDate = events.value[i + 1] ? new Date(events.value[i + 1].timestamp) : null; + + results.push({ + id: item.id, + type: 'event', + timestamp: item.timestamp, + delta: i === events.value.length - 1 ? 0 : item.timestamp - events.value[i + 1].timestamp, + data: item.data, + }); + + if ( + i !== events.value.length - 1 && + nextDate != null && ( + date.getFullYear() !== nextDate.getFullYear() || + date.getMonth() !== nextDate.getMonth() || + date.getDate() !== nextDate.getDate() || + date.getHours() !== nextDate.getHours() + ) + ) { + results.push({ + id: `date-${item.id}`, + type: 'date', + prev: date, + prevText: getDateText(date), + next: nextDate, + nextText: getDateText(nextDate), + }); + } + } + return results; + }); +</script> + +<style lang="scss" module> +.root { + +} + +.items { + display: grid; + grid-template-columns: max-content 18px 1fr; + gap: 0 8px; +} + +.item { +} + +.center { + position: relative; + + &.date { + .centerPoint::before { + position: absolute; + content: ""; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: auto; + width: 7px; + height: 7px; + background: var(--MI_THEME-bg); + border-radius: 50%; + } + } +} + +.centerLine { + position: absolute; + top: 0; + left: 0; + right: 0; + margin: auto; + width: 3px; + height: 100%; + background: color-mix(in srgb, var(--MI_THEME-accent), var(--MI_THEME-bg) 75%); +} +.centerPoint { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: auto; + width: 13px; + height: 13px; + background: color-mix(in srgb, var(--MI_THEME-accent), var(--MI_THEME-bg) 75%); + border-radius: 50%; +} + +.left { + min-width: 0; + align-self: center; + justify-self: right; +} + +.right { + min-width: 0; + align-self: center; +} + +.dateLabel { + opacity: 0.7; + font-size: 90%; + padding: 4px; + margin: 8px 0; +} +</style> diff --git a/packages/frontend/src/components/SkUserRecentNotes.vue b/packages/frontend/src/components/SkUserRecentNotes.vue index fd67464234..b66a33f644 100644 --- a/packages/frontend/src/components/SkUserRecentNotes.vue +++ b/packages/frontend/src/components/SkUserRecentNotes.vue @@ -67,7 +67,7 @@ async function reload(): Promise<void> { // An additional request is needed to "upgrade" the object. misskeyApi('users/show', { userId: props.userId }), - // Wait for 1 second to match the animation effects in MkHorizontalSwipe, MkPullToRefresh, and MkPagination. + // Wait for 1 second to match the animation effects in MkSwiper, MkPullToRefresh, and MkPagination. // Otherwise, the page appears to load "backwards". new Promise(resolve => window.setTimeout(resolve, 1000)), ]) diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index da518829e2..42bb49e8d9 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -140,11 +140,18 @@ onUnmounted(() => { <style lang="scss" module> .root { - background: color(from var(--MI_THEME-bg) srgb r g b / 0.75); + background: color(from var(--MI_THEME-pageHeaderBg) srgb r g b / 0.75); -webkit-backdrop-filter: var(--MI-blur, blur(15px)); backdrop-filter: var(--MI-blur, blur(15px)); - border-bottom: solid 0.5px var(--MI_THEME-divider); + border-bottom: solid 0.5px transparent; width: 100%; + color: var(--MI_THEME-pageHeaderFg); +} + +@container style(--MI_THEME-pageHeaderBg: var(--MI_THEME-bg)) { + .root { + border-bottom: solid 0.5px var(--MI_THEME-divider); + } } .upper, diff --git a/packages/frontend/src/components/global/PageWithHeader.vue b/packages/frontend/src/components/global/PageWithHeader.vue index cd289d04fb..85e61fd532 100644 --- a/packages/frontend/src/components/global/PageWithHeader.vue +++ b/packages/frontend/src/components/global/PageWithHeader.vue @@ -20,6 +20,7 @@ import { useTemplateRef } from 'vue'; import { scrollInContainer } from '@@/js/scroll.js'; import type { PageHeaderItem } from '@/types/page-header.js'; import type { Tab } from './MkPageHeader.tabs.vue'; +import { useScrollPositionKeeper } from '@/use/use-scroll-position-keeper.js'; const props = withDefaults(defineProps<{ tabs?: Tab[]; @@ -36,6 +37,8 @@ const props = withDefaults(defineProps<{ const tab = defineModel<string>('tab'); const rootEl = useTemplateRef('rootEl'); +useScrollPositionKeeper(rootEl); + defineExpose({ scrollToTop: () => { if (rootEl.value) scrollInContainer(rootEl.value, { top: 0, behavior: 'smooth' }); diff --git a/packages/frontend/src/deck.ts b/packages/frontend/src/deck.ts index 269a763ce7..cd6fc9bb5d 100644 --- a/packages/frontend/src/deck.ts +++ b/packages/frontend/src/deck.ts @@ -39,6 +39,7 @@ export const columnTypes = [ 'mentions', 'direct', 'roleTimeline', + 'chat', 'following', ] as const; diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index 1dee8c30c7..d955857e20 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <MkSpacer v-if="tab === 'overview'" :contentMax="600" :marginMin="20"> <XOverview/> </MkSpacer> @@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSpacer v-else-if="tab === 'charts'" :contentMax="1000" :marginMin="20"> <MkInstanceStats/> </MkSpacer> - </MkHorizontalSwipe> + </MkSwiper> </PageWithHeader> </template> @@ -28,7 +28,7 @@ import { instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { claimAchievement } from '@/utility/achievements.js'; import { definePage } from '@/page.js'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; const XOverview = defineAsyncComponent(() => import('@/pages/about.overview.vue')); const XEmojis = defineAsyncComponent(() => import('@/pages/about.emojis.vue')); diff --git a/packages/frontend/src/pages/admin/_header_.vue b/packages/frontend/src/pages/admin/_header_.vue deleted file mode 100644 index fa16f8892c..0000000000 --- a/packages/frontend/src/pages/admin/_header_.vue +++ /dev/null @@ -1,296 +0,0 @@ -<!-- -SPDX-FileCopyrightText: syuilo and misskey-project -SPDX-License-Identifier: AGPL-3.0-only ---> - -<template> -<div ref="el" class="fdidabkc" :style="{ background: bg }" @click="onClick"> - <template v-if="pageMetadata"> - <div class="titleContainer" @click="showTabsPopup"> - <i v-if="pageMetadata.icon" class="icon" :class="pageMetadata.icon"></i> - - <div class="title"> - <div class="title">{{ pageMetadata.title }}</div> - </div> - </div> - <div class="tabs"> - <button v-for="tab in tabs" :ref="(el) => tabRefs[tab.key] = el" v-tooltip.noDelay="tab.title" class="tab _button" :class="{ active: tab.key != null && tab.key === props.tab }" @mousedown="(ev) => onTabMousedown(tab, ev)" @click="(ev) => onTabClick(tab, ev)"> - <i v-if="tab.icon" class="icon" :class="tab.icon"></i> - <span v-if="!tab.iconOnly" class="title">{{ tab.title }}</span> - </button> - <div ref="tabHighlightEl" class="highlight"></div> - </div> - </template> - <div class="buttons right"> - <template v-if="actions"> - <template v-for="action in actions"> - <MkButton v-if="action.asFullButton" class="fullButton" primary :disabled="action.disabled" @click.stop="action.handler"><i :class="action.icon" style="margin-right: 6px;"></i>{{ action.text }}</MkButton> - <button v-else v-tooltip.noDelay="action.text" class="_button button" :class="{ highlighted: action.highlighted }" :disabled="action.disabled" @click.stop="action.handler" @touchstart="preventDrag"><i :class="action.icon"></i></button> - </template> - </template> - </div> -</div> -</template> - -<script lang="ts" setup> -import { computed, onMounted, onUnmounted, ref, useTemplateRef, watch, nextTick, inject } from 'vue'; -import tinycolor from 'tinycolor2'; -import { scrollToTop } from '@@/js/scroll.js'; -import { popupMenu } from '@/os.js'; -import MkButton from '@/components/MkButton.vue'; -import { globalEvents } from '@/events.js'; -import { DI } from '@/di.js'; - -type Tab = { - key?: string | null; - title: string; - icon?: string; - iconOnly?: boolean; - onClick?: (ev: MouseEvent) => void; -}; - -const props = defineProps<{ - tabs?: Tab[]; - tab?: string; - actions?: { - text: string; - icon: string; - asFullButton?: boolean; - disabled?: boolean; - handler: (ev: MouseEvent) => void; - }[]; - thin?: boolean; -}>(); - -const emit = defineEmits<{ - (ev: 'update:tab', key: string); -}>(); - -const pageMetadata = inject(DI.pageMetadata, ref(null)); - -const el = useTemplateRef('el'); -const tabHighlightEl = useTemplateRef('tabHighlightEl'); -const tabRefs = {}; -const bg = ref<string | null>(null); -const height = ref(0); -const hasTabs = computed(() => { - return props.tabs && props.tabs.length > 0; -}); - -const showTabsPopup = (ev: MouseEvent) => { - if (!hasTabs.value) return; - ev.preventDefault(); - ev.stopPropagation(); - const menu = props.tabs.map(tab => ({ - text: tab.title, - icon: tab.icon, - active: tab.key != null && tab.key === props.tab, - action: (ev) => { - onTabClick(tab, ev); - }, - })); - popupMenu(menu, ev.currentTarget ?? ev.target); -}; - -const preventDrag = (ev: TouchEvent) => { - ev.stopPropagation(); -}; - -const onClick = () => { - scrollToTop(el.value, { behavior: 'smooth' }); -}; - -function onTabMousedown(tab: Tab, ev: MouseEvent): void { - // ユーザビリティの観点からmousedown時にはonClickは呼ばない - if (tab.key) { - emit('update:tab', tab.key); - } -} - -function onTabClick(tab: Tab, ev: MouseEvent): void { - if (tab.onClick) { - ev.preventDefault(); - ev.stopPropagation(); - tab.onClick(ev); - } - if (tab.key) { - emit('update:tab', tab.key); - } -} - -const calcBg = () => { - const rawBg = pageMetadata.value.bg ?? 'var(--MI_THEME-bg)'; - const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(window.document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); - tinyBg.setAlpha(0.85); - bg.value = tinyBg.toRgbString(); -}; - -onMounted(() => { - calcBg(); - globalEvents.on('themeChanging', calcBg); - - watch(() => [props.tab, props.tabs], () => { - nextTick(() => { - const tabEl = tabRefs[props.tab]; - if (tabEl && tabHighlightEl.value) { - // offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある - // https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4 - const parentRect = tabEl.parentElement.getBoundingClientRect(); - const rect = tabEl.getBoundingClientRect(); - tabHighlightEl.value.style.width = rect.width + 'px'; - tabHighlightEl.value.style.left = (rect.left - parentRect.left) + 'px'; - } - }); - }, { - immediate: true, - }); -}); - -onUnmounted(() => { - globalEvents.off('themeChanging', calcBg); -}); -</script> - -<style lang="scss" scoped> -.fdidabkc { - --height: 60px; - display: flex; - width: 100%; - -webkit-backdrop-filter: var(--MI-blur, blur(15px)); - backdrop-filter: var(--MI-blur, blur(15px)); - - > .buttons { - --margin: 8px; - display: flex; - align-items: center; - height: var(--height); - margin: 0 var(--margin); - - &.right { - margin-left: auto; - } - - &:empty { - width: var(--height); - } - - > .button { - display: flex; - align-items: center; - justify-content: center; - height: calc(var(--height) - (var(--margin) * 2)); - width: calc(var(--height) - (var(--margin) * 2)); - box-sizing: border-box; - position: relative; - border-radius: var(--MI-radius-xs); - - &:hover { - background: rgba(0, 0, 0, 0.05); - } - - &.highlighted { - color: var(--MI_THEME-accent); - } - } - - > .fullButton { - & + .fullButton { - margin-left: 12px; - } - } - } - - > .titleContainer { - display: flex; - align-items: center; - max-width: 400px; - overflow: auto; - white-space: nowrap; - text-align: left; - font-weight: bold; - flex-shrink: 0; - margin-left: 24px; - - > .avatar { - $size: 32px; - display: inline-block; - width: $size; - height: $size; - vertical-align: bottom; - margin: 0 8px; - pointer-events: none; - } - - > .icon { - margin-right: 8px; - width: 16px; - text-align: center; - } - - > .title { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - line-height: 1.1; - - > .subtitle { - opacity: 0.6; - font-size: 0.8em; - font-weight: normal; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - - &.activeTab { - text-align: center; - - > .chevron { - display: inline-block; - margin-left: 6px; - } - } - } - } - } - - > .tabs { - position: relative; - margin-left: 16px; - font-size: 0.8em; - overflow: auto; - white-space: nowrap; - - > .tab { - display: inline-block; - position: relative; - padding: 0 10px; - height: 100%; - font-weight: normal; - opacity: 0.7; - - &:hover { - opacity: 1; - } - - &.active { - opacity: 1; - } - - > .icon + .title { - margin-left: 8px; - } - } - - > .highlight { - position: absolute; - bottom: 0; - height: 3px; - background: var(--MI_THEME-accent); - border-radius: var(--MI-radius-ellipse); - transition: all 0.2s ease; - pointer-events: none; - } - } -} -</style> diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue index ee87fae606..a569ab7c33 100644 --- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue +++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue @@ -4,11 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header> - <XHeader :actions="headerActions" :tabs="headerTabs"/> - </template> - +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="900"> <div :class="$style.root" class="_gaps_m"> <div :class="$style.addButton"> @@ -41,14 +37,13 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script setup lang="ts"> import { entities } from 'misskey-js'; import { computed, defineAsyncComponent, onMounted, ref } from 'vue'; import XRecipient from './notification-recipient.item.vue'; -import XHeader from '@/pages/admin/_header_.vue'; import { misskeyApi } from '@/utility/misskey-api.js'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue index 67d54a85ad..3f1df95f25 100644 --- a/packages/frontend/src/pages/admin/abuses.vue +++ b/packages/frontend/src/pages/admin/abuses.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="900"> <div :class="$style.root" class="_gaps"> <div :class="$style.subMenus" class="_gaps"> @@ -55,12 +54,11 @@ SPDX-License-Identifier: AGPL-3.0-only </MkPagination> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { computed, useTemplateRef, ref } from 'vue'; -import XHeader from './_header_.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkPagination from '@/components/MkPagination.vue'; import XAbuseReport from '@/components/MkAbuseReport.vue'; diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue index ebc3d23296..aa8ba2f7c3 100644 --- a/packages/frontend/src/pages/admin/ads.vue +++ b/packages/frontend/src/pages/admin/ads.vue @@ -4,10 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header> - <XHeader :actions="headerActions" :tabs="headerTabs"/> - </template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="900"> <MkSelect v-model="filterType" :class="$style.input" @update:modelValue="filterItems"> <template #label>{{ i18n.ts.state }}</template> @@ -81,13 +78,12 @@ SPDX-License-Identifier: AGPL-3.0-only </MkButton> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; import * as Misskey from 'misskey-js'; -import XHeader from './_header_.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkTextarea from '@/components/MkTextarea.vue'; diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue index f6b331455f..ea7f0cc73d 100644 --- a/packages/frontend/src/pages/admin/announcements.vue +++ b/packages/frontend/src/pages/admin/announcements.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="900"> <div class="_gaps"> <MkInfo>{{ i18n.ts._announcement.shouldNotBeUsedToPresentPermanentInfo }}</MkInfo> @@ -81,12 +80,11 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed, watch } from 'vue'; -import XHeader from './_header_.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; diff --git a/packages/frontend/src/pages/admin/approvals.vue b/packages/frontend/src/pages/admin/approvals.vue index 521346bd98..c972728c85 100644 --- a/packages/frontend/src/pages/admin/approvals.vue +++ b/packages/frontend/src/pages/admin/approvals.vue @@ -5,8 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div> - <MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> + <PageWithHeader :tabs="headerTabs"> <MkSpacer :contentMax="900"> <div class="_gaps_m"> <MkPagination ref="paginationComponent" :pagination="pagination" :displayLimit="50"> @@ -18,13 +17,12 @@ SPDX-License-Identifier: AGPL-3.0-only </MkPagination> </div> </MkSpacer> - </MkStickyContainer> + </PageWithHeader> </div> </template> <script lang="ts" setup> import { computed, useTemplateRef } from 'vue'; -import XHeader from './_header_.vue'; import MkPagination from '@/components/MkPagination.vue'; import SkApprovalUser from '@/components/SkApprovalUser.vue'; import { i18n } from '@/i18n.js'; diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue index 84d8ea85da..1afc643a0a 100644 --- a/packages/frontend/src/pages/admin/branding.vue +++ b/packages/frontend/src/pages/admin/branding.vue @@ -4,125 +4,122 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :tabs="headerTabs"/></template> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> - <FormSuspense :p="init"> - <div class="_gaps_m"> - <MkInput v-model="iconUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts._serverSettings.iconUrl }}</template> - </MkInput> +<PageWithHeader :tabs="headerTabs"> + <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <FormSuspense :p="init"> + <div class="_gaps_m"> + <MkInput v-model="iconUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts._serverSettings.iconUrl }}</template> + </MkInput> - <MkInput v-model="app192IconUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/192px)</template> - <template #caption> - <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div> - <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div> - <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div> - <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '192x192px' }) }}</strong></div> - </template> - </MkInput> + <MkInput v-model="app192IconUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/192px)</template> + <template #caption> + <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div> + <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div> + <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div> + <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '192x192px' }) }}</strong></div> + </template> + </MkInput> - <MkInput v-model="app512IconUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/512px)</template> - <template #caption> - <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div> - <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div> - <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div> - <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '512x512px' }) }}</strong></div> - </template> - </MkInput> + <MkInput v-model="app512IconUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/512px)</template> + <template #caption> + <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div> + <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div> + <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div> + <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '512x512px' }) }}</strong></div> + </template> + </MkInput> - <MkInput v-model="sidebarLogoUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts._serverSettings.sidebarLogoUrl }}</template> - <template #caption> - <div>{{ i18n.ts._serverSettings.sidebarLogoDescription }}</div> - <div>({{ i18n.ts._serverSettings.sidebarLogoUsageExample }})</div> - </template> - </MkInput> + <MkInput v-model="sidebarLogoUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts._serverSettings.sidebarLogoUrl }}</template> + <template #caption> + <div>{{ i18n.ts._serverSettings.sidebarLogoDescription }}</div> + <div>({{ i18n.ts._serverSettings.sidebarLogoUsageExample }})</div> + </template> + </MkInput> - <MkInput v-model="bannerUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.bannerUrl }}</template> - </MkInput> + <MkInput v-model="bannerUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.bannerUrl }}</template> + </MkInput> - <MkInput v-model="backgroundImageUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.backgroundImageUrl }}</template> - </MkInput> + <MkInput v-model="backgroundImageUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.backgroundImageUrl }}</template> + </MkInput> - <FromSlot> - <template #label>{{ i18n.ts.defaultLike }}</template> - <MkCustomEmoji v-if="defaultLike.startsWith(':')" style="max-height: 3em; font-size: 1.1em;" :useOriginalSize="false" :name="defaultLike" :normal="true" :noStyle="true"/> - <MkEmoji v-else :emoji="defaultLike" style="max-height: 3em; font-size: 1.1em;" :normal="true" :noStyle="true"/> - <MkButton rounded :small="true" @click="chooseNewLike"><i class="ph-smiley ph-bold ph-lg"></i> Change</MkButton> - </FromSlot> + <FromSlot> + <template #label>{{ i18n.ts.defaultLike }}</template> + <MkCustomEmoji v-if="defaultLike.startsWith(':')" style="max-height: 3em; font-size: 1.1em;" :useOriginalSize="false" :name="defaultLike" :normal="true" :noStyle="true"/> + <MkEmoji v-else :emoji="defaultLike" style="max-height: 3em; font-size: 1.1em;" :normal="true" :noStyle="true"/> + <MkButton rounded :small="true" @click="chooseNewLike"><i class="ph-smiley ph-bold ph-lg"></i> Change</MkButton> + </FromSlot> - <MkInput v-model="notFoundImageUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.notFoundDescription }}</template> - </MkInput> + <MkInput v-model="notFoundImageUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.notFoundDescription }}</template> + </MkInput> - <MkInput v-model="infoImageUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.nothing }}</template> - </MkInput> + <MkInput v-model="infoImageUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.nothing }}</template> + </MkInput> - <MkInput v-model="serverErrorImageUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.somethingHappened }}</template> - </MkInput> + <MkInput v-model="serverErrorImageUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.somethingHappened }}</template> + </MkInput> - <MkColorInput v-model="themeColor"> - <template #label>{{ i18n.ts.themeColor }}</template> - </MkColorInput> + <MkColorInput v-model="themeColor"> + <template #label>{{ i18n.ts.themeColor }}</template> + </MkColorInput> - <MkTextarea v-model="defaultLightTheme"> - <template #label>{{ i18n.ts.instanceDefaultLightTheme }}</template> - <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> - </MkTextarea> + <MkTextarea v-model="defaultLightTheme"> + <template #label>{{ i18n.ts.instanceDefaultLightTheme }}</template> + <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> + </MkTextarea> - <MkTextarea v-model="defaultDarkTheme"> - <template #label>{{ i18n.ts.instanceDefaultDarkTheme }}</template> - <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> - </MkTextarea> + <MkTextarea v-model="defaultDarkTheme"> + <template #label>{{ i18n.ts.instanceDefaultDarkTheme }}</template> + <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> + </MkTextarea> - <MkInput v-model="repositoryUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.repositoryUrl }}</template> - </MkInput> + <MkInput v-model="repositoryUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.repositoryUrl }}</template> + </MkInput> - <MkInput v-model="feedbackUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.feedbackUrl }}</template> - </MkInput> + <MkInput v-model="feedbackUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.feedbackUrl }}</template> + </MkInput> - <MkTextarea v-model="manifestJsonOverride"> - <template #label>{{ i18n.ts._serverSettings.manifestJsonOverride }}</template> - </MkTextarea> - </div> - </FormSuspense> - </MkSpacer> - <template #footer> - <div :class="$style.footer"> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="16"> - <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> - </MkSpacer> + <MkTextarea v-model="manifestJsonOverride"> + <template #label>{{ i18n.ts._serverSettings.manifestJsonOverride }}</template> + </MkTextarea> </div> - </template> - </MkStickyContainer> -</div> + </FormSuspense> + </MkSpacer> + <template #footer> + <div :class="$style.footer"> + <MkSpacer :contentMax="700" :marginMin="16" :marginMax="16"> + <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> + </MkSpacer> + </div> + </template> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; import JSON5 from 'json5'; -import XHeader from './_header_.vue'; +import { host } from '@@/js/config.js'; import MkInput from '@/components/MkInput.vue'; import MkTextarea from '@/components/MkTextarea.vue'; import FromSlot from '@/components/form/slot.vue'; @@ -134,7 +131,6 @@ import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import MkButton from '@/components/MkButton.vue'; import MkColorInput from '@/components/MkColorInput.vue'; -import { host } from '@@/js/config.js'; const iconUrl = ref<string | null>(null); const sidebarLogoUrl = ref<string | null>(null); diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue index d018977bda..f1827d756b 100644 --- a/packages/frontend/src/pages/admin/email-settings.vue +++ b/packages/frontend/src/pages/admin/email-settings.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :tabs="headerTabs"/></template> +<PageWithHeader :tabs="headerTabs"> <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> <FormSuspense :p="init"> <div class="_gaps_m"> @@ -60,12 +59,11 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSpacer> </div> </template> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; -import XHeader from './_header_.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkInput from '@/components/MkInput.vue'; import FormInfo from '@/components/MkInfo.vue'; diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue index f9dd5dc568..131989844a 100644 --- a/packages/frontend/src/pages/admin/external-services.vue +++ b/packages/frontend/src/pages/admin/external-services.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> <FormSuspense :p="init"> <div class="_gaps_m"> @@ -54,12 +53,11 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </FormSuspense> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; -import XHeader from './_header_.vue'; import MkInput from '@/components/MkInput.vue'; import MkButton from '@/components/MkButton.vue'; import MkSwitch from '@/components/MkSwitch.vue'; diff --git a/packages/frontend/src/pages/admin/queue.chart.chart.vue b/packages/frontend/src/pages/admin/federation-job-queue.chart.chart.vue index 5dd2887024..5dd2887024 100644 --- a/packages/frontend/src/pages/admin/queue.chart.chart.vue +++ b/packages/frontend/src/pages/admin/federation-job-queue.chart.chart.vue diff --git a/packages/frontend/src/pages/admin/queue.chart.vue b/packages/frontend/src/pages/admin/federation-job-queue.chart.vue index 1ba02d6e0e..4b10d682a5 100644 --- a/packages/frontend/src/pages/admin/queue.chart.vue +++ b/packages/frontend/src/pages/admin/federation-job-queue.chart.vue @@ -50,8 +50,8 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { markRaw, onMounted, onUnmounted, ref, useTemplateRef } from 'vue'; import * as Misskey from 'misskey-js'; -import XChart from './queue.chart.chart.vue'; -import type { ApQueueDomain } from '@/pages/admin/queue.vue'; +import XChart from './federation-job-queue.chart.chart.vue'; +import type { ApQueueDomain } from '@/pages/admin/federation-job-queue.vue'; import number from '@/filters/number.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { useStream } from '@/stream.js'; diff --git a/packages/frontend/src/pages/admin/queue.vue b/packages/frontend/src/pages/admin/federation-job-queue.vue index b5aee1e51e..77e460d0eb 100644 --- a/packages/frontend/src/pages/admin/queue.vue +++ b/packages/frontend/src/pages/admin/federation-job-queue.vue @@ -4,22 +4,22 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="800"> <XQueue v-if="tab === 'deliver'" domain="deliver"/> <XQueue v-else-if="tab === 'inbox'" domain="inbox"/> <br> - <MkButton @click="promoteAllQueues"><i class="ti ti-reload"></i> {{ i18n.ts.retryAllQueuesNow }}</MkButton> + <div class="_buttons"> + <MkButton @click="promoteAllQueues"><i class="ti ti-reload"></i> {{ i18n.ts.retryAllQueuesNow }}</MkButton> + <MkButton danger @click="clear"><i class="ti ti-trash"></i> {{ i18n.ts.clearQueue }}</MkButton> + </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; -import * as config from '@@/js/config.js'; -import XQueue from './queue.chart.vue'; -import XHeader from './_header_.vue'; +import XQueue from './federation-job-queue.chart.vue'; import type { Ref } from 'vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; @@ -38,7 +38,7 @@ function clear() { }).then(({ canceled }) => { if (canceled) return; - os.apiWithDialog('admin/queue/clear'); + os.apiWithDialog('admin/queue/clear', { queue: tab.value, state: '*' }); }); } @@ -50,7 +50,7 @@ function promoteAllQueues() { }).then(({ canceled }) => { if (canceled) return; - os.apiWithDialog('admin/queue/promote', { type: tab.value }); + os.apiWithDialog('admin/queue/promote-jobs', { queue: tab.value }); }); } @@ -65,7 +65,7 @@ const headerTabs = computed(() => [{ }]); definePage(() => ({ - title: i18n.ts.jobQueue, + title: i18n.ts.federationJobs, icon: 'ti ti-clock-play', })); </script> diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue index 6c8979c5b5..dcc8c52e55 100644 --- a/packages/frontend/src/pages/admin/federation.vue +++ b/packages/frontend/src/pages/admin/federation.vue @@ -4,65 +4,61 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :actions="headerActions"/></template> - <MkSpacer :contentMax="900"> - <div class="_gaps"> - <div> - <MkInput v-model="host" :debounce="true" class=""> - <template #prefix><i class="ti ti-search"></i></template> - <template #label>{{ i18n.ts.host }}</template> - </MkInput> - <FormSplit style="margin-top: var(--MI-margin);"> - <MkSelect v-model="state"> - <template #label>{{ i18n.ts.state }}</template> - <option value="all">{{ i18n.ts.all }}</option> - <option value="federating">{{ i18n.ts.federating }}</option> - <option value="subscribing">{{ i18n.ts.subscribing }}</option> - <option value="publishing">{{ i18n.ts.publishing }}</option> - <!-- TODO translate --> - <option value="nsfw">NSFW</option> - <option value="suspended">{{ i18n.ts.suspended }}</option> - <option value="blocked">{{ i18n.ts.blocked }}</option> - <option value="silenced">{{ i18n.ts.silence }}</option> - <option value="notResponding">{{ i18n.ts.notResponding }}</option> - </MkSelect> - <MkSelect v-model="sort"> - <template #label>{{ i18n.ts.sort }}</template> - <option value="+pubSub">{{ i18n.ts.pubSub }} ({{ i18n.ts.descendingOrder }})</option> - <option value="-pubSub">{{ i18n.ts.pubSub }} ({{ i18n.ts.ascendingOrder }})</option> - <option value="+notes">{{ i18n.ts.notes }} ({{ i18n.ts.descendingOrder }})</option> - <option value="-notes">{{ i18n.ts.notes }} ({{ i18n.ts.ascendingOrder }})</option> - <option value="+users">{{ i18n.ts.users }} ({{ i18n.ts.descendingOrder }})</option> - <option value="-users">{{ i18n.ts.users }} ({{ i18n.ts.ascendingOrder }})</option> - <option value="+following">{{ i18n.ts.following }} ({{ i18n.ts.descendingOrder }})</option> - <option value="-following">{{ i18n.ts.following }} ({{ i18n.ts.ascendingOrder }})</option> - <option value="+followers">{{ i18n.ts.followers }} ({{ i18n.ts.descendingOrder }})</option> - <option value="-followers">{{ i18n.ts.followers }} ({{ i18n.ts.ascendingOrder }})</option> - <option value="+firstRetrievedAt">{{ i18n.ts.registeredAt }} ({{ i18n.ts.descendingOrder }})</option> - <option value="-firstRetrievedAt">{{ i18n.ts.registeredAt }} ({{ i18n.ts.ascendingOrder }})</option> - </MkSelect> - </FormSplit> - </div> - - <MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination" :displayLimit="50"> - <div :class="$style.instances"> - <MkA v-for="instance in items" :key="instance.id" v-tooltip.mfm="`Status: ${getStatus(instance)}`" :class="$style.instance" :to="`/instance-info/${instance.host}`"> - <MkInstanceCardMini :instance="instance"/> - </MkA> - </div> - </MkPagination> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> + <MkSpacer :contentMax="900"> + <div class="_gaps"> + <div> + <MkInput v-model="host" :debounce="true" class=""> + <template #prefix><i class="ti ti-search"></i></template> + <template #label>{{ i18n.ts.host }}</template> + </MkInput> + <FormSplit style="margin-top: var(--MI-margin);"> + <MkSelect v-model="state"> + <template #label>{{ i18n.ts.state }}</template> + <option value="all">{{ i18n.ts.all }}</option> + <option value="federating">{{ i18n.ts.federating }}</option> + <option value="subscribing">{{ i18n.ts.subscribing }}</option> + <option value="publishing">{{ i18n.ts.publishing }}</option> + <!-- TODO translate --> + <option value="nsfw">NSFW</option> + <option value="suspended">{{ i18n.ts.suspended }}</option> + <option value="blocked">{{ i18n.ts.blocked }}</option> + <option value="silenced">{{ i18n.ts.silence }}</option> + <option value="notResponding">{{ i18n.ts.notResponding }}</option> + </MkSelect> + <MkSelect v-model="sort"> + <template #label>{{ i18n.ts.sort }}</template> + <option value="+pubSub">{{ i18n.ts.pubSub }} ({{ i18n.ts.descendingOrder }})</option> + <option value="-pubSub">{{ i18n.ts.pubSub }} ({{ i18n.ts.ascendingOrder }})</option> + <option value="+notes">{{ i18n.ts.notes }} ({{ i18n.ts.descendingOrder }})</option> + <option value="-notes">{{ i18n.ts.notes }} ({{ i18n.ts.ascendingOrder }})</option> + <option value="+users">{{ i18n.ts.users }} ({{ i18n.ts.descendingOrder }})</option> + <option value="-users">{{ i18n.ts.users }} ({{ i18n.ts.ascendingOrder }})</option> + <option value="+following">{{ i18n.ts.following }} ({{ i18n.ts.descendingOrder }})</option> + <option value="-following">{{ i18n.ts.following }} ({{ i18n.ts.ascendingOrder }})</option> + <option value="+followers">{{ i18n.ts.followers }} ({{ i18n.ts.descendingOrder }})</option> + <option value="-followers">{{ i18n.ts.followers }} ({{ i18n.ts.ascendingOrder }})</option> + <option value="+firstRetrievedAt">{{ i18n.ts.registeredAt }} ({{ i18n.ts.descendingOrder }})</option> + <option value="-firstRetrievedAt">{{ i18n.ts.registeredAt }} ({{ i18n.ts.ascendingOrder }})</option> + </MkSelect> + </FormSplit> </div> - </MkSpacer> - </MkStickyContainer> -</div> + + <MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination" :displayLimit="50"> + <div :class="$style.instances"> + <MkA v-for="instance in items" :key="instance.id" v-tooltip.mfm="`Status: ${getStatus(instance)}`" :class="$style.instance" :to="`/instance-info/${instance.host}`"> + <MkInstanceCardMini :instance="instance"/> + </MkA> + </div> + </MkPagination> + </div> + </MkSpacer> +</PageWithHeader> </template> <script lang="ts" setup> import * as Misskey from 'misskey-js'; import { computed, ref } from 'vue'; -import XHeader from './_header_.vue'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkPagination from '@/components/MkPagination.vue'; diff --git a/packages/frontend/src/pages/admin/files.vue b/packages/frontend/src/pages/admin/files.vue index e15724c2a7..12c633bf7f 100644 --- a/packages/frontend/src/pages/admin/files.vue +++ b/packages/frontend/src/pages/admin/files.vue @@ -4,40 +4,36 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :actions="headerActions"/></template> - <MkSpacer :contentMax="900"> - <div class="_gaps"> - <div class="inputs" style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;"> - <MkSelect v-model="origin" style="margin: 0; flex: 1;"> - <template #label>{{ i18n.ts.instance }}</template> - <option value="combined">{{ i18n.ts.all }}</option> - <option value="local">{{ i18n.ts.local }}</option> - <option value="remote">{{ i18n.ts.remote }}</option> - </MkSelect> - <MkInput v-model="searchHost" :debounce="true" type="search" style="margin: 0; flex: 1;" :disabled="pagination.params.origin === 'local'"> - <template #label>{{ i18n.ts.host }}</template> - </MkInput> - </div> - <div class="inputs" style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;"> - <MkInput v-model="userId" :debounce="true" type="search" style="margin: 0; flex: 1;"> - <template #label>User ID</template> - </MkInput> - <MkInput v-model="type" :debounce="true" type="search" style="margin: 0; flex: 1;"> - <template #label>MIME type</template> - </MkInput> - </div> - <MkFileListForAdmin :pagination="pagination" :viewMode="viewMode"/> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> + <MkSpacer :contentMax="900"> + <div class="_gaps"> + <div class="inputs" style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;"> + <MkSelect v-model="origin" style="margin: 0; flex: 1;"> + <template #label>{{ i18n.ts.instance }}</template> + <option value="combined">{{ i18n.ts.all }}</option> + <option value="local">{{ i18n.ts.local }}</option> + <option value="remote">{{ i18n.ts.remote }}</option> + </MkSelect> + <MkInput v-model="searchHost" :debounce="true" type="search" style="margin: 0; flex: 1;" :disabled="pagination.params.origin === 'local'"> + <template #label>{{ i18n.ts.host }}</template> + </MkInput> </div> - </MkSpacer> - </MkStickyContainer> -</div> + <div class="inputs" style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;"> + <MkInput v-model="userId" :debounce="true" type="search" style="margin: 0; flex: 1;"> + <template #label>User ID</template> + </MkInput> + <MkInput v-model="type" :debounce="true" type="search" style="margin: 0; flex: 1;"> + <template #label>MIME type</template> + </MkInput> + </div> + <MkFileListForAdmin :pagination="pagination" :viewMode="viewMode"/> + </div> + </MkSpacer> +</PageWithHeader> </template> <script lang="ts" setup> import { computed, ref } from 'vue'; -import XHeader from './_header_.vue'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue'; diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index 85c8ba15ba..c366a7cd6a 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkSpacer> </div> - <div v-if="!(narrow && currentPage?.route.name == null)" class="main"> + <div v-if="!(narrow && currentPage?.route.name == null)" class="main _pageContainer" style="height: 100%;"> <NestedRouterView/> </div> </div> @@ -162,9 +162,14 @@ const menuDef = computed<SuperMenuDef[]>(() => [{ active: currentPage.value?.route.name === 'federation', }, { icon: 'ti ti-clock-play', + text: i18n.ts.federationJobs, + to: '/admin/federation-job-queue', + active: currentPage.value?.route.name === 'federationJobQueue', + }, { + icon: 'ti ti-clock-play', text: i18n.ts.jobQueue, - to: '/admin/queue', - active: currentPage.value?.route.name === 'queue', + to: '/admin/job-queue', + active: currentPage.value?.route.name === 'jobQueue', }, { icon: 'ti ti-cloud', text: i18n.ts.files, @@ -351,6 +356,8 @@ defineExpose({ <style lang="scss" scoped> .hiyeyicy { + height: 100%; + &.wide { display: flex; margin: 0 auto; diff --git a/packages/frontend/src/pages/admin/invites.vue b/packages/frontend/src/pages/admin/invites.vue index 7dae1db839..7f0c35d6bc 100644 --- a/packages/frontend/src/pages/admin/invites.vue +++ b/packages/frontend/src/pages/admin/invites.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="800"> <div class="_gaps_m"> <MkFolder :expanded="false"> @@ -51,12 +50,11 @@ SPDX-License-Identifier: AGPL-3.0-only </MkPagination> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { computed, ref, useTemplateRef } from 'vue'; -import XHeader from './_header_.vue'; import type { Paging } from '@/components/MkPagination.vue'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; diff --git a/packages/frontend/src/pages/admin/job-queue.chart.vue b/packages/frontend/src/pages/admin/job-queue.chart.vue new file mode 100644 index 0000000000..f42b35105e --- /dev/null +++ b/packages/frontend/src/pages/admin/job-queue.chart.vue @@ -0,0 +1,127 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<canvas ref="chartEl"></canvas> +</template> + +<script lang="ts" setup> +import { onMounted, useTemplateRef, watch } from 'vue'; +import { Chart } from 'chart.js'; +import { store } from '@/store.js'; +import { useChartTooltip } from '@/use/use-chart-tooltip.js'; +import { chartVLine } from '@/utility/chart-vline.js'; +import { alpha } from '@/utility/color.js'; +import { initChart } from '@/utility/init-chart.js'; + +initChart(); + +const props = defineProps<{ + dataSet: { + completed: number[]; + failed: number[]; + }; + aspectRatio?: number; +}>(); + +const chartEl = useTemplateRef('chartEl'); + +const { handler: externalTooltipHandler } = useChartTooltip(); + +let chartInstance: Chart; + +function setData() { + if (chartInstance == null) return; + chartInstance.data.labels = []; + for (let i = 0; i < Math.max(props.dataSet.completed.length, props.dataSet.failed.length); i++) { + chartInstance.data.labels.push(''); + } + chartInstance.data.datasets[0].data = props.dataSet.completed; + chartInstance.data.datasets[1].data = props.dataSet.failed; + chartInstance.update(); +} + +watch(() => props.dataSet, () => { + setData(); +}); + +onMounted(() => { + const vLineColor = store.s.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; + + chartInstance = new Chart(chartEl.value, { + type: 'line', + data: { + labels: [], + datasets: [{ + label: 'Completed', + pointRadius: 0, + tension: 0.3, + borderWidth: 2, + borderJoinStyle: 'round', + borderColor: '#4caf50', + backgroundColor: alpha('#4caf50', 0.2), + fill: true, + data: [], + }, { + label: 'Failed', + pointRadius: 0, + tension: 0.3, + borderWidth: 2, + borderJoinStyle: 'round', + borderColor: '#ff0000', + backgroundColor: alpha('#ff0000', 0.2), + fill: true, + data: [], + }], + }, + options: { + aspectRatio: props.aspectRatio ?? 2.5, + layout: { + padding: { + left: 0, + right: 0, + top: 0, + bottom: 0, + }, + }, + scales: { + x: { + grid: { + display: true, + }, + ticks: { + display: false, + maxTicksLimit: 10, + }, + }, + y: { + min: 0, + grid: { + }, + }, + }, + interaction: { + intersect: false, + }, + plugins: { + legend: { + display: false, + }, + tooltip: { + enabled: false, + mode: 'index', + animation: { + duration: 0, + }, + external: externalTooltipHandler, + }, + }, + }, + plugins: [chartVLine(vLineColor)], + }); + + setData(); +}); +</script> diff --git a/packages/frontend/src/pages/admin/job-queue.job.vue b/packages/frontend/src/pages/admin/job-queue.job.vue new file mode 100644 index 0000000000..71efab0272 --- /dev/null +++ b/packages/frontend/src/pages/admin/job-queue.job.vue @@ -0,0 +1,280 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkFolder> + <template #label> + <span v-if="job.opts.repeat != null" style="margin-right: 1em;"><repeat></span> + <span v-else style="margin-right: 1em;">#{{ job.id }}</span> + <span>{{ job.name }}</span> + </template> + <template #suffix> + <MkTime :time="job.finishedOn ?? job.processedOn ?? job.timestamp" mode="relative"/> + <span v-if="job.progress != null && job.progress > 0" style="margin-left: 1em;">{{ Math.floor(job.progress * 100) }}%</span> + <span v-if="job.opts.attempts != null && job.opts.attempts > 0 && job.attempts > 1" style="margin-left: 1em; color: var(--MI_THEME-warn); font-variant-numeric: diagonal-fractions;">{{ job.attempts }}/{{ job.opts.attempts }}</span> + <span v-if="job.isFailed && job.finishedOn != null" style="margin-left: 1em; color: var(--MI_THEME-error)"><i class="ti ti-circle-x"></i></span> + <span v-else-if="job.isFailed" style="margin-left: 1em; color: var(--MI_THEME-warn)"><i class="ti ti-alert-triangle"></i></span> + <span v-else-if="job.finishedOn != null" style="margin-left: 1em; color: var(--MI_THEME-success)"><i class="ti ti-check"></i></span> + <span v-else-if="job.delay != null && job.delay != 0" style="margin-left: 1em;"><i class="ti ti-clock"></i></span> + <span v-else-if="job.processedOn != null" style="margin-left: 1em; color: var(--MI_THEME-success)"><i class="ti ti-player-play"></i></span> + </template> + <template #header> + <MkTabs + v-model:tab="tab" + :tabs="[{ + key: 'info', + title: 'Info', + icon: 'ti ti-info-circle', + }, { + key: 'timeline', + title: 'Timeline', + icon: 'ti ti-timeline-event', + }, { + key: 'data', + title: 'Data', + icon: 'ti ti-package', + }, ...(canEdit ? [{ + key: 'dataEdit', + title: 'Data (edit)', + icon: 'ti ti-package', + }] : []), + ...(job.returnValue != null ? [{ + key: 'result', + title: 'Result', + icon: 'ti ti-check', + }] : []), + ...(job.stacktrace.length > 0 ? [{ + key: 'error', + title: 'Error', + icon: 'ti ti-alert-triangle', + }] : []), { + key: 'logs', + title: 'Logs', + icon: 'ti ti-logs', + }]" + /> + </template> + <template #footer> + <div class="_buttons"> + <MkButton rounded @click="copyRaw()"><i class="ti ti-copy"></i> Copy raw</MkButton> + <MkButton rounded @click="refresh()"><i class="ti ti-reload"></i> Refresh view</MkButton> + <MkButton rounded @click="promoteJob()"><i class="ti ti-player-track-next"></i> Promote</MkButton> + <MkButton rounded @click="moveJob"><i class="ti ti-arrow-right"></i> Move to</MkButton> + <MkButton danger rounded style="margin-left: auto;" @click="removeJob()"><i class="ti ti-trash"></i> Remove</MkButton> + </div> + </template> + + <div v-if="tab === 'info'" class="_gaps_s"> + <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 12px;"> + <MkKeyValue> + <template #key>ID</template> + <template #value>{{ job.id }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>Created at</template> + <template #value><MkTime :time="job.timestamp" mode="detail"/></template> + </MkKeyValue> + <MkKeyValue v-if="job.processedOn != null"> + <template #key>Processed at</template> + <template #value><MkTime :time="job.processedOn" mode="detail"/></template> + </MkKeyValue> + <MkKeyValue v-if="job.finishedOn != null"> + <template #key>Finished at</template> + <template #value><MkTime :time="job.finishedOn" mode="detail"/></template> + </MkKeyValue> + <MkKeyValue v-if="job.processedOn != null && job.finishedOn != null"> + <template #key>Spent</template> + <template #value>{{ job.finishedOn - job.processedOn }}ms</template> + </MkKeyValue> + <MkKeyValue v-if="job.failedReason != null"> + <template #key>Failed reason</template> + <template #value><i style="color: var(--MI_THEME-error)" class="ti ti-alert-triangle"></i> {{ job.failedReason }}</template> + </MkKeyValue> + <MkKeyValue v-if="job.opts.attempts != null && job.opts.attempts > 0"> + <template #key>Attempts</template> + <template #value>{{ job.attempts }} of {{ job.opts.attempts }}</template> + </MkKeyValue> + <MkKeyValue v-if="job.progress != null && job.progress > 0"> + <template #key>Progress</template> + <template #value>{{ Math.floor(job.progress * 100) }}%</template> + </MkKeyValue> + </div> + <MkFolder :withSpacer="false"> + <template #label>Options</template> + <MkCode :code="JSON5.stringify(job.opts, null, '\t')" lang="js"/> + </MkFolder> + </div> + <div v-else-if="tab === 'timeline'"> + <MkTl :events="timeline"> + <template #left="{ event }"> + <div> + <template v-if="event.type === 'finished'"> + <template v-if="job.isFailed"> + <b>Finished</b> <i class="ti ti-circle-x" style="color: var(--MI_THEME-error);"></i> + </template> + <template v-else> + <b>Finished</b> <i class="ti ti-check" style="color: var(--MI_THEME-success);"></i> + </template> + </template> + <template v-else-if="event.type === 'processed'"> + <b>Processed</b> <i class="ti ti-player-play"></i> + </template> + <template v-else-if="event.type === 'attempt'"> + <b>Attempt #{{ event.attempt }}</b> <i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> + </template> + <template v-else-if="event.type === 'created'"> + <b>Created</b> <i class="ti ti-plus"></i> + </template> + </div> + </template> + <template #right="{ event, timestamp, delta }"> + <div style="margin: 8px 0;"> + <template v-if="event.type === 'attempt'"> + <div>at ?</div> + </template> + <template v-else> + <div>at <MkTime :time="timestamp" mode="detail"/></div> + <div style="font-size: 90%; opacity: 0.7;">{{ timestamp }} (+{{ msSMH(delta) }})</div> + </template> + </div> + </template> + </MkTl> + </div> + <div v-else-if="tab === 'data'"> + <MkCode :code="JSON5.stringify(job.data, null, '\t')" lang="js"/> + </div> + <div v-else-if="tab === 'dataEdit'" class="_gaps_s"> + <MkCodeEditor v-model="editData" lang="json5"></MkCodeEditor> + <MkButton><i class="ti ti-device-floppy"></i> Update</MkButton> + </div> + <div v-else-if="tab === 'result'"> + <MkCode :code="job.returnValue"/> + </div> + <div v-else-if="tab === 'error'" class="_gaps_s"> + <MkCode v-for="log in job.stacktrace" :code="log" lang="stacktrace"/> + </div> +</MkFolder> +</template> + +<script lang="ts" setup> +import { ref, computed, watch } from 'vue'; +import JSON5 from 'json5'; +import type { Ref } from 'vue'; +import * as os from '@/os.js'; +import { i18n } from '@/i18n.js'; +import MkButton from '@/components/MkButton.vue'; +import { misskeyApi } from '@/utility/misskey-api.js'; +import MkTabs from '@/components/MkTabs.vue'; +import MkFolder from '@/components/MkFolder.vue'; +import MkCode from '@/components/MkCode.vue'; +import MkKeyValue from '@/components/MkKeyValue.vue'; +import MkCodeEditor from '@/components/MkCodeEditor.vue'; +import MkTl from '@/components/MkTl.vue'; +import kmg from '@/filters/kmg.js'; +import bytes from '@/filters/bytes.js'; +import { copyToClipboard } from '@/utility/copy-to-clipboard.js'; + +function msSMH(v: number | null) { + if (v == null) return 'N/A'; + if (v === 0) return '0'; + const suffixes = ['ms', 's', 'm', 'h']; + const isMinus = v < 0; + if (isMinus) v = -v; + const i = Math.floor(Math.log(v) / Math.log(1000)); + const value = v / Math.pow(1000, i); + const suffix = suffixes[i]; + return `${isMinus ? '-' : ''}${value.toFixed(1)}${suffix}`; +} + +const props = defineProps<{ + job: any; + queueType: string; +}>(); + +const emit = defineEmits<{ + (ev: 'needRefresh'): void, +}>(); + +const tab = ref('info'); +const editData = ref(JSON5.stringify(props.job.data, null, '\t')); +const canEdit = true; +const timeline = computed(() => { + const events = [{ + id: 'created', + timestamp: props.job.timestamp, + data: { + type: 'created', + }, + }]; + if (props.job.attempts > 1) { + for (let i = 1; i < props.job.attempts; i++) { + events.push({ + id: `attempt-${i}`, + timestamp: props.job.timestamp + i, + data: { + type: 'attempt', + attempt: i, + }, + }); + } + } + if (props.job.processedOn != null) { + events.push({ + id: 'processed', + timestamp: props.job.processedOn, + data: { + type: 'processed', + }, + }); + } + if (props.job.finishedOn != null) { + events.push({ + id: 'finished', + timestamp: props.job.finishedOn, + data: { + type: 'finished', + }, + }); + } + return events; +}); + +async function promoteJob() { + const { canceled } = await os.confirm({ + type: 'warning', + title: i18n.ts.areYouSure, + }); + if (canceled) return; + + os.apiWithDialog('admin/queue/retry-job', { queue: props.queueType, jobId: props.job.id }); +} + +async function removeJob() { + const { canceled } = await os.confirm({ + type: 'warning', + title: i18n.ts.areYouSure, + }); + if (canceled) return; + + os.apiWithDialog('admin/queue/remove-job', { queue: props.queueType, jobId: props.job.id }); +} + +function moveJob() { + // TODO +} + +function refresh() { + emit('needRefresh'); +} + +function copyRaw() { + const raw = JSON.stringify(props.job, null, '\t'); + copyToClipboard(raw); +} +</script> + +<style lang="scss" module> + +</style> diff --git a/packages/frontend/src/pages/admin/job-queue.vue b/packages/frontend/src/pages/admin/job-queue.vue new file mode 100644 index 0000000000..528c473c4f --- /dev/null +++ b/packages/frontend/src/pages/admin/job-queue.vue @@ -0,0 +1,370 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> + <MkSpacer> + <div v-if="tab === '-'" class="_gaps"> + <div :class="$style.queues"> + <div v-for="q in queueInfos" :key="q.name" :class="$style.queue" @click="tab = q.name"> + <div style="display: flex; align-items: center; font-weight: bold;"><i class="ti ti-http-que" style="margin-right: 0.5em;"></i>{{ q.name }}<i v-if="!q.isPaused" style="color: var(--MI_THEME-success); margin-left: auto;" class="ti ti-player-play"></i></div> + <div :class="$style.queueCounts"> + <MkKeyValue> + <template #key>Active</template> + <template #value>{{ kmg(q.counts.active, 2) }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>Delayed</template> + <template #value>{{ kmg(q.counts.delayed, 2) }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>Waiting</template> + <template #value>{{ kmg(q.counts.waiting, 2) }}</template> + </MkKeyValue> + </div> + <XChart :dataSet="{ completed: q.metrics.completed.data, failed: q.metrics.failed.data }"/> + </div> + </div> + </div> + <div v-else-if="queueInfo" class="_gaps"> + <MkFolder :defaultOpen="true"> + <template #label>Overview: {{ tab }}</template> + <template #icon><i class="ti ti-http-que"></i></template> + <template #suffix>#{{ queueInfo.db.processId }}:{{ queueInfo.db.port }} / {{ queueInfo.db.runId }}</template> + <template #caption>{{ queueInfo.qualifiedName }}</template> + <template #footer> + <div class="_buttons"> + <MkButton rounded @click="promoteAllJobs"><i class="ti ti-player-track-next"></i> Promote all jobs</MkButton> + <MkButton rounded @click="createJob"><i class="ti ti-plus"></i> Add job</MkButton> + <MkButton v-if="queueInfo.isPaused" rounded @click="resumeQueue"><i class="ti ti-player-play"></i> Resume queue</MkButton> + <MkButton v-else rounded danger @click="pauseQueue"><i class="ti ti-player-pause"></i> Pause queue</MkButton> + <MkButton rounded danger @click="clearQueue"><i class="ti ti-trash"></i> Empty queue</MkButton> + </div> + </template> + + <div class="_gaps"> + <XChart :dataSet="{ completed: queueInfo.metrics.completed.data, failed: queueInfo.metrics.failed.data }" :aspectRatio="5"/> + <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px;"> + <MkKeyValue> + <template #key>Active</template> + <template #value>{{ kmg(queueInfo.counts.active, 2) }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>Delayed</template> + <template #value>{{ kmg(queueInfo.counts.delayed, 2) }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>Waiting</template> + <template #value>{{ kmg(queueInfo.counts.waiting, 2) }}</template> + </MkKeyValue> + </div> + <hr> + <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px;"> + <MkKeyValue> + <template #key>Clients: Connected</template> + <template #value>{{ queueInfo.db.clients.connected }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>Clients: Blocked</template> + <template #value>{{ queueInfo.db.clients.blocked }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>Memory: Peak</template> + <template #value>{{ bytes(queueInfo.db.memory.peak, 1) }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>Memory: Total</template> + <template #value>{{ bytes(queueInfo.db.memory.total, 1) }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>Memory: Used</template> + <template #value>{{ bytes(queueInfo.db.memory.used, 1) }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>Uptime</template> + <template #value>{{ queueInfo.db.uptime }}</template> + </MkKeyValue> + </div> + </div> + </MkFolder> + + <MkFolder :defaultOpen="true" :withSpacer="false"> + <template #label>Jobs: {{ tab }}</template> + <template #icon><i class="ti ti-list-check"></i></template> + <template #suffix><A:{{ kmg(queueInfo.counts.active, 2) }}> <D:{{ kmg(queueInfo.counts.delayed, 2) }}> <W:{{ kmg(queueInfo.counts.waiting, 2) }}></template> + <template #header> + <MkTabs + v-model:tab="jobState" + :class="$style.jobsTabs" :tabs="[{ + key: 'all', + title: 'All', + icon: 'ti ti-code-asterisk', + }, { + key: 'latest', + title: 'Latest', + icon: 'ti ti-logs', + }, { + key: 'completed', + title: 'Completed', + icon: 'ti ti-check', + }, { + key: 'failed', + title: 'Failed', + icon: 'ti ti-circle-x', + }, { + key: 'active', + title: 'Active', + icon: 'ti ti-player-play', + }, { + key: 'delayed', + title: 'Delayed', + icon: 'ti ti-clock', + }, { + key: 'wait', + title: 'Waiting', + icon: 'ti ti-hourglass-high', + }, { + key: 'paused', + title: 'Paused', + icon: 'ti ti-player-pause', + }]" + /> + </template> + <template #footer> + <div class="_buttons"> + <MkButton rounded @click="fetchJobs()"><i class="ti ti-reload"></i> Refresh view</MkButton> + <MkButton rounded danger style="margin-left: auto;" @click="removeJobs"><i class="ti ti-trash"></i> Remove jobs</MkButton> + </div> + </template> + + <MkSpacer> + <MkInput + v-model="searchQuery" + :placeholder="i18n.ts.search" + type="search" + style="margin-bottom: 16px;" + > + <template #prefix><i class="ti ti-search"></i></template> + </MkInput> + + <MkLoading v-if="jobsFetching"/> + <MkTl + v-else + :events="jobs.map((job) => ({ + id: job.id, + timestamp: job.finishedOn ?? job.processedOn ?? job.timestamp, + data: job, + }))" + class="_monospace" + > + <template #right="{ event: job }"> + <XJob :job="job" :queueType="tab" style="margin: 4px 0;" @needRefresh="refreshJob(job.id)"/> + </template> + </MkTl> + </MkSpacer> + </MkFolder> + </div> + </MkSpacer> +</PageWithHeader> +</template> + +<script lang="ts" setup> +import { ref, computed, watch } from 'vue'; +import JSON5 from 'json5'; +import { debounce } from 'throttle-debounce'; +import { useInterval } from '@@/js/use-interval.js'; +import XChart from './job-queue.chart.vue'; +import XJob from './job-queue.job.vue'; +import type { Ref } from 'vue'; +import * as os from '@/os.js'; +import { i18n } from '@/i18n.js'; +import { definePage } from '@/page.js'; +import MkButton from '@/components/MkButton.vue'; +import { misskeyApi } from '@/utility/misskey-api.js'; +import MkTabs from '@/components/MkTabs.vue'; +import MkFolder from '@/components/MkFolder.vue'; +import MkCode from '@/components/MkCode.vue'; +import MkKeyValue from '@/components/MkKeyValue.vue'; +import MkTl from '@/components/MkTl.vue'; +import kmg from '@/filters/kmg.js'; +import MkInput from '@/components/MkInput.vue'; +import bytes from '@/filters/bytes.js'; +import { copyToClipboard } from '@/utility/copy-to-clipboard.js'; + +const QUEUE_TYPES = [ + 'system', + 'endedPollNotification', + 'deliver', + 'inbox', + 'db', + 'relationship', + 'objectStorage', + 'userWebhookDeliver', + 'systemWebhookDeliver', +] as const; + +const tab: Ref<typeof QUEUE_TYPES[number] | '-'> = ref('-'); +const jobState = ref('all'); +const jobs = ref([]); +const jobsFetching = ref(true); +const queueInfos = ref([]); +const queueInfo = ref(); +const searchQuery = ref(''); + +async function fetchQueues() { + if (tab.value !== '-') return; + queueInfos.value = await misskeyApi('admin/queue/queues'); +} + +async function fetchCurrentQueue() { + if (tab.value === '-') return; + queueInfo.value = await misskeyApi('admin/queue/queue-stats', { queue: tab.value }); +} + +async function fetchJobs() { + jobsFetching.value = true; + const state = jobState.value; + jobs.value = await misskeyApi('admin/queue/jobs', { + queue: tab.value, + state: state === 'all' ? ['completed', 'failed', 'active', 'delayed', 'wait'] : state === 'latest' ? ['completed', 'failed'] : [state], + search: searchQuery.value.trim() === '' ? undefined : searchQuery.value, + }).then(res => { + if (state === 'all') { + res.sort((a, b) => (a.processedOn ?? a.timestamp) > (b.processedOn ?? b.timestamp) ? -1 : 1); + } else if (state === 'latest') { + res.sort((a, b) => a.processedOn > b.processedOn ? -1 : 1); + } else if (state === 'delayed') { + res.sort((a, b) => (a.processedOn ?? a.timestamp) > (b.processedOn ?? b.timestamp) ? -1 : 1); + } + return res; + }); + jobsFetching.value = false; +} + +watch([tab], async () => { + if (tab.value === '-') { + fetchQueues(); + } else { + fetchCurrentQueue(); + fetchJobs(); + } +}, { immediate: true }); + +watch([jobState], () => { + fetchJobs(); +}); + +const search = debounce(1000, () => { + fetchJobs(); +}); + +watch([searchQuery], () => { + search(); +}); + +useInterval(() => { + if (tab.value === '-') { + fetchQueues(); + } else { + fetchCurrentQueue(); + } +}, 1000 * 10, { + immediate: false, + afterMounted: true, +}); + +async function clearQueue() { + const { canceled } = await os.confirm({ + type: 'warning', + title: i18n.ts.areYouSure, + }); + if (canceled) return; + + os.apiWithDialog('admin/queue/clear', { queue: tab.value, state: '*' }); + + fetchCurrentQueue(); + fetchJobs(); +} + +async function promoteAllJobs() { + const { canceled } = await os.confirm({ + type: 'warning', + title: i18n.ts.areYouSure, + }); + if (canceled) return; + + os.apiWithDialog('admin/queue/promote-jobs', { queue: tab.value }); + + fetchCurrentQueue(); + fetchJobs(); +} + +async function removeJobs() { + const { canceled } = await os.confirm({ + type: 'warning', + title: i18n.ts.areYouSure, + }); + if (canceled) return; + + os.apiWithDialog('admin/queue/clear', { queue: tab.value, state: jobState.value }); + + fetchCurrentQueue(); + fetchJobs(); +} + +async function refreshJob(jobId: string) { + const newJob = await misskeyApi('admin/queue/show-job', { queue: tab.value, jobId }); + const index = jobs.value.findIndex((job) => job.id === jobId); + if (index !== -1) { + jobs.value[index] = newJob; + } +} + +const headerActions = computed(() => []); + +const headerTabs = computed(() => + [{ + key: '-', + title: i18n.ts.overview, + icon: 'ti ti-dashboard', + }].concat(QUEUE_TYPES.map((t) => ({ + key: t, + title: t, + }))), +); + +definePage(() => ({ + title: i18n.ts.jobQueue, + icon: 'ti ti-clock-play', + needWideArea: true, +})); +</script> + +<style lang="scss" module> +.queues { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); + gap: 14px; +} + +.queue { + padding: 14px 18px; + background-color: var(--MI_THEME-panel); + border-radius: 8px; + cursor: pointer; +} + +.queueCounts { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); + gap: 8px; + font-size: 85%; + margin: 6px 0; +} + +.jobsTabs { + +} +</style> diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue index a71c8f435d..5491a2df45 100644 --- a/packages/frontend/src/pages/admin/moderation.vue +++ b/packages/frontend/src/pages/admin/moderation.vue @@ -4,160 +4,156 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :tabs="headerTabs"/></template> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> - <FormSuspense :p="init"> - <div class="_gaps_m"> - <MkSwitch :modelValue="enableRegistration" @update:modelValue="onChange_enableRegistration"> - <template #label>{{ i18n.ts._serverSettings.openRegistration }}</template> - <template #caption> - <div>{{ i18n.ts._serverSettings.thisSettingWillAutomaticallyOffWhenModeratorsInactive }}</div> - <div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._serverSettings.openRegistrationWarning }}</div> - </template> - </MkSwitch> +<PageWithHeader :tabs="headerTabs"> + <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <FormSuspense :p="init"> + <div class="_gaps_m"> + <MkSwitch :modelValue="enableRegistration" @update:modelValue="onChange_enableRegistration"> + <template #label>{{ i18n.ts._serverSettings.openRegistration }}</template> + <template #caption> + <div>{{ i18n.ts._serverSettings.thisSettingWillAutomaticallyOffWhenModeratorsInactive }}</div> + <div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._serverSettings.openRegistrationWarning }}</div> + </template> + </MkSwitch> - <MkSwitch v-model="emailRequiredForSignup" @change="onChange_emailRequiredForSignup"> - <template #label>{{ i18n.ts.emailRequiredForSignup }}</template> - </MkSwitch> + <MkSwitch v-model="emailRequiredForSignup" @change="onChange_emailRequiredForSignup"> + <template #label>{{ i18n.ts.emailRequiredForSignup }}</template> + </MkSwitch> - <MkSwitch v-model="approvalRequiredForSignup" @change="onChange_approvalRequiredForSignup"> - <template #label>{{ i18n.ts.approvalRequiredForSignup }}</template> - </MkSwitch> + <MkSwitch v-model="approvalRequiredForSignup" @change="onChange_approvalRequiredForSignup"> + <template #label>{{ i18n.ts.approvalRequiredForSignup }}</template> + </MkSwitch> - <FormLink to="/admin/server-rules">{{ i18n.ts.serverRules }}</FormLink> + <FormLink to="/admin/server-rules">{{ i18n.ts.serverRules }}</FormLink> - <!-- TODO translate --> - <MkFolder v-if="bubbleTimelineEnabled"> - <template #icon><i class="ph-drop ph-bold ph-lg"></i></template> - <template #label>Bubble timeline</template> + <!-- TODO translate --> + <MkFolder v-if="bubbleTimelineEnabled"> + <template #icon><i class="ph-drop ph-bold ph-lg"></i></template> + <template #label>Bubble timeline</template> - <div class="_gaps"> - <MkTextarea v-model="bubbleTimeline"> - <template #caption>Choose which instances should be displayed in the bubble.</template> - </MkTextarea> - <MkButton primary @click="save_bubbleTimeline">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="bubbleTimeline"> + <template #caption>Choose which instances should be displayed in the bubble.</template> + </MkTextarea> + <MkButton primary @click="save_bubbleTimeline">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> - <MkFolder> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.trustedLinkUrlPatterns }}</template> + <MkFolder> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.trustedLinkUrlPatterns }}</template> - <div class="_gaps"> - <MkTextarea v-model="trustedLinkUrlPatterns"> - <template #caption>{{ i18n.ts.trustedLinkUrlPatternsDescription }}</template> - </MkTextarea> - <MkButton primary @click="save_trustedLinkUrlPatterns">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="trustedLinkUrlPatterns"> + <template #caption>{{ i18n.ts.trustedLinkUrlPatternsDescription }}</template> + </MkTextarea> + <MkButton primary @click="save_trustedLinkUrlPatterns">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-lock-star"></i></template> - <template #label>{{ i18n.ts.preservedUsernames }}</template> + <MkFolder> + <template #icon><i class="ti ti-lock-star"></i></template> + <template #label>{{ i18n.ts.preservedUsernames }}</template> - <div class="_gaps"> - <MkTextarea v-model="preservedUsernames"> - <template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template> - </MkTextarea> - <MkButton primary @click="save_preservedUsernames">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="preservedUsernames"> + <template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template> + </MkTextarea> + <MkButton primary @click="save_preservedUsernames">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-message-exclamation"></i></template> - <template #label>{{ i18n.ts.sensitiveWords }}</template> + <MkFolder> + <template #icon><i class="ti ti-message-exclamation"></i></template> + <template #label>{{ i18n.ts.sensitiveWords }}</template> - <div class="_gaps"> - <MkTextarea v-model="sensitiveWords"> - <template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template> - </MkTextarea> - <MkButton primary @click="save_sensitiveWords">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="sensitiveWords"> + <template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template> + </MkTextarea> + <MkButton primary @click="save_sensitiveWords">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-message-x"></i></template> - <template #label>{{ i18n.ts.prohibitedWords }}</template> + <MkFolder> + <template #icon><i class="ti ti-message-x"></i></template> + <template #label>{{ i18n.ts.prohibitedWords }}</template> - <div class="_gaps"> - <MkTextarea v-model="prohibitedWords"> - <template #caption>{{ i18n.ts.prohibitedWordsDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template> - </MkTextarea> - <MkButton primary @click="save_prohibitedWords">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="prohibitedWords"> + <template #caption>{{ i18n.ts.prohibitedWordsDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template> + </MkTextarea> + <MkButton primary @click="save_prohibitedWords">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-user-x"></i></template> - <template #label>{{ i18n.ts.prohibitedWordsForNameOfUser }}</template> + <MkFolder> + <template #icon><i class="ti ti-user-x"></i></template> + <template #label>{{ i18n.ts.prohibitedWordsForNameOfUser }}</template> - <div class="_gaps"> - <MkTextarea v-model="prohibitedWordsForNameOfUser"> - <template #caption>{{ i18n.ts.prohibitedWordsForNameOfUserDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template> - </MkTextarea> - <MkButton primary @click="save_prohibitedWordsForNameOfUser">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="prohibitedWordsForNameOfUser"> + <template #caption>{{ i18n.ts.prohibitedWordsForNameOfUserDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template> + </MkTextarea> + <MkButton primary @click="save_prohibitedWordsForNameOfUser">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-eye-off"></i></template> - <template #label>{{ i18n.ts.hiddenTags }}</template> + <MkFolder> + <template #icon><i class="ti ti-eye-off"></i></template> + <template #label>{{ i18n.ts.hiddenTags }}</template> - <div class="_gaps"> - <MkTextarea v-model="hiddenTags"> - <template #caption>{{ i18n.ts.hiddenTagsDescription }}</template> - </MkTextarea> - <MkButton primary @click="save_hiddenTags">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="hiddenTags"> + <template #caption>{{ i18n.ts.hiddenTagsDescription }}</template> + </MkTextarea> + <MkButton primary @click="save_hiddenTags">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-eye-off"></i></template> - <template #label>{{ i18n.ts.silencedInstances }}</template> + <MkFolder> + <template #icon><i class="ti ti-eye-off"></i></template> + <template #label>{{ i18n.ts.silencedInstances }}</template> - <div class="_gaps"> - <MkTextarea v-model="silencedHosts"> - <template #caption>{{ i18n.ts.silencedInstancesDescription }}</template> - </MkTextarea> - <MkButton primary @click="save_silencedHosts">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="silencedHosts"> + <template #caption>{{ i18n.ts.silencedInstancesDescription }}</template> + </MkTextarea> + <MkButton primary @click="save_silencedHosts">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-eye-off"></i></template> - <template #label>{{ i18n.ts.mediaSilencedInstances }}</template> + <MkFolder> + <template #icon><i class="ti ti-eye-off"></i></template> + <template #label>{{ i18n.ts.mediaSilencedInstances }}</template> - <div class="_gaps"> - <MkTextarea v-model="mediaSilencedHosts"> - <template #caption>{{ i18n.ts.mediaSilencedInstancesDescription }}</template> - </MkTextarea> - <MkButton primary @click="save_mediaSilencedHosts">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="mediaSilencedHosts"> + <template #caption>{{ i18n.ts.mediaSilencedInstancesDescription }}</template> + </MkTextarea> + <MkButton primary @click="save_mediaSilencedHosts">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-ban"></i></template> - <template #label>{{ i18n.ts.blockedInstances }}</template> + <MkFolder> + <template #icon><i class="ti ti-ban"></i></template> + <template #label>{{ i18n.ts.blockedInstances }}</template> - <div class="_gaps"> - <MkTextarea v-model="blockedHosts"> - <template #caption>{{ i18n.ts.blockedInstancesDescription }}</template> - </MkTextarea> - <MkButton primary @click="save_blockedHosts">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> - </div> - </FormSuspense> - </MkSpacer> - </MkStickyContainer> -</div> + <div class="_gaps"> + <MkTextarea v-model="blockedHosts"> + <template #caption>{{ i18n.ts.blockedInstancesDescription }}</template> + </MkTextarea> + <MkButton primary @click="save_blockedHosts">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> + </div> + </FormSuspense> + </MkSpacer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; -import XHeader from './_header_.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkInput from '@/components/MkInput.vue'; import MkTextarea from '@/components/MkTextarea.vue'; diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue index 5ea1c7f599..5b049fb976 100644 --- a/packages/frontend/src/pages/admin/modlog.ModLog.vue +++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue @@ -131,8 +131,49 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-else-if="log.type === 'addRelay'">: {{ log.info.inbox }}</span> <span v-else-if="log.type === 'removeRelay'">: {{ log.info.inbox }}</span> </template> - <template v-if="log.user" #icon> - <MkAvatar :user="log.user" :class="$style.avatar"/> + <template #icon> + <i v-if="log.type === 'updateServerSettings'" class="ti ti-settings"></i> + <i v-else-if="log.type === 'updateUserNote'" class="ti ti-pencil"></i> + <i v-else-if="log.type === 'suspend'" class="ti ti-user-x"></i> + <i v-else-if="log.type === 'unsuspend'" class="ti ti-user-check"></i> + <i v-else-if="log.type === 'resetPassword'" class="ti ti-key"></i> + <i v-else-if="log.type === 'assignRole'" class="ti ti-user-plus"></i> + <i v-else-if="log.type === 'unassignRole'" class="ti ti-user-minus"></i> + <i v-else-if="log.type === 'createRole'" class="ti ti-plus"></i> + <i v-else-if="log.type === 'updateRole'" class="ti ti-pencil"></i> + <i v-else-if="log.type === 'deleteRole'" class="ti ti-trash"></i> + <i v-else-if="log.type === 'addCustomEmoji'" class="ti ti-plus"></i> + <i v-else-if="log.type === 'updateCustomEmoji'" class="ti ti-pencil"></i> + <i v-else-if="log.type === 'deleteCustomEmoji'" class="ti ti-trash"></i> + <i v-else-if="log.type === 'markSensitiveDriveFile'" class="ti ti-eye-exclamation"></i> + <i v-else-if="log.type === 'unmarkSensitiveDriveFile'" class="ti ti-eye"></i> + <i v-else-if="log.type === 'suspendRemoteInstance'" class="ti ti-x"></i> + <i v-else-if="log.type === 'unsuspendRemoteInstance'" class="ti ti-check"></i> + <i v-else-if="log.type === 'createGlobalAnnouncement'" class="ti ti-plus"></i> + <i v-else-if="log.type === 'updateGlobalAnnouncement'" class="ti ti-pencil"></i> + <i v-else-if="log.type === 'deleteGlobalAnnouncement'" class="ti ti-trash"></i> + <i v-else-if="log.type === 'createUserAnnouncement'" class="ti ti-plus"></i> + <i v-else-if="log.type === 'updateUserAnnouncement'" class="ti ti-pencil"></i> + <i v-else-if="log.type === 'deleteUserAnnouncement'" class="ti ti-trash"></i> + <i v-else-if="log.type === 'deleteNote'" class="ti ti-trash"></i> + <i v-else-if="log.type === 'deleteDriveFile'" class="ti ti-trash"></i> + <i v-else-if="log.type === 'createAd'" class="ti ti-plus"></i> + <i v-else-if="log.type === 'updateAd'" class="ti ti-pencil"></i> + <i v-else-if="log.type === 'deleteAd'" class="ti ti-trash"></i> + <i v-else-if="log.type === 'createAvatarDecoration'" class="ti ti-plus"></i> + <i v-else-if="log.type === 'updateAvatarDecoration'" class="ti ti-pencil"></i> + <i v-else-if="log.type === 'deleteAvatarDecoration'" class="ti ti-trash"></i> + <i v-else-if="log.type === 'createSystemWebhook'" class="ti ti-plus"></i> + <i v-else-if="log.type === 'updateSystemWebhook'" class="ti ti-pencil"></i> + <i v-else-if="log.type === 'deleteSystemWebhook'" class="ti ti-trash"></i> + <i v-else-if="log.type === 'createAbuseReportNotificationRecipient'" class="ti ti-plus"></i> + <i v-else-if="log.type === 'updateAbuseReportNotificationRecipient'" class="ti ti-pencil"></i> + <i v-else-if="log.type === 'deleteAbuseReportNotificationRecipient'" class="ti ti-trash"></i> + <i v-else-if="log.type === 'deleteAccount'" class="ti ti-trash"></i> + <i v-else-if="log.type === 'deletePage'" class="ti ti-trash"></i> + <i v-else-if="log.type === 'deleteFlash'" class="ti ti-trash"></i> + <i v-else-if="log.type === 'deleteGalleryPost'" class="ti ti-trash"></i> + <i v-else-if="log.type === 'deleteChatRoom'" class="ti ti-trash"></i> </template> <template #suffix> <MkTime :time="log.createdAt"/> @@ -304,11 +345,6 @@ const props = defineProps<{ </script> <style lang="scss" module> -.avatar { - width: 18px; - height: 18px; -} - .diff { background: #fff; color: #000; diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue index 32a20e6dd4..2d43e3b790 100644 --- a/packages/frontend/src/pages/admin/modlog.vue +++ b/packages/frontend/src/pages/admin/modlog.vue @@ -4,10 +4,9 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="900"> - <div> + <div class="_gaps"> <div style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;"> <MkSelect v-model="type" style="margin: 0; flex: 1;"> <template #label>{{ i18n.ts.type }}</template> @@ -19,41 +18,68 @@ SPDX-License-Identifier: AGPL-3.0-only </MkInput> </div> - <MkPagination v-slot="{items}" ref="logs" :pagination="pagination" :displayLimit="50" style="margin-top: var(--MI-margin);"> - <MkDateSeparatedList v-slot="{ item }" :items="items" :noGap="false" style="--MI-margin: 8px;"> - <XModLog :key="item.id" :log="item"/> - </MkDateSeparatedList> - </MkPagination> + <MkTl :events="timeline" :displayLimit="50" style="margin-top: var(--MI-margin);"> + <template #left="{ event }"> + <div> + <MkAvatar :user="event.user" style="width: 24px; height: 24px;"/> + </div> + </template> + <template #right="{ event, timestamp, delta }"> + <div style="margin: 4px 0;"> + <XModLog :key="event.id" :log="event"/> + </div> + </template> + </MkTl> + + <MkButton primary rounded style="margin: 0 auto;" @click="fetchMore">{{ i18n.ts.loadMore }}</MkButton> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> -import { computed, useTemplateRef, ref } from 'vue'; +import { computed, useTemplateRef, ref, watch } from 'vue'; import * as Misskey from 'misskey-js'; -import XHeader from './_header_.vue'; import XModLog from './modlog.ModLog.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkInput from '@/components/MkInput.vue'; -import MkPagination from '@/components/MkPagination.vue'; +import MkTl from '@/components/MkTl.vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; -import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue'; - -const logs = useTemplateRef('logs'); +import { misskeyApi } from '@/utility/misskey-api.js'; +import MkButton from '@/components/MkButton.vue'; const type = ref<string | null>(null); const moderatorId = ref(''); -const pagination = { - endpoint: 'admin/show-moderation-logs' as const, - limit: 30, - params: computed(() => ({ +const timeline = ref([]); + +watch([type, moderatorId], async () => { + const res = await misskeyApi('admin/show-moderation-logs', { + type: type.value, + userId: moderatorId.value === '' ? null : moderatorId.value, + }); + timeline.value = res.map(x => ({ + id: x.id, + timestamp: x.createdAt, + data: x, + })); +}, { immediate: true }); + +function fetchMore() { + const last = timeline.value[timeline.value.length - 1]; + misskeyApi('admin/show-moderation-logs', { type: type.value, userId: moderatorId.value === '' ? null : moderatorId.value, - })), -}; + untilId: last.id, + }).then(res => { + timeline.value.push(...res.map(x => ({ + id: x.id, + timestamp: x.createdAt, + data: x, + }))); + }); +} const headerActions = computed(() => []); diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue index da96eb4881..36f4392142 100644 --- a/packages/frontend/src/pages/admin/object-storage.vue +++ b/packages/frontend/src/pages/admin/object-storage.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :tabs="headerTabs"/></template> +<PageWithHeader :tabs="headerTabs"> <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> <FormSuspense :p="init"> <div class="_gaps_m"> @@ -79,12 +78,11 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSpacer> </div> </template> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; -import XHeader from './_header_.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkInput from '@/components/MkInput.vue'; import FormSuspense from '@/components/form/suspense.vue'; diff --git a/packages/frontend/src/pages/admin/performance.vue b/packages/frontend/src/pages/admin/performance.vue index 6bb0918fea..075db4ebef 100644 --- a/packages/frontend/src/pages/admin/performance.vue +++ b/packages/frontend/src/pages/admin/performance.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> <div class="_gaps"> <div class="_panel" style="padding: 16px;"> @@ -104,12 +103,11 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; -import XHeader from './_header_.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { fetchInstance } from '@/instance.js'; diff --git a/packages/frontend/src/pages/admin/relays.vue b/packages/frontend/src/pages/admin/relays.vue index a6280e7075..7803edc360 100644 --- a/packages/frontend/src/pages/admin/relays.vue +++ b/packages/frontend/src/pages/admin/relays.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="800"> <div class="_gaps"> <div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel" style="padding: 16px;"> @@ -20,13 +19,12 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; import * as Misskey from 'misskey-js'; -import XHeader from './_header_.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue index 7741064685..62777f59ef 100644 --- a/packages/frontend/src/pages/admin/roles.edit.vue +++ b/packages/frontend/src/pages/admin/roles.edit.vue @@ -4,28 +4,24 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :tabs="headerTabs"/></template> - <MkSpacer :contentMax="600" :marginMin="16" :marginMax="32"> - <XEditor v-if="data" v-model="data"/> - </MkSpacer> - <template #footer> - <div :class="$style.footer"> - <MkSpacer :contentMax="600" :marginMin="16" :marginMax="16"> - <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> - </MkSpacer> - </div> - </template> - </MkStickyContainer> -</div> +<PageWithHeader :tabs="headerTabs"> + <MkSpacer :contentMax="600" :marginMin="16" :marginMax="32"> + <XEditor v-if="data" v-model="data"/> + </MkSpacer> + <template #footer> + <div :class="$style.footer"> + <MkSpacer :contentMax="600" :marginMin="16" :marginMax="16"> + <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> + </MkSpacer> + </div> + </template> +</PageWithHeader> </template> <script lang="ts" setup> import { computed, ref } from 'vue'; import * as Misskey from 'misskey-js'; import { v4 as uuid } from 'uuid'; -import XHeader from './_header_.vue'; import XEditor from './roles.editor.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue index 887787e168..992f47bff5 100644 --- a/packages/frontend/src/pages/admin/roles.role.vue +++ b/packages/frontend/src/pages/admin/roles.role.vue @@ -4,66 +4,62 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> - <MkSpacer :contentMax="700"> - <div class="_gaps"> - <div class="_buttons"> - <MkButton primary rounded @click="edit"><i class="ti ti-pencil"></i> {{ i18n.ts.edit }}</MkButton> - <MkButton danger rounded @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> - </div> - <MkFolder> - <template #icon><i class="ti ti-info-circle"></i></template> - <template #label>{{ i18n.ts.info }}</template> - <XEditor :modelValue="role" readonly/> - </MkFolder> - <MkFolder v-if="role.target === 'manual'" defaultOpen> - <template #icon><i class="ti ti-users"></i></template> - <template #label>{{ i18n.ts.users }}</template> - <template #suffix>{{ role.usersCount }}</template> - <div class="_gaps"> - <MkButton primary rounded @click="assign"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> + <MkSpacer :contentMax="700"> + <div class="_gaps"> + <div class="_buttons"> + <MkButton primary rounded @click="edit"><i class="ti ti-pencil"></i> {{ i18n.ts.edit }}</MkButton> + <MkButton danger rounded @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> + </div> + <MkFolder> + <template #icon><i class="ti ti-info-circle"></i></template> + <template #label>{{ i18n.ts.info }}</template> + <XEditor :modelValue="role" readonly/> + </MkFolder> + <MkFolder v-if="role.target === 'manual'" defaultOpen> + <template #icon><i class="ti ti-users"></i></template> + <template #label>{{ i18n.ts.users }}</template> + <template #suffix>{{ role.usersCount }}</template> + <div class="_gaps"> + <MkButton primary rounded @click="assign"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton> - <MkPagination :pagination="usersPagination" :displayLimit="50"> - <template #empty> - <div class="_fullinfo"> - <img :src="infoImageUrl" draggable="false"/> - <div>{{ i18n.ts.noUsers }}</div> - </div> - </template> + <MkPagination :pagination="usersPagination" :displayLimit="50"> + <template #empty> + <div class="_fullinfo"> + <img :src="infoImageUrl" draggable="false"/> + <div>{{ i18n.ts.noUsers }}</div> + </div> + </template> - <template #default="{ items }"> - <div class="_gaps_s"> - <div v-for="item in items" :key="item.user.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedItems.includes(item.id) }]"> - <div :class="$style.userItemMain"> - <MkA :class="$style.userItemMainBody" :to="`/admin/user/${item.user.id}`"> - <MkUserCardMini :user="item.user"/> - </MkA> - <button class="_button" :class="$style.userToggle" @click="toggleItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> - <button class="_button" :class="$style.unassign" @click="unassign(item.user, $event)"><i class="ti ti-x"></i></button> - </div> - <div v-if="expandedItems.includes(item.id)" :class="$style.userItemSub"> - <div>Assigned: <MkTime :time="item.createdAt" mode="detail"/></div> - <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div> - <div v-else>Period: {{ i18n.ts.indefinitely }}</div> - </div> + <template #default="{ items }"> + <div class="_gaps_s"> + <div v-for="item in items" :key="item.user.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedItems.includes(item.id) }]"> + <div :class="$style.userItemMain"> + <MkA :class="$style.userItemMainBody" :to="`/admin/user/${item.user.id}`"> + <MkUserCardMini :user="item.user"/> + </MkA> + <button class="_button" :class="$style.userToggle" @click="toggleItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> + <button class="_button" :class="$style.unassign" @click="unassign(item.user, $event)"><i class="ti ti-x"></i></button> + </div> + <div v-if="expandedItems.includes(item.id)" :class="$style.userItemSub"> + <div>Assigned: <MkTime :time="item.createdAt" mode="detail"/></div> + <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div> + <div v-else>Period: {{ i18n.ts.indefinitely }}</div> </div> </div> - </template> - </MkPagination> - </div> - </MkFolder> - <MkInfo v-else>{{ i18n.ts._role.isConditionalRole }}</MkInfo> - </div> - </MkSpacer> - </MkStickyContainer> -</div> + </div> + </template> + </MkPagination> + </div> + </MkFolder> + <MkInfo v-else>{{ i18n.ts._role.isConditionalRole }}</MkInfo> + </div> + </MkSpacer> +</PageWithHeader> </template> <script lang="ts" setup> import { computed, reactive, ref } from 'vue'; -import XHeader from './_header_.vue'; import XEditor from './roles.editor.vue'; import MkFolder from '@/components/MkFolder.vue'; import * as os from '@/os.js'; diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 63e89a47f2..2e9a33d1b8 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -4,323 +4,319 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> - <MkSpacer :contentMax="700"> - <div class="_gaps"> - <MkFolder> - <template #label>{{ i18n.ts._role.baseRole }}</template> - <template #footer> - <MkButton primary rounded @click="updateBaseRole">{{ i18n.ts.save }}</MkButton> - </template> - <div class="_gaps_s"> - <MkInput v-model="baseRoleQ" type="search"> - <template #prefix><i class="ti ti-search"></i></template> - </MkInput> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> + <MkSpacer :contentMax="700"> + <div class="_gaps"> + <MkFolder> + <template #label>{{ i18n.ts._role.baseRole }}</template> + <template #footer> + <MkButton primary rounded @click="updateBaseRole">{{ i18n.ts.save }}</MkButton> + </template> + <div class="_gaps_s"> + <MkInput v-model="baseRoleQ" type="search"> + <template #prefix><i class="ti ti-search"></i></template> + </MkInput> - <MkFolder v-if="matchQuery([i18n.ts._role._options.rateLimitFactor, 'rateLimitFactor'])"> - <template #label>{{ i18n.ts._role._options.rateLimitFactor }}</template> - <template #suffix>{{ Math.floor(policies.rateLimitFactor * 100) }}%</template> - <MkRange :modelValue="policies.rateLimitFactor * 100" :min="30" :max="300" :step="10" :textConverter="(v) => `${v}%`" @update:modelValue="v => policies.rateLimitFactor = (v / 100)"> - <template #caption>{{ i18n.ts._role._options.descriptionOfRateLimitFactor }}</template> - </MkRange> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.rateLimitFactor, 'rateLimitFactor'])"> + <template #label>{{ i18n.ts._role._options.rateLimitFactor }}</template> + <template #suffix>{{ Math.floor(policies.rateLimitFactor * 100) }}%</template> + <MkRange :modelValue="policies.rateLimitFactor * 100" :min="30" :max="300" :step="10" :textConverter="(v) => `${v}%`" @update:modelValue="v => policies.rateLimitFactor = (v / 100)"> + <template #caption>{{ i18n.ts._role._options.descriptionOfRateLimitFactor }}</template> + </MkRange> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.gtlAvailable, 'gtlAvailable'])"> - <template #label>{{ i18n.ts._role._options.gtlAvailable }}</template> - <template #suffix>{{ policies.gtlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.gtlAvailable"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.gtlAvailable, 'gtlAvailable'])"> + <template #label>{{ i18n.ts._role._options.gtlAvailable }}</template> + <template #suffix>{{ policies.gtlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.gtlAvailable"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <!-- TODO translate --> - <MkFolder v-if="matchQuery([i18n.ts._role._options.btlAvailable, 'btlAvailable'])"> - <template #label>{{ i18n.ts._role._options.btlAvailable }}</template> - <template #suffix>{{ policies.btlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> - <div class="_gaps_s"> - <MkInfo :warn="true">After enabling this option navigate to the Moderation section to configure which instances should be shown.</MkInfo> - <MkSwitch v-model="policies.btlAvailable"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </div> - </MkFolder> - - <MkFolder v-if="matchQuery([i18n.ts._role._options.ltlAvailable, 'ltlAvailable'])"> - <template #label>{{ i18n.ts._role._options.ltlAvailable }}</template> - <template #suffix>{{ policies.ltlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.ltlAvailable"> + <!-- TODO translate --> + <MkFolder v-if="matchQuery([i18n.ts._role._options.btlAvailable, 'btlAvailable'])"> + <template #label>{{ i18n.ts._role._options.btlAvailable }}</template> + <template #suffix>{{ policies.btlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> + <div class="_gaps_s"> + <MkInfo :warn="true">After enabling this option navigate to the Moderation section to configure which instances should be shown.</MkInfo> + <MkSwitch v-model="policies.btlAvailable"> <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> - </MkFolder> + </div> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canPublicNote, 'canPublicNote'])"> - <template #label>{{ i18n.ts._role._options.canPublicNote }}</template> - <template #suffix>{{ policies.canPublicNote ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canPublicNote"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.ltlAvailable, 'ltlAvailable'])"> + <template #label>{{ i18n.ts._role._options.ltlAvailable }}</template> + <template #suffix>{{ policies.ltlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.ltlAvailable"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportNotes, 'canImportNotes'])"> - <template #label>{{ i18n.ts._role._options.canImportNotes }}</template> - <template #suffix>{{ policies.canImportNotes ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canImportNotes"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canPublicNote, 'canPublicNote'])"> + <template #label>{{ i18n.ts._role._options.canPublicNote }}</template> + <template #suffix>{{ policies.canPublicNote ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canPublicNote"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.scheduleNoteMax, 'scheduleNoteMax'])"> - <template #label>{{ i18n.ts._role._options.scheduleNoteMax }}</template> - <template #suffix>{{ policies.scheduleNoteMax }}</template> - <MkInput v-model="policies.scheduleNoteMax" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportNotes, 'canImportNotes'])"> + <template #label>{{ i18n.ts._role._options.canImportNotes }}</template> + <template #suffix>{{ policies.canImportNotes ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canImportNotes"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.chatAvailability, 'chatAvailability'])"> - <template #label>{{ i18n.ts._role._options.chatAvailability }}</template> - <template #suffix>{{ policies.chatAvailability === 'available' ? i18n.ts.yes : policies.chatAvailability === 'readonly' ? i18n.ts.readonly : i18n.ts.no }}</template> - <MkSelect v-model="policies.chatAvailability"> - <template #label>{{ i18n.ts.enable }}</template> - <option value="available">{{ i18n.ts.enabled }}</option> - <option value="readonly">{{ i18n.ts.readonly }}</option> - <option value="unavailable">{{ i18n.ts.disabled }}</option> - </MkSelect> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.scheduleNoteMax, 'scheduleNoteMax'])"> + <template #label>{{ i18n.ts._role._options.scheduleNoteMax }}</template> + <template #suffix>{{ policies.scheduleNoteMax }}</template> + <MkInput v-model="policies.scheduleNoteMax" type="number"> + </MkInput> + </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.chatAvailability, 'chatAvailability'])"> + <template #label>{{ i18n.ts._role._options.chatAvailability }}</template> + <template #suffix>{{ policies.chatAvailability === 'available' ? i18n.ts.yes : policies.chatAvailability === 'readonly' ? i18n.ts.readonly : i18n.ts.no }}</template> + <MkSelect v-model="policies.chatAvailability"> + <template #label>{{ i18n.ts.enable }}</template> + <option value="available">{{ i18n.ts.enabled }}</option> + <option value="readonly">{{ i18n.ts.readonly }}</option> + <option value="unavailable">{{ i18n.ts.disabled }}</option> + </MkSelect> + </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> - <MkSwitch v-model="policies.canInvite"> - <template #label>{{ i18n.ts.enable }}</template> - </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.inviteLimit, 'inviteLimit'])"> - <template #label>{{ i18n.ts._role._options.inviteLimit }}</template> - <template #suffix>{{ policies.inviteLimit }}</template> - <MkInput v-model="policies.inviteLimit" 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> + <MkSwitch v-model="policies.canInvite"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteLimitCycle, 'inviteLimitCycle'])"> - <template #label>{{ i18n.ts._role._options.inviteLimitCycle }}</template> - <template #suffix>{{ policies.inviteLimitCycle + i18n.ts._time.minute }}</template> - <MkInput v-model="policies.inviteLimitCycle" type="number"> - <template #suffix>{{ i18n.ts._time.minute }}</template> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteLimit, 'inviteLimit'])"> + <template #label>{{ i18n.ts._role._options.inviteLimit }}</template> + <template #suffix>{{ policies.inviteLimit }}</template> + <MkInput v-model="policies.inviteLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteExpirationTime, 'inviteExpirationTime'])"> - <template #label>{{ i18n.ts._role._options.inviteExpirationTime }}</template> - <template #suffix>{{ policies.inviteExpirationTime + i18n.ts._time.minute }}</template> - <MkInput v-model="policies.inviteExpirationTime" type="number"> - <template #suffix>{{ i18n.ts._time.minute }}</template> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteLimitCycle, 'inviteLimitCycle'])"> + <template #label>{{ i18n.ts._role._options.inviteLimitCycle }}</template> + <template #suffix>{{ policies.inviteLimitCycle + i18n.ts._time.minute }}</template> + <MkInput v-model="policies.inviteLimitCycle" type="number"> + <template #suffix>{{ i18n.ts._time.minute }}</template> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageAvatarDecorations, 'canManageAvatarDecorations'])"> - <template #label>{{ i18n.ts._role._options.canManageAvatarDecorations }}</template> - <template #suffix>{{ policies.canManageAvatarDecorations ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canManageAvatarDecorations"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteExpirationTime, 'inviteExpirationTime'])"> + <template #label>{{ i18n.ts._role._options.inviteExpirationTime }}</template> + <template #suffix>{{ policies.inviteExpirationTime + i18n.ts._time.minute }}</template> + <MkInput v-model="policies.inviteExpirationTime" type="number"> + <template #suffix>{{ i18n.ts._time.minute }}</template> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageCustomEmojis, 'canManageCustomEmojis'])"> - <template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template> - <template #suffix>{{ policies.canManageCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canManageCustomEmojis"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageAvatarDecorations, 'canManageAvatarDecorations'])"> + <template #label>{{ i18n.ts._role._options.canManageAvatarDecorations }}</template> + <template #suffix>{{ policies.canManageAvatarDecorations ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canManageAvatarDecorations"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])"> - <template #label>{{ i18n.ts._role._options.canSearchNotes }}</template> - <template #suffix>{{ policies.canSearchNotes ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canSearchNotes"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageCustomEmojis, 'canManageCustomEmojis'])"> + <template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template> + <template #suffix>{{ policies.canManageCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canManageCustomEmojis"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canUseTranslator, 'canSearchNotes'])"> - <template #label>{{ i18n.ts._role._options.canUseTranslator }}</template> - <template #suffix>{{ policies.canUseTranslator ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canUseTranslator"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])"> + <template #label>{{ i18n.ts._role._options.canSearchNotes }}</template> + <template #suffix>{{ policies.canSearchNotes ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canSearchNotes"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])"> - <template #label>{{ i18n.ts._role._options.driveCapacity }}</template> - <template #suffix>{{ policies.driveCapacityMb }}MB</template> - <MkInput v-model="policies.driveCapacityMb" type="number"> - <template #suffix>MB</template> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canUseTranslator, 'canSearchNotes'])"> + <template #label>{{ i18n.ts._role._options.canUseTranslator }}</template> + <template #suffix>{{ policies.canUseTranslator ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canUseTranslator"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.alwaysMarkNsfw, 'alwaysMarkNsfw'])"> - <template #label>{{ i18n.ts._role._options.alwaysMarkNsfw }}</template> - <template #suffix>{{ policies.alwaysMarkNsfw ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.alwaysMarkNsfw"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])"> + <template #label>{{ i18n.ts._role._options.driveCapacity }}</template> + <template #suffix>{{ policies.driveCapacityMb }}MB</template> + <MkInput v-model="policies.driveCapacityMb" type="number"> + <template #suffix>MB</template> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canUpdateBioMedia, 'canUpdateBioMedia'])"> - <template #label>{{ i18n.ts._role._options.canUpdateBioMedia }}</template> - <template #suffix>{{ policies.canUpdateBioMedia ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canUpdateBioMedia"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.alwaysMarkNsfw, 'alwaysMarkNsfw'])"> + <template #label>{{ i18n.ts._role._options.alwaysMarkNsfw }}</template> + <template #suffix>{{ policies.alwaysMarkNsfw ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.alwaysMarkNsfw"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])"> - <template #label>{{ i18n.ts._role._options.pinMax }}</template> - <template #suffix>{{ policies.pinLimit }}</template> - <MkInput v-model="policies.pinLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canUpdateBioMedia, 'canUpdateBioMedia'])"> + <template #label>{{ i18n.ts._role._options.canUpdateBioMedia }}</template> + <template #suffix>{{ policies.canUpdateBioMedia ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canUpdateBioMedia"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.antennaMax, 'antennaLimit'])"> - <template #label>{{ i18n.ts._role._options.antennaMax }}</template> - <template #suffix>{{ policies.antennaLimit }}</template> - <MkInput v-model="policies.antennaLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])"> + <template #label>{{ i18n.ts._role._options.pinMax }}</template> + <template #suffix>{{ policies.pinLimit }}</template> + <MkInput v-model="policies.pinLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.wordMuteMax, 'wordMuteLimit'])"> - <template #label>{{ i18n.ts._role._options.wordMuteMax }}</template> - <template #suffix>{{ policies.wordMuteLimit }}</template> - <MkInput v-model="policies.wordMuteLimit" type="number"> - <template #suffix>chars</template> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.antennaMax, 'antennaLimit'])"> + <template #label>{{ i18n.ts._role._options.antennaMax }}</template> + <template #suffix>{{ policies.antennaLimit }}</template> + <MkInput v-model="policies.antennaLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.webhookMax, 'webhookLimit'])"> - <template #label>{{ i18n.ts._role._options.webhookMax }}</template> - <template #suffix>{{ policies.webhookLimit }}</template> - <MkInput v-model="policies.webhookLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.wordMuteMax, 'wordMuteLimit'])"> + <template #label>{{ i18n.ts._role._options.wordMuteMax }}</template> + <template #suffix>{{ policies.wordMuteLimit }}</template> + <MkInput v-model="policies.wordMuteLimit" type="number"> + <template #suffix>chars</template> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.clipMax, 'clipLimit'])"> - <template #label>{{ i18n.ts._role._options.clipMax }}</template> - <template #suffix>{{ policies.clipLimit }}</template> - <MkInput v-model="policies.clipLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.webhookMax, 'webhookLimit'])"> + <template #label>{{ i18n.ts._role._options.webhookMax }}</template> + <template #suffix>{{ policies.webhookLimit }}</template> + <MkInput v-model="policies.webhookLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.noteEachClipsMax, 'noteEachClipsLimit'])"> - <template #label>{{ i18n.ts._role._options.noteEachClipsMax }}</template> - <template #suffix>{{ policies.noteEachClipsLimit }}</template> - <MkInput v-model="policies.noteEachClipsLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.clipMax, 'clipLimit'])"> + <template #label>{{ i18n.ts._role._options.clipMax }}</template> + <template #suffix>{{ policies.clipLimit }}</template> + <MkInput v-model="policies.clipLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.userListMax, 'userListLimit'])"> - <template #label>{{ i18n.ts._role._options.userListMax }}</template> - <template #suffix>{{ policies.userListLimit }}</template> - <MkInput v-model="policies.userListLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.noteEachClipsMax, 'noteEachClipsLimit'])"> + <template #label>{{ i18n.ts._role._options.noteEachClipsMax }}</template> + <template #suffix>{{ policies.noteEachClipsLimit }}</template> + <MkInput v-model="policies.noteEachClipsLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.userEachUserListsMax, 'userEachUserListsLimit'])"> - <template #label>{{ i18n.ts._role._options.userEachUserListsMax }}</template> - <template #suffix>{{ policies.userEachUserListsLimit }}</template> - <MkInput v-model="policies.userEachUserListsLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.userListMax, 'userListLimit'])"> + <template #label>{{ i18n.ts._role._options.userListMax }}</template> + <template #suffix>{{ policies.userListLimit }}</template> + <MkInput v-model="policies.userListLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canHideAds, 'canHideAds'])"> - <template #label>{{ i18n.ts._role._options.canHideAds }}</template> - <template #suffix>{{ policies.canHideAds ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canHideAds"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.userEachUserListsMax, 'userEachUserListsLimit'])"> + <template #label>{{ i18n.ts._role._options.userEachUserListsMax }}</template> + <template #suffix>{{ policies.userEachUserListsLimit }}</template> + <MkInput v-model="policies.userEachUserListsLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.avatarDecorationLimit, 'avatarDecorationLimit'])"> - <template #label>{{ i18n.ts._role._options.avatarDecorationLimit }}</template> - <template #suffix>{{ policies.avatarDecorationLimit }}</template> - <MkInput v-model="avatarDecorationLimit" type="number" :min="0" :max="16" @update:modelValue="updateAvatarDecorationLimit"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canHideAds, 'canHideAds'])"> + <template #label>{{ i18n.ts._role._options.canHideAds }}</template> + <template #suffix>{{ policies.canHideAds ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canHideAds"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportAntennas, 'canImportAntennas'])"> - <template #label>{{ i18n.ts._role._options.canImportAntennas }}</template> - <template #suffix>{{ policies.canImportAntennas ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canImportAntennas"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.avatarDecorationLimit, 'avatarDecorationLimit'])"> + <template #label>{{ i18n.ts._role._options.avatarDecorationLimit }}</template> + <template #suffix>{{ policies.avatarDecorationLimit }}</template> + <MkInput v-model="avatarDecorationLimit" type="number" :min="0" :max="16" @update:modelValue="updateAvatarDecorationLimit"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportBlocking, 'canImportBlocking'])"> - <template #label>{{ i18n.ts._role._options.canImportBlocking }}</template> - <template #suffix>{{ policies.canImportBlocking ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canImportBlocking"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportAntennas, 'canImportAntennas'])"> + <template #label>{{ i18n.ts._role._options.canImportAntennas }}</template> + <template #suffix>{{ policies.canImportAntennas ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canImportAntennas"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportFollowing, 'canImportFollowing'])"> - <template #label>{{ i18n.ts._role._options.canImportFollowing }}</template> - <template #suffix>{{ policies.canImportFollowing ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canImportFollowing"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportBlocking, 'canImportBlocking'])"> + <template #label>{{ i18n.ts._role._options.canImportBlocking }}</template> + <template #suffix>{{ policies.canImportBlocking ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canImportBlocking"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportMuting, 'canImportMuting'])"> - <template #label>{{ i18n.ts._role._options.canImportMuting }}</template> - <template #suffix>{{ policies.canImportMuting ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canImportMuting"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportFollowing, 'canImportFollowing'])"> + <template #label>{{ i18n.ts._role._options.canImportFollowing }}</template> + <template #suffix>{{ policies.canImportFollowing ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canImportFollowing"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportUserLists, 'canImportUserList'])"> - <template #label>{{ i18n.ts._role._options.canImportUserLists }}</template> - <template #suffix>{{ policies.canImportUserLists ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canImportUserLists"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> - </div> - </MkFolder> - <MkButton primary rounded @click="create"><i class="ti ti-plus"></i> {{ i18n.ts._role.new }}</MkButton> - <div class="_gaps_s"> - <MkFoldableSection> - <template #header>{{ i18n.ts._role.manualRoles }}</template> - <div class="_gaps_s"> - <MkRolePreview v-for="role in roles.filter(x => x.target === 'manual')" :key="role.id" :role="role" :forModeration="true"/> - </div> - </MkFoldableSection> - <MkFoldableSection> - <template #header>{{ i18n.ts._role.conditionalRoles }}</template> - <div class="_gaps_s"> - <MkRolePreview v-for="role in roles.filter(x => x.target === 'conditional')" :key="role.id" :role="role" :forModeration="true"/> - </div> - </MkFoldableSection> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportMuting, 'canImportMuting'])"> + <template #label>{{ i18n.ts._role._options.canImportMuting }}</template> + <template #suffix>{{ policies.canImportMuting ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canImportMuting"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + + <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportUserLists, 'canImportUserList'])"> + <template #label>{{ i18n.ts._role._options.canImportUserLists }}</template> + <template #suffix>{{ policies.canImportUserLists ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canImportUserLists"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> </div> + </MkFolder> + <MkButton primary rounded @click="create"><i class="ti ti-plus"></i> {{ i18n.ts._role.new }}</MkButton> + <div class="_gaps_s"> + <MkFoldableSection> + <template #header>{{ i18n.ts._role.manualRoles }}</template> + <div class="_gaps_s"> + <MkRolePreview v-for="role in roles.filter(x => x.target === 'manual')" :key="role.id" :role="role" :forModeration="true"/> + </div> + </MkFoldableSection> + <MkFoldableSection> + <template #header>{{ i18n.ts._role.conditionalRoles }}</template> + <div class="_gaps_s"> + <MkRolePreview v-for="role in roles.filter(x => x.target === 'conditional')" :key="role.id" :role="role" :forModeration="true"/> + </div> + </MkFoldableSection> </div> - </MkSpacer> - </MkStickyContainer> -</div> + </div> + </MkSpacer> +</PageWithHeader> </template> <script lang="ts" setup> import { computed, reactive, ref } from 'vue'; import { ROLE_POLICIES } from '@@/js/const.js'; -import XHeader from './_header_.vue'; import MkInput from '@/components/MkInput.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkSwitch from '@/components/MkSwitch.vue'; diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue index 84827b799f..537c788ccf 100644 --- a/packages/frontend/src/pages/admin/security.vue +++ b/packages/frontend/src/pages/admin/security.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> <div class="_gaps_m"> <MkFolder v-if="meta.federation !== 'none'"> @@ -89,13 +88,12 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; import XBotProtection from './bot-protection.vue'; -import XHeader from './_header_.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkRadios from '@/components/MkRadios.vue'; import MkSwitch from '@/components/MkSwitch.vue'; diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue index 3b0f1066f5..98cd0775fe 100644 --- a/packages/frontend/src/pages/admin/server-rules.vue +++ b/packages/frontend/src/pages/admin/server-rules.vue @@ -4,45 +4,41 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :tabs="headerTabs"/></template> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> - <div class="_gaps_m"> - <div>{{ i18n.ts._serverRules.description }}</div> - <Sortable - v-model="serverRules" - class="_gaps_m" - :itemKey="(_, i) => i" - :animation="150" - :handle="'.' + $style.itemHandle" - @start="e => e.item.classList.add('active')" - @end="e => e.item.classList.remove('active')" - > - <template #item="{element,index}"> - <div :class="$style.item"> - <div :class="$style.itemHeader"> - <div :class="$style.itemNumber" v-text="String(index + 1)"/> - <span :class="$style.itemHandle"><i class="ti ti-menu"/></span> - <button class="_button" :class="$style.itemRemove" @click="remove(index)"><i class="ti ti-x"></i></button> - </div> - <MkInput v-model="serverRules[index]"/> +<PageWithHeader :tabs="headerTabs"> + <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <div class="_gaps_m"> + <div>{{ i18n.ts._serverRules.description }}</div> + <Sortable + v-model="serverRules" + class="_gaps_m" + :itemKey="(_, i) => i" + :animation="150" + :handle="'.' + $style.itemHandle" + @start="e => e.item.classList.add('active')" + @end="e => e.item.classList.remove('active')" + > + <template #item="{element,index}"> + <div :class="$style.item"> + <div :class="$style.itemHeader"> + <div :class="$style.itemNumber" v-text="String(index + 1)"/> + <span :class="$style.itemHandle"><i class="ti ti-menu"/></span> + <button class="_button" :class="$style.itemRemove" @click="remove(index)"><i class="ti ti-x"></i></button> </div> - </template> - </Sortable> - <div :class="$style.commands"> - <MkButton rounded @click="serverRules.push('')"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> - <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> - </div> + <MkInput v-model="serverRules[index]"/> + </div> + </template> + </Sortable> + <div :class="$style.commands"> + <MkButton rounded @click="serverRules.push('')"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> + <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> </div> - </MkSpacer> - </MkStickyContainer> -</div> + </div> + </MkSpacer> +</PageWithHeader> </template> <script lang="ts" setup> import { defineAsyncComponent, ref, computed } from 'vue'; -import XHeader from './_header_.vue'; import * as os from '@/os.js'; import { fetchInstance, instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue index 50187a03ee..21fd1b4b1a 100644 --- a/packages/frontend/src/pages/admin/settings.vue +++ b/packages/frontend/src/pages/admin/settings.vue @@ -4,294 +4,290 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :tabs="headerTabs"/></template> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> - <div class="_gaps_m"> - <MkFolder :defaultOpen="true"> - <template #icon><i class="ti ti-info-circle"></i></template> - <template #label>{{ i18n.ts.info }}</template> - <template v-if="infoForm.modified.value" #footer> - <MkFormFooter :form="infoForm"/> - </template> - - <div class="_gaps"> - <MkInput v-model="infoForm.state.name"> - <template #label>{{ i18n.ts.instanceName }}<span v-if="infoForm.modifiedStates.name" class="_modified">{{ i18n.ts.modified }}</span></template> - </MkInput> +<PageWithHeader :tabs="headerTabs"> + <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <div class="_gaps_m"> + <MkFolder :defaultOpen="true"> + <template #icon><i class="ti ti-info-circle"></i></template> + <template #label>{{ i18n.ts.info }}</template> + <template v-if="infoForm.modified.value" #footer> + <MkFormFooter :form="infoForm"/> + </template> - <MkInput v-model="infoForm.state.shortName"> - <template #label>{{ i18n.ts._serverSettings.shortName }} ({{ i18n.ts.optional }})<span v-if="infoForm.modifiedStates.shortName" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._serverSettings.shortNameDescription }}</template> - </MkInput> + <div class="_gaps"> + <MkInput v-model="infoForm.state.name"> + <template #label>{{ i18n.ts.instanceName }}<span v-if="infoForm.modifiedStates.name" class="_modified">{{ i18n.ts.modified }}</span></template> + </MkInput> - <MkTextarea v-model="infoForm.state.description"> - <template #label>{{ i18n.ts.instanceDescription }}<span v-if="infoForm.modifiedStates.description" class="_modified">{{ i18n.ts.modified }}</span></template> - </MkTextarea> + <MkInput v-model="infoForm.state.shortName"> + <template #label>{{ i18n.ts._serverSettings.shortName }} ({{ i18n.ts.optional }})<span v-if="infoForm.modifiedStates.shortName" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._serverSettings.shortNameDescription }}</template> + </MkInput> - <FormSplit :minWidth="300"> - <MkInput v-model="infoForm.state.maintainerName"> - <template #label>{{ i18n.ts.maintainerName }}<span v-if="infoForm.modifiedStates.maintainerName" class="_modified">{{ i18n.ts.modified }}</span></template> - </MkInput> - - <MkInput v-model="infoForm.state.maintainerEmail" type="email"> - <template #label>{{ i18n.ts.maintainerEmail }}<span v-if="infoForm.modifiedStates.maintainerEmail" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #prefix><i class="ti ti-mail"></i></template> - </MkInput> - </FormSplit> + <MkTextarea v-model="infoForm.state.description"> + <template #label>{{ i18n.ts.instanceDescription }}<span v-if="infoForm.modifiedStates.description" class="_modified">{{ i18n.ts.modified }}</span></template> + </MkTextarea> - <MkInput v-model="infoForm.state.tosUrl" type="url"> - <template #label>{{ i18n.ts.tosUrl }}<span v-if="infoForm.modifiedStates.tosUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #prefix><i class="ti ti-link"></i></template> + <FormSplit :minWidth="300"> + <MkInput v-model="infoForm.state.maintainerName"> + <template #label>{{ i18n.ts.maintainerName }}<span v-if="infoForm.modifiedStates.maintainerName" class="_modified">{{ i18n.ts.modified }}</span></template> </MkInput> - <MkInput v-model="infoForm.state.privacyPolicyUrl" type="url"> - <template #label>{{ i18n.ts.privacyPolicyUrl }}<span v-if="infoForm.modifiedStates.privacyPolicyUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #prefix><i class="ti ti-link"></i></template> + <MkInput v-model="infoForm.state.maintainerEmail" type="email"> + <template #label>{{ i18n.ts.maintainerEmail }}<span v-if="infoForm.modifiedStates.maintainerEmail" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #prefix><i class="ti ti-mail"></i></template> </MkInput> + </FormSplit> - <MkInput v-model="infoForm.state.inquiryUrl" type="url"> - <template #label>{{ i18n.ts._serverSettings.inquiryUrl }}<span v-if="infoForm.modifiedStates.inquiryUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</template> - <template #prefix><i class="ti ti-link"></i></template> - </MkInput> + <MkInput v-model="infoForm.state.tosUrl" type="url"> + <template #label>{{ i18n.ts.tosUrl }}<span v-if="infoForm.modifiedStates.tosUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #prefix><i class="ti ti-link"></i></template> + </MkInput> - <MkInput v-model="infoForm.state.repositoryUrl" type="url"> - <template #label>{{ i18n.ts.repositoryUrl }}<span v-if="infoForm.modifiedStates.repositoryUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.repositoryUrlDescription }}</template> - <template #prefix><i class="ti ti-link"></i></template> - </MkInput> + <MkInput v-model="infoForm.state.privacyPolicyUrl" type="url"> + <template #label>{{ i18n.ts.privacyPolicyUrl }}<span v-if="infoForm.modifiedStates.privacyPolicyUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #prefix><i class="ti ti-link"></i></template> + </MkInput> - <MkInfo v-if="!instance.providesTarball && !infoForm.state.repositoryUrl" warn> - {{ i18n.ts.repositoryUrlOrTarballRequired }} - </MkInfo> + <MkInput v-model="infoForm.state.inquiryUrl" type="url"> + <template #label>{{ i18n.ts._serverSettings.inquiryUrl }}<span v-if="infoForm.modifiedStates.inquiryUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</template> + <template #prefix><i class="ti ti-link"></i></template> + </MkInput> - <MkInput v-model="infoForm.state.impressumUrl" type="url"> - <template #label>{{ i18n.ts.impressumUrl }}<span v-if="infoForm.modifiedStates.impressumUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.impressumDescription }}</template> - <template #prefix><i class="ti ti-link"></i></template> - </MkInput> + <MkInput v-model="infoForm.state.repositoryUrl" type="url"> + <template #label>{{ i18n.ts.repositoryUrl }}<span v-if="infoForm.modifiedStates.repositoryUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.repositoryUrlDescription }}</template> + <template #prefix><i class="ti ti-link"></i></template> + </MkInput> - <MkInput v-model="infoForm.state.donationUrl" type="url"> - <template #label>{{ i18n.ts.donationUrl }}</template> - <template #prefix><i class="ph-link ph-bold ph-lg"></i></template> - </MkInput> - </div> - </MkFolder> + <MkInfo v-if="!instance.providesTarball && !infoForm.state.repositoryUrl" warn> + {{ i18n.ts.repositoryUrlOrTarballRequired }} + </MkInfo> - <MkFolder> - <template #icon><i class="ti ti-user-star"></i></template> - <template #label>{{ i18n.ts.pinnedUsers }}</template> - <template v-if="pinnedUsersForm.modified.value" #footer> - <MkFormFooter :form="pinnedUsersForm"/> - </template> + <MkInput v-model="infoForm.state.impressumUrl" type="url"> + <template #label>{{ i18n.ts.impressumUrl }}<span v-if="infoForm.modifiedStates.impressumUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.impressumDescription }}</template> + <template #prefix><i class="ti ti-link"></i></template> + </MkInput> - <MkTextarea v-model="pinnedUsersForm.state.pinnedUsers"> - <template #label>{{ i18n.ts.pinnedUsers }}<span v-if="pinnedUsersForm.modifiedStates.pinnedUsers" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.pinnedUsersDescription }}</template> - </MkTextarea> - </MkFolder> + <MkInput v-model="infoForm.state.donationUrl" type="url"> + <template #label>{{ i18n.ts.donationUrl }}</template> + <template #prefix><i class="ph-link ph-bold ph-lg"></i></template> + </MkInput> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-cloud"></i></template> - <template #label>{{ i18n.ts.files }}</template> - <template v-if="filesForm.modified.value" #footer> - <MkFormFooter :form="filesForm"/> - </template> + <MkFolder> + <template #icon><i class="ti ti-user-star"></i></template> + <template #label>{{ i18n.ts.pinnedUsers }}</template> + <template v-if="pinnedUsersForm.modified.value" #footer> + <MkFormFooter :form="pinnedUsersForm"/> + </template> - <div class="_gaps"> - <MkSwitch v-model="filesForm.state.cacheRemoteFiles"> - <template #label>{{ i18n.ts.cacheRemoteFiles }}<span v-if="filesForm.modifiedStates.cacheRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}{{ i18n.ts.youCanCleanRemoteFilesCache }}</template> - </MkSwitch> + <MkTextarea v-model="pinnedUsersForm.state.pinnedUsers"> + <template #label>{{ i18n.ts.pinnedUsers }}<span v-if="pinnedUsersForm.modifiedStates.pinnedUsers" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.pinnedUsersDescription }}</template> + </MkTextarea> + </MkFolder> - <template v-if="filesForm.state.cacheRemoteFiles"> - <MkSwitch v-model="filesForm.state.cacheRemoteSensitiveFiles"> - <template #label>{{ i18n.ts.cacheRemoteSensitiveFiles }}<span v-if="filesForm.modifiedStates.cacheRemoteSensitiveFiles" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.cacheRemoteSensitiveFilesDescription }}</template> - </MkSwitch> - </template> - </div> - </MkFolder> + <MkFolder> + <template #icon><i class="ti ti-cloud"></i></template> + <template #label>{{ i18n.ts.files }}</template> + <template v-if="filesForm.modified.value" #footer> + <MkFormFooter :form="filesForm"/> + </template> - <MkFolder> - <template #icon><i class="ti ti-world-cog"></i></template> - <template #label>ServiceWorker</template> - <template v-if="serviceWorkerForm.modified.value" #footer> - <MkFormFooter :form="serviceWorkerForm"/> - </template> + <div class="_gaps"> + <MkSwitch v-model="filesForm.state.cacheRemoteFiles"> + <template #label>{{ i18n.ts.cacheRemoteFiles }}<span v-if="filesForm.modifiedStates.cacheRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}{{ i18n.ts.youCanCleanRemoteFilesCache }}</template> + </MkSwitch> - <div class="_gaps"> - <MkSwitch v-model="serviceWorkerForm.state.enableServiceWorker"> - <template #label>{{ i18n.ts.enableServiceworker }}<span v-if="serviceWorkerForm.modifiedStates.enableServiceWorker" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.serviceworkerInfo }}</template> + <template v-if="filesForm.state.cacheRemoteFiles"> + <MkSwitch v-model="filesForm.state.cacheRemoteSensitiveFiles"> + <template #label>{{ i18n.ts.cacheRemoteSensitiveFiles }}<span v-if="filesForm.modifiedStates.cacheRemoteSensitiveFiles" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.cacheRemoteSensitiveFilesDescription }}</template> </MkSwitch> + </template> + </div> + </MkFolder> - <template v-if="serviceWorkerForm.state.enableServiceWorker"> - <MkInput v-model="serviceWorkerForm.state.swPublicKey"> - <template #label>Public key<span v-if="serviceWorkerForm.modifiedStates.swPublicKey" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #prefix><i class="ti ti-key"></i></template> - </MkInput> + <MkFolder> + <template #icon><i class="ti ti-world-cog"></i></template> + <template #label>ServiceWorker</template> + <template v-if="serviceWorkerForm.modified.value" #footer> + <MkFormFooter :form="serviceWorkerForm"/> + </template> - <MkInput v-model="serviceWorkerForm.state.swPrivateKey"> - <template #label>Private key<span v-if="serviceWorkerForm.modifiedStates.swPrivateKey" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #prefix><i class="ti ti-key"></i></template> - </MkInput> + <div class="_gaps"> + <MkSwitch v-model="serviceWorkerForm.state.enableServiceWorker"> + <template #label>{{ i18n.ts.enableServiceworker }}<span v-if="serviceWorkerForm.modifiedStates.enableServiceWorker" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.serviceworkerInfo }}</template> + </MkSwitch> - <MkButton primary @click="genKeys">{{ i18n.ts.genKeys }}</MkButton> - </template> - </div> - </MkFolder> + <template v-if="serviceWorkerForm.state.enableServiceWorker"> + <MkInput v-model="serviceWorkerForm.state.swPublicKey"> + <template #label>Public key<span v-if="serviceWorkerForm.modifiedStates.swPublicKey" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #prefix><i class="ti ti-key"></i></template> + </MkInput> + + <MkInput v-model="serviceWorkerForm.state.swPrivateKey"> + <template #label>Private key<span v-if="serviceWorkerForm.modifiedStates.swPrivateKey" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #prefix><i class="ti ti-key"></i></template> + </MkInput> - <MkFolder> - <template #icon><i class="ph-faders ph-bold ph-lg ti-fw"></i></template> - <template #label>{{ i18n.ts.otherSettings }}</template> - <template v-if="otherForm.modified.value" #footer> - <MkFormFooter :form="otherForm"/> + <MkButton primary @click="genKeys">{{ i18n.ts.genKeys }}</MkButton> </template> + </div> + </MkFolder> - <div class="_gaps"> - <MkSwitch v-model="otherForm.state.enableAchievements"> - <template #label>{{ i18n.ts.enableAchievements }}<span v-if="otherForm.modifiedStates.enableAchievements" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.turnOffAchievements}}</template> - </MkSwitch> + <MkFolder> + <template #icon><i class="ph-faders ph-bold ph-lg ti-fw"></i></template> + <template #label>{{ i18n.ts.otherSettings }}</template> + <template v-if="otherForm.modified.value" #footer> + <MkFormFooter :form="otherForm"/> + </template> - <MkSwitch v-model="otherForm.state.enableBotTrending"> - <template #label>{{ i18n.ts.enableBotTrending }}<span v-if="otherForm.modifiedStates.enableBotTrending" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.turnOffBotTrending }}</template> - </MkSwitch> + <div class="_gaps"> + <MkSwitch v-model="otherForm.state.enableAchievements"> + <template #label>{{ i18n.ts.enableAchievements }}<span v-if="otherForm.modifiedStates.enableAchievements" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.turnOffAchievements}}</template> + </MkSwitch> - <MkTextarea v-model="otherForm.state.robotsTxt"> - <template #label>{{ i18n.ts.robotsTxt }}<span v-if="otherForm.modifiedStates.robotsTxt" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.robotsTxtDescription }}</template> - </MkTextarea> - </div> - </MkFolder> + <MkSwitch v-model="otherForm.state.enableBotTrending"> + <template #label>{{ i18n.ts.enableBotTrending }}<span v-if="otherForm.modifiedStates.enableBotTrending" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.turnOffBotTrending }}</template> + </MkSwitch> - <MkFolder> - <template #icon><i class="ti ti-ad"></i></template> - <template #label>{{ i18n.ts._ad.adsSettings }}</template> - <template v-if="adForm.modified.value" #footer> - <MkFormFooter :form="adForm"/> - </template> + <MkTextarea v-model="otherForm.state.robotsTxt"> + <template #label>{{ i18n.ts.robotsTxt }}<span v-if="otherForm.modifiedStates.robotsTxt" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.robotsTxtDescription }}</template> + </MkTextarea> + </div> + </MkFolder> - <div class="_gaps"> - <div class="_gaps_s"> - <MkInput v-model="adForm.state.notesPerOneAd" :min="0" type="number"> - <template #label>{{ i18n.ts._ad.notesPerOneAd }}<span v-if="adForm.modifiedStates.notesPerOneAd" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._ad.setZeroToDisable }}</template> - </MkInput> - <MkInfo v-if="adForm.state.notesPerOneAd > 0 && adForm.state.notesPerOneAd < 20" :warn="true"> - {{ i18n.ts._ad.adsTooClose }} - </MkInfo> - </div> + <MkFolder> + <template #icon><i class="ti ti-ad"></i></template> + <template #label>{{ i18n.ts._ad.adsSettings }}</template> + <template v-if="adForm.modified.value" #footer> + <MkFormFooter :form="adForm"/> + </template> + + <div class="_gaps"> + <div class="_gaps_s"> + <MkInput v-model="adForm.state.notesPerOneAd" :min="0" type="number"> + <template #label>{{ i18n.ts._ad.notesPerOneAd }}<span v-if="adForm.modifiedStates.notesPerOneAd" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._ad.setZeroToDisable }}</template> + </MkInput> + <MkInfo v-if="adForm.state.notesPerOneAd > 0 && adForm.state.notesPerOneAd < 20" :warn="true"> + {{ i18n.ts._ad.adsTooClose }} + </MkInfo> </div> - </MkFolder> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-world-search"></i></template> - <template #label>{{ i18n.ts._urlPreviewSetting.title }}</template> - <template v-if="urlPreviewForm.modified.value" #footer> - <MkFormFooter :form="urlPreviewForm"/> - </template> + <MkFolder> + <template #icon><i class="ti ti-world-search"></i></template> + <template #label>{{ i18n.ts._urlPreviewSetting.title }}</template> + <template v-if="urlPreviewForm.modified.value" #footer> + <MkFormFooter :form="urlPreviewForm"/> + </template> - <div class="_gaps"> - <MkSwitch v-model="urlPreviewForm.state.urlPreviewEnabled"> - <template #label>{{ i18n.ts._urlPreviewSetting.enable }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewEnabled" class="_modified">{{ i18n.ts.modified }}</span></template> + <div class="_gaps"> + <MkSwitch v-model="urlPreviewForm.state.urlPreviewEnabled"> + <template #label>{{ i18n.ts._urlPreviewSetting.enable }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewEnabled" class="_modified">{{ i18n.ts.modified }}</span></template> + </MkSwitch> + + <template v-if="urlPreviewForm.state.urlPreviewEnabled"> + <MkSwitch v-model="urlPreviewForm.state.urlPreviewRequireContentLength"> + <template #label>{{ i18n.ts._urlPreviewSetting.requireContentLength }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewRequireContentLength" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._urlPreviewSetting.requireContentLengthDescription }}</template> </MkSwitch> - <template v-if="urlPreviewForm.state.urlPreviewEnabled"> - <MkSwitch v-model="urlPreviewForm.state.urlPreviewRequireContentLength"> - <template #label>{{ i18n.ts._urlPreviewSetting.requireContentLength }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewRequireContentLength" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._urlPreviewSetting.requireContentLengthDescription }}</template> - </MkSwitch> + <MkInput v-model="urlPreviewForm.state.urlPreviewMaximumContentLength" type="number"> + <template #label>{{ i18n.ts._urlPreviewSetting.maximumContentLength }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewMaximumContentLength" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._urlPreviewSetting.maximumContentLengthDescription }}</template> + </MkInput> - <MkInput v-model="urlPreviewForm.state.urlPreviewMaximumContentLength" type="number"> - <template #label>{{ i18n.ts._urlPreviewSetting.maximumContentLength }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewMaximumContentLength" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._urlPreviewSetting.maximumContentLengthDescription }}</template> - </MkInput> + <MkInput v-model="urlPreviewForm.state.urlPreviewTimeout" type="number"> + <template #label>{{ i18n.ts._urlPreviewSetting.timeout }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewTimeout" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._urlPreviewSetting.timeoutDescription }}</template> + </MkInput> - <MkInput v-model="urlPreviewForm.state.urlPreviewTimeout" type="number"> - <template #label>{{ i18n.ts._urlPreviewSetting.timeout }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewTimeout" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._urlPreviewSetting.timeoutDescription }}</template> - </MkInput> + <MkInput v-model="urlPreviewForm.state.urlPreviewUserAgent" type="text"> + <template #label>{{ i18n.ts._urlPreviewSetting.userAgent }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewUserAgent" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._urlPreviewSetting.userAgentDescription }}</template> + </MkInput> - <MkInput v-model="urlPreviewForm.state.urlPreviewUserAgent" type="text"> - <template #label>{{ i18n.ts._urlPreviewSetting.userAgent }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewUserAgent" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._urlPreviewSetting.userAgentDescription }}</template> + <div> + <MkInput v-model="urlPreviewForm.state.urlPreviewSummaryProxyUrl" type="text"> + <template #label>{{ i18n.ts._urlPreviewSetting.summaryProxy }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewSummaryProxyUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>[{{ i18n.ts.notUsePleaseLeaveBlank }}] {{ i18n.ts._urlPreviewSetting.summaryProxyDescription }}</template> </MkInput> - <div> - <MkInput v-model="urlPreviewForm.state.urlPreviewSummaryProxyUrl" type="text"> - <template #label>{{ i18n.ts._urlPreviewSetting.summaryProxy }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewSummaryProxyUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>[{{ i18n.ts.notUsePleaseLeaveBlank }}] {{ i18n.ts._urlPreviewSetting.summaryProxyDescription }}</template> - </MkInput> - - <div :class="$style.subCaption"> - {{ i18n.ts._urlPreviewSetting.summaryProxyDescription2 }} - <ul style="padding-left: 20px; margin: 4px 0"> - <li>{{ i18n.ts._urlPreviewSetting.timeout }} / key:timeout</li> - <li>{{ i18n.ts._urlPreviewSetting.maximumContentLength }} / key:contentLengthLimit</li> - <li>{{ i18n.ts._urlPreviewSetting.requireContentLength }} / key:contentLengthRequired</li> - <li>{{ i18n.ts._urlPreviewSetting.userAgent }} / key:userAgent</li> - </ul> - </div> + <div :class="$style.subCaption"> + {{ i18n.ts._urlPreviewSetting.summaryProxyDescription2 }} + <ul style="padding-left: 20px; margin: 4px 0"> + <li>{{ i18n.ts._urlPreviewSetting.timeout }} / key:timeout</li> + <li>{{ i18n.ts._urlPreviewSetting.maximumContentLength }} / key:contentLengthLimit</li> + <li>{{ i18n.ts._urlPreviewSetting.requireContentLength }} / key:contentLengthRequired</li> + <li>{{ i18n.ts._urlPreviewSetting.userAgent }} / key:userAgent</li> + </ul> </div> - </template> - </div> - </MkFolder> - - <MkFolder> - <template #icon><i class="ti ti-planet"></i></template> - <template #label>{{ i18n.ts.federation }}</template> - <template v-if="federationForm.savedState.federation === 'all'" #suffix>{{ i18n.ts.all }}</template> - <template v-else-if="federationForm.savedState.federation === 'specified'" #suffix>{{ i18n.ts.specifyHost }}</template> - <template v-else-if="federationForm.savedState.federation === 'none'" #suffix>{{ i18n.ts.none }}</template> - <template v-if="federationForm.modified.value" #footer> - <MkFormFooter :form="federationForm"/> + </div> </template> + </div> + </MkFolder> - <div class="_gaps"> - <MkRadios v-model="federationForm.state.federation"> - <template #label>{{ i18n.ts.behavior }}<span v-if="federationForm.modifiedStates.federation" class="_modified">{{ i18n.ts.modified }}</span></template> - <option value="all">{{ i18n.ts.all }}</option> - <option value="specified">{{ i18n.ts.specifyHost }}</option> - <option value="none">{{ i18n.ts.none }}</option> - </MkRadios> + <MkFolder> + <template #icon><i class="ti ti-planet"></i></template> + <template #label>{{ i18n.ts.federation }}</template> + <template v-if="federationForm.savedState.federation === 'all'" #suffix>{{ i18n.ts.all }}</template> + <template v-else-if="federationForm.savedState.federation === 'specified'" #suffix>{{ i18n.ts.specifyHost }}</template> + <template v-else-if="federationForm.savedState.federation === 'none'" #suffix>{{ i18n.ts.none }}</template> + <template v-if="federationForm.modified.value" #footer> + <MkFormFooter :form="federationForm"/> + </template> - <MkTextarea v-if="federationForm.state.federation === 'specified'" v-model="federationForm.state.federationHosts"> - <template #label>{{ i18n.ts.federationAllowedHosts }}<span v-if="federationForm.modifiedStates.federationHosts" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.federationAllowedHostsDescription }}</template> - </MkTextarea> - </div> - </MkFolder> + <div class="_gaps"> + <MkRadios v-model="federationForm.state.federation"> + <template #label>{{ i18n.ts.behavior }}<span v-if="federationForm.modifiedStates.federation" class="_modified">{{ i18n.ts.modified }}</span></template> + <option value="all">{{ i18n.ts.all }}</option> + <option value="specified">{{ i18n.ts.specifyHost }}</option> + <option value="none">{{ i18n.ts.none }}</option> + </MkRadios> - <MkFolder> - <template #icon><i class="ti ti-ghost"></i></template> - <template #label>{{ i18n.ts.proxyAccount }}</template> - <template v-if="proxyAccountForm.modified.value" #footer> - <MkFormFooter :form="proxyAccountForm"/> - </template> + <MkTextarea v-if="federationForm.state.federation === 'specified'" v-model="federationForm.state.federationHosts"> + <template #label>{{ i18n.ts.federationAllowedHosts }}<span v-if="federationForm.modifiedStates.federationHosts" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.federationAllowedHostsDescription }}</template> + </MkTextarea> + </div> + </MkFolder> - <div class="_gaps"> - <MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo> + <MkFolder> + <template #icon><i class="ti ti-ghost"></i></template> + <template #label>{{ i18n.ts.proxyAccount }}</template> + <template v-if="proxyAccountForm.modified.value" #footer> + <MkFormFooter :form="proxyAccountForm"/> + </template> - <MkTextarea v-model="proxyAccountForm.state.description" :max="500" tall mfmAutocomplete :mfmPreview="true"> - <template #label>{{ i18n.ts._profile.description }}</template> - <template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template> - </MkTextarea> - </div> - </MkFolder> - </div> - </MkSpacer> - </MkStickyContainer> -</div> + <div class="_gaps"> + <MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo> + + <MkTextarea v-model="proxyAccountForm.state.description" :max="500" tall mfmAutocomplete :mfmPreview="true"> + <template #label>{{ i18n.ts._profile.description }}</template> + <template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template> + </MkTextarea> + </div> + </MkFolder> + </div> + </MkSpacer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed, reactive } from 'vue'; -import XHeader from './_header_.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkInput from '@/components/MkInput.vue'; import MkTextarea from '@/components/MkTextarea.vue'; diff --git a/packages/frontend/src/pages/admin/system-webhook.vue b/packages/frontend/src/pages/admin/system-webhook.vue index d8eb9b92ee..a3214028e6 100644 --- a/packages/frontend/src/pages/admin/system-webhook.vue +++ b/packages/frontend/src/pages/admin/system-webhook.vue @@ -4,11 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header> - <XHeader :actions="headerActions" :tabs="headerTabs"/> - </template> - +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="900"> <div class="_gaps_m"> <MkButton primary @click="onCreateWebhookClicked"> @@ -22,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only </FormSection> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> @@ -32,7 +28,6 @@ import XItem from './system-webhook.item.vue'; import FormSection from '@/components/form/section.vue'; import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; -import XHeader from '@/pages/admin/_header_.vue'; import MkButton from '@/components/MkButton.vue'; import { misskeyApi } from '@/utility/misskey-api.js'; import { showSystemWebhookEditorDialog } from '@/components/MkSystemWebhookEditor.impl.js'; diff --git a/packages/frontend/src/pages/admin/users.vue b/packages/frontend/src/pages/admin/users.vue index 3b95b78c72..d37f7dbf80 100644 --- a/packages/frontend/src/pages/admin/users.vue +++ b/packages/frontend/src/pages/admin/users.vue @@ -4,66 +4,62 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> - <MkSpacer :contentMax="900"> - <div class="_gaps"> - <div :class="$style.inputs"> - <MkButton style="margin-left: auto" @click="resetQuery">{{ i18n.ts.reset }}</MkButton> - </div> - <div :class="$style.inputs"> - <MkSelect v-model="sort" style="flex: 1;"> - <template #label>{{ i18n.ts.sort }}</template> - <option value="-createdAt">{{ i18n.ts.registeredDate }} ({{ i18n.ts.ascendingOrder }})</option> - <option value="+createdAt">{{ i18n.ts.registeredDate }} ({{ i18n.ts.descendingOrder }})</option> - <option value="-updatedAt">{{ i18n.ts.lastUsed }} ({{ i18n.ts.ascendingOrder }})</option> - <option value="+updatedAt">{{ i18n.ts.lastUsed }} ({{ i18n.ts.descendingOrder }})</option> - </MkSelect> - <MkSelect v-model="state" style="flex: 1;"> - <template #label>{{ i18n.ts.state }}</template> - <option value="all">{{ i18n.ts.all }}</option> - <option value="available">{{ i18n.ts.normal }}</option> - <option value="approved">{{ i18n.ts.notApproved }}</option> - <option value="admin">{{ i18n.ts.administrator }}</option> - <option value="moderator">{{ i18n.ts.moderator }}</option> - <option value="suspended">{{ i18n.ts.suspend }}</option> - </MkSelect> - <MkSelect v-model="origin" style="flex: 1;"> - <template #label>{{ i18n.ts.instance }}</template> - <option value="combined">{{ i18n.ts.all }}</option> - <option value="local">{{ i18n.ts.local }}</option> - <option value="remote">{{ i18n.ts.remote }}</option> - </MkSelect> - </div> - <div :class="$style.inputs"> - <MkInput v-model="searchUsername" style="flex: 1;" type="text" :spellcheck="false" debounce> - <template #prefix>@</template> - <template #label>{{ i18n.ts.username }}</template> - </MkInput> - <MkInput v-model="searchHost" style="flex: 1;" type="text" :spellcheck="false" :disabled="pagination.params.origin === 'local'" debounce> - <template #prefix>@</template> - <template #label>{{ i18n.ts.host }}</template> - </MkInput> - </div> - - <MkPagination v-slot="{items}" ref="paginationComponent" :pagination="pagination" :displayLimit="50"> - <div :class="$style.users"> - <MkA v-for="user in items" :key="user.id" v-tooltip.mfm="`Last posted: ${dateString(user.updatedAt)}`" :class="$style.user" :to="`/admin/user/${user.id}`"> - <MkUserCardMini :user="user"/> - </MkA> - </div> - </MkPagination> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> + <MkSpacer :contentMax="900"> + <div class="_gaps"> + <div :class="$style.inputs"> + <MkButton style="margin-left: auto" @click="resetQuery">{{ i18n.ts.reset }}</MkButton> </div> - </MkSpacer> - </MkStickyContainer> -</div> + <div :class="$style.inputs"> + <MkSelect v-model="sort" style="flex: 1;"> + <template #label>{{ i18n.ts.sort }}</template> + <option value="-createdAt">{{ i18n.ts.registeredDate }} ({{ i18n.ts.ascendingOrder }})</option> + <option value="+createdAt">{{ i18n.ts.registeredDate }} ({{ i18n.ts.descendingOrder }})</option> + <option value="-updatedAt">{{ i18n.ts.lastUsed }} ({{ i18n.ts.ascendingOrder }})</option> + <option value="+updatedAt">{{ i18n.ts.lastUsed }} ({{ i18n.ts.descendingOrder }})</option> + </MkSelect> + <MkSelect v-model="state" style="flex: 1;"> + <template #label>{{ i18n.ts.state }}</template> + <option value="all">{{ i18n.ts.all }}</option> + <option value="available">{{ i18n.ts.normal }}</option> + <option value="approved">{{ i18n.ts.notApproved }}</option> + <option value="admin">{{ i18n.ts.administrator }}</option> + <option value="moderator">{{ i18n.ts.moderator }}</option> + <option value="suspended">{{ i18n.ts.suspend }}</option> + </MkSelect> + <MkSelect v-model="origin" style="flex: 1;"> + <template #label>{{ i18n.ts.instance }}</template> + <option value="combined">{{ i18n.ts.all }}</option> + <option value="local">{{ i18n.ts.local }}</option> + <option value="remote">{{ i18n.ts.remote }}</option> + </MkSelect> + </div> + <div :class="$style.inputs"> + <MkInput v-model="searchUsername" style="flex: 1;" type="text" :spellcheck="false" debounce> + <template #prefix>@</template> + <template #label>{{ i18n.ts.username }}</template> + </MkInput> + <MkInput v-model="searchHost" style="flex: 1;" type="text" :spellcheck="false" :disabled="pagination.params.origin === 'local'"> + <template #prefix>@</template> + <template #label>{{ i18n.ts.host }}</template> + </MkInput> + </div> + + <MkPagination v-slot="{items}" ref="paginationComponent" :pagination="pagination" :displayLimit="50"> + <div :class="$style.users"> + <MkA v-for="user in items" :key="user.id" v-tooltip.mfm="`Last posted: ${dateString(user.updatedAt)}`" :class="$style.user" :to="`/admin/user/${user.id}`"> + <MkUserCardMini :user="user"/> + </MkA> + </div> + </MkPagination> + </div> + </MkSpacer> +</PageWithHeader> </template> <script lang="ts" setup> import { computed, useTemplateRef, ref, watchEffect } from 'vue'; -import XHeader from './_header_.vue'; -import { defaultMemoryStorage } from '@/memory-storage.js'; +import { defaultMemoryStorage } from '@/memory-storage'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; diff --git a/packages/frontend/src/pages/announcement.vue b/packages/frontend/src/pages/announcement.vue index b9c903e9bf..4bba0acdc2 100644 --- a/packages/frontend/src/pages/announcement.vue +++ b/packages/frontend/src/pages/announcement.vue @@ -23,10 +23,10 @@ SPDX-License-Identifier: AGPL-3.0-only <i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--MI_THEME-error);"></i> <i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--MI_THEME-success);"></i> </span> - <Mfm :text="announcement.title"/> + <Mfm :text="announcement.title" class="_selectable"/> </div> <div :class="$style.content"> - <Mfm :text="announcement.text" :isBlock="true"/> + <Mfm :text="announcement.text" :isBlock="true" class="_selectable"/> <img v-if="announcement.imageUrl" :src="announcement.imageUrl"/> <div style="margin-top: 8px; opacity: 0.7; font-size: 85%;"> {{ i18n.ts.createdAt }}: <MkTime :time="announcement.createdAt" mode="detail"/> diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue index 36e9e4342a..d529ad592c 100644 --- a/packages/frontend/src/pages/announcements.vue +++ b/packages/frontend/src/pages/announcements.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="800"> - <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <div class="_gaps"> <MkInfo v-if="$i && $i.hasUnreadAnnouncement && tab === 'current'" warn>{{ i18n.ts.youHaveUnreadAnnouncements }}</MkInfo> <MkPagination ref="paginationEl" :key="tab" v-slot="{items}" :pagination="tab === 'current' ? paginationCurrent : paginationPast" class="_gaps"> @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkA :to="`/announcements/${announcement.id}`"><span>{{ announcement.title }}</span></MkA> </div> <div :class="$style.content"> - <Mfm :text="announcement.text" :isBlock="true" /> + <Mfm :text="announcement.text" :isBlock="true" class="_selectable"/> <img v-if="announcement.imageUrl" :src="announcement.imageUrl"/> <MkA :to="`/announcements/${announcement.id}`"> <div style="margin-top: 8px; opacity: 0.7; font-size: 85%;"> @@ -40,7 +40,7 @@ SPDX-License-Identifier: AGPL-3.0-only </section> </MkPagination> </div> - </MkHorizontalSwipe> + </MkSwiper> </MkSpacer> </PageWithHeader> </template> @@ -50,7 +50,7 @@ import { ref, computed } from 'vue'; import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; import MkInfo from '@/components/MkInfo.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { i18n } from '@/i18n.js'; diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue index e9357577ff..606fb06324 100644 --- a/packages/frontend/src/pages/channel.vue +++ b/packages/frontend/src/pages/channel.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="700"> - <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <div v-if="channel && tab === 'overview'" class="_gaps"> <div class="_panel" :class="$style.bannerContainer"> <XChannelFollowButton :channel="channel" :full="true" :class="$style.subscribe"/> @@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInfo warn>{{ i18n.ts.notesSearchNotAvailable }}</MkInfo> </div> </div> - </MkHorizontalSwipe> + </MkSwiper> </MkSpacer> <template #footer> <div :class="$style.footer"> @@ -93,7 +93,7 @@ import { prefer } from '@/preferences.js'; import MkNote from '@/components/MkNote.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import { isSupportShare } from '@/utility/navigator.js'; import { copyToClipboard } from '@/utility/copy-to-clipboard.js'; import { notesSearchAvailable } from '@/utility/check-permissions.js'; diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue index 76800aaf70..27a6a6168d 100644 --- a/packages/frontend/src/pages/channels.vue +++ b/packages/frontend/src/pages/channels.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="1200"> - <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <div v-if="tab === 'search'" :class="$style.searchRoot"> <div class="_gaps"> <MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search"> @@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkPagination> </div> - </MkHorizontalSwipe> + </MkSwiper> </MkSpacer> </PageWithHeader> </template> @@ -67,7 +67,7 @@ import MkInput from '@/components/MkInput.vue'; import MkRadios from '@/components/MkRadios.vue'; import MkButton from '@/components/MkButton.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; import { useRouter } from '@/router.js'; diff --git a/packages/frontend/src/pages/chat/home.home.vue b/packages/frontend/src/pages/chat/home.home.vue index a8ed891de0..a0853fb0c9 100644 --- a/packages/frontend/src/pages/chat/home.home.vue +++ b/packages/frontend/src/pages/chat/home.home.vue @@ -34,34 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkFoldableSection> <template #header>{{ i18n.ts._chat.history }}</template> - <div v-if="history.length > 0" class="_gaps_s"> - <MkA - v-for="item in history" - :key="item.id" - :class="[$style.message, { [$style.isMe]: item.isMe, [$style.isRead]: item.message.isRead }]" - class="_panel" - :to="item.message.toRoomId ? `/chat/room/${item.message.toRoomId}` : `/chat/user/${item.other!.id}`" - > - <MkAvatar v-if="item.message.toRoomId" :class="$style.messageAvatar" :user="item.message.fromUser" indicator :preview="false"/> - <MkAvatar v-else-if="item.other" :class="$style.messageAvatar" :user="item.other" indicator :preview="false"/> - <div :class="$style.messageBody"> - <header v-if="item.message.toRoom" :class="$style.messageHeader"> - <span :class="$style.messageHeaderName"><i class="ti ti-users"></i> {{ item.message.toRoom.name }}</span> - <MkTime :time="item.message.createdAt" :class="$style.messageHeaderTime"/> - </header> - <header v-else :class="$style.messageHeader"> - <MkUserName :class="$style.messageHeaderName" :user="item.other!"/> - <MkAcct :class="$style.messageHeaderUsername" :user="item.other!"/> - <MkTime :time="item.message.createdAt" :class="$style.messageHeaderTime"/> - </header> - <div :class="$style.messageBodyText"><span v-if="item.isMe" :class="$style.youSaid">{{ i18n.ts.you }}:</span>{{ item.message.text }}</div> - </div> - </MkA> - </div> - <div v-if="!initializing && history.length == 0" class="_fullinfo"> - <div>{{ i18n.ts._chat.noHistory }}</div> - </div> - <MkLoading v-if="initializing"/> + <MkChatHistories/> </MkFoldableSection> </div> </template> @@ -81,20 +54,12 @@ import { updateCurrentAccountPartial } from '@/accounts.js'; import MkInput from '@/components/MkInput.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkInfo from '@/components/MkInfo.vue'; +import MkChatHistories from '@/components/MkChatHistories.vue'; const $i = ensureSignin(); const router = useRouter(); -const initializing = ref(true); -const fetching = ref(false); -const history = ref<{ - id: string; - message: Misskey.entities.ChatMessage; - other: Misskey.entities.ChatMessage['fromUser'] | Misskey.entities.ChatMessage['toUser'] | null; - isMe: boolean; -}[]>([]); - const searchQuery = ref(''); const searched = ref(false); const searchResults = ref<Misskey.entities.ChatMessage[]>([]); @@ -148,57 +113,8 @@ async function search() { searched.value = true; } -async function fetchHistory() { - if (fetching.value) return; - - fetching.value = true; - - const [userMessages, roomMessages] = await Promise.all([ - misskeyApi('chat/history', { room: false }), - misskeyApi('chat/history', { room: true }), - ]); - - history.value = [...userMessages, ...roomMessages] - .toSorted((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) - .map(m => ({ - id: m.id, - message: m, - other: (!('room' in m) || m.room == null) ? (m.fromUserId === $i.id ? m.toUser : m.fromUser) : null, - isMe: m.fromUserId === $i.id, - })); - - fetching.value = false; - initializing.value = false; - - updateCurrentAccountPartial({ hasUnreadChatMessages: false }); -} - -let isActivated = true; - -onActivated(() => { - isActivated = true; -}); - -onDeactivated(() => { - isActivated = false; -}); - -useInterval(() => { - // TODO: DOM的にバックグラウンドになっていないかどうかも考慮する - if (!window.document.hidden && isActivated) { - fetchHistory(); - } -}, 1000 * 10, { - immediate: false, - afterMounted: true, -}); - -onActivated(() => { - fetchHistory(); -}); - onMounted(() => { - fetchHistory(); + updateCurrentAccountPartial({ hasUnreadChatMessages: false }); }); </script> @@ -207,77 +123,6 @@ onMounted(() => { margin: 0 auto; } -.message { - position: relative; - display: flex; - padding: 16px 24px; - - &.isRead, - &.isMe { - opacity: 0.8; - } - - &:not(.isMe):not(.isRead) { - &::before { - content: ''; - position: absolute; - top: 8px; - right: 8px; - width: 8px; - height: 8px; - border-radius: 100%; - background-color: var(--MI_THEME-accent); - } - } -} - -.messageAvatar { - width: 50px; - height: 50px; - margin: 0 16px 0 0; -} - -.messageBody { - flex: 1; - min-width: 0; -} - -.messageHeader { - display: flex; - align-items: center; - margin-bottom: 2px; - white-space: nowrap; - overflow: clip; -} - -.messageHeaderName { - margin: 0; - padding: 0; - overflow: hidden; - text-overflow: ellipsis; - font-size: 1em; - font-weight: bold; -} - -.messageHeaderUsername { - margin: 0 8px; -} - -.messageHeaderTime { - margin-left: auto; -} - -.messageBodyText { - overflow: hidden; - overflow-wrap: break-word; - font-size: 1.1em; -} - -.youSaid { - font-weight: bold; - margin-right: 0.5em; -} - .searchResultItem { padding: 12px; border: solid 1px var(--MI_THEME-divider); diff --git a/packages/frontend/src/pages/chat/home.vue b/packages/frontend/src/pages/chat/home.vue index e29ab28f2d..1edd18ddf0 100644 --- a/packages/frontend/src/pages/chat/home.vue +++ b/packages/frontend/src/pages/chat/home.vue @@ -7,12 +7,12 @@ SPDX-License-Identifier: AGPL-3.0-only <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> <MkPolkadots v-if="tab === 'home'" accented/> <MkSpacer :contentMax="700"> - <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <XHome v-if="tab === 'home'"/> <XInvitations v-else-if="tab === 'invitations'"/> <XJoiningRooms v-else-if="tab === 'joiningRooms'"/> <XOwnedRooms v-else-if="tab === 'ownedRooms'"/> - </MkHorizontalSwipe> + </MkSwiper> </MkSpacer> </PageWithHeader> </template> @@ -25,7 +25,7 @@ import XJoiningRooms from './home.joiningRooms.vue'; import XOwnedRooms from './home.ownedRooms.vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import MkPolkadots from '@/components/MkPolkadots.vue'; const tab = ref('home'); diff --git a/packages/frontend/src/pages/drive.file.vue b/packages/frontend/src/pages/drive.file.vue index 3063d5a4d6..170d48064f 100644 --- a/packages/frontend/src/pages/drive.file.vue +++ b/packages/frontend/src/pages/drive.file.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/> </template> - <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <MkSpacer v-if="tab === 'info'" :contentMax="800"> <XFileInfo :fileId="fileId"/> </MkSpacer> @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSpacer v-else-if="tab === 'notes'" :contentMax="800"> <XNotes :fileId="fileId"/> </MkSpacer> - </MkHorizontalSwipe> + </MkSwiper> </MkStickyContainer> </template> @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed, ref, defineAsyncComponent } from 'vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; const props = defineProps<{ fileId: string; diff --git a/packages/frontend/src/pages/explore.vue b/packages/frontend/src/pages/explore.vue index 85b9fe4932..bcece47e35 100644 --- a/packages/frontend/src/pages/explore.vue +++ b/packages/frontend/src/pages/explore.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <div v-if="tab === 'featured'"> <XFeatured/> </div> @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-else-if="tab === 'roles'"> <XRoles/> </div> - </MkHorizontalSwipe> + </MkSwiper> </PageWithHeader> </template> @@ -25,7 +25,7 @@ import XFeatured from './explore.featured.vue'; import XUsers from './explore.users.vue'; import XRoles from './explore.roles.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue index 98ab587b55..4ef33cbe0f 100644 --- a/packages/frontend/src/pages/flash/flash-index.vue +++ b/packages/frontend/src/pages/flash/flash-index.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="700"> - <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <div v-if="tab === 'featured'"> <MkPagination v-slot="{items}" :pagination="featuredFlashsPagination"> <div class="_gaps_s"> @@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkPagination> </div> - </MkHorizontalSwipe> + </MkSwiper> </MkSpacer> </PageWithHeader> </template> @@ -43,7 +43,7 @@ import { computed, ref } from 'vue'; import MkFlashPreview from '@/components/MkFlashPreview.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { useRouter } from '@/router.js'; diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue index 744fa215cd..f6357ba1b1 100644 --- a/packages/frontend/src/pages/follow-requests.vue +++ b/packages/frontend/src/pages/follow-requests.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="800"> - <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <MkPagination ref="paginationComponent" :pagination="pagination"> <template #empty> <div class="_fullinfo"> @@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </template> </MkPagination> - </MkHorizontalSwipe> + </MkSwiper> </MkSpacer> </PageWithHeader> </template> @@ -52,7 +52,7 @@ import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { infoImageUrl } from '@/instance.js'; import { $i } from '@/i.js'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; const paginationComponent = useTemplateRef('paginationComponent'); diff --git a/packages/frontend/src/pages/following-feed.vue b/packages/frontend/src/pages/following-feed.vue index 84cccb0a5a..7f4110f66c 100644 --- a/packages/frontend/src/pages/following-feed.vue +++ b/packages/frontend/src/pages/following-feed.vue @@ -11,15 +11,15 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div ref="noteScroll" :class="$style.notes"> - <MkHorizontalSwipe v-model:tab="userList" :tabs="headerTabs"> + <MkSwiper v-model:tab="userList" :tabs="headerTabs"> <SkFollowingRecentNotes ref="followingRecentNotes" :selectedUserId="selectedUserId" :userList="userList" :withNonPublic="withNonPublic" :withQuotes="withQuotes" :withBots="withBots" :withReplies="withReplies" :onlyFiles="onlyFiles" @userSelected="userSelected" @loaded="listReady"/> - </MkHorizontalSwipe> + </MkSwiper> </div> <MkLazy ref="userScroll" :class="$style.user"> - <MkHorizontalSwipe v-if="selectedUserId" v-model:tab="userList" :tabs="headerTabs"> + <MkSwiper v-if="selectedUserId" v-model:tab="userList" :tabs="headerTabs"> <SkUserRecentNotes ref="userRecentNotes" :userId="selectedUserId" :withNonPublic="withNonPublic" :withQuotes="withQuotes" :withBots="withBots" :withReplies="withReplies" :onlyFiles="onlyFiles"/> - </MkHorizontalSwipe> + </MkSwiper> </MkLazy> </div> </template> @@ -30,7 +30,7 @@ import type { ComputedRef, Ref } from 'vue'; import type { Tab } from '@/components/global/MkPageHeader.tabs.vue'; import type { PageHeaderItem } from '@/types/page-header.js'; import { i18n } from '@/i18n.js'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import MkPageHeader from '@/components/global/MkPageHeader.vue'; import SkUserRecentNotes from '@/components/SkUserRecentNotes.vue'; import { createModel, createHeaderItem, followingFeedTabs, followingTabIcon, followingTabName, followingTab } from '@/utility/following-feed-utils.js'; diff --git a/packages/frontend/src/pages/gallery/index.vue b/packages/frontend/src/pages/gallery/index.vue index 806b6df671..f9e1c9c9a3 100644 --- a/packages/frontend/src/pages/gallery/index.vue +++ b/packages/frontend/src/pages/gallery/index.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="1400"> - <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <div v-if="tab === 'explore'"> <MkFoldableSection class="_margin"> <template #header><i class="ti ti-clock"></i>{{ i18n.ts.recentPosts }}</template> @@ -40,7 +40,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkPagination> </div> - </MkHorizontalSwipe> + </MkSwiper> </MkSpacer> </PageWithHeader> </template> @@ -50,7 +50,7 @@ import { watch, ref, computed } from 'vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; import { useRouter } from '@/router.js'; diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index 853887d61e..eff513d241 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> <MkSpacer v-if="instance" :contentMax="600" :marginMin="16" :marginMax="32"> - <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <div v-if="tab === 'overview'" class="_gaps_m"> <div class="fnfelxur"> <img :src="faviconUrl" alt="" class="icon"/> @@ -165,7 +165,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkObjectView tall :value="instance"> </MkObjectView> </div> - </MkHorizontalSwipe> + </MkSwiper> </MkSpacer> </PageWithHeader> </template> @@ -192,7 +192,7 @@ import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkPagination from '@/components/MkPagination.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import { getProxiedImageUrlNullable } from '@/utility/media-proxy.js'; import { dateString } from '@/filters/date.js'; import MkTextarea from '@/components/MkTextarea.vue'; diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue index 1525bbef9b..5b9b3af90b 100644 --- a/packages/frontend/src/pages/my-clips/index.vue +++ b/packages/frontend/src/pages/my-clips/index.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="700"> - <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <div v-if="tab === 'my'" class="_gaps"> <MkButton primary rounded class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-else-if="tab === 'favorites'" class="_gaps"> <MkClipPreview v-for="item in favorites" :key="item.id" :clip="item"/> </div> - </MkHorizontalSwipe> + </MkSwiper> </MkSpacer> </PageWithHeader> </template> @@ -33,7 +33,7 @@ import { misskeyApi } from '@/utility/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { clipsCache } from '@/cache.js'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; const pagination = { endpoint: 'clips/list' as const, diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue index 0a2bc02de5..61a1b2725c 100644 --- a/packages/frontend/src/pages/notifications.vue +++ b/packages/frontend/src/pages/notifications.vue @@ -24,7 +24,7 @@ import { computed, ref } from 'vue'; import { notificationTypes } from '@@/js/const.js'; import XNotifications from '@/components/MkNotifications.vue'; import MkNotes from '@/components/MkNotes.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; diff --git a/packages/frontend/src/pages/pages.vue b/packages/frontend/src/pages/pages.vue index c99d7f1a0f..d412bad616 100644 --- a/packages/frontend/src/pages/pages.vue +++ b/packages/frontend/src/pages/pages.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="700"> - <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <div v-if="tab === 'featured'"> <MkPagination v-slot="{items}" :pagination="featuredPagesPagination"> <div class="_gaps"> @@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkPagination> </div> - </MkHorizontalSwipe> + </MkSwiper> </MkSpacer> </PageWithHeader> </template> @@ -41,7 +41,7 @@ import { computed, ref } from 'vue'; import MkPagePreview from '@/components/MkPagePreview.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { useRouter } from '@/router.js'; diff --git a/packages/frontend/src/pages/search.vue b/packages/frontend/src/pages/search.vue index e0cb2dcbab..814ddf3cb9 100644 --- a/packages/frontend/src/pages/search.vue +++ b/packages/frontend/src/pages/search.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <MkSpacer v-if="tab === 'note'" :contentMax="800"> <div v-if="notesSearchAvailable || ignoreNotesSearchAvailable"> <XNote v-bind="props"/> @@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSpacer v-else-if="tab === 'user'" :contentMax="800"> <XUser v-bind="props"/> </MkSpacer> - </MkHorizontalSwipe> + </MkSwiper> </PageWithHeader> </template> @@ -28,7 +28,7 @@ import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { notesSearchAvailable } from '@/utility/check-permissions.js'; import MkInfo from '@/components/MkInfo.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; const props = withDefaults(defineProps<{ query?: string, diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index 5921a8c812..a11ae2a6f6 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -177,7 +177,8 @@ const menuDef = computed<SuperMenuDef[]>(() => [{ action: async () => { const { canceled } = await os.confirm({ type: 'warning', - text: i18n.ts.logoutConfirm, + title: i18n.ts.logoutConfirm, + text: i18n.ts.logoutWillClearClientData, }); if (canceled) return; signout(); diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index b8e14cb920..731242425c 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -53,12 +53,15 @@ import { miLocalStorage } from '@/local-storage.js'; import { availableBasicTimelines, hasWithReplies, isAvailableBasicTimeline, isBasicTimeline, basicTimelineIconClass } from '@/timelines.js'; import { prefer } from '@/preferences.js'; import { useRouter } from '@/router.js'; +import { useScrollPositionKeeper } from '@/use/use-scroll-position-keeper.js'; provide('shouldOmitHeaderTitle', true); const tlComponent = useTemplateRef('tlComponent'); const rootEl = useTemplateRef('rootEl'); +useScrollPositionKeeper(rootEl); + const router = useRouter(); router.useListener('same', () => { top(); diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 88a5ee8849..c057312f63 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -61,7 +61,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="user.followedMessage != null" class="followedMessage"> <MkFukidashi class="fukidashi" :tail="narrow ? 'none' : 'left'" negativeMargin> <div class="messageHeader">{{ i18n.ts.messageToFollower }}</div> - <div><MkSparkle><Mfm :plain="true" :text="user.followedMessage" :author="user"/></MkSparkle></div> + <div><MkSparkle><Mfm :plain="true" :text="user.followedMessage" :author="user" class="_selectable"/></MkSparkle></div> </MkFukidashi> </div> <div v-if="user.roles.length > 0" class="roles"> @@ -93,8 +93,10 @@ SPDX-License-Identifier: AGPL-3.0-only /> </div> <div class="description"> - <Mfm v-if="user.description" :text="user.description" :isBlock="true" :isNote="false" :author="user"/> - <p v-else class="empty">{{ i18n.ts.noAccountDescription }}</p> + <MkOmit> + <Mfm v-if="user.description" :text="user.description" :isBlock="true" :isNote="false" :author="user" class="_selectable"/> + <p v-else class="empty">{{ i18n.ts.noAccountDescription }}</p> + </MkOmit> </div> <div class="fields system"> <dl v-if="user.location" class="field"> @@ -113,10 +115,10 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="user.fields.length > 0" class="fields"> <dl v-for="(field, i) in user.fields" :key="i" class="field"> <dt class="name"> - <Mfm :text="field.name" :author="user" :plain="true" :colored="false"/> + <Mfm :text="field.name" :author="user" :plain="true" :colored="false" class="_selectable"/> </dt> <dd class="value"> - <Mfm :text="field.value" :author="user" :colored="false"/> + <Mfm :text="field.value" :author="user" :colored="false" class="_selectable"/> <i v-if="user.verifiedLinks.includes(field.value)" v-tooltip:dialog="i18n.ts.verifiedLink" class="ti ti-circle-check" :class="$style.verifiedLink"></i> </dd> </dl> diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue index 9d8eb0da2b..17dd1d5f3c 100644 --- a/packages/frontend/src/pages/user/index.vue +++ b/packages/frontend/src/pages/user/index.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :displayBackButton="true" :tabs="headerTabs" :actions="headerActions"> <div v-if="user"> - <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <XHome v-if="tab === 'home'" :user="user" @unfoldFiles="() => { tab = 'files'; }"/> <MkSpacer v-else-if="tab === 'notes'" :contentMax="800" style="padding-top: 0"> <XTimeline :user="user"/> @@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only <XFlashs v-else-if="tab === 'flashs'" :user="user"/> <XGallery v-else-if="tab === 'gallery'" :user="user"/> <XRaw v-else-if="tab === 'raw'" :user="user"/> - </MkHorizontalSwipe> + </MkSwiper> </div> <MkError v-else-if="error" @retry="fetchUser()"/> <MkLoading v-else/> @@ -36,7 +36,7 @@ import { misskeyApi } from '@/utility/misskey-api.js'; import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/i.js'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import { serverContext, assertServerContext } from '@/server-context.js'; const XHome = defineAsyncComponent(() => import('./home.vue')); diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts index 8560ebefaf..6dd6c15ec5 100644 --- a/packages/frontend/src/preferences/def.ts +++ b/packages/frontend/src/preferences/def.ts @@ -35,10 +35,11 @@ export type SoundStore = { // NOTE: デフォルト値は他の設定の状態に依存してはならない(依存していた場合、ユーザーがその設定項目単体で「初期値にリセット」した場合不具合の原因になる) export const PREF_DEF = { - // TODO: 持つのはホストやユーザーID、ユーザー名など最低限にしといて、その他のプロフィール情報はpreferences外で管理した方が綺麗そう - // 現状だと、updateCurrentAccount/updateCurrentAccountPartialが呼ばれるたびに「設定」へのcommitが行われて不自然(明らかに設定の更新とは捉えにくい)だし accounts: { - default: [] as [host: string, user: Misskey.entities.User][], + default: [] as [host: string, user: { + id: string; + username: string; + }][], }, pinnedUserLists: { diff --git a/packages/frontend/src/router.definition.ts b/packages/frontend/src/router.definition.ts index 0058fdad39..1bd1f4fd13 100644 --- a/packages/frontend/src/router.definition.ts +++ b/packages/frontend/src/router.definition.ts @@ -408,9 +408,13 @@ export const ROUTE_DEF = [{ name: 'avatarDecorations', component: page(() => import('@/pages/avatar-decorations.vue')), }, { - path: '/queue', - name: 'queue', - component: page(() => import('@/pages/admin/queue.vue')), + path: '/federation-job-queue', + name: 'federationJobQueue', + component: page(() => import('@/pages/admin/federation-job-queue.vue')), + }, { + path: '/job-queue', + name: 'jobQueue', + component: page(() => import('@/pages/admin/job-queue.vue')), }, { path: '/files', name: 'files', diff --git a/packages/frontend/src/signout.ts b/packages/frontend/src/signout.ts index f86e098e90..703c6fc534 100644 --- a/packages/frontend/src/signout.ts +++ b/packages/frontend/src/signout.ts @@ -4,7 +4,8 @@ */ import { apiUrl } from '@@/js/config.js'; -import { defaultMemoryStorage } from '@/memory-storage.js'; +import { cloudBackup } from '@/preferences/utility.js'; +import { store } from '@/store.js'; import { waiting } from '@/os.js'; import { unisonReload } from '@/utility/unison-reload.js'; import { clear } from '@/utility/idb-proxy.js'; @@ -13,12 +14,13 @@ import { $i } from '@/i.js'; export async function signout() { if (!$i) return; - // TODO: preferの自動バックアップがオンの場合、いろいろ消す前に強制バックアップ - waiting(); + if (store.s.enablePreferencesAutoCloudBackup) { + await cloudBackup(); + } + localStorage.clear(); - defaultMemoryStorage.clear(); const idbAbortController = new AbortController(); const timeout = window.setTimeout(() => idbAbortController.abort(), 5000); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 32acaeaf99..36949e272e 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -112,6 +112,10 @@ export const store = markRaw(new Pizzax('base', { where: 'device', default: {} as Record<string, string>, // host/userId, token }, + accountInfos: { + where: 'device', + default: {} as Record<string, Misskey.entities.User>, // host/userId, user + }, enablePreferencesAutoCloudBackup: { where: 'device', diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index d32f628f07..304df91617 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -194,7 +194,6 @@ rt { .ph-bold { width: 1.28em; vertical-align: -12%; - line-height: 1em; &::before { font-size: 128%; diff --git a/packages/frontend/src/ui/_common_/mobile-footer-menu.vue b/packages/frontend/src/ui/_common_/mobile-footer-menu.vue index 37b70847ca..7c2de12221 100644 --- a/packages/frontend/src/ui/_common_/mobile-footer-menu.vue +++ b/packages/frontend/src/ui/_common_/mobile-footer-menu.vue @@ -77,14 +77,17 @@ watch(rootEl, () => { <style lang="scss" module> .root { + position: relative; + z-index: 1; padding: 12px 12px max(12px, env(safe-area-inset-bottom, 0px)) 12px; display: grid; grid-template-columns: 1fr 1fr 1fr 1fr 1fr; grid-gap: 8px; width: 100%; box-sizing: border-box; - background: var(--MI_THEME-bg); - border-top: solid 0.5px var(--MI_THEME-divider); + background: var(--MI_THEME-navBg); + color: var(--MI_THEME-navFg); + box-shadow: 0px 0px 6px 6px #0000000f; } .item { @@ -109,19 +112,17 @@ watch(rootEl, () => { padding: 0; aspect-ratio: 1; width: 100%; - max-width: 50px; + max-width: 45px; margin: auto; align-content: center; border-radius: 100%; - background: var(--MI_THEME-panel); - color: var(--MI_THEME-fg); &:hover { background: var(--MI_THEME-panelHighlight); } &:active { - background: hsl(from var(--MI_THEME-panel) h s calc(l - 2)); + background: var(--MI_THEME-panelHighlight); } } @@ -131,14 +132,16 @@ watch(rootEl, () => { .itemIndicator { position: absolute; - top: 0; + bottom: -4px; left: 0; + right: 0; color: var(--MI_THEME-indicator); - font-size: 16px; + font-size: 10px; + pointer-events: none; &:has(.itemIndicateValueIcon) { animation: none; - font-size: 12px; + font-size: 8px; } } </style> diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue index 6323d3c5fd..fbe86bc4cb 100644 --- a/packages/frontend/src/ui/deck.vue +++ b/packages/frontend/src/ui/deck.vue @@ -102,6 +102,7 @@ import XWidgetsColumn from '@/ui/deck/widgets-column.vue'; 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 XChatColumn from '@/ui/deck/chat-column.vue'; import XFollowingColumn from '@/ui/deck/following-column.vue'; import { mainRouter } from '@/router.js'; import { columns, layout, columnTypes, switchProfileMenu, addColumn as addColumnToStore, deleteProfile as deleteProfile_ } from '@/deck.js'; @@ -120,6 +121,7 @@ const columnComponents = { mentions: XMentionsColumn, direct: XDirectColumn, roleTimeline: XRoleTimelineColumn, + chat: XChatColumn, following: XFollowingColumn, }; diff --git a/packages/frontend/src/ui/deck/chat-column.vue b/packages/frontend/src/ui/deck/chat-column.vue new file mode 100644 index 0000000000..791af2e44c --- /dev/null +++ b/packages/frontend/src/ui/deck/chat-column.vue @@ -0,0 +1,27 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<XColumn :column="column" :isStacked="isStacked"> + <template #header><i class="ti ti-messages" style="margin-right: 8px;"></i>{{ column.name || i18n.ts._deck._columns.chat }}</template> + + <div style="padding: 8px;"> + <MkChatHistories/> + </div> +</XColumn> +</template> + +<script lang="ts" setup> +import { ref } from 'vue'; +import { i18n } from '../../i18n.js'; +import XColumn from './column.vue'; +import type { Column } from '@/deck.js'; +import MkChatHistories from '@/components/MkChatHistories.vue'; + +defineProps<{ + column: Column; + isStacked: boolean; +}>(); +</script> diff --git a/packages/frontend/src/use/use-scroll-position-keeper.ts b/packages/frontend/src/use/use-scroll-position-keeper.ts new file mode 100644 index 0000000000..b584171cbe --- /dev/null +++ b/packages/frontend/src/use/use-scroll-position-keeper.ts @@ -0,0 +1,77 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { throttle } from 'throttle-debounce'; +import { nextTick, onActivated, onDeactivated, onUnmounted, watch } from 'vue'; +import type { Ref } from 'vue'; + +// note render skippingがオンだとズレるため、遷移直前にスクロール範囲に表示されているdata-scroll-anchor要素を特定して、復元時に当該要素までスクロールするようにする + +// TODO: data-scroll-anchor がひとつも存在しない場合、または手動で useAnchor みたいなフラグをfalseで呼ばれた場合、単純にスクロール位置を使用する処理にフォールバックするようにする + +export function useScrollPositionKeeper(scrollContainerRef: Ref<HTMLElement | null | undefined>): void { + let anchorId: string | null = null; + let ready = true; + + watch(scrollContainerRef, (el) => { + if (!el) return; + + const onScroll = () => { + if (!el) return; + if (!ready) return; + + const scrollContainerRect = el.getBoundingClientRect(); + const viewPosition = scrollContainerRect.height / 2; + + const anchorEls = el.querySelectorAll('[data-scroll-anchor]'); + for (let i = anchorEls.length - 1; i > -1; i--) { // 下から見た方が速い + const anchorEl = anchorEls[i] as HTMLElement; + const anchorRect = anchorEl.getBoundingClientRect(); + const anchorTop = anchorRect.top; + const anchorBottom = anchorRect.bottom; + if (anchorTop <= viewPosition && anchorBottom >= viewPosition) { + anchorId = anchorEl.getAttribute('data-scroll-anchor'); + break; + } + } + }; + + // ほんとはscrollイベントじゃなくてonBeforeDeactivatedでやりたい + // https://github.com/vuejs/vue/issues/9454 + // https://github.com/vuejs/rfcs/pull/284 + el.addEventListener('scroll', throttle(1000, onScroll), { passive: true }); + }, { + immediate: true, + }); + + const restore = () => { + if (!anchorId) return; + const scrollContainer = scrollContainerRef.value; + if (!scrollContainer) return; + const scrollAnchorEl = scrollContainer.querySelector(`[data-scroll-anchor="${anchorId}"]`); + if (!scrollAnchorEl) return; + scrollAnchorEl.scrollIntoView({ + behavior: 'instant', + block: 'center', + inline: 'center', + }); + }; + + onDeactivated(() => { + ready = false; + }); + + onActivated(() => { + restore(); + nextTick(() => { + restore(); + window.setTimeout(() => { + restore(); + + ready = true; + }, 100); + }); + }); +} diff --git a/packages/frontend/src/utility/autocomplete.ts b/packages/frontend/src/utility/autocomplete.ts index 0276cc3c20..885c57caa9 100644 --- a/packages/frontend/src/utility/autocomplete.ts +++ b/packages/frontend/src/utility/autocomplete.ts @@ -7,6 +7,7 @@ import { nextTick, ref, defineAsyncComponent } from 'vue'; import getCaretCoordinates from 'textarea-caret'; import { toASCII } from 'punycode.js'; import type { Ref } from 'vue'; +import type { CompleteInfo } from '@/components/MkAutocomplete.vue'; import { popup } from '@/os.js'; export type SuggestionType = 'user' | 'hashtag' | 'emoji' | 'mfmTag' | 'mfmParam'; @@ -19,7 +20,7 @@ export class Autocomplete { close: () => void; } | null; private textarea: HTMLInputElement | HTMLTextAreaElement; - private currentType: string; + private currentType: keyof CompleteInfo | undefined; private textRef: Ref<string | number | null>; private opening: boolean; private onlyType: SuggestionType[]; @@ -74,7 +75,7 @@ export class Autocomplete { * テキスト入力時 */ private onInput() { - const caretPos = this.textarea.selectionStart; + const caretPos = Number(this.textarea.selectionStart); const text = this.text.substring(0, caretPos).split('\n').pop()!; const mentionIndex = text.lastIndexOf('@'); @@ -101,6 +102,8 @@ export class Autocomplete { const isMfmParam = mfmParamIndex !== -1 && afterLastMfmParam?.includes('.') && !afterLastMfmParam.includes(' '); const isMfmTag = mfmTagIndex !== -1 && !isMfmParam; const isEmoji = emojiIndex !== -1 && text.split(/:[\p{Letter}\p{Number}\p{Mark}_+-]+:/u).pop()!.includes(':'); + // :ok:などを🆗にするたいおぷ + const isEmojiCompleteToUnicode = !isEmoji && emojiIndex === text.length - 1; let opened = false; @@ -137,6 +140,14 @@ export class Autocomplete { } } + if (isEmojiCompleteToUnicode && !opened && this.onlyType.includes('emoji')) { + const emoji = text.substring(text.lastIndexOf(':', text.length - 2) + 1, text.length - 1); + if (!emoji.includes(' ')) { + this.open('emojiComplete', emoji); + opened = true; + } + } + if (isMfmTag && !opened && this.onlyType.includes('mfmTag')) { const mfmTag = text.substring(mfmTagIndex + 1); if (!mfmTag.includes(' ')) { @@ -164,7 +175,7 @@ export class Autocomplete { /** * サジェストを提示します。 */ - private async open(type: string, q: any) { + private async open<T extends keyof CompleteInfo>(type: T, q: CompleteInfo[T]['query']) { if (type !== this.currentType) { this.close(); } @@ -231,10 +242,10 @@ export class Autocomplete { /** * オートコンプリートする */ - private complete({ type, value }) { + private complete<T extends keyof CompleteInfo>({ type, value }: { type: T; value: CompleteInfo[T]['payload'] }) { this.close(); - const caret = this.textarea.selectionStart; + const caret = Number(this.textarea.selectionStart); if (type === 'user') { const source = this.text; @@ -286,6 +297,22 @@ export class Autocomplete { const pos = trimmedBefore.length + value.length; this.textarea.setSelectionRange(pos, pos); }); + } else if (type === 'emojiComplete') { + const source = this.text; + + const before = source.substring(0, caret); + const trimmedBefore = before.substring(0, before.lastIndexOf(':', before.length - 2)); + const after = source.substring(caret); + + // 挿入 + this.text = trimmedBefore + value + after; + + // キャレットを戻す + nextTick(() => { + this.textarea.focus(); + const pos = trimmedBefore.length + value.length; + this.textarea.setSelectionRange(pos, pos); + }); } else if (type === 'mfmTag') { const source = this.text; diff --git a/packages/frontend/src/utility/search-emoji.ts b/packages/frontend/src/utility/search-emoji.ts index 4192a2df8f..8e6d657310 100644 --- a/packages/frontend/src/utility/search-emoji.ts +++ b/packages/frontend/src/utility/search-emoji.ts @@ -104,3 +104,33 @@ export function searchEmoji(query: string | null, emojiDb: EmojiDef[], max = 30) .slice(0, max) .map(it => it.emoji); } + +export function searchEmojiExact(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; + }); + } + + return [...matched.values()] + .sort((x, y) => y.score - x.score) + .slice(0, max) + .map(it => it.emoji); +} diff --git a/packages/frontend/src/utility/touch.ts b/packages/frontend/src/utility/touch.ts index adc2e4c093..361246b328 100644 --- a/packages/frontend/src/utility/touch.ts +++ b/packages/frontend/src/utility/touch.ts @@ -18,5 +18,5 @@ if (isTouchSupported && !isTouchUsing) { }, { passive: true }); } -/** (MkHorizontalSwipe) 横スワイプ中か? */ +/** (MkSwiper) 横スワイプ中か? */ export const isHorizontalSwipeSwiping = ref(false); diff --git a/packages/frontend/src/widgets/WidgetChat.vue b/packages/frontend/src/widgets/WidgetChat.vue new file mode 100644 index 0000000000..43b2a6e522 --- /dev/null +++ b/packages/frontend/src/widgets/WidgetChat.vue @@ -0,0 +1,52 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkContainer :showHeader="widgetProps.showHeader" class="mkw-chat"> + <template #icon><i class="ti ti-users"></i></template> + <template #header>{{ i18n.ts._widgets.chat }}</template> + <template #func="{ buttonStyleClass }"><button class="_button" :class="buttonStyleClass" @click="configure()"><i class="ti ti-settings"></i></button></template> + + <div> + <MkChatHistories/> + </div> +</MkContainer> +</template> + +<script lang="ts" setup> +import { } from 'vue'; +import { useWidgetPropsManager } from './widget.js'; +import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import type { GetFormResultType } from '@/utility/form.js'; +import MkContainer from '@/components/MkContainer.vue'; +import { i18n } from '@/i18n.js'; +import MkChatHistories from '@/components/MkChatHistories.vue'; + +const name = 'chat'; + +const widgetPropsDef = { + showHeader: { + type: 'boolean' as const, + default: true, + }, +}; + +type WidgetProps = GetFormResultType<typeof widgetPropsDef>; + +const props = defineProps<WidgetComponentProps<WidgetProps>>(); +const emit = defineEmits<WidgetComponentEmits<WidgetProps>>(); + +const { widgetProps, configure, save } = useWidgetPropsManager(name, + widgetPropsDef, + props, + emit, +); + +defineExpose<WidgetComponentExpose>({ + name, + configure, + id: props.widget ? props.widget.id : null, +}); +</script> diff --git a/packages/frontend/src/widgets/index.ts b/packages/frontend/src/widgets/index.ts index 047c6d7104..f495234e58 100644 --- a/packages/frontend/src/widgets/index.ts +++ b/packages/frontend/src/widgets/index.ts @@ -36,6 +36,7 @@ export default function(app: App) { app.component('WidgetClicker', defineAsyncComponent(() => import('./WidgetClicker.vue'))); app.component('WidgetSearch', defineAsyncComponent(() => import('./WidgetSearch.vue'))); app.component('WidgetBirthdayFollowings', defineAsyncComponent(() => import('./WidgetBirthdayFollowings.vue'))); + app.component('WidgetChat', defineAsyncComponent(() => import('./WidgetChat.vue'))); } // 連合関連のウィジェット(連合無効時に隠す) @@ -72,6 +73,7 @@ export const widgets = [ 'clicker', 'search', 'birthdayFollowings', + 'chat', ...federationWidgets, ]; diff --git a/packages/frontend/vite-node.config.ts b/packages/frontend/vite-node.config.ts deleted file mode 100644 index c049f46e10..0000000000 --- a/packages/frontend/vite-node.config.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { defineConfig } from 'vite'; - -export default defineConfig({}); diff --git a/packages/frontend/vite.replaceIcons.ts b/packages/frontend/vite.replaceIcons.ts index 50220124ce..4165c98c8c 100644 --- a/packages/frontend/vite.replaceIcons.ts +++ b/packages/frontend/vite.replaceIcons.ts @@ -409,6 +409,13 @@ export function pluginReplaceIcons() { 'ti ti-ticket': 'ph-ticket ph-bold ph-lg', 'ti ti-user-cog': 'ph-user-circle-gear ph-bold ph-lg', 'ti ti-users-group': 'ph-users-three ph-bold ph-lg', + 'ti ti-code-asterisk': 'ph-brackets-curly ph-bold ph-lg', + 'ti ti-hourglass-high': 'ph-hourglass-high ph-bold ph-lg', + 'ti ti-http-que': 'ph-queue ph-bold ph-lg', + 'ti ti-list-check': 'ph-list-checks ph-bold ph-lg', + 'ti ti-logs': 'ph-list-dashes ph-bold ph-lg', + 'ti ti-timeline-event': 'ph-map-pin-simple-line ph-bold ph-lg', + 'ti ti-user-minus': 'ph-user-minus ph-bold ph-lg', }, }), ]; diff --git a/packages/misskey-bubble-game/package.json b/packages/misskey-bubble-game/package.json index 464f22bddb..4f0c5b2577 100644 --- a/packages/misskey-bubble-game/package.json +++ b/packages/misskey-bubble-game/package.json @@ -24,13 +24,13 @@ "devDependencies": { "@types/matter-js": "0.19.8", "@types/seedrandom": "3.0.8", - "@types/node": "22.13.11", - "@typescript-eslint/eslint-plugin": "8.27.0", - "@typescript-eslint/parser": "8.27.0", + "@types/node": "22.14.0", + "@typescript-eslint/eslint-plugin": "8.29.1", + "@typescript-eslint/parser": "8.29.1", "nodemon": "3.1.9", "execa": "9.5.2", - "typescript": "5.8.2", - "esbuild": "0.25.1", + "typescript": "5.8.3", + "esbuild": "0.25.2", "glob": "11.0.1" }, "files": [ diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 30c4273dbd..b2a4fb9cab 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -270,13 +270,31 @@ type AdminNsfwUserRequest = operations['admin___nsfw-user']['requestBody']['cont type AdminPromoCreateRequest = operations['admin___promo___create']['requestBody']['content']['application/json']; // @public (undocumented) +type AdminQueueClearRequest = operations['admin___queue___clear']['requestBody']['content']['application/json']; + +// @public (undocumented) type AdminQueueDeliverDelayedResponse = operations['admin___queue___deliver-delayed']['responses']['200']['content']['application/json']; // @public (undocumented) type AdminQueueInboxDelayedResponse = operations['admin___queue___inbox-delayed']['responses']['200']['content']['application/json']; // @public (undocumented) -type AdminQueuePromoteRequest = operations['admin___queue___promote']['requestBody']['content']['application/json']; +type AdminQueueJobsRequest = operations['admin___queue___jobs']['requestBody']['content']['application/json']; + +// @public (undocumented) +type AdminQueuePromoteJobsRequest = operations['admin___queue___promote-jobs']['requestBody']['content']['application/json']; + +// @public (undocumented) +type AdminQueueQueueStatsRequest = operations['admin___queue___queue-stats']['requestBody']['content']['application/json']; + +// @public (undocumented) +type AdminQueueRemoveJobRequest = operations['admin___queue___remove-job']['requestBody']['content']['application/json']; + +// @public (undocumented) +type AdminQueueRetryJobRequest = operations['admin___queue___retry-job']['requestBody']['content']['application/json']; + +// @public (undocumented) +type AdminQueueShowJobRequest = operations['admin___queue___show-job']['requestBody']['content']['application/json']; // @public (undocumented) type AdminQueueStatsResponse = operations['admin___queue___stats']['responses']['200']['content']['application/json']; @@ -1569,9 +1587,15 @@ declare namespace entities { AdminMetaResponse, AdminNsfwUserRequest, AdminPromoCreateRequest, + AdminQueueClearRequest, AdminQueueDeliverDelayedResponse, AdminQueueInboxDelayedResponse, - AdminQueuePromoteRequest, + AdminQueueJobsRequest, + AdminQueuePromoteJobsRequest, + AdminQueueQueueStatsRequest, + AdminQueueRemoveJobRequest, + AdminQueueRetryJobRequest, + AdminQueueShowJobRequest, AdminQueueStatsResponse, AdminRejectQuotesRequest, AdminRelaysAddRequest, diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 61a51640b0..8b52924b29 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2025.4.0", + "version": "2025.4.1-alpha.2", "description": "Misskey SDK for JavaScript", "license": "MIT", "main": "./built/index.js", diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts index acbe6fa527..75b3c5769e 100644 --- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts +++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts @@ -694,9 +694,75 @@ declare module '../api.js' { /** * No description provided. * + * **Credential required**: *Yes* / **Permission**: *read:admin:queue* + */ + request<E extends 'admin/queue/jobs', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:queue* + */ + request<E extends 'admin/queue/promote-jobs', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:admin:queue* + */ + request<E extends 'admin/queue/queue-stats', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:admin:queue* + */ + request<E extends 'admin/queue/queues', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:queue* + */ + request<E extends 'admin/queue/remove-job', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * * **Credential required**: *Yes* / **Permission**: *write:admin:queue* */ - request<E extends 'admin/queue/promote', P extends Endpoints[E]['req']>( + request<E extends 'admin/queue/retry-job', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:admin:queue* + */ + request<E extends 'admin/queue/show-job', P extends Endpoints[E]['req']>( endpoint: E, params: P, credential?: string | null, diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index 2214fc641d..6c66c4beb0 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -79,9 +79,15 @@ import type { AdminMetaResponse, AdminNsfwUserRequest, AdminPromoCreateRequest, + AdminQueueClearRequest, AdminQueueDeliverDelayedResponse, AdminQueueInboxDelayedResponse, - AdminQueuePromoteRequest, + AdminQueueJobsRequest, + AdminQueuePromoteJobsRequest, + AdminQueueQueueStatsRequest, + AdminQueueRemoveJobRequest, + AdminQueueRetryJobRequest, + AdminQueueShowJobRequest, AdminQueueStatsResponse, AdminRejectQuotesRequest, AdminRelaysAddRequest, @@ -721,10 +727,16 @@ export type Endpoints = { 'admin/meta': { req: EmptyRequest; res: AdminMetaResponse }; 'admin/nsfw-user': { req: AdminNsfwUserRequest; res: EmptyResponse }; 'admin/promo/create': { req: AdminPromoCreateRequest; res: EmptyResponse }; - 'admin/queue/clear': { req: EmptyRequest; res: EmptyResponse }; + 'admin/queue/clear': { req: AdminQueueClearRequest; res: EmptyResponse }; 'admin/queue/deliver-delayed': { req: EmptyRequest; res: AdminQueueDeliverDelayedResponse }; 'admin/queue/inbox-delayed': { req: EmptyRequest; res: AdminQueueInboxDelayedResponse }; - 'admin/queue/promote': { req: AdminQueuePromoteRequest; res: EmptyResponse }; + 'admin/queue/jobs': { req: AdminQueueJobsRequest; res: EmptyResponse }; + 'admin/queue/promote-jobs': { req: AdminQueuePromoteJobsRequest; res: EmptyResponse }; + 'admin/queue/queue-stats': { req: AdminQueueQueueStatsRequest; res: EmptyResponse }; + 'admin/queue/queues': { req: EmptyRequest; res: EmptyResponse }; + 'admin/queue/remove-job': { req: AdminQueueRemoveJobRequest; res: EmptyResponse }; + 'admin/queue/retry-job': { req: AdminQueueRetryJobRequest; res: EmptyResponse }; + 'admin/queue/show-job': { req: AdminQueueShowJobRequest; res: EmptyResponse }; 'admin/queue/stats': { req: EmptyRequest; res: AdminQueueStatsResponse }; 'admin/reject-quotes': { req: AdminRejectQuotesRequest; res: EmptyResponse }; 'admin/relays/add': { req: AdminRelaysAddRequest; res: AdminRelaysAddResponse }; diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index 3ab3e4f740..404746b3f2 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -82,9 +82,15 @@ export type AdminInviteListResponse = operations['admin___invite___list']['respo export type AdminMetaResponse = operations['admin___meta']['responses']['200']['content']['application/json']; export type AdminNsfwUserRequest = operations['admin___nsfw-user']['requestBody']['content']['application/json']; export type AdminPromoCreateRequest = operations['admin___promo___create']['requestBody']['content']['application/json']; +export type AdminQueueClearRequest = operations['admin___queue___clear']['requestBody']['content']['application/json']; export type AdminQueueDeliverDelayedResponse = operations['admin___queue___deliver-delayed']['responses']['200']['content']['application/json']; export type AdminQueueInboxDelayedResponse = operations['admin___queue___inbox-delayed']['responses']['200']['content']['application/json']; -export type AdminQueuePromoteRequest = operations['admin___queue___promote']['requestBody']['content']['application/json']; +export type AdminQueueJobsRequest = operations['admin___queue___jobs']['requestBody']['content']['application/json']; +export type AdminQueuePromoteJobsRequest = operations['admin___queue___promote-jobs']['requestBody']['content']['application/json']; +export type AdminQueueQueueStatsRequest = operations['admin___queue___queue-stats']['requestBody']['content']['application/json']; +export type AdminQueueRemoveJobRequest = operations['admin___queue___remove-job']['requestBody']['content']['application/json']; +export type AdminQueueRetryJobRequest = operations['admin___queue___retry-job']['requestBody']['content']['application/json']; +export type AdminQueueShowJobRequest = operations['admin___queue___show-job']['requestBody']['content']['application/json']; export type AdminQueueStatsResponse = operations['admin___queue___stats']['responses']['200']['content']['application/json']; export type AdminRejectQuotesRequest = operations['admin___reject-quotes']['requestBody']['content']['application/json']; export type AdminRelaysAddRequest = operations['admin___relays___add']['requestBody']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index c0a32a8fb7..46b9972bbb 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -576,14 +576,68 @@ export type paths = { */ post: operations['admin___queue___inbox-delayed']; }; - '/admin/queue/promote': { + '/admin/queue/jobs': { /** - * admin/queue/promote + * admin/queue/jobs + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:admin:queue* + */ + post: operations['admin___queue___jobs']; + }; + '/admin/queue/promote-jobs': { + /** + * admin/queue/promote-jobs + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:queue* + */ + post: operations['admin___queue___promote-jobs']; + }; + '/admin/queue/queue-stats': { + /** + * admin/queue/queue-stats + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:admin:queue* + */ + post: operations['admin___queue___queue-stats']; + }; + '/admin/queue/queues': { + /** + * admin/queue/queues + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:admin:queue* + */ + post: operations['admin___queue___queues']; + }; + '/admin/queue/remove-job': { + /** + * admin/queue/remove-job * @description No description provided. * * **Credential required**: *Yes* / **Permission**: *write:admin:queue* */ - post: operations['admin___queue___promote']; + post: operations['admin___queue___remove-job']; + }; + '/admin/queue/retry-job': { + /** + * admin/queue/retry-job + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:queue* + */ + post: operations['admin___queue___retry-job']; + }; + '/admin/queue/show-job': { + /** + * admin/queue/show-job + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:admin:queue* + */ + post: operations['admin___queue___show-job']; }; '/admin/queue/stats': { /** @@ -9371,6 +9425,16 @@ export type operations = { * **Credential required**: *Yes* / **Permission**: *write:admin:queue* */ admin___queue___clear: { + requestBody: { + content: { + 'application/json': { + /** @enum {string} */ + queue: 'system' | 'endedPollNotification' | 'deliver' | 'inbox' | 'db' | 'relationship' | 'objectStorage' | 'userWebhookDeliver' | 'systemWebhookDeliver'; + /** @enum {string} */ + state: '*' | 'completed' | 'wait' | 'active' | 'paused' | 'prioritized' | 'delayed' | 'failed'; + }; + }; + }; responses: { /** @description OK (without any results) */ 204: { @@ -9501,17 +9565,326 @@ export type operations = { }; }; /** - * admin/queue/promote + * admin/queue/jobs + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:admin:queue* + */ + admin___queue___jobs: { + requestBody: { + content: { + 'application/json': { + /** @enum {string} */ + queue: 'system' | 'endedPollNotification' | 'deliver' | 'inbox' | 'db' | 'relationship' | 'objectStorage' | 'userWebhookDeliver' | 'systemWebhookDeliver'; + state: ('active' | 'wait' | 'delayed' | 'completed' | 'failed')[]; + search?: string; + }; + }; + }; + 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']; + }; + }; + }; + }; + /** + * admin/queue/promote-jobs * @description No description provided. * * **Credential required**: *Yes* / **Permission**: *write:admin:queue* */ - admin___queue___promote: { + 'admin___queue___promote-jobs': { + requestBody: { + content: { + 'application/json': { + /** @enum {string} */ + queue: 'system' | 'endedPollNotification' | 'deliver' | 'inbox' | 'db' | 'relationship' | 'objectStorage' | 'userWebhookDeliver' | 'systemWebhookDeliver'; + }; + }; + }; + 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']; + }; + }; + }; + }; + /** + * admin/queue/queue-stats + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:admin:queue* + */ + 'admin___queue___queue-stats': { + requestBody: { + content: { + 'application/json': { + /** @enum {string} */ + queue: 'system' | 'endedPollNotification' | 'deliver' | 'inbox' | 'db' | 'relationship' | 'objectStorage' | 'userWebhookDeliver' | 'systemWebhookDeliver'; + }; + }; + }; + 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']; + }; + }; + }; + }; + /** + * admin/queue/queues + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:admin:queue* + */ + admin___queue___queues: { + 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']; + }; + }; + }; + }; + /** + * admin/queue/remove-job + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:queue* + */ + 'admin___queue___remove-job': { + requestBody: { + content: { + 'application/json': { + /** @enum {string} */ + queue: 'system' | 'endedPollNotification' | 'deliver' | 'inbox' | 'db' | 'relationship' | 'objectStorage' | 'userWebhookDeliver' | 'systemWebhookDeliver'; + jobId: string; + }; + }; + }; + 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']; + }; + }; + }; + }; + /** + * admin/queue/retry-job + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:queue* + */ + 'admin___queue___retry-job': { + requestBody: { + content: { + 'application/json': { + /** @enum {string} */ + queue: 'system' | 'endedPollNotification' | 'deliver' | 'inbox' | 'db' | 'relationship' | 'objectStorage' | 'userWebhookDeliver' | 'systemWebhookDeliver'; + jobId: string; + }; + }; + }; + 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']; + }; + }; + }; + }; + /** + * admin/queue/show-job + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:admin:queue* + */ + 'admin___queue___show-job': { requestBody: { content: { 'application/json': { /** @enum {string} */ - type: 'deliver' | 'inbox'; + queue: 'system' | 'endedPollNotification' | 'deliver' | 'inbox' | 'db' | 'relationship' | 'objectStorage' | 'userWebhookDeliver' | 'systemWebhookDeliver'; + jobId: string; }; }; }; diff --git a/packages/misskey-reversi/package.json b/packages/misskey-reversi/package.json index 2bf6319430..7e12bc4a23 100644 --- a/packages/misskey-reversi/package.json +++ b/packages/misskey-reversi/package.json @@ -22,13 +22,13 @@ "lint": "pnpm typecheck && pnpm eslint" }, "devDependencies": { - "@types/node": "22.13.11", - "@typescript-eslint/eslint-plugin": "8.27.0", - "@typescript-eslint/parser": "8.27.0", + "@types/node": "22.14.0", + "@typescript-eslint/eslint-plugin": "8.29.1", + "@typescript-eslint/parser": "8.29.1", "execa": "9.5.2", "nodemon": "3.1.9", - "typescript": "5.8.2", - "esbuild": "0.25.1", + "typescript": "5.8.3", + "esbuild": "0.25.2", "glob": "11.0.1" }, "files": [ diff --git a/packages/sw/package.json b/packages/sw/package.json index aa5cfd1e62..1a493e916c 100644 --- a/packages/sw/package.json +++ b/packages/sw/package.json @@ -9,16 +9,16 @@ "lint": "pnpm typecheck && pnpm eslint" }, "dependencies": { - "esbuild": "0.25.1", + "esbuild": "0.25.2", "idb-keyval": "6.2.1", "misskey-js": "workspace:*" }, "devDependencies": { - "@typescript-eslint/parser": "8.27.0", + "@typescript-eslint/parser": "8.29.1", "@typescript/lib-webworker": "npm:@types/serviceworker@0.0.74", "eslint-plugin-import": "2.31.0", "nodemon": "3.1.9", - "typescript": "5.8.2" + "typescript": "5.8.3" }, "type": "module" } diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts index 65e84e4609..68b5db54b7 100644 --- a/packages/sw/src/scripts/create-notification.ts +++ b/packages/sw/src/scripts/create-notification.ts @@ -291,6 +291,24 @@ async function composeNotification(data: PushNotificationDataMap[keyof PushNotif data, renotify: true, }]; + case 'newChatMessage': + if (data.body.toRoom != null) { + return [`${data.body.toRoom.name}: ${getUserName(data.body.fromUser)}: ${data.body.text}`, { + icon: data.body.fromUser.avatarUrl ?? undefined, + badge: iconUrl('messages'), + tag: `chat:room:${data.body.toRoomId}`, + data, + renotify: true, + }]; + } else { + return [`${getUserName(data.body.fromUser)}: ${data.body.text}`, { + icon: data.body.fromUser.avatarUrl ?? undefined, + badge: iconUrl('messages'), + tag: `chat:user:${data.body.fromUserId}`, + data, + renotify: true, + }]; + } default: return null; } diff --git a/packages/sw/src/scripts/operations.ts b/packages/sw/src/scripts/operations.ts index 8862c6faa5..3e72b7e7c2 100644 --- a/packages/sw/src/scripts/operations.ts +++ b/packages/sw/src/scripts/operations.ts @@ -16,7 +16,7 @@ export const cli = new Misskey.api.APIClient({ origin, fetch: (...args): Promise export async function api< E extends keyof Misskey.Endpoints, - P extends Misskey.Endpoints[E]['req'] + P extends Misskey.Endpoints[E]['req'], >(endpoint: E, userId?: string, params?: P): Promise<Misskey.api.SwitchCaseResponseType<E, P> | undefined> { let account: Pick<Misskey.entities.SignupResponse, 'id' | 'token'> | undefined; @@ -60,6 +60,14 @@ export function openAntenna(antennaId: string, loginId: string): ReturnType<type return openClient('push', `/timeline/antenna/${antennaId}`, loginId, { antennaId }); } +export function openChat(body: any, loginId: string): ReturnType<typeof openClient> { + if (body.toRoomId != null) { + return openClient('push', `/chat/room/${body.toRoomId}`, loginId, { body }); + } else { + return openClient('push', `/chat/user/${body.toUserId}`, loginId, { body }); + } +} + // post-formのオプションから投稿フォームを開く export async function openPost(options: { initialText?: string; reply?: Misskey.entities.Note; renote?: Misskey.entities.Note }, loginId?: string): ReturnType<typeof openClient> { // クエリを作成しておく diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts index a7b9f27e2c..3da8efed28 100644 --- a/packages/sw/src/sw.ts +++ b/packages/sw/src/sw.ts @@ -76,6 +76,7 @@ globalThis.addEventListener('push', ev => { // case 'driveFileCreated': case 'notification': case 'unreadAntennaNote': + case 'newChatMessage': // 1日以上経過している場合は無視 if (Date.now() - data.dateTime > 1000 * 60 * 60 * 24) break; @@ -158,6 +159,9 @@ globalThis.addEventListener('notificationclick', (ev: ServiceWorkerGlobalScopeEv case 'unreadAntennaNote': client = await swos.openAntenna(data.body.antenna.id, loginId); break; + case 'newChatMessage': + client = await swos.openChat(data.body, loginId); + break; default: switch (action) { case 'markAllAsRead': diff --git a/packages/sw/src/types.ts b/packages/sw/src/types.ts index 4f82779808..549220396c 100644 --- a/packages/sw/src/types.ts +++ b/packages/sw/src/types.ts @@ -23,6 +23,7 @@ type PushNotificationDataSourceMap = { note: Misskey.entities.Note; }; readAllNotifications: undefined; + newChatMessage: Misskey.entities.ChatMessage; }; export type PushNotificationData<K extends keyof PushNotificationDataSourceMap> = { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4552815288..6c71fe2153 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,11 +84,11 @@ importers: packages/backend: dependencies: '@aws-sdk/client-s3': - specifier: 3.772.0 - version: 3.772.0 + specifier: 3.782.0 + version: 3.782.0 '@aws-sdk/lib-storage': - specifier: 3.772.0 - version: 3.772.0(@aws-sdk/client-s3@3.772.0) + specifier: 3.782.0 + version: 3.782.0(@aws-sdk/client-s3@3.782.0) '@discordapp/twemoji': specifier: 15.1.0 version: 15.1.0 @@ -117,20 +117,20 @@ importers: specifier: 10.0.2 version: 10.0.2 '@misskey-dev/sharp-read-bmp': - specifier: 1.2.0 - version: 1.2.0 + specifier: 1.3.0 + version: 1.3.0 '@misskey-dev/summaly': specifier: 5.2.0 version: 5.2.0 '@nestjs/common': - specifier: 11.0.12 - version: 11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2) + specifier: 11.0.16 + version: 11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': - specifier: 11.0.12 - version: 11.0.12(@nestjs/common@11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2) + specifier: 11.0.15 + version: 11.0.15(@nestjs/common@11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/testing': - specifier: 11.0.12 - version: 11.0.12(@nestjs/common@11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.0.12(@nestjs/common@11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.15(@nestjs/common@11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.0.12)) + specifier: 11.0.15 + version: 11.0.15(@nestjs/common@11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.0.15(@nestjs/common@11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.15(@nestjs/common@11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.0.15)) '@peertube/http-signature': specifier: 1.7.0 version: 1.7.0 @@ -151,10 +151,10 @@ importers: version: 2.5.0 '@swc/cli': specifier: 0.6.0 - version: 0.6.0(@swc/core@1.11.11)(chokidar@4.0.3) + version: 0.6.0(@swc/core@1.11.18)(chokidar@4.0.3) '@swc/core': - specifier: 1.11.11 - version: 1.11.11 + specifier: 1.11.18 + version: 1.11.18 '@transfem-org/sfm-js': specifier: 0.24.6 version: 0.24.6 @@ -164,6 +164,9 @@ importers: '@types/psl': specifier: ^1.1.3 version: 1.1.3 + '@types/redis-info': + specifier: 3.0.3 + version: 3.0.3 accepts: specifier: 1.3.8 version: 1.3.8 @@ -189,8 +192,8 @@ importers: specifier: 1.20.3 version: 1.20.3 bullmq: - specifier: 5.44.1 - version: 5.44.1 + specifier: 5.48.1 + version: 5.48.1 cacheable-lookup: specifier: 7.0.0 version: 7.0.0 @@ -228,8 +231,8 @@ importers: specifier: 4.4.1 version: 4.4.1 fastify: - specifier: 5.2.1 - version: 5.2.1 + specifier: 5.3.2 + version: 5.3.2 fastify-multer: specifier: ^2.0.3 version: 2.0.3 @@ -252,8 +255,8 @@ importers: specifier: 11.0.0 version: 11.0.0 got: - specifier: 14.4.6 - version: 14.4.6 + specifier: 14.4.7 + version: 14.4.7 happy-dom: specifier: 16.8.1 version: 16.8.1 @@ -345,8 +348,8 @@ importers: specifier: 0.0.14 version: 0.0.14 otpauth: - specifier: 9.3.6 - version: 9.3.6 + specifier: 9.4.0 + version: 9.4.0 parse5: specifier: 7.2.1 version: 7.2.1 @@ -383,6 +386,9 @@ importers: re2: specifier: 1.21.4 version: 1.21.4 + redis-info: + specifier: 3.1.0 + version: 3.1.0 redis-lock: specifier: 0.1.4 version: 0.1.4 @@ -405,8 +411,8 @@ importers: specifier: 3.0.2 version: 3.0.2 sharp: - specifier: 0.33.5 - version: 0.33.5 + specifier: 0.34.1 + version: 0.34.1 slacc: specifier: 0.0.10 version: 0.0.10 @@ -426,17 +432,17 @@ importers: specifier: 0.2.3 version: 0.2.3 tsc-alias: - specifier: 1.8.11 - version: 1.8.11 + specifier: 1.8.15 + version: 1.8.15 tsconfig-paths: specifier: 4.2.0 version: 4.2.0 typeorm: - specifier: 0.3.21 - version: 0.3.21(ioredis@5.6.0)(pg@8.14.1)(reflect-metadata@0.2.2) + specifier: 0.3.22 + version: 0.3.22(ioredis@5.6.0)(pg@8.14.1)(reflect-metadata@0.2.2) typescript: - specifier: 5.8.2 - version: 5.8.2 + specifier: 5.8.3 + version: 5.8.3 ulid: specifier: 2.4.0 version: 2.4.0 @@ -460,38 +466,38 @@ importers: specifier: 1.3.11 version: 1.3.11 '@swc/core-darwin-arm64': - specifier: 1.11.11 - version: 1.11.11 + specifier: 1.11.18 + version: 1.11.18 '@swc/core-darwin-x64': - specifier: 1.11.11 - version: 1.11.11 + specifier: 1.11.18 + version: 1.11.18 '@swc/core-freebsd-x64': specifier: 1.3.11 version: 1.3.11 '@swc/core-linux-arm-gnueabihf': - specifier: 1.11.11 - version: 1.11.11 + specifier: 1.11.18 + version: 1.11.18 '@swc/core-linux-arm64-gnu': - specifier: 1.11.11 - version: 1.11.11 + specifier: 1.11.18 + version: 1.11.18 '@swc/core-linux-arm64-musl': - specifier: 1.11.11 - version: 1.11.11 + specifier: 1.11.18 + version: 1.11.18 '@swc/core-linux-x64-gnu': - specifier: 1.11.11 - version: 1.11.11 + specifier: 1.11.18 + version: 1.11.18 '@swc/core-linux-x64-musl': - specifier: 1.11.11 - version: 1.11.11 + specifier: 1.11.18 + version: 1.11.18 '@swc/core-win32-arm64-msvc': - specifier: 1.11.11 - version: 1.11.11 + specifier: 1.11.18 + version: 1.11.18 '@swc/core-win32-ia32-msvc': - specifier: 1.11.11 - version: 1.11.11 + specifier: 1.11.18 + version: 1.11.18 '@swc/core-win32-x64-msvc': - specifier: 1.11.11 - version: 1.11.11 + specifier: 1.11.18 + version: 1.11.18 bufferutil: specifier: 4.0.9 version: 4.0.9 @@ -543,16 +549,16 @@ importers: version: 29.7.0 '@nestjs/platform-express': specifier: 10.4.15 - version: 10.4.15(@nestjs/common@11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.0.12) + version: 10.4.15(@nestjs/common@11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.0.15) '@sentry/vue': - specifier: 9.8.0 - version: 9.8.0(vue@3.5.13(typescript@5.8.2)) + specifier: 9.12.0 + version: 9.12.0(vue@3.5.13(typescript@5.8.3)) '@simplewebauthn/types': specifier: 12.0.0 version: 12.0.0 '@swc/jest': specifier: 0.2.37 - version: 0.2.37(@swc/core@1.11.11) + version: 0.2.37(@swc/core@1.11.18) '@types/accepts': specifier: 1.3.7 version: 1.3.7 @@ -602,8 +608,8 @@ importers: specifier: 0.7.34 version: 0.7.34 '@types/node': - specifier: 22.13.10 - version: 22.13.10 + specifier: 22.14.0 + version: 22.14.0 '@types/nodemailer': specifier: 6.4.17 version: 6.4.17 @@ -638,11 +644,11 @@ importers: specifier: 1.0.7 version: 1.0.7 '@types/sanitize-html': - specifier: 2.13.0 - version: 2.13.0 + specifier: 2.15.0 + version: 2.15.0 '@types/semver': - specifier: 7.5.8 - version: 7.5.8 + specifier: 7.7.0 + version: 7.7.0 '@types/simple-oauth2': specifier: 5.0.7 version: 5.0.7 @@ -665,14 +671,14 @@ importers: specifier: 3.6.4 version: 3.6.4 '@types/ws': - specifier: 8.18.0 - version: 8.18.0 + specifier: 8.18.1 + version: 8.18.1 '@typescript-eslint/eslint-plugin': - specifier: 8.27.0 - version: 8.27.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0)(typescript@5.8.2))(eslint@9.23.0)(typescript@5.8.2) + specifier: 8.29.1 + version: 8.29.1(@typescript-eslint/parser@8.29.1(eslint@9.23.0)(typescript@5.8.3))(eslint@9.23.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: 8.27.0 - version: 8.27.0(eslint@9.23.0)(typescript@5.8.2) + specifier: 8.29.1 + version: 8.29.1(eslint@9.23.0)(typescript@5.8.3) aws-sdk-client-mock: specifier: 4.1.0 version: 4.1.0 @@ -681,7 +687,7 @@ importers: version: 7.0.3 eslint-plugin-import: specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0)(typescript@5.8.2))(eslint@9.23.0) + version: 2.31.0(@typescript-eslint/parser@8.29.1(eslint@9.23.0)(typescript@5.8.3))(eslint@9.23.0) execa: specifier: 8.0.1 version: 8.0.1 @@ -690,7 +696,7 @@ importers: version: 9.0.0 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@22.13.10) + version: 29.7.0(@types/node@22.14.0) jest-mock: specifier: 29.7.0 version: 29.7.0 @@ -723,19 +729,19 @@ importers: version: 2.1.1 '@rollup/plugin-json': specifier: 6.1.0 - version: 6.1.0(rollup@4.36.0) + version: 6.1.0(rollup@4.39.0) '@rollup/plugin-replace': specifier: 6.0.2 - version: 6.0.2(rollup@4.36.0) + version: 6.0.2(rollup@4.39.0) '@rollup/pluginutils': specifier: 5.1.4 - version: 5.1.4(rollup@4.36.0) + version: 5.1.4(rollup@4.39.0) '@ruffle-rs/ruffle': specifier: 0.1.0-nightly.2024.10.15 version: 0.1.0-nightly.2024.10.15 '@sentry/vue': - specifier: 9.8.0 - version: 9.8.0(vue@3.5.13(typescript@5.8.2)) + specifier: 9.12.0 + version: 9.12.0(vue@3.5.13(typescript@5.8.3)) '@syuilo/aiscript': specifier: 0.19.0 version: 0.19.0 @@ -747,7 +753,7 @@ importers: version: 15.1.1 '@vitejs/plugin-vue': specifier: 5.2.3 - version: 5.2.3(vite@6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2)) + version: 5.2.3(vite@6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.3)) '@vue/compiler-sfc': specifier: 3.5.13 version: 3.5.13 @@ -758,8 +764,8 @@ importers: specifier: 1.9.0 version: 1.9.0 broadcast-channel: - specifier: 7.0.0 - version: 7.0.0 + specifier: 7.1.0 + version: 7.1.0 buraha: specifier: 0.0.1 version: 0.0.1 @@ -782,8 +788,8 @@ importers: specifier: 2.2.0 version: 2.2.0(chart.js@4.4.8) chromatic: - specifier: 11.27.0 - version: 11.27.0 + specifier: 11.28.0 + version: 11.28.0 compare-versions: specifier: 6.1.1 version: 6.1.1 @@ -842,17 +848,17 @@ importers: specifier: 2.3.1 version: 2.3.1 rollup: - specifier: 4.36.0 - version: 4.36.0 + specifier: 4.39.0 + version: 4.39.0 sanitize-html: specifier: 2.15.0 version: 2.15.0 sass: - specifier: 1.86.0 - version: 1.86.0 + specifier: 1.86.3 + version: 1.86.3 shiki: - specifier: 3.2.1 - version: 3.2.1 + specifier: 3.2.2 + version: 3.2.2 strict-event-emitter-types: specifier: 2.0.0 version: 2.0.0 @@ -860,8 +866,8 @@ importers: specifier: 3.1.0 version: 3.1.0 three: - specifier: 0.174.0 - version: 0.174.0 + specifier: 0.175.0 + version: 0.175.0 throttle-debounce: specifier: 5.0.2 version: 5.0.2 @@ -869,29 +875,29 @@ importers: specifier: 1.6.0 version: 1.6.0 tsc-alias: - specifier: 1.8.11 - version: 1.8.11 + specifier: 1.8.15 + version: 1.8.15 tsconfig-paths: specifier: 4.2.0 version: 4.2.0 typescript: - specifier: 5.8.2 - version: 5.8.2 + specifier: 5.8.3 + version: 5.8.3 uuid: specifier: 11.1.0 version: 11.1.0 v-code-diff: specifier: 1.13.1 - version: 1.13.1(vue@3.5.13(typescript@5.8.2)) + version: 1.13.1(vue@3.5.13(typescript@5.8.3)) vite: - specifier: 6.2.4 - version: 6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) + specifier: 6.3.1 + version: 6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) vue: specifier: 3.5.13 - version: 3.5.13(typescript@5.8.2) + version: 3.5.13(typescript@5.8.3) vuedraggable: specifier: next - version: 4.1.0(vue@3.5.13(typescript@5.8.2)) + version: 4.1.0(vue@3.5.13(typescript@5.8.3)) wanakana: specifier: 5.3.1 version: 5.3.1 @@ -904,68 +910,68 @@ importers: specifier: 5.2.0 version: 5.2.0 '@storybook/addon-actions': - specifier: 8.6.7 - version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.12 + version: 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/addon-essentials': - specifier: 8.6.7 - version: 8.6.7(@types/react@18.0.28)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.12 + version: 8.6.12(@types/react@18.0.28)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/addon-interactions': - specifier: 8.6.7 - version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.12 + version: 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/addon-links': - specifier: 8.6.7 - version: 8.6.7(react@19.0.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.12 + version: 8.6.12(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/addon-mdx-gfm': - specifier: 8.6.7 - version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.12 + version: 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/addon-storysource': - specifier: 8.6.7 - version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.12 + version: 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/blocks': - specifier: 8.6.7 - version: 8.6.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.12 + version: 8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/components': - specifier: 8.6.7 - version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.12 + version: 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/core-events': - specifier: 8.6.7 - version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.12 + version: 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/manager-api': - specifier: 8.6.7 - version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.12 + version: 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/preview-api': - specifier: 8.6.7 - version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.12 + version: 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/react': - specifier: 8.6.7 - version: 8.6.7(@storybook/test@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2) + specifier: 8.6.12 + version: 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3) '@storybook/react-vite': - specifier: 8.6.7 - version: 8.6.7(@storybook/test@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.36.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2)(vite@6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)) + specifier: 8.6.12 + version: 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.39.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3)(vite@6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3)) '@storybook/test': - specifier: 8.6.7 - version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.12 + version: 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/theming': - specifier: 8.6.7 - version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.12 + version: 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/types': - specifier: 8.6.7 - version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.12 + version: 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/vue3': - specifier: 8.6.7 - version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.13(typescript@5.8.2)) + specifier: 8.6.12 + version: 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.13(typescript@5.8.3)) '@storybook/vue3-vite': - specifier: 8.6.7 - version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2)) + specifier: 8.6.12 + version: 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.3)) '@testing-library/vue': specifier: 8.1.0 - version: 8.1.0(@vue/compiler-sfc@3.5.13)(@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2)) + version: 8.1.0(@vue/compiler-sfc@3.5.13)(@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.8.3)))(vue@3.5.13(typescript@5.8.3)) '@types/canvas-confetti': specifier: 1.9.0 version: 1.9.0 '@types/estree': - specifier: 1.0.6 - version: 1.0.6 + specifier: 1.0.7 + version: 1.0.7 '@types/katex': specifier: ^0.16.7 version: 0.16.7 @@ -976,14 +982,14 @@ importers: specifier: 4.0.9 version: 4.0.9 '@types/node': - specifier: 22.13.11 - version: 22.13.11 + specifier: 22.14.0 + version: 22.14.0 '@types/punycode.js': specifier: npm:@types/punycode@2.1.4 version: '@types/punycode@2.1.4' '@types/sanitize-html': - specifier: 2.13.0 - version: 2.13.0 + specifier: 2.15.0 + version: 2.15.0 '@types/seedrandom': specifier: 3.0.8 version: 3.0.8 @@ -994,17 +1000,17 @@ importers: specifier: 1.4.6 version: 1.4.6 '@types/ws': - specifier: 8.18.0 - version: 8.18.0 + specifier: 8.18.1 + version: 8.18.1 '@typescript-eslint/eslint-plugin': - specifier: 8.27.0 - version: 8.27.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0)(typescript@5.8.2))(eslint@9.23.0)(typescript@5.8.2) + specifier: 8.29.1 + version: 8.29.1(@typescript-eslint/parser@8.29.1(eslint@9.23.0)(typescript@5.8.3))(eslint@9.23.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: 8.27.0 - version: 8.27.0(eslint@9.23.0)(typescript@5.8.2) + specifier: 8.29.1 + version: 8.29.1(eslint@9.23.0)(typescript@5.8.3) '@vitest/coverage-v8': - specifier: 3.0.9 - version: 3.0.9(vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.13.11)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)) + specifier: 3.1.1 + version: 3.1.1(vitest@3.1.1(@types/debug@4.1.12)(@types/node@22.14.0)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3))(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3)) '@vue/compiler-core': specifier: 3.5.13 version: 3.5.13 @@ -1019,10 +1025,10 @@ importers: version: 7.0.3 eslint-plugin-import: specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0)(typescript@5.8.2))(eslint@9.23.0) + version: 2.31.0(@typescript-eslint/parser@8.29.1(eslint@9.23.0)(typescript@5.8.3))(eslint@9.23.0) eslint-plugin-vue: specifier: 10.0.0 - version: 10.0.0(eslint@9.23.0)(vue-eslint-parser@10.1.1(eslint@9.23.0)) + version: 10.0.0(eslint@9.23.0)(vue-eslint-parser@10.1.3(eslint@9.23.0)) fast-glob: specifier: 3.3.3 version: 3.3.3 @@ -1040,10 +1046,10 @@ importers: version: 10.0.1 msw: specifier: 2.7.3 - version: 2.7.3(@types/node@22.13.11)(typescript@5.8.2) + version: 2.7.3(@types/node@22.14.0)(typescript@5.8.3) msw-storybook-addon: specifier: 2.0.4 - version: 2.0.4(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2)) + version: 2.0.4(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3)) nodemon: specifier: 3.1.9 version: 3.1.9 @@ -1051,11 +1057,11 @@ importers: specifier: 3.5.3 version: 3.5.3 react: - specifier: 19.0.0 - version: 19.0.0 + specifier: 19.1.0 + version: 19.1.0 react-dom: - specifier: 19.0.0 - version: 19.0.0(react@19.0.0) + specifier: 19.1.0 + version: 19.1.0(react@19.1.0) seedrandom: specifier: 3.0.5 version: 3.0.5 @@ -1063,32 +1069,29 @@ importers: specifier: 2.0.11 version: 2.0.11 storybook: - specifier: 8.6.7 - version: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + specifier: 8.6.12 + version: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) storybook-addon-misskey-theme: specifier: github:misskey-dev/storybook-addon-misskey-theme - version: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.6.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/components@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/core-events@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/manager-api@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/preview-api@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/theming@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/types@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - vite-node: - specifier: 3.0.9 - version: 3.0.9(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) + version: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/components@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/core-events@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/manager-api@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/preview-api@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/theming@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/types@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) vite-plugin-turbosnap: specifier: 1.0.3 version: 1.0.3 vitest: - specifier: 3.0.9 - version: 3.0.9(@types/debug@4.1.12)(@types/node@22.13.11)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) + specifier: 3.1.1 + version: 3.1.1(@types/debug@4.1.12)(@types/node@22.14.0)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3))(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) vitest-fetch-mock: specifier: 0.4.5 - version: 0.4.5(vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.13.11)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)) + version: 0.4.5(vitest@3.1.1(@types/debug@4.1.12)(@types/node@22.14.0)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3))(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3)) vue-component-type-helpers: specifier: 2.2.8 version: 2.2.8 vue-eslint-parser: - specifier: 10.1.1 - version: 10.1.1(eslint@9.23.0) + specifier: 10.1.3 + version: 10.1.3(eslint@9.23.0) vue-tsc: specifier: 2.2.8 - version: 2.2.8(typescript@5.8.2) + version: 2.2.8(typescript@5.8.3) packages/frontend-embed: dependencies: @@ -1100,13 +1103,13 @@ importers: version: 2.1.1 '@rollup/plugin-json': specifier: 6.1.0 - version: 6.1.0(rollup@4.36.0) + version: 6.1.0(rollup@4.39.0) '@rollup/plugin-replace': specifier: 6.0.2 - version: 6.0.2(rollup@4.36.0) + version: 6.0.2(rollup@4.39.0) '@rollup/pluginutils': specifier: 5.1.4 - version: 5.1.4(rollup@4.36.0) + version: 5.1.4(rollup@4.39.0) '@transfem-org/sfm-js': specifier: 0.24.5 version: 0.24.5 @@ -1115,7 +1118,7 @@ importers: version: 15.1.1 '@vitejs/plugin-vue': specifier: 5.2.3 - version: 5.2.3(vite@6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2)) + version: 5.2.3(vite@6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.3)) '@vue/compiler-sfc': specifier: 3.5.13 version: 3.5.13 @@ -1141,51 +1144,51 @@ importers: specifier: 2.3.1 version: 2.3.1 rollup: - specifier: 4.36.0 - version: 4.36.0 + specifier: 4.39.0 + version: 4.39.0 sass: - specifier: 1.86.0 - version: 1.86.0 + specifier: 1.86.3 + version: 1.86.3 shiki: - specifier: 3.2.1 - version: 3.2.1 + specifier: 3.2.2 + version: 3.2.2 tinycolor2: specifier: 1.6.0 version: 1.6.0 tsc-alias: - specifier: 1.8.11 - version: 1.8.11 + specifier: 1.8.15 + version: 1.8.15 tsconfig-paths: specifier: 4.2.0 version: 4.2.0 typescript: - specifier: 5.8.2 - version: 5.8.2 + specifier: 5.8.3 + version: 5.8.3 uuid: specifier: 11.1.0 version: 11.1.0 vite: - specifier: 6.2.4 - version: 6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) + specifier: 6.3.1 + version: 6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) vue: specifier: 3.5.13 - version: 3.5.13(typescript@5.8.2) + version: 3.5.13(typescript@5.8.3) devDependencies: '@misskey-dev/summaly': specifier: 5.2.0 version: 5.2.0 '@testing-library/vue': specifier: 8.1.0 - version: 8.1.0(@vue/compiler-sfc@3.5.13)(@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2)) + version: 8.1.0(@vue/compiler-sfc@3.5.13)(@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.8.3)))(vue@3.5.13(typescript@5.8.3)) '@types/estree': - specifier: 1.0.6 - version: 1.0.6 + specifier: 1.0.7 + version: 1.0.7 '@types/micromatch': specifier: 4.0.9 version: 4.0.9 '@types/node': - specifier: 22.13.11 - version: 22.13.11 + specifier: 22.14.0 + version: 22.14.0 '@types/punycode.js': specifier: npm:@types/punycode@2.1.4 version: '@types/punycode@2.1.4' @@ -1193,17 +1196,17 @@ importers: specifier: 1.4.6 version: 1.4.6 '@types/ws': - specifier: 8.18.0 - version: 8.18.0 + specifier: 8.18.1 + version: 8.18.1 '@typescript-eslint/eslint-plugin': - specifier: 8.27.0 - version: 8.27.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0)(typescript@5.8.2))(eslint@9.23.0)(typescript@5.8.2) + specifier: 8.29.1 + version: 8.29.1(@typescript-eslint/parser@8.29.1(eslint@9.23.0)(typescript@5.8.3))(eslint@9.23.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: 8.27.0 - version: 8.27.0(eslint@9.23.0)(typescript@5.8.2) + specifier: 8.29.1 + version: 8.29.1(eslint@9.23.0)(typescript@5.8.3) '@vitest/coverage-v8': - specifier: 3.0.9 - version: 3.0.9(vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.13.11)(happy-dom@17.4.4)(jsdom@26.0.0)(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)) + specifier: 3.1.1 + version: 3.1.1(vitest@3.1.1(@types/debug@4.1.12)(@types/node@22.14.0)(happy-dom@17.4.4)(jsdom@26.0.0)(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3))(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3)) '@vue/runtime-core': specifier: 3.5.13 version: 3.5.13 @@ -1215,10 +1218,10 @@ importers: version: 7.0.3 eslint-plugin-import: specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0)(typescript@5.8.2))(eslint@9.23.0) + version: 2.31.0(@typescript-eslint/parser@8.29.1(eslint@9.23.0)(typescript@5.8.3))(eslint@9.23.0) eslint-plugin-vue: specifier: 10.0.0 - version: 10.0.0(eslint@9.23.0)(vue-eslint-parser@10.1.1(eslint@9.23.0)) + version: 10.0.0(eslint@9.23.0)(vue-eslint-parser@10.1.3(eslint@9.23.0)) fast-glob: specifier: 3.3.3 version: 3.3.3 @@ -1233,7 +1236,7 @@ importers: version: 4.0.8 msw: specifier: 2.7.3 - version: 2.7.3(@types/node@22.13.11)(typescript@5.8.2) + version: 2.7.3(@types/node@22.14.0)(typescript@5.8.3) nodemon: specifier: 3.1.9 version: 3.1.9 @@ -1250,11 +1253,11 @@ importers: specifier: 2.2.8 version: 2.2.8 vue-eslint-parser: - specifier: 10.1.1 - version: 10.1.1(eslint@9.23.0) + specifier: 10.1.3 + version: 10.1.3(eslint@9.23.0) vue-tsc: specifier: 2.2.8 - version: 2.2.8(typescript@5.8.2) + version: 2.2.8(typescript@5.8.3) packages/frontend-shared: dependencies: @@ -1266,29 +1269,29 @@ importers: version: 3.1.7 vue: specifier: 3.5.13 - version: 3.5.13(typescript@5.8.2) + version: 3.5.13(typescript@5.8.3) devDependencies: '@types/node': - specifier: 22.13.11 - version: 22.13.11 + specifier: 22.14.0 + version: 22.14.0 '@typescript-eslint/eslint-plugin': - specifier: 8.27.0 - version: 8.27.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0)(typescript@5.8.2))(eslint@9.23.0)(typescript@5.8.2) + specifier: 8.29.1 + version: 8.29.1(@typescript-eslint/parser@8.29.1(eslint@9.23.0)(typescript@5.8.3))(eslint@9.23.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: 8.27.0 - version: 8.27.0(eslint@9.23.0)(typescript@5.8.2) + specifier: 8.29.1 + version: 8.29.1(eslint@9.23.0)(typescript@5.8.3) esbuild: - specifier: 0.25.1 - version: 0.25.1 + specifier: 0.25.2 + version: 0.25.2 eslint-plugin-vue: specifier: 10.0.0 - version: 10.0.0(eslint@9.23.0)(vue-eslint-parser@10.1.1(eslint@9.23.0)) + version: 10.0.0(eslint@9.23.0)(vue-eslint-parser@10.1.3(eslint@9.23.0)) typescript: - specifier: 5.8.2 - version: 5.8.2 + specifier: 5.8.3 + version: 5.8.3 vue-eslint-parser: - specifier: 10.1.1 - version: 10.1.1(eslint@9.23.0) + specifier: 10.1.3 + version: 10.1.3(eslint@9.23.0) packages/megalodon: dependencies: @@ -1364,7 +1367,7 @@ importers: version: 9.1.0(eslint@9.22.0) jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@22.13.15) + version: 29.7.0(@types/node@22.13.10) jest-worker: specifier: ^29.7.0 version: 29.7.0 @@ -1376,7 +1379,7 @@ importers: version: 3.3.3 ts-jest: specifier: ^29.1.1 - version: 29.1.2(@babel/core@7.23.5)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.5))(esbuild@0.25.0)(jest@29.7.0(@types/node@22.13.15))(typescript@5.1.6) + version: 29.1.2(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(esbuild@0.25.0)(jest@29.7.0(@types/node@22.13.10))(typescript@5.1.6) typedoc: specifier: ^0.25.3 version: 0.25.13(typescript@5.1.6) @@ -1397,20 +1400,20 @@ importers: specifier: 0.19.8 version: 0.19.8 '@types/node': - specifier: 22.13.11 - version: 22.13.11 + specifier: 22.14.0 + version: 22.14.0 '@types/seedrandom': specifier: 3.0.8 version: 3.0.8 '@typescript-eslint/eslint-plugin': - specifier: 8.27.0 - version: 8.27.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0)(typescript@5.8.2))(eslint@9.23.0)(typescript@5.8.2) + specifier: 8.29.1 + version: 8.29.1(@typescript-eslint/parser@8.29.1(eslint@9.23.0)(typescript@5.8.3))(eslint@9.23.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: 8.27.0 - version: 8.27.0(eslint@9.23.0)(typescript@5.8.2) + specifier: 8.29.1 + version: 8.29.1(eslint@9.23.0)(typescript@5.8.3) esbuild: - specifier: 0.25.1 - version: 0.25.1 + specifier: 0.25.2 + version: 0.25.2 execa: specifier: 9.5.2 version: 9.5.2 @@ -1421,8 +1424,8 @@ importers: specifier: 3.1.9 version: 3.1.9 typescript: - specifier: 5.8.2 - version: 5.8.2 + specifier: 5.8.3 + version: 5.8.3 packages/misskey-js: dependencies: @@ -1441,7 +1444,7 @@ importers: version: 7.52.2(@types/node@22.13.15) '@swc/jest': specifier: 0.2.37 - version: 0.2.37(@swc/core@1.11.11) + version: 0.2.37(@swc/core@1.11.18) '@types/jest': specifier: 29.5.14 version: 29.5.14 @@ -1528,17 +1531,17 @@ importers: version: 1.2.2 devDependencies: '@types/node': - specifier: 22.13.11 - version: 22.13.11 + specifier: 22.14.0 + version: 22.14.0 '@typescript-eslint/eslint-plugin': - specifier: 8.27.0 - version: 8.27.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0)(typescript@5.8.2))(eslint@9.23.0)(typescript@5.8.2) + specifier: 8.29.1 + version: 8.29.1(@typescript-eslint/parser@8.29.1(eslint@9.23.0)(typescript@5.8.3))(eslint@9.23.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: 8.27.0 - version: 8.27.0(eslint@9.23.0)(typescript@5.8.2) + specifier: 8.29.1 + version: 8.29.1(eslint@9.23.0)(typescript@5.8.3) esbuild: - specifier: 0.25.1 - version: 0.25.1 + specifier: 0.25.2 + version: 0.25.2 execa: specifier: 9.5.2 version: 9.5.2 @@ -1549,14 +1552,14 @@ importers: specifier: 3.1.9 version: 3.1.9 typescript: - specifier: 5.8.2 - version: 5.8.2 + specifier: 5.8.3 + version: 5.8.3 packages/sw: dependencies: esbuild: - specifier: 0.25.1 - version: 0.25.1 + specifier: 0.25.2 + version: 0.25.2 idb-keyval: specifier: 6.2.1 version: 6.2.1 @@ -1565,20 +1568,20 @@ importers: version: link:../misskey-js devDependencies: '@typescript-eslint/parser': - specifier: 8.27.0 - version: 8.27.0(eslint@9.23.0)(typescript@5.8.2) + specifier: 8.29.1 + version: 8.29.1(eslint@9.23.0)(typescript@5.8.3) '@typescript/lib-webworker': specifier: npm:@types/serviceworker@0.0.74 version: '@types/serviceworker@0.0.74' eslint-plugin-import: specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0)(typescript@5.8.2))(eslint@9.23.0) + version: 2.31.0(@typescript-eslint/parser@8.29.1(eslint@9.23.0)(typescript@5.8.3))(eslint@9.23.0) nodemon: specifier: 3.1.9 version: 3.1.9 typescript: - specifier: 5.8.2 - version: 5.8.2 + specifier: 5.8.3 + version: 5.8.3 packages: @@ -1627,129 +1630,129 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.772.0': - resolution: {integrity: sha512-HQXlQIyyLp47h1/Hdjr36yK8/gsAAFX2vRzgDJhSRaz0vWZlWX07AJdYfrxapLUXfVU6DbBu3rwi2UGqM7ixqQ==} + '@aws-sdk/client-s3@3.782.0': + resolution: {integrity: sha512-V6JR2JAGYQY7J8wk5un5n/ja2nfCUyyoRCF8Du8JL91NGI8i41Mdr/TzuOGwTgFl6RSXb/ge1K1jk30OH4MugQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-sso@3.772.0': - resolution: {integrity: sha512-sDdxepi74+cL6gXJJ2yw3UNSI7GBvoGTwZqFyPoNAzcURvaYwo8dBr7G4jS9GDanjTlO3CGVAf2VMcpqEvmoEw==} + '@aws-sdk/client-sso@3.782.0': + resolution: {integrity: sha512-5GlJBejo8wqMpSSEKb45WE82YxI2k73YuebjLH/eWDNQeE6VI5Bh9lA1YQ7xNkLLH8hIsb0pSfKVuwh0VEzVrg==} engines: {node: '>=18.0.0'} - '@aws-sdk/core@3.758.0': - resolution: {integrity: sha512-0RswbdR9jt/XKemaLNuxi2gGr4xGlHyGxkTdhSQzCyUe9A9OPCoLl3rIESRguQEech+oJnbHk/wuiwHqTuP9sg==} + '@aws-sdk/core@3.775.0': + resolution: {integrity: sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-env@3.758.0': - resolution: {integrity: sha512-N27eFoRrO6MeUNumtNHDW9WOiwfd59LPXPqDrIa3kWL/s+fOKFHb9xIcF++bAwtcZnAxKkgpDCUP+INNZskE+w==} + '@aws-sdk/credential-provider-env@3.775.0': + resolution: {integrity: sha512-6ESVxwCbGm7WZ17kY1fjmxQud43vzJFoLd4bmlR+idQSWdqlzGDYdcfzpjDKTcivdtNrVYmFvcH1JBUwCRAZhw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-http@3.758.0': - resolution: {integrity: sha512-Xt9/U8qUCiw1hihztWkNeIR+arg6P+yda10OuCHX6kFVx3auTlU7+hCqs3UxqniGU4dguHuftf3mRpi5/GJ33Q==} + '@aws-sdk/credential-provider-http@3.775.0': + resolution: {integrity: sha512-PjDQeDH/J1S0yWV32wCj2k5liRo0ssXMseCBEkCsD3SqsU8o5cU82b0hMX4sAib/RkglCSZqGO0xMiN0/7ndww==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-ini@3.772.0': - resolution: {integrity: sha512-T1Ec9Q25zl5c/eZUPHZsiq8vgBeWBjHM7WM5xtZszZRPqqhQGnmFlomz1r9rwhW8RFB5k8HRaD/SLKo6jtYl/A==} + '@aws-sdk/credential-provider-ini@3.782.0': + resolution: {integrity: sha512-wd4KdRy2YjLsE4Y7pz00470Iip06GlRHkG4dyLW7/hFMzEO2o7ixswCWp6J2VGZVAX64acknlv2Q0z02ebjmhw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-node@3.772.0': - resolution: {integrity: sha512-0IdVfjBO88Mtekq/KaScYSIEPIeR+ABRvBOWyj/c/qQ2KJyI0GRlSAzpANfxDLHVPn3yEHuZd9nRL6sOmOMI0A==} + '@aws-sdk/credential-provider-node@3.782.0': + resolution: {integrity: sha512-HZiAF+TCEyKjju9dgysjiPIWgt/+VerGaeEp18mvKLNfgKz1d+/82A2USEpNKTze7v3cMFASx3CvL8yYyF7mJw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-process@3.758.0': - resolution: {integrity: sha512-AzcY74QTPqcbXWVgjpPZ3HOmxQZYPROIBz2YINF0OQk0MhezDWV/O7Xec+K1+MPGQO3qS6EDrUUlnPLjsqieHA==} + '@aws-sdk/credential-provider-process@3.775.0': + resolution: {integrity: sha512-A6k68H9rQp+2+7P7SGO90Csw6nrUEm0Qfjpn9Etc4EboZhhCLs9b66umUsTsSBHus4FDIe5JQxfCUyt1wgNogg==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-sso@3.772.0': - resolution: {integrity: sha512-yR3Y5RAVPa4ogojcBOpZUx6XyRVAkynIJCjd0avdlxW1hhnzSr5/pzoiJ6u21UCbkxlJJTDZE3jfFe7tt+HA4w==} + '@aws-sdk/credential-provider-sso@3.782.0': + resolution: {integrity: sha512-1y1ucxTtTIGDSNSNxriQY8msinilhe9gGvQpUDYW9gboyC7WQJPDw66imy258V6osdtdi+xoHzVCbCz3WhosMQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-web-identity@3.772.0': - resolution: {integrity: sha512-yHAT5Y2y0fnecSuWRUn8NMunKfDqFYhnOpGq8UyCEcwz9aXzibU0hqRIEm51qpR81hqo0GMFDH0EOmegZ/iW5w==} + '@aws-sdk/credential-provider-web-identity@3.782.0': + resolution: {integrity: sha512-xCna0opVPaueEbJoclj5C6OpDNi0Gynj+4d7tnuXGgQhTHPyAz8ZyClkVqpi5qvHTgxROdUEDxWqEO5jqRHZHQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/lib-storage@3.772.0': - resolution: {integrity: sha512-d59NJAHDa7mGf82ApOdIp1FsN1i7/VlNTk2t3NIepH/8FsMTzKPZQvbMzqE33ba4oik7y6QrDliYXoRXB/nZPg==} + '@aws-sdk/lib-storage@3.782.0': + resolution: {integrity: sha512-UQYnIzpBReLko2XhDgG/rWpoHTWv4/zqUNl4XJXZRo9akLzrxGKtPrp5nJ4OLUkH3tIm1cvmI3XlSjHUW/OxWw==} engines: {node: '>=18.0.0'} peerDependencies: - '@aws-sdk/client-s3': ^3.772.0 + '@aws-sdk/client-s3': ^3.782.0 - '@aws-sdk/middleware-bucket-endpoint@3.734.0': - resolution: {integrity: sha512-etC7G18aF7KdZguW27GE/wpbrNmYLVT755EsFc8kXpZj8D6AFKxc7OuveinJmiy0bYXAMspJUWsF6CrGpOw6CQ==} + '@aws-sdk/middleware-bucket-endpoint@3.775.0': + resolution: {integrity: sha512-qogMIpVChDYr4xiUNC19/RDSw/sKoHkAhouS6Skxiy6s27HBhow1L3Z1qVYXuBmOZGSWPU0xiyZCvOyWrv9s+Q==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-expect-continue@3.734.0': - resolution: {integrity: sha512-P38/v1l6HjuB2aFUewt7ueAW5IvKkFcv5dalPtbMGRhLeyivBOHwbCyuRKgVs7z7ClTpu9EaViEGki2jEQqEsQ==} + '@aws-sdk/middleware-expect-continue@3.775.0': + resolution: {integrity: sha512-Apd3owkIeUW5dnk3au9np2IdW2N0zc9NjTjHiH+Mx3zqwSrc+m+ANgJVgk9mnQjMzU/vb7VuxJ0eqdEbp5gYsg==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.758.0': - resolution: {integrity: sha512-o8Rk71S08YTKLoSobucjnbj97OCGaXgpEDNKXpXaavUM5xLNoHCLSUPRCiEN86Ivqxg1n17Y2nSRhfbsveOXXA==} + '@aws-sdk/middleware-flexible-checksums@3.775.0': + resolution: {integrity: sha512-OmHLfRIb7IIXsf9/X/pMOlcSV3gzW/MmtPSZTkrz5jCTKzWXd7eRoyOJqewjsaC6KMAxIpNU77FoAd16jOZ21A==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-host-header@3.734.0': - resolution: {integrity: sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw==} + '@aws-sdk/middleware-host-header@3.775.0': + resolution: {integrity: sha512-tkSegM0Z6WMXpLB8oPys/d+umYIocvO298mGvcMCncpRl77L9XkvSLJIFzaHes+o7djAgIduYw8wKIMStFss2w==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-location-constraint@3.734.0': - resolution: {integrity: sha512-EJEIXwCQhto/cBfHdm3ZOeLxd2NlJD+X2F+ZTOxzokuhBtY0IONfC/91hOo5tWQweerojwshSMHRCKzRv1tlwg==} + '@aws-sdk/middleware-location-constraint@3.775.0': + resolution: {integrity: sha512-8TMXEHZXZTFTckQLyBT5aEI8fX11HZcwZseRifvBKKpj0RZDk4F0EEYGxeNSPpUQ7n+PRWyfAEnnZNRdAj/1NQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-logger@3.734.0': - resolution: {integrity: sha512-mUMFITpJUW3LcKvFok176eI5zXAUomVtahb9IQBwLzkqFYOrMJvWAvoV4yuxrJ8TlQBG8gyEnkb9SnhZvjg67w==} + '@aws-sdk/middleware-logger@3.775.0': + resolution: {integrity: sha512-FaxO1xom4MAoUJsldmR92nT1G6uZxTdNYOFYtdHfd6N2wcNaTuxgjIvqzg5y7QIH9kn58XX/dzf1iTjgqUStZw==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-recursion-detection@3.772.0': - resolution: {integrity: sha512-zg0LjJa4v7fcLzn5QzZvtVS+qyvmsnu7oQnb86l6ckduZpWDCDC9+A0ZzcXTrxblPCJd3JqkoG1+Gzi4S4Ny/Q==} + '@aws-sdk/middleware-recursion-detection@3.775.0': + resolution: {integrity: sha512-GLCzC8D0A0YDG5u3F5U03Vb9j5tcOEFhr8oc6PDk0k0vm5VwtZOE6LvK7hcCSoAB4HXyOUM0sQuXrbaAh9OwXA==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-sdk-s3@3.758.0': - resolution: {integrity: sha512-6mJ2zyyHPYSV6bAcaFpsdoXZJeQlR1QgBnZZ6juY/+dcYiuyWCdyLUbGzSZSE7GTfx6i+9+QWFeoIMlWKgU63A==} + '@aws-sdk/middleware-sdk-s3@3.775.0': + resolution: {integrity: sha512-zsvcu7cWB28JJ60gVvjxPCI7ZU7jWGcpNACPiZGyVtjYXwcxyhXbYEVDSWKsSA6ERpz9XrpLYod8INQWfW3ECg==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-ssec@3.734.0': - resolution: {integrity: sha512-d4yd1RrPW/sspEXizq2NSOUivnheac6LPeLSLnaeTbBG9g1KqIqvCzP1TfXEqv2CrWfHEsWtJpX7oyjySSPvDQ==} + '@aws-sdk/middleware-ssec@3.775.0': + resolution: {integrity: sha512-Iw1RHD8vfAWWPzBBIKaojO4GAvQkHOYIpKdAfis/EUSUmSa79QsnXnRqsdcE0mCB0Ylj23yi+ah4/0wh9FsekA==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-user-agent@3.758.0': - resolution: {integrity: sha512-iNyehQXtQlj69JCgfaOssgZD4HeYGOwxcaKeG6F+40cwBjTAi0+Ph1yfDwqk2qiBPIRWJ/9l2LodZbxiBqgrwg==} + '@aws-sdk/middleware-user-agent@3.782.0': + resolution: {integrity: sha512-i32H2R6IItX+bQ2p4+v2gGO2jA80jQoJO2m1xjU9rYWQW3+ErWy4I5YIuQHTBfb6hSdAHbaRfqPDgbv9J2rjEg==} engines: {node: '>=18.0.0'} - '@aws-sdk/nested-clients@3.772.0': - resolution: {integrity: sha512-gNJbBxR5YlEumsCS9EWWEASXEnysL0aDnr9MNPX1ip/g1xOqRHmytgV/+t8RFZFTKg0OprbWTq5Ich3MqsEuCQ==} + '@aws-sdk/nested-clients@3.782.0': + resolution: {integrity: sha512-QOYC8q7luzHFXrP0xYAqBctoPkynjfV0r9dqntFu4/IWMTyC1vlo1UTxFAjIPyclYw92XJyEkVCVg9v/nQnsUA==} engines: {node: '>=18.0.0'} - '@aws-sdk/region-config-resolver@3.734.0': - resolution: {integrity: sha512-Lvj1kPRC5IuJBr9DyJ9T9/plkh+EfKLy+12s/mykOy1JaKHDpvj+XGy2YO6YgYVOb8JFtaqloid+5COtje4JTQ==} + '@aws-sdk/region-config-resolver@3.775.0': + resolution: {integrity: sha512-40iH3LJjrQS3LKUJAl7Wj0bln7RFPEvUYKFxtP8a+oKFDO0F65F52xZxIJbPn6sHkxWDAnZlGgdjZXM3p2g5wQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/signature-v4-multi-region@3.758.0': - resolution: {integrity: sha512-0RPCo8fYJcrenJ6bRtiUbFOSgQ1CX/GpvwtLU2Fam1tS9h2klKK8d74caeV6A1mIUvBU7bhyQ0wMGlwMtn3EYw==} + '@aws-sdk/signature-v4-multi-region@3.775.0': + resolution: {integrity: sha512-cnGk8GDfTMJ8p7+qSk92QlIk2bmTmFJqhYxcXZ9PysjZtx0xmfCMxnG3Hjy1oU2mt5boPCVSOptqtWixayM17g==} engines: {node: '>=18.0.0'} - '@aws-sdk/token-providers@3.772.0': - resolution: {integrity: sha512-d1Waa1vyebuokcAWYlkZdtFlciIgob7B39vPRmtxMObbGumJKiOy/qCe2/FB/72h1Ej9Ih32lwvbxUjORQWN4g==} + '@aws-sdk/token-providers@3.782.0': + resolution: {integrity: sha512-4tPuk/3+THPrzKaXW4jE2R67UyGwHLFizZ47pcjJWbhb78IIJAy94vbeqEQ+veS84KF5TXcU7g5jGTXC0D70Wg==} engines: {node: '>=18.0.0'} - '@aws-sdk/types@3.734.0': - resolution: {integrity: sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg==} + '@aws-sdk/types@3.775.0': + resolution: {integrity: sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA==} engines: {node: '>=18.0.0'} '@aws-sdk/util-arn-parser@3.723.0': resolution: {integrity: sha512-ZhEfvUwNliOQROcAk34WJWVYTlTa4694kSVhDSjW6lE1bMataPnIN8A0ycukEzBXmd8ZSoBcQLn6lKGl7XIJ5w==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-endpoints@3.743.0': - resolution: {integrity: sha512-sN1l559zrixeh5x+pttrnd0A3+r34r0tmPkJ/eaaMaAzXqsmKU/xYre9K3FNnsSS1J1k4PEfk/nHDTVUgFYjnw==} + '@aws-sdk/util-endpoints@3.782.0': + resolution: {integrity: sha512-/RJOAO7o7HI6lEa4ASbFFLHGU9iPK876BhsVfnl54MvApPVYWQ9sHO0anOUim2S5lQTwd/6ghuH3rFYSq/+rdw==} engines: {node: '>=18.0.0'} '@aws-sdk/util-locate-window@3.208.0': resolution: {integrity: sha512-iua1A2+P7JJEDHVgvXrRJSvsnzG7stYSGQnBVphIUlemwl6nN5D+QrgbjECtrbxRz8asYFHSzhdhECqN+tFiBg==} engines: {node: '>=14.0.0'} - '@aws-sdk/util-user-agent-browser@3.734.0': - resolution: {integrity: sha512-xQTCus6Q9LwUuALW+S76OL0jcWtMOVu14q+GoLnWPUM7QeUw963oQcLhF7oq0CtaLLKyl4GOUfcwc773Zmwwng==} + '@aws-sdk/util-user-agent-browser@3.775.0': + resolution: {integrity: sha512-txw2wkiJmZKVdDbscK7VBK+u+TJnRtlUjRTLei+elZg2ADhpQxfVAQl436FUeIv6AhB/oRHW6/K/EAGXUSWi0A==} - '@aws-sdk/util-user-agent-node@3.758.0': - resolution: {integrity: sha512-A5EZw85V6WhoKMV2hbuFRvb9NPlxEErb4HPO6/SPXYY4QrjprIzScHxikqcWv1w4J3apB1wto9LPU3IMsYtfrw==} + '@aws-sdk/util-user-agent-node@3.782.0': + resolution: {integrity: sha512-dMFkUBgh2Bxuw8fYZQoH/u3H4afQ12VSkzEi//qFiDTwbKYq+u+RYjc8GLDM6JSK1BShMu5AVR7HD4ap1TYUnA==} engines: {node: '>=18.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -1757,8 +1760,8 @@ packages: aws-crt: optional: true - '@aws-sdk/xml-builder@3.734.0': - resolution: {integrity: sha512-Zrjxi5qwGEcUsJ0ru7fRtW74WcTS0rbLcehoFB+rN1GRi2hbLcFaYs4PwVA5diLeAJH0gszv3x4Hr/S87MfbKQ==} + '@aws-sdk/xml-builder@3.775.0': + resolution: {integrity: sha512-b9NGO6FKJeLGYnV7Z1yvcP1TNU4dkD5jNsLWOF1/sygZoASaQhNOlaiJ/1OH331YQ1R1oWk38nBb0frsYkDsOQ==} engines: {node: '>=18.0.0'} '@babel/code-frame@7.23.5': @@ -1968,6 +1971,10 @@ packages: resolution: {integrity: sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.27.0': + resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==} + engines: {node: '>=6.9.0'} + '@babel/template@7.22.15': resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} engines: {node: '>=6.9.0'} @@ -2093,8 +2100,8 @@ packages: '@discordapp/twemoji@15.1.0': resolution: {integrity: sha512-QdpV4ifTONAXvDjRrMohausZeGrQ1ac/Ox6togUh6Xl3XKJ/KAaMMuAEi0qsb0wDwoVTSZBll5Y6+N3hB2ktBw==} - '@emnapi/runtime@1.3.0': - resolution: {integrity: sha512-XMBySMuNZs3DM96xcJmLW4EfGnf+uGmFNjzpehMjuX5PLB5j87ar2Zc4e3PVeZ3I5g3tYtAqskB28manlF69Zw==} + '@emnapi/runtime@1.4.3': + resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} '@esbuild/aix-ppc64@0.25.0': resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} @@ -2102,12 +2109,6 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.25.1': - resolution: {integrity: sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.25.2': resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==} engines: {node: '>=18'} @@ -2120,12 +2121,6 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.25.1': - resolution: {integrity: sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.25.2': resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==} engines: {node: '>=18'} @@ -2138,12 +2133,6 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.25.1': - resolution: {integrity: sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.25.2': resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==} engines: {node: '>=18'} @@ -2156,12 +2145,6 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.25.1': - resolution: {integrity: sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.25.2': resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==} engines: {node: '>=18'} @@ -2174,12 +2157,6 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.25.1': - resolution: {integrity: sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.25.2': resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==} engines: {node: '>=18'} @@ -2192,12 +2169,6 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.25.1': - resolution: {integrity: sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.25.2': resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==} engines: {node: '>=18'} @@ -2210,12 +2181,6 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.25.1': - resolution: {integrity: sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.25.2': resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==} engines: {node: '>=18'} @@ -2228,12 +2193,6 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.1': - resolution: {integrity: sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.25.2': resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==} engines: {node: '>=18'} @@ -2246,12 +2205,6 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.25.1': - resolution: {integrity: sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.25.2': resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==} engines: {node: '>=18'} @@ -2264,12 +2217,6 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.25.1': - resolution: {integrity: sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.25.2': resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==} engines: {node: '>=18'} @@ -2282,12 +2229,6 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.25.1': - resolution: {integrity: sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.25.2': resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==} engines: {node: '>=18'} @@ -2300,12 +2241,6 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.25.1': - resolution: {integrity: sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.25.2': resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==} engines: {node: '>=18'} @@ -2318,12 +2253,6 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.25.1': - resolution: {integrity: sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.25.2': resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==} engines: {node: '>=18'} @@ -2336,12 +2265,6 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.25.1': - resolution: {integrity: sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.25.2': resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==} engines: {node: '>=18'} @@ -2354,12 +2277,6 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.25.1': - resolution: {integrity: sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.25.2': resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==} engines: {node: '>=18'} @@ -2372,12 +2289,6 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.25.1': - resolution: {integrity: sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.25.2': resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==} engines: {node: '>=18'} @@ -2390,12 +2301,6 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.25.1': - resolution: {integrity: sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.25.2': resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==} engines: {node: '>=18'} @@ -2408,12 +2313,6 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.25.1': - resolution: {integrity: sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - '@esbuild/netbsd-arm64@0.25.2': resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==} engines: {node: '>=18'} @@ -2426,12 +2325,6 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.1': - resolution: {integrity: sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.25.2': resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==} engines: {node: '>=18'} @@ -2444,12 +2337,6 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.25.1': - resolution: {integrity: sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-arm64@0.25.2': resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==} engines: {node: '>=18'} @@ -2462,12 +2349,6 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.1': - resolution: {integrity: sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.25.2': resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==} engines: {node: '>=18'} @@ -2480,12 +2361,6 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.25.1': - resolution: {integrity: sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.25.2': resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==} engines: {node: '>=18'} @@ -2498,12 +2373,6 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.25.1': - resolution: {integrity: sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.25.2': resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==} engines: {node: '>=18'} @@ -2516,12 +2385,6 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.25.1': - resolution: {integrity: sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.25.2': resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==} engines: {node: '>=18'} @@ -2534,12 +2397,6 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.25.1': - resolution: {integrity: sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.25.2': resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==} engines: {node: '>=18'} @@ -2716,107 +2573,112 @@ packages: resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} engines: {node: '>=18.18'} - '@img/sharp-darwin-arm64@0.33.5': - resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} + '@img/sharp-darwin-arm64@0.34.1': + resolution: {integrity: sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.33.5': - resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} + '@img/sharp-darwin-x64@0.34.1': + resolution: {integrity: sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.0.4': - resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} + '@img/sharp-libvips-darwin-arm64@1.1.0': + resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.0.4': - resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} + '@img/sharp-libvips-darwin-x64@1.1.0': + resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.0.4': - resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} + '@img/sharp-libvips-linux-arm64@1.1.0': + resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linux-arm@1.0.5': - resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} + '@img/sharp-libvips-linux-arm@1.1.0': + resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==} cpu: [arm] os: [linux] - '@img/sharp-libvips-linux-s390x@1.0.4': - resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} + '@img/sharp-libvips-linux-ppc64@1.1.0': + resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.1.0': + resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==} cpu: [s390x] os: [linux] - '@img/sharp-libvips-linux-x64@1.0.4': - resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} + '@img/sharp-libvips-linux-x64@1.1.0': + resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==} cpu: [x64] os: [linux] - '@img/sharp-libvips-linuxmusl-arm64@1.0.4': - resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} + '@img/sharp-libvips-linuxmusl-arm64@1.1.0': + resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linuxmusl-x64@1.0.4': - resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} + '@img/sharp-libvips-linuxmusl-x64@1.1.0': + resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==} cpu: [x64] os: [linux] - '@img/sharp-linux-arm64@0.33.5': - resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} + '@img/sharp-linux-arm64@0.34.1': + resolution: {integrity: sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linux-arm@0.33.5': - resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} + '@img/sharp-linux-arm@0.34.1': + resolution: {integrity: sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - '@img/sharp-linux-s390x@0.33.5': - resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} + '@img/sharp-linux-s390x@0.34.1': + resolution: {integrity: sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - '@img/sharp-linux-x64@0.33.5': - resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} + '@img/sharp-linux-x64@0.34.1': + resolution: {integrity: sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-linuxmusl-arm64@0.33.5': - resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} + '@img/sharp-linuxmusl-arm64@0.34.1': + resolution: {integrity: sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linuxmusl-x64@0.33.5': - resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} + '@img/sharp-linuxmusl-x64@0.34.1': + resolution: {integrity: sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-wasm32@0.33.5': - resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} + '@img/sharp-wasm32@0.34.1': + resolution: {integrity: sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-win32-ia32@0.33.5': - resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} + '@img/sharp-win32-ia32@0.34.1': + resolution: {integrity: sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.33.5': - resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} + '@img/sharp-win32-x64@0.34.1': + resolution: {integrity: sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] @@ -3022,8 +2884,8 @@ packages: eslint-plugin-import: '>= 2' globals: '>= 15' - '@misskey-dev/sharp-read-bmp@1.2.0': - resolution: {integrity: sha512-er4pRakXzHYfEgOFAFfQagqDouG+wLm+kwNq1I30oSdIHDa0wM3KjFpfIGQ25Fks4GcmOl1s7Zh6xoQu5dNjTw==} + '@misskey-dev/sharp-read-bmp@1.3.0': + resolution: {integrity: sha512-18K95y0tXTtwl4BVfQb0JCr/9KHoHOfTKUUmZ7ibjzbS4bR/kGKoRkADsrdqBllF3nvu7PQN8zjUoM4SWoBLBg==} '@misskey-dev/summaly@5.2.0': resolution: {integrity: sha512-Djg6emAOdcY81a8TMTFi/qgIdTO96Z2qZfhHE+k+YRd8z9V/5MxauJr+U9z4kUVjSJJ1yzdOWqi26OsmVUaXZA==} @@ -3062,11 +2924,12 @@ packages: resolution: {integrity: sha512-wK+5pLK5XFmgtH3aQ2YVvA3HohS3xqV/OxuVOdNx9Wpnz7VE/fnC+e1A7ln6LFYeck7gOJ/dsZV6OLplOtAJ2w==} engines: {node: '>=18'} - '@nestjs/common@11.0.12': - resolution: {integrity: sha512-6PXxmDe2iYmb57xacnxzpW1NAxRZ7Gf+acMT7/hmRB/4KpZiFU/cNvLWwgbM2BL5QSzQulOwY6ny5bbKnPpB+A==} + '@nestjs/common@11.0.16': + resolution: {integrity: sha512-agvuQ8su4aZ+PVxAmY89odG1eR97HEQvxPmTMdDqyvDWzNerl7WQhUEd+j4/UyNWcF1or1UVcrtPj52x+eUSsA==} peerDependencies: class-transformer: '*' class-validator: '*' + file-type: ^20.4.1 reflect-metadata: ^0.1.12 || ^0.2.0 rxjs: ^7.1.0 peerDependenciesMeta: @@ -3075,8 +2938,8 @@ packages: class-validator: optional: true - '@nestjs/core@11.0.12': - resolution: {integrity: sha512-micQrbh9iL0PuYVx2vsUojuNmMUyqoMCuj7eGAUhvjiZUh4DBLPdxYmJEayCT/equHSiw9vNC95Vm0JigVZ44g==} + '@nestjs/core@11.0.15': + resolution: {integrity: sha512-MVVoTzG3vCeIjjmGmKWTZqwJYF3OAHXUFYKRV+bwRaFANcK+54TJpTSmf34eOPnDrlFnurTldSBZV2Cja+uSRw==} engines: {node: '>= 20'} peerDependencies: '@nestjs/common': ^11.0.0 @@ -3099,8 +2962,8 @@ packages: '@nestjs/common': ^10.0.0 '@nestjs/core': ^10.0.0 - '@nestjs/testing@11.0.12': - resolution: {integrity: sha512-jl1McTqrY+zRBFIWcFMVwesY2v++mAdHrrzXsLxatgkf6wRVh6te1MQ6LikgQ6qz4P5qzVV6EiXQVLGvARe5Xw==} + '@nestjs/testing@11.0.15': + resolution: {integrity: sha512-IMeDGWuzcmEVClOC+jYVJFtyjIG4clzllndhp6ECNiWHNdKR55PU6ugjKBB8kZ5JszME8OaIXUYFTdiR5dcXXA==} peerDependencies: '@nestjs/common': ^11.0.0 '@nestjs/core': ^11.0.0 @@ -3112,8 +2975,8 @@ packages: '@nestjs/platform-express': optional: true - '@noble/hashes@1.6.1': - resolution: {integrity: sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w==} + '@noble/hashes@1.7.1': + resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} engines: {node: ^14.21.3 || >=16} '@nodelib/fs.scandir@2.1.5': @@ -3535,98 +3398,103 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.36.0': - resolution: {integrity: sha512-jgrXjjcEwN6XpZXL0HUeOVGfjXhPyxAbbhD0BlXUB+abTOpbPiN5Wb3kOT7yb+uEtATNYF5x5gIfwutmuBA26w==} + '@rollup/rollup-android-arm-eabi@4.39.0': + resolution: {integrity: sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.36.0': - resolution: {integrity: sha512-NyfuLvdPdNUfUNeYKUwPwKsE5SXa2J6bCt2LdB/N+AxShnkpiczi3tcLJrm5mA+eqpy0HmaIY9F6XCa32N5yzg==} + '@rollup/rollup-android-arm64@4.39.0': + resolution: {integrity: sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.36.0': - resolution: {integrity: sha512-JQ1Jk5G4bGrD4pWJQzWsD8I1n1mgPXq33+/vP4sk8j/z/C2siRuxZtaUA7yMTf71TCZTZl/4e1bfzwUmFb3+rw==} + '@rollup/rollup-darwin-arm64@4.39.0': + resolution: {integrity: sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.36.0': - resolution: {integrity: sha512-6c6wMZa1lrtiRsbDziCmjE53YbTkxMYhhnWnSW8R/yqsM7a6mSJ3uAVT0t8Y/DGt7gxUWYuFM4bwWk9XCJrFKA==} + '@rollup/rollup-darwin-x64@4.39.0': + resolution: {integrity: sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.36.0': - resolution: {integrity: sha512-KXVsijKeJXOl8QzXTsA+sHVDsFOmMCdBRgFmBb+mfEb/7geR7+C8ypAml4fquUt14ZyVXaw2o1FWhqAfOvA4sg==} + '@rollup/rollup-freebsd-arm64@4.39.0': + resolution: {integrity: sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.36.0': - resolution: {integrity: sha512-dVeWq1ebbvByI+ndz4IJcD4a09RJgRYmLccwlQ8bPd4olz3Y213uf1iwvc7ZaxNn2ab7bjc08PrtBgMu6nb4pQ==} + '@rollup/rollup-freebsd-x64@4.39.0': + resolution: {integrity: sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.36.0': - resolution: {integrity: sha512-bvXVU42mOVcF4le6XSjscdXjqx8okv4n5vmwgzcmtvFdifQ5U4dXFYaCB87namDRKlUL9ybVtLQ9ztnawaSzvg==} + '@rollup/rollup-linux-arm-gnueabihf@4.39.0': + resolution: {integrity: sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.36.0': - resolution: {integrity: sha512-JFIQrDJYrxOnyDQGYkqnNBtjDwTgbasdbUiQvcU8JmGDfValfH1lNpng+4FWlhaVIR4KPkeddYjsVVbmJYvDcg==} + '@rollup/rollup-linux-arm-musleabihf@4.39.0': + resolution: {integrity: sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.36.0': - resolution: {integrity: sha512-KqjYVh3oM1bj//5X7k79PSCZ6CvaVzb7Qs7VMWS+SlWB5M8p3FqufLP9VNp4CazJ0CsPDLwVD9r3vX7Ci4J56A==} + '@rollup/rollup-linux-arm64-gnu@4.39.0': + resolution: {integrity: sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.36.0': - resolution: {integrity: sha512-QiGnhScND+mAAtfHqeT+cB1S9yFnNQ/EwCg5yE3MzoaZZnIV0RV9O5alJAoJKX/sBONVKeZdMfO8QSaWEygMhw==} + '@rollup/rollup-linux-arm64-musl@4.39.0': + resolution: {integrity: sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.36.0': - resolution: {integrity: sha512-1ZPyEDWF8phd4FQtTzMh8FQwqzvIjLsl6/84gzUxnMNFBtExBtpL51H67mV9xipuxl1AEAerRBgBwFNpkw8+Lg==} + '@rollup/rollup-linux-loongarch64-gnu@4.39.0': + resolution: {integrity: sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.36.0': - resolution: {integrity: sha512-VMPMEIUpPFKpPI9GZMhJrtu8rxnp6mJR3ZzQPykq4xc2GmdHj3Q4cA+7avMyegXy4n1v+Qynr9fR88BmyO74tg==} + '@rollup/rollup-linux-powerpc64le-gnu@4.39.0': + resolution: {integrity: sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.36.0': - resolution: {integrity: sha512-ttE6ayb/kHwNRJGYLpuAvB7SMtOeQnVXEIpMtAvx3kepFQeowVED0n1K9nAdraHUPJ5hydEMxBpIR7o4nrm8uA==} + '@rollup/rollup-linux-riscv64-gnu@4.39.0': + resolution: {integrity: sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.39.0': + resolution: {integrity: sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.36.0': - resolution: {integrity: sha512-4a5gf2jpS0AIe7uBjxDeUMNcFmaRTbNv7NxI5xOCs4lhzsVyGR/0qBXduPnoWf6dGC365saTiwag8hP1imTgag==} + '@rollup/rollup-linux-s390x-gnu@4.39.0': + resolution: {integrity: sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.36.0': - resolution: {integrity: sha512-5KtoW8UWmwFKQ96aQL3LlRXX16IMwyzMq/jSSVIIyAANiE1doaQsx/KRyhAvpHlPjPiSU/AYX/8m+lQ9VToxFQ==} + '@rollup/rollup-linux-x64-gnu@4.39.0': + resolution: {integrity: sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.36.0': - resolution: {integrity: sha512-sycrYZPrv2ag4OCvaN5js+f01eoZ2U+RmT5as8vhxiFz+kxwlHrsxOwKPSA8WyS+Wc6Epid9QeI/IkQ9NkgYyQ==} + '@rollup/rollup-linux-x64-musl@4.39.0': + resolution: {integrity: sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.36.0': - resolution: {integrity: sha512-qbqt4N7tokFwwSVlWDsjfoHgviS3n/vZ8LK0h1uLG9TYIRuUTJC88E1xb3LM2iqZ/WTqNQjYrtmtGmrmmawB6A==} + '@rollup/rollup-win32-arm64-msvc@4.39.0': + resolution: {integrity: sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.36.0': - resolution: {integrity: sha512-t+RY0JuRamIocMuQcfwYSOkmdX9dtkr1PbhKW42AMvaDQa+jOdpUYysroTF/nuPpAaQMWp7ye+ndlmmthieJrQ==} + '@rollup/rollup-win32-ia32-msvc@4.39.0': + resolution: {integrity: sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.36.0': - resolution: {integrity: sha512-aRXd7tRZkWLqGbChgcMMDEHjOKudo1kChb1Jt1IfR8cY/KIpgNviLeJy5FUb9IpSuQj8dU2fAYNMPW/hLKOSTw==} + '@rollup/rollup-win32-x64-msvc@4.39.0': + resolution: {integrity: sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==} cpu: [x64] os: [win32] @@ -3661,32 +3529,32 @@ packages: '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - '@sentry-internal/browser-utils@9.8.0': - resolution: {integrity: sha512-7aQDeU9ogMLKnEBFM/vvgMMgZDkfMhoZCtX8kq65gn33L4X2B8sI5oyUj2QJtXaRSsiUjbdCaquDLqZBCaLQHA==} + '@sentry-internal/browser-utils@9.12.0': + resolution: {integrity: sha512-GXuDEG2Ix8DmVtTkjsItWdusk2CvJ6EPWKYVqFKifxt+IAT3ZbhGZd99Rg3wdRmt9xhCNuS4QrDzDTPMPgfdCw==} engines: {node: '>=18'} - '@sentry-internal/feedback@9.8.0': - resolution: {integrity: sha512-xWiCJkD8ROuy2pnojuRLcLI6sezK399gasA5ZL4MCXdkryqZYs55Ef2Ofj4z0RdUc8gMUb81+LTqwbmbfTqNlQ==} + '@sentry-internal/feedback@9.12.0': + resolution: {integrity: sha512-3+UxoT97QIXNSUQS4ATL1FFws0RkUb6PeaQN8CPndI6mFlqTW5tuVVLNg9Eo1seNg7R/dfk6WHCWrYN1NbFFKQ==} engines: {node: '>=18'} - '@sentry-internal/replay-canvas@9.8.0': - resolution: {integrity: sha512-/6ELOnyCOItvqv2Os29JhE8ydDds3xibMQ+FomsSkClQdC4bbc/L74nm/fdXVpJkMswtjksiTwZo1nYTS3JsIw==} + '@sentry-internal/replay-canvas@9.12.0': + resolution: {integrity: sha512-p8LuKZgWT/CoQBbDOXkSGjWWnc8WsnAayWgna8M/ZFWNITCNEM2rCuqZOyWOElIlrni+M7qoEA3jS7MZe8Ejxw==} engines: {node: '>=18'} - '@sentry-internal/replay@9.8.0': - resolution: {integrity: sha512-YJhhNnrsufYVIX9s5lNSFFQrBJjUtn5AxvrcnN0fvLymNg3Y73GOUpFmhTxyELjQneKiOViClxjoWSVAN7sqQA==} + '@sentry-internal/replay@9.12.0': + resolution: {integrity: sha512-njEQosFeO/UX+gG+DMRANkPUuz6OIJLb+A1GVylhq9adUgFQydQ9Ay3v7/x1gMhdfHVP6Jeb27qkti0BWYbzBQ==} engines: {node: '>=18'} - '@sentry/browser@9.8.0': - resolution: {integrity: sha512-iFM4PGLc6qCb0GaHnA5Uy09k25RXVSepAgS574cm1CH7II1wrRjTozKnPKROW89WDMuxoTOL7Tk7qPGCyWmA4g==} + '@sentry/browser@9.12.0': + resolution: {integrity: sha512-4xQYoZqi+VVhNvlhWiwRd57+SMr3Og4sLjuayAA+zIp1Wx/bDcIld697cugLwml/BR+mVJI2eokkgh1CBl6zag==} engines: {node: '>=18'} '@sentry/core@8.55.0': resolution: {integrity: sha512-6g7jpbefjHYs821Z+EBJ8r4Z7LT5h80YSWRJaylGS4nW5W5Z2KXzpdnyFarv37O7QjauzVC2E+PABmpkw5/JGA==} engines: {node: '>=14.18'} - '@sentry/core@9.8.0': - resolution: {integrity: sha512-EnN2yLWCbWjooWBPzwlXdZoJG/Bqn3ymbuXX++DUJuBGjSmtixQeTf/hKeVzj4zbib3BbbYsNBasRVjq8Rk5ng==} + '@sentry/core@9.12.0': + resolution: {integrity: sha512-jOqQK/90uzHmsBvkPTj/DAEFvA5poX4ZRyC7LE1zjg4F5jdOp3+M4W3qCy0CkSTu88Zu5VWBoppCU2Bs34XEqg==} engines: {node: '>=18'} '@sentry/node@8.55.0': @@ -3709,8 +3577,8 @@ packages: engines: {node: '>=14.18'} hasBin: true - '@sentry/vue@9.8.0': - resolution: {integrity: sha512-E+27lL+aU8HjDo3DD3TlgStTIxBZHVqz6jZcL0/tig/JldpFRetO77terRHNfSVlPc0m3aNXuARu7G438f7ZlQ==} + '@sentry/vue@9.12.0': + resolution: {integrity: sha512-9/k6Jo1/nCJzj5qUNhQzzMCfBGT613CTrI2AauECyf44kkSE1FllvbCwT/V4eUMd+IiEL9qzqMLGTVwYUynxHw==} engines: {node: '>=18'} peerDependencies: pinia: 2.x || 3.x @@ -3719,23 +3587,23 @@ packages: pinia: optional: true - '@shikijs/core@3.2.1': - resolution: {integrity: sha512-FhsdxMWYu/C11sFisEp7FMGBtX/OSSbnXZDMBhGuUDBNTdsoZlMSgQv5f90rwvzWAdWIW6VobD+G3IrazxA6dQ==} + '@shikijs/core@3.2.2': + resolution: {integrity: sha512-yvlSKVMLjddAGBa2Yu+vUZxuu3sClOWW1AG+UtJkvejYuGM5BVL35s6Ijiwb75O9QdEx6IkMxinHZSi8ZyrBaA==} - '@shikijs/engine-javascript@3.2.1': - resolution: {integrity: sha512-eMdcUzN3FMQYxOmRf2rmU8frikzoSHbQDFH2hIuXsrMO+IBOCI9BeeRkCiBkcLDHeRKbOCtYMJK3D6U32ooU9Q==} + '@shikijs/engine-javascript@3.2.2': + resolution: {integrity: sha512-tlDKfhWpF4jKLUyVAnmL+ggIC+0VyteNsUpBzh1iwWLZu4i+PelIRr0TNur6pRRo5UZIv3ss/PLMuwahg9S2hg==} - '@shikijs/engine-oniguruma@3.2.1': - resolution: {integrity: sha512-wZZAkayEn6qu2+YjenEoFqj0OyQI64EWsNR6/71d1EkG4sxEOFooowKivsWPpaWNBu3sxAG+zPz5kzBL/SsreQ==} + '@shikijs/engine-oniguruma@3.2.2': + resolution: {integrity: sha512-vyXRnWVCSvokwbaUD/8uPn6Gqsf5Hv7XwcW4AgiU4Z2qwy19sdr6VGzMdheKKN58tJOOe5MIKiNb901bgcUXYQ==} - '@shikijs/langs@3.2.1': - resolution: {integrity: sha512-If0iDHYRSGbihiA8+7uRsgb1er1Yj11pwpX1c6HLYnizDsKAw5iaT3JXj5ZpaimXSWky/IhxTm7C6nkiYVym+A==} + '@shikijs/langs@3.2.2': + resolution: {integrity: sha512-NY0Urg2dV9ETt3JIOWoMPuoDNwte3geLZ4M1nrPHbkDS8dWMpKcEwlqiEIGqtwZNmt5gKyWpR26ln2Bg2ecPgw==} - '@shikijs/themes@3.2.1': - resolution: {integrity: sha512-k5DKJUT8IldBvAm8WcrDT5+7GA7se6lLksR+2E3SvyqGTyFMzU2F9Gb7rmD+t+Pga1MKrYFxDIeyWjMZWM6uBQ==} + '@shikijs/themes@3.2.2': + resolution: {integrity: sha512-Zuq4lgAxVKkb0FFdhHSdDkALuRpsj1so1JdihjKNQfgM78EHxV2JhO10qPsMrm01FkE3mDRTdF68wfmsqjt6HA==} - '@shikijs/types@3.2.1': - resolution: {integrity: sha512-/NTWAk4KE2M8uac0RhOsIhYQf4pdU0OywQuYDGIGAJ6Mjunxl2cGiuLkvu4HLCMn+OTTLRWkjZITp+aYJv60yA==} + '@shikijs/types@3.2.2': + resolution: {integrity: sha512-a5TiHk7EH5Lso8sHcLHbVNNhWKP0Wi3yVnXnu73g86n3WoDgEra7n3KszyeCGuyoagspQ2fzvy4cpSc8pKhb0A==} '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} @@ -4040,120 +3908,120 @@ packages: '@sqltools/formatter@1.2.5': resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} - '@storybook/addon-actions@8.6.7': - resolution: {integrity: sha512-XgZCwIcZGThEyD7e2q7rN/jzg7ZHUxn/ln403eex04jWAGBBbtC2IVuowwCWV8HwDihnhpCZEP6HlgjakOYZbQ==} + '@storybook/addon-actions@8.6.12': + resolution: {integrity: sha512-B5kfiRvi35oJ0NIo53CGH66H471A3XTzrfaa6SxXEJsgxxSeKScG5YeXcCvLiZfvANRQ7QDsmzPUgg0o3hdMXw==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/addon-backgrounds@8.6.7': - resolution: {integrity: sha512-aDFzi83gDhYn0+FGjRYbY5TfBtoG/UgVr9Abi7s5ceabZRhPrYikMyFX0o8V3Z8COl6wUmWmF1luYE4MfXgN2g==} + '@storybook/addon-backgrounds@8.6.12': + resolution: {integrity: sha512-lmIAma9BiiCTbJ8YfdZkXjpnAIrOUcgboLkt1f6XJ78vNEMnLNzD9gnh7Tssz1qrqvm34v9daDjIb+ggdiKp3Q==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/addon-controls@8.6.7': - resolution: {integrity: sha512-6ReB1Sc1qlqvAM7NUmtw2K1cKCgGBs8zYRgL44Q2ti+r55a2ownhm6WUm/kZs2ixSkV9ehm1osiqbGBfAn0Isw==} + '@storybook/addon-controls@8.6.12': + resolution: {integrity: sha512-9VSRPJWQVb9wLp21uvpxDGNctYptyUX0gbvxIWOHMH3R2DslSoq41lsC/oQ4l4zSHVdL+nq8sCTkhBxIsjKqdQ==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/addon-docs@8.6.7': - resolution: {integrity: sha512-kgNPEVuLGNJE8EdVQi5Tg2DYgR66/gut07jvhqnJfNqUkj6UpBHad0JR1uwrd7xS3kJs29Fs7UyU87RJnSlwcg==} + '@storybook/addon-docs@8.6.12': + resolution: {integrity: sha512-kEezQjAf/p3SpDzLABgg4fbT48B6dkT2LiZCKTRmCrJVtuReaAr4R9MMM6Jsph6XjbIj/SvOWf3CMeOPXOs9sg==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/addon-essentials@8.6.7': - resolution: {integrity: sha512-PFT62xuknk4wD1hTZEnYbGP1mJFPlhk7zVVlMjoldMUhmbHsFRhdWCpo93Vu9E3BWVxFxL3Jj+UwSwH4uVmekQ==} + '@storybook/addon-essentials@8.6.12': + resolution: {integrity: sha512-Y/7e8KFlttaNfv7q2zoHMPdX6hPXHdsuQMAjYl5NG9HOAJREu4XBy4KZpbcozRe4ApZ78rYsN/MO1EuA+bNMIA==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/addon-highlight@8.6.7': - resolution: {integrity: sha512-4KE1RF4XfqII7XrJPgf/1W0t0EWRKmik5Rrpb6WofXfgZ2QYzLFnyESjf67/g2TMgDnle2drfa/pt5tGV4+I2Q==} + '@storybook/addon-highlight@8.6.12': + resolution: {integrity: sha512-9FITVxdoycZ+eXuAZL9ElWyML/0fPPn9UgnnAkrU7zkMi+Segq/Tx7y+WWanC5zfWZrXAuG6WTOYEXeWQdm//w==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/addon-interactions@8.6.7': - resolution: {integrity: sha512-FbEWWxCl/5DJDyEGTJqtTJ5XbxM2rOUGCPy+3CkPSpI9yvz3zprRTJRHPFrh7hUqQ4Qkqfjm7JCO29+0CmeE0g==} + '@storybook/addon-interactions@8.6.12': + resolution: {integrity: sha512-cTAJlTq6uVZBEbtwdXkXoPQ4jHOAGKQnYSezBT4pfNkdjn/FnEeaQhMBDzf14h2wr5OgBnJa6Lmd8LD9ficz4A==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/addon-links@8.6.7': - resolution: {integrity: sha512-fIiXlaOa9Bv2tbBshQbh/BjzGOilXVx+6nrX9VkLOg7UvzAvivtSraRmPWjgdtsChAHC8Xac42KUCNGQ/rkf5w==} + '@storybook/addon-links@8.6.12': + resolution: {integrity: sha512-AfKujFHoAxhxq4yu+6NwylltS9lf5MPs1eLLXvOlwo3l7Y/c68OdxJ7j68vLQhs9H173WVYjKyjbjFxJWf/YYg==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.6.7 + storybook: ^8.6.12 peerDependenciesMeta: react: optional: true - '@storybook/addon-mdx-gfm@8.6.7': - resolution: {integrity: sha512-IfGgPnOMq51yBpnaY2w5hlm4pBgIMig61vsigqySU7KKFY6qxD/LcIJAxOPh2s9dLhYGYDrO0hFGR9fQ7Niu5A==} + '@storybook/addon-mdx-gfm@8.6.12': + resolution: {integrity: sha512-OKI5+O8xyK8axGPFwkl38NGJ6Rjf7kyhiBPxw5NuHOjOnU/FL4Pw3QmY47TT96TVws27vP3gF5+FX8lj3Dd3rQ==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/addon-measure@8.6.7': - resolution: {integrity: sha512-4dkkCltjKRcJH+ZMv5nbNT0LBQfcXIydVfN9mAvhDsiPFD5eZcHbN4XVfUslECWgrkaa/a6FE1W9PNEUBjCJaA==} + '@storybook/addon-measure@8.6.12': + resolution: {integrity: sha512-tACmwqqOvutaQSduw8SMb62wICaT1rWaHtMN3vtWXuxgDPSdJQxLP+wdVyRYMAgpxhLyIO7YRf++Hfha9RHgFg==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/addon-outline@8.6.7': - resolution: {integrity: sha512-atCpCi2CqAWQwL1nu1l5VpIA4fRMnbD4RZMsEiib1suUfNyJv0RdsSgZhp/f+e9sUS0TtMdwhzWT36eEA7VxhQ==} + '@storybook/addon-outline@8.6.12': + resolution: {integrity: sha512-1ylwm+n1s40S91No0v9T4tCjZORu3GbnjINlyjYTDLLhQHyBQd3nWR1Y1eewU4xH4cW9SnSLcMQFS/82xHqU6A==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/addon-storysource@8.6.7': - resolution: {integrity: sha512-tIoTQp3MMyF3S4XarMOBVO40DofILO3Mz8upT4wGEfQULLjgCkS2K5c4BbT4de1hF49JsqvPByVlavntWQFTdg==} + '@storybook/addon-storysource@8.6.12': + resolution: {integrity: sha512-EAvf7DubbIw8OnTCp/blmgDaO4hzL8rROR+SpNMx6t3NwFgfJTP4VosiNOFIrtdGOaUeG0I815XSUphjNQ14lw==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/addon-toolbars@8.6.7': - resolution: {integrity: sha512-gR+mRs+Cc5GINZdKgE7afJLFCSMHkz40+zzdrPu6yY2P4B3UOvuQpt+zC/Er5YQ31EEjIvM6/XMQTM0i2db8AA==} + '@storybook/addon-toolbars@8.6.12': + resolution: {integrity: sha512-HEcSzo1DyFtIu5/ikVOmh5h85C1IvK9iFKSzBR6ice33zBOaehVJK+Z5f487MOXxPsZ63uvWUytwPyViGInj+g==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/addon-viewport@8.6.7': - resolution: {integrity: sha512-kTrt6ByCbBIbqoRqQO9watDl5nSIKCC+R0/EmpEl6ZtzBV3l8trZHdvCHhIqOyv7nfaa7pIeTTG1GD6Gdrxk3w==} + '@storybook/addon-viewport@8.6.12': + resolution: {integrity: sha512-EXK2LArAnABsPP0leJKy78L/lbMWow+EIJfytEP5fHaW4EhMR6h7Hzaqzre6U0IMMr/jVFa1ci+m0PJ0eQc2bw==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/blocks@8.6.7': - resolution: {integrity: sha512-IFhIKO7R1UPpnoG/5tZH0FgC79oYgXNf+7aGUwq29M/CQWy6p/Pvp0y4P962btY1UZRol+SsU//33nH8o6yNRw==} + '@storybook/blocks@8.6.12': + resolution: {integrity: sha512-DohlTq6HM1jDbHYiXL4ZvZ00VkhpUp5uftzj/CZDLY1fYHRjqtaTwWm2/OpceivMA8zDitLcq5atEZN+f+siTg==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^8.6.7 + storybook: ^8.6.12 peerDependenciesMeta: react: optional: true react-dom: optional: true - '@storybook/builder-vite@8.6.7': - resolution: {integrity: sha512-hgYnVu2cy8clrmDwidu4XjvFMTEi9WiblLH5cPI3LWQjVajIQmDpcWVp6kbD063sIOphh9zYP7cVKGO7ktMB/g==} + '@storybook/builder-vite@8.6.12': + resolution: {integrity: sha512-Gju21ud/3Qw4v2vLNaa5SuJECsI9ICNRr2G0UyCCzRvCHg8jpA9lDReu2NqhLDyFIuDG+ZYT38gcaHEUoNQ8KQ==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 vite: ^4.0.0 || ^5.0.0 || ^6.0.0 - '@storybook/components@8.6.7': - resolution: {integrity: sha512-8pnjH1w7PZ/Iiuve1/BJY7EO/kmu0qdE34X1ZM8DyHzuy33EL/PfUuhxNkrL4ayMXrEDp/EJMHx2bqO1RdRV6A==} + '@storybook/components@8.6.12': + resolution: {integrity: sha512-FiaE8xvCdvKC2arYusgtlDNZ77b8ysr8njAYQZwwaIHjy27TbR2tEpLDCmUwSbANNmivtc/xGEiDDwcNppMWlQ==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/core-events@8.6.7': - resolution: {integrity: sha512-7g6Ic2L60SwKiPyqDUBfb5z1hw6YKmA2KpV1ndOFxMQm052zTnGequXdLXG6kIpgw30pws5eq1tjh3tkPCPFew==} + '@storybook/core-events@8.6.12': + resolution: {integrity: sha512-j2MUlSfYOhTsjlruRWTqSVwYreJGFIsWeqHFAhCdtmXe3qpFBM/LuxTKuaM1uWvs6vEAyGEzDw8+DXwuO6uISg==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/core@8.6.7': - resolution: {integrity: sha512-FcvLFA+Qn3+D6LgQkk0MOXA5FBz8DGc0UZmZuVbIwIUV4MV4ywCMwtKdG0cyhtzQg0YNyfiIYWJr7lZ4jLLhYg==} + '@storybook/core@8.6.12': + resolution: {integrity: sha512-t+ZuDzAlsXKa6tLxNZT81gEAt4GNwsKP/Id2wluhmUWD/lwYW0uum1JiPUuanw8xD6TdakCW/7ULZc7aQUBLCQ==} peerDependencies: prettier: ^2 || ^3 peerDependenciesMeta: prettier: optional: true - '@storybook/csf-plugin@8.6.7': - resolution: {integrity: sha512-HK7yQD4kFu04JOKnUwoFeR58r5WY6ucF0D8zfW4Gx+r8hBJ5K4t3z6k2dlIlRQF1X5+2vNkQOwD8liHjckuZ8Q==} + '@storybook/csf-plugin@8.6.12': + resolution: {integrity: sha512-6s8CnP1aoKPb3XtC0jRLUp8M5vTA8RhGAwQDKUsFpCC7g89JR9CaKs9FY2ZSzsNbjR15uASi7b3K8BzeYumYQg==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 '@storybook/global@5.0.0': resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} @@ -4165,49 +4033,49 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - '@storybook/instrumenter@8.6.7': - resolution: {integrity: sha512-FeQiV0g5crCWs0P1wKY4xZzb4PxAYNcrm2+9LLGVqwnC7qzrSCPf0p10MlveVfwsen1m6Wbqfe+wl21c31Hfmg==} + '@storybook/instrumenter@8.6.12': + resolution: {integrity: sha512-VK5fYAF8jMwWP/u3YsmSwKGh+FeSY8WZn78flzRUwirp2Eg1WWjsqPRubAk7yTpcqcC/km9YMF3KbqfzRv2s/A==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/manager-api@8.6.7': - resolution: {integrity: sha512-BA8RxaLP07WGF660LWo7qB3Jomr/+MPuCZmuKPqXxPhfIovqYjr0hnugxJBjEah0ic31aNX4NucNfDRuV7F5sA==} + '@storybook/manager-api@8.6.12': + resolution: {integrity: sha512-O0SpISeJLNTQvhSBOsWzzkCgs8vCjOq1578rwqHlC6jWWm4QmtfdyXqnv7rR1Hk08kQ+Dzqh0uhwHx0nfwy4nQ==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/preview-api@8.6.7': - resolution: {integrity: sha512-Rz83Nx43v3Dn9/SjhIsorkcx1gPmlclueuzf6YywJTqE1E/L4dgoe2mOA9MfF0jr0bh3TwEA2J3ii0Jstg1Orw==} + '@storybook/preview-api@8.6.12': + resolution: {integrity: sha512-84FE3Hrs0AYKHqpDZOwx1S/ffOfxBdL65lhCoeI8GoWwCkzwa9zEP3kvXBo/BnEDO7nAfxvMhjASTZXbKRJh5Q==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/react-dom-shim@8.6.7': - resolution: {integrity: sha512-+JH7gbRI6NRbt9o0l1rY4wFdeVt8wGRddm0b55OBlwBGlFo2nvGVOH73J4AGphXVhfY7z33I3TXIjXQ561UdEQ==} + '@storybook/react-dom-shim@8.6.12': + resolution: {integrity: sha512-51QvoimkBzYs8s3rCYnY5h0cFqLz/Mh0vRcughwYaXckWzDBV8l67WBO5Xf5nBsukCbWyqBVPpEQLww8s7mrLA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/react-vite@8.6.7': - resolution: {integrity: sha512-KiTeYaZ+AUQ1AFHSItP8dhUbd2v7Qy8+BB7w64VxQMw/dw5n0Z38lo4Tzdlkn22q2smW2ce4QwAzh2pfTz3b8g==} + '@storybook/react-vite@8.6.12': + resolution: {integrity: sha512-UA2Kule99oyFgHdhcuhrRwCKyWu/yMbqbl9U7NwowFHNwWWFjVMMir/AmfShb/H1C1DQ3LqOad6/QwJyPLjP8g==} engines: {node: '>=18.0.0'} peerDependencies: - '@storybook/test': 8.6.7 + '@storybook/test': 8.6.12 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.6.7 + storybook: ^8.6.12 vite: ^4.0.0 || ^5.0.0 || ^6.0.0 peerDependenciesMeta: '@storybook/test': optional: true - '@storybook/react@8.6.7': - resolution: {integrity: sha512-6R8znSm7kzsoAJyRbEiDWE+5xjeAIzwEcfT60fqx+uMdd0vDFM7f2uT4fYy+CijWas1oFWcNV/LMd3EqSkBGsQ==} + '@storybook/react@8.6.12': + resolution: {integrity: sha512-NzxlHLA5DkDgZM/dMwTYinuzRs6rsUPmlqP+NIv6YaciQ4NGnTYyOC7R/SqI6HHFm8ZZ5eMYvpfiFmhZ9rU+rQ==} engines: {node: '>=18.0.0'} peerDependencies: - '@storybook/test': 8.6.7 + '@storybook/test': 8.6.12 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.6.7 + storybook: ^8.6.12 typescript: '>= 4.2.x' peerDependenciesMeta: '@storybook/test': @@ -4215,38 +4083,38 @@ packages: typescript: optional: true - '@storybook/source-loader@8.6.7': - resolution: {integrity: sha512-ycfrPHCs5OUrJTLCXDxvxLVB1zjL7IEepPs53o4RGRWO8xV1z0QfXXiX1drk48rep6dDu+a3mRWfNJ8m0RV/GA==} + '@storybook/source-loader@8.6.12': + resolution: {integrity: sha512-Yfq54Vh1RnsUXqda6yd79gUoqjOfvig9t6a2eZDkLSBlFiYIUqHYCfMBFXxQTJN2pn0BlZccZs5ho85q3ULWWQ==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/test@8.6.7': - resolution: {integrity: sha512-uF1JbBtdT7tuiXfEtHsUShBHIhm2vc0C39nKVJaTWyK9CybajXaj2Ny3IRa3oY9NKnklwGgN+kZ/Z9YiIOc4MQ==} + '@storybook/test@8.6.12': + resolution: {integrity: sha512-0BK1Eg+VD0lNMB1BtxqHE3tP9FdkUmohtvWG7cq6lWvMrbCmAmh3VWai3RMCCDOukPFpjabOr8BBRLVvhNpv2w==} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 - '@storybook/theming@8.6.7': - resolution: {integrity: sha512-F/i4XS5bew9dvtNiHvDJF0mko1IUbPM9PUjTYPaw6cK8ytS0kdec703MsJ/GUA7seeEWBeGdZjV3ua0pys650A==} + '@storybook/theming@8.6.12': + resolution: {integrity: sha512-6VjZg8HJ2Op7+KV7ihJpYrDnFtd9D1jrQnUS8LckcpuBXrIEbaut5+34ObY8ssQnSqkk2GwIZBBBQYQBCVvkOw==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/types@8.6.7': - resolution: {integrity: sha512-Zs8SWF099Bkw39vSdS+81X/12PEiVzg5s84j47YUXF4XTPtjB38Fl36TdEUXUcL/VOOU7uyRQyZu3EoB+sambg==} + '@storybook/types@8.6.12': + resolution: {integrity: sha512-G/nR+js7KV1qKH3nAcOfwceERBic5e03dpkeA6PDmqBiQ8XeM9B6N4NTMhXi/2gM5ZAGJ+NxJMaW6zLnc32DjA==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/vue3-vite@8.6.7': - resolution: {integrity: sha512-CHaWS218UXP2dB23LsCOPCPvImb2KkD5Qiz9fvT7U5RukWbHomQNKo9EQs3ujIjwVjomotBXMm1TfMUIE2Q3lg==} + '@storybook/vue3-vite@8.6.12': + resolution: {integrity: sha512-ihYH2TiV14B8V1mrCVVrbjuf+F6+V/78oWofVkvnUQnpwH4CnAySGf6bz6c6/Y6qEr9r30ECUe6/sS0TMt1ZAQ==} engines: {node: '>=18.0.0'} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 vite: ^4.0.0 || ^5.0.0 || ^6.0.0 - '@storybook/vue3@8.6.7': - resolution: {integrity: sha512-w32n6x4bOtvNGT2hz+MepJ6pwOJr0ARw2vagMEhYGnwinwCIM7yNaaQv2a3uy87BQnH5miyFKFXEL0Axrj9wOg==} + '@storybook/vue3@8.6.12': + resolution: {integrity: sha512-mgGRMrFghDW5nHCDbdbhC4YUrOs7mCzwEuLZtdcvpB8TUPP62lTSnv3Gvcz8r12HjyIK6Jow9WgjTtdownGzkA==} engines: {node: '>=18.0.0'} peerDependencies: - storybook: ^8.6.7 + storybook: ^8.6.12 vue: ^3.0.0 '@stylistic/eslint-plugin@4.2.0': @@ -4272,14 +4140,14 @@ packages: cpu: [arm64] os: [android] - '@swc/core-darwin-arm64@1.11.11': - resolution: {integrity: sha512-vJcjGVDB8cZH7zyOkC0AfpFYI/7GHKG0NSsH3tpuKrmoAXJyCYspKPGid7FT53EAlWreN7+Pew+bukYf5j+Fmg==} + '@swc/core-darwin-arm64@1.11.18': + resolution: {integrity: sha512-K6AntdUlNMQg8aChqjeXwnVhK6d4WRZ9TgtLSTmdU0Ugll4an7QK49s9NrT7XQU91cEsVvzdr++p1bNImx0hJg==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.11.11': - resolution: {integrity: sha512-/N4dGdqEYvD48mCF3QBSycAbbQd3yoZ2YHSzYesQf8usNc2YpIhYqEH3sql02UsxTjEFOJSf1bxZABDdhbSl6A==} + '@swc/core-darwin-x64@1.11.18': + resolution: {integrity: sha512-RCRvC6Q9M5BArTvj/IzUAAYGrgxYFbTTnAtf6UX7JFq2DAn+hEwYUjmC1m0gFso9HqFU0m5QZUGfZvVmACGWUw==} engines: {node: '>=10'} cpu: [x64] os: [darwin] @@ -4290,56 +4158,56 @@ packages: cpu: [x64] os: [freebsd] - '@swc/core-linux-arm-gnueabihf@1.11.11': - resolution: {integrity: sha512-hsBhKK+wVXdN3x9MrL5GW0yT8o9GxteE5zHAI2HJjRQel3HtW7m5Nvwaq+q8rwMf4YQRd8ydbvwl4iUOZx7i2Q==} + '@swc/core-linux-arm-gnueabihf@1.11.18': + resolution: {integrity: sha512-wteAKf8YKb3jOnZFm3EzuIMzzCVXMuQOLHsz1IgEOc44/gdgNXKxaYTWAowZuej7t68tf/w0cRNMc7Le414v/g==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.11.11': - resolution: {integrity: sha512-YOCdxsqbnn/HMPCNM6nrXUpSndLXMUssGTtzT7ffXqr7WuzRg2e170FVDVQFIkb08E7Ku5uOnnUVAChAJQbMOQ==} + '@swc/core-linux-arm64-gnu@1.11.18': + resolution: {integrity: sha512-hY6jJYZ6PKHSBo5OATswfyKsUgsWu9+4nDcN8liYIRRgz3E0G9wk0VUTP4cFPivBFeHWTTAGz687/Nf2aQEIpw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.11.11': - resolution: {integrity: sha512-nR2tfdQRRzwqR2XYw9NnBk9Fdvff/b8IiJzDL28gRR2QiJWLaE8LsRovtWrzCOYq6o5Uu9cJ3WbabWthLo4jLw==} + '@swc/core-linux-arm64-musl@1.11.18': + resolution: {integrity: sha512-slu0mlP2nucvQalttnapfpqpD/LlM9NHx9g3ofgsLzjObyMEBiX4ZysQ3y65U8Mjw71RNqtLd/ZmvxI6OmLdiQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.11.11': - resolution: {integrity: sha512-b4gBp5HA9xNWNC5gsYbdzGBJWx4vKSGybGMGOVWWuF+ynx10+0sA/o4XJGuNHm8TEDuNh9YLKf6QkIO8+GPJ1g==} + '@swc/core-linux-x64-gnu@1.11.18': + resolution: {integrity: sha512-h9a/8PA25arMCQ9t8CE8rA1s0c77z4kCZZ7dUuUkD88yEXIrARMca1IKR7of+S3slfQrf1Zlq3Ac1Fb1HVJziQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.11.11': - resolution: {integrity: sha512-dEvqmQVswjNvMBwXNb8q5uSvhWrJLdttBSef3s6UC5oDSwOr00t3RQPzyS3n5qmGJ8UMTdPRmsopxmqaODISdg==} + '@swc/core-linux-x64-musl@1.11.18': + resolution: {integrity: sha512-0sMDJj5qUGK9QEw4lrxLxkTP/4AoKciqNzXvqbk+J9XuXN2aIv4BsR1Y7z3GwAeMFGsba2lbHLOtJlDsaqIsiA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.11.11': - resolution: {integrity: sha512-aZNZznem9WRnw2FbTqVpnclvl8Q2apOBW2B316gZK+qxbe+ktjOUnYaMhdCG3+BYggyIBDOnaJeQrXbKIMmNdw==} + '@swc/core-win32-arm64-msvc@1.11.18': + resolution: {integrity: sha512-zGv9HnfgBcKyt54MJRWdwRNu9BuYkAFM7bx+tWtKhd37Ef7ZX20QLs9xXl5wWDXCbsOdRxXIZgXs6PEL+Pzmrw==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.11.11': - resolution: {integrity: sha512-DjeJn/IfjgOddmJ8IBbWuDK53Fqw7UvOz7kyI/728CSdDYC3LXigzj3ZYs4VvyeOt+ZcQZUB2HA27edOifomGw==} + '@swc/core-win32-ia32-msvc@1.11.18': + resolution: {integrity: sha512-uBKj0S1lYv/E2ZhxHZOxSiQwoegYmzbPRpjq6eHBZDv97mu7W3K27/lsnPbvAfQ6b6rnv8BI+EsmJ7VLQBAHBQ==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.11.11': - resolution: {integrity: sha512-Gp/SLoeMtsU4n0uRoKDOlGrRC6wCfifq7bqLwSlAG8u8MyJYJCcwjg7ggm0rhLdC2vbiZ+lLVl3kkETp+JUvKg==} + '@swc/core-win32-x64-msvc@1.11.18': + resolution: {integrity: sha512-8USTRcdgeFMNBgvVXl8tz6n4+9s9m+zHsfDeBT4jPgwnq2bnLBlTUlwnPwzDxfg9nUJr6RFD4xeKfWyZZRosZg==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.11.11': - resolution: {integrity: sha512-pCVY2Wn6dV/labNvssk9b3Owi4WOYsapcbWm90XkIj4xH/56Z6gzja9fsU+4MdPuEfC2Smw835nZHcdCFGyX6A==} + '@swc/core@1.11.18': + resolution: {integrity: sha512-ORZxyCKKiqYt2iHdh1C7pfVR1GBjkuFOdwqZggQzaq0vt22DpGca+2JsUtkUoWQmWcct04v5+ScwgvsHuMObxA==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '*' @@ -4356,8 +4224,8 @@ packages: peerDependencies: '@swc/core': '*' - '@swc/types@0.1.19': - resolution: {integrity: sha512-WkAZaAfj44kh/UFdAQcrMP1I0nwRqpt27u+08LMBYMqmQfwwMofYoMh/48NGkMMRfC4ynpfwRbJuu8ErfNloeA==} + '@swc/types@0.1.21': + resolution: {integrity: sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ==} '@swc/wasm@1.2.130': resolution: {integrity: sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==} @@ -4492,8 +4360,8 @@ packages: '@types/eslint@7.29.0': resolution: {integrity: sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==} - '@types/estree@1.0.6': - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} '@types/express-serve-static-core@4.17.33': resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==} @@ -4592,12 +4460,12 @@ packages: '@types/node@22.13.10': resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==} - '@types/node@22.13.11': - resolution: {integrity: sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g==} - '@types/node@22.13.15': resolution: {integrity: sha512-imAbQEEbVni6i6h6Bd5xkCRwLqFc8hihCsi2GbtDoAtUcAFQ6Zs4pFXTZUUbroTkXdImczWM9AI8eZUuybXE3w==} + '@types/node@22.14.0': + resolution: {integrity: sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==} + '@types/nodemailer@6.4.17': resolution: {integrity: sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==} @@ -4664,14 +4532,17 @@ packages: '@types/readdir-glob@1.1.1': resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==} + '@types/redis-info@3.0.3': + resolution: {integrity: sha512-VIkNy6JbYI/RLdbPHdm9JQvv6RVld2uE2/6Hdid38Qdq+zvDli2FTpImI8pC5zwp8xS8qVqfzlfyAub8xZEd5g==} + '@types/rename@1.0.7': resolution: {integrity: sha512-E9qapfghUGfBMi3jNhsmCKPIp3f2zvNKpaX1BDGLGJNjzpgsZ/RTx7NaNksFjGoJ+r9NvWF1NSM5vVecnNjVmw==} '@types/resolve@1.20.3': resolution: {integrity: sha512-NH5oErHOtHZYcjCtg69t26aXEk4BN2zLWqf7wnDZ+dpe0iR7Rds1SPGEItl3fca21oOe0n3OCnZ4W7jBxu7FOw==} - '@types/sanitize-html@2.13.0': - resolution: {integrity: sha512-X31WxbvW9TjIhZZNyNBZ/p5ax4ti7qsNDBDEnH4zAgmEh35YnFD1UiS6z9Cd34kKm0LslFW0KPmTQzu/oGtsqQ==} + '@types/sanitize-html@2.15.0': + resolution: {integrity: sha512-71Z6PbYsVKfp4i6Jvr37s5ql6if1Q/iJQT80NbaSi7uGaG8CqBMXP0pk/EsURAOuGdk5IJCd/vnzKrR7S3Txsw==} '@types/scheduler@0.23.0': resolution: {integrity: sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==} @@ -4679,8 +4550,8 @@ packages: '@types/seedrandom@3.0.8': resolution: {integrity: sha512-TY1eezMU2zH2ozQoAFAQFOPpvP15g+ZgSfTZt31AUUH/Rxtnz3H+A/Sv1Snw2/amp//omibc+AEkTaA8KUeOLQ==} - '@types/semver@7.5.8': - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + '@types/semver@7.7.0': + resolution: {integrity: sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==} '@types/serve-static@1.15.1': resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==} @@ -4745,6 +4616,9 @@ packages: '@types/ws@8.18.0': resolution: {integrity: sha512-8svvI3hMyvN0kKCJMvTJP/x6Y/EoQbepff882wL+Sn5QsXb3etnamgrJq4isrBxSJj5L2AuXcI0+bgkoAXGUJw==} + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@types/yargs-parser@21.0.0': resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} @@ -4770,6 +4644,14 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/eslint-plugin@8.29.1': + resolution: {integrity: sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/parser@8.27.0': resolution: {integrity: sha512-XGwIabPallYipmcOk45DpsBSgLC64A0yvdAkrwEzwZ2viqGqRUJ8eEYoPz0CWnutgAFbNMPdsGGvzjSmcWVlEA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4784,6 +4666,13 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/parser@8.29.1': + resolution: {integrity: sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/scope-manager@8.27.0': resolution: {integrity: sha512-8oI9GwPMQmBryaaxG1tOZdxXVeMDte6NyJA4i7/TWa4fBwgnAXYlIQP+uYOeqAaLJ2JRxlG9CAyL+C+YE9Xknw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4792,6 +4681,10 @@ packages: resolution: {integrity: sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.29.1': + resolution: {integrity: sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/type-utils@8.27.0': resolution: {integrity: sha512-wVArTVcz1oJOIEJxui/nRhV0TXzD/zMSOYi/ggCfNq78EIszddXcJb7r4RCp/oBrjt8n9A0BSxRMKxHftpDxDA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4806,6 +4699,13 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/type-utils@8.29.1': + resolution: {integrity: sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/types@8.27.0': resolution: {integrity: sha512-/6cp9yL72yUHAYq9g6DsAU+vVfvQmd1a8KyA81uvfDE21O2DwQ/qxlM4AR8TSdAu+kJLBDrEHKC5/W2/nxsY0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4814,6 +4714,10 @@ packages: resolution: {integrity: sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.29.1': + resolution: {integrity: sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.27.0': resolution: {integrity: sha512-BnKq8cqPVoMw71O38a1tEb6iebEgGA80icSxW7g+kndx0o6ot6696HjG7NdgfuAVmVEtwXUr3L8R9ZuVjoQL6A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4826,6 +4730,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/typescript-estree@8.29.1': + resolution: {integrity: sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/utils@8.27.0': resolution: {integrity: sha512-njkodcwH1yvmo31YWgRHNb/x1Xhhq4/m81PhtvmRngD8iHPehxffz1SNCO+kwaePhATC+kOa/ggmvPoPza5i0Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4840,6 +4750,13 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/utils@8.29.1': + resolution: {integrity: sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/visitor-keys@8.27.0': resolution: {integrity: sha512-WsXQwMkILJvffP6z4U3FYJPlbf/j07HIxmDjZpbNvBJkMfvwXj5ACRkkHwBDvLBbDbtX5TdU64/rcvKJ/vuInQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4848,6 +4765,10 @@ packages: resolution: {integrity: sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.29.1': + resolution: {integrity: sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} @@ -4858,11 +4779,11 @@ packages: vite: ^5.0.0 || ^6.0.0 vue: ^3.2.25 - '@vitest/coverage-v8@3.0.9': - resolution: {integrity: sha512-15OACZcBtQ34keIEn19JYTVuMFTlFrClclwWjHo/IRPg/8ELpkgNTl0o7WLP9WO9XGH6+tip9CPYtEOrIDJvBA==} + '@vitest/coverage-v8@3.1.1': + resolution: {integrity: sha512-MgV6D2dhpD6Hp/uroUoAIvFqA8AuvXEFBC2eepG3WFc1pxTfdk1LEqqkWoWhjz+rytoqrnUUCdf6Lzco3iHkLQ==} peerDependencies: - '@vitest/browser': 3.0.9 - vitest: 3.0.9 + '@vitest/browser': 3.1.1 + vitest: 3.1.1 peerDependenciesMeta: '@vitest/browser': optional: true @@ -4870,11 +4791,11 @@ packages: '@vitest/expect@2.0.5': resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} - '@vitest/expect@3.0.9': - resolution: {integrity: sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==} + '@vitest/expect@3.1.1': + resolution: {integrity: sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==} - '@vitest/mocker@3.0.9': - resolution: {integrity: sha512-ryERPIBOnvevAkTq+L1lD+DTFBRcjueL9lOUfXsLfwP92h4e+Heb+PjiqS3/OURWPtywfafK0kj++yDFjWUmrA==} + '@vitest/mocker@3.1.1': + resolution: {integrity: sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==} peerDependencies: msw: ^2.4.9 vite: ^5.0.0 || ^6.0.0 @@ -4890,20 +4811,23 @@ packages: '@vitest/pretty-format@2.1.2': resolution: {integrity: sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==} - '@vitest/pretty-format@3.0.9': - resolution: {integrity: sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==} + '@vitest/pretty-format@3.1.1': + resolution: {integrity: sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==} + + '@vitest/pretty-format@3.1.2': + resolution: {integrity: sha512-R0xAiHuWeDjTSB3kQ3OQpT8Rx3yhdOAIm/JM4axXxnG7Q/fS8XUwggv/A4xzbQA+drYRjzkMnpYnOGAc4oeq8w==} - '@vitest/runner@3.0.9': - resolution: {integrity: sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw==} + '@vitest/runner@3.1.1': + resolution: {integrity: sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==} - '@vitest/snapshot@3.0.9': - resolution: {integrity: sha512-AiLUiuZ0FuA+/8i19mTYd+re5jqjEc2jZbgJ2up0VY0Ddyyxg/uUtBDpIFAy4uzKaQxOW8gMgBdAJJ2ydhu39A==} + '@vitest/snapshot@3.1.1': + resolution: {integrity: sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==} '@vitest/spy@2.0.5': resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==} - '@vitest/spy@3.0.9': - resolution: {integrity: sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==} + '@vitest/spy@3.1.1': + resolution: {integrity: sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==} '@vitest/utils@2.0.5': resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==} @@ -4911,8 +4835,8 @@ packages: '@vitest/utils@2.1.2': resolution: {integrity: sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==} - '@vitest/utils@3.0.9': - resolution: {integrity: sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==} + '@vitest/utils@3.1.1': + resolution: {integrity: sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==} '@volar/language-core@2.2.0': resolution: {integrity: sha512-a8WG9+4OdeNDW4ywABZIM6S6UN7em8uIlM/BZ2pWQUYrVmX+m8sj/X+QadvO+Li/t/LjAqbWJQtVgxdpEWLALQ==} @@ -5428,8 +5352,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - broadcast-channel@7.0.0: - resolution: {integrity: sha512-a2tW0Ia1pajcPBOGUF2jXlDnvE9d5/dg6BG9h60OmRUcZVr/veUrU8vEQFwwQIhwG3KVzYwSk3v2nRRGFgQDXQ==} + broadcast-channel@7.1.0: + resolution: {integrity: sha512-InJljddsYWbEL8LBnopnCg+qMQp9KcowvYWOt4YWrjD5HmxzDYKdVbDS1w/ji5rFZdRD58V5UxJPtBdpEbEJYw==} browser-assert@1.2.1: resolution: {integrity: sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==} @@ -5477,8 +5401,8 @@ packages: resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} engines: {node: '>=6.14.2'} - bullmq@5.44.1: - resolution: {integrity: sha512-NEWQIixKw5QdXmSnhQU25A336N10DvxuDcmiVXQyuwSx1lrzfNIdQA3BNADfpAUwHtuJkJImHVLCM3/KEr9nWA==} + bullmq@5.48.1: + resolution: {integrity: sha512-WA/NlPwmxgbDsL8KGIkQvGlRkBdVAyRiypP+Witm6Tyd2PwnBQ8K62ifnlA16rF59vpnEKwO+rmh/ZM7gKmDqg==} buraha@0.0.1: resolution: {integrity: sha512-G563A0mTbzknm2jDaNxfZuNKIdeArs8T+XQN6t+KbmgnOoevXSXhKDkyf8Md/36Jrx99ikwbCag37VGe3myExQ==} @@ -5574,10 +5498,6 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chai@5.1.1: - resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} - engines: {node: '>=12'} - chai@5.2.0: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} @@ -5673,8 +5593,8 @@ packages: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} - chromatic@11.27.0: - resolution: {integrity: sha512-jQ2ufjS+ePpg+NtcPI9B2eOi+pAzlRd2nhd1LgNMsVCC9Bzf5t8mJtyd8v2AUuJS0LdX0QVBgkOnlNv9xviHzA==} + chromatic@11.28.0: + resolution: {integrity: sha512-Xy3907MXY5UP7LoMksmsT02xCUsoLZpcC6sRITjd+KiXBteOxPF7J+QsFqy/VzQCEN6fpt/R2bOIpE+PwPfcZA==} hasBin: true peerDependencies: '@chromatic-com/cypress': ^0.*.* || ^1.0.0 @@ -6014,6 +5934,9 @@ packages: dayjs@1.11.10: resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + de-indent@1.0.2: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} @@ -6252,8 +6175,8 @@ packages: domutils@3.1.0: resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} - dotenv@16.0.3: - resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} + dotenv@16.5.0: + resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} engines: {node: '>=12'} dunder-proto@1.0.1: @@ -6395,11 +6318,6 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.25.1: - resolution: {integrity: sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==} - engines: {node: '>=18'} - hasBin: true - esbuild@0.25.2: resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==} engines: {node: '>=18'} @@ -6493,10 +6411,6 @@ packages: eslint-rule-docs@1.1.235: resolution: {integrity: sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A==} - eslint-scope@8.2.0: - resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint-scope@8.3.0: resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6717,8 +6631,8 @@ packages: resolution: {integrity: sha512-2qfoaQ3BQDhZ1gtbkKZd6n0kKxJISJGM6u/skD9ljdWItAscjXrtZ1lnjr7PavmXX9j4EyCPmBDiIsLn07d5vA==} engines: {node: '>= 10'} - fastify@5.2.1: - resolution: {integrity: sha512-rslrNBF67eg8/Gyn7P2URV8/6pz8kSAscFL4EThZJ8JBMaXacVdVE4hmUcnPNKERl5o/xTiBSLfdowBRhVF1WA==} + fastify@5.3.2: + resolution: {integrity: sha512-AIPqBgtqBAwkOkrnwesEE+dOyU30dQ4kh7udxeGVR05CRGwubZx+p2H8P0C4cRnQT0+EPK4VGea2DTL2RtWttg==} fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -6729,6 +6643,14 @@ packages: fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fdir@6.4.4: + resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + feed@4.2.2: resolution: {integrity: sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==} engines: {node: '>=0.4.0'} @@ -7036,8 +6958,8 @@ packages: resolution: {integrity: sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==} engines: {node: '>=16'} - got@14.4.6: - resolution: {integrity: sha512-rnhwfM/PhMNJ1i17k3DuDqgj0cKx3IHxBKVv/WX1uDKqrhi2Gv3l7rhPThR/Cc6uU++dD97W9c8Y0qyw9x0jag==} + got@14.4.7: + resolution: {integrity: sha512-DI8zV1231tqiGzOiOzQWDhsBmncFW7oQDH6Zgy6pDPrqJuVZMtoSgPLLsBZQj8Jg4JFfwoOsDA8NGtLQLnIx2g==} engines: {node: '>=20'} graceful-fs@4.2.11: @@ -7595,10 +7517,6 @@ packages: resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} engines: {node: '>=10'} - istanbul-reports@3.1.6: - resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==} - engines: {node: '>=8'} - istanbul-reports@3.1.7: resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} engines: {node: '>=8'} @@ -7998,9 +7916,6 @@ packages: longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - loupe@3.1.2: - resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==} - loupe@3.1.3: resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} @@ -8739,8 +8654,8 @@ packages: ospath@1.2.2: resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==} - otpauth@9.3.6: - resolution: {integrity: sha512-eIcCvuEvcAAPHxUKC9Q4uCe0Fh/yRc5jv9z+f/kvyIF2LPrhgAOuLB7J9CssGYhND/BL8M9hlHBTFmffpoQlMQ==} + otpauth@9.4.0: + resolution: {integrity: sha512-fHIfzIG5RqCkK9cmV8WU+dPQr9/ebR5QOwGZn2JAr1RQF+lmAuLL2YdtdqvmBjNmgJlYk3KZ4a0XokaEhg1Jsw==} outvariant@1.4.3: resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} @@ -9295,6 +9210,9 @@ packages: process-warning@4.0.0: resolution: {integrity: sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==} + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} @@ -9478,10 +9396,10 @@ packages: resolution: {integrity: sha512-rCz0HBIT0LWbIM+///LfRrJoTKftIzzwsYDf0ns5KwaEjejMHQRtphcns+IXFHDNY9pnz6G8l/JbbI6pD4EAIA==} engines: {node: '>=16.14.0'} - react-dom@19.0.0: - resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} + react-dom@19.1.0: + resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} peerDependencies: - react: ^19.0.0 + react: ^19.1.0 react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} @@ -9489,8 +9407,8 @@ packages: react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} - react@19.0.0: - resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} + react@19.1.0: + resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} engines: {node: '>=0.10.0'} read-pkg-up@7.0.1: @@ -9538,6 +9456,9 @@ packages: resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} engines: {node: '>=4'} + redis-info@3.1.0: + resolution: {integrity: sha512-ER4L9Sh/vm63DkIE0bkSjxluQlioBiBgf5w1UuldaW/3vPcecdljVDisZhmnCMvsxHNiARTTDDHGg9cGwTfrKg==} + redis-lock@0.1.4: resolution: {integrity: sha512-7/+zu86XVQfJVx1nHTzux5reglDiyUCDwmW7TSlvVezfhH2YLc/Rc8NE0ejQG+8/0lwKzm29/u/4+ogKeLosiA==} engines: {node: '>=0.6'} @@ -9655,8 +9576,8 @@ packages: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true - rollup@4.36.0: - resolution: {integrity: sha512-zwATAXNQxUcd40zgtQG0ZafcRK4g004WtEl7kbuhTWPvf07PsfohXl39jVUvPF7jvNAIkKPQ2XrsDlWuxBd++Q==} + rollup@4.39.0: + resolution: {integrity: sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -9699,8 +9620,8 @@ packages: sanitize-html@2.15.0: resolution: {integrity: sha512-wIjst57vJGpLyBP8ioUbg6ThwJie5SuSIjHxJg53v5Fg+kUK+AXlb7bK3RNXpp315MvwM+0OBGCV6h5pPHsVhA==} - sass@1.86.0: - resolution: {integrity: sha512-zV8vGUld/+mP4KbMLJMX7TyGCuUp7hnkOScgCMsWuHtns8CWBoz+vmEhoGMXsaJrbUP8gj+F1dLvVe79sK8UdA==} + sass@1.86.3: + resolution: {integrity: sha512-iGtg8kus4GrsGLRDLRBRHY9dNVA78ZaS7xr01cWnS7PEMQyFtTqBiyCrfpTYTZXRWM94akzckYjh8oADfFNTzw==} engines: {node: '>=14.0.0'} hasBin: true @@ -9711,8 +9632,8 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} - scheduler@0.25.0: - resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} secure-json-parse@2.7.0: resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} @@ -9720,6 +9641,9 @@ packages: secure-json-parse@3.0.2: resolution: {integrity: sha512-H6nS2o8bWfpFEV6U38sOSjS7bTbdgbCGU9wEM6W14P5H0QOsz94KCusifV44GpHDTu2nqZbuDNhTzu+mjDSw1w==} + secure-json-parse@4.0.0: + resolution: {integrity: sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==} + seedrandom@3.0.5: resolution: {integrity: sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==} @@ -9795,8 +9719,8 @@ packages: resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} hasBin: true - sharp@0.33.5: - resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} + sharp@0.34.1: + resolution: {integrity: sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@2.0.0: @@ -9810,8 +9734,8 @@ packages: shiki@0.14.7: resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==} - shiki@3.2.1: - resolution: {integrity: sha512-VML/2o1/KGYkEf/stJJ+s9Ypn7jUKQPomGLGYso4JJFMFxVDyPNsjsI3MB3KLjlMOeH44gyaPdXC6rik2WXvUQ==} + shiki@3.2.2: + resolution: {integrity: sha512-0qWBkM2t/0NXPRcVgtLhtHv6Ak3Q5yI4K/ggMqcgLRKm4+pCs3namgZlhlat/7u2CuqNtlShNs9lENOG6n7UaQ==} shimmer@1.2.1: resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} @@ -10091,8 +10015,8 @@ packages: react-dom: optional: true - storybook@8.6.7: - resolution: {integrity: sha512-9gktoFMQDSCINNGQH869d/sar9rVtAhr0HchcvDA6bssAqgQJvTphY4qC9lH54SxfTJm/7Sy+BKEngMK+dziJg==} + storybook@8.6.12: + resolution: {integrity: sha512-Z/nWYEHBTLK1ZBtAWdhxC0l5zf7ioJ7G4+zYqtTdYeb67gTnxNj80gehf8o8QY9L2zA2+eyMRGLC2V5fI7Z3Tw==} hasBin: true peerDependencies: prettier: ^2 || ^3 @@ -10313,8 +10237,8 @@ packages: thread-stream@3.1.0: resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} - three@0.174.0: - resolution: {integrity: sha512-p+WG3W6Ov74alh3geCMkGK9NWuT62ee21cV3jEnun201zodVF4tCE5aZa2U122/mkLRmhJJUQmLLW1BH00uQJQ==} + three@0.175.0: + resolution: {integrity: sha512-nNE3pnTHxXN/Phw768u0Grr7W4+rumGg/H6PgeseNJojkJtmeHJfZWi41Gp2mpXl1pg1pf1zjwR4McM1jTqkpg==} throttle-debounce@5.0.2: resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} @@ -10338,6 +10262,10 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyglobby@0.2.13: + resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + engines: {node: '>=12.0.0'} + tinypool@1.0.2: resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -10464,8 +10392,9 @@ packages: ts-map@1.0.3: resolution: {integrity: sha512-vDWbsl26LIcPGmDpoVzjEP6+hvHZkBkLW7JpvwbCv/5IYPJlsbzCVXY3wsCeAxAUeTclNOUZxnLdGh3VBD/J6w==} - tsc-alias@1.8.11: - resolution: {integrity: sha512-2DuEQ58A9Rj2NE2c1+/qaGKlshni9MCK95MJzRGhQG0CYLw0bE/ACgbhhTSf/p1svLelwqafOd8stQate2bYbg==} + tsc-alias@1.8.15: + resolution: {integrity: sha512-yKLVx8ddUurRwhVcS6JFF2ZjksOX2ZWDRIdgt+PQhJBDegIdAdilptiHsuAbx9UFxa16GFrxeKQ2kTcGvR6fkQ==} + engines: {node: '>=16.20.2'} hasBin: true tsconfig-paths@3.15.0: @@ -10562,17 +10491,17 @@ packages: peerDependencies: typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x - typeorm@0.3.21: - resolution: {integrity: sha512-lh4rUWl1liZGjyPTWpwcK8RNI5x4ekln+/JJOox1wCd7xbucYDOXWD+1cSzTN3L0wbTGxxOtloM5JlxbOxEufA==} + typeorm@0.3.22: + resolution: {integrity: sha512-P/Tsz3UpJ9+K0oryC0twK5PO27zejLYYwMsE8SISfZc1lVHX+ajigiOyWsKbuXpEFMjD9z7UjLzY3+ElVOMMDA==} engines: {node: '>=16.13.0'} hasBin: true peerDependencies: - '@google-cloud/spanner': ^5.18.0 + '@google-cloud/spanner': ^5.18.0 || ^6.0.0 || ^7.0.0 '@sap/hana-client': ^2.12.25 better-sqlite3: ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 hdb-pool: ^0.1.6 ioredis: ^5.0.4 - mongodb: ^5.8.0 + mongodb: ^5.8.0 || ^6.0.0 mssql: ^9.1.1 || ^10.0.1 || ^11.0.1 mysql2: ^2.2.5 || ^3.0.1 oracledb: ^6.3.0 @@ -10584,7 +10513,7 @@ packages: sql.js: ^1.4.0 sqlite3: ^5.0.3 ts-node: ^10.7.0 - typeorm-aurora-data-api-driver: ^2.0.0 + typeorm-aurora-data-api-driver: ^2.0.0 || ^3.0.0 peerDependenciesMeta: '@google-cloud/spanner': optional: true @@ -10631,6 +10560,11 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + uid2@0.0.4: resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==} @@ -10658,6 +10592,9 @@ packages: undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici@5.28.2: resolution: {integrity: sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w==} engines: {node: '>=14.0'} @@ -10804,16 +10741,16 @@ packages: vfile@6.0.1: resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==} - vite-node@3.0.9: - resolution: {integrity: sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==} + vite-node@3.1.1: + resolution: {integrity: sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true vite-plugin-turbosnap@1.0.3: resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==} - vite@6.2.4: - resolution: {integrity: sha512-veHMSew8CcRzhL5o8ONjy8gkfmFJAd5Ac16oxBUjlwgX3Gq2Wqr+qNC3TjPIpy7TPV/KporLga5GT9HqdrCizw==} + vite@6.3.1: + resolution: {integrity: sha512-kkzzkqtMESYklo96HKKPE5KKLkC1amlsqt+RjFMlX2AvbRB/0wghap19NdBxxwGZ+h/C6DLCrcEphPIItlGrRQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -10858,16 +10795,16 @@ packages: peerDependencies: vitest: '>=2.0.0' - vitest@3.0.9: - resolution: {integrity: sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ==} + vitest@3.1.1: + resolution: {integrity: sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/debug': ^4.1.12 '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.0.9 - '@vitest/ui': 3.0.9 + '@vitest/browser': 3.1.1 + '@vitest/ui': 3.1.1 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -10934,6 +10871,9 @@ packages: vue-component-type-helpers@2.0.16: resolution: {integrity: sha512-qisL/iAfdO++7w+SsfYQJVPj6QKvxp4i1MMxvsNO41z/8zu3KuAw9LkhKUfP/kcOWGDxESp+pQObWppXusejCA==} + vue-component-type-helpers@2.2.10: + resolution: {integrity: sha512-iDUO7uQK+Sab2tYuiP9D1oLujCWlhHELHMgV/cB13cuGbG4qwkLHvtfWb6FzvxrIOPDnU0oHsz2MlQjhYDeaHA==} + vue-component-type-helpers@2.2.8: resolution: {integrity: sha512-4bjIsC284coDO9om4HPA62M7wfsTvcmZyzdfR0aUlFXqq4tXxM1APyXpNVxPC8QazKw9OhmZNHBVDA6ODaZsrA==} @@ -10953,8 +10893,8 @@ packages: peerDependencies: vue: '>=2' - vue-eslint-parser@10.1.1: - resolution: {integrity: sha512-bh2Z/Au5slro9QJ3neFYLanZtb1jH+W2bKqGHXAoYD4vZgNG3KeotL7JpPv5xzY4UXUXJl7TrIsnzECH63kd3Q==} + vue-eslint-parser@10.1.3: + resolution: {integrity: sha512-dbCBnd2e02dYWsXoqX5yKUZlOt+ExIpq7hmHKPb5ZqKcjf++Eo0hMseFTZMLKThrUk61m+Uv6A2YSBve6ZvuDQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -11276,74 +11216,74 @@ snapshots: '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.734.0 - tslib: 2.7.0 + '@aws-sdk/types': 3.775.0 + tslib: 2.8.1 '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.734.0 - tslib: 2.7.0 + '@aws-sdk/types': 3.775.0 + tslib: 2.8.1 '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.734.0 + '@aws-sdk/types': 3.775.0 '@aws-sdk/util-locate-window': 3.208.0 '@smithy/util-utf8': 2.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@aws-crypto/sha256-browser@5.2.0': dependencies: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.734.0 + '@aws-sdk/types': 3.775.0 '@aws-sdk/util-locate-window': 3.208.0 '@smithy/util-utf8': 2.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.734.0 - tslib: 2.7.0 + '@aws-sdk/types': 3.775.0 + tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.734.0 + '@aws-sdk/types': 3.775.0 '@smithy/util-utf8': 2.0.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/client-s3@3.772.0': + '@aws-sdk/client-s3@3.782.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.758.0 - '@aws-sdk/credential-provider-node': 3.772.0 - '@aws-sdk/middleware-bucket-endpoint': 3.734.0 - '@aws-sdk/middleware-expect-continue': 3.734.0 - '@aws-sdk/middleware-flexible-checksums': 3.758.0 - '@aws-sdk/middleware-host-header': 3.734.0 - '@aws-sdk/middleware-location-constraint': 3.734.0 - '@aws-sdk/middleware-logger': 3.734.0 - '@aws-sdk/middleware-recursion-detection': 3.772.0 - '@aws-sdk/middleware-sdk-s3': 3.758.0 - '@aws-sdk/middleware-ssec': 3.734.0 - '@aws-sdk/middleware-user-agent': 3.758.0 - '@aws-sdk/region-config-resolver': 3.734.0 - '@aws-sdk/signature-v4-multi-region': 3.758.0 - '@aws-sdk/types': 3.734.0 - '@aws-sdk/util-endpoints': 3.743.0 - '@aws-sdk/util-user-agent-browser': 3.734.0 - '@aws-sdk/util-user-agent-node': 3.758.0 - '@aws-sdk/xml-builder': 3.734.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/credential-provider-node': 3.782.0 + '@aws-sdk/middleware-bucket-endpoint': 3.775.0 + '@aws-sdk/middleware-expect-continue': 3.775.0 + '@aws-sdk/middleware-flexible-checksums': 3.775.0 + '@aws-sdk/middleware-host-header': 3.775.0 + '@aws-sdk/middleware-location-constraint': 3.775.0 + '@aws-sdk/middleware-logger': 3.775.0 + '@aws-sdk/middleware-recursion-detection': 3.775.0 + '@aws-sdk/middleware-sdk-s3': 3.775.0 + '@aws-sdk/middleware-ssec': 3.775.0 + '@aws-sdk/middleware-user-agent': 3.782.0 + '@aws-sdk/region-config-resolver': 3.775.0 + '@aws-sdk/signature-v4-multi-region': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-endpoints': 3.782.0 + '@aws-sdk/util-user-agent-browser': 3.775.0 + '@aws-sdk/util-user-agent-node': 3.782.0 + '@aws-sdk/xml-builder': 3.775.0 '@smithy/config-resolver': 4.1.0 '@smithy/core': 3.2.0 '@smithy/eventstream-serde-browser': 4.0.2 @@ -11377,24 +11317,24 @@ snapshots: '@smithy/util-stream': 4.2.0 '@smithy/util-utf8': 4.0.0 '@smithy/util-waiter': 4.0.3 - tslib: 2.7.0 + tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.772.0': + '@aws-sdk/client-sso@3.782.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.758.0 - '@aws-sdk/middleware-host-header': 3.734.0 - '@aws-sdk/middleware-logger': 3.734.0 - '@aws-sdk/middleware-recursion-detection': 3.772.0 - '@aws-sdk/middleware-user-agent': 3.758.0 - '@aws-sdk/region-config-resolver': 3.734.0 - '@aws-sdk/types': 3.734.0 - '@aws-sdk/util-endpoints': 3.743.0 - '@aws-sdk/util-user-agent-browser': 3.734.0 - '@aws-sdk/util-user-agent-node': 3.758.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/middleware-host-header': 3.775.0 + '@aws-sdk/middleware-logger': 3.775.0 + '@aws-sdk/middleware-recursion-detection': 3.775.0 + '@aws-sdk/middleware-user-agent': 3.782.0 + '@aws-sdk/region-config-resolver': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-endpoints': 3.782.0 + '@aws-sdk/util-user-agent-browser': 3.775.0 + '@aws-sdk/util-user-agent-node': 3.782.0 '@smithy/config-resolver': 4.1.0 '@smithy/core': 3.2.0 '@smithy/fetch-http-handler': 5.0.2 @@ -11420,13 +11360,13 @@ snapshots: '@smithy/util-middleware': 4.0.2 '@smithy/util-retry': 4.0.2 '@smithy/util-utf8': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.758.0': + '@aws-sdk/core@3.775.0': dependencies: - '@aws-sdk/types': 3.734.0 + '@aws-sdk/types': 3.775.0 '@smithy/core': 3.2.0 '@smithy/node-config-provider': 4.0.2 '@smithy/property-provider': 4.0.2 @@ -11436,20 +11376,20 @@ snapshots: '@smithy/types': 4.2.0 '@smithy/util-middleware': 4.0.2 fast-xml-parser: 4.4.1 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.758.0': + '@aws-sdk/credential-provider-env@3.775.0': dependencies: - '@aws-sdk/core': 3.758.0 - '@aws-sdk/types': 3.734.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 '@smithy/property-provider': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.758.0': + '@aws-sdk/credential-provider-http@3.775.0': dependencies: - '@aws-sdk/core': 3.758.0 - '@aws-sdk/types': 3.734.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 '@smithy/fetch-http-handler': 5.0.2 '@smithy/node-http-handler': 4.0.4 '@smithy/property-provider': 4.0.2 @@ -11457,111 +11397,111 @@ snapshots: '@smithy/smithy-client': 4.2.0 '@smithy/types': 4.2.0 '@smithy/util-stream': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.772.0': + '@aws-sdk/credential-provider-ini@3.782.0': dependencies: - '@aws-sdk/core': 3.758.0 - '@aws-sdk/credential-provider-env': 3.758.0 - '@aws-sdk/credential-provider-http': 3.758.0 - '@aws-sdk/credential-provider-process': 3.758.0 - '@aws-sdk/credential-provider-sso': 3.772.0 - '@aws-sdk/credential-provider-web-identity': 3.772.0 - '@aws-sdk/nested-clients': 3.772.0 - '@aws-sdk/types': 3.734.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/credential-provider-env': 3.775.0 + '@aws-sdk/credential-provider-http': 3.775.0 + '@aws-sdk/credential-provider-process': 3.775.0 + '@aws-sdk/credential-provider-sso': 3.782.0 + '@aws-sdk/credential-provider-web-identity': 3.782.0 + '@aws-sdk/nested-clients': 3.782.0 + '@aws-sdk/types': 3.775.0 '@smithy/credential-provider-imds': 4.0.2 '@smithy/property-provider': 4.0.2 '@smithy/shared-ini-file-loader': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.772.0': + '@aws-sdk/credential-provider-node@3.782.0': dependencies: - '@aws-sdk/credential-provider-env': 3.758.0 - '@aws-sdk/credential-provider-http': 3.758.0 - '@aws-sdk/credential-provider-ini': 3.772.0 - '@aws-sdk/credential-provider-process': 3.758.0 - '@aws-sdk/credential-provider-sso': 3.772.0 - '@aws-sdk/credential-provider-web-identity': 3.772.0 - '@aws-sdk/types': 3.734.0 + '@aws-sdk/credential-provider-env': 3.775.0 + '@aws-sdk/credential-provider-http': 3.775.0 + '@aws-sdk/credential-provider-ini': 3.782.0 + '@aws-sdk/credential-provider-process': 3.775.0 + '@aws-sdk/credential-provider-sso': 3.782.0 + '@aws-sdk/credential-provider-web-identity': 3.782.0 + '@aws-sdk/types': 3.775.0 '@smithy/credential-provider-imds': 4.0.2 '@smithy/property-provider': 4.0.2 '@smithy/shared-ini-file-loader': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.758.0': + '@aws-sdk/credential-provider-process@3.775.0': dependencies: - '@aws-sdk/core': 3.758.0 - '@aws-sdk/types': 3.734.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 '@smithy/property-provider': 4.0.2 '@smithy/shared-ini-file-loader': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.772.0': + '@aws-sdk/credential-provider-sso@3.782.0': dependencies: - '@aws-sdk/client-sso': 3.772.0 - '@aws-sdk/core': 3.758.0 - '@aws-sdk/token-providers': 3.772.0 - '@aws-sdk/types': 3.734.0 + '@aws-sdk/client-sso': 3.782.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/token-providers': 3.782.0 + '@aws-sdk/types': 3.775.0 '@smithy/property-provider': 4.0.2 '@smithy/shared-ini-file-loader': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.772.0': + '@aws-sdk/credential-provider-web-identity@3.782.0': dependencies: - '@aws-sdk/core': 3.758.0 - '@aws-sdk/nested-clients': 3.772.0 - '@aws-sdk/types': 3.734.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/nested-clients': 3.782.0 + '@aws-sdk/types': 3.775.0 '@smithy/property-provider': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/lib-storage@3.772.0(@aws-sdk/client-s3@3.772.0)': + '@aws-sdk/lib-storage@3.782.0(@aws-sdk/client-s3@3.782.0)': dependencies: - '@aws-sdk/client-s3': 3.772.0 + '@aws-sdk/client-s3': 3.782.0 '@smithy/abort-controller': 4.0.2 '@smithy/middleware-endpoint': 4.1.0 '@smithy/smithy-client': 4.2.0 buffer: 5.6.0 events: 3.3.0 stream-browserify: 3.0.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/middleware-bucket-endpoint@3.734.0': + '@aws-sdk/middleware-bucket-endpoint@3.775.0': dependencies: - '@aws-sdk/types': 3.734.0 + '@aws-sdk/types': 3.775.0 '@aws-sdk/util-arn-parser': 3.723.0 '@smithy/node-config-provider': 4.0.2 '@smithy/protocol-http': 5.1.0 '@smithy/types': 4.2.0 '@smithy/util-config-provider': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/middleware-expect-continue@3.734.0': + '@aws-sdk/middleware-expect-continue@3.775.0': dependencies: - '@aws-sdk/types': 3.734.0 + '@aws-sdk/types': 3.775.0 '@smithy/protocol-http': 5.1.0 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.758.0': + '@aws-sdk/middleware-flexible-checksums@3.775.0': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.758.0 - '@aws-sdk/types': 3.734.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 '@smithy/is-array-buffer': 4.0.0 '@smithy/node-config-provider': 4.0.2 '@smithy/protocol-http': 5.1.0 @@ -11569,38 +11509,38 @@ snapshots: '@smithy/util-middleware': 4.0.2 '@smithy/util-stream': 4.2.0 '@smithy/util-utf8': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.734.0': + '@aws-sdk/middleware-host-header@3.775.0': dependencies: - '@aws-sdk/types': 3.734.0 + '@aws-sdk/types': 3.775.0 '@smithy/protocol-http': 5.1.0 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/middleware-location-constraint@3.734.0': + '@aws-sdk/middleware-location-constraint@3.775.0': dependencies: - '@aws-sdk/types': 3.734.0 + '@aws-sdk/types': 3.775.0 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.734.0': + '@aws-sdk/middleware-logger@3.775.0': dependencies: - '@aws-sdk/types': 3.734.0 + '@aws-sdk/types': 3.775.0 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.772.0': + '@aws-sdk/middleware-recursion-detection@3.775.0': dependencies: - '@aws-sdk/types': 3.734.0 + '@aws-sdk/types': 3.775.0 '@smithy/protocol-http': 5.1.0 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.758.0': + '@aws-sdk/middleware-sdk-s3@3.775.0': dependencies: - '@aws-sdk/core': 3.758.0 - '@aws-sdk/types': 3.734.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 '@aws-sdk/util-arn-parser': 3.723.0 '@smithy/core': 3.2.0 '@smithy/node-config-provider': 4.0.2 @@ -11612,38 +11552,38 @@ snapshots: '@smithy/util-middleware': 4.0.2 '@smithy/util-stream': 4.2.0 '@smithy/util-utf8': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/middleware-ssec@3.734.0': + '@aws-sdk/middleware-ssec@3.775.0': dependencies: - '@aws-sdk/types': 3.734.0 + '@aws-sdk/types': 3.775.0 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.758.0': + '@aws-sdk/middleware-user-agent@3.782.0': dependencies: - '@aws-sdk/core': 3.758.0 - '@aws-sdk/types': 3.734.0 - '@aws-sdk/util-endpoints': 3.743.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-endpoints': 3.782.0 '@smithy/core': 3.2.0 '@smithy/protocol-http': 5.1.0 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/nested-clients@3.772.0': + '@aws-sdk/nested-clients@3.782.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.758.0 - '@aws-sdk/middleware-host-header': 3.734.0 - '@aws-sdk/middleware-logger': 3.734.0 - '@aws-sdk/middleware-recursion-detection': 3.772.0 - '@aws-sdk/middleware-user-agent': 3.758.0 - '@aws-sdk/region-config-resolver': 3.734.0 - '@aws-sdk/types': 3.734.0 - '@aws-sdk/util-endpoints': 3.743.0 - '@aws-sdk/util-user-agent-browser': 3.734.0 - '@aws-sdk/util-user-agent-node': 3.758.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/middleware-host-header': 3.775.0 + '@aws-sdk/middleware-logger': 3.775.0 + '@aws-sdk/middleware-recursion-detection': 3.775.0 + '@aws-sdk/middleware-user-agent': 3.782.0 + '@aws-sdk/region-config-resolver': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-endpoints': 3.782.0 + '@aws-sdk/util-user-agent-browser': 3.775.0 + '@aws-sdk/util-user-agent-node': 3.782.0 '@smithy/config-resolver': 4.1.0 '@smithy/core': 3.2.0 '@smithy/fetch-http-handler': 5.0.2 @@ -11669,78 +11609,78 @@ snapshots: '@smithy/util-middleware': 4.0.2 '@smithy/util-retry': 4.0.2 '@smithy/util-utf8': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/region-config-resolver@3.734.0': + '@aws-sdk/region-config-resolver@3.775.0': dependencies: - '@aws-sdk/types': 3.734.0 + '@aws-sdk/types': 3.775.0 '@smithy/node-config-provider': 4.0.2 '@smithy/types': 4.2.0 '@smithy/util-config-provider': 4.0.0 '@smithy/util-middleware': 4.0.2 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.758.0': + '@aws-sdk/signature-v4-multi-region@3.775.0': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.758.0 - '@aws-sdk/types': 3.734.0 + '@aws-sdk/middleware-sdk-s3': 3.775.0 + '@aws-sdk/types': 3.775.0 '@smithy/protocol-http': 5.1.0 '@smithy/signature-v4': 5.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/token-providers@3.772.0': + '@aws-sdk/token-providers@3.782.0': dependencies: - '@aws-sdk/nested-clients': 3.772.0 - '@aws-sdk/types': 3.734.0 + '@aws-sdk/nested-clients': 3.782.0 + '@aws-sdk/types': 3.775.0 '@smithy/property-provider': 4.0.2 '@smithy/shared-ini-file-loader': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/types@3.734.0': + '@aws-sdk/types@3.775.0': dependencies: '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@aws-sdk/util-arn-parser@3.723.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.743.0': + '@aws-sdk/util-endpoints@3.782.0': dependencies: - '@aws-sdk/types': 3.734.0 + '@aws-sdk/types': 3.775.0 '@smithy/types': 4.2.0 '@smithy/util-endpoints': 3.0.2 - tslib: 2.7.0 + tslib: 2.8.1 '@aws-sdk/util-locate-window@3.208.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.734.0': + '@aws-sdk/util-user-agent-browser@3.775.0': dependencies: - '@aws-sdk/types': 3.734.0 + '@aws-sdk/types': 3.775.0 '@smithy/types': 4.2.0 bowser: 2.11.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.758.0': + '@aws-sdk/util-user-agent-node@3.782.0': dependencies: - '@aws-sdk/middleware-user-agent': 3.758.0 - '@aws-sdk/types': 3.734.0 + '@aws-sdk/middleware-user-agent': 3.782.0 + '@aws-sdk/types': 3.775.0 '@smithy/node-config-provider': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 - '@aws-sdk/xml-builder@3.734.0': + '@aws-sdk/xml-builder@3.775.0': dependencies: '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@babel/code-frame@7.23.5': dependencies: @@ -11925,26 +11865,56 @@ snapshots: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 @@ -11955,36 +11925,78 @@ snapshots: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.22.5 + optional: true + '@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 @@ -11994,6 +12006,10 @@ snapshots: dependencies: regenerator-runtime: 0.14.0 + '@babel/runtime@7.27.0': + dependencies: + regenerator-runtime: 0.14.0 + '@babel/template@7.22.15': dependencies: '@babel/code-frame': 7.24.7 @@ -12194,233 +12210,158 @@ snapshots: jsonfile: 5.0.0 universalify: 0.1.2 - '@emnapi/runtime@1.3.0': + '@emnapi/runtime@1.4.3': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 optional: true '@esbuild/aix-ppc64@0.25.0': optional: true - '@esbuild/aix-ppc64@0.25.1': - optional: true - '@esbuild/aix-ppc64@0.25.2': optional: true '@esbuild/android-arm64@0.25.0': optional: true - '@esbuild/android-arm64@0.25.1': - optional: true - '@esbuild/android-arm64@0.25.2': optional: true '@esbuild/android-arm@0.25.0': optional: true - '@esbuild/android-arm@0.25.1': - optional: true - '@esbuild/android-arm@0.25.2': optional: true '@esbuild/android-x64@0.25.0': optional: true - '@esbuild/android-x64@0.25.1': - optional: true - '@esbuild/android-x64@0.25.2': optional: true '@esbuild/darwin-arm64@0.25.0': optional: true - '@esbuild/darwin-arm64@0.25.1': - optional: true - '@esbuild/darwin-arm64@0.25.2': optional: true '@esbuild/darwin-x64@0.25.0': optional: true - '@esbuild/darwin-x64@0.25.1': - optional: true - '@esbuild/darwin-x64@0.25.2': optional: true '@esbuild/freebsd-arm64@0.25.0': optional: true - '@esbuild/freebsd-arm64@0.25.1': - optional: true - '@esbuild/freebsd-arm64@0.25.2': optional: true '@esbuild/freebsd-x64@0.25.0': optional: true - '@esbuild/freebsd-x64@0.25.1': - optional: true - '@esbuild/freebsd-x64@0.25.2': optional: true '@esbuild/linux-arm64@0.25.0': optional: true - '@esbuild/linux-arm64@0.25.1': - optional: true - '@esbuild/linux-arm64@0.25.2': optional: true '@esbuild/linux-arm@0.25.0': optional: true - '@esbuild/linux-arm@0.25.1': - optional: true - '@esbuild/linux-arm@0.25.2': optional: true '@esbuild/linux-ia32@0.25.0': optional: true - '@esbuild/linux-ia32@0.25.1': - optional: true - '@esbuild/linux-ia32@0.25.2': optional: true '@esbuild/linux-loong64@0.25.0': optional: true - '@esbuild/linux-loong64@0.25.1': - optional: true - '@esbuild/linux-loong64@0.25.2': optional: true '@esbuild/linux-mips64el@0.25.0': optional: true - '@esbuild/linux-mips64el@0.25.1': - optional: true - '@esbuild/linux-mips64el@0.25.2': optional: true '@esbuild/linux-ppc64@0.25.0': optional: true - '@esbuild/linux-ppc64@0.25.1': - optional: true - '@esbuild/linux-ppc64@0.25.2': optional: true '@esbuild/linux-riscv64@0.25.0': optional: true - '@esbuild/linux-riscv64@0.25.1': - optional: true - '@esbuild/linux-riscv64@0.25.2': optional: true '@esbuild/linux-s390x@0.25.0': optional: true - '@esbuild/linux-s390x@0.25.1': - optional: true - '@esbuild/linux-s390x@0.25.2': optional: true '@esbuild/linux-x64@0.25.0': optional: true - '@esbuild/linux-x64@0.25.1': - optional: true - '@esbuild/linux-x64@0.25.2': optional: true '@esbuild/netbsd-arm64@0.25.0': optional: true - '@esbuild/netbsd-arm64@0.25.1': - optional: true - '@esbuild/netbsd-arm64@0.25.2': optional: true '@esbuild/netbsd-x64@0.25.0': optional: true - '@esbuild/netbsd-x64@0.25.1': - optional: true - '@esbuild/netbsd-x64@0.25.2': optional: true '@esbuild/openbsd-arm64@0.25.0': optional: true - '@esbuild/openbsd-arm64@0.25.1': - optional: true - '@esbuild/openbsd-arm64@0.25.2': optional: true '@esbuild/openbsd-x64@0.25.0': optional: true - '@esbuild/openbsd-x64@0.25.1': - optional: true - '@esbuild/openbsd-x64@0.25.2': optional: true '@esbuild/sunos-x64@0.25.0': optional: true - '@esbuild/sunos-x64@0.25.1': - optional: true - '@esbuild/sunos-x64@0.25.2': optional: true '@esbuild/win32-arm64@0.25.0': optional: true - '@esbuild/win32-arm64@0.25.1': - optional: true - '@esbuild/win32-arm64@0.25.2': optional: true '@esbuild/win32-ia32@0.25.0': optional: true - '@esbuild/win32-ia32@0.25.1': - optional: true - '@esbuild/win32-ia32@0.25.2': optional: true '@esbuild/win32-x64@0.25.0': optional: true - '@esbuild/win32-x64@0.25.1': - optional: true - '@esbuild/win32-x64@0.25.2': optional: true @@ -12630,91 +12571,94 @@ snapshots: '@humanwhocodes/retry@0.4.2': {} - '@img/sharp-darwin-arm64@0.33.5': + '@img/sharp-darwin-arm64@0.34.1': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.0.4 + '@img/sharp-libvips-darwin-arm64': 1.1.0 optional: true - '@img/sharp-darwin-x64@0.33.5': + '@img/sharp-darwin-x64@0.34.1': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.0.4 + '@img/sharp-libvips-darwin-x64': 1.1.0 optional: true - '@img/sharp-libvips-darwin-arm64@1.0.4': + '@img/sharp-libvips-darwin-arm64@1.1.0': optional: true - '@img/sharp-libvips-darwin-x64@1.0.4': + '@img/sharp-libvips-darwin-x64@1.1.0': optional: true - '@img/sharp-libvips-linux-arm64@1.0.4': + '@img/sharp-libvips-linux-arm64@1.1.0': optional: true - '@img/sharp-libvips-linux-arm@1.0.5': + '@img/sharp-libvips-linux-arm@1.1.0': optional: true - '@img/sharp-libvips-linux-s390x@1.0.4': + '@img/sharp-libvips-linux-ppc64@1.1.0': optional: true - '@img/sharp-libvips-linux-x64@1.0.4': + '@img/sharp-libvips-linux-s390x@1.1.0': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + '@img/sharp-libvips-linux-x64@1.1.0': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.0.4': + '@img/sharp-libvips-linuxmusl-arm64@1.1.0': optional: true - '@img/sharp-linux-arm64@0.33.5': + '@img/sharp-libvips-linuxmusl-x64@1.1.0': + optional: true + + '@img/sharp-linux-arm64@0.34.1': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.0.4 + '@img/sharp-libvips-linux-arm64': 1.1.0 optional: true - '@img/sharp-linux-arm@0.33.5': + '@img/sharp-linux-arm@0.34.1': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.0.5 + '@img/sharp-libvips-linux-arm': 1.1.0 optional: true - '@img/sharp-linux-s390x@0.33.5': + '@img/sharp-linux-s390x@0.34.1': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.0.4 + '@img/sharp-libvips-linux-s390x': 1.1.0 optional: true - '@img/sharp-linux-x64@0.33.5': + '@img/sharp-linux-x64@0.34.1': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.0.4 + '@img/sharp-libvips-linux-x64': 1.1.0 optional: true - '@img/sharp-linuxmusl-arm64@0.33.5': + '@img/sharp-linuxmusl-arm64@0.34.1': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 optional: true - '@img/sharp-linuxmusl-x64@0.33.5': + '@img/sharp-linuxmusl-x64@0.34.1': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + '@img/sharp-libvips-linuxmusl-x64': 1.1.0 optional: true - '@img/sharp-wasm32@0.33.5': + '@img/sharp-wasm32@0.34.1': dependencies: - '@emnapi/runtime': 1.3.0 + '@emnapi/runtime': 1.4.3 optional: true - '@img/sharp-win32-ia32@0.33.5': + '@img/sharp-win32-ia32@0.34.1': optional: true - '@img/sharp-win32-x64@0.33.5': + '@img/sharp-win32-x64@0.34.1': optional: true - '@inquirer/confirm@5.0.2(@types/node@22.13.11)': + '@inquirer/confirm@5.0.2(@types/node@22.14.0)': dependencies: - '@inquirer/core': 10.1.0(@types/node@22.13.11) - '@inquirer/type': 3.0.1(@types/node@22.13.11) - '@types/node': 22.13.11 + '@inquirer/core': 10.1.0(@types/node@22.14.0) + '@inquirer/type': 3.0.1(@types/node@22.14.0) + '@types/node': 22.14.0 - '@inquirer/core@10.1.0(@types/node@22.13.11)': + '@inquirer/core@10.1.0(@types/node@22.14.0)': dependencies: '@inquirer/figures': 1.0.8 - '@inquirer/type': 3.0.1(@types/node@22.13.11) + '@inquirer/type': 3.0.1(@types/node@22.14.0) ansi-escapes: 4.3.2 cli-width: 4.1.0 mute-stream: 2.0.0 @@ -12727,9 +12671,9 @@ snapshots: '@inquirer/figures@1.0.8': {} - '@inquirer/type@3.0.1(@types/node@22.13.11)': + '@inquirer/type@3.0.1(@types/node@22.14.0)': dependencies: - '@types/node': 22.13.11 + '@types/node': 22.14.0 '@ioredis/commands@1.2.0': {} @@ -12759,7 +12703,7 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 22.13.15 + '@types/node': 22.14.0 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -12772,14 +12716,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.13.15 + '@types/node': 22.14.0 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@22.13.15) + jest-config: 29.7.0(@types/node@22.14.0) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -12808,7 +12752,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.13.15 + '@types/node': 22.14.0 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -12826,7 +12770,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.13.15 + '@types/node': 22.14.0 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -12848,7 +12792,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 22.13.11 + '@types/node': 22.14.0 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -12858,7 +12802,7 @@ snapshots: istanbul-lib-instrument: 6.0.0 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.6 + istanbul-reports: 3.1.7 jest-message-util: 29.7.0 jest-util: 29.7.0 jest-worker: 29.7.0 @@ -12918,18 +12862,18 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 22.13.15 + '@types/node': 22.14.0 '@types/yargs': 17.0.19 chalk: 4.1.2 - '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.8.2)(vite@6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.8.3)(vite@6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3))': dependencies: - glob: 10.3.10 + glob: 10.4.5 magic-string: 0.27.0 - react-docgen-typescript: 2.2.2(typescript@5.8.2) - vite: 6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) + react-docgen-typescript: 2.2.2(typescript@5.8.3) + vite: 6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) optionalDependencies: - typescript: 5.8.2 + typescript: 5.8.3 '@jridgewell/gen-mapping@0.3.5': dependencies: @@ -12976,11 +12920,11 @@ snapshots: dependencies: '@mcaptcha/core-glue': 0.1.0-alpha-5 - '@mdx-js/react@3.0.1(@types/react@18.0.28)(react@19.0.0)': + '@mdx-js/react@3.0.1(@types/react@18.0.28)(react@19.1.0)': dependencies: '@types/mdx': 2.0.3 '@types/react': 18.0.28 - react: 19.0.0 + react: 19.1.0 '@microsoft/api-extractor-model@7.30.5(@types/node@22.13.15)': dependencies: @@ -13029,17 +12973,17 @@ snapshots: eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.27.0(eslint@9.22.0)(typescript@5.8.2))(eslint@9.22.0) globals: 16.0.0 - '@misskey-dev/sharp-read-bmp@1.2.0': + '@misskey-dev/sharp-read-bmp@1.3.0': dependencies: decode-bmp: 0.2.1 decode-ico: 0.4.1 - sharp: 0.33.5 + sharp: 0.34.1 '@misskey-dev/summaly@5.2.0': dependencies: cheerio: 1.0.0 escape-regexp: 0.0.1 - got: 14.4.6 + got: 14.4.7 html-entities: 2.5.2 iconv-lite: 0.6.3 jschardet: 3.1.4 @@ -13072,17 +13016,18 @@ snapshots: outvariant: 1.4.3 strict-event-emitter: 0.5.1 - '@nestjs/common@11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/common@11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: + file-type: 19.6.0 iterare: 1.2.1 reflect-metadata: 0.2.2 rxjs: 7.8.2 tslib: 2.8.1 uid: 2.0.2 - '@nestjs/core@11.0.12(@nestjs/common@11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/core@11.0.15(@nestjs/common@11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nuxt/opencollective': 0.4.1 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -13092,12 +13037,12 @@ snapshots: tslib: 2.8.1 uid: 2.0.2 optionalDependencies: - '@nestjs/platform-express': 10.4.15(@nestjs/common@11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.0.12) + '@nestjs/platform-express': 10.4.15(@nestjs/common@11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.0.15) - '@nestjs/platform-express@10.4.15(@nestjs/common@11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.0.12)': + '@nestjs/platform-express@10.4.15(@nestjs/common@11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.0.15)': dependencies: - '@nestjs/common': 11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.0.12(@nestjs/common@11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.0.15(@nestjs/common@11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2) body-parser: 1.20.3 cors: 2.8.5 express: 4.21.2 @@ -13106,15 +13051,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@nestjs/testing@11.0.12(@nestjs/common@11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.0.12(@nestjs/common@11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.15(@nestjs/common@11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.0.12))': + '@nestjs/testing@11.0.15(@nestjs/common@11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.0.15(@nestjs/common@11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.15(@nestjs/common@11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.0.15))': dependencies: - '@nestjs/common': 11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.0.12(@nestjs/common@11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.0.15(@nestjs/common@11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2) tslib: 2.8.1 optionalDependencies: - '@nestjs/platform-express': 10.4.15(@nestjs/common@11.0.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.0.12) + '@nestjs/platform-express': 10.4.15(@nestjs/common@11.0.16(file-type@19.6.0)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.0.15) - '@noble/hashes@1.6.1': {} + '@noble/hashes@1.7.1': {} '@nodelib/fs.scandir@2.1.5': dependencies: @@ -13595,82 +13540,85 @@ snapshots: '@readme/openapi-schemas@3.1.0': {} - '@rollup/plugin-json@6.1.0(rollup@4.36.0)': + '@rollup/plugin-json@6.1.0(rollup@4.39.0)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.36.0) + '@rollup/pluginutils': 5.1.4(rollup@4.39.0) optionalDependencies: - rollup: 4.36.0 + rollup: 4.39.0 - '@rollup/plugin-replace@6.0.2(rollup@4.36.0)': + '@rollup/plugin-replace@6.0.2(rollup@4.39.0)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.36.0) + '@rollup/pluginutils': 5.1.4(rollup@4.39.0) magic-string: 0.30.17 optionalDependencies: - rollup: 4.36.0 + rollup: 4.39.0 - '@rollup/pluginutils@5.1.4(rollup@4.36.0)': + '@rollup/pluginutils@5.1.4(rollup@4.39.0)': dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.7 estree-walker: 2.0.2 picomatch: 4.0.2 optionalDependencies: - rollup: 4.36.0 + rollup: 4.39.0 - '@rollup/rollup-android-arm-eabi@4.36.0': + '@rollup/rollup-android-arm-eabi@4.39.0': optional: true - '@rollup/rollup-android-arm64@4.36.0': + '@rollup/rollup-android-arm64@4.39.0': optional: true - '@rollup/rollup-darwin-arm64@4.36.0': + '@rollup/rollup-darwin-arm64@4.39.0': optional: true - '@rollup/rollup-darwin-x64@4.36.0': + '@rollup/rollup-darwin-x64@4.39.0': optional: true - '@rollup/rollup-freebsd-arm64@4.36.0': + '@rollup/rollup-freebsd-arm64@4.39.0': optional: true - '@rollup/rollup-freebsd-x64@4.36.0': + '@rollup/rollup-freebsd-x64@4.39.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.36.0': + '@rollup/rollup-linux-arm-gnueabihf@4.39.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.36.0': + '@rollup/rollup-linux-arm-musleabihf@4.39.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.36.0': + '@rollup/rollup-linux-arm64-gnu@4.39.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.36.0': + '@rollup/rollup-linux-arm64-musl@4.39.0': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.36.0': + '@rollup/rollup-linux-loongarch64-gnu@4.39.0': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.36.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.39.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.36.0': + '@rollup/rollup-linux-riscv64-gnu@4.39.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.36.0': + '@rollup/rollup-linux-riscv64-musl@4.39.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.36.0': + '@rollup/rollup-linux-s390x-gnu@4.39.0': optional: true - '@rollup/rollup-linux-x64-musl@4.36.0': + '@rollup/rollup-linux-x64-gnu@4.39.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.36.0': + '@rollup/rollup-linux-x64-musl@4.39.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.36.0': + '@rollup/rollup-win32-arm64-msvc@4.39.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.36.0': + '@rollup/rollup-win32-ia32-msvc@4.39.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.39.0': optional: true '@rtsao/scc@1.1.0': {} @@ -13713,35 +13661,35 @@ snapshots: '@sec-ant/readable-stream@0.4.1': {} - '@sentry-internal/browser-utils@9.8.0': + '@sentry-internal/browser-utils@9.12.0': dependencies: - '@sentry/core': 9.8.0 + '@sentry/core': 9.12.0 - '@sentry-internal/feedback@9.8.0': + '@sentry-internal/feedback@9.12.0': dependencies: - '@sentry/core': 9.8.0 + '@sentry/core': 9.12.0 - '@sentry-internal/replay-canvas@9.8.0': + '@sentry-internal/replay-canvas@9.12.0': dependencies: - '@sentry-internal/replay': 9.8.0 - '@sentry/core': 9.8.0 + '@sentry-internal/replay': 9.12.0 + '@sentry/core': 9.12.0 - '@sentry-internal/replay@9.8.0': + '@sentry-internal/replay@9.12.0': dependencies: - '@sentry-internal/browser-utils': 9.8.0 - '@sentry/core': 9.8.0 + '@sentry-internal/browser-utils': 9.12.0 + '@sentry/core': 9.12.0 - '@sentry/browser@9.8.0': + '@sentry/browser@9.12.0': dependencies: - '@sentry-internal/browser-utils': 9.8.0 - '@sentry-internal/feedback': 9.8.0 - '@sentry-internal/replay': 9.8.0 - '@sentry-internal/replay-canvas': 9.8.0 - '@sentry/core': 9.8.0 + '@sentry-internal/browser-utils': 9.12.0 + '@sentry-internal/feedback': 9.12.0 + '@sentry-internal/replay': 9.12.0 + '@sentry-internal/replay-canvas': 9.12.0 + '@sentry/core': 9.12.0 '@sentry/core@8.55.0': {} - '@sentry/core@9.8.0': {} + '@sentry/core@9.12.0': {} '@sentry/node@8.55.0': dependencies: @@ -13802,39 +13750,39 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/vue@9.8.0(vue@3.5.13(typescript@5.8.2))': + '@sentry/vue@9.12.0(vue@3.5.13(typescript@5.8.3))': dependencies: - '@sentry/browser': 9.8.0 - '@sentry/core': 9.8.0 - vue: 3.5.13(typescript@5.8.2) + '@sentry/browser': 9.12.0 + '@sentry/core': 9.12.0 + vue: 3.5.13(typescript@5.8.3) - '@shikijs/core@3.2.1': + '@shikijs/core@3.2.2': dependencies: - '@shikijs/types': 3.2.1 + '@shikijs/types': 3.2.2 '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 hast-util-to-html: 9.0.5 - '@shikijs/engine-javascript@3.2.1': + '@shikijs/engine-javascript@3.2.2': dependencies: - '@shikijs/types': 3.2.1 + '@shikijs/types': 3.2.2 '@shikijs/vscode-textmate': 10.0.2 oniguruma-to-es: 4.1.0 - '@shikijs/engine-oniguruma@3.2.1': + '@shikijs/engine-oniguruma@3.2.2': dependencies: - '@shikijs/types': 3.2.1 + '@shikijs/types': 3.2.2 '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/langs@3.2.1': + '@shikijs/langs@3.2.2': dependencies: - '@shikijs/types': 3.2.1 + '@shikijs/types': 3.2.2 - '@shikijs/themes@3.2.1': + '@shikijs/themes@3.2.2': dependencies: - '@shikijs/types': 3.2.1 + '@shikijs/types': 3.2.2 - '@shikijs/types@3.2.1': + '@shikijs/types@3.2.2': dependencies: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 @@ -13921,16 +13869,16 @@ snapshots: '@smithy/abort-controller@4.0.2': dependencies: '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/chunked-blob-reader-native@4.0.0': dependencies: '@smithy/util-base64': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/chunked-blob-reader@5.0.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/config-resolver@4.1.0': dependencies: @@ -13938,7 +13886,7 @@ snapshots: '@smithy/types': 4.2.0 '@smithy/util-config-provider': 4.0.0 '@smithy/util-middleware': 4.0.2 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/core@3.2.0': dependencies: @@ -13949,7 +13897,7 @@ snapshots: '@smithy/util-middleware': 4.0.2 '@smithy/util-stream': 4.2.0 '@smithy/util-utf8': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/credential-provider-imds@4.0.2': dependencies: @@ -13957,37 +13905,37 @@ snapshots: '@smithy/property-provider': 4.0.2 '@smithy/types': 4.2.0 '@smithy/url-parser': 4.0.2 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/eventstream-codec@4.0.2': dependencies: '@aws-crypto/crc32': 5.2.0 '@smithy/types': 4.2.0 '@smithy/util-hex-encoding': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/eventstream-serde-browser@4.0.2': dependencies: '@smithy/eventstream-serde-universal': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/eventstream-serde-config-resolver@4.1.0': dependencies: '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/eventstream-serde-node@4.0.2': dependencies: '@smithy/eventstream-serde-universal': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/eventstream-serde-universal@4.0.2': dependencies: '@smithy/eventstream-codec': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/fetch-http-handler@5.0.2': dependencies: @@ -13995,52 +13943,52 @@ snapshots: '@smithy/querystring-builder': 4.0.2 '@smithy/types': 4.2.0 '@smithy/util-base64': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/hash-blob-browser@4.0.2': dependencies: '@smithy/chunked-blob-reader': 5.0.0 '@smithy/chunked-blob-reader-native': 4.0.0 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/hash-node@4.0.2': dependencies: '@smithy/types': 4.2.0 '@smithy/util-buffer-from': 4.0.0 '@smithy/util-utf8': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/hash-stream-node@4.0.2': dependencies: '@smithy/types': 4.2.0 '@smithy/util-utf8': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/invalid-dependency@4.0.2': dependencies: '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/is-array-buffer@2.0.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/is-array-buffer@4.0.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/md5-js@4.0.2': dependencies: '@smithy/types': 4.2.0 '@smithy/util-utf8': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/middleware-content-length@4.0.2': dependencies: '@smithy/protocol-http': 5.1.0 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/middleware-endpoint@4.1.0': dependencies: @@ -14051,7 +13999,7 @@ snapshots: '@smithy/types': 4.2.0 '@smithy/url-parser': 4.0.2 '@smithy/util-middleware': 4.0.2 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/middleware-retry@4.1.0': dependencies: @@ -14062,25 +14010,25 @@ snapshots: '@smithy/types': 4.2.0 '@smithy/util-middleware': 4.0.2 '@smithy/util-retry': 4.0.2 - tslib: 2.7.0 + tslib: 2.8.1 uuid: 9.0.1 '@smithy/middleware-serde@4.0.3': dependencies: '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/middleware-stack@4.0.2': dependencies: '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/node-config-provider@4.0.2': dependencies: '@smithy/property-provider': 4.0.2 '@smithy/shared-ini-file-loader': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/node-http-handler@2.5.0': dependencies: @@ -14096,12 +14044,12 @@ snapshots: '@smithy/protocol-http': 5.1.0 '@smithy/querystring-builder': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/property-provider@4.0.2': dependencies: '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/protocol-http@3.3.0': dependencies: @@ -14111,7 +14059,7 @@ snapshots: '@smithy/protocol-http@5.1.0': dependencies: '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/querystring-builder@2.2.0': dependencies: @@ -14123,12 +14071,12 @@ snapshots: dependencies: '@smithy/types': 4.2.0 '@smithy/util-uri-escape': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/querystring-parser@4.0.2': dependencies: '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/service-error-classification@4.0.2': dependencies: @@ -14137,7 +14085,7 @@ snapshots: '@smithy/shared-ini-file-loader@4.0.2': dependencies: '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/signature-v4@5.0.2': dependencies: @@ -14148,7 +14096,7 @@ snapshots: '@smithy/util-middleware': 4.0.2 '@smithy/util-uri-escape': 4.0.0 '@smithy/util-utf8': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/smithy-client@4.2.0': dependencies: @@ -14158,7 +14106,7 @@ snapshots: '@smithy/protocol-http': 5.1.0 '@smithy/types': 4.2.0 '@smithy/util-stream': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/types@2.12.0': dependencies: @@ -14166,41 +14114,41 @@ snapshots: '@smithy/types@4.2.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/url-parser@4.0.2': dependencies: '@smithy/querystring-parser': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-base64@4.0.0': dependencies: '@smithy/util-buffer-from': 4.0.0 '@smithy/util-utf8': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-body-length-browser@4.0.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-body-length-node@4.0.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-buffer-from@2.0.0': dependencies: '@smithy/is-array-buffer': 2.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-buffer-from@4.0.0': dependencies: '@smithy/is-array-buffer': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-config-provider@4.0.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-defaults-mode-browser@4.0.8': dependencies: @@ -14208,7 +14156,7 @@ snapshots: '@smithy/smithy-client': 4.2.0 '@smithy/types': 4.2.0 bowser: 2.11.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-defaults-mode-node@4.0.8': dependencies: @@ -14218,28 +14166,28 @@ snapshots: '@smithy/property-provider': 4.0.2 '@smithy/smithy-client': 4.2.0 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-endpoints@3.0.2': dependencies: '@smithy/node-config-provider': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-hex-encoding@4.0.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-middleware@4.0.2': dependencies: '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-retry@4.0.2': dependencies: '@smithy/service-error-classification': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-stream@4.2.0': dependencies: @@ -14250,7 +14198,7 @@ snapshots: '@smithy/util-buffer-from': 4.0.0 '@smithy/util-hex-encoding': 4.0.0 '@smithy/util-utf8': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-uri-escape@2.2.0': dependencies: @@ -14258,164 +14206,164 @@ snapshots: '@smithy/util-uri-escape@4.0.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-utf8@2.0.0': dependencies: '@smithy/util-buffer-from': 2.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-utf8@4.0.0': dependencies: '@smithy/util-buffer-from': 4.0.0 - tslib: 2.7.0 + tslib: 2.8.1 '@smithy/util-waiter@4.0.3': dependencies: '@smithy/abort-controller': 4.0.2 '@smithy/types': 4.2.0 - tslib: 2.7.0 + tslib: 2.8.1 '@sqltools/formatter@1.2.5': {} - '@storybook/addon-actions@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-actions@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 '@types/uuid': 9.0.8 dequal: 2.0.3 polished: 4.2.2 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) uuid: 9.0.1 - '@storybook/addon-backgrounds@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-backgrounds@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 memoizerific: 1.11.3 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - '@storybook/addon-controls@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-controls@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 dequal: 2.0.3 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - '@storybook/addon-docs@8.6.7(@types/react@18.0.28)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-docs@8.6.12(@types/react@18.0.28)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - '@mdx-js/react': 3.0.1(@types/react@18.0.28)(react@19.0.0) - '@storybook/blocks': 8.6.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/csf-plugin': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/react-dom-shim': 8.6.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + '@mdx-js/react': 3.0.1(@types/react@18.0.28)(react@19.1.0) + '@storybook/blocks': 8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/csf-plugin': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/react-dom-shim': 8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-essentials@8.6.7(@types/react@18.0.28)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-essentials@8.6.12(@types/react@18.0.28)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - '@storybook/addon-actions': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-backgrounds': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-controls': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-docs': 8.6.7(@types/react@18.0.28)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-highlight': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-measure': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-outline': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-toolbars': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-viewport': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + '@storybook/addon-actions': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/addon-backgrounds': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/addon-controls': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/addon-docs': 8.6.12(@types/react@18.0.28)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/addon-highlight': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/addon-measure': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/addon-outline': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/addon-toolbars': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/addon-viewport': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-highlight@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-highlight@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/addon-interactions@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-interactions@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - '@storybook/instrumenter': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/test': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/instrumenter': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/test': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) polished: 4.2.2 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - '@storybook/addon-links@8.6.7(react@19.0.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-links@8.6.12(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 optionalDependencies: - react: 19.0.0 + react: 19.1.0 - '@storybook/addon-mdx-gfm@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-mdx-gfm@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: remark-gfm: 4.0.0 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 transitivePeerDependencies: - supports-color - '@storybook/addon-measure@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-measure@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) tiny-invariant: 1.3.3 - '@storybook/addon-outline@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-outline@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - '@storybook/addon-storysource@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-storysource@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - '@storybook/source-loader': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/source-loader': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) estraverse: 5.3.0 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) tiny-invariant: 1.3.3 - '@storybook/addon-toolbars@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-toolbars@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/addon-viewport@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-viewport@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: memoizerific: 1.11.3 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/blocks@8.6.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/blocks@8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - '@storybook/icons': 1.2.12(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + '@storybook/icons': 1.2.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 optionalDependencies: - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) - '@storybook/builder-vite@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))': + '@storybook/builder-vite@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3))': dependencies: - '@storybook/csf-plugin': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/csf-plugin': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) browser-assert: 1.2.1 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - vite: 6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) + vite: 6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) - '@storybook/components@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/components@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/core-events@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/core-events@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/core@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(utf-8-validate@6.0.5)': + '@storybook/core@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(utf-8-validate@6.0.5)': dependencies: - '@storybook/theming': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/theming': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) better-opn: 3.0.2 browser-assert: 1.2.1 esbuild: 0.25.2 @@ -14423,7 +14371,7 @@ snapshots: jsdoc-type-pratt-parser: 4.1.0 process: 0.11.10 recast: 0.23.6 - semver: 7.6.3 + semver: 7.7.1 util: 0.12.5 ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) optionalDependencies: @@ -14434,128 +14382,128 @@ snapshots: - supports-color - utf-8-validate - '@storybook/csf-plugin@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/csf-plugin@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) unplugin: 1.4.0 '@storybook/global@5.0.0': {} - '@storybook/icons@1.2.12(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@storybook/icons@1.2.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) - '@storybook/instrumenter@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/instrumenter@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 '@vitest/utils': 2.1.2 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/manager-api@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/manager-api@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/preview-api@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/preview-api@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/react-dom-shim@8.6.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/react-dom-shim@8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/react-vite@8.6.7(@storybook/test@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.36.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2)(vite@6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))': + '@storybook/react-vite@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.39.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3)(vite@6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.8.2)(vite@6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)) - '@rollup/pluginutils': 5.1.4(rollup@4.36.0) - '@storybook/builder-vite': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)) - '@storybook/react': 8.6.7(@storybook/test@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.8.3)(vite@6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3)) + '@rollup/pluginutils': 5.1.4(rollup@4.39.0) + '@storybook/builder-vite': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3)) + '@storybook/react': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3) find-up: 5.0.0 magic-string: 0.30.17 - react: 19.0.0 + react: 19.1.0 react-docgen: 7.0.1 - react-dom: 19.0.0(react@19.0.0) + react-dom: 19.1.0(react@19.1.0) resolve: 1.22.8 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) tsconfig-paths: 4.2.0 - vite: 6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) + vite: 6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) optionalDependencies: - '@storybook/test': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/test': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) transitivePeerDependencies: - rollup - supports-color - typescript - '@storybook/react@8.6.7(@storybook/test@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2)': + '@storybook/react@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3)': dependencies: - '@storybook/components': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/components': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/global': 5.0.0 - '@storybook/manager-api': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/preview-api': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/react-dom-shim': 8.6.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/theming': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + '@storybook/manager-api': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/preview-api': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/react-dom-shim': 8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/theming': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) optionalDependencies: - '@storybook/test': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - typescript: 5.8.2 + '@storybook/test': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + typescript: 5.8.3 - '@storybook/source-loader@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/source-loader@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: es-toolkit: 1.27.0 estraverse: 5.3.0 prettier: 3.5.3 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/test@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/test@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - '@storybook/instrumenter': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/instrumenter': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@testing-library/dom': 10.4.0 '@testing-library/jest-dom': 6.5.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) '@vitest/expect': 2.0.5 '@vitest/spy': 2.0.5 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/theming@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/theming@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/types@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/types@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/vue3-vite@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2))': + '@storybook/vue3-vite@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.3))': dependencies: - '@storybook/builder-vite': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)) - '@storybook/vue3': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.13(typescript@5.8.2)) + '@storybook/builder-vite': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3)) + '@storybook/vue3': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.13(typescript@5.8.3)) find-package-json: 1.2.0 magic-string: 0.30.17 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - typescript: 5.8.2 - vite: 6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) - vue-component-meta: 2.0.16(typescript@5.8.2) - vue-docgen-api: 4.75.1(vue@3.5.13(typescript@5.8.2)) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + typescript: 5.8.3 + vite: 6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) + vue-component-meta: 2.0.16(typescript@5.8.3) + vue-docgen-api: 4.75.1(vue@3.5.13(typescript@5.8.3)) transitivePeerDependencies: - vue - '@storybook/vue3@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.13(typescript@5.8.2))': + '@storybook/vue3@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.13(typescript@5.8.3))': dependencies: - '@storybook/components': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/components': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/global': 5.0.0 - '@storybook/manager-api': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/preview-api': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/theming': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/manager-api': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/preview-api': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/theming': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@vue/compiler-core': 3.5.13 - storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 type-fest: 2.19.0 - vue: 3.5.13(typescript@5.8.2) - vue-component-type-helpers: 2.2.8 + vue: 3.5.13(typescript@5.8.3) + vue-component-type-helpers: 2.2.10 '@stylistic/eslint-plugin@4.2.0(eslint@9.22.0)(typescript@5.8.2)': dependencies: @@ -14569,9 +14517,9 @@ snapshots: - supports-color - typescript - '@swc/cli@0.6.0(@swc/core@1.11.11)(chokidar@4.0.3)': + '@swc/cli@0.6.0(@swc/core@1.11.18)(chokidar@4.0.3)': dependencies: - '@swc/core': 1.11.11 + '@swc/core': 1.11.18 '@swc/counter': 0.1.3 '@xhmikosr/bin-wrapper': 13.0.5 commander: 8.3.0 @@ -14589,10 +14537,10 @@ snapshots: '@swc/wasm': 1.2.130 optional: true - '@swc/core-darwin-arm64@1.11.11': + '@swc/core-darwin-arm64@1.11.18': optional: true - '@swc/core-darwin-x64@1.11.11': + '@swc/core-darwin-x64@1.11.18': optional: true '@swc/core-freebsd-x64@1.3.11': @@ -14600,56 +14548,56 @@ snapshots: '@swc/wasm': 1.2.130 optional: true - '@swc/core-linux-arm-gnueabihf@1.11.11': + '@swc/core-linux-arm-gnueabihf@1.11.18': optional: true - '@swc/core-linux-arm64-gnu@1.11.11': + '@swc/core-linux-arm64-gnu@1.11.18': optional: true - '@swc/core-linux-arm64-musl@1.11.11': + '@swc/core-linux-arm64-musl@1.11.18': optional: true - '@swc/core-linux-x64-gnu@1.11.11': + '@swc/core-linux-x64-gnu@1.11.18': optional: true - '@swc/core-linux-x64-musl@1.11.11': + '@swc/core-linux-x64-musl@1.11.18': optional: true - '@swc/core-win32-arm64-msvc@1.11.11': + '@swc/core-win32-arm64-msvc@1.11.18': optional: true - '@swc/core-win32-ia32-msvc@1.11.11': + '@swc/core-win32-ia32-msvc@1.11.18': optional: true - '@swc/core-win32-x64-msvc@1.11.11': + '@swc/core-win32-x64-msvc@1.11.18': optional: true - '@swc/core@1.11.11': + '@swc/core@1.11.18': dependencies: '@swc/counter': 0.1.3 - '@swc/types': 0.1.19 + '@swc/types': 0.1.21 optionalDependencies: - '@swc/core-darwin-arm64': 1.11.11 - '@swc/core-darwin-x64': 1.11.11 - '@swc/core-linux-arm-gnueabihf': 1.11.11 - '@swc/core-linux-arm64-gnu': 1.11.11 - '@swc/core-linux-arm64-musl': 1.11.11 - '@swc/core-linux-x64-gnu': 1.11.11 - '@swc/core-linux-x64-musl': 1.11.11 - '@swc/core-win32-arm64-msvc': 1.11.11 - '@swc/core-win32-ia32-msvc': 1.11.11 - '@swc/core-win32-x64-msvc': 1.11.11 + '@swc/core-darwin-arm64': 1.11.18 + '@swc/core-darwin-x64': 1.11.18 + '@swc/core-linux-arm-gnueabihf': 1.11.18 + '@swc/core-linux-arm64-gnu': 1.11.18 + '@swc/core-linux-arm64-musl': 1.11.18 + '@swc/core-linux-x64-gnu': 1.11.18 + '@swc/core-linux-x64-musl': 1.11.18 + '@swc/core-win32-arm64-msvc': 1.11.18 + '@swc/core-win32-ia32-msvc': 1.11.18 + '@swc/core-win32-x64-msvc': 1.11.18 '@swc/counter@0.1.3': {} - '@swc/jest@0.2.37(@swc/core@1.11.11)': + '@swc/jest@0.2.37(@swc/core@1.11.18)': dependencies: '@jest/create-cache-key-function': 29.7.0 - '@swc/core': 1.11.11 + '@swc/core': 1.11.18 '@swc/counter': 0.1.3 jsonc-parser: 3.2.0 - '@swc/types@0.1.19': + '@swc/types@0.1.21': dependencies: '@swc/counter': 0.1.3 @@ -14702,12 +14650,12 @@ snapshots: dependencies: '@testing-library/dom': 10.4.0 - '@testing-library/vue@8.1.0(@vue/compiler-sfc@3.5.13)(@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2))': + '@testing-library/vue@8.1.0(@vue/compiler-sfc@3.5.13)(@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.8.3)))(vue@3.5.13(typescript@5.8.3))': dependencies: '@babel/runtime': 7.23.4 '@testing-library/dom': 9.3.4 - '@vue/test-utils': 2.4.1(@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2)) - vue: 3.5.13(typescript@5.8.2) + '@vue/test-utils': 2.4.1(@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.8.3)))(vue@3.5.13(typescript@5.8.3)) + vue: 3.5.13(typescript@5.8.3) optionalDependencies: '@vue/compiler-sfc': 3.5.13 transitivePeerDependencies: @@ -14735,7 +14683,7 @@ snapshots: '@types/accepts@1.3.7': dependencies: - '@types/node': 22.13.10 + '@types/node': 22.14.0 '@types/archiver@6.0.3': dependencies: @@ -14771,7 +14719,7 @@ snapshots: '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.35 - '@types/node': 22.13.10 + '@types/node': 22.14.0 '@types/braces@3.0.1': {} @@ -14785,11 +14733,11 @@ snapshots: '@types/connect@3.4.35': dependencies: - '@types/node': 22.13.15 + '@types/node': 22.14.0 '@types/connect@3.4.36': dependencies: - '@types/node': 22.13.15 + '@types/node': 22.14.0 '@types/content-disposition@0.5.8': {} @@ -14807,14 +14755,14 @@ snapshots: '@types/eslint@7.29.0': dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.7 '@types/json-schema': 7.0.15 - '@types/estree@1.0.6': {} + '@types/estree@1.0.7': {} '@types/express-serve-static-core@4.17.33': dependencies: - '@types/node': 22.13.15 + '@types/node': 22.14.0 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 @@ -14827,7 +14775,7 @@ snapshots: '@types/fluent-ffmpeg@2.1.27': dependencies: - '@types/node': 22.13.10 + '@types/node': 22.14.0 '@types/form-data@2.5.2': dependencies: @@ -14835,7 +14783,7 @@ snapshots: '@types/graceful-fs@4.1.6': dependencies: - '@types/node': 22.13.15 + '@types/node': 22.14.0 '@types/hammerjs@2.0.46': {} @@ -14849,7 +14797,7 @@ snapshots: '@types/http-link-header@1.0.7': dependencies: - '@types/node': 22.13.10 + '@types/node': 22.14.0 '@types/istanbul-lib-coverage@2.0.4': {} @@ -14875,7 +14823,7 @@ snapshots: '@types/jsdom@21.1.7': dependencies: - '@types/node': 22.13.10 + '@types/node': 22.14.0 '@types/tough-cookie': 4.0.2 parse5: 7.2.1 @@ -14911,23 +14859,23 @@ snapshots: '@types/mysql@2.15.26': dependencies: - '@types/node': 22.13.15 + '@types/node': 22.14.0 '@types/node@22.13.10': dependencies: undici-types: 6.20.0 - '@types/node@22.13.11': + '@types/node@22.13.15': dependencies: undici-types: 6.20.0 - '@types/node@22.13.15': + '@types/node@22.14.0': dependencies: - undici-types: 6.20.0 + undici-types: 6.21.0 '@types/nodemailer@6.4.17': dependencies: - '@types/node': 22.13.10 + '@types/node': 22.14.0 '@types/normalize-package-data@2.4.1': {} @@ -14938,11 +14886,11 @@ snapshots: '@types/oauth2orize@1.11.5': dependencies: '@types/express': 4.17.17 - '@types/node': 22.13.10 + '@types/node': 22.14.0 '@types/oauth@0.9.6': dependencies: - '@types/node': 22.13.10 + '@types/node': 22.14.0 '@types/object-assign-deep@0.4.3': {} @@ -14954,13 +14902,13 @@ snapshots: '@types/pg@8.11.11': dependencies: - '@types/node': 22.13.10 + '@types/node': 22.14.0 pg-protocol: 1.7.0 pg-types: 4.0.1 '@types/pg@8.6.1': dependencies: - '@types/node': 22.13.11 + '@types/node': 22.14.0 pg-protocol: 1.8.0 pg-types: 2.2.0 @@ -14968,7 +14916,7 @@ snapshots: '@types/proxy-addr@2.0.3': dependencies: - '@types/node': 22.13.11 + '@types/node': 22.14.0 '@types/psl@1.1.3': {} @@ -14978,7 +14926,7 @@ snapshots: '@types/qrcode@1.5.5': dependencies: - '@types/node': 22.13.10 + '@types/node': 22.14.0 '@types/qs@6.9.7': {} @@ -14996,13 +14944,15 @@ snapshots: '@types/readdir-glob@1.1.1': dependencies: - '@types/node': 22.13.15 + '@types/node': 22.14.0 + + '@types/redis-info@3.0.3': {} '@types/rename@1.0.7': {} '@types/resolve@1.20.3': {} - '@types/sanitize-html@2.13.0': + '@types/sanitize-html@2.15.0': dependencies: htmlparser2: 8.0.1 @@ -15010,12 +14960,12 @@ snapshots: '@types/seedrandom@3.0.8': {} - '@types/semver@7.5.8': {} + '@types/semver@7.7.0': {} '@types/serve-static@1.15.1': dependencies: '@types/mime': 3.0.1 - '@types/node': 22.13.15 + '@types/node': 22.14.0 '@types/serviceworker@0.0.74': {} @@ -15041,7 +14991,7 @@ snapshots: '@types/tedious@4.0.14': dependencies: - '@types/node': 22.13.15 + '@types/node': 22.14.0 '@types/throttle-debounce@5.0.2': {} @@ -15059,15 +15009,19 @@ snapshots: '@types/vary@1.1.3': dependencies: - '@types/node': 22.13.10 + '@types/node': 22.14.0 '@types/web-push@3.6.4': dependencies: - '@types/node': 22.13.10 + '@types/node': 22.14.0 '@types/ws@8.18.0': dependencies: - '@types/node': 22.13.10 + '@types/node': 22.14.0 + + '@types/ws@8.18.1': + dependencies: + '@types/node': 22.14.0 '@types/yargs-parser@21.0.0': {} @@ -15077,7 +15031,7 @@ snapshots: '@types/yauzl@2.10.0': dependencies: - '@types/node': 22.13.15 + '@types/node': 22.14.0 optional: true '@typescript-eslint/eslint-plugin@8.27.0(@typescript-eslint/parser@8.27.0(eslint@9.22.0)(typescript@5.1.6))(eslint@9.22.0)(typescript@5.1.6)': @@ -15114,23 +15068,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.27.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0)(typescript@5.8.2))(eslint@9.23.0)(typescript@5.8.2)': - dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.27.0(eslint@9.23.0)(typescript@5.8.2) - '@typescript-eslint/scope-manager': 8.27.0 - '@typescript-eslint/type-utils': 8.27.0(eslint@9.23.0)(typescript@5.8.2) - '@typescript-eslint/utils': 8.27.0(eslint@9.23.0)(typescript@5.8.2) - '@typescript-eslint/visitor-keys': 8.27.0 - eslint: 9.23.0 - graphemer: 1.4.0 - ignore: 5.3.2 - natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.8.2) - typescript: 5.8.2 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/eslint-plugin@8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.22.0)(typescript@5.8.2))(eslint@9.22.0)(typescript@5.8.2)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -15165,6 +15102,23 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/eslint-plugin@8.29.1(@typescript-eslint/parser@8.29.1(eslint@9.23.0)(typescript@5.8.3))(eslint@9.23.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.29.1(eslint@9.23.0)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.29.1 + '@typescript-eslint/type-utils': 8.29.1(eslint@9.23.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.29.1(eslint@9.23.0)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.29.1 + eslint: 9.23.0 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/parser@8.27.0(eslint@9.22.0)(typescript@5.1.6)': dependencies: '@typescript-eslint/scope-manager': 8.27.0 @@ -15189,18 +15143,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.27.0(eslint@9.23.0)(typescript@5.8.2)': - dependencies: - '@typescript-eslint/scope-manager': 8.27.0 - '@typescript-eslint/types': 8.27.0 - '@typescript-eslint/typescript-estree': 8.27.0(typescript@5.8.2) - '@typescript-eslint/visitor-keys': 8.27.0 - debug: 4.4.0(supports-color@8.1.1) - eslint: 9.23.0 - typescript: 5.8.2 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/parser@8.29.0(eslint@9.22.0)(typescript@5.8.2)': dependencies: '@typescript-eslint/scope-manager': 8.29.0 @@ -15225,6 +15167,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/parser@8.29.1(eslint@9.23.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.29.1 + '@typescript-eslint/types': 8.29.1 + '@typescript-eslint/typescript-estree': 8.29.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.29.1 + debug: 4.4.0(supports-color@8.1.1) + eslint: 9.23.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@8.27.0': dependencies: '@typescript-eslint/types': 8.27.0 @@ -15235,6 +15189,11 @@ snapshots: '@typescript-eslint/types': 8.29.0 '@typescript-eslint/visitor-keys': 8.29.0 + '@typescript-eslint/scope-manager@8.29.1': + dependencies: + '@typescript-eslint/types': 8.29.1 + '@typescript-eslint/visitor-keys': 8.29.1 + '@typescript-eslint/type-utils@8.27.0(eslint@9.22.0)(typescript@5.1.6)': dependencies: '@typescript-eslint/typescript-estree': 8.27.0(typescript@5.1.6) @@ -15257,17 +15216,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.27.0(eslint@9.23.0)(typescript@5.8.2)': - dependencies: - '@typescript-eslint/typescript-estree': 8.27.0(typescript@5.8.2) - '@typescript-eslint/utils': 8.27.0(eslint@9.23.0)(typescript@5.8.2) - debug: 4.4.0(supports-color@8.1.1) - eslint: 9.23.0 - ts-api-utils: 2.1.0(typescript@5.8.2) - typescript: 5.8.2 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/type-utils@8.29.0(eslint@9.22.0)(typescript@5.8.2)': dependencies: '@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.2) @@ -15290,10 +15238,23 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/type-utils@8.29.1(eslint@9.23.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.29.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.29.1(eslint@9.23.0)(typescript@5.8.3) + debug: 4.4.0(supports-color@8.1.1) + eslint: 9.23.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/types@8.27.0': {} '@typescript-eslint/types@8.29.0': {} + '@typescript-eslint/types@8.29.1': {} + '@typescript-eslint/typescript-estree@8.27.0(typescript@5.1.6)': dependencies: '@typescript-eslint/types': 8.27.0 @@ -15336,6 +15297,20 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.29.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/types': 8.29.1 + '@typescript-eslint/visitor-keys': 8.29.1 + debug: 4.4.0(supports-color@8.1.1) + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.1 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@8.27.0(eslint@9.22.0)(typescript@5.1.6)': dependencies: '@eslint-community/eslint-utils': 4.5.1(eslint@9.22.0) @@ -15358,17 +15333,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.27.0(eslint@9.23.0)(typescript@5.8.2)': - dependencies: - '@eslint-community/eslint-utils': 4.5.1(eslint@9.23.0) - '@typescript-eslint/scope-manager': 8.27.0 - '@typescript-eslint/types': 8.27.0 - '@typescript-eslint/typescript-estree': 8.27.0(typescript@5.8.2) - eslint: 9.23.0 - typescript: 5.8.2 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/utils@8.29.0(eslint@9.22.0)(typescript@5.8.2)': dependencies: '@eslint-community/eslint-utils': 4.5.1(eslint@9.22.0) @@ -15391,6 +15355,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.29.1(eslint@9.23.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.5.1(eslint@9.23.0) + '@typescript-eslint/scope-manager': 8.29.1 + '@typescript-eslint/types': 8.29.1 + '@typescript-eslint/typescript-estree': 8.29.1(typescript@5.8.3) + eslint: 9.23.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@8.27.0': dependencies: '@typescript-eslint/types': 8.27.0 @@ -15401,14 +15376,19 @@ snapshots: '@typescript-eslint/types': 8.29.0 eslint-visitor-keys: 4.2.0 + '@typescript-eslint/visitor-keys@8.29.1': + dependencies: + '@typescript-eslint/types': 8.29.1 + eslint-visitor-keys: 4.2.0 + '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-vue@5.2.3(vite@6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2))': + '@vitejs/plugin-vue@5.2.3(vite@6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.3))': dependencies: - vite: 6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) - vue: 3.5.13(typescript@5.8.2) + vite: 6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) + vue: 3.5.13(typescript@5.8.3) - '@vitest/coverage-v8@3.0.9(vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.13.11)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))': + '@vitest/coverage-v8@3.1.1(vitest@3.1.1(@types/debug@4.1.12)(@types/node@22.14.0)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3))(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 @@ -15422,11 +15402,11 @@ snapshots: std-env: 3.8.1 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.0.9(@types/debug@4.1.12)(@types/node@22.13.11)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) + vitest: 3.1.1(@types/debug@4.1.12)(@types/node@22.14.0)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3))(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@3.0.9(vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.13.11)(happy-dom@17.4.4)(jsdom@26.0.0)(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))': + '@vitest/coverage-v8@3.1.1(vitest@3.1.1(@types/debug@4.1.12)(@types/node@22.14.0)(happy-dom@17.4.4)(jsdom@26.0.0)(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3))(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 @@ -15440,7 +15420,7 @@ snapshots: std-env: 3.8.1 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.0.9(@types/debug@4.1.12)(@types/node@22.13.11)(happy-dom@17.4.4)(jsdom@26.0.0)(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) + vitest: 3.1.1(@types/debug@4.1.12)(@types/node@22.14.0)(happy-dom@17.4.4)(jsdom@26.0.0)(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3))(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) transitivePeerDependencies: - supports-color @@ -15448,24 +15428,24 @@ snapshots: dependencies: '@vitest/spy': 2.0.5 '@vitest/utils': 2.0.5 - chai: 5.1.1 + chai: 5.2.0 tinyrainbow: 1.2.0 - '@vitest/expect@3.0.9': + '@vitest/expect@3.1.1': dependencies: - '@vitest/spy': 3.0.9 - '@vitest/utils': 3.0.9 + '@vitest/spy': 3.1.1 + '@vitest/utils': 3.1.1 chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.0.9(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(vite@6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))': + '@vitest/mocker@3.1.1(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3))(vite@6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3))': dependencies: - '@vitest/spy': 3.0.9 + '@vitest/spy': 3.1.1 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - msw: 2.7.3(@types/node@22.13.11)(typescript@5.8.2) - vite: 6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) + msw: 2.7.3(@types/node@22.14.0)(typescript@5.8.3) + vite: 6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) '@vitest/pretty-format@2.0.5': dependencies: @@ -15475,18 +15455,22 @@ snapshots: dependencies: tinyrainbow: 1.2.0 - '@vitest/pretty-format@3.0.9': + '@vitest/pretty-format@3.1.1': dependencies: tinyrainbow: 2.0.0 - '@vitest/runner@3.0.9': + '@vitest/pretty-format@3.1.2': dependencies: - '@vitest/utils': 3.0.9 + tinyrainbow: 2.0.0 + + '@vitest/runner@3.1.1': + dependencies: + '@vitest/utils': 3.1.1 pathe: 2.0.3 - '@vitest/snapshot@3.0.9': + '@vitest/snapshot@3.1.1': dependencies: - '@vitest/pretty-format': 3.0.9 + '@vitest/pretty-format': 3.1.1 magic-string: 0.30.17 pathe: 2.0.3 @@ -15494,7 +15478,7 @@ snapshots: dependencies: tinyspy: 3.0.2 - '@vitest/spy@3.0.9': + '@vitest/spy@3.1.1': dependencies: tinyspy: 3.0.2 @@ -15502,18 +15486,18 @@ snapshots: dependencies: '@vitest/pretty-format': 2.0.5 estree-walker: 3.0.3 - loupe: 3.1.2 + loupe: 3.1.3 tinyrainbow: 1.2.0 '@vitest/utils@2.1.2': dependencies: '@vitest/pretty-format': 2.1.2 - loupe: 3.1.2 + loupe: 3.1.3 tinyrainbow: 1.2.0 - '@vitest/utils@3.0.9': + '@vitest/utils@3.1.1': dependencies: - '@vitest/pretty-format': 3.0.9 + '@vitest/pretty-format': 3.1.1 loupe: 3.1.3 tinyrainbow: 2.0.0 @@ -15590,19 +15574,19 @@ snapshots: de-indent: 1.0.2 he: 1.2.0 - '@vue/language-core@2.0.16(typescript@5.8.2)': + '@vue/language-core@2.0.16(typescript@5.8.3)': dependencies: '@volar/language-core': 2.2.0 - '@vue/compiler-dom': 3.5.12 - '@vue/shared': 3.5.12 + '@vue/compiler-dom': 3.5.13 + '@vue/shared': 3.5.13 computeds: 0.0.1 minimatch: 9.0.5 path-browserify: 1.0.1 vue-template-compiler: 2.7.14 optionalDependencies: - typescript: 5.8.2 + typescript: 5.8.3 - '@vue/language-core@2.2.8(typescript@5.8.2)': + '@vue/language-core@2.2.8(typescript@5.8.3)': dependencies: '@volar/language-core': 2.4.12 '@vue/compiler-dom': 3.5.12 @@ -15613,7 +15597,7 @@ snapshots: muggle-string: 0.4.1 path-browserify: 1.0.1 optionalDependencies: - typescript: 5.8.2 + typescript: 5.8.3 '@vue/reactivity@3.5.13': dependencies: @@ -15631,23 +15615,23 @@ snapshots: '@vue/shared': 3.5.13 csstype: 3.1.3 - '@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.8.2))': + '@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.8.3))': dependencies: '@vue/compiler-ssr': 3.5.13 '@vue/shared': 3.5.13 - vue: 3.5.13(typescript@5.8.2) + vue: 3.5.13(typescript@5.8.3) '@vue/shared@3.5.12': {} '@vue/shared@3.5.13': {} - '@vue/test-utils@2.4.1(@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2))': + '@vue/test-utils@2.4.1(@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.8.3)))(vue@3.5.13(typescript@5.8.3))': dependencies: js-beautify: 1.14.9 - vue: 3.5.13(typescript@5.8.2) + vue: 3.5.13(typescript@5.8.3) vue-component-type-helpers: 1.8.4 optionalDependencies: - '@vue/server-renderer': 3.5.13(vue@3.5.13(typescript@5.8.2)) + '@vue/server-renderer': 3.5.13(vue@3.5.13(typescript@5.8.3)) '@xhmikosr/archive-type@7.0.0': dependencies: @@ -15978,7 +15962,7 @@ snapshots: ast-types@0.16.1: dependencies: - tslib: 2.7.0 + tslib: 2.8.1 astral-regex@2.0.0: optional: true @@ -16060,6 +16044,20 @@ snapshots: transitivePeerDependencies: - supports-color + babel-jest@29.7.0(@babel/core@7.24.7): + dependencies: + '@babel/core': 7.24.7 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.0 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.24.7) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + optional: true + babel-plugin-istanbul@6.1.1: dependencies: '@babel/helper-plugin-utils': 7.22.5 @@ -16093,12 +16091,36 @@ snapshots: '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.5) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.5) + babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.7): + dependencies: + '@babel/core': 7.24.7 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.7) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.7) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7) + optional: true + babel-preset-jest@29.6.3(@babel/core@7.23.5): dependencies: '@babel/core': 7.23.5 babel-plugin-jest-hoist: 29.6.3 babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.5) + babel-preset-jest@29.6.3(@babel/core@7.24.7): + dependencies: + '@babel/core': 7.24.7 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.7) + optional: true + babel-walk@3.0.0-canary-5: dependencies: '@babel/types': 7.24.7 @@ -16179,9 +16201,9 @@ snapshots: dependencies: fill-range: 7.1.1 - broadcast-channel@7.0.0: + broadcast-channel@7.1.0: dependencies: - '@babel/runtime': 7.23.4 + '@babel/runtime': 7.27.0 oblivious-set: 1.4.0 p-queue: 6.6.2 unload: 2.4.1 @@ -16238,14 +16260,14 @@ snapshots: node-gyp-build: 4.8.1 optional: true - bullmq@5.44.1: + bullmq@5.48.1: dependencies: cron-parser: 4.9.0 ioredis: 5.6.0 msgpackr: 1.11.2 node-abort-controller: 3.1.1 - semver: 7.6.3 - tslib: 2.7.0 + semver: 7.7.1 + tslib: 2.8.1 uuid: 9.0.1 transitivePeerDependencies: - supports-color @@ -16361,20 +16383,12 @@ snapshots: ccount@2.0.1: {} - chai@5.1.1: - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.1 - deep-eql: 5.0.2 - loupe: 3.1.2 - pathval: 2.0.0 - chai@5.2.0: dependencies: assertion-error: 2.0.1 check-error: 2.1.1 deep-eql: 5.0.2 - loupe: 3.1.2 + loupe: 3.1.3 pathval: 2.0.0 chalk-template@1.1.0: @@ -16471,7 +16485,7 @@ snapshots: chownr@3.0.0: {} - chromatic@11.27.0: {} + chromatic@11.28.0: {} ci-info@3.7.1: {} @@ -16691,6 +16705,21 @@ snapshots: - supports-color - ts-node + create-jest@29.7.0(@types/node@22.14.0): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@22.14.0) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + cron-parser@4.9.0: dependencies: luxon: 3.3.0 @@ -16895,6 +16924,8 @@ snapshots: dayjs@1.11.10: {} + dayjs@1.11.13: {} + de-indent@1.0.2: {} debug@2.6.9: @@ -17118,7 +17149,7 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 - dotenv@16.0.3: {} + dotenv@16.5.0: {} dunder-proto@1.0.1: dependencies: @@ -17334,34 +17365,6 @@ snapshots: '@esbuild/win32-ia32': 0.25.0 '@esbuild/win32-x64': 0.25.0 - esbuild@0.25.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.1 - '@esbuild/android-arm': 0.25.1 - '@esbuild/android-arm64': 0.25.1 - '@esbuild/android-x64': 0.25.1 - '@esbuild/darwin-arm64': 0.25.1 - '@esbuild/darwin-x64': 0.25.1 - '@esbuild/freebsd-arm64': 0.25.1 - '@esbuild/freebsd-x64': 0.25.1 - '@esbuild/linux-arm': 0.25.1 - '@esbuild/linux-arm64': 0.25.1 - '@esbuild/linux-ia32': 0.25.1 - '@esbuild/linux-loong64': 0.25.1 - '@esbuild/linux-mips64el': 0.25.1 - '@esbuild/linux-ppc64': 0.25.1 - '@esbuild/linux-riscv64': 0.25.1 - '@esbuild/linux-s390x': 0.25.1 - '@esbuild/linux-x64': 0.25.1 - '@esbuild/netbsd-arm64': 0.25.1 - '@esbuild/netbsd-x64': 0.25.1 - '@esbuild/openbsd-arm64': 0.25.1 - '@esbuild/openbsd-x64': 0.25.1 - '@esbuild/sunos-x64': 0.25.1 - '@esbuild/win32-arm64': 0.25.1 - '@esbuild/win32-ia32': 0.25.1 - '@esbuild/win32-x64': 0.25.1 - esbuild@0.25.2: optionalDependencies: '@esbuild/aix-ppc64': 0.25.2 @@ -17441,11 +17444,11 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@9.23.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.29.1(eslint@9.23.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.23.0): dependencies: debug: 3.2.7(supports-color@8.1.1) optionalDependencies: - '@typescript-eslint/parser': 8.27.0(eslint@9.23.0)(typescript@5.8.2) + '@typescript-eslint/parser': 8.29.1(eslint@9.23.0)(typescript@5.8.3) eslint: 9.23.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: @@ -17480,7 +17483,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0)(typescript@5.8.2))(eslint@9.23.0): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.29.1(eslint@9.23.0)(typescript@5.8.3))(eslint@9.23.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -17491,7 +17494,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.23.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@9.23.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.29.1(eslint@9.23.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.23.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -17503,13 +17506,13 @@ snapshots: string.prototype.trimend: 1.0.8 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.27.0(eslint@9.23.0)(typescript@5.8.2) + '@typescript-eslint/parser': 8.29.1(eslint@9.23.0)(typescript@5.8.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-vue@10.0.0(eslint@9.23.0)(vue-eslint-parser@10.1.1(eslint@9.23.0)): + eslint-plugin-vue@10.0.0(eslint@9.23.0)(vue-eslint-parser@10.1.3(eslint@9.23.0)): dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@9.23.0) eslint: 9.23.0 @@ -17517,16 +17520,11 @@ snapshots: nth-check: 2.1.1 postcss-selector-parser: 6.0.16 semver: 7.6.3 - vue-eslint-parser: 10.1.1(eslint@9.23.0) + vue-eslint-parser: 10.1.3(eslint@9.23.0) xml-name-validator: 4.0.0 eslint-rule-docs@1.1.235: {} - eslint-scope@8.2.0: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - eslint-scope@8.3.0: dependencies: esrecurse: 4.3.0 @@ -17549,7 +17547,7 @@ snapshots: '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.2 - '@types/estree': 1.0.6 + '@types/estree': 1.0.7 '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 @@ -17589,7 +17587,7 @@ snapshots: '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.2 - '@types/estree': 1.0.6 + '@types/estree': 1.0.7 '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 @@ -17638,7 +17636,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.7 esutils@2.0.3: {} @@ -17917,7 +17915,7 @@ snapshots: raw-body: 3.0.0 secure-json-parse: 2.7.0 - fastify@5.2.1: + fastify@5.3.2: dependencies: '@fastify/ajv-compiler': 4.0.1 '@fastify/error': 4.0.0 @@ -17929,10 +17927,10 @@ snapshots: find-my-way: 9.1.0 light-my-request: 6.1.0 pino: 9.2.0 - process-warning: 4.0.0 + process-warning: 5.0.0 rfdc: 1.4.1 - secure-json-parse: 3.0.2 - semver: 7.6.3 + secure-json-parse: 4.0.0 + semver: 7.7.1 toad-cache: 3.7.0 fastq@1.17.1: @@ -17948,6 +17946,10 @@ snapshots: pend: 1.2.0 optional: true + fdir@6.4.4(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + feed@4.2.2: dependencies: xml-js: 1.6.11 @@ -18313,7 +18315,7 @@ snapshots: p-cancelable: 3.0.0 responselike: 3.0.0 - got@14.4.6: + got@14.4.7: dependencies: '@sindresorhus/is': 7.0.1 '@szmarczak/http-timer': 5.0.1 @@ -18864,11 +18866,6 @@ snapshots: transitivePeerDependencies: - supports-color - istanbul-reports@3.1.6: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - istanbul-reports@3.1.7: dependencies: html-escaper: 2.0.2 @@ -18906,7 +18903,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.13.15 + '@types/node': 22.14.0 chalk: 4.1.2 co: 4.6.0 dedent: 1.3.0 @@ -18964,6 +18961,25 @@ snapshots: - supports-color - ts-node + jest-cli@29.7.0(@types/node@22.14.0): + dependencies: + '@jest/core': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@22.14.0) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@22.14.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest-config@29.7.0(@types/node@22.13.10): dependencies: '@babel/core': 7.23.5 @@ -19024,6 +19040,36 @@ snapshots: - babel-plugin-macros - supports-color + jest-config@29.7.0(@types/node@22.14.0): + dependencies: + '@babel/core': 7.23.5 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.23.5) + chalk: 4.1.2 + ci-info: 3.7.1 + deepmerge: 4.2.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 22.14.0 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-diff@29.7.0: dependencies: chalk: 4.1.2 @@ -19048,7 +19094,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.13.15 + '@types/node': 22.14.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -19065,7 +19111,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.6 - '@types/node': 22.13.15 + '@types/node': 22.14.0 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -19104,7 +19150,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.13.10 + '@types/node': 22.14.0 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -19139,7 +19185,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.13.15 + '@types/node': 22.14.0 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -19167,7 +19213,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.13.15 + '@types/node': 22.14.0 chalk: 4.1.2 cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 @@ -19213,7 +19259,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.13.15 + '@types/node': 22.14.0 chalk: 4.1.2 ci-info: 3.7.1 graceful-fs: 4.2.11 @@ -19232,7 +19278,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.13.15 + '@types/node': 22.14.0 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -19246,7 +19292,7 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 22.13.15 + '@types/node': 22.14.0 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -19275,6 +19321,18 @@ snapshots: - supports-color - ts-node + jest@29.7.0(@types/node@22.14.0): + dependencies: + '@jest/core': 29.7.0 + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@22.14.0) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jju@1.4.0: {} joi@17.11.0: @@ -19578,8 +19636,6 @@ snapshots: longest-streak@3.1.0: {} - loupe@3.1.2: {} - loupe@3.1.3: {} lowercase-keys@3.0.0: {} @@ -19624,7 +19680,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.6.3 + semver: 7.7.1 make-error@1.3.6: {} @@ -20156,17 +20212,17 @@ snapshots: optionalDependencies: msgpackr-extract: 3.0.2 - msw-storybook-addon@2.0.4(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2)): + msw-storybook-addon@2.0.4(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3)): dependencies: is-node-process: 1.2.0 - msw: 2.7.3(@types/node@22.13.11)(typescript@5.8.2) + msw: 2.7.3(@types/node@22.14.0)(typescript@5.8.3) - msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2): + msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3): dependencies: '@bundled-es-modules/cookie': 2.0.1 '@bundled-es-modules/statuses': 1.0.1 '@bundled-es-modules/tough-cookie': 0.1.6 - '@inquirer/confirm': 5.0.2(@types/node@22.13.11) + '@inquirer/confirm': 5.0.2(@types/node@22.14.0) '@mswjs/interceptors': 0.37.6 '@open-draft/deferred-promise': 2.2.0 '@open-draft/until': 2.1.0 @@ -20182,7 +20238,7 @@ snapshots: type-fest: 4.27.0 yargs: 17.7.2 optionalDependencies: - typescript: 5.8.2 + typescript: 5.8.3 transitivePeerDependencies: - '@types/node' @@ -20508,9 +20564,9 @@ snapshots: ospath@1.2.2: optional: true - otpauth@9.3.6: + otpauth@9.4.0: dependencies: - '@noble/hashes': 1.6.1 + '@noble/hashes': 1.7.1 outvariant@1.4.3: {} @@ -20614,7 +20670,7 @@ snapshots: path-scurry@1.11.1: dependencies: - lru-cache: 10.2.2 + lru-cache: 10.4.3 minipass: 7.1.2 path-scurry@2.0.0: @@ -21023,6 +21079,8 @@ snapshots: process-warning@4.0.0: {} + process-warning@5.0.0: {} + process@0.11.10: {} promise-limit@2.7.0: {} @@ -21220,9 +21278,9 @@ snapshots: transitivePeerDependencies: - supports-color - react-docgen-typescript@2.2.2(typescript@5.8.2): + react-docgen-typescript@2.2.2(typescript@5.8.3): dependencies: - typescript: 5.8.2 + typescript: 5.8.3 react-docgen@7.0.1: dependencies: @@ -21239,16 +21297,16 @@ snapshots: transitivePeerDependencies: - supports-color - react-dom@19.0.0(react@19.0.0): + react-dom@19.1.0(react@19.1.0): dependencies: - react: 19.0.0 - scheduler: 0.25.0 + react: 19.1.0 + scheduler: 0.26.0 react-is@17.0.2: {} react-is@18.2.0: {} - react@19.0.0: {} + react@19.1.0: {} read-pkg-up@7.0.1: dependencies: @@ -21300,7 +21358,7 @@ snapshots: esprima: 4.0.1 source-map: 0.6.1 tiny-invariant: 1.3.3 - tslib: 2.7.0 + tslib: 2.8.1 reconnecting-websocket@4.4.0: {} @@ -21311,6 +21369,10 @@ snapshots: redis-errors@1.2.0: {} + redis-info@3.1.0: + dependencies: + lodash: 4.17.21 + redis-lock@0.1.4: {} redis-parser@3.0.0: @@ -21439,29 +21501,30 @@ snapshots: dependencies: glob: 10.3.10 - rollup@4.36.0: + rollup@4.39.0: dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.7 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.36.0 - '@rollup/rollup-android-arm64': 4.36.0 - '@rollup/rollup-darwin-arm64': 4.36.0 - '@rollup/rollup-darwin-x64': 4.36.0 - '@rollup/rollup-freebsd-arm64': 4.36.0 - '@rollup/rollup-freebsd-x64': 4.36.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.36.0 - '@rollup/rollup-linux-arm-musleabihf': 4.36.0 - '@rollup/rollup-linux-arm64-gnu': 4.36.0 - '@rollup/rollup-linux-arm64-musl': 4.36.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.36.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.36.0 - '@rollup/rollup-linux-riscv64-gnu': 4.36.0 - '@rollup/rollup-linux-s390x-gnu': 4.36.0 - '@rollup/rollup-linux-x64-gnu': 4.36.0 - '@rollup/rollup-linux-x64-musl': 4.36.0 - '@rollup/rollup-win32-arm64-msvc': 4.36.0 - '@rollup/rollup-win32-ia32-msvc': 4.36.0 - '@rollup/rollup-win32-x64-msvc': 4.36.0 + '@rollup/rollup-android-arm-eabi': 4.39.0 + '@rollup/rollup-android-arm64': 4.39.0 + '@rollup/rollup-darwin-arm64': 4.39.0 + '@rollup/rollup-darwin-x64': 4.39.0 + '@rollup/rollup-freebsd-arm64': 4.39.0 + '@rollup/rollup-freebsd-x64': 4.39.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.39.0 + '@rollup/rollup-linux-arm-musleabihf': 4.39.0 + '@rollup/rollup-linux-arm64-gnu': 4.39.0 + '@rollup/rollup-linux-arm64-musl': 4.39.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.39.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.39.0 + '@rollup/rollup-linux-riscv64-gnu': 4.39.0 + '@rollup/rollup-linux-riscv64-musl': 4.39.0 + '@rollup/rollup-linux-s390x-gnu': 4.39.0 + '@rollup/rollup-linux-x64-gnu': 4.39.0 + '@rollup/rollup-linux-x64-musl': 4.39.0 + '@rollup/rollup-win32-arm64-msvc': 4.39.0 + '@rollup/rollup-win32-ia32-msvc': 4.39.0 + '@rollup/rollup-win32-x64-msvc': 4.39.0 fsevents: 2.3.3 rrweb-cssom@0.8.0: {} @@ -21513,7 +21576,7 @@ snapshots: parse-srcset: 1.0.2 postcss: 8.5.3 - sass@1.86.0: + sass@1.86.3: dependencies: chokidar: 4.0.3 immutable: 5.0.3 @@ -21527,12 +21590,14 @@ snapshots: dependencies: xmlchars: 2.2.0 - scheduler@0.25.0: {} + scheduler@0.26.0: {} secure-json-parse@2.7.0: {} secure-json-parse@3.0.2: {} + secure-json-parse@4.0.0: {} + seedrandom@3.0.5: {} seek-bzip@2.0.0: @@ -21617,31 +21682,32 @@ snapshots: inherits: 2.0.4 safe-buffer: 5.2.1 - sharp@0.33.5: + sharp@0.34.1: dependencies: color: 4.2.3 detect-libc: 2.0.3 - semver: 7.6.3 + semver: 7.7.1 optionalDependencies: - '@img/sharp-darwin-arm64': 0.33.5 - '@img/sharp-darwin-x64': 0.33.5 - '@img/sharp-libvips-darwin-arm64': 1.0.4 - '@img/sharp-libvips-darwin-x64': 1.0.4 - '@img/sharp-libvips-linux-arm': 1.0.5 - '@img/sharp-libvips-linux-arm64': 1.0.4 - '@img/sharp-libvips-linux-s390x': 1.0.4 - '@img/sharp-libvips-linux-x64': 1.0.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 - '@img/sharp-libvips-linuxmusl-x64': 1.0.4 - '@img/sharp-linux-arm': 0.33.5 - '@img/sharp-linux-arm64': 0.33.5 - '@img/sharp-linux-s390x': 0.33.5 - '@img/sharp-linux-x64': 0.33.5 - '@img/sharp-linuxmusl-arm64': 0.33.5 - '@img/sharp-linuxmusl-x64': 0.33.5 - '@img/sharp-wasm32': 0.33.5 - '@img/sharp-win32-ia32': 0.33.5 - '@img/sharp-win32-x64': 0.33.5 + '@img/sharp-darwin-arm64': 0.34.1 + '@img/sharp-darwin-x64': 0.34.1 + '@img/sharp-libvips-darwin-arm64': 1.1.0 + '@img/sharp-libvips-darwin-x64': 1.1.0 + '@img/sharp-libvips-linux-arm': 1.1.0 + '@img/sharp-libvips-linux-arm64': 1.1.0 + '@img/sharp-libvips-linux-ppc64': 1.1.0 + '@img/sharp-libvips-linux-s390x': 1.1.0 + '@img/sharp-libvips-linux-x64': 1.1.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 + '@img/sharp-libvips-linuxmusl-x64': 1.1.0 + '@img/sharp-linux-arm': 0.34.1 + '@img/sharp-linux-arm64': 0.34.1 + '@img/sharp-linux-s390x': 0.34.1 + '@img/sharp-linux-x64': 0.34.1 + '@img/sharp-linuxmusl-arm64': 0.34.1 + '@img/sharp-linuxmusl-x64': 0.34.1 + '@img/sharp-wasm32': 0.34.1 + '@img/sharp-win32-ia32': 0.34.1 + '@img/sharp-win32-x64': 0.34.1 shebang-command@2.0.0: dependencies: @@ -21656,14 +21722,14 @@ snapshots: vscode-oniguruma: 1.7.0 vscode-textmate: 8.0.0 - shiki@3.2.1: + shiki@3.2.2: dependencies: - '@shikijs/core': 3.2.1 - '@shikijs/engine-javascript': 3.2.1 - '@shikijs/engine-oniguruma': 3.2.1 - '@shikijs/langs': 3.2.1 - '@shikijs/themes': 3.2.1 - '@shikijs/types': 3.2.1 + '@shikijs/core': 3.2.2 + '@shikijs/engine-javascript': 3.2.2 + '@shikijs/engine-oniguruma': 3.2.2 + '@shikijs/langs': 3.2.2 + '@shikijs/themes': 3.2.2 + '@shikijs/types': 3.2.2 '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 @@ -21941,22 +22007,22 @@ snapshots: dependencies: internal-slot: 1.0.5 - storybook-addon-misskey-theme@https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.6.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/components@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/core-events@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/manager-api@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/preview-api@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/theming@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/types@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + storybook-addon-misskey-theme@https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/components@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/core-events@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/manager-api@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/preview-api@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/theming@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/types@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - '@storybook/blocks': 8.6.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/components': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/core-events': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/manager-api': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/preview-api': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/theming': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/types': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/blocks': 8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/components': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/core-events': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/manager-api': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/preview-api': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/theming': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/types': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) optionalDependencies: - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) - storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5): + storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5): dependencies: - '@storybook/core': 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(utf-8-validate@6.0.5) + '@storybook/core': 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(utf-8-validate@6.0.5) optionalDependencies: prettier: 3.5.3 transitivePeerDependencies: @@ -22212,7 +22278,7 @@ snapshots: dependencies: real-require: 0.2.0 - three@0.174.0: {} + three@0.175.0: {} throttle-debounce@5.0.2: {} @@ -22229,6 +22295,11 @@ snapshots: tinyexec@0.3.2: {} + tinyglobby@0.2.13: + dependencies: + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + tinypool@1.0.2: {} tinyrainbow@1.2.0: {} @@ -22304,15 +22375,19 @@ snapshots: dependencies: typescript: 5.8.2 + ts-api-utils@2.1.0(typescript@5.8.3): + dependencies: + typescript: 5.8.3 + ts-case-convert@2.1.0: {} ts-dedent@2.2.0: {} - ts-jest@29.1.2(@babel/core@7.23.5)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.5))(esbuild@0.25.0)(jest@29.7.0(@types/node@22.13.15))(typescript@5.1.6): + ts-jest@29.1.2(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(esbuild@0.25.0)(jest@29.7.0(@types/node@22.13.10))(typescript@5.1.6): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.13.15) + jest: 29.7.0(@types/node@22.13.10) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -22321,17 +22396,18 @@ snapshots: typescript: 5.1.6 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.23.5 + '@babel/core': 7.24.7 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.23.5) + babel-jest: 29.7.0(@babel/core@7.24.7) esbuild: 0.25.0 ts-map@1.0.3: {} - tsc-alias@1.8.11: + tsc-alias@1.8.15: dependencies: chokidar: 4.0.3 commander: 9.5.0 + get-tsconfig: 4.10.0 globby: 11.1.0 mylas: 2.1.13 normalize-path: 3.0.0 @@ -22368,7 +22444,7 @@ snapshots: tsx@4.19.3: dependencies: - esbuild: 0.25.1 + esbuild: 0.25.2 get-tsconfig: 4.10.0 optionalDependencies: fsevents: 2.3.3 @@ -22444,20 +22520,20 @@ snapshots: shiki: 0.14.7 typescript: 5.1.6 - typeorm@0.3.21(ioredis@5.6.0)(pg@8.14.1)(reflect-metadata@0.2.2): + typeorm@0.3.22(ioredis@5.6.0)(pg@8.14.1)(reflect-metadata@0.2.2): dependencies: '@sqltools/formatter': 1.2.5 ansis: 3.17.0 app-root-path: 3.1.0 buffer: 6.0.3 - dayjs: 1.11.10 - debug: 4.3.7(supports-color@8.1.1) - dotenv: 16.0.3 + dayjs: 1.11.13 + debug: 4.4.0(supports-color@8.1.1) + dotenv: 16.5.0 glob: 10.4.5 reflect-metadata: 0.2.2 sha.js: 2.4.11 sql-highlight: 6.0.0 - tslib: 2.7.0 + tslib: 2.8.1 uuid: 11.1.0 yargs: 17.7.2 optionalDependencies: @@ -22470,6 +22546,8 @@ snapshots: typescript@5.8.2: {} + typescript@5.8.3: {} + uid2@0.0.4: {} uid@2.0.2: @@ -22496,6 +22574,8 @@ snapshots: undici-types@6.20.0: {} + undici-types@6.21.0: {} + undici@5.28.2: dependencies: '@fastify/busboy': 2.1.0 @@ -22614,13 +22694,13 @@ snapshots: uuid@9.0.1: {} - v-code-diff@1.13.1(vue@3.5.13(typescript@5.8.2)): + v-code-diff@1.13.1(vue@3.5.13(typescript@5.8.3)): dependencies: diff: 5.2.0 diff-match-patch: 1.0.5 highlight.js: 11.10.0 - vue: 3.5.13(typescript@5.8.2) - vue-demi: 0.14.7(vue@3.5.13(typescript@5.8.2)) + vue: 3.5.13(typescript@5.8.3) + vue-demi: 0.14.7(vue@3.5.13(typescript@5.8.3)) v8-to-istanbul@9.2.0: dependencies: @@ -22654,13 +22734,13 @@ snapshots: unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 - vite-node@3.0.9(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3): + vite-node@3.1.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3): dependencies: cac: 6.7.14 debug: 4.4.0(supports-color@8.1.1) es-module-lexer: 1.6.0 pathe: 2.0.3 - vite: 6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) + vite: 6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) transitivePeerDependencies: - '@types/node' - jiti @@ -22677,31 +22757,34 @@ snapshots: vite-plugin-turbosnap@1.0.3: {} - vite@6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3): + vite@6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3): dependencies: - esbuild: 0.25.1 + esbuild: 0.25.2 + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 postcss: 8.5.3 - rollup: 4.36.0 + rollup: 4.39.0 + tinyglobby: 0.2.13 optionalDependencies: - '@types/node': 22.13.11 + '@types/node': 22.14.0 fsevents: 2.3.3 - sass: 1.86.0 + sass: 1.86.3 terser: 5.39.0 tsx: 4.19.3 - vitest-fetch-mock@0.4.5(vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.13.11)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)): + vitest-fetch-mock@0.4.5(vitest@3.1.1(@types/debug@4.1.12)(@types/node@22.14.0)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3))(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3)): dependencies: - vitest: 3.0.9(@types/debug@4.1.12)(@types/node@22.13.11)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) + vitest: 3.1.1(@types/debug@4.1.12)(@types/node@22.14.0)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3))(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) - vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.13.11)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3): + vitest@3.1.1(@types/debug@4.1.12)(@types/node@22.14.0)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3))(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3): dependencies: - '@vitest/expect': 3.0.9 - '@vitest/mocker': 3.0.9(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(vite@6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)) - '@vitest/pretty-format': 3.0.9 - '@vitest/runner': 3.0.9 - '@vitest/snapshot': 3.0.9 - '@vitest/spy': 3.0.9 - '@vitest/utils': 3.0.9 + '@vitest/expect': 3.1.1 + '@vitest/mocker': 3.1.1(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3))(vite@6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3)) + '@vitest/pretty-format': 3.1.2 + '@vitest/runner': 3.1.1 + '@vitest/snapshot': 3.1.1 + '@vitest/spy': 3.1.1 + '@vitest/utils': 3.1.1 chai: 5.2.0 debug: 4.4.0(supports-color@8.1.1) expect-type: 1.2.0 @@ -22712,12 +22795,12 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) - vite-node: 3.0.9(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) + vite: 6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) + vite-node: 3.1.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 - '@types/node': 22.13.11 + '@types/node': 22.14.0 happy-dom: 17.4.4 jsdom: 26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5) transitivePeerDependencies: @@ -22734,15 +22817,15 @@ snapshots: - tsx - yaml - vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.13.11)(happy-dom@17.4.4)(jsdom@26.0.0)(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3): + vitest@3.1.1(@types/debug@4.1.12)(@types/node@22.14.0)(happy-dom@17.4.4)(jsdom@26.0.0)(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3))(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3): dependencies: - '@vitest/expect': 3.0.9 - '@vitest/mocker': 3.0.9(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(vite@6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)) - '@vitest/pretty-format': 3.0.9 - '@vitest/runner': 3.0.9 - '@vitest/snapshot': 3.0.9 - '@vitest/spy': 3.0.9 - '@vitest/utils': 3.0.9 + '@vitest/expect': 3.1.1 + '@vitest/mocker': 3.1.1(msw@2.7.3(@types/node@22.14.0)(typescript@5.8.3))(vite@6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3)) + '@vitest/pretty-format': 3.1.2 + '@vitest/runner': 3.1.1 + '@vitest/snapshot': 3.1.1 + '@vitest/spy': 3.1.1 + '@vitest/utils': 3.1.1 chai: 5.2.0 debug: 4.4.0(supports-color@8.1.1) expect-type: 1.2.0 @@ -22753,12 +22836,12 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 6.2.4(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) - vite-node: 3.0.9(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3) + vite: 6.3.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) + vite-node: 3.1.1(@types/node@22.14.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.19.3) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 - '@types/node': 22.13.11 + '@types/node': 22.14.0 happy-dom: 17.4.4 jsdom: 26.0.0 transitivePeerDependencies: @@ -22782,7 +22865,7 @@ snapshots: vscode-languageclient@9.0.1: dependencies: minimatch: 5.1.6 - semver: 7.6.3 + semver: 7.7.1 vscode-languageserver-protocol: 3.17.5 vscode-languageserver-protocol@3.17.5: @@ -22804,30 +22887,32 @@ snapshots: vscode-uri@3.0.8: {} - vue-component-meta@2.0.16(typescript@5.8.2): + vue-component-meta@2.0.16(typescript@5.8.3): dependencies: '@volar/typescript': 2.2.0 - '@vue/language-core': 2.0.16(typescript@5.8.2) + '@vue/language-core': 2.0.16(typescript@5.8.3) path-browserify: 1.0.1 vue-component-type-helpers: 2.0.16 optionalDependencies: - typescript: 5.8.2 + typescript: 5.8.3 vue-component-type-helpers@1.8.4: {} vue-component-type-helpers@2.0.16: {} + vue-component-type-helpers@2.2.10: {} + vue-component-type-helpers@2.2.8: {} - vue-demi@0.14.7(vue@3.5.13(typescript@5.8.2)): + vue-demi@0.14.7(vue@3.5.13(typescript@5.8.3)): dependencies: - vue: 3.5.13(typescript@5.8.2) + vue: 3.5.13(typescript@5.8.3) - vue-docgen-api@4.75.1(vue@3.5.13(typescript@5.8.2)): + vue-docgen-api@4.75.1(vue@3.5.13(typescript@5.8.3)): dependencies: '@babel/parser': 7.25.7 '@babel/types': 7.25.7 - '@vue/compiler-dom': 3.5.12 + '@vue/compiler-dom': 3.5.13 '@vue/compiler-sfc': 3.5.13 ast-types: 0.16.1 hash-sum: 2.0.0 @@ -22835,51 +22920,51 @@ snapshots: pug: 3.0.3 recast: 0.23.6 ts-map: 1.0.3 - vue: 3.5.13(typescript@5.8.2) - vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.5.13(typescript@5.8.2)) + vue: 3.5.13(typescript@5.8.3) + vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.5.13(typescript@5.8.3)) - vue-eslint-parser@10.1.1(eslint@9.23.0): + vue-eslint-parser@10.1.3(eslint@9.23.0): dependencies: debug: 4.4.0(supports-color@8.1.1) eslint: 9.23.0 - eslint-scope: 8.2.0 + eslint-scope: 8.3.0 eslint-visitor-keys: 4.2.0 espree: 10.3.0 esquery: 1.6.0 lodash: 4.17.21 - semver: 7.6.3 + semver: 7.7.1 transitivePeerDependencies: - supports-color - vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.5.13(typescript@5.8.2)): + vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.5.13(typescript@5.8.3)): dependencies: - vue: 3.5.13(typescript@5.8.2) + vue: 3.5.13(typescript@5.8.3) vue-template-compiler@2.7.14: dependencies: de-indent: 1.0.2 he: 1.2.0 - vue-tsc@2.2.8(typescript@5.8.2): + vue-tsc@2.2.8(typescript@5.8.3): dependencies: '@volar/typescript': 2.4.12 - '@vue/language-core': 2.2.8(typescript@5.8.2) - typescript: 5.8.2 + '@vue/language-core': 2.2.8(typescript@5.8.3) + typescript: 5.8.3 - vue@3.5.13(typescript@5.8.2): + vue@3.5.13(typescript@5.8.3): dependencies: '@vue/compiler-dom': 3.5.13 '@vue/compiler-sfc': 3.5.13 '@vue/runtime-dom': 3.5.13 - '@vue/server-renderer': 3.5.13(vue@3.5.13(typescript@5.8.2)) + '@vue/server-renderer': 3.5.13(vue@3.5.13(typescript@5.8.3)) '@vue/shared': 3.5.13 optionalDependencies: - typescript: 5.8.2 + typescript: 5.8.3 - vuedraggable@4.1.0(vue@3.5.13(typescript@5.8.2)): + vuedraggable@4.1.0(vue@3.5.13(typescript@5.8.3)): dependencies: sortablejs: 1.14.0 - vue: 3.5.13(typescript@5.8.2) + vue: 3.5.13(typescript@5.8.3) w3c-xmlserializer@5.0.0: dependencies: diff --git a/scripts/dependency-patches/vite.patch b/scripts/dependency-patches/vite.patch deleted file mode 100644 index 3fe28ea67e..0000000000 --- a/scripts/dependency-patches/vite.patch +++ /dev/null @@ -1,31 +0,0 @@ -diff --git a/dist/node/chunks/dep-DrOo5SEf.js b/dist/node/chunks/dep-DrOo5SEf.js -index 329e68bd27e55a56d815fa6b4de2d615a8c2b343..9d9f58e90ae836f80063b698e307fec436e53e07 100644 ---- a/dist/node/chunks/dep-DrOo5SEf.js -+++ b/dist/node/chunks/dep-DrOo5SEf.js -@@ -45971,7 +45971,7 @@ function importAnalysisPlugin(config) { - let isPartiallySelfAccepting = false; - const importedBindings = enablePartialAccept ? /* @__PURE__ */ new Map() : null; - const toAbsoluteUrl = (url) => path$d.posix.resolve(path$d.posix.dirname(importerModule.url), url); -- const normalizeUrl = async (url, pos, forceSkipImportAnalysis = false) => { -+ const normalizeUrl = async (url, pos, forceSkipImportAnalysis = false, stripBase2 = false) => { - url = stripBase(url, base); - let importerFile = importer; - if (depsOptimizer && moduleListContains(depsOptimizer.options.exclude, url)) { -@@ -46031,7 +46031,7 @@ function importAnalysisPlugin(config) { - e.pos = pos; - throw e; - } -- if (!ssr) url = joinUrlSegments(base, url); -+ if (!ssr && !stripBase2) url = joinUrlSegments(base, url); - return [url, resolved.id]; - }; - const orderedImportedUrls = new Array(imports.length); -@@ -46288,7 +46288,7 @@ See ${colors$1.blue( - const pluginImports = this._addedImports; - if (pluginImports) { - (await Promise.all( -- [...pluginImports].map((id) => normalizeUrl(id, 0, true)) -+ [...pluginImports].map((id) => normalizeUrl(id, 0, true, true)) - )).forEach(([url]) => importedUrls.add(url)); - } - if (ssr && importerModule.isSelfAccepting) { |