Merge branch 'develop' into perf-fe-lowres-time

This commit is contained in:
かっこかり 2025-08-31 20:29:58 +09:00 committed by GitHub
commit d68be1d0d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 259 additions and 542 deletions

View File

@ -1,3 +1,15 @@
## Unreleased
### General
-
### Client
- Enhance: 時刻計算のための基準値を一か所で管理するようにし、パフォーマンスを向上
### Server
-
## 2025.8.0 ## 2025.8.0
### Note ### Note
@ -6,14 +18,12 @@
### General ### General
- ノートを削除した際、関連するノートが同時に削除されないようになりました - ノートを削除した際、関連するノートが同時に削除されないようになりました
- APIで、「replyIdが存在しているのにreplyがnull」や「renoteIdが存在しているのにrenoteがnull」であるという、今までにはなかったパターンが表れることになります - APIで、「replyIdが存在しているのにreplyがnull」や「renoteIdが存在しているのにrenoteがnull」であるという、今までにはなかったパターンが表れることになります
- 定期的に参照されていない古いリモートの投稿を削除する機能が実装されました(コントロールパネル→パフォーマンス→Remote Notes Cleaning) - 定期的に古いリモートの投稿を削除する機能が実装されました
- 既存のサーバーでは**デフォルトでオフ**、新規サーバーでは**デフォルトでオン**になります - コントロールパネル→パフォーマンス→Remote Notes Cleaning で有効化できます
- データベースの肥大化を防止することが可能です - データベースの肥大化を防止することが可能です
- 既存のサーバーで当機能を有効化した場合は、処理量が多くなるため、一時的にストレージ使用量が増加する可能性があります。 - 既存のサーバーで当機能を有効化した場合は、処理量が多くなるため、一時的にストレージ使用量が増加する可能性があります。
- 増加量を抑えるには、最大処理継続時間をデフォルトより短くしてください。 - 増加量を抑えるには、最大処理継続時間をデフォルトより短くしてください。
- データベースサイズへの効果が見られない場合はautovacuumが有効になっているか確認してください - データベースサイズへの効果が見られない場合はautovacuumが有効になっているか確認してください
- ハイパーリンクによる参照は検知できないためリンク切れとなります。
- 現時点では、2023-10-01以前にクリップされたリモートのートは検知しないため削除対象となります。
- サーバーの初期設定が完了するまでは連合がオンにならないようになりました - サーバーの初期設定が完了するまでは連合がオンにならないようになりました
- 日本語における公開範囲名称の「ダイレクト」が「指名」に改称されました - 日本語における公開範囲名称の「ダイレクト」が「指名」に改称されました
- 実際の動作に即した名称になり、馴染みのない人でも理解しやすくなりました - 実際の動作に即した名称になり、馴染みのない人でも理解しやすくなりました
@ -46,10 +56,11 @@
- Enhance: トルコ語 (tr-TR) に対応 - Enhance: トルコ語 (tr-TR) に対応
- Enhance: 不必要な翻訳データを読み込まなくなり、パフォーマンスが向上しました - Enhance: 不必要な翻訳データを読み込まなくなり、パフォーマンスが向上しました
- Enhance: 画像エフェクトのパラメータ名の多言語対応 - Enhance: 画像エフェクトのパラメータ名の多言語対応
- Enhance: 依存ソフトウェアの更新
- Enhance: ートを非表示にする相対期間を1ヶ月単位で自由に指定できるように - Enhance: ートを非表示にする相対期間を1ヶ月単位で自由に指定できるように
- Enhance: メールアドレス確認画面のUIを改善 - Enhance: メールアドレス確認画面のUIを改善
- Enhance: 時刻計算のための基準値を一か所で管理するようにし、パフォーマンスを向上 - Enhance: アイコンのスクロール追従を無効化する際の適用範囲を強化
- Enhance: レンダリングパフォーマンスの向上
- Enhance: 依存ソフトウェアの更新
- Fix: 投稿フォームでファイルのアップロードが中止または失敗した際のハンドリングを修正 - Fix: 投稿フォームでファイルのアップロードが中止または失敗した際のハンドリングを修正
- Fix: 一部の設定検索結果が存在しないパスになる問題を修正 - Fix: 一部の設定検索結果が存在しないパスになる問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1171) (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1171)
@ -61,6 +72,7 @@
- Fix: ユーザーの前後ノートを閲覧する機能が動作しない問題を修正 - Fix: ユーザーの前後ノートを閲覧する機能が動作しない問題を修正
- Fix: 照会ダイアログでap/showでローカルユーザーを解決した際@username@nullに飛ばされる問題を修正 - Fix: 照会ダイアログでap/showでローカルユーザーを解決した際@username@nullに飛ばされる問題を修正
- Fix: アイコンのデコレーションを付ける際にデコレーションが表示されなくなる問題を修正 - Fix: アイコンのデコレーションを付ける際にデコレーションが表示されなくなる問題を修正
- Fix: タッチ操作時にマウスホバー時のユーザープレビューが開くことがある問題を修正
- Fix: 管理中アカウント一覧で正しい表示が行われない問題を修正 - Fix: 管理中アカウント一覧で正しい表示が行われない問題を修正
- Fix: lookupページでリモートURLを指定した際に正しく動作しない問題を修正 - Fix: lookupページでリモートURLを指定した際に正しく動作しない問題を修正
@ -77,6 +89,7 @@
- Fix: SystemWebhook設定でsecretを空に出来ない問題を修正 - Fix: SystemWebhook設定でsecretを空に出来ない問題を修正
- Fix: 削除されたユーザーがチャットメッセージにリアクションしている場合`chat/history`などでエラーになる問題を修正 - Fix: 削除されたユーザーがチャットメッセージにリアクションしている場合`chat/history`などでエラーになる問題を修正
- Fix: Pageのアイキャッチ画像をドライブから消してもPageごと消えないように - Fix: Pageのアイキャッチ画像をドライブから消してもPageごと消えないように
- Fix: タイムラインAPIの withRenotes: false 時のレスポンスを修正
## 2025.7.0 ## 2025.7.0

View File

@ -3120,7 +3120,6 @@ _serverSetupWizard:
youCanConfigureMoreFederationSettingsLater: "Les configuracions avançades, com especificar els servidors amb els quals es pot federar, es poden fer més tard." youCanConfigureMoreFederationSettingsLater: "Les configuracions avançades, com especificar els servidors amb els quals es pot federar, es poden fer més tard."
remoteContentsCleaning: "Neteja automàtica del contingut rebut" remoteContentsCleaning: "Neteja automàtica del contingut rebut"
remoteContentsCleaning_description: "Quan es comença a federar es rep un munt de contingut, quan s'activa la neteja automàtica el contingut antic que no es consulta serà eliminat del servidor, el que permet estalviar espai d'emmagatzematge." remoteContentsCleaning_description: "Quan es comença a federar es rep un munt de contingut, quan s'activa la neteja automàtica el contingut antic que no es consulta serà eliminat del servidor, el que permet estalviar espai d'emmagatzematge."
remoteContentsCleaning_description2: "Alguns mètodes de referència, com els enllaços, no poden ser detectats pel sistema."
adminInfo: "Informació de l'administrador " adminInfo: "Informació de l'administrador "
adminInfo_description: "Estableix la informació de l'administrador que es farà servir per rebre consultes." adminInfo_description: "Estableix la informació de l'administrador que es farà servir per rebre consultes."
adminInfo_mustBeFilled: "Aquesta informació ha de ser omplerta si el servidor té els registres oberts o la federació es troba activada." adminInfo_mustBeFilled: "Aquesta informació ha de ser omplerta si el servidor té els registres oberts o la federació es troba activada."

View File

@ -1668,6 +1668,7 @@ _serverSettings:
restartServerSetupWizardConfirm_text: "Some current settings will be reset." restartServerSetupWizardConfirm_text: "Some current settings will be reset."
entrancePageStyle: "Entrance page style" entrancePageStyle: "Entrance page style"
showTimelineForVisitor: "Show timeline" showTimelineForVisitor: "Show timeline"
showActivitiesForVisitor: "Show activities"
_userGeneratedContentsVisibilityForVisitor: _userGeneratedContentsVisibilityForVisitor:
all: "Everything is public" all: "Everything is public"
localOnly: "Only local content is published, remote content is kept private" localOnly: "Only local content is published, remote content is kept private"

View File

@ -3120,7 +3120,6 @@ _serverSetupWizard:
youCanConfigureMoreFederationSettingsLater: "Los ajustes avanzados, como la especificación de servidores federados, pueden configurarse más adelante." youCanConfigureMoreFederationSettingsLater: "Los ajustes avanzados, como la especificación de servidores federados, pueden configurarse más adelante."
remoteContentsCleaning: "Limpieza automática de los contenidos recibidos" remoteContentsCleaning: "Limpieza automática de los contenidos recibidos"
remoteContentsCleaning_description: "La federación puede dar lugar a un flujo continuo de contenido. Al habilitar la limpieza automática, se eliminará del servidor el contenido obsoleto y sin referencias para ahorrar espacio de almacenamiento." remoteContentsCleaning_description: "La federación puede dar lugar a un flujo continuo de contenido. Al habilitar la limpieza automática, se eliminará del servidor el contenido obsoleto y sin referencias para ahorrar espacio de almacenamiento."
remoteContentsCleaning_description2: "Ciertos métodos de referencia, como los hipervínculos, no pueden ser detectados por el sistema."
adminInfo: "Información del administrador" adminInfo: "Información del administrador"
adminInfo_description: "Establece la información del administrador para recibir consultas." adminInfo_description: "Establece la información del administrador para recibir consultas."
adminInfo_mustBeFilled: "Esta información debe ser introducida en el caso de registros abiertos o la federación esté activada." adminInfo_mustBeFilled: "Esta información debe ser introducida en el caso de registros abiertos o la federación esté activada."

8
locales/index.d.ts vendored
View File

@ -6531,7 +6531,7 @@ export interface Locale extends ILocale {
*/ */
"remoteNotesCleaning": string; "remoteNotesCleaning": string;
/** /**
* 稿 * 稿
*/ */
"remoteNotesCleaning_description": string; "remoteNotesCleaning_description": string;
/** /**
@ -12036,13 +12036,9 @@ export interface Locale extends ILocale {
*/ */
"remoteContentsCleaning": string; "remoteContentsCleaning": string;
/** /**
* *
*/ */
"remoteContentsCleaning_description": string; "remoteContentsCleaning_description": string;
/**
*
*/
"remoteContentsCleaning_description2": string;
/** /**
* *
*/ */

View File

@ -139,7 +139,7 @@ overwriteFromPinnedEmojis: "Sovrascrivi con le impostazioni globali"
reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere." reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere."
rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note" rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note"
attachCancel: "Rimuovi allegato" attachCancel: "Rimuovi allegato"
deleteFile: "File da Drive eliminato" deleteFile: "Elimina un file dal Drive"
markAsSensitive: "Segna come esplicito" markAsSensitive: "Segna come esplicito"
unmarkAsSensitive: "Non segnare come esplicito " unmarkAsSensitive: "Non segnare come esplicito "
enterFileName: "Nome del file" enterFileName: "Nome del file"
@ -2222,7 +2222,7 @@ _theme:
hashtag: "Hashtag" hashtag: "Hashtag"
mention: "Menzioni" mention: "Menzioni"
mentionMe: "Menzioni (di me)" mentionMe: "Menzioni (di me)"
renote: "Renota" renote: "Rinota"
modalBg: "Sfondo modale." modalBg: "Sfondo modale."
divider: "Interruzione di linea" divider: "Interruzione di linea"
scrollbarHandle: "Maniglie della barra di scorrimento" scrollbarHandle: "Maniglie della barra di scorrimento"
@ -2663,7 +2663,7 @@ _notification:
createToken: "È stato creato un token di accesso" createToken: "È stato creato un token di accesso"
createTokenDescription: "In caso contrario, eliminare il token di accesso tramite ({text})." createTokenDescription: "In caso contrario, eliminare il token di accesso tramite ({text})."
_types: _types:
all: "Tutto" all: "Tutte"
note: "Nuove Note" note: "Nuove Note"
follow: "Follower" follow: "Follower"
mention: "Menzioni" mention: "Menzioni"
@ -2671,7 +2671,7 @@ _notification:
renote: "Rinota" renote: "Rinota"
quote: "Cita" quote: "Cita"
reaction: "Reazioni" reaction: "Reazioni"
pollEnded: "Sondaggio chiuso." pollEnded: "Sondaggio terminato"
receiveFollowRequest: "Richieste di follow in arrivo" receiveFollowRequest: "Richieste di follow in arrivo"
followRequestAccepted: "Richieste di follow accettate" followRequestAccepted: "Richieste di follow accettate"
roleAssigned: "Ruolo concesso" roleAssigned: "Ruolo concesso"
@ -2679,7 +2679,7 @@ _notification:
achievementEarned: "Risultato raggiunto" achievementEarned: "Risultato raggiunto"
exportCompleted: "Esportazione completata" exportCompleted: "Esportazione completata"
login: "Accessi" login: "Accessi"
createToken: "Creare un token di accesso" createToken: "Aggiunto un token di accesso"
test: "Notifiche di test" test: "Notifiche di test"
app: "Notifiche da applicazioni" app: "Notifiche da applicazioni"
_actions: _actions:
@ -2771,56 +2771,56 @@ _abuseReport:
notifiedWebhook: "Webhook da usare" notifiedWebhook: "Webhook da usare"
deleteConfirm: "Vuoi davvero rimuovere il destinatario della notifica?" deleteConfirm: "Vuoi davvero rimuovere il destinatario della notifica?"
_moderationLogTypes: _moderationLogTypes:
createRole: "Ruolo creato" createRole: "Crea un Ruolo"
deleteRole: "Ruolo eliminato" deleteRole: "Elimina un Ruolo"
updateRole: "Ruolo aggiornato" updateRole: "Modifica un ruolo"
assignRole: "Ruolo assegnato" assignRole: "Assegna un Ruolo"
unassignRole: "Ruolo disassegnato" unassignRole: "Toglie un Ruolo al Profilo"
suspend: "Sospensione" suspend: "Sospende"
unsuspend: "Sospensione rimossa" unsuspend: "Solleva la sospensione"
addCustomEmoji: "Emoji personalizzata aggiunta" addCustomEmoji: "Aggiunge Emoji personalizzata"
updateCustomEmoji: "Emoji personalizzata aggiornata" updateCustomEmoji: "Modifica Emoji personalizzata"
deleteCustomEmoji: "Emoji personalizzata eliminata" deleteCustomEmoji: "Elimina Emoji personalizzata"
updateServerSettings: "Impostazioni del server aggiornate" updateServerSettings: "Modifica le impostazioni del server"
updateUserNote: "Promemoria di moderazione aggiornato" updateUserNote: "Modifica un promemoria di moderazione"
deleteDriveFile: "File da Drive eliminato" deleteDriveFile: "Elimina un file dal Drive"
deleteNote: "Nota eliminata" deleteNote: "Elimina una Nota"
createGlobalAnnouncement: "Annuncio globale creato" createGlobalAnnouncement: "Crea un annuncio globale"
createUserAnnouncement: "Annuncio ai profili iscritti creato" createUserAnnouncement: "Crea un annuncio ai profili già iscritti"
updateGlobalAnnouncement: "Annuncio globale aggiornato" updateGlobalAnnouncement: "Modifica un annuncio globale"
updateUserAnnouncement: "Annuncio ai profili iscritti aggiornato" updateUserAnnouncement: "Modifica un annuncio ai profili già iscritti"
deleteGlobalAnnouncement: "Annuncio globale eliminato" deleteGlobalAnnouncement: "Elimina un annuncio globale"
deleteUserAnnouncement: "Annuncio ai profili iscritti eliminato" deleteUserAnnouncement: "Elimina un annuncio ai profili già iscritti"
resetPassword: "Password azzerata" resetPassword: "Azzera la password"
suspendRemoteInstance: "Istanza remota sospesa" suspendRemoteInstance: "Sospende una istanza remota"
unsuspendRemoteInstance: "Istanza remota riattivata" unsuspendRemoteInstance: "Riattiva una istanza remota"
updateRemoteInstanceNote: "Aggiornamento del promemoria di moderazione per il server remoto" updateRemoteInstanceNote: "Modifica il promemoria di moderazione per il server remoto"
markSensitiveDriveFile: "File nel Drive segnato come esplicito" markSensitiveDriveFile: "Aggiunge NSFW a un file nel Drive"
unmarkSensitiveDriveFile: "File nel Drive segnato come non esplicito" unmarkSensitiveDriveFile: "Toglie NSFW da un file nel Drive"
resolveAbuseReport: "Segnalazione risolta" resolveAbuseReport: "Risolve una segnalazione"
forwardAbuseReport: "Segnalazione inoltrata" forwardAbuseReport: "Inoltra una segnalazione"
updateAbuseReportNote: "Ha aggiornato la segnalazione" updateAbuseReportNote: "Modifica una segnalazione"
createInvitation: "Genera codice di invito" createInvitation: "Genera un codice di invito"
createAd: "Banner creato" createAd: "Aggiunge un Banner"
deleteAd: "Banner eliminato" deleteAd: "Elimina un Banner"
updateAd: "Banner aggiornato" updateAd: "Modifica un Banner"
createAvatarDecoration: "Creazione decorazione della foto profilo" createAvatarDecoration: "Crea una decorazione della foto profilo"
updateAvatarDecoration: "Aggiornamento decorazione foto profilo" updateAvatarDecoration: "Modifica una decorazione della foto profilo"
deleteAvatarDecoration: "Eliminazione decorazione della foto profilo" deleteAvatarDecoration: "Elimina una decorazione della foto profilo"
unsetUserAvatar: "Rimossa foto profilo" unsetUserAvatar: "Toglie una foto profilo"
unsetUserBanner: "Rimossa intestazione profilo" unsetUserBanner: "Toglie una immagine di intestazione profilo"
createSystemWebhook: "Crea un SystemWebhook" createSystemWebhook: "Aggiunge un System Webhook"
updateSystemWebhook: "Modifica SystemWebhook" updateSystemWebhook: "Modifica un System Webhook"
deleteSystemWebhook: "Elimina SystemWebhook" deleteSystemWebhook: "Elimina un System Webhook"
createAbuseReportNotificationRecipient: "Crea destinatario per le notifiche di segnalazioni" createAbuseReportNotificationRecipient: "Crea destinatario per le notifiche di segnalazioni"
updateAbuseReportNotificationRecipient: "Aggiorna destinatario notifiche di segnalazioni" updateAbuseReportNotificationRecipient: "Modifica un destinatario per le notifiche di segnalazioni"
deleteAbuseReportNotificationRecipient: "Elimina destinatario notifiche di segnalazioni" deleteAbuseReportNotificationRecipient: "Elimina un destinatario per le notifiche di segnalazioni"
deleteAccount: "Quando viene eliminato un profilo" deleteAccount: "Elimina un profilo"
deletePage: "Pagina eliminata" deletePage: "Elimina una Pagina"
deleteFlash: "Play eliminato" deleteFlash: "Elimina un Play"
deleteGalleryPost: "Eliminazione pubblicazione nella Galleria" deleteGalleryPost: "Elimina pubblicazione nella Galleria"
deleteChatRoom: "Elimina chat" deleteChatRoom: "Elimina una Chat"
updateProxyAccountDescription: "Aggiornata la descrizione del profilo proxy" updateProxyAccountDescription: "Aggiorna la descrizione del profilo proxy"
_fileViewer: _fileViewer:
title: "Dettagli del file" title: "Dettagli del file"
type: "Tipo di file" type: "Tipo di file"
@ -3120,7 +3120,6 @@ _serverSetupWizard:
youCanConfigureMoreFederationSettingsLater: "Puoi svolgere la configurazione avanzata anche dopo. Ad esempio specificando quali server possono federarsi." youCanConfigureMoreFederationSettingsLater: "Puoi svolgere la configurazione avanzata anche dopo. Ad esempio specificando quali server possono federarsi."
remoteContentsCleaning: "Pulizia automatica dei contenuti in arrivo" remoteContentsCleaning: "Pulizia automatica dei contenuti in arrivo"
remoteContentsCleaning_description: "Con la federazione funzionante, riceverai sempre più contenuti. Abilitando la pulizia automatica, i contenuti non referenziati e obsoleti verranno rimossi automaticamente dai tuoi server, risparmiando spazio di archiviazione." remoteContentsCleaning_description: "Con la federazione funzionante, riceverai sempre più contenuti. Abilitando la pulizia automatica, i contenuti non referenziati e obsoleti verranno rimossi automaticamente dai tuoi server, risparmiando spazio di archiviazione."
remoteContentsCleaning_description2: "Alcuni metodi di riferimento, come i collegamenti ipertestuali, non possono essere rilevati sul sistema."
adminInfo: "Informazioni sull'amministratore" adminInfo: "Informazioni sull'amministratore"
adminInfo_description: "Imposta le informazioni dell'amministratore utilizzate per accettare le richieste." adminInfo_description: "Imposta le informazioni dell'amministratore utilizzate per accettare le richieste."
adminInfo_mustBeFilled: "Questa operazione è necessaria su un server aperto o se è attiva la federazione." adminInfo_mustBeFilled: "Questa operazione è necessaria su un server aperto o se è attiva la federazione."

View File

@ -1660,7 +1660,7 @@ _serverSettings:
fanoutTimelineDbFallbackDescription: "有効にすると、タイムラインがキャッシュされていない場合にDBへ追加で問い合わせを行うフォールバック処理を行います。無効にすると、フォールバック処理を行わないことでさらにサーバーの負荷を軽減することができますが、タイムラインが取得できる範囲に制限が生じます。" fanoutTimelineDbFallbackDescription: "有効にすると、タイムラインがキャッシュされていない場合にDBへ追加で問い合わせを行うフォールバック処理を行います。無効にすると、フォールバック処理を行わないことでさらにサーバーの負荷を軽減することができますが、タイムラインが取得できる範囲に制限が生じます。"
reactionsBufferingDescription: "有効にすると、リアクション作成時のパフォーマンスが大幅に向上し、データベースへの負荷を軽減することが可能です。ただし、Redisのメモリ使用量は増加します。" reactionsBufferingDescription: "有効にすると、リアクション作成時のパフォーマンスが大幅に向上し、データベースへの負荷を軽減することが可能です。ただし、Redisのメモリ使用量は増加します。"
remoteNotesCleaning: "リモート投稿の自動クリーニング" remoteNotesCleaning: "リモート投稿の自動クリーニング"
remoteNotesCleaning_description: "有効にすると、参照されていない古いリモートの投稿を定期的にクリーンアップしてデータベースの肥大化を抑制します。" remoteNotesCleaning_description: "有効にすると、一定期間経過したリモートの投稿を定期的にクリーンアップしてデータベースの肥大化を抑制します。"
remoteNotesCleaningMaxProcessingDuration: "最大クリーニング処理継続時間" remoteNotesCleaningMaxProcessingDuration: "最大クリーニング処理継続時間"
remoteNotesCleaningExpiryDaysForEachNotes: "最低ノート保持日数" remoteNotesCleaningExpiryDaysForEachNotes: "最低ノート保持日数"
inquiryUrl: "問い合わせ先URL" inquiryUrl: "問い合わせ先URL"
@ -3217,8 +3217,7 @@ _serverSetupWizard:
doYouConnectToFediverse_description2: "Fediverseと接続することは「連合」とも呼ばれます。" doYouConnectToFediverse_description2: "Fediverseと接続することは「連合」とも呼ばれます。"
youCanConfigureMoreFederationSettingsLater: "連合可能なサーバーの指定など、高度な設定も後ほど可能です。" youCanConfigureMoreFederationSettingsLater: "連合可能なサーバーの指定など、高度な設定も後ほど可能です。"
remoteContentsCleaning: "リモートコンテンツの自動クリーニング" remoteContentsCleaning: "リモートコンテンツの自動クリーニング"
remoteContentsCleaning_description: "連合を行うと、継続して多くのコンテンツを受信します。自動クリーニングを有効にすると、参照されていない古くなったリモートコンテンツを自動でサーバーから削除し、ストレージを節約できます。" remoteContentsCleaning_description: "連合を行うと、継続して多くのコンテンツを受信します。自動クリーニングを有効にすると、一定期間経過したリモートコンテンツを自動でサーバーから削除し、ストレージを節約できます。"
remoteContentsCleaning_description2: "ローカル内リモートコンテンツへのハイパーリンクはリンク切れとなります。"
adminInfo: "管理者情報" adminInfo: "管理者情報"
adminInfo_description: "問い合わせを受け付けるために使用される管理者情報を設定します。" adminInfo_description: "問い合わせを受け付けるために使用される管理者情報を設定します。"
adminInfo_mustBeFilled: "オープンサーバー、または連合がオンの場合は必ず入力が必要です。" adminInfo_mustBeFilled: "オープンサーバー、または連合がオンの場合は必ず入力が必要です。"

View File

@ -3120,7 +3120,6 @@ _serverSetupWizard:
youCanConfigureMoreFederationSettingsLater: "나중에 연합 가능한 서버의 지정 등 고급 설정을 할 수 있습니다." youCanConfigureMoreFederationSettingsLater: "나중에 연합 가능한 서버의 지정 등 고급 설정을 할 수 있습니다."
remoteContentsCleaning: "리모트 콘텐츠 자동 정리" remoteContentsCleaning: "리모트 콘텐츠 자동 정리"
remoteContentsCleaning_description: "연합 중인 서버가 있는 경우, 리모트 서버에서 대단히 많은 콘텐츠를 받아오게 됩니다. 자동 정리 기능을 활성화하면, 오래되고 서버에서 더 이상 조회되지 않는 콘텐츠를 자동으로 서버에서 삭제하여, 스토리지를 절약할 수 있습니다." remoteContentsCleaning_description: "연합 중인 서버가 있는 경우, 리모트 서버에서 대단히 많은 콘텐츠를 받아오게 됩니다. 자동 정리 기능을 활성화하면, 오래되고 서버에서 더 이상 조회되지 않는 콘텐츠를 자동으로 서버에서 삭제하여, 스토리지를 절약할 수 있습니다."
remoteContentsCleaning_description2: "로컬 내 원격 콘텐츠로의 하이퍼링크는 깨진 링크로 됩니다."
adminInfo: "관리자 정보" adminInfo: "관리자 정보"
adminInfo_description: "문의 접수를 위해 사용되는 관리자 정보를 설정합니다." adminInfo_description: "문의 접수를 위해 사용되는 관리자 정보를 설정합니다."
adminInfo_mustBeFilled: "오픈 서버 혹은 연합이 켜져 있는 경우 반드시 입력해야 합니다." adminInfo_mustBeFilled: "오픈 서버 혹은 연합이 켜져 있는 경우 반드시 입력해야 합니다."

View File

@ -3120,7 +3120,6 @@ _serverSetupWizard:
youCanConfigureMoreFederationSettingsLater: "可在之后进行如哪些服务器可以进行联合等高级设置。" youCanConfigureMoreFederationSettingsLater: "可在之后进行如哪些服务器可以进行联合等高级设置。"
remoteContentsCleaning: "自动清理传入内容" remoteContentsCleaning: "自动清理传入内容"
remoteContentsCleaning_description: "加入联合后,服务器将持续接收大量内容。打开自动清理后,将自动删除无法找到的旧内容,可节省存储空间。" remoteContentsCleaning_description: "加入联合后,服务器将持续接收大量内容。打开自动清理后,将自动删除无法找到的旧内容,可节省存储空间。"
remoteContentsCleaning_description2: "如超链接之类的某些引用方法无法被系统检测到。"
adminInfo: "管理员信息" adminInfo: "管理员信息"
adminInfo_description: "设置用于接受询问的管理员信息。" adminInfo_description: "设置用于接受询问的管理员信息。"
adminInfo_mustBeFilled: "开放服务器或开启了联合的情况下必须输入。" adminInfo_mustBeFilled: "开放服务器或开启了联合的情况下必须输入。"

View File

@ -3120,7 +3120,6 @@ _serverSetupWizard:
youCanConfigureMoreFederationSettingsLater: "您可以在稍後進行更高級的設定,例如指定可以聯繫的伺服器等。\n" youCanConfigureMoreFederationSettingsLater: "您可以在稍後進行更高級的設定,例如指定可以聯繫的伺服器等。\n"
remoteContentsCleaning: "自動清理接收的內容" remoteContentsCleaning: "自動清理接收的內容"
remoteContentsCleaning_description: "進行聯邦後,會持續接收大量內容。啟用自動清理功能後,系統會自動從伺服器中刪除未被參照的過時內容,以節省儲存空間。" remoteContentsCleaning_description: "進行聯邦後,會持續接收大量內容。啟用自動清理功能後,系統會自動從伺服器中刪除未被參照的過時內容,以節省儲存空間。"
remoteContentsCleaning_description2: "有些引用方式系統上無法檢測到,例如超連結。"
adminInfo: "管理員資訊" adminInfo: "管理員資訊"
adminInfo_description: "設定用於接收查詢的管理者資訊。\n" adminInfo_description: "設定用於接收查詢的管理者資訊。\n"
adminInfo_mustBeFilled: "當設置為開放伺服器或啟用聯邦時,必須填寫此資訊。\n" adminInfo_mustBeFilled: "當設置為開放伺服器或啟用聯邦時,必須填寫此資訊。\n"

View File

@ -1,6 +1,6 @@
{ {
"name": "misskey", "name": "misskey",
"version": "2025.8.0-beta.5", "version": "2025.8.0",
"codename": "nasubi", "codename": "nasubi",
"repository": { "repository": {
"type": "git", "type": "git",
@ -67,6 +67,7 @@
}, },
"devDependencies": { "devDependencies": {
"@misskey-dev/eslint-plugin": "2.1.0", "@misskey-dev/eslint-plugin": "2.1.0",
"@types/js-yaml": "4.0.9",
"@types/node": "22.17.2", "@types/node": "22.17.2",
"@typescript-eslint/eslint-plugin": "8.40.0", "@typescript-eslint/eslint-plugin": "8.40.0",
"@typescript-eslint/parser": "8.40.0", "@typescript-eslint/parser": "8.40.0",

View File

@ -91,6 +91,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
qb.orWhere(new Brackets(qb => { qb.orWhere(new Brackets(qb => {
qb.where('note.text IS NOT NULL'); qb.where('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\''); qb.orWhere('note.fileIds != \'{}\'');
qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
})); }));
})); }));
} }

View File

@ -242,6 +242,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
qb.orWhere(new Brackets(qb => { qb.orWhere(new Brackets(qb => {
qb.orWhere('note.text IS NOT NULL'); qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\''); qb.orWhere('note.fileIds != \'{}\'');
qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
})); }));
})); }));
} }

View File

@ -223,6 +223,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
qb.orWhere(new Brackets(qb => { qb.orWhere(new Brackets(qb => {
qb.orWhere('note.text IS NOT NULL'); qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\''); qb.orWhere('note.fileIds != \'{}\'');
qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
})); }));
})); }));
} }

View File

@ -127,7 +127,7 @@ export function galleryPost(isSensitive = false) {
} }
} }
export function file(isSensitive = false) { export function file(isSensitive = false): entities.DriveFile {
return { return {
id: 'somefileid', id: 'somefileid',
createdAt: '2016-12-28T22:49:51.000Z', createdAt: '2016-12-28T22:49:51.000Z',

View File

@ -86,7 +86,7 @@ export function createAiScriptEnv(opts: { storageKey: string, token?: string })
throw new errors.AiScriptRuntimeError('expected param'); throw new errors.AiScriptRuntimeError('expected param');
} }
utils.assertObject(param); utils.assertObject(param);
return misskeyApi(ep.value, utils.valToJs(param) as object, actualToken).then(res => { return misskeyApi(ep.value as keyof Misskey.Endpoints, utils.valToJs(param) as object, actualToken).then(res => {
return utils.jsToVal(res); return utils.jsToVal(res);
}, err => { }, err => {
return values.ERROR('request_failed', utils.jsToVal(err)); return values.ERROR('request_failed', utils.jsToVal(err));

View File

@ -167,9 +167,13 @@ async function init() {
for (const user of usersRes) { for (const user of usersRes) {
if (users.value.has(user.id)) continue; if (users.value.has(user.id)) continue;
const account = accounts.find(a => a.id === user.id);
if (!account || account.token == null) continue;
users.value.set(user.id, { users.value.set(user.id, {
...user, ...user,
token: accounts.find(a => a.id === user.id)!.token, token: account.token,
}); });
} }
} }

View File

@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkCustomEmoji v-if="'isCustomEmoji' in emoji && emoji.isCustomEmoji" :name="emoji.emoji" :class="$style.emoji" :fallbackToImage="true"/> <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"/> <MkEmoji v-else :emoji="emoji.emoji" :class="$style.emoji"/>
<!-- eslint-disable-next-line vue/no-v-html --> <!-- eslint-disable-next-line vue/no-v-html -->
<span v-if="q" :class="$style.emojiName" v-html="sanitizeHtml(emoji.name.replace(q, `<b>${q}</b>`))"></span> <span v-if="q != null && typeof q === 'string'" :class="$style.emojiName" v-html="sanitizeHtml(emoji.name.replace(q, `<b>${q}</b>`))"></span>
<span v-else v-text="emoji.name"></span> <span v-else v-text="emoji.name"></span>
<span v-if="emoji.aliasOf" :class="$style.emojiAlias">({{ emoji.aliasOf }})</span> <span v-if="emoji.aliasOf" :class="$style.emojiAlias">({{ emoji.aliasOf }})</span>
</li> </li>
@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</li> </li>
</ol> </ol>
<ol v-else-if="type === 'mfmParam' && 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"> <li v-for="param in mfmParams" tabindex="-1" :class="$style.item" @click="completeMfmParam(param)" @keydown="onKeydown">
<span>{{ param }}</span> <span>{{ param }}</span>
</li> </li>
</ol> </ol>
@ -194,6 +194,11 @@ const mfmParams = ref<string[]>([]);
const select = ref(-1); const select = ref(-1);
const zIndex = os.claimZIndex('high'); const zIndex = os.claimZIndex('high');
function completeMfmParam(param: string) {
if (props.type !== 'mfmParam') throw new Error('Invalid type');
complete('mfmParam', props.q.params.toSpliced(-1, 1, param).join(','));
}
function complete<T extends keyof CompleteInfo>(type: T, value: CompleteInfo[T]['payload']) { function complete<T extends keyof CompleteInfo>(type: T, value: CompleteInfo[T]['payload']) {
emit('done', { type, value }); emit('done', { type, value });
emit('closed'); emit('closed');

View File

@ -38,7 +38,7 @@ export const Default = {
}; };
}, },
args: { args: {
file: file(), imageFile: file(),
aspectRatio: NaN, aspectRatio: NaN,
}, },
parameters: { parameters: {

View File

@ -699,7 +699,7 @@ useGlobalEvent('driveFoldersDeleted', (folders) => {
} }
}); });
let connection: Misskey.ChannelConnection<Misskey.Channels['drive']> | null = null; let connection: Misskey.IChannelConnection<Misskey.Channels['drive']> | null = null;
onMounted(() => { onMounted(() => {
if (store.s.realtimeMode) { if (store.s.realtimeMode) {

View File

@ -160,7 +160,7 @@ const embedPreviewUrl = computed(() => {
const isEmbedWithScrollbar = computed(() => embedRouteWithScrollbar.includes(props.entity)); const isEmbedWithScrollbar = computed(() => embedRouteWithScrollbar.includes(props.entity));
const header = ref(props.params?.header ?? true); const header = ref(props.params?.header ?? true);
const maxHeight = ref(props.params?.maxHeight !== 0 ? props.params?.maxHeight ?? undefined : 500); const maxHeight = ref(props.params?.maxHeight !== 0 ? props.params?.maxHeight ?? null : 500);
const colorMode = ref<'light' | 'dark' | 'auto'>(props.params?.colorMode ?? 'auto'); const colorMode = ref<'light' | 'dark' | 'auto'>(props.params?.colorMode ?? 'auto');
const rounded = ref(props.params?.rounded ?? true); const rounded = ref(props.params?.rounded ?? true);

View File

@ -41,11 +41,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch> </MkSwitch>
<MkSelect v-else-if="v.type === 'enum'" v-model="values[k]"> <MkSelect v-else-if="v.type === 'enum'" v-model="values[k]">
<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template> <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
<option v-for="option in v.enum" :key="option.value" :value="option.value">{{ option.label }}</option> <option v-for="option in v.enum" :key="getEnumKey(option)" :value="getEnumValue(option)">{{ getEnumLabel(option) }}</option>
</MkSelect> </MkSelect>
<MkRadios v-else-if="v.type === 'radio'" v-model="values[k]"> <MkRadios v-else-if="v.type === 'radio'" v-model="values[k]">
<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template> <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
<option v-for="option in v.options" :key="option.value" :value="option.value">{{ option.label }}</option> <option v-for="option in v.options" :key="getRadioKey(option)" :value="option.value">{{ option.label }}</option>
</MkRadios> </MkRadios>
<MkRange v-else-if="v.type === 'range'" v-model="values[k]" :min="v.min" :max="v.max" :step="v.step" :textConverter="v.textConverter"> <MkRange v-else-if="v.type === 'range'" v-model="values[k]" :min="v.min" :max="v.max" :step="v.step" :textConverter="v.textConverter">
<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template> <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
@ -77,7 +77,7 @@ import MkRange from './MkRange.vue';
import MkButton from './MkButton.vue'; import MkButton from './MkButton.vue';
import MkRadios from './MkRadios.vue'; import MkRadios from './MkRadios.vue';
import XFile from './MkFormDialog.file.vue'; import XFile from './MkFormDialog.file.vue';
import type { Form } from '@/utility/form.js'; import type { EnumItem, Form, RadioFormItem } from '@/utility/form.js';
import MkModalWindow from '@/components/MkModalWindow.vue'; import MkModalWindow from '@/components/MkModalWindow.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
@ -99,7 +99,11 @@ const dialog = useTemplateRef('dialog');
const values = reactive({}); const values = reactive({});
for (const item in props.form) { for (const item in props.form) {
values[item] = props.form[item].default ?? null; if ('default' in props.form[item]) {
values[item] = props.form[item].default ?? null;
} else {
values[item] = null;
}
} }
function ok() { function ok() {
@ -115,4 +119,20 @@ function cancel() {
}); });
dialog.value?.close(); dialog.value?.close();
} }
function getEnumLabel(e: EnumItem) {
return typeof e === 'string' ? e : e.label;
}
function getEnumValue(e: EnumItem) {
return typeof e === 'string' ? e : e.value;
}
function getEnumKey(e: EnumItem) {
return typeof e === 'string' ? e : typeof e.value === 'string' ? e.value : JSON.stringify(e.value);
}
function getRadioKey(e: RadioFormItem['options'][number]) {
return typeof e.value === 'string' ? e.value : JSON.stringify(e.value);
}
</script> </script>

View File

@ -43,7 +43,15 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts">
type SupportedTypes = 'text' | 'password' | 'email' | 'url' | 'tel' | 'number' | 'search';
type ModelValueType<T extends SupportedTypes> =
T extends 'number' ? number :
T extends 'text' | 'password' | 'email' | 'url' | 'tel' | 'search' ? string :
never;
</script>
<script lang="ts" setup generic="T extends SupportedTypes = 'text'">
import { onMounted, onUnmounted, nextTick, ref, useTemplateRef, watch, computed, toRefs } from 'vue'; import { onMounted, onUnmounted, nextTick, ref, useTemplateRef, watch, computed, toRefs } from 'vue';
import { debounce } from 'throttle-debounce'; import { debounce } from 'throttle-debounce';
import { useInterval } from '@@/js/use-interval.js'; import { useInterval } from '@@/js/use-interval.js';
@ -55,8 +63,8 @@ import { Autocomplete } from '@/utility/autocomplete.js';
import { genId } from '@/utility/id.js'; import { genId } from '@/utility/id.js';
const props = defineProps<{ const props = defineProps<{
modelValue: string | number | null; modelValue: ModelValueType<T> | null;
type?: InputHTMLAttributes['type']; type?: T;
required?: boolean; required?: boolean;
readonly?: boolean; readonly?: boolean;
disabled?: boolean; disabled?: boolean;
@ -83,11 +91,11 @@ const emit = defineEmits<{
(ev: 'change', _ev: KeyboardEvent): void; (ev: 'change', _ev: KeyboardEvent): void;
(ev: 'keydown', _ev: KeyboardEvent): void; (ev: 'keydown', _ev: KeyboardEvent): void;
(ev: 'enter', _ev: KeyboardEvent): void; (ev: 'enter', _ev: KeyboardEvent): void;
(ev: 'update:modelValue', value: string | number): void; (ev: 'update:modelValue', value: ModelValueType<T>): void;
}>(); }>();
const { modelValue, type, autofocus } = toRefs(props); const { modelValue } = toRefs(props);
const v = ref(modelValue.value); const v = ref<ModelValueType<T> | null>(modelValue.value);
const id = genId(); const id = genId();
const focused = ref(false); const focused = ref(false);
const changed = ref(false); const changed = ref(false);
@ -120,8 +128,8 @@ const onKeydown = (ev: KeyboardEvent) => {
const updated = () => { const updated = () => {
changed.value = false; changed.value = false;
if (type.value === 'number') { if (props.type === 'number') {
emit('update:modelValue', typeof v.value === 'number' ? v.value : parseFloat(v.value ?? '0')); emit('update:modelValue', typeof v.value === 'number' ? v.value as ModelValueType<T> : parseFloat(v.value ?? '0') as ModelValueType<T>);
} else { } else {
emit('update:modelValue', v.value ?? ''); emit('update:modelValue', v.value ?? '');
} }
@ -167,7 +175,7 @@ useInterval(() => {
onMounted(() => { onMounted(() => {
nextTick(() => { nextTick(() => {
if (autofocus.value) { if (props.autofocus) {
focus(); focus();
} }
}); });

View File

@ -89,6 +89,7 @@ import { Chart } from 'chart.js';
import type { HeatmapSource } from '@/components/MkHeatmap.vue'; import type { HeatmapSource } from '@/components/MkHeatmap.vue';
import MkSelect from '@/components/MkSelect.vue'; import MkSelect from '@/components/MkSelect.vue';
import MkChart from '@/components/MkChart.vue'; import MkChart from '@/components/MkChart.vue';
import type { ChartSrc } from '@/components/MkChart.vue';
import { useChartTooltip } from '@/composables/use-chart-tooltip.js'; import { useChartTooltip } from '@/composables/use-chart-tooltip.js';
import { $i } from '@/i.js'; import { $i } from '@/i.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
@ -107,7 +108,7 @@ const shouldShowFederation = computed(() => instance.federation !== 'none' || $i
const chartLimit = 500; const chartLimit = 500;
const chartSpan = ref<'hour' | 'day'>('hour'); const chartSpan = ref<'hour' | 'day'>('hour');
const chartSrc = ref('active-users'); const chartSrc = ref<ChartSrc>('active-users');
const heatmapSrc = ref<HeatmapSource>('active-users'); const heatmapSrc = ref<HeatmapSource>('active-users');
const subDoughnutEl = useTemplateRef('subDoughnutEl'); const subDoughnutEl = useTemplateRef('subDoughnutEl');
const pubDoughnutEl = useTemplateRef('pubDoughnutEl'); const pubDoughnutEl = useTemplateRef('pubDoughnutEl');

View File

@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:class="[$style.root, { [$style.showActionsOnlyHover]: prefer.s.showNoteActionsOnlyHover, [$style.skipRender]: prefer.s.skipNoteRender }]" :class="[$style.root, { [$style.showActionsOnlyHover]: prefer.s.showNoteActionsOnlyHover, [$style.skipRender]: prefer.s.skipNoteRender }]"
tabindex="0" tabindex="0"
> >
<MkNoteSub v-if="appearNote.replyId && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/> <MkNoteSub v-if="appearNote.replyId && !renoteCollapsed" :note="appearNote?.reply ?? null" :class="$style.replyTo"/>
<div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div> <div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div>
<div v-if="isRenote" :class="$style.renote"> <div v-if="isRenote" :class="$style.renote">
<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div> <div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
@ -99,7 +99,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="isEnabledUrlPreview"> <div v-if="isEnabledUrlPreview">
<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :class="$style.urlPreview"/> <MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :class="$style.urlPreview"/>
</div> </div>
<div v-if="appearNote.renoteId" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div> <div v-if="appearNote.renoteId" :class="$style.quote"><MkNoteSimple :note="appearNote?.renote ?? null" :class="$style.quoteNote"/></div>
<button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click="collapsed = false"> <button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click="collapsed = false">
<span :class="$style.collapsedLabel">{{ i18n.ts.showMore }}</span> <span :class="$style.collapsedLabel">{{ i18n.ts.showMore }}</span>
</button> </button>

View File

@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<div v-if="note" :class="$style.root"> <div v-if="note" :class="$style.root">
<MkAvatar :class="$style.avatar" :user="note.user" link preview/> <MkAvatar :class="[$style.avatar, prefer.s.useStickyIcons ? $style.useSticky : null]" :user="note.user" link preview/>
<div :class="$style.main"> <div :class="$style.main">
<MkNoteHeader :class="$style.header" :note="note" :mini="true"/> <MkNoteHeader :class="$style.header" :note="note" :mini="true"/>
<div> <div>
@ -31,6 +31,7 @@ import MkNoteHeader from '@/components/MkNoteHeader.vue';
import MkSubNoteContent from '@/components/MkSubNoteContent.vue'; import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
import MkCwButton from '@/components/MkCwButton.vue'; import MkCwButton from '@/components/MkCwButton.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
const props = defineProps<{ const props = defineProps<{
note: Misskey.entities.Note | null; note: Misskey.entities.Note | null;
@ -54,9 +55,12 @@ const showContent = ref(false);
width: 34px; width: 34px;
height: 34px; height: 34px;
border-radius: 8px; border-radius: 8px;
position: sticky !important;
top: calc(16px + var(--MI-stickyTop, 0px)); &.useSticky {
left: 0; position: sticky !important;
top: calc(16px + var(--MI-stickyTop, 0px));
left: 0;
}
} }
.main { .main {

View File

@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkButton> </MkButton>
<MkLoading v-else/> <MkLoading v-else/>
</div> </div>
<slot :items="unref(paginator.items)" :fetching="paginator.fetching.value || paginator.fetchingOlder.value"></slot> <slot :items="getValue(paginator.items)" :fetching="paginator.fetching.value || paginator.fetchingOlder.value"></slot>
<div v-if="direction === 'down' || direction === 'both'" v-show="downButtonVisible"> <div v-if="direction === 'down' || direction === 'both'" v-show="downButtonVisible">
<MkButton v-if="!downButtonLoading" :class="$style.more" primary rounded @click="downButtonClick"> <MkButton v-if="!downButtonLoading" :class="$style.more" primary rounded @click="downButtonClick">
{{ i18n.ts.loadMore }} {{ i18n.ts.loadMore }}
@ -90,6 +90,10 @@ function onContextmenu(ev: MouseEvent) {
}], ev); }], ev);
} }
function getValue(v: IPaginator['items']) {
return unref(v) as UnwrapRef<T['items']>;
}
if (props.autoLoad) { if (props.autoLoad) {
onMounted(() => { onMounted(() => {
props.paginator.init(); props.paginator.init();
@ -134,7 +138,7 @@ function downButtonClick() {
defineSlots<{ defineSlots<{
empty: () => void; empty: () => void;
default: (props: { items: UnwrapRef<T['items']> }) => void; default: (props: { items: UnwrapRef<T['items']>, fetching: boolean }) => void;
}>(); }>();
</script> </script>

View File

@ -66,7 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-if="q_federation === 'yes'" v-model="q_remoteContentsCleaning"> <MkSwitch v-if="q_federation === 'yes'" v-model="q_remoteContentsCleaning">
<template #label>{{ i18n.ts._serverSetupWizard.remoteContentsCleaning }}</template> <template #label>{{ i18n.ts._serverSetupWizard.remoteContentsCleaning }}</template>
<template #caption>{{ i18n.ts._serverSetupWizard.remoteContentsCleaning_description }} ({{ i18n.ts._serverSetupWizard.remoteContentsCleaning_description2 }})</template> <template #caption>{{ i18n.ts._serverSetupWizard.remoteContentsCleaning_description }}</template>
</MkSwitch> </MkSwitch>
</div> </div>
</MkFolder> </MkFolder>
@ -134,7 +134,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div> <div>
<div><b>{{ i18n.ts._serverSettings.entrancePageStyle }}:</b></div> <div><b>{{ i18n.ts._serverSettings.entrancePageStyle }}:</b></div>
<div>{{ serverSettings.clientOptions.entrancePageStyle }}</div> <div>{{ serverSettings.clientOptions?.entrancePageStyle }}</div>
</div> </div>
<div> <div>
@ -240,12 +240,12 @@ const serverSettings = computed<Misskey.entities.AdminUpdateMetaRequest>(() => {
enableReactionsBuffering, enableReactionsBuffering,
clientOptions: { clientOptions: {
entrancePageStyle: q_use.value === 'open' ? 'classic' : 'simple', entrancePageStyle: q_use.value === 'open' ? 'classic' : 'simple',
}, } as any,
}; };
}); });
const defaultPolicies = computed<Partial<Record<typeof ROLE_POLICIES[number], any>>>(() => { const defaultPolicies = computed<Partial<Misskey.entities.RolePolicies>>(() => {
let driveCapacityMb; let driveCapacityMb: Misskey.entities.RolePolicies['driveCapacityMb'] | undefined;
if (q_use.value === 'single') { if (q_use.value === 'single') {
driveCapacityMb = 8192; driveCapacityMb = 8192;
} else if (q_use.value === 'group') { } else if (q_use.value === 'group') {
@ -254,7 +254,7 @@ const defaultPolicies = computed<Partial<Record<typeof ROLE_POLICIES[number], an
driveCapacityMb = 100; driveCapacityMb = 100;
} }
let rateLimitFactor; let rateLimitFactor: Misskey.entities.RolePolicies['rateLimitFactor'] | undefined;
if (q_use.value === 'single') { if (q_use.value === 'single') {
rateLimitFactor = 0.3; rateLimitFactor = 0.3;
} else if (q_use.value === 'group') { } else if (q_use.value === 'group') {
@ -269,7 +269,7 @@ const defaultPolicies = computed<Partial<Record<typeof ROLE_POLICIES[number], an
} }
} }
let userListLimit; let userListLimit: Misskey.entities.RolePolicies['userListLimit'] | undefined;
if (q_use.value === 'single') { if (q_use.value === 'single') {
userListLimit = 100; userListLimit = 100;
} else if (q_use.value === 'group') { } else if (q_use.value === 'group') {
@ -278,7 +278,7 @@ const defaultPolicies = computed<Partial<Record<typeof ROLE_POLICIES[number], an
userListLimit = 3; userListLimit = 3;
} }
let antennaLimit; let antennaLimit: Misskey.entities.RolePolicies['antennaLimit'] | undefined;
if (q_use.value === 'single') { if (q_use.value === 'single') {
antennaLimit = 100; antennaLimit = 100;
} else if (q_use.value === 'group') { } else if (q_use.value === 'group') {
@ -287,7 +287,7 @@ const defaultPolicies = computed<Partial<Record<typeof ROLE_POLICIES[number], an
antennaLimit = 0; antennaLimit = 0;
} }
let webhookLimit; let webhookLimit: Misskey.entities.RolePolicies['webhookLimit'] | undefined;
if (q_use.value === 'single') { if (q_use.value === 'single') {
webhookLimit = 100; webhookLimit = 100;
} else if (q_use.value === 'group') { } else if (q_use.value === 'group') {
@ -296,35 +296,35 @@ const defaultPolicies = computed<Partial<Record<typeof ROLE_POLICIES[number], an
webhookLimit = 0; webhookLimit = 0;
} }
let canImportFollowing; let canImportFollowing: Misskey.entities.RolePolicies['canImportFollowing'];
if (q_use.value === 'single') { if (q_use.value === 'single') {
canImportFollowing = true; canImportFollowing = true;
} else { } else {
canImportFollowing = false; canImportFollowing = false;
} }
let canImportMuting; let canImportMuting: Misskey.entities.RolePolicies['canImportMuting'];
if (q_use.value === 'single') { if (q_use.value === 'single') {
canImportMuting = true; canImportMuting = true;
} else { } else {
canImportMuting = false; canImportMuting = false;
} }
let canImportBlocking; let canImportBlocking: Misskey.entities.RolePolicies['canImportBlocking'];
if (q_use.value === 'single') { if (q_use.value === 'single') {
canImportBlocking = true; canImportBlocking = true;
} else { } else {
canImportBlocking = false; canImportBlocking = false;
} }
let canImportUserLists; let canImportUserLists: Misskey.entities.RolePolicies['canImportUserLists'];
if (q_use.value === 'single') { if (q_use.value === 'single') {
canImportUserLists = true; canImportUserLists = true;
} else { } else {
canImportUserLists = false; canImportUserLists = false;
} }
let canImportAntennas; let canImportAntennas: Misskey.entities.RolePolicies['canImportAntennas'];
if (q_use.value === 'single') { if (q_use.value === 'single') {
canImportAntennas = true; canImportAntennas = true;
} else { } else {
@ -355,6 +355,7 @@ function applySettings() {
maintainerEmail: q_adminEmail.value === '' ? undefined : q_adminEmail.value, maintainerEmail: q_adminEmail.value === '' ? undefined : q_adminEmail.value,
}, props.token), }, props.token),
misskeyApi('admin/roles/update-default-policies', { misskeyApi('admin/roles/update-default-policies', {
// @ts-expect-error
policies: defaultPolicies.value, policies: defaultPolicies.value,
}, props.token), }, props.token),
]).then(() => { ]).then(() => {

View File

@ -66,7 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkCaptcha v-if="instance.enableMcaptcha" ref="mcaptcha" v-model="mCaptchaResponse" :class="$style.captcha" provider="mcaptcha" :sitekey="instance.mcaptchaSiteKey" :instanceUrl="instance.mcaptchaInstanceUrl"/> <MkCaptcha v-if="instance.enableMcaptcha" ref="mcaptcha" v-model="mCaptchaResponse" :class="$style.captcha" provider="mcaptcha" :sitekey="instance.mcaptchaSiteKey" :instanceUrl="instance.mcaptchaInstanceUrl"/>
<MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" :class="$style.captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/> <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" :class="$style.captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/>
<MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" :class="$style.captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/> <MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" :class="$style.captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/>
<MkCaptcha v-if="instance.enableTestcaptcha" ref="testcaptcha" v-model="testcaptchaResponse" :class="$style.captcha" provider="testcaptcha"/> <MkCaptcha v-if="instance.enableTestcaptcha" ref="testcaptcha" v-model="testcaptchaResponse" :class="$style.captcha" provider="testcaptcha" :sitekey="null"/>
<MkButton type="submit" :disabled="shouldDisableSubmitting" large gradate rounded data-cy-signup-submit style="margin: 0 auto;"> <MkButton type="submit" :disabled="shouldDisableSubmitting" large gradate rounded data-cy-signup-submit style="margin: 0 auto;">
<template v-if="submitting"> <template v-if="submitting">
<MkLoading :em="true" :colored="false"/> <MkLoading :em="true" :colored="false"/>

View File

@ -6,6 +6,7 @@
import { defineAsyncComponent, ref } from 'vue'; import { defineAsyncComponent, ref } from 'vue';
import type { Directive } from 'vue'; import type { Directive } from 'vue';
import { popup } from '@/os.js'; import { popup } from '@/os.js';
import { isTouchUsing } from '@/utility/touch.js';
export class UserPreview { export class UserPreview {
private el; private el;
@ -107,6 +108,7 @@ export class UserPreview {
export default { export default {
mounted(el: HTMLElement, binding, vn) { mounted(el: HTMLElement, binding, vn) {
if (binding.value == null) return; if (binding.value == null) return;
if (isTouchUsing) return;
// TODO: 新たにプロパティを作るのをやめMapを使う // TODO: 新たにプロパティを作るのをやめMapを使う
// ただメモリ的には↓の方が省メモリかもしれないので検討中 // ただメモリ的には↓の方が省メモリかもしれないので検討中

View File

@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<div :class="[$style.root, { [$style.isMe]: isMe }]"> <div :class="[$style.root, { [$style.isMe]: isMe }]">
<MkAvatar :class="$style.avatar" :user="message.fromUser!" :link="!isMe" :preview="false"/> <MkAvatar :class="[$style.avatar, prefer.s.useStickyIcons ? $style.useSticky : null]" :user="message.fromUser!" :link="!isMe" :preview="false"/>
<div :class="[$style.body, message.file != null ? $style.fullWidth : null]" @contextmenu.stop="onContextmenu"> <div :class="[$style.body, message.file != null ? $style.fullWidth : null]" @contextmenu.stop="onContextmenu">
<div :class="$style.header"><MkUserName v-if="!isMe && prefer.s['chat.showSenderName'] && message.fromUser != null" :user="message.fromUser"/></div> <div :class="$style.header"><MkUserName v-if="!isMe && prefer.s['chat.showSenderName'] && message.fromUser != null" :user="message.fromUser"/></div>
<MkFukidashi :class="$style.fukidashi" :tail="isMe ? 'right' : 'left'" :fullWidth="message.file != null" :accented="isMe"> <MkFukidashi :class="$style.fukidashi" :tail="isMe ? 'right' : 'left'" :fullWidth="message.file != null" :accented="isMe">
@ -231,11 +231,14 @@ function showMenu(ev: MouseEvent, contextmenu = false) {
} }
.avatar { .avatar {
position: sticky;
top: calc(16px + var(--MI-stickyTop, 0px));
display: block; display: block;
width: 50px; width: 50px;
height: 50px; height: 50px;
&.useSticky {
position: sticky;
top: calc(16px + var(--MI-stickyTop, 0px));
}
} }
@container (max-width: 450px) { @container (max-width: 450px) {

View File

@ -22,6 +22,7 @@
"isolatedModules": true, "isolatedModules": true,
"useDefineForClassFields": true, "useDefineForClassFields": true,
"verbatimModuleSyntax": true, "verbatimModuleSyntax": true,
"skipLibCheck": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@/*": ["./src/*"], "@/*": ["./src/*"],
@ -46,8 +47,6 @@
}, },
"compileOnSave": false, "compileOnSave": false,
"include": [ "include": [
"./build.ts",
"./lib/**/*.ts",
"./src/**/*.ts", "./src/**/*.ts",
"./src/**/*.vue", "./src/**/*.vue",
"./test/**/*.ts", "./test/**/*.ts",
@ -55,7 +54,6 @@
"./@types/**/*.ts" "./@types/**/*.ts"
], ],
"exclude": [ "exclude": [
".storybook/**/*",
"./src/**/*.stories.ts" "./src/**/*.stories.ts"
] ]
} }

View File

@ -2809,7 +2809,7 @@ type ModerationLog = {
id: ID; id: ID;
createdAt: DateString; createdAt: DateString;
userId: User['id']; userId: User['id'];
user: UserDetailedNotMe | null; user: UserDetailedNotMe;
} & ({ } & ({
type: 'updateServerSettings'; type: 'updateServerSettings';
info: ModerationLogPayloads['updateServerSettings']; info: ModerationLogPayloads['updateServerSettings'];
@ -3538,6 +3538,7 @@ type SignupRequest = {
'g-recaptcha-response'?: string | null; 'g-recaptcha-response'?: string | null;
'turnstile-response'?: string | null; 'turnstile-response'?: string | null;
'm-captcha-response'?: string | null; 'm-captcha-response'?: string | null;
'testcaptcha-response'?: string | null;
}; };
// @public (undocumented) // @public (undocumented)

View File

@ -1,7 +1,7 @@
{ {
"type": "module", "type": "module",
"name": "misskey-js", "name": "misskey-js",
"version": "2025.8.0-beta.5", "version": "2025.8.0",
"description": "Misskey SDK for JavaScript", "description": "Misskey SDK for JavaScript",
"license": "MIT", "license": "MIT",
"main": "./built/index.js", "main": "./built/index.js",

View File

@ -269,6 +269,7 @@ export type SignupRequest = {
'g-recaptcha-response'?: string | null; 'g-recaptcha-response'?: string | null;
'turnstile-response'?: string | null; 'turnstile-response'?: string | null;
'm-captcha-response'?: string | null; 'm-captcha-response'?: string | null;
'testcaptcha-response'?: string | null;
}; };
export type SignupResponse = MeDetailed & { export type SignupResponse = MeDetailed & {

File diff suppressed because it is too large Load Diff