Compare commits

..

52 Commits

Author SHA1 Message Date
github-actions[bot] df54bd92d0 Bump version to 2025.11.1-beta.1 2025-11-26 10:45:40 +00:00
syuilo 8cb37792c9 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2025-11-26 19:39:46 +09:00
syuilo 3f539916d9 enhance(frontend): GIF画像を再生するか・動くMFMを表示するかの設定でOSのアクセシビリティ設定を参照しないように
・OSのUIのアニメーション設定がMisskey内のGIFなどにまで連動して適用されるのは分かりにくい・期待しない動作との声が多いため
・動きのあるカスタム絵文字などのコンテンツはMisskeyの主要部分であり、それを止める設定は、本来の挙動に影響しない範囲でアニメーションを減らすアクセシビリティ設定とは趣旨が異なるため(OSでアニメーションを減らす設定を有効にしたからといってOSで動画の再生がされなくなるわけではないのと同じ)
2025-11-26 19:39:36 +09:00
renovate[bot] 415339b30b chore(deps): update [misskey-js] update dependencies [ci skip] (#16863)
* chore(deps): update [misskey-js] update dependencies

* update deps

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
2025-11-26 19:37:20 +09:00
syuilo de7cbb376e fix(frontend): 初回読み込み時にエラーになることがある問題を修正
Fix #16562
2025-11-26 19:26:27 +09:00
syuilo 6cb6f794e5 clean up 2025-11-26 19:14:40 +09:00
syuilo 236c235115 enhance(frontend): viewportの属性を起動後に変化させないことにより処理を簡略化+安定性向上 2025-11-26 19:12:03 +09:00
renovate[bot] 71808d3cc0 chore(deps): update [tools] update dependencies [ci skip] (#16865)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-26 19:00:05 +09:00
renovate[bot] 989c1b351a fix(deps): update [root] update dependencies [ci skip] (#16862)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-26 18:59:34 +09:00
かっこかり 0c5f61721a fix(frontend): フォロー申請のキャンセル時に確認ダイアログを出すように (#16834)
* fix(frontend): フォロー申請のキャンセル時に確認ダイアログを出すように

* Update Changelog

* fix: 注釈は書かない
2025-11-26 13:07:28 +09:00
github-actions[bot] e0e17a78f1 Bump version to 2025.11.1-beta.0 2025-11-26 01:01:54 +00:00
syuilo 2ad393ea45 fix(backend): ワードミュートの文字数計算を修正 2025-11-26 09:55:02 +09:00
syuilo cdf059cc11 chore(dev): use postgresql 18 (#16850) 2025-11-25 19:38:57 +09:00
renovate[bot] 0fdd88f38e fix(deps): update [frontend] update dependencies [ci skip] (#16802)
* fix(deps): update [frontend] update dependencies

* run pnpm dedupe

* [ci skip] run dedupe

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
2025-11-25 10:45:36 +09:00
github-actions[bot] 4679b2b34d Bump version to 2025.11.1-alpha.2 2025-11-25 01:33:05 +00:00
syuilo 052b1a6c76 Update CHANGELOG.md 2025-11-25 10:27:53 +09:00
syuilo fd15a7fc23 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2025-11-25 10:20:46 +09:00
syuilo b895088546 🎨 2025-11-25 10:20:36 +09:00
renovate[bot] 1e5592a5bd fix(deps): update [backend] update dependencies [ci skip] (#16801)
* fix(deps): update [backend] update dependencies

* fix types

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
2025-11-25 10:20:06 +09:00
syuilo c3ad46ad6f fix(frontend): ナビゲーションバーを下に表示しているときに、項目数が多いと表示が崩れる問題を修正 2025-11-25 09:46:07 +09:00
syuilo 8c7e1bd287 chore(backend): tweak message 2025-11-25 09:26:22 +09:00
かっこかり 043b9b3d26 fix(frontend): MkRadiosのslotでv-ifを使用すると空白のoptionが生成される問題を修正 (#16832)
* fix: MkRadiosのslotでv-ifを使用すると空白のoptionが生成される問題を修正 (MisskeyIO#1105)

* Update Changelog

* Update CHANGELOG.md

---------

Co-authored-by: あわわわとーにゅ <17376330+u1-liquid@users.noreply.github.com>
2025-11-25 08:56:30 +09:00
かっこかり 91dafc26a7 refactor(frontend/aiscript): AiScriptバージョン取得・判定ロジックを統一 (#16845)
* refactor(frontend): AiScriptバージョン取得・判定ロジックを統一

* fix
2025-11-25 07:23:21 +09:00
github-actions[bot] 4edd6a68e6 Bump version to 2025.11.1-alpha.1 2025-11-24 12:06:01 +00:00
かっこかり f801d1cf0b fix(backend): DBレプリケーションを利用する環境でクエリーが失敗する問題を修正 (#16842)
* fix: DBレプリケーションを利用する環境でクエリーが失敗する問題を修正 (MisskeyIO#1123)

* Update Changelog

---------

Co-authored-by: あわわわとーにゅ <17376330+u1-liquid@users.noreply.github.com>
2025-11-24 20:59:25 +09:00
かっこかり 42706970f2 fix(frontend): PlayのAiScriptバージョン判定が正しく動作しない問題を修正 (#16843)
* fix: aiscript 1.0.0 以外が全部レガシー扱いになる問題を修正 (MisskeyIO#1129)

* Update Changelog

---------

Co-authored-by: あわわわとーにゅ <17376330+u1-liquid@users.noreply.github.com>
2025-11-24 20:53:39 +09:00
syuilo 14730e429a New Crowdin updates (#16797)
* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Spanish)
2025-11-24 17:40:29 +09:00
かっこかり 2ee04860fb enhance(frontend): preferenceのタブ間同期にBroadcast Channelを使用するように (#16819)
* enhance(frontend): preferenceのタブ間同期にBroadcast Channelを使用するように

* fix

* refactor: EventEmitterをextendする形に変更
2025-11-24 16:52:46 +09:00
github-actions[bot] 25afb5d279 Bump version to 2025.11.1-alpha.0 2025-11-24 03:05:55 +00:00
かっこかり c4f53aba3f fix(frontend): 一部のシチュエーションで投稿フォームのツアーが表示されない問題を修正 (#16837)
* fix(frontend): 一部のシチュエーションで投稿フォームのツアーが表示されない問題を修正

* Update Changelog

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-11-24 11:18:01 +09:00
かっこかり 86e4f15e95 fix(frontend): リセットボタンでCWがリセットされない問題を修正 (#16820)
* fix(frontend): リセットボタンでCWがリセットされない問題を修正

* update changelog
2025-11-24 11:13:36 +09:00
かっこかり 6c190e7a5d fix(backend): チャンネルのリアルタイム更新で非ログイン時非表示設定が考慮されていない問題を修正 (#16833)
* fix(backend): チャンネルのリアルタイム更新でロックダウン設定が考慮されていない問題を修正

* Update Changelog

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-11-24 11:11:59 +09:00
zyoshoka 1b46813e7a chore(dev): correct entrypoint path for embed on dev build (#16836) 2025-11-24 11:08:44 +09:00
syuilo 0ea0e05e61 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2025-11-24 11:01:19 +09:00
syuilo 015e680133 chore(backend/dev): remove cli-highlight dependency to reduce memory usage 2025-11-24 11:01:00 +09:00
かっこかり ea40a0756f fix(backend): DeepL翻訳のAPIキー指定方式変更に対応 (#16839)
* spec: DeepL Deprecation of query parameter and request body authentication (MisskeyIO#1096)

https://developers.deepl.com/docs/resources/breaking-changes-change-notices/november-2025-deprecation-of-legacy-auth-methods

* Update Changelog

* Update Changelog

* ✌️ [ci skip]

---------

Co-authored-by: あわわわとーにゅ <17376330+u1-liquid@users.noreply.github.com>
2025-11-24 10:59:50 +09:00
かっこかり 70fa621e22 fix(backend): clips/my-favorites APIをページネーションに対応させる (#16835)
* fix(backend): `clips/my-favorites` APIをページネーションに対応させる

* fix

* fix test

* fix
2025-11-23 22:41:14 +09:00
syuilo c741aa5d7d chore(dev): add start:inspect command for debugging 2025-11-21 10:04:16 +09:00
かっこかり 7afe0d44d1 fix(frontend): pageheaderの型を修正 (#16803) 2025-11-20 15:40:52 +09:00
果物リン e588615ea9 fix: ヘッダーメニューのチャンネルからチャンネルを新規作成の遷移先修正 (#16816)
* fix: ヘッダーメニューのチャンネルからチャンネルを新規作成の遷移先修正

* add changelog

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-11-20 15:39:50 +09:00
かっこかり 7e56fed164 fix(frontend): ページのコンテンツがはみ出る問題を修正 (#16817)
* fix(frontend): ページのコンテンツがはみ出る問題を修正

* Update Changelog
2025-11-20 15:38:25 +09:00
kami8 9f810d701d enhance(frontend): リアクションの受け入れ設定にキャプションを追加 (#16807)
* enhance(frontend): リアクションの受け入れ設定にキャプションを追加

* Update Changelog

* CHANGELOG.mdを修正

* CHANGELOG.mdのコンフリクトを解消し、再度変更内容を記載

* 条件分岐をswitch文に変更

* chore: trigger CI re-run
2025-11-19 19:17:43 +09:00
おさむのひと 2f3421645a fix(devcontainer): devcontainerのバージョンアップ+Renovateでbump出来るように設定を追加 (#16793)
* fix(devcontainer): devcontainerのバージョンアップ+Renovateでbump出来るように設定を追加

* Update renovate.json5

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update renovate.json5

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix matchFileNames

* using trixie

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-11-19 12:34:27 +09:00
syuilo 4c0f215fc5 refactor 2025-11-18 16:23:57 +09:00
github-actions[bot] 449b00c934 [skip ci] Update CHANGELOG.md (prepend template) 2025-11-16 08:23:49 +00:00
github-actions[bot] cdcff3ede8 Release: 2025.11.0 2025-11-16 08:23:43 +00:00
syuilo 9d1c3f053c New Crowdin updates (#16781)
* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Italian)
2025-11-16 09:12:02 +09:00
github-actions[bot] 8c5d571975 Bump version to 2025.11.0-rc.0 2025-11-15 12:20:55 +00:00
おさむのひと c5e9f7add4 fix(ci): DockleのCIが落ちるのを修正 (#16794)
* fix(ci): DockleのCIが落ちるのを修正

* fix

* fix

* fix

* fix

* fix

* downgrade dockle

* fix
2025-11-15 21:04:45 +09:00
おさむのひと e15fdd05b7 fix(frontend): カスタム絵文字(β)画面で変更行が正しくハイライトされない問題を修正 (#16785)
* fix(frontend): カスタム絵文字(β)画面で変更行が正しくハイライトされない問題を修正

* rollback

* 詳細度->!important
2025-11-15 12:13:50 +09:00
おさむのひと 41e945b0ef fix(frontend): 投稿フォームのアバター画像が縮むのを修正 (#16790)
* fix(frontend): 投稿フォームのアバター画像が縮むのを修正

* fix CHANGELOG.md

* fix
2025-11-15 11:33:37 +09:00
syuilo f89faae0ab chore(frontend): add tip for preference restore 2025-11-13 14:11:38 +09:00
77 changed files with 5962 additions and 5301 deletions
+1 -1
View File
@@ -1 +1 @@
FROM mcr.microsoft.com/devcontainers/javascript-node:0-18
FROM mcr.microsoft.com/devcontainers/javascript-node:4.0.3-24-trixie
+1 -1
View File
@@ -28,7 +28,7 @@ services:
db:
restart: unless-stopped
image: postgres:15-alpine
image: postgres:18-alpine
networks:
- internal_network
environment:
+1 -1
View File
@@ -76,7 +76,7 @@ body:
* Installation Method or Hosting Service: docker compose, k8s/docker, systemd, "Misskey install shell script", development environment
* Misskey: 2025.x.x
* Node: 20.x.x
* PostgreSQL: 15.x.x
* PostgreSQL: 18.x.x
* Redis: 7.x.x
* OS and Architecture: Ubuntu 24.04.2 LTS aarch64
value: |
+22 -6
View File
@@ -13,20 +13,36 @@ jobs:
runs-on: ubuntu-latest
env:
DOCKER_CONTENT_TRUST: 1
DOCKLE_VERSION: 0.4.14
DOCKLE_VERSION: 0.4.15
steps:
- uses: actions/checkout@v4.3.0
- name: Download and install dockle v${{ env.DOCKLE_VERSION }}
run: |
curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v${DOCKLE_VERSION}/dockle_${DOCKLE_VERSION}_Linux-64bit.deb"
sudo dpkg -i dockle.deb
- run: |
cp .config/docker_example.env .config/docker.env
cp ./compose_example.yml ./compose.yml
- run: |
docker compose up -d web
docker tag "$(docker compose images --format json web | jq -r '.[] | .ID')" misskey-web:latest
- run: |
cmd="dockle --exit-code 1 misskey-web:latest ${image_name}"
echo "> ${cmd}"
eval "${cmd}"
IMAGE_ID=$(docker compose images --format json web | jq -r '.[0].ID')
docker tag "${IMAGE_ID}" misskey-web:latest
- name: Prune docker junk (optional but recommended)
run: |
docker system prune -af
docker volume prune -f
- name: Save image for Dockle
run: |
docker save misskey-web:latest -o ./misskey-web.tar
ls -lh ./misskey-web.tar
- name: Run Dockle with tar input
run: |
dockle --exit-code 1 --input ./misskey-web.tar
+3 -3
View File
@@ -38,7 +38,7 @@ jobs:
services:
postgres:
image: postgres:15
image: postgres:18
ports:
- 54312:5432
env:
@@ -117,7 +117,7 @@ jobs:
services:
postgres:
image: postgres:15
image: postgres:18
ports:
- 54312:5432
env:
@@ -165,7 +165,7 @@ jobs:
services:
postgres:
image: postgres:15
image: postgres:18
ports:
- 54312:5432
env:
+1 -1
View File
@@ -64,7 +64,7 @@ jobs:
services:
postgres:
image: postgres:15
image: postgres:18
ports:
- 54312:5432
env:
+34
View File
@@ -1,3 +1,35 @@
## 2025.11.1
### General
-
### Client
- Enhance: リアクションの受け入れ設定にキャプションを追加 #15921
- Fix: ページの内容がはみ出ることがある問題を修正
- Fix: ナビゲーションバーを下に表示しているときに、項目数が多いと表示が崩れる問題を修正
- Fix: ヘッダーメニューのチャンネルの新規作成の項目でチャンネル作成ページに飛べない問題を修正 #16816
- Fix: ラジオボタンに空白の選択肢が表示される問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1105)
- Fix: 一部のシチュエーションで投稿フォームのツアーが正しく表示されない問題を修正
- Fix: 投稿フォームのリセットボタンで注釈がリセットされない問題を修正
- Fix: PlayのAiScriptバージョン判定(v0.x系・v1.x系の判定)が正しく動作しない問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1129)
- Fix: フォロー申請をキャンセルする際の確認ダイアログの文言が不正確な問題を修正
- Fix: 初回読み込み時にエラーになることがある問題を修正
### Server
- Enhance: `clips/my-favorites` APIがページネーションに対応しました
- Enhance: メモリ使用量を削減しました
- Enhance: 依存関係の更新
- Fix: ワードミュートの文字数計算を修正
- Fix: チャンネルのリアルタイム更新時に、ロックダウン設定にて非ログイン時にノートを表示しない設定にしている場合でもノートが表示されてしまう問題を修正
- Fix: DeepL APIのAPIキー指定方式変更に対応
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1096)
- 内部実装の変更にて対応可能な更新です。Misskey側の設定方法に変更はありません。
- Fix: DBレプリケーションを利用する環境でクエリーが失敗する問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1123)
## 2025.11.0
### General
@@ -22,6 +54,8 @@
- Fix: 紙吹雪エフェクトがアニメーション設定を考慮せず常に表示される問題を修正
- Fix: ナビゲーションバーのリアルタイムモード切替ボタンの状態をよりわかりやすく表示するように
- Fix: ページのタイトルが長いとき、はみ出る問題を修正
- Fix: 投稿フォームのアバターが正しく表示されない問題を修正 #16789
- FIx: カスタム絵文字(β)画面で変更行が正しくハイライトされない問題を修正 #16626
### Server
- Enhance: Remote Notes Cleaningが複雑度が高いノートの処理を中断せずに次のノートから再開するように
+1 -1
View File
@@ -27,7 +27,7 @@ spec:
ports:
- containerPort: 3000
- name: postgres
image: postgres:15-alpine
image: postgres:18-alpine
env:
- name: POSTGRES_USER
value: "example-misskey-user"
+1 -1
View File
@@ -15,7 +15,7 @@ services:
db:
restart: always
image: postgres:15-alpine
image: postgres:18-alpine
ports:
- "5432:5432"
env_file:
+1 -1
View File
@@ -37,7 +37,7 @@ services:
db:
restart: always
image: postgres:15-alpine
image: postgres:18-alpine
networks:
- internal_network
env_file:
+2
View File
@@ -1556,6 +1556,8 @@ _preferencesProfile:
profileNameDescription: "Estableix un nom que identifiqui aquest dispositiu."
profileNameDescription2: "Per exemple: \"PC Principal\", \"Smartphone\", etc"
manageProfiles: "Gestionar perfils"
shareSameProfileBetweenDevicesIsNotRecommended: "No recomanem compartir el mateix perfil en diferents dispositius."
useSyncBetweenDevicesOptionIfYouWantToSyncSetting: "Si hi ha ajustos que vols sincronitzar entre diferents dispositius activa l'opció \"Sincronitza entre diferents dispositius\" individualment per cada una de les diferents opcions."
_preferencesBackup:
autoBackup: "Còpia de seguretat automàtica "
restoreFromBackup: "Restaurar des d'una còpia de seguretat"
+2
View File
@@ -1556,6 +1556,8 @@ _preferencesProfile:
profileNameDescription: "Set a name that identifies this device."
profileNameDescription2: "Example: \"Main PC\", \"Smartphone\""
manageProfiles: "Manage Profiles"
shareSameProfileBetweenDevicesIsNotRecommended: "We do not recommend sharing the same profile across multiple devices."
useSyncBetweenDevicesOptionIfYouWantToSyncSetting: "If there are settings you wish to synchronize across multiple devices, enable the “Synchronize across multiple devices” option individually for each device."
_preferencesBackup:
autoBackup: "Auto backup"
restoreFromBackup: "Restore from backup"
+10 -7
View File
@@ -5,7 +5,7 @@ introMisskey: "¡Bienvenido/a! Misskey es un servicio de microblogging descentra
poweredByMisskeyDescription: "{name} es uno de los servicios (también llamado instancia) que usa la plataforma de código abierto <b>Misskey</b>"
monthAndDay: "{day}/{month}"
search: "Buscar"
reset: "Reiniciar"
reset: "Restablecer"
notifications: "Notificaciones"
username: "Nombre de usuario"
password: "Contraseña"
@@ -43,7 +43,7 @@ favorite: "Añadir a favoritos"
favorites: "Favoritos"
unfavorite: "Quitar de favoritos"
favorited: "Añadido a favoritos."
alreadyFavorited: "Ya había sido añadido a favoritos"
alreadyFavorited: "Ya añadido a favoritos."
cantFavorite: "No se puede añadir a favoritos."
pin: "Fijar al perfil"
unpin: "Desfijar"
@@ -986,7 +986,7 @@ typeToConfirm: "Ingrese {x} para confirmar"
deleteAccount: "Borrar cuenta"
document: "Documento"
numberOfPageCache: "Cantidad de páginas cacheadas"
numberOfPageCacheDescription: "Al aumentar el número mejora la conveniencia pero tambien puede aumentar la carga y la memoria a usarse"
numberOfPageCacheDescription: "Al aumentar el número mejora la conveniencia pero también puede aumentar la carga y la memoria a usarse"
logoutConfirm: "¿Cerrar sesión?"
logoutWillClearClientData: "Al cerrar la sesión, la información de configuración del cliente se borra del navegador. Para garantizar que la información de configuración se pueda restaurar al volver a iniciar sesión, active la copia de seguridad automática de la configuración."
lastActiveDate: "Utilizado por última vez el"
@@ -1354,7 +1354,7 @@ textCount: "caracteres"
information: "Información"
chat: "Chat"
directMessage: "Chatear"
directMessage_short: "Mensaje"
directMessage_short: "Mensajes"
migrateOldSettings: "Migrar la configuración anterior"
migrateOldSettings_description: "Esto debería hacerse automáticamente, pero si por alguna razón la migración no ha tenido éxito, puede activar usted mismo el proceso de migración manualmente. Se sobrescribirá la información de configuración actual."
compress: "Compresión de la imagen"
@@ -1457,7 +1457,7 @@ _order:
newest: "Los más recientes primero"
oldest: "Los más antiguos primero"
_chat:
messages: "Mensaje"
messages: "Mensajes"
noMessagesYet: "Aún no hay mensajes"
newMessage: "Mensajes nuevos"
individualChat: "Chat individual"
@@ -1547,6 +1547,7 @@ _settings:
showAvailableReactionsFirstInNote: "Mostrar las reacciones disponibles en la parte superior."
showPageTabBarBottom: "Mostrar la barra de pestañas de la página en la parte inferior."
emojiPaletteBanner: "Puedes registrar ajustes preestablecidos como paletas para que se muestren permanentemente en el selector de emojis, o personalizar el método de visualización del selector."
enableAnimatedImages: "Habilitar imágenes animadas"
_chat:
showSenderName: "Mostrar el nombre del remitente"
sendOnEnter: "Intro para enviar"
@@ -1555,6 +1556,8 @@ _preferencesProfile:
profileNameDescription: "Establece un nombre que identifique al dispositivo"
profileNameDescription2: "Por ejemplo: \"PC Principal\",\"Teléfono\""
manageProfiles: "Administrar perfiles"
shareSameProfileBetweenDevicesIsNotRecommended: "No recomendamos compartir el mismo perfil en varios dispositivos."
useSyncBetweenDevicesOptionIfYouWantToSyncSetting: "Si hay ajustes que deseas sincronizar en varios dispositivos, activa la opción «Sincronizar en varios dispositivos» individualmente para cada uno de ellos."
_preferencesBackup:
autoBackup: "Respaldo automático"
restoreFromBackup: "Restaurar desde copia de seguridad"
@@ -2147,7 +2150,7 @@ _accountDelete:
accountDelete: "Eliminar Cuenta"
mayTakeTime: "La eliminación de la cuenta es un proceso que precisa de carga. Puede pasar un tiempo hasta que se complete si es mucho el contenido creado y los archivos subidos."
sendEmail: "Cuando se termine de borrar la cuenta, se enviará un correo a la dirección usada para el registro."
requestAccountDelete: "Pedir la eliminación de la cuenta."
requestAccountDelete: "Solicitar la eliminación de la cuenta."
started: "El proceso de eliminación ha comenzado."
inProgress: "La eliminación está en proceso."
_ad:
@@ -2545,7 +2548,7 @@ _poll:
noOnlyOneChoice: "Se necesitan al menos 2 opciones"
choiceN: "Opción {n}"
noMore: "No se pueden agregar más"
canMultipleVote: "Permitir más de una respuesta"
canMultipleVote: "Permitir seleccionar varias opciones"
expiration: "Termina el"
infinite: "Sin límite de tiempo"
at: "Elegir fecha y hora"
+3 -1
View File
@@ -240,6 +240,8 @@ blockedInstances: "Instansi terblokir"
blockedInstancesDescription: "Daftar nama host dari instansi yang diperlukan untuk diblokir. Instansi yang didaftarkan tidak akan dapat berkomunikasi dengan instansi ini."
silencedInstances: "Instansi yang disenyapkan"
silencedInstancesDescription: "Daftar nama host dari instansi yang ingin kamu senyapkan. Semua akun dari instansi yang terdaftar akan diperlakukan sebagai disenyapkan. Hal ini membuat akun hanya dapat membuat permintaan mengikuti, dan tidak dapat menyebutkan akun lokal apabila tidak mengikuti. Hal ini tidak akan mempengaruhi instansi yang diblokir."
mediaSilencedInstances: "Server dengan media dibisukan"
mediaSilencedInstancesDescription: "Masukkan host server yang medianya ingin Anda bisukan, pisahkan dengan baris baru. Semua berkas dari akun di server ini akan dianggap sebagai sensitif dan emoji kustom tidak akan tersedia. Ini tidak akan membengaruhi server yang diblokir."
federationAllowedHosts: "Server yang membolehkan federasi"
muteAndBlock: "Bisukan / Blokir"
mutedUsers: "Pengguna yang dibisukan"
@@ -398,7 +400,7 @@ enableHcaptcha: "Nyalakan hCaptcha"
hcaptchaSiteKey: "Site Key"
hcaptchaSecretKey: "Secret Key"
mcaptcha: "mCaptcha"
enableMcaptcha: "Nyalakan mCaptcha"
enableMcaptcha: ""
mcaptchaSiteKey: "Site key"
mcaptchaSecretKey: "Secret Key"
mcaptchaInstanceUrl: "URL instansi mCaptcha"
+16
View File
@@ -350,6 +350,14 @@ export interface Locale extends ILocale {
* {name}
*/
"unfollowConfirm": ParameterizedString<"name">;
/**
* {name}
*/
"cancelFollowRequestConfirm": ParameterizedString<"name">;
/**
* {name}
*/
"rejectFollowRequestConfirm": ParameterizedString<"name">;
/**
*
*/
@@ -6219,6 +6227,14 @@ export interface Locale extends ILocale {
*
*/
"manageProfiles": string;
/**
*
*/
"shareSameProfileBetweenDevicesIsNotRecommended": string;
/**
*
*/
"useSyncBetweenDevicesOptionIfYouWantToSyncSetting": string;
};
"_preferencesBackup": {
/**
+11 -8
View File
@@ -144,7 +144,7 @@ markAsSensitive: "Segna come esplicito"
unmarkAsSensitive: "Non segnare come esplicito "
enterFileName: "Nome del file"
mute: "Silenziare"
unmute: "Riattiva l'audio"
unmute: "Dai voce"
renoteMute: "Silenziare le Rinota"
renoteUnmute: "Non silenziare le Rinota"
block: "Bloccare"
@@ -691,16 +691,16 @@ emptyToDisableSmtpAuth: "Lasciare i campi vuoti se non c'è autenticazione SMTP"
smtpSecure: "Usare SSL/TLS implicito per le connessioni SMTP"
smtpSecureInfo: "Disabilitare quando è attivo STARTTLS."
testEmail: "Verifica il funzionamento"
wordMute: "Filtri parole"
wordMuteDescription: "Contrae le Note con la parola o la frase specificata. Permette di espandere le Note, cliccandole."
hardWordMute: "Filtro parole forte"
wordMute: "Parole silenziate"
wordMuteDescription: "Comprimi le Note che hanno la parola o la regola specificata. Cliccale per espanderle e leggerne comunque il contenuto."
hardWordMute: "Filtro per parole"
showMutedWord: "Elenca le parole silenziate"
hardWordMuteDescription: "Nasconde le Note con la parola o la frase specificata. A differenza delle parole silenziate, la Nota non verrà federata."
hardWordMuteDescription: "Ignora le Note con la parola o la regola specificata. A differenza delle \"Parole Silenziate\", queste Note non ti verranno proprio recapitate."
regexpError: "errore regex"
regexpErrorDescription: "Si è verificato un errore nell'espressione regolare alla riga {line} della parola muta {tab}:"
instanceMute: "Silenziare l'istanza"
userSaysSomething: "{name} ha detto qualcosa"
userSaysSomethingAbout: "{name} ha Notato a riguardo di \"{word}\""
userSaysSomethingAbout: "{name} ha anNotato qualcosa su \"{word}\""
makeActive: "Attiva"
display: "Visualizza"
copy: "Copia"
@@ -1547,6 +1547,7 @@ _settings:
showAvailableReactionsFirstInNote: "Mostra le reazioni disponibili in alto"
showPageTabBarBottom: "Visualizza le schede della pagina nella parte inferiore"
emojiPaletteBanner: "Puoi salvare i le emoji predefinite da appuntare in alto nel raccoglitore emoji come tavolozza e personalizzare in che modo visualizzare il raccoglitore."
enableAnimatedImages: "Attivare le immagini animate"
_chat:
showSenderName: "Mostra il nome del mittente"
sendOnEnter: "Invio spedisce"
@@ -1555,6 +1556,8 @@ _preferencesProfile:
profileNameDescription: "Impostare il nome che indentifica questo dispositivo."
profileNameDescription2: "Es: \"PC principale\" o \"Cellulare\""
manageProfiles: "Gestione profili"
shareSameProfileBetweenDevicesIsNotRecommended: "Si sconsiglia di condividere lo stesso profilo su più dispositivi."
useSyncBetweenDevicesOptionIfYouWantToSyncSetting: "Se intendi sincronizzare solo alcuni parametri di configurazione su più dispositivi, devi attivare l'opzione \"Sincronizzazione tra dispositivi\" per ogni parametro interessato."
_preferencesBackup:
autoBackup: "Backup automatico"
restoreFromBackup: "Ripristinare da backup"
@@ -2591,9 +2594,9 @@ _postForm:
visibility_title: "Visibilità"
visibility_description: "Puoi impostare il grado di visibilità delle Note."
menu_title: "Menù"
menu_description: "Puoi svolgere altre azioni, come il salvataggio in bozza, i post di prenotazione, l'impostazione delle reazioni e così via."
menu_description: "Puoi svolgere varie azioni, come salvare in bozza, pianificare le annotazioni, regolare le reazioni ricevute e altro."
submit_title: "Bottone invia"
submit_description: "Posterò un biglietto. Puoi anche fare Ctrl + Inserisci oppure Cmd + Inserisci."
submit_description: "Pubblica la Nota. Funziona anche con \"Ctrl + Invio\", oppure \"Cmd + Invio\"."
_placeholders:
a: "Come va?"
b: "Hai qualcosa da raccontare? Inizia pure..."
+4
View File
@@ -83,6 +83,8 @@ files: "ファイル"
download: "ダウンロード"
driveFileDeleteConfirm: "ファイル「{name}」を削除しますか?このファイルを使用した一部のコンテンツも削除されます。"
unfollowConfirm: "{name}のフォローを解除しますか?"
cancelFollowRequestConfirm: "{name}へのフォロー申請をキャンセルしますか?"
rejectFollowRequestConfirm: "{name}からのフォロー申請を拒否しますか?"
exportRequested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、「ドライブ」に追加されます。"
importRequested: "インポートをリクエストしました。これには時間がかかる場合があります。"
lists: "リスト"
@@ -1565,6 +1567,8 @@ _preferencesProfile:
profileNameDescription: "このデバイスを識別する名前を設定してください。"
profileNameDescription2: "例: 「メインPC」、「スマホ」など"
manageProfiles: "プロファイルの管理"
shareSameProfileBetweenDevicesIsNotRecommended: "複数のデバイスで同一のプロファイルを共有することは推奨しません。"
useSyncBetweenDevicesOptionIfYouWantToSyncSetting: "複数のデバイスで同期したい設定項目が存在する場合は、個別に「複数のデバイスで同期」オプションを有効にしてください。"
_preferencesBackup:
autoBackup: "自動バックアップ"
+3
View File
@@ -1547,6 +1547,7 @@ _settings:
showAvailableReactionsFirstInNote: "이용 가능한 리액션을 선두로 표시"
showPageTabBarBottom: "페이지의 탭 바를 아래쪽에 표시"
emojiPaletteBanner: "이모티콘 선택기에 고정 표시되는 프리셋을 팔레트로 등록하거나 선택기의 표시 방법을 커스터마이징할 수 있습니다."
enableAnimatedImages: "애니메이션 이미지 활성화"
_chat:
showSenderName: "발신자 이름 표시"
sendOnEnter: "엔터로 보내기"
@@ -1555,6 +1556,8 @@ _preferencesProfile:
profileNameDescription: "이 디바이스를 식별할 이름을 설정해 주세요."
profileNameDescription2: "예: '메인PC', '스마트폰' 등"
manageProfiles: "프로파일 관리"
shareSameProfileBetweenDevicesIsNotRecommended: "여러 장치에서 동일한 프로필을 공유하는 것은 권장하지 않습니다."
useSyncBetweenDevicesOptionIfYouWantToSyncSetting: "여러 장치에서 동기화하고 싶은 설정 항목이 있는 경우에는 개별로 '여러 장치에서 동기화' 옵션을 활성화해 주십시오."
_preferencesBackup:
autoBackup: "자동 백업"
restoreFromBackup: "백업으로 복구"
+5 -3
View File
@@ -87,7 +87,7 @@ exportRequested: "Вы запросили экспорт. Это может за
importRequested: "Вы запросили импорт. Это может занять некоторое время."
lists: "Списки"
noLists: "Нет ни одного списка"
note: "Заметка"
note: "Пост"
notes: "Заметки"
following: "Подписки"
followers: "Подписчики"
@@ -122,7 +122,7 @@ inChannelRenote: "В канале"
inChannelQuote: "Заметки в канале"
renoteToChannel: "Репостнуть в канал"
renoteToOtherChannel: "Репостнуть в другой канал"
pinnedNote: "Закреплённая заметка"
pinnedNote: "Закреплённый пост"
pinned: "Закрепить в профиле"
you: "Вы"
clickToShow: "Нажмите для просмотра"
@@ -253,6 +253,7 @@ noteDeleteConfirm: "Вы хотите удалить эту заметку?"
pinLimitExceeded: "Нельзя закрепить ещё больше заметок"
done: "Готово"
processing: "Обработка"
preprocessing: "Подготовка..."
preview: "Предпросмотр"
default: "По умолчанию"
defaultValueIs: "По умолчанию: {value}"
@@ -396,7 +397,7 @@ pinnedUsersDescription: "Перечислите по одному имени п
pinnedPages: "Закрепленные страницы"
pinnedPagesDescription: "Если хотите закрепить страницы на главной сайта, сюда можно добавить пути к ним, каждый в отдельной строке."
pinnedClipId: "Идентификатор закреплённой подборки"
pinnedNotes: "Закреплённая заметка"
pinnedNotes: "Закреплённый пост"
hcaptcha: "hCaptcha"
enableHcaptcha: "Включить hCaptcha"
hcaptchaSiteKey: "Ключ сайта"
@@ -1164,6 +1165,7 @@ installed: "Установлено"
branding: "Бренд"
enableServerMachineStats: "Опубликовать характеристики сервера"
enableIdenticonGeneration: "Включить генерацию иконки пользователя"
showRoleBadgesOfRemoteUsers: "Display the role badges assigned to remote users"
turnOffToImprovePerformance: "Отключение этого параметра может повысить производительность."
createInviteCode: "Создать код приглашения"
createWithOptions: "Используйте параметры для создания"
+49 -34
View File
@@ -474,7 +474,7 @@ passwordLessLogin: "无密码登录"
passwordLessLoginDescription: "不使用密码,仅使用安全密钥或 Passkey 登录"
resetPassword: "重置密码"
newPasswordIs: "新的密码是「{password}」"
reduceUiAnimation: "减少UI动画"
reduceUiAnimation: "减少 UI 动画"
share: "分享"
notFound: "未找到"
notFoundDescription: "没有与指定 URL 对应的页面。"
@@ -795,7 +795,7 @@ makeExplorable: "使账号可见。"
makeExplorableDescription: "关闭时,账号不会显示在\"发现\"中。"
duplicate: "复制"
left: "左"
center: "中"
center: "中"
wide: "宽"
narrow: "窄"
reloadToApplySetting: "页面刷新后设置才会生效。是否现在刷新页面?"
@@ -817,7 +817,7 @@ advanced: "高级"
advancedSettings: "高级设置"
value: "值"
createdAt: "创建日期"
updatedAt: "更新时间"
updatedAt: "更新日期"
saveConfirm: "确定保存?"
deleteConfirm: "确定删除?"
invalidValue: "无效值。"
@@ -1023,6 +1023,9 @@ pushNotificationAlreadySubscribed: "推送通知消息已启用"
pushNotificationNotSupported: "浏览器或服务器不支持推送通知消息"
sendPushNotificationReadMessage: "删除已读推送通知消息"
sendPushNotificationReadMessageCaption: "您终端设备的电池消耗可能会增加。"
pleaseAllowPushNotification: "请在浏览器中启用推送通知"
browserPushNotificationDisabled: "未能获取发送通知的权限"
browserPushNotificationDisabledDescription: "{serverName}无权限发送通知。请在浏览器设置中允许通知后重新尝试。"
windowMaximize: "最大化"
windowMinimize: "最小化"
windowRestore: "还原"
@@ -1207,7 +1210,7 @@ renotes: "转发"
loadReplies: "查看回复"
loadConversation: "查看对话"
pinnedList: "已置顶的列表"
keepScreenOn: "保持设备屏幕开启"
keepScreenOn: "保持屏幕常亮"
verifiedLink: "已验证的链接"
notifyNotes: "打开发帖通知"
unnotifyNotes: "关闭发帖通知"
@@ -1332,7 +1335,7 @@ accessibility: "辅助功能"
preferencesProfile: "设置的配置"
copyPreferenceId: "复制设置 ID"
resetToDefaultValue: "重置为默认值"
overrideByAccount: "用账户覆盖"
overrideByAccount: "覆盖账号"
untitled: "未命名"
noName: "没有名字"
skip: "跳过"
@@ -1398,13 +1401,14 @@ widgets: "小工具"
deviceInfo: "设备信息"
deviceInfoDescription: "咨询技术问题时,将以下信息一并发送有助于解决问题。"
youAreAdmin: "你是管理员"
frame: "边框"
presets: "预设值"
zeroPadding: "填充 0"
_imageEditing:
_vars:
caption: "文件标题"
filename: "文件名称"
filename_without_ext: "无扩展文件名"
filename_without_ext: "不带扩展名的文件名"
year: "拍摄年"
month: "拍摄月"
day: "拍摄日"
@@ -1414,26 +1418,31 @@ _imageEditing:
camera_model: "相机名称"
camera_lens_model: "镜头型号"
camera_mm: "焦距"
camera_mm_35: "焦距(35mm等效)"
camera_f: "光圈"
camera_s: "快门速度"
camera_iso: "ISO"
gps_lat: "纬度"
gps_long: "经度"
_imageFrameEditor:
title: "编辑边框"
tip: "您可以通过添加包含边框和元数据的标签来装饰图片。"
header: "顶栏"
footer: "底部"
footer: "页脚"
borderThickness: "边框宽度"
labelThickness: "标签宽度"
labelScale: "标签比例"
centered: "居中"
captionMain: "标题(大)"
captionSub: "标题(小)"
availableVariables: "可变量"
availableVariables: "可修改的变量"
withQrCode: "二维码"
backgroundColor: "背景色"
textColor: "文色"
backgroundColor: "背景色"
textColor: "文本颜色"
font: "字体"
fontSerif: "衬线字体"
fontSansSerif: "无衬线字体"
quitWithoutSaveConfirm: "不保存就退出吗"
quitWithoutSaveConfirm: "放弃未保存的更改"
failedToLoadImage: "图片加载失败"
_compression:
_quality:
@@ -1452,36 +1461,36 @@ _chat:
noMessagesYet: "还没有消息"
newMessage: "新消息"
individualChat: "私聊"
individualChat_description: "可以与特定用户进行一对一聊天。"
individualChat_description: "与特定用户单独聊天。"
roomChat: "群聊"
roomChat_description: "支持多人同时进行消息交流。\n即使部分用户未开放私信权限,只要接受邀请,仍可进行聊天。"
createRoom: "创建群"
inviteUserToChat: "邀请用户来开始聊天"
yourRooms: "创建的群"
joiningRooms: "已加入的群"
roomChat_description: "支持多人同时聊天。\n即使对方不允许私聊,只要接受邀请也能加入。"
createRoom: "创建群"
inviteUserToChat: "邀请用户来聊天"
yourRooms: "创建的群"
joiningRooms: "已加入的群"
invitations: "邀请"
noInvitations: "没有邀请"
history: "历史"
noHistory: "没有历史记录"
noRooms: "没有群"
noRooms: "没有群"
inviteUser: "邀请用户"
sentInvitations: "已发送的邀请"
join: "加入"
ignore: "忽略"
leave: "退出房间"
leave: "退出群聊"
members: "成员"
searchMessages: "搜索消息"
home: "首页"
send: "发送"
newline: "换行"
muteThisRoom: "屏蔽该群"
deleteRoom: "删除群"
muteThisRoom: "屏蔽该群"
deleteRoom: "删除群"
chatNotAvailableForThisAccountOrServer: "此服务器或者账户还未开启聊天功能。"
chatIsReadOnlyForThisAccountOrServer: "此服务器或者账户内的聊天为只读。无法发布新信息或创建及加入群聊。"
chatNotAvailableInOtherAccount: "对方的账户当前无法使用私信。"
cannotChatWithTheUser: "无法私信该用户"
cannotChatWithTheUser_description: "可能现在无法使用聊天,或者对方未开启聊天。"
youAreNotAMemberOfThisRoomButInvited: "您未加入此房间,但已收到邀请。如要加入,请接受邀请。"
youAreNotAMemberOfThisRoomButInvited: "您未加入此群组,但已收到加入邀请。请接受邀请加入。"
doYouAcceptInvitation: "要接受邀请吗?"
chatWithThisUser: "私信"
thisUserAllowsChatOnlyFromFollowers: "此用户仅接受关注者发起的聊天。"
@@ -1538,6 +1547,7 @@ _settings:
showAvailableReactionsFirstInNote: "在顶部显示可用的回应"
showPageTabBarBottom: "在下方显示页面标签栏"
emojiPaletteBanner: "可以将固定显示表情符号选择器的预设注册至调色板,也可以自定义表情符号选择器的显示方式。"
enableAnimatedImages: "启用动画图像"
_chat:
showSenderName: "显示发送者的名字"
sendOnEnter: "回车键发送"
@@ -1546,6 +1556,8 @@ _preferencesProfile:
profileNameDescription: "请指定用于识别此设备的名称"
profileNameDescription2: "如「PC」、「手机」等"
manageProfiles: "管理配置文件"
shareSameProfileBetweenDevicesIsNotRecommended: "不建议在多个设备间共用同一个配置文件。"
useSyncBetweenDevicesOptionIfYouWantToSyncSetting: "若想在多个设备间同步某些设置,请为每个设置打开「多设备间同步」选项。"
_preferencesBackup:
autoBackup: "自动备份"
restoreFromBackup: "从备份恢复"
@@ -1555,6 +1567,7 @@ _preferencesBackup:
youNeedToNameYourProfileToEnableAutoBackup: "需指定配置名以开启自动备份。"
autoPreferencesBackupIsNotEnabledForThisDevice: "此设备未开启自动备份"
backupFound: "已找到备份"
forceBackup: "强制备份设置"
_accountSettings:
requireSigninToViewContents: "需要登录才能显示内容"
requireSigninToViewContentsDescription1: "您发布的所有帖子将变成需要登入后才会显示。有望防止爬虫收集各种信息。"
@@ -2216,7 +2229,7 @@ _instanceTicker:
_serverDisconnectedBehavior:
reload: "自动重载"
dialog: "对话框警告"
quiet: "静警告"
quiet: "静警告"
_channel:
create: "创建频道"
edit: "编辑频道"
@@ -2535,7 +2548,7 @@ _poll:
noOnlyOneChoice: "需要至少两个选项"
choiceN: "选项{n}"
noMore: "无法再添加更多了"
canMultipleVote: "允许选择多个选项"
canMultipleVote: "允许选"
expiration: "截止时间"
infinite: "永久"
at: "指定日期"
@@ -2734,7 +2747,7 @@ _notification:
newNote: "新的帖子"
unreadAntennaNote: "天线 {name}"
roleAssigned: "授予的角色"
chatRoomInvitationReceived: "受邀加入聊天室"
chatRoomInvitationReceived: "您已被邀请加入群聊"
emptyPushNotificationMessage: "推送通知已更新"
achievementEarned: "获得成就"
testNotification: "测试通知"
@@ -2765,7 +2778,7 @@ _notification:
receiveFollowRequest: "收到关注请求"
followRequestAccepted: "关注请求已通过"
roleAssigned: "授予的角色"
chatRoomInvitationReceived: "受邀加入聊天室"
chatRoomInvitationReceived: "您已被邀请加入群聊"
achievementEarned: "取得的成就"
exportCompleted: "已完成导出"
login: "登录"
@@ -2861,6 +2874,8 @@ _abuseReport:
notifiedWebhook: "使用的 webhook"
deleteConfirm: "要删除通知吗?"
_moderationLogTypes:
clearQueue: "清除队列"
promoteQueue: "重新执行队列中的任务"
createRole: "创建角色"
deleteRole: "删除角色"
updateRole: "更新角色"
@@ -2905,11 +2920,11 @@ _moderationLogTypes:
createAbuseReportNotificationRecipient: "新建了举报通知"
updateAbuseReportNotificationRecipient: "更新了举报通知"
deleteAbuseReportNotificationRecipient: "删除了举报通知"
deleteAccount: "删除了账户"
deletePage: "删除页面"
deleteFlash: "删除 Play"
deleteAccount: "删除户"
deletePage: "删除页面"
deleteFlash: "删除 Play"
deleteGalleryPost: "删除图集内容"
deleteChatRoom: "删除聊天室"
deleteChatRoom: "删除聊"
updateProxyAccountDescription: "更新代理账户的简介"
_fileViewer:
title: "文件信息"
@@ -3182,7 +3197,7 @@ _search:
serverHostPlaceholder: "如:misskey.example.com"
_serverSetupWizard:
installCompleted: "Misskey 安装完成!"
firstCreateAccount: "首先创建管理员账号吧。"
firstCreateAccount: "首先创建一个管理员帐户。"
accountCreated: "管理员账号已创建!"
serverSetting: "服务器设置"
youCanEasilyConfigureOptimalServerSettingsWithThisWizard: "用此向导来轻松地以最佳方式配置服务器。"
@@ -3192,7 +3207,7 @@ _serverSetupWizard:
single: "单用户服务器"
single_description: "仅供自己使用的单人服务器"
single_youCanCreateMultipleAccounts: "使用单用户服务器模式使用时,也可以根据需要创建多个账号。"
group: "小圈子服务器"
group: "群组服务器"
group_description: "邀请其他可信用户一起使用的多人服务器"
open: "开放服务器"
open_description: "以容纳不限定数量的用户的模式运行"
@@ -3229,7 +3244,7 @@ _uploader:
compressedToX: "压缩 {x}"
savedXPercent: "节省了 {x}% 的空间"
abortConfirm: "还有未上传的文件,要中止吗?"
doneConfirm: "还有未上传的文件,要完成吗"
doneConfirm: "部分文件尚未上传,是否继续"
maxFileSizeIsX: "可上传最大 {x} 的文件。"
allowedTypes: "可上传的文件类型"
tip: "文件还没有被上传。可在此对话框中进行上传前确认、重命名、压缩、裁剪等操作。准备完成后,点击「上传」即可开始上传。"
@@ -3249,7 +3264,7 @@ watermark: "水印"
defaultPreset: "默认预设"
_watermarkEditor:
tip: "可在图像内增加包含作者等信息的水印。"
quitWithoutSaveConfirm: "不保存就退出吗"
quitWithoutSaveConfirm: "放弃未保存的更改"
driveFileTypeWarn: "不支持此文件"
driveFileTypeWarnDescription: "请选择图像文件"
title: "编辑水印"
+4 -1
View File
@@ -172,7 +172,7 @@ emojiUrl: "表情符號 URL"
addEmoji: "新增表情符號"
settingGuide: "推薦設定"
cacheRemoteFiles: "快取遠端檔案"
cacheRemoteFilesDescription: "啟用設定後,遠端檔案會被快取在本伺服器的儲存空間中。雖然顯示圖片會變快,但會消耗較多伺服器的儲存空間。至於要快取遠端使用者到什麼程度,是依照角色的雲端硬碟容量而定。當超過這個限制時,從較舊的檔案開始自快取中刪除並改連結。關閉這個設定,遠端檔案一開始就維持連結的方式,但建議將 default.yml 的 proxyRemoteFiles 設為 true,以便產生圖片的縮圖並保護使用者的隱私。"
cacheRemoteFilesDescription: "啟用這個設定後,遠端檔案會被快取到這台伺服器的儲存空間中。這樣能加快圖片的顯示速度,但會多占用伺服器的儲存容量。遠端使用者能保留多少快取,取決於其角色所設定的硬碟容量上限。若超過這個上限,系統會從最舊的檔案開始刪除快取並改連結。若停用這個設定,遠端檔案一開始就只會以連結的形式保留。"
youCanCleanRemoteFilesCache: "按檔案管理的🗑️按鈕,可將快取全部刪除。"
cacheRemoteSensitiveFiles: "快取遠端的敏感檔案"
cacheRemoteSensitiveFilesDescription: "若停用這個設定,則不會快取遠端的敏感檔案,而是直接連結。"
@@ -1547,6 +1547,7 @@ _settings:
showAvailableReactionsFirstInNote: "將可用的反應顯示在頂部"
showPageTabBarBottom: "在底部顯示頁面的標籤列"
emojiPaletteBanner: "可以將固定顯示在表情符號選擇器的預設項目註冊為調色盤,或者自訂選擇器的顯示方式。"
enableAnimatedImages: "啟用動畫圖片"
_chat:
showSenderName: "顯示發送者的名稱"
sendOnEnter: "按下 Enter 發送訊息"
@@ -1555,6 +1556,8 @@ _preferencesProfile:
profileNameDescription: "設定一個名稱來識別此裝置。"
profileNameDescription2: "例如:「主要個人電腦」、「智慧型手機」等"
manageProfiles: "管理個人檔案"
shareSameProfileBetweenDevicesIsNotRecommended: "不建議在多個裝置上共用同一個設定檔。"
useSyncBetweenDevicesOptionIfYouWantToSyncSetting: "如果您希望在多個裝置之間同步某些設定項目,請分別啟用「跨裝置同步」選項。"
_preferencesBackup:
autoBackup: "自動備份"
restoreFromBackup: "從備份還原"
+13 -12
View File
@@ -1,12 +1,12 @@
{
"name": "misskey",
"version": "2025.11.0-beta.0",
"version": "2025.11.1-beta.1",
"codename": "nasubi",
"repository": {
"type": "git",
"url": "https://github.com/misskey-dev/misskey.git"
},
"packageManager": "pnpm@10.20.0",
"packageManager": "pnpm@10.22.0",
"workspaces": [
"packages/frontend-shared",
"packages/frontend",
@@ -26,6 +26,7 @@
"build-storybook": "pnpm --filter frontend build-storybook",
"build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json --no-build && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
"start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js",
"start:inspect": "cd packages/backend && node --inspect ./built/boot/entry.js",
"start:test": "ncp ./.github/misskey/test.yml ./.config/test.yml && cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js",
"cli": "cd packages/backend && pnpm cli",
"init": "pnpm migrate",
@@ -54,7 +55,7 @@
},
"dependencies": {
"cssnano": "7.1.2",
"esbuild": "0.25.11",
"esbuild": "0.27.0",
"execa": "9.6.0",
"fast-glob": "3.3.3",
"glob": "11.0.3",
@@ -62,22 +63,22 @@
"js-yaml": "4.1.0",
"postcss": "8.5.6",
"tar": "7.5.2",
"terser": "5.44.0",
"terser": "5.44.1",
"typescript": "5.9.3"
},
"devDependencies": {
"@eslint/js": "9.39.0",
"@misskey-dev/eslint-plugin": "2.1.0",
"@eslint/js": "9.39.1",
"@misskey-dev/eslint-plugin": "2.2.0",
"@types/js-yaml": "4.0.9",
"@types/node": "24.9.2",
"@typescript-eslint/eslint-plugin": "8.46.2",
"@typescript-eslint/parser": "8.46.2",
"@types/node": "24.10.1",
"@typescript-eslint/eslint-plugin": "8.47.0",
"@typescript-eslint/parser": "8.47.0",
"cross-env": "10.1.0",
"cypress": "15.5.0",
"eslint": "9.39.0",
"cypress": "15.6.0",
"eslint": "9.39.1",
"globals": "16.5.0",
"ncp": "2.0.0",
"pnpm": "10.20.0",
"pnpm": "10.22.0",
"start-server-and-test": "2.1.2"
},
"optionalDependencies": {
+25 -25
View File
@@ -8,6 +8,7 @@
},
"scripts": {
"start": "node ./built/boot/entry.js",
"start:inspect": "node --inspect ./built/boot/entry.js",
"start:test": "cross-env NODE_ENV=test node ./built/boot/entry.js",
"migrate": "pnpm typeorm migration:run -d ormconfig.js",
"revert": "pnpm typeorm migration:revert -d ormconfig.js",
@@ -39,17 +40,17 @@
},
"optionalDependencies": {
"@swc/core-android-arm64": "1.3.11",
"@swc/core-darwin-arm64": "1.14.0",
"@swc/core-darwin-x64": "1.14.0",
"@swc/core-darwin-arm64": "1.15.1",
"@swc/core-darwin-x64": "1.15.1",
"@swc/core-freebsd-x64": "1.3.11",
"@swc/core-linux-arm-gnueabihf": "1.14.0",
"@swc/core-linux-arm64-gnu": "1.14.0",
"@swc/core-linux-arm64-musl": "1.14.0",
"@swc/core-linux-x64-gnu": "1.14.0",
"@swc/core-linux-x64-musl": "1.14.0",
"@swc/core-win32-arm64-msvc": "1.14.0",
"@swc/core-win32-ia32-msvc": "1.14.0",
"@swc/core-win32-x64-msvc": "1.14.0",
"@swc/core-linux-arm-gnueabihf": "1.15.1",
"@swc/core-linux-arm64-gnu": "1.15.1",
"@swc/core-linux-arm64-musl": "1.15.1",
"@swc/core-linux-x64-gnu": "1.15.1",
"@swc/core-linux-x64-musl": "1.15.1",
"@swc/core-win32-arm64-msvc": "1.15.1",
"@swc/core-win32-ia32-msvc": "1.15.1",
"@swc/core-win32-x64-msvc": "1.15.1",
"@tensorflow/tfjs": "4.22.0",
"@tensorflow/tfjs-node": "4.22.0",
"bufferutil": "4.0.9",
@@ -69,8 +70,8 @@
"utf-8-validate": "6.0.5"
},
"dependencies": {
"@aws-sdk/client-s3": "3.922.0",
"@aws-sdk/lib-storage": "3.922.0",
"@aws-sdk/client-s3": "3.927.0",
"@aws-sdk/lib-storage": "3.927.0",
"@discordapp/twemoji": "16.0.1",
"@fastify/accepts": "5.0.3",
"@fastify/cookie": "11.0.2",
@@ -87,13 +88,13 @@
"@nestjs/core": "11.1.8",
"@nestjs/testing": "11.1.8",
"@peertube/http-signature": "1.7.0",
"@sentry/node": "10.22.0",
"@sentry/profiling-node": "10.22.0",
"@sentry/node": "10.23.0",
"@sentry/profiling-node": "10.23.0",
"@simplewebauthn/server": "12.0.0",
"@sinonjs/fake-timers": "11.3.1",
"@smithy/node-http-handler": "2.5.0",
"@swc/cli": "0.7.8",
"@swc/core": "1.14.0",
"@swc/cli": "0.7.9",
"@swc/core": "1.15.1",
"@twemoji/parser": "16.0.0",
"@types/redis-info": "3.0.3",
"accepts": "1.3.8",
@@ -109,18 +110,17 @@
"chalk": "5.6.2",
"chalk-template": "1.1.2",
"chokidar": "4.0.3",
"cli-highlight": "2.1.11",
"color-convert": "2.0.1",
"content-disposition": "0.5.4",
"date-fns": "2.30.0",
"deep-email-validator": "0.1.21",
"fastify": "5.6.1",
"fastify": "5.6.2",
"fastify-raw-body": "5.0.0",
"feed": "4.2.2",
"file-type": "21.0.0",
"fluent-ffmpeg": "2.1.3",
"form-data": "4.0.4",
"got": "14.6.1",
"got": "14.6.3",
"happy-dom": "20.0.10",
"hpagent": "1.2.0",
"htmlescape": "1.1.1",
@@ -161,7 +161,7 @@
"qrcode": "1.5.4",
"random-seed": "0.3.0",
"ratelimiter": "3.4.1",
"re2": "1.22.1",
"re2": "1.22.3",
"redis-info": "3.1.0",
"redis-lock": "0.1.4",
"reflect-metadata": "0.2.2",
@@ -170,8 +170,8 @@
"rxjs": "7.8.2",
"sanitize-html": "2.17.0",
"secure-json-parse": "3.0.2",
"sharp": "0.33.5",
"semver": "7.7.3",
"sharp": "0.33.5",
"slacc": "0.0.10",
"strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0",
@@ -191,7 +191,7 @@
"devDependencies": {
"@jest/globals": "29.7.0",
"@nestjs/platform-express": "10.4.20",
"@sentry/vue": "10.22.0",
"@sentry/vue": "10.23.0",
"@simplewebauthn/types": "12.0.0",
"@swc/jest": "0.2.39",
"@types/accepts": "1.3.7",
@@ -210,7 +210,7 @@
"@types/jsrsasign": "10.5.15",
"@types/mime-types": "2.1.4",
"@types/ms": "0.7.34",
"@types/node": "24.9.2",
"@types/node": "24.10.0",
"@types/nodemailer": "6.4.21",
"@types/oauth": "0.9.6",
"@types/oauth2orize": "1.11.5",
@@ -231,8 +231,8 @@
"@types/vary": "1.1.3",
"@types/web-push": "3.6.4",
"@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.46.2",
"@typescript-eslint/parser": "8.46.2",
"@typescript-eslint/eslint-plugin": "8.46.3",
"@typescript-eslint/parser": "8.46.3",
"aws-sdk-client-mock": "4.1.0",
"cross-env": "7.0.3",
"eslint-plugin-import": "2.32.0",
+1 -1
View File
@@ -41,7 +41,7 @@ function greet() {
//#endregion
console.log(' Misskey is an open-source decentralized microblogging platform.');
console.log(chalk.rgb(255, 136, 0)(' If you like Misskey, please donate to support development. https://www.patreon.com/syuilo'));
console.log(chalk.rgb(255, 136, 0)(' If you like Misskey, please consider donating to support dev. https://misskey-hub.net/docs/donate/'));
console.log('');
console.log(chalkTemplate`--- ${os.hostname()} {gray (PID: ${process.pid.toString()})} ---`);
+1 -64
View File
@@ -5,18 +5,9 @@
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 { MiAbuseReportNotificationRecipient } from '@/models/AbuseReportNotificationRecipient.js';
import { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
import { MiAccessToken } from '@/models/AccessToken.js';
@@ -96,66 +87,12 @@ import { MiWebhook } from '@/models/Webhook.js';
import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.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;
}
export const miRepository = {
createTableColumnNames() {
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!;
const name = mainAlias.name;
mainAlias.name = 't';
const columnNames = this.createTableColumnNames();
queryBuilder.returning(columnNames.reduce((a, c) => `${a}, ${queryBuilder.escape(c)}`, '').slice(2));
// ---- 共通テーブル式(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);
if (findOptions) {
builder.setFindOptions(findOptions);
}
const raw = await builder.execute();
mainAlias.name = name;
const relationId = await new RelationIdLoader(builder.connection, this.queryRunner, builder.expressionMap.relationIdAttributes).load(raw);
const relationCount = await new RelationCountLoader(builder.connection, this.queryRunner, builder.expressionMap.relationCountAttributes).load(raw);
const result = new RawSqlResultsToEntityTransformer(builder.expressionMap, builder.connection.driver, relationId, relationCount, this.queryRunner).transform(raw, mainAlias);
return result[0];
},
selectAliasColumnNames(queryBuilder, builder) {
let selectOrAddSelect = (selection: string, selectionAliasName?: string) => {
selectOrAddSelect = (selection, selectionAliasName) => builder.addSelect(selection, selectionAliasName);
return builder.select(selection, selectionAliasName);
};
for (const columnName of this.createTableColumnNames()) {
selectOrAddSelect(`${builder.alias}.${columnName}`, `${builder.alias}_${columnName}`);
}
return await this.insert(entity).then(x => this.findOneOrFail({ where: x.identifiers[0], ...findOptions }));
},
} satisfies MiRepository<ObjectLiteral>;
+2 -9
View File
@@ -6,7 +6,6 @@
// https://github.com/typeorm/typeorm/issues/2400
import pg from 'pg';
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';
import MisskeyLogger from '@/logger.js';
@@ -25,7 +24,7 @@ import { MiAuthSession } from '@/models/AuthSession.js';
import { MiBlocking } from '@/models/Blocking.js';
import { MiChannelFollowing } from '@/models/ChannelFollowing.js';
import { MiChannelFavorite } from '@/models/ChannelFavorite.js';
import { MiChannelMuting } from "@/models/ChannelMuting.js";
import { MiChannelMuting } from '@/models/ChannelMuting.js';
import { MiClip } from '@/models/Clip.js';
import { MiClipNote } from '@/models/ClipNote.js';
import { MiClipFavorite } from '@/models/ClipFavorite.js';
@@ -101,12 +100,6 @@ export type LoggerProps = {
printReplicationMode?: boolean,
};
function highlightSql(sql: string) {
return highlight.highlight(sql, {
language: 'sql', ignoreIllegals: true,
});
}
function truncateSql(sql: string) {
return sql.length > 100 ? `${sql.substring(0, 100)}...` : sql;
}
@@ -132,7 +125,7 @@ class MyCustomLogger implements Logger {
modded = truncateSql(modded);
}
return highlightSql(modded);
return modded;
}
@bindThis
@@ -5,6 +5,7 @@
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueryService } from '@/core/QueryService.js';
import type { ClipFavoritesRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { ClipEntityService } from '@/core/entities/ClipEntityService.js';
@@ -30,6 +31,11 @@ export const meta = {
export const paramDef = {
type: 'object',
properties: {
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
sinceId: { type: 'string', format: 'misskey:id' },
untilId: { type: 'string', format: 'misskey:id' },
sinceDate: { type: 'integer' },
untilDate: { type: 'integer' },
},
required: [],
} as const;
@@ -40,14 +46,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.clipFavoritesRepository)
private clipFavoritesRepository: ClipFavoritesRepository,
private queryService: QueryService,
private clipEntityService: ClipEntityService,
) {
super(meta, paramDef, async (ps, me) => {
const query = this.clipFavoritesRepository.createQueryBuilder('favorite')
const query = this.queryService.makePaginationQuery(this.clipFavoritesRepository.createQueryBuilder('favorite'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
.andWhere('favorite.userId = :meId', { meId: me.id })
.leftJoinAndSelect('favorite.clip', 'clip');
const favorites = await query
.limit(ps.limit)
.getMany();
return this.clipEntityService.packMany(favorites.map(x => x.clip!), me);
@@ -295,8 +295,20 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (ps.chatScope !== undefined) updates.chatScope = ps.chatScope;
function checkMuteWordCount(mutedWords: (string[] | string)[], limit: number) {
// TODO: ちゃんと数える
const length = JSON.stringify(mutedWords).length;
const count = (arr: (string[] | string)[]) => {
let length = 0;
for (const item of arr) {
if (typeof item === 'string') {
length += item.length;
} else if (Array.isArray(item)) {
for (const subItem of item) {
length += subItem.length;
}
}
}
return length;
};
const length = count(mutedWords);
if (length > limit) {
throw new ApiError(meta.errors.tooManyMutedWords);
}
@@ -95,7 +95,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (targetLang.includes('-')) targetLang = targetLang.split('-')[0];
const params = new URLSearchParams();
params.append('auth_key', this.serverSettings.deeplAuthKey);
params.append('text', note.text);
params.append('target_lang', targetLang);
@@ -104,6 +103,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const res = await this.httpRequestService.send(endpoint, {
method: 'POST',
headers: {
'Authorization': `DeepL-Auth-Key ${this.serverSettings.deeplAuthKey}`,
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json, */*',
},
@@ -41,6 +41,10 @@ class ChannelChannel extends Channel {
private async onNote(note: Packed<'Note'>) {
if (note.channelId !== this.channelId) return;
if (note.user.requireSigninToViewContents && this.user == null) return;
if (note.renote && note.renote.user.requireSigninToViewContents && this.user == null) return;
if (note.reply && note.reply.user.requireSigninToViewContents && this.user == null) return;
if (this.isNoteMutedOrBlocked(note)) return;
if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
@@ -50,7 +50,7 @@ import { AnnouncementEntityService } from '@/core/entities/AnnouncementEntitySer
import { FeedService } from './FeedService.js';
import { UrlPreviewService } from './UrlPreviewService.js';
import { ClientLoggerService } from './ClientLoggerService.js';
import type { FastifyInstance, FastifyPluginOptions, FastifyReply } from 'fastify';
import type { FastifyError, FastifyInstance, FastifyPluginOptions, FastifyReply } from 'fastify';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
@@ -918,7 +918,7 @@ export class ClientServerService {
return await renderBase(reply);
});
fastify.setErrorHandler(async (error, request, reply) => {
fastify.setErrorHandler<FastifyError>(async (error, request, reply) => {
const errId = randomUUID();
this.clientLoggerService.logger.error(`Internal error occurred in ${request.routeOptions.url}: ${error.message}`, {
path: request.routeOptions.url,
@@ -55,7 +55,7 @@
//#region Script
async function importAppScript() {
await import(CLIENT_ENTRY ? `/embed_vite/${CLIENT_ENTRY.replace('scripts', lang)}` : '/embed_vite/src/_boot_.ts')
await import(CLIENT_ENTRY ? `/embed_vite/${CLIENT_ENTRY.replace('scripts', lang)}` : '/embed_vite/src/boot.ts')
.catch(async e => {
console.error(e);
renderError('APP_IMPORT');
@@ -28,7 +28,7 @@ html
meta(name='theme-color-orig' content= themeColor || '#86b300')
meta(property='og:site_name' content= instanceName || 'Misskey')
meta(property='instance_url' content= instanceUrl)
meta(name='viewport' content='width=device-width, initial-scale=1')
meta(name='viewport' content='width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover')
meta(name='format-detection' content='telephone=no,date=no,address=no,email=no,url=no')
link(rel='icon' href= icon || '/favicon.ico')
link(rel='apple-touch-icon' href= appleTouchIcon || '/apple-touch-icon.png')
@@ -95,7 +95,7 @@ services:
retries: 20
db:
image: postgres:15-alpine
image: postgres:18-alpine
env_file:
- ./.config/docker.env
volumes:
+1 -1
View File
@@ -5,7 +5,7 @@ services:
- "127.0.0.1:56312:6379"
dbtest:
image: postgres:15
image: postgres:18
ports:
- "127.0.0.1:54312:5432"
environment:
+8 -7
View File
@@ -506,10 +506,10 @@ describe('クリップ', () => {
});
};
const myFavorites = async (request: Partial<ApiRequest<'clips/my-favorites'>> = {}): Promise<Misskey.entities.Clip[]> => {
const myFavorites = async (parameters: Misskey.entities.ClipsMyFavoritesRequest, request: Partial<ApiRequest<'clips/my-favorites'>> = {}): Promise<Misskey.entities.Clip[]> => {
return successfulApiCall({
endpoint: 'clips/my-favorites',
parameters: {},
parameters,
user: alice,
...request,
});
@@ -562,8 +562,9 @@ describe('クリップ', () => {
await favorite({ clipId: clip.id });
}
// pagenationはない。全部一気にとれる。
const favorited = await myFavorites();
const favorited = await myFavorites({
limit: 30,
});
assert.strictEqual(favorited.length, clips.length);
for (const clip of favorited) {
assert.strictEqual(clip.favoritedCount, 1);
@@ -617,7 +618,7 @@ describe('クリップ', () => {
const clip = await show({ clipId: aliceClip.id });
assert.strictEqual(clip.favoritedCount, 0);
assert.strictEqual(clip.isFavorited, false);
assert.deepStrictEqual(await myFavorites(), []);
assert.deepStrictEqual(await myFavorites({}), []);
});
test.each([
@@ -651,13 +652,13 @@ describe('クリップ', () => {
test('を取得できる。', async () => {
await favorite({ clipId: aliceClip.id });
const favorited = await myFavorites();
const favorited = await myFavorites({});
assert.deepStrictEqual(favorited, [await show({ clipId: aliceClip.id })]);
});
test('を取得したとき他人のお気に入りは含まない。', async () => {
await favorite({ clipId: aliceClip.id });
const favorited = await myFavorites({ user: bob });
const favorited = await myFavorites({}, { user: bob });
assert.deepStrictEqual(favorited, []);
});
});
+5 -5
View File
@@ -11,15 +11,15 @@
},
"devDependencies": {
"@types/estree": "1.0.8",
"@types/node": "24.9.2",
"@typescript-eslint/eslint-plugin": "8.46.2",
"@typescript-eslint/parser": "8.46.2",
"rollup": "4.52.5",
"@types/node": "24.10.0",
"@typescript-eslint/eslint-plugin": "8.46.3",
"@typescript-eslint/parser": "8.46.3",
"rollup": "4.53.1",
"typescript": "5.9.3"
},
"dependencies": {
"estree-walker": "3.0.3",
"magic-string": "0.30.21",
"vite": "7.1.11"
"vite": "7.2.2"
}
}
+12 -12
View File
@@ -16,7 +16,7 @@
"@rollup/pluginutils": "5.3.0",
"@twemoji/parser": "16.0.0",
"@vitejs/plugin-vue": "6.0.1",
"@vue/compiler-sfc": "3.5.22",
"@vue/compiler-sfc": "3.5.24",
"astring": "1.9.0",
"buraha": "0.0.1",
"estree-walker": "3.0.3",
@@ -26,16 +26,16 @@
"mfm-js": "0.25.0",
"misskey-js": "workspace:*",
"punycode.js": "2.3.1",
"rollup": "4.52.5",
"rollup": "4.53.1",
"sass": "1.93.3",
"shiki": "3.14.0",
"shiki": "3.15.0",
"tinycolor2": "1.6.0",
"tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0",
"typescript": "5.9.3",
"uuid": "13.0.0",
"vite": "7.1.11",
"vue": "3.5.22"
"vite": "7.2.2",
"vue": "3.5.24"
},
"devDependencies": {
"@misskey-dev/summaly": "5.2.5",
@@ -43,14 +43,14 @@
"@testing-library/vue": "8.1.0",
"@types/estree": "1.0.8",
"@types/micromatch": "4.0.10",
"@types/node": "24.9.2",
"@types/node": "24.10.0",
"@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.46.2",
"@typescript-eslint/parser": "8.46.2",
"@typescript-eslint/eslint-plugin": "8.46.3",
"@typescript-eslint/parser": "8.46.3",
"@vitest/coverage-v8": "3.2.4",
"@vue/runtime-core": "3.5.22",
"@vue/runtime-core": "3.5.24",
"acorn": "8.15.0",
"cross-env": "10.1.0",
"eslint-plugin-import": "2.32.0",
@@ -59,14 +59,14 @@
"happy-dom": "20.0.10",
"intersection-observer": "0.12.2",
"micromatch": "4.0.8",
"msw": "2.11.6",
"msw": "2.12.1",
"nodemon": "3.1.10",
"prettier": "3.6.2",
"start-server-and-test": "2.1.2",
"tsx": "4.20.6",
"vite-plugin-turbosnap": "1.0.3",
"vue-component-type-helpers": "3.1.2",
"vue-component-type-helpers": "3.1.3",
"vue-eslint-parser": "10.2.0",
"vue-tsc": "3.1.2"
"vue-tsc": "3.1.3"
}
}
+5 -5
View File
@@ -21,10 +21,10 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
"@types/node": "24.9.2",
"@typescript-eslint/eslint-plugin": "8.46.2",
"@typescript-eslint/parser": "8.46.2",
"esbuild": "0.25.11",
"@types/node": "24.10.0",
"@typescript-eslint/eslint-plugin": "8.46.3",
"@typescript-eslint/parser": "8.46.3",
"esbuild": "0.27.0",
"eslint-plugin-vue": "10.5.1",
"nodemon": "3.1.10",
"typescript": "5.9.3",
@@ -35,6 +35,6 @@
],
"dependencies": {
"misskey-js": "workspace:*",
"vue": "3.5.22"
"vue": "3.5.24"
}
}
+16 -16
View File
@@ -24,12 +24,12 @@
"@rollup/plugin-json": "6.1.0",
"@rollup/plugin-replace": "6.0.3",
"@rollup/pluginutils": "5.3.0",
"@sentry/vue": "10.22.0",
"@sentry/vue": "10.23.0",
"@syuilo/aiscript": "1.1.2",
"@syuilo/aiscript-0-19-0": "npm:@syuilo/aiscript@^0.19.0",
"@twemoji/parser": "16.0.0",
"@vitejs/plugin-vue": "6.0.1",
"@vue/compiler-sfc": "3.5.22",
"@vue/compiler-sfc": "3.5.24",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15",
"analytics": "0.8.19",
"astring": "1.9.0",
@@ -58,7 +58,7 @@
"json5": "2.2.3",
"magic-string": "0.30.21",
"matter-js": "0.20.0",
"mediabunny": "1.24.2",
"mediabunny": "1.24.4",
"mfm-js": "0.25.0",
"misskey-bubble-game": "workspace:*",
"misskey-js": "workspace:*",
@@ -67,10 +67,10 @@
"punycode.js": "2.3.1",
"qr-code-styling": "1.9.2",
"qr-scanner": "1.4.2",
"rollup": "4.52.5",
"rollup": "4.53.1",
"sanitize-html": "2.17.0",
"sass": "1.93.3",
"shiki": "3.14.0",
"shiki": "3.15.0",
"strict-event-emitter-types": "2.0.0",
"textarea-caret": "3.1.0",
"three": "0.181.0",
@@ -80,8 +80,8 @@
"tsconfig-paths": "4.2.0",
"typescript": "5.9.3",
"v-code-diff": "1.13.1",
"vite": "7.1.11",
"vue": "3.5.22",
"vite": "7.2.2",
"vue": "3.5.24",
"vuedraggable": "next",
"wanakana": "5.3.1"
},
@@ -110,21 +110,21 @@
"@types/estree": "1.0.8",
"@types/matter-js": "0.20.2",
"@types/micromatch": "4.0.10",
"@types/node": "24.9.2",
"@types/node": "24.10.0",
"@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/sanitize-html": "2.16.0",
"@types/seedrandom": "3.0.8",
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.46.2",
"@typescript-eslint/parser": "8.46.2",
"@typescript-eslint/eslint-plugin": "8.46.3",
"@typescript-eslint/parser": "8.46.3",
"@vitest/coverage-v8": "3.2.4",
"@vue/compiler-core": "3.5.22",
"@vue/runtime-core": "3.5.22",
"@vue/compiler-core": "3.5.24",
"@vue/runtime-core": "3.5.24",
"acorn": "8.15.0",
"cross-env": "10.1.0",
"cypress": "15.5.0",
"cypress": "15.6.0",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-vue": "10.5.1",
"fast-glob": "3.3.3",
@@ -132,7 +132,7 @@
"intersection-observer": "0.12.2",
"micromatch": "4.0.8",
"minimatch": "10.1.1",
"msw": "2.11.6",
"msw": "2.12.1",
"msw-storybook-addon": "2.0.6",
"nodemon": "3.1.10",
"prettier": "3.6.2",
@@ -147,8 +147,8 @@
"vite-plugin-turbosnap": "1.0.3",
"vitest": "3.2.4",
"vitest-fetch-mock": "0.4.5",
"vue-component-type-helpers": "3.1.2",
"vue-component-type-helpers": "3.1.3",
"vue-eslint-parser": "10.2.0",
"vue-tsc": "3.1.2"
"vue-tsc": "3.1.3"
}
}
+10 -16
View File
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { computed, watch, version as vueVersion } from 'vue';
import { watch, version as vueVersion } from 'vue';
import { compareVersions } from 'compare-versions';
import { version, lang, apiUrl, isSafeMode } from '@@/js/config.js';
import defaultLightTheme from '@@/themes/l-light.json5';
@@ -15,11 +15,11 @@ import directives from '@/directives/index.js';
import components from '@/components/index.js';
import { applyTheme } from '@/theme.js';
import { isDeviceDarkmode } from '@/utility/is-device-darkmode.js';
import { updateI18n, i18n } from '@/i18n.js';
import { i18n } from '@/i18n.js';
import { refreshCurrentAccount, login } from '@/accounts.js';
import { store } from '@/store.js';
import { fetchInstance, instance } from '@/instance.js';
import { deviceKind, updateDeviceKind } from '@/utility/device-kind.js';
import { updateDeviceKind } from '@/utility/device-kind.js';
import { reloadChannel } from '@/utility/unison-reload.js';
import { getUrlWithoutLoginId } from '@/utility/login-id.js';
import { getAccountFromId } from '@/utility/get-account-from-id.js';
@@ -109,13 +109,6 @@ export async function common(createVue: () => Promise<App<Element>>) {
else window.location.reload();
});
// If mobile, insert the viewport meta tag
if (['smartphone', 'tablet'].includes(deviceKind)) {
const viewport = window.document.getElementsByName('viewport').item(0);
viewport.setAttribute('content',
`${viewport.getAttribute('content')}, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover`);
}
//#region Set lang attr
const html = window.document.documentElement;
html.setAttribute('lang', lang);
@@ -160,8 +153,15 @@ export async function common(createVue: () => Promise<App<Element>>) {
});
//#endregion
if (!isSafeMode) {
// TODO: instance.defaultLightTheme/instance.defaultDarkThemeが不正な形式だった場合のケア
if (prefer.s.lightTheme == null && instance.defaultLightTheme != null) prefer.commit('lightTheme', JSON.parse(instance.defaultLightTheme));
if (prefer.s.darkTheme == null && instance.defaultDarkTheme != null) prefer.commit('darkTheme', JSON.parse(instance.defaultDarkTheme));
}
// NOTE: この処理は必ずクライアント更新チェック処理より後に来ること(テーマ再構築のため)
// NOTE: この処理は必ずダークモード判定処理より後に来ること(初回のテーマ適用のため)
// NOTE: この処理は必ずサーバーテーマ適用処理より後に来ること(二重applyTheme発火を防ぐため)
// see: https://github.com/misskey-dev/misskey/issues/16562
watch(store.r.darkMode, (darkMode) => {
const theme = (() => {
@@ -192,12 +192,6 @@ export async function common(createVue: () => Promise<App<Element>>) {
applyTheme(theme ?? defaultLightTheme);
}
});
fetchInstanceMetaPromise.then(() => {
// TODO: instance.defaultLightTheme/instance.defaultDarkThemeが不正な形式だった場合のケア
if (prefer.s.lightTheme == null && instance.defaultLightTheme != null) prefer.commit('lightTheme', JSON.parse(instance.defaultLightTheme));
if (prefer.s.darkTheme == null && instance.defaultDarkTheme != null) prefer.commit('darkTheme', JSON.parse(instance.defaultDarkTheme));
});
}
watch(prefer.r.overridedDeviceKind, (kind) => {
@@ -102,6 +102,21 @@ async function onClick() {
await misskeyApi('following/delete', {
userId: props.user.id,
});
} else if (hasPendingFollowRequestFromYou.value) {
const { canceled } = await os.confirm({
type: 'question',
text: i18n.tsx.cancelFollowRequestConfirm({ name: props.user.name || props.user.username }),
});
if (canceled) {
wait.value = false;
return;
}
await misskeyApi('following/requests/cancel', {
userId: props.user.id,
});
hasPendingFollowRequestFromYou.value = false;
} else {
if (prefer.s.alwaysConfirmFollow) {
const { canceled } = await os.confirm({
@@ -115,41 +130,34 @@ async function onClick() {
}
}
if (hasPendingFollowRequestFromYou.value) {
await misskeyApi('following/requests/cancel', {
userId: props.user.id,
});
hasPendingFollowRequestFromYou.value = false;
} else {
await misskeyApi('following/create', {
userId: props.user.id,
withReplies: prefer.s.defaultFollowWithReplies,
});
emit('update:user', {
...props.user,
withReplies: prefer.s.defaultFollowWithReplies,
});
hasPendingFollowRequestFromYou.value = true;
await misskeyApi('following/create', {
userId: props.user.id,
withReplies: prefer.s.defaultFollowWithReplies,
});
emit('update:user', {
...props.user,
withReplies: prefer.s.defaultFollowWithReplies,
});
hasPendingFollowRequestFromYou.value = true;
if ($i == null) {
wait.value = false;
return;
}
if ($i == null) {
wait.value = false;
return;
}
claimAchievement('following1');
claimAchievement('following1');
if ($i.followingCount >= 10) {
claimAchievement('following10');
}
if ($i.followingCount >= 50) {
claimAchievement('following50');
}
if ($i.followingCount >= 100) {
claimAchievement('following100');
}
if ($i.followingCount >= 300) {
claimAchievement('following300');
}
if ($i.followingCount >= 10) {
claimAchievement('following10');
}
if ($i.followingCount >= 50) {
claimAchievement('following50');
}
if ($i.followingCount >= 100) {
claimAchievement('following100');
}
if ($i.followingCount >= 300) {
claimAchievement('following300');
}
}
} catch (err) {
@@ -608,11 +608,30 @@ async function toggleReactionAcceptance() {
//#region popup
function showOtherSettings() {
let reactionAcceptanceIcon = 'ti ti-icons';
let reactionAcceptanceCaption = '';
if (reactionAcceptance.value === 'likeOnly') {
reactionAcceptanceIcon = 'ti ti-heart _love';
} else if (reactionAcceptance.value === 'likeOnlyForRemote') {
reactionAcceptanceIcon = 'ti ti-heart-plus';
switch (reactionAcceptance.value) {
case 'likeOnly':
reactionAcceptanceIcon = 'ti ti-heart _love';
reactionAcceptanceCaption = i18n.ts.likeOnly;
break;
case 'likeOnlyForRemote':
reactionAcceptanceIcon = 'ti ti-heart-plus';
reactionAcceptanceCaption = i18n.ts.likeOnlyForRemote;
break;
case 'nonSensitiveOnly':
reactionAcceptanceCaption = i18n.ts.nonSensitiveOnly;
break;
case 'nonSensitiveOnlyForLocalLikeOnlyForRemote':
reactionAcceptanceCaption = i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote;
break;
default:
reactionAcceptanceCaption = i18n.ts.all;
break;
}
const menuItems = [{
@@ -624,6 +643,7 @@ function showOtherSettings() {
}, { type: 'divider' }, {
icon: reactionAcceptanceIcon,
text: i18n.ts.reactionAcceptance,
caption: reactionAcceptanceCaption,
action: () => {
toggleReactionAcceptance();
},
@@ -692,6 +712,7 @@ function removeVisibleUser(user) {
function clear() {
text.value = '';
cw.value = null;
files.value = [];
poll.value = null;
quoteId.value = null;
@@ -1465,6 +1486,7 @@ defineExpose({
width: 28px;
height: 28px;
margin: auto;
object-fit: cover;
}
.headerRight {
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<script lang="ts">
import { defineComponent, h, ref, watch } from 'vue';
import { Comment, defineComponent, h, ref, watch } from 'vue';
import MkRadio from './MkRadio.vue';
import type { VNode } from 'vue';
@@ -35,7 +35,7 @@ export default defineComponent({
if (options.length === 1 && options[0].props == null) options = options[0].children as VNode[];
// vnodev-if=false(trueoptiontype)
options = options.filter(vnode => !(typeof vnode.type === 'symbol' && vnode.type.description === 'v-cmt' && vnode.children === 'v-if'));
options = options.filter(vnode => vnode.type !== Comment);
return () => h('div', {
class: [
+5 -1
View File
@@ -110,7 +110,11 @@ onUnmounted(() => {
<style lang="scss" module>
.root {
position: absolute;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.bg {
@@ -5,31 +5,31 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<svg v-if="type === 'info'" :class="[$style.icon, $style.info]" viewBox="0 0 160 160">
<path d="M80,108L80,72" style="--l:37;" :class="[$style.line, $style.animLine]"/>
<path d="M80,108L80,72" pathLength="1" :class="[$style.line, $style.animLine]"/>
<path d="M80,52L80,52" :class="[$style.line, $style.animFade]"/>
<circle cx="80" cy="80" r="56" style="--l:350;" :class="[$style.line, $style.animCircle]"/>
<circle cx="80" cy="80" r="56" pathLength="1" :class="[$style.line, $style.animCircle]"/>
</svg>
<svg v-else-if="type === 'question'" :class="[$style.icon, $style.question]" viewBox="0 0 160 160">
<path d="M80,92L79.991,84C88.799,83.98 96,76.962 96,68C96,59.038 88.953,52 79.991,52C71.03,52 64,59.038 64,68" style="--l:85;" :class="[$style.line, $style.animLine]"/>
<path d="M80,92L79.991,84C88.799,83.98 96,76.962 96,68C96,59.038 88.953,52 79.991,52C71.03,52 64,59.038 64,68" pathLength="1" :class="[$style.line, $style.animLine]"/>
<path d="M80,108L80,108" :class="[$style.line, $style.animFade]"/>
<circle cx="80" cy="80" r="56" style="--l:350;" :class="[$style.line, $style.animCircle]"/>
<circle cx="80" cy="80" r="56" pathLength="1" :class="[$style.line, $style.animCircle]"/>
</svg>
<svg v-else-if="type === 'success'" :class="[$style.icon, $style.success]" viewBox="0 0 160 160">
<path d="M62,80L74,92L98,68" style="--l:50;" :class="[$style.line, $style.animLine]"/>
<circle cx="80" cy="80" r="56" style="--l:350;" :class="[$style.line, $style.animCircle]"/>
<path d="M62,80L74,92L98,68" pathLength="1" :class="[$style.line, $style.animLine]"/>
<circle cx="80" cy="80" r="56" pathLength="1" :class="[$style.line, $style.animCircle]"/>
</svg>
<svg v-else-if="type === 'warn'" :class="[$style.icon, $style.warn]" viewBox="0 0 160 160">
<path d="M80,64L80,88" style="--l:27;" :class="[$style.line, $style.animLine]"/>
<path d="M80,64L80,88" pathLength="1" :class="[$style.line, $style.animLine]"/>
<path d="M80,108L80,108" :class="[$style.line, $style.animFade]"/>
<path d="M92,28L144,116C148.709,124.65 144.083,135.82 136,136L24,136C15.917,135.82 11.291,124.65 16,116L68,28C73.498,19.945 86.771,19.945 92,28Z" style="--l:395;" :class="[$style.line, $style.animLine]"/>
<path d="M92,28L144,116C148.709,124.65 144.083,135.82 136,136L24,136C15.917,135.82 11.291,124.65 16,116L68,28C73.498,19.945 86.771,19.945 92,28Z" pathLength="1" :class="[$style.line, $style.animLine]"/>
</svg>
<svg v-else-if="type === 'error'" :class="[$style.icon, $style.error]" viewBox="0 0 160 160">
<path d="M63,63L96,96" style="--l:47;--duration:0.3s;" :class="[$style.line, $style.animLine]"/>
<path d="M96,63L63,96" style="--l:47;--duration:0.3s;--delay:0.2s;" :class="[$style.line, $style.animLine]"/>
<circle cx="80" cy="80" r="56" style="--l:350;" :class="[$style.line, $style.animCircle]"/>
<path d="M63,63L96,96" pathLength="1" style="--duration:0.3s;" :class="[$style.line, $style.animLine]"/>
<path d="M96,63L63,96" pathLength="1" style="--duration:0.3s;--delay:0.2s;" :class="[$style.line, $style.animLine]"/>
<circle cx="80" cy="80" r="56" pathLength="1" :class="[$style.line, $style.animCircle]"/>
</svg>
<svg v-else-if="type === 'waiting'" :class="[$style.icon, $style.waiting]" viewBox="0 0 160 160">
<circle cx="80" cy="80" r="56" style="--l:350;" :class="[$style.line, $style.animCircleWaiting]"/>
<circle cx="80" cy="80" r="56" pathLength="1" :class="[$style.line, $style.animCircleWaiting]"/>
<circle cx="80" cy="80" r="56" style="opacity: 0.25;" :class="[$style.line]"/>
</svg>
</template>
@@ -80,15 +80,15 @@ const props = defineProps<{
}
.animLine {
stroke-dasharray: var(--l);
stroke-dashoffset: var(--l);
stroke-dasharray: 1;
stroke-dashoffset: 1;
animation: line var(--duration, 0.5s) cubic-bezier(0,0,.25,1) 1 forwards;
animation-delay: var(--delay, 0s);
}
.animCircle {
stroke-dasharray: var(--l);
stroke-dashoffset: var(--l);
stroke-dasharray: 1;
stroke-dashoffset: 1;
animation: line var(--duration, 0.5s) cubic-bezier(0,0,.25,1) 1 forwards;
animation-delay: var(--delay, 0s);
transform-origin: center;
@@ -96,8 +96,8 @@ const props = defineProps<{
}
.animCircleWaiting {
stroke-dasharray: var(--l);
stroke-dashoffset: calc(var(--l) / 1.5);
stroke-dasharray: 1;
stroke-dashoffset: calc(1 / 1.5);
animation: waiting 0.75s linear infinite;
transform-origin: center;
}
@@ -110,7 +110,7 @@ const props = defineProps<{
@keyframes line {
0% {
stroke-dashoffset: var(--l);
stroke-dashoffset: 1;
opacity: 0;
}
100% {
@@ -600,16 +600,12 @@ const headerActions = computed(() => [{
</script>
<style module lang="scss">
.violationRow {
background-color: var(--MI_THEME-infoWarnBg);
}
.changedRow {
background-color: var(--MI_THEME-infoBg);
background-color: var(--MI_THEME-infoBg) !important;
}
.editedRow {
background-color: var(--MI_THEME-infoBg);
.violationRow {
background-color: var(--MI_THEME-infoWarnBg) !important;
}
.main {
@@ -419,7 +419,7 @@ onMounted(async () => {
}
.changedRow {
background-color: var(--MI_THEME-infoBg);
background-color: var(--MI_THEME-infoBg) !important;
}
.searchArea {
@@ -447,7 +447,6 @@ const headerTabs = computed(() => room.value ? [{
const headerActions = computed<PageHeaderItem[]>(() => [{
icon: 'ti ti-dots',
text: '',
handler: showMenu,
}]);
+13 -1
View File
@@ -63,6 +63,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, onDeactivated, onUnmounted, ref, watch, shallowRef, defineAsyncComponent } from 'vue';
import * as Misskey from 'misskey-js';
import { utils } from '@syuilo/aiscript';
import { compareVersions } from 'compare-versions';
import { url } from '@@/js/config.js';
import type { Ref } from 'vue';
import type { AsUiComponent, AsUiRoot } from '@/aiscript/ui.js';
@@ -190,11 +192,21 @@ function start() {
run();
}
function getIsLegacy(version: string | null): boolean {
if (version == null) return false;
try {
return compareVersions(version, '1.0.0') < 0;
} catch {
return false;
}
}
async function run() {
if (aiscript.value) aiscript.value.abort();
if (!flash.value) return;
const isLegacy = !flash.value.script.replaceAll(' ', '').startsWith('///@1.0.0');
const version = utils.getLangVersion(flash.value.script);
const isLegacy = version != null && getIsLegacy(version);
const { Interpreter, Parser, values } = isLegacy ? (await import('@syuilo/aiscript-0-19-0') as any) : await import('@syuilo/aiscript');
@@ -63,14 +63,28 @@ function accept(user: Misskey.entities.UserLite) {
});
}
function reject(user: Misskey.entities.UserLite) {
os.apiWithDialog('following/requests/reject', { userId: user.id }).then(() => {
async function reject(user: Misskey.entities.UserLite) {
const { canceled } = await os.confirm({
type: 'question',
text: i18n.tsx.rejectFollowRequestConfirm({ name: user.name || user.username }),
});
if (canceled) return;
await os.apiWithDialog('following/requests/reject', { userId: user.id }).then(() => {
paginator.reload();
});
}
function cancel(user: Misskey.entities.UserLite) {
os.apiWithDialog('following/requests/cancel', { userId: user.id }).then(() => {
async function cancel(user: Misskey.entities.UserLite) {
const { canceled } = await os.confirm({
type: 'question',
text: i18n.tsx.cancelFollowRequestConfirm({ name: user.name || user.username }),
});
if (canceled) return;
await os.apiWithDialog('following/requests/cancel', { userId: user.id }).then(() => {
paginator.reload();
});
}
+1
View File
@@ -465,6 +465,7 @@ definePage(() => ({
}
.pageContent {
contain: content;
margin-bottom: 1.5rem;
}
+1 -1
View File
@@ -163,7 +163,7 @@ async function chooseChannel(ev: MouseEvent): Promise<void> {
type: 'link',
icon: 'ti ti-plus',
text: i18n.ts.createNew,
to: '/channels',
to: '/channels/new',
},
];
os.popupMenu(items.filter(i => i != null), ev.currentTarget ?? ev.target);
+40 -24
View File
@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { BroadcastChannel } from 'broadcast-channel';
import type { StorageProvider } from '@/preferences/manager.js';
import { cloudBackup } from '@/preferences/utility.js';
import { miLocalStorage } from '@/local-storage.js';
@@ -12,6 +13,7 @@ import { $i } from '@/i.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { TAB_ID } from '@/tab-id.js';
// クラウド同期用グループ名
const syncGroup = 'default';
const io: StorageProvider = {
@@ -26,7 +28,6 @@ const io: StorageProvider = {
save: (ctx) => {
miLocalStorage.setItem('preferences', JSON.stringify(ctx.profile));
miLocalStorage.setItem('latestPreferencesUpdate', `${TAB_ID}/${Date.now()}`);
},
cloudGet: async (ctx) => {
@@ -99,33 +100,47 @@ const io: StorageProvider = {
export const prefer = new PreferencesManager(io, $i);
let latestSyncedAt = Date.now();
//#region タブ間同期
let latestPreferencesUpdate: {
tabId: string;
timestamp: number;
} | null = null;
function syncBetweenTabs() {
const latest = miLocalStorage.getItem('latestPreferencesUpdate');
if (latest == null) return;
const preferencesChannel = new BroadcastChannel<{
type: 'preferencesUpdate';
tabId: string;
timestamp: number;
}>('preferences');
const latestTab = latest.split('/')[0];
const latestAt = parseInt(latest.split('/')[1]);
if (latestTab === TAB_ID) return;
if (latestAt <= latestSyncedAt) return;
prefer.reloadProfile();
latestSyncedAt = Date.now();
if (_DEV_) console.log('prefer:synced');
}
window.setInterval(syncBetweenTabs, 5000);
window.document.addEventListener('visibilitychange', () => {
if (window.document.visibilityState === 'visible') {
syncBetweenTabs();
}
prefer.on('committed', () => {
latestPreferencesUpdate = {
tabId: TAB_ID,
timestamp: Date.now(),
};
preferencesChannel.postMessage({
type: 'preferencesUpdate',
tabId: TAB_ID,
timestamp: latestPreferencesUpdate.timestamp,
});
});
preferencesChannel.addEventListener('message', (msg) => {
if (msg.type === 'preferencesUpdate') {
if (msg.tabId === TAB_ID) return;
if (latestPreferencesUpdate != null) {
if (msg.timestamp <= latestPreferencesUpdate.timestamp) return;
}
prefer.reloadProfile();
if (_DEV_) console.log('prefer:received update from other tab');
latestPreferencesUpdate = {
tabId: msg.tabId,
timestamp: msg.timestamp,
};
}
});
//#endregion
//#region 定期クラウドバックアップ
let latestBackupAt = 0;
window.setInterval(() => {
@@ -138,6 +153,7 @@ window.setInterval(() => {
latestBackupAt = Date.now();
});
}, 1000 * 60 * 3);
//#endregion
if (_DEV_) {
(window as any).prefer = prefer;
+2 -2
View File
@@ -216,7 +216,7 @@ export const PREF_DEF = definePreferences({
default: !prefersReducedMotion,
},
animatedMfm: {
default: !prefersReducedMotion,
default: true,
},
advancedMfm: {
default: true,
@@ -234,7 +234,7 @@ export const PREF_DEF = definePreferences({
default: false,
},
disableShowingAnimatedImages: {
default: prefersReducedMotion,
default: false,
},
emojiStyle: {
default: 'twemoji', // twemoji / fluentEmoji / native
+18 -1
View File
@@ -4,6 +4,7 @@
*/
import { computed, onUnmounted, ref, watch } from 'vue';
import { EventEmitter } from 'eventemitter3';
import { host, version } from '@@/js/config.js';
import { PREF_DEF } from './def.js';
import type { Ref, WritableComputedRef } from 'vue';
@@ -100,6 +101,14 @@ type PreferencesDefinitionRecord<Default, T = Default extends (...args: any) =>
export type PreferencesDefinition = Record<string, PreferencesDefinitionRecord<any>>;
type PreferencesManagerEvents = {
'committed': <K extends keyof PREF>(ctx: {
key: K;
value: ValueOf<K>;
oldValue: ValueOf<K>;
}) => void;
};
export function definePreferences<T extends Record<string, unknown>>(x: {
[K in keyof T]: PreferencesDefinitionRecord<T[K]>
}): {
@@ -180,7 +189,7 @@ function normalizePreferences(preferences: PossiblyNonNormalizedPreferencesProfi
// TODO: PreferencesManagerForGuest のような非ログイン専用のクラスを分離すればthis.currentAccountのnullチェックやaccountがnullであるスコープのレコード挿入などが不要になり綺麗になるかもしれない
// と思ったけど操作アカウントが存在しない場合も考慮する現在の設計の方が汎用的かつ堅牢かもしれない
// NOTE: accountDependentな設定は初期状態であってもアカウントごとのスコープでレコードを作成しておかないと、サーバー同期する際に正しく動作しなくなる
export class PreferencesManager {
export class PreferencesManager extends EventEmitter<PreferencesManagerEvents> {
private io: StorageProvider;
private currentAccount: { id: string } | null;
public profile: PreferencesProfile;
@@ -201,6 +210,8 @@ export class PreferencesManager {
};
constructor(io: StorageProvider, currentAccount: { id: string } | null) {
super();
this.io = io;
this.currentAccount = currentAccount;
@@ -246,6 +257,12 @@ export class PreferencesManager {
this.rewriteRawState(key, v);
this.emit('committed', {
key,
value: v,
oldValue: this.s[key],
});
const record = this.getMatchedRecordOf(key);
if (parseScope(record[0]).account == null && isAccountDependentKey(key) && currentAccount != null) {
@@ -186,6 +186,7 @@ export async function restoreFromCloudBackup() {
const select = await os.select({
title: i18n.ts._preferencesBackup.selectBackupToRestore,
text: '️ ' + i18n.ts._preferencesProfile.shareSameProfileBetweenDevicesIsNotRecommended + ' ' + i18n.ts._preferencesProfile.useSyncBetweenDevicesOptionIfYouWantToSyncSetting,
items: backups.map(backup => ({
label: backup.name,
value: backup.name,
+1 -3
View File
@@ -5,7 +5,5 @@
import { genId } from '@/utility/id.js';
// HMR有効時にバグか知らんけど複数回実行されるのでその対策
export const TAB_ID = window.sessionStorage.getItem('TAB_ID') ?? genId();
window.sessionStorage.setItem('TAB_ID', TAB_ID);
export const TAB_ID = genId();
if (_DEV_) console.log('TAB_ID', TAB_ID);
+2
View File
@@ -158,6 +158,8 @@ export function applyTheme(theme: Theme, persist = true) {
// 様々な理由により startViewTransition は失敗することがある
// ref. https://github.com/misskey-dev/misskey/issues/16562
// FIXME: viewTransitonエラーはtry~catch貫通してそうな気配がする
console.error(err);
window.document.documentElement.classList.remove('_themeChanging_');
+4 -4
View File
@@ -4,8 +4,8 @@
*/
export type PageHeaderItem = {
text: string;
icon: string;
highlighted?: boolean;
handler: (ev: MouseEvent) => void;
text?: string;
icon: string;
highlighted?: boolean;
handler: (ev: MouseEvent) => void;
};
+155 -132
View File
@@ -4,40 +4,40 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div class="azykntjl">
<div class="body">
<div class="left">
<button v-click-anime class="item _button instance" @click="openInstanceMenu">
<img :src="instance.iconUrl ?? '/favicon.ico'" draggable="false"/>
<div :class="[$style.root, acrylic ? $style.acrylic : null]">
<div :class="$style.body">
<div :class="$style.left">
<button v-click-anime :class="[$style.item, $style.instance]" class="_button" @click="openInstanceMenu">
<img :class="$style.instanceIcon" :src="instance.iconUrl ?? '/favicon.ico'" draggable="false"/>
</button>
<MkA v-click-anime v-tooltip="i18n.ts.timeline" class="item index" activeClass="active" to="/" exact>
<i class="ti ti-home ti-fw"></i>
<MkA v-click-anime v-tooltip="i18n.ts.timeline" :class="$style.item" activeClass="active" to="/" exact>
<i :class="$style.itemIcon" class="ti ti-home ti-fw"></i>
</MkA>
<template v-for="item in menu">
<div v-if="item === '-'" class="divider"></div>
<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime v-tooltip="navbarItemDef[item].title" class="item _button" :class="item" activeClass="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
<i class="ti-fw" :class="navbarItemDef[item].icon"></i>
<span v-if="navbarItemDef[item].indicated" class="indicator _blink"><i class="_indicatorCircle"></i></span>
<div v-if="item === '-'" :class="$style.divider"></div>
<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime v-tooltip="navbarItemDef[item].title" class="_button" :class="$style.item" activeClass="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
<i :class="[$style.itemIcon, navbarItemDef[item].icon]" class="ti-fw"></i>
<span v-if="navbarItemDef[item].indicated" :class="$style.indicator" class="_blink"><i class="_indicatorCircle"></i></span>
</component>
</template>
<div class="divider"></div>
<div :class="$style.divider"></div>
<MkA v-if="$i && ($i.isAdmin || $i.isModerator)" v-click-anime v-tooltip="i18n.ts.controlPanel" class="item" activeClass="active" to="/admin" :behavior="settingsWindowed ? 'window' : null">
<i class="ti ti-dashboard ti-fw"></i>
<i :class="$style.itemIcon" class="ti ti-dashboard ti-fw"></i>
</MkA>
<button v-click-anime class="item _button" @click="more">
<i class="ti ti-dots ti-fw"></i>
<span v-if="otherNavItemIndicated" class="indicator _blink"><i class="_indicatorCircle"></i></span>
<button v-click-anime :class="$style.item" class="_button" @click="more">
<i :class="$style.itemIcon" class="ti ti-dots ti-fw"></i>
<span v-if="otherNavItemIndicated" :class="$style.indicator" class="_blink"><i class="_indicatorCircle"></i></span>
</button>
</div>
<div class="right">
<MkA v-click-anime v-tooltip="i18n.ts.settings" class="item" activeClass="active" to="/settings" :behavior="settingsWindowed ? 'window' : null">
<i class="ti ti-settings ti-fw"></i>
<div :class="$style.right">
<MkA v-click-anime v-tooltip="i18n.ts.settings" :class="$style.item" activeClass="active" to="/settings" :behavior="settingsWindowed ? 'window' : null">
<i :class="$style.itemIcon" class="ti ti-settings ti-fw"></i>
</MkA>
<button v-if="$i" v-click-anime class="item _button account" @click="openAccountMenu">
<MkAvatar :user="$i" class="avatar"/><MkAcct class="acct" :user="$i"/>
<button v-if="$i" v-click-anime :class="[$style.item, $style.account]" class="_button" @click="openAccountMenu">
<MkAvatar :user="$i" :class="$style.avatar"/><MkAcct :class="$style.acct" :user="$i"/>
</button>
<div class="post" @click="os.post()">
<MkButton class="button" gradate full rounded>
<div :class="$style.post" @click="os.post()">
<MkButton :class="$style.postButton" gradate rounded>
<i class="ti ti-pencil ti-fw"></i>
</MkButton>
</div>
@@ -61,6 +61,10 @@ import { getHTMLElementOrNull } from '@/utility/get-dom-node-or-null.js';
const WINDOW_THRESHOLD = 1400;
const props = defineProps<{
acrylic?: boolean;
}>();
const settingsWindowed = ref(window.innerWidth > WINDOW_THRESHOLD);
const menu = ref(prefer.s.menu);
// const menuDisplay = computed(store.makeGetterSetter('menuDisplay'));
@@ -100,121 +104,140 @@ onMounted(() => {
</script>
<style lang="scss" scoped>
.azykntjl {
$height: 60px;
$avatar-size: 32px;
$avatar-margin: 8px;
<style lang="scss" module>
.root {
--height: 60px;
position: sticky;
top: 0;
z-index: 1000;
width: 100%;
height: $height;
background: color(from var(--MI_THEME-bg) srgb r g b / 0.75);
-webkit-backdrop-filter: var(--MI-blur, blur(15px));
backdrop-filter: var(--MI-blur, blur(15px));
height: var(--height);
contain: strict;
background: var(--MI_THEME-navBg);
> .body {
max-width: 1380px;
margin: 0 auto;
display: flex;
> .right,
> .left {
> .item {
position: relative;
font-size: 0.9em;
display: inline-block;
padding: 0 12px;
line-height: $height;
> i,
> .avatar {
margin-right: 0;
}
> i {
left: 10px;
}
> .avatar {
width: $avatar-size;
height: $avatar-size;
vertical-align: middle;
}
> .indicator {
position: absolute;
top: 0;
left: 0;
color: var(--MI_THEME-navIndicator);
font-size: 8px;
}
&:hover {
text-decoration: none;
color: light-dark(hsl(from var(--MI_THEME-navFg) h s calc(l - 17)), hsl(from var(--MI_THEME-navFg) h s calc(l + 17)));
}
&.active {
color: var(--MI_THEME-navActive);
}
}
> .divider {
display: inline-block;
height: 16px;
margin: 0 10px;
border-right: solid 0.5px var(--MI_THEME-divider);
}
> .instance {
display: inline-block;
position: relative;
width: 56px;
height: 100%;
vertical-align: bottom;
> img {
display: inline-block;
width: 24px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
}
> .post {
display: inline-block;
> .button {
width: 40px;
height: 40px;
padding: 0;
min-width: 0;
}
}
> .account {
display: inline-flex;
align-items: center;
vertical-align: top;
margin-right: 8px;
> .acct {
margin-left: 8px;
}
}
}
> .right {
margin-left: auto;
}
&.acrylic {
background: color(from var(--MI_THEME-bg) srgb r g b / 0.75);
-webkit-backdrop-filter: var(--MI-blur, blur(15px));
backdrop-filter: var(--MI-blur, blur(15px));
}
}
.body {
max-width: 1380px;
margin: 0 auto;
display: flex;
overflow: auto;
overflow-y: clip;
white-space: nowrap;
}
.item {
position: relative;
font-size: 0.9em;
display: inline-block;
padding: 0 12px;
line-height: var(--height);
&:hover {
text-decoration: none;
color: light-dark(hsl(from var(--MI_THEME-navFg) h s calc(l - 17)), hsl(from var(--MI_THEME-navFg) h s calc(l + 17)));
}
&.active {
color: var(--MI_THEME-navActive);
}
}
.itemIcon {
margin-right: 0;
left: 10px;
}
.avatar {
margin-right: 0;
width: 32px;
height: 32px;
vertical-align: middle;
}
.acct {
margin-left: 8px;
@media (max-width: 1200px) {
display: none;
}
}
.indicator {
position: absolute;
top: 0;
left: 0;
color: var(--MI_THEME-navIndicator);
font-size: 8px;
}
.divider {
display: inline-block;
height: 16px;
margin: 0 10px;
border-right: solid 0.5px var(--MI_THEME-divider);
}
.instance {
display: inline-block;
position: relative;
width: 56px;
height: 100%;
vertical-align: bottom;
position: sticky;
top: 0;
left: 0;
z-index: 1;
}
.instanceIcon {
display: inline-block;
width: 24px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
.right {
display: flex;
align-items: center;
margin-left: auto;
position: sticky;
top: 0;
right: 0;
z-index: 1;
contain: content;
background: var(--MI_THEME-navBg);
}
.acrylic .right {
background: transparent;
}
.post {
display: inline-block;
margin-right: 8px;
}
.postButton {
width: 40px;
height: 40px;
padding: 0;
min-width: 0;
}
.account {
display: inline-flex;
align-items: center;
vertical-align: top;
margin-right: 8px;
}
</style>
+2 -2
View File
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<XSidebar v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'left'"/>
<div :class="[$style.main, { [$style.withWallpaper]: withWallpaper, [$style.withSidebarAndTitlebar]: !isMobile && prefer.r['deck.navbarPosition'].value === 'left' && prefer.r.showTitlebar.value }]" :style="{ backgroundImage: prefer.s['deck.wallpaper'] != null ? `url(${ prefer.s['deck.wallpaper'] })` : '' }">
<XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'top'"/>
<XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'top'" :acrylic="withWallpaper"/>
<XReloadSuggestion v-if="shouldSuggestReload"/>
<XPreferenceRestore v-if="shouldSuggestRestoreBackup"/>
@@ -71,7 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
<XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'bottom'"/>
<XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'bottom'" :acrylic="withWallpaper"/>
<XMobileFooterMenu v-if="isMobile" v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/>
</div>
+2 -2
View File
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { computed, ref, shallowRef, watch } from 'vue';
import { computed, ref, shallowRef, watch, defineAsyncComponent } from 'vue';
import * as os from '@/os.js';
type TourStep = {
@@ -26,7 +26,7 @@ export function startTour(steps: TourStep[]) {
anchorElementRef.value = step.element;
});
const { dispose } = os.popup(await import('@/components/MkSpot.vue').then(x => x.default), {
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkSpot.vue')), {
title: titleRef,
description: descriptionRef,
anchorElement: anchorElementRef,
+3 -3
View File
@@ -11,10 +11,10 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
"@types/node": "24.9.2",
"@types/node": "24.10.0",
"@types/wawoff2": "1.0.2",
"@typescript-eslint/eslint-plugin": "8.46.2",
"@typescript-eslint/parser": "8.46.2"
"@typescript-eslint/eslint-plugin": "8.46.3",
"@typescript-eslint/parser": "8.46.3"
},
"dependencies": {
"@tabler/icons-webfont": "3.35.0",
+4 -4
View File
@@ -25,11 +25,11 @@
},
"devDependencies": {
"@types/matter-js": "0.20.2",
"@types/node": "24.9.2",
"@types/node": "24.10.0",
"@types/seedrandom": "3.0.8",
"@typescript-eslint/eslint-plugin": "8.46.2",
"@typescript-eslint/parser": "8.46.2",
"esbuild": "0.25.11",
"@typescript-eslint/eslint-plugin": "8.46.3",
"@typescript-eslint/parser": "8.46.3",
"esbuild": "0.27.0",
"execa": "9.6.0",
"glob": "11.0.3",
"nodemon": "3.1.10",
@@ -1223,6 +1223,9 @@ type ClipsListRequest = operations['clips___list']['requestBody']['content']['ap
// @public (undocumented)
type ClipsListResponse = operations['clips___list']['responses']['200']['content']['application/json'];
// @public (undocumented)
type ClipsMyFavoritesRequest = operations['clips___my-favorites']['requestBody']['content']['application/json'];
// @public (undocumented)
type ClipsMyFavoritesResponse = operations['clips___my-favorites']['responses']['200']['content']['application/json'];
@@ -1774,6 +1777,7 @@ declare namespace entities {
ClipsFavoriteRequest,
ClipsListRequest,
ClipsListResponse,
ClipsMyFavoritesRequest,
ClipsMyFavoritesResponse,
ClipsNotesRequest,
ClipsNotesResponse,
+5 -5
View File
@@ -7,16 +7,16 @@
"generate": "tsx src/generator.ts && eslint ./built/**/*.ts --fix"
},
"devDependencies": {
"@readme/openapi-parser": "5.2.0",
"@types/node": "24.9.2",
"@typescript-eslint/eslint-plugin": "8.46.2",
"@typescript-eslint/parser": "8.46.2",
"@readme/openapi-parser": "5.2.1",
"@types/node": "24.10.1",
"@typescript-eslint/eslint-plugin": "8.47.0",
"@typescript-eslint/parser": "8.47.0",
"openapi-types": "12.1.3",
"openapi-typescript": "7.10.1",
"ts-case-convert": "2.1.0",
"tsx": "4.20.6",
"typescript": "5.9.3",
"eslint": "9.39.0"
"eslint": "9.39.1"
},
"files": [
"built"
+10 -10
View File
@@ -1,7 +1,7 @@
{
"type": "module",
"name": "misskey-js",
"version": "2025.11.0-beta.0",
"version": "2025.11.1-beta.1",
"description": "Misskey SDK for JavaScript",
"license": "MIT",
"main": "./built/index.js",
@@ -37,19 +37,19 @@
"directory": "packages/misskey-js"
},
"devDependencies": {
"@microsoft/api-extractor": "7.53.3",
"@types/node": "24.9.2",
"@typescript-eslint/eslint-plugin": "8.46.2",
"@typescript-eslint/parser": "8.46.2",
"@vitest/coverage-v8": "3.2.4",
"esbuild": "0.25.11",
"@microsoft/api-extractor": "7.55.0",
"@types/node": "24.10.1",
"@typescript-eslint/eslint-plugin": "8.47.0",
"@typescript-eslint/parser": "8.47.0",
"@vitest/coverage-v8": "4.0.10",
"esbuild": "0.27.0",
"execa": "9.6.0",
"glob": "11.0.3",
"glob": "12.0.0",
"ncp": "2.0.0",
"nodemon": "3.1.10",
"nodemon": "3.1.11",
"tsd": "0.33.0",
"typescript": "5.9.3",
"vitest": "3.2.4",
"vitest": "4.0.10",
"vitest-websocket-mock": "0.5.0"
},
"files": [
+2 -1
View File
@@ -269,6 +269,7 @@ import type {
ClipsFavoriteRequest,
ClipsListRequest,
ClipsListResponse,
ClipsMyFavoritesRequest,
ClipsMyFavoritesResponse,
ClipsNotesRequest,
ClipsNotesResponse,
@@ -838,7 +839,7 @@ export type Endpoints = {
'clips/delete': { req: ClipsDeleteRequest; res: EmptyResponse };
'clips/favorite': { req: ClipsFavoriteRequest; res: EmptyResponse };
'clips/list': { req: ClipsListRequest; res: ClipsListResponse };
'clips/my-favorites': { req: EmptyRequest; res: ClipsMyFavoritesResponse };
'clips/my-favorites': { req: ClipsMyFavoritesRequest; res: ClipsMyFavoritesResponse };
'clips/notes': { req: ClipsNotesRequest; res: ClipsNotesResponse };
'clips/remove-note': { req: ClipsRemoveNoteRequest; res: EmptyResponse };
'clips/show': { req: ClipsShowRequest; res: ClipsShowResponse };
@@ -272,6 +272,7 @@ export type ClipsDeleteRequest = operations['clips___delete']['requestBody']['co
export type ClipsFavoriteRequest = operations['clips___favorite']['requestBody']['content']['application/json'];
export type ClipsListRequest = operations['clips___list']['requestBody']['content']['application/json'];
export type ClipsListResponse = operations['clips___list']['responses']['200']['content']['application/json'];
export type ClipsMyFavoritesRequest = operations['clips___my-favorites']['requestBody']['content']['application/json'];
export type ClipsMyFavoritesResponse = operations['clips___my-favorites']['responses']['200']['content']['application/json'];
export type ClipsNotesRequest = operations['clips___notes']['requestBody']['content']['application/json'];
export type ClipsNotesResponse = operations['clips___notes']['responses']['200']['content']['application/json'];
+14
View File
@@ -18638,6 +18638,20 @@ export interface operations {
};
};
'clips___my-favorites': {
requestBody: {
content: {
'application/json': {
/** @default 10 */
limit?: number;
/** Format: misskey:id */
sinceId?: string;
/** Format: misskey:id */
untilId?: string;
sinceDate?: number;
untilDate?: number;
};
};
};
responses: {
/** @description OK (with results) */
200: {
+4 -4
View File
@@ -24,10 +24,10 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
"@types/node": "24.9.2",
"@typescript-eslint/eslint-plugin": "8.46.2",
"@typescript-eslint/parser": "8.46.2",
"esbuild": "0.25.11",
"@types/node": "24.10.0",
"@typescript-eslint/eslint-plugin": "8.46.3",
"@typescript-eslint/parser": "8.46.3",
"esbuild": "0.27.0",
"execa": "9.6.0",
"glob": "11.0.3",
"nodemon": "3.1.10",
+2 -2
View File
@@ -9,12 +9,12 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"dependencies": {
"esbuild": "0.25.11",
"esbuild": "0.27.0",
"idb-keyval": "6.2.2",
"misskey-js": "workspace:*"
},
"devDependencies": {
"@typescript-eslint/parser": "8.46.2",
"@typescript-eslint/parser": "8.46.3",
"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.74",
"eslint-plugin-import": "2.32.0",
"nodemon": "3.1.10",
+4916 -3847
View File
File diff suppressed because it is too large Load Diff
+12
View File
@@ -106,6 +106,18 @@
'.devcontainer/**',
],
},
{
// devcontainer (Dockerfile用の設定)
groupName: '[devcontainer] Update dependencies',
matchFileNames: ['.devcontainer/Dockerfile'],
matchDepNames: ['mcr.microsoft.com/devcontainers/javascript-node'],
allowedVersions: '/-[0-9]+-trixie$/',
// major/minor/patch: devcontainer の semver
// build: Node メジャー
// dist: Debian codename (e.g., bullseye, bookworm)(比較には使わない)
versioning: 'regex:^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)-(?<build>\\d+)-(?<dist>.+)$',
},
],
customManagers: [
{
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -11,14 +11,14 @@
"devDependencies": {
"@types/mdast": "4.0.4",
"@types/node": "24.9.1",
"@vitest/coverage-v8": "3.2.4",
"@vitest/coverage-v8": "4.0.10",
"mdast-util-to-string": "4.0.0",
"remark": "15.0.1",
"remark-parse": "11.0.0",
"typescript": "5.9.3",
"unified": "11.0.5",
"vite": "6.4.1",
"vite-node": "3.2.4",
"vitest": "3.2.4"
"vite": "7.2.2",
"vite-node": "5.2.0",
"vitest": "4.0.10"
}
}