1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-05-09 08:35:28 +02:00

Compare commits

..

1 Commits

Author SHA1 Message Date
syuilo
2da1984b2c Update MkSwitch.vue 2026-04-24 10:36:08 +09:00
777 changed files with 4226 additions and 20771 deletions

View File

@@ -36,7 +36,7 @@ services:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: misskey
volumes:
- postgres-data:/var/lib/postgresql
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
interval: 5s

View File

@@ -19,10 +19,10 @@ jobs:
uses: actions/checkout@v6.0.2
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
uses: pnpm/action-setup@v4.4.0
- name: Setup Node.js
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v6.3.0
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

@@ -14,7 +14,7 @@ jobs:
- name: Checkout head
uses: actions/checkout@v6.0.2
- name: Setup Node.js
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v6.3.0
with:
node-version-file: '.node-version'

View File

@@ -25,11 +25,11 @@ jobs:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
- name: setup pnpm
uses: pnpm/action-setup@v6
uses: pnpm/action-setup@v4
- name: setup node
id: setup-node
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v6.3.0
with:
node-version-file: '.node-version'
cache: pnpm
@@ -53,7 +53,7 @@ jobs:
# packages/misskey-js/generator/built/autogen
- name: Upload Generated
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: generated-misskey-js
path: packages/misskey-js/generator/built/autogen
@@ -73,7 +73,7 @@ jobs:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
- name: Upload From Merged
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: actual-misskey-js
path: packages/misskey-js/src/autogen
@@ -86,13 +86,13 @@ jobs:
pull-requests: write
steps:
- name: download generated-misskey-js
uses: actions/download-artifact@v8
uses: actions/download-artifact@v7
with:
name: generated-misskey-js
path: misskey-js-generated
- name: download actual-misskey-js
uses: actions/download-artifact@v8
uses: actions/download-artifact@v7
with:
name: actual-misskey-js
path: misskey-js-actual

View File

@@ -29,15 +29,15 @@ jobs:
- name: Check out the repo
uses: actions/checkout@v6.0.2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v4
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push by digest
id: build
uses: docker/build-push-action@v7
uses: docker/build-push-action@v6
with:
context: .
push: true
@@ -53,7 +53,7 @@ jobs:
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/*
@@ -66,15 +66,15 @@ jobs:
- build
steps:
- name: Download digests
uses: actions/download-artifact@v8
uses: actions/download-artifact@v7
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v4
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

View File

@@ -34,21 +34,21 @@ jobs:
- name: Check out the repo
uses: actions/checkout@v6.0.2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY_IMAGE }}
tags: ${{ env.TAGS }}
- name: Log in to Docker Hub
uses: docker/login-action@v4
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and Push to Docker Hub
id: build
uses: docker/build-push-action@v7
uses: docker/build-push-action@v6
with:
context: .
push: true
@@ -64,7 +64,7 @@ jobs:
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/*
@@ -77,21 +77,21 @@ jobs:
- build
steps:
- name: Download digests
uses: actions/download-artifact@v8
uses: actions/download-artifact@v7
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY_IMAGE }}
tags: ${{ env.TAGS }}
- name: Login to Docker Hub
uses: docker/login-action@v4
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

View File

@@ -30,9 +30,9 @@ jobs:
ref: ${{ matrix.ref }}
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
uses: pnpm/action-setup@v4.4.0
- name: Use Node.js
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v6.3.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -48,7 +48,7 @@ jobs:
- name: Copy API.json
run: cp packages/backend/built/api.json ${{ matrix.api-json-name }}
- name: Upload Artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: api-artifact-${{ matrix.api-json-name }}
path: ${{ matrix.api-json-name }}
@@ -61,7 +61,7 @@ jobs:
PR_NUMBER: ${{ github.event.number }}
run: |
echo "$PR_NUMBER" > ./pr_number
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@v6
with:
name: api-artifact-pr-number
path: pr_number

View File

@@ -35,7 +35,7 @@ jobs:
POSTGRES_DB: test-misskey
POSTGRES_HOST_AUTH_METHOD: trust
redis:
image: redis:8
image: redis:7
ports:
- 56312:6379
@@ -45,9 +45,9 @@ jobs:
ref: ${{ matrix.ref }}
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
uses: pnpm/action-setup@v4.4.0
- name: Use Node.js
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v6.3.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -67,7 +67,7 @@ jobs:
# Start the server and measure memory usage
node packages/backend/scripts/measure-memory.mjs > ${{ matrix.memory-json-name }}
- name: Upload Artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: memory-artifact-${{ matrix.memory-json-name }}
path: ${{ matrix.memory-json-name }}
@@ -81,7 +81,7 @@ jobs:
PR_NUMBER: ${{ github.event.number }}
run: |
echo "$PR_NUMBER" > ./pr_number
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@v6
with:
name: memory-artifact-pr-number
path: pr_number

View File

@@ -41,8 +41,8 @@ jobs:
fetch-depth: 0
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
- uses: actions/setup-node@v6.4.0
uses: pnpm/action-setup@v4.4.0
- uses: actions/setup-node@v6.3.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -74,14 +74,14 @@ jobs:
fetch-depth: 0
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
- uses: actions/setup-node@v6.4.0
uses: pnpm/action-setup@v4.4.0
- uses: actions/setup-node@v6.3.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: pnpm i --frozen-lockfile
- name: Restore eslint cache
uses: actions/cache@v5.0.5
uses: actions/cache@v4.3.0
with:
path: ${{ env.eslint-cache-path }}
key: eslint-${{ env.eslint-cache-version }}-${{ matrix.workspace }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.ref_name }}-${{ github.sha }}
@@ -105,8 +105,8 @@ jobs:
fetch-depth: 0
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
- uses: actions/setup-node@v6.4.0
uses: pnpm/action-setup@v4.4.0
- uses: actions/setup-node@v6.3.0
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

@@ -21,8 +21,8 @@ jobs:
fetch-depth: 0
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
- uses: actions/setup-node@v6.4.0
uses: pnpm/action-setup@v4.4.0
- uses: actions/setup-node@v6.3.0
with:
node-version-file: ".node-version"
cache: "pnpm"

View File

@@ -20,9 +20,9 @@ jobs:
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
uses: pnpm/action-setup@v4.4.0
- name: Use Node.js
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v6.3.0
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

@@ -16,7 +16,7 @@ jobs:
# api-artifact
steps:
- name: Download artifact
uses: actions/github-script@v9
uses: actions/github-script@v8.0.0
with:
script: |
const fs = require('fs');
@@ -60,7 +60,7 @@ jobs:
- name: Echo full diff
run: cat ./api-full.json.diff
- name: Upload full diff to Artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: api-artifact
path: |

View File

@@ -15,7 +15,7 @@ jobs:
steps:
- name: Download artifact
uses: actions/github-script@v9
uses: actions/github-script@v8.0.0
with:
script: |
const fs = require('fs');

View File

@@ -14,7 +14,7 @@ jobs:
pull-requests: write
steps:
- name: Reply
uses: actions/github-script@v9
uses: actions/github-script@v8
with:
script: |
const body = `To dev team (@misskey-dev/dev):

View File

@@ -37,9 +37,9 @@ jobs:
if: github.event_name == 'pull_request_target'
run: git checkout "$(git rev-list --parents -n1 HEAD | cut -d" " -f3)"
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
uses: pnpm/action-setup@v4.4.0
- name: Use Node.js
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v6.3.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -90,7 +90,7 @@ jobs:
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- name: Notify that Chromatic detects changes
uses: actions/github-script@v9
uses: actions/github-script@v8.0.0
if: github.event_name != 'pull_request_target' && steps.chromatic_push.outputs.success == 'false'
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
@@ -102,7 +102,7 @@ jobs:
body: 'Chromatic detects changes. Please [review the changes on Chromatic](https://www.chromatic.com/builds?appId=6428f7d7b962f0b79f97d6e4).'
})
- name: Upload Artifacts
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: storybook
path: packages/frontend/storybook-static

View File

@@ -45,11 +45,11 @@ jobs:
POSTGRES_DB: test-misskey
POSTGRES_HOST_AUTH_METHOD: trust
redis:
image: redis:8
image: redis:7
ports:
- 56312:6379
meilisearch:
image: getmeili/meilisearch:v1.42.1
image: getmeili/meilisearch:v1.38.2
ports:
- 57712:7700
env:
@@ -61,13 +61,13 @@ jobs:
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
uses: pnpm/action-setup@v4.4.0
- name: Get current date
id: current-date
run: echo "today=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Setup and Restore ffmpeg/ffprobe Cache
id: cache-ffmpeg
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: |
/usr/local/bin/ffmpeg
@@ -93,7 +93,7 @@ jobs:
fi
done
- name: Use Node.js
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v6.3.0
with:
node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm'
@@ -107,7 +107,7 @@ jobs:
- name: Test
run: pnpm --filter backend test-and-coverage
- name: Upload to Codecov
uses: codecov/codecov-action@v6
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/backend/coverage/coverage-final.json
@@ -131,7 +131,7 @@ jobs:
POSTGRES_DB: test-misskey
POSTGRES_HOST_AUTH_METHOD: trust
redis:
image: redis:8
image: redis:7
ports:
- 56312:6379
@@ -140,9 +140,9 @@ jobs:
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
uses: pnpm/action-setup@v4.4.0
- name: Use Node.js
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v6.3.0
with:
node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm'
@@ -156,7 +156,7 @@ jobs:
- name: Test
run: pnpm --filter backend test-and-coverage:e2e
- name: Upload to Codecov
uses: codecov/codecov-action@v6
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/backend/coverage/coverage-final.json
@@ -184,12 +184,12 @@ jobs:
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
uses: pnpm/action-setup@v4.4.0
- name: Get current date
id: current-date
run: echo "today=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Use Node.js
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v6.3.0
with:
node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm'

View File

@@ -36,13 +36,13 @@ jobs:
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
uses: pnpm/action-setup@v4.4.0
- name: Get current date
id: current-date
run: echo "today=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Setup and Restore ffmpeg/ffprobe Cache
id: cache-ffmpeg
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: |
/usr/local/bin/ffmpeg
@@ -68,7 +68,7 @@ jobs:
fi
done
- name: Use Node.js
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v6.3.0
with:
node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm'

View File

@@ -32,9 +32,9 @@ jobs:
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
uses: pnpm/action-setup@v4.4.0
- name: Use Node.js
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v6.3.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -48,7 +48,7 @@ jobs:
- name: Test
run: pnpm --filter frontend test-and-coverage
- name: Upload Coverage
uses: codecov/codecov-action@v6
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/frontend/coverage/coverage-final.json
@@ -71,7 +71,7 @@ jobs:
POSTGRES_DB: test-misskey
POSTGRES_HOST_AUTH_METHOD: trust
redis:
image: redis:8
image: redis:7
ports:
- 56312:6379
@@ -86,9 +86,9 @@ jobs:
#- uses: browser-actions/setup-firefox@latest
# if: ${{ matrix.browser == 'firefox' }}
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
uses: pnpm/action-setup@v4.4.0
- name: Use Node.js
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v6.3.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -105,7 +105,7 @@ jobs:
- name: Cypress install
run: pnpm exec cypress install
- name: Cypress run
uses: cypress-io/github-action@v7.1.9
uses: cypress-io/github-action@v6
timeout-minutes: 15
with:
install: false
@@ -113,12 +113,12 @@ jobs:
wait-on: 'http://localhost:61812'
headed: true
browser: ${{ matrix.browser }}
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@v6
if: failure()
with:
name: ${{ matrix.browser }}-cypress-screenshots
path: cypress/screenshots
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@v6
if: always()
with:
name: ${{ matrix.browser }}-cypress-videos

View File

@@ -25,10 +25,10 @@ jobs:
uses: actions/checkout@v6.0.2
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
uses: pnpm/action-setup@v4.4.0
- name: Setup Node.js
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v6.3.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -48,7 +48,7 @@ jobs:
CI: true
- name: Upload Coverage
uses: codecov/codecov-action@v6
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/misskey-js/coverage/coverage-final.json

View File

@@ -20,9 +20,9 @@ jobs:
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
uses: pnpm/action-setup@v4.4.0
- name: Use Node.js
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v6.3.0
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

@@ -21,9 +21,9 @@ jobs:
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v6.0.3
uses: pnpm/action-setup@v4.4.0
- name: Use Node.js
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v6.3.0
with:
node-version-file: '.node-version'
cache: 'pnpm'

3
.gitignore vendored
View File

@@ -81,6 +81,3 @@ vite.config.local-dev.ts.timestamp-*
# VSCode addon
.favorites.json
# Affinity
*.af~lock~

View File

@@ -1,47 +1,13 @@
## Unreleased
### General
-
### Client
- Enhance: テーマのプレビュー時、リロードせずにもとのテーマに戻せるように
- Fix: テーマエディター使用時に、最初の変更のみ適用される問題を修正
- Fix: テーマのプレビュー時、既存のテーマとIDが被っている場合にプレビューできない問題を修正
- Fix: テーマのインストールエラーの表示を改善
### Server
-
## 2026.5.1
### General
- Enhance: チャンネルの作成の可否をロールポリシーで制御できるように
- Fix: `.devcontainer/compose.yml`のvolumeのマウントパスを修正
### Client
- Enhance: ノートの詳細表示での公開範囲の表示を改善
(Cherry-picked from https://github.com/kokonect-link/cherrypick/commit/ecc75563f4e428b66adccc379bf317b5b21ed8e6)
- Fix: ロール設定画面でロールをアサイン/アサイン解除した際、リロードしなくても画面に反映されるよう修正
### Server
- Fix: ID生成アルゴリズムにULIDを使用している場合に通知が約10秒遅延する問題を修正
- Fix: 公開範囲がフォロワーの投稿が通知されない問題を修正
- Fix: URLプレビューが動作しない問題を修正
## 2026.5.0
## 2026.4.0
### General
- Enhance: アバターデコレーションにカテゴリを設定できるように
### Client
- Enhance: チャンネル指定リノートでリノート先のチャンネルに移動できるように
- Enhance: ベータ版でのアップデート時のダイアログの更新情報リンクをGitHubのReleasesページに遷移するようにし、正しく閲覧できるように
- Fix: 一部のページ内リンクが正しく動作しない問題を修正
- Fix: ドライブへの画像アップロード時にファイル名の変更が無視される不具合を修正
- Fix: 連合が無効化されたサーバーで一部の設定項目が空欄で表示される問題を修正
- Fix: オーディオ、動画の再生速度メニューが開けない問題を修正
### Server
- Enhance: メモリ使用量を削減
@@ -57,14 +23,6 @@
- Fix: ID生成アルゴリズムにULIDを使用している場合にMisskeyが正しく動作しない問題を修正
- Fix: リレー経由で届いたノートがリノートとして表示される問題を修正
- Fix: robots.txtの内容を調整
- Fix: 特定のユーザーに管理者権限を持つロールが複数ついている際に、取得できるユーザーIDが重複する問題を修正
(Cherry-picked from https://github.com/lqvp/misskey-tempura/commit/17ed4108cec4b6bd2fd989db5a9091db91fa37a7)
- Fix: ブロックしたサーバーからのInboxジョブが蓄積し続ける問題を修正
(Cherry-picked from https://github.com/lqvp/misskey-tempura/commit/3f0f4bfe923f2b3a7837017b54841598f421c6ef)
- Fix: support activity with `actor` as an id string or embedded object in inbox processor and ActivityPub inbox service
- Fix: コンフィグファイルに `meilisearch` の設定がある状態でほかの検索プロバイダを利用すると、UI上からリモートのートの検索ができない問題を修正
- Fix: ノートに関する通知で公開範囲が考慮されていない問題を修正
(Cherry-picked from https://github.com/lqvp/misskey-tempura/commit/cbce96c520a138b8bcd16890ff6f2952830fa166 originally presented in https://github.com/yojo-art/cherrypick/pull/743)
## 2026.3.2

View File

@@ -1,6 +1,6 @@
# syntax = docker/dockerfile:1.23
# syntax = docker/dockerfile:1.21
ARG NODE_VERSION=22.22.2-bookworm
ARG NODE_VERSION=22.22.0-bookworm
# build assets & compile TypeScript

View File

@@ -1408,7 +1408,6 @@ frame: "Frame"
presets: "Preset"
zeroPadding: "Zero padding"
nothingToConfigure: "No configurable options available"
viewRenotedChannel: "Show renoted channel"
_imageEditing:
_vars:
caption: "File caption"
@@ -3402,8 +3401,6 @@ _imageEffector:
threshold: "Threshold"
centerX: "Center X"
centerY: "Center Y"
density: "Density"
zoomLinesOutlineThickness: "Outline shadow thickness"
zoomLinesMaskSize: "Center diameter"
circle: "Circular"
drafts: "Drafts"

View File

@@ -1408,7 +1408,6 @@ frame: "Marco"
presets: "Predefinido"
zeroPadding: "Relleno cero"
nothingToConfigure: "No hay nada que configurar"
viewRenotedChannel: "Ver el canal al que te has suscrito"
_imageEditing:
_vars:
caption: "Título del archivo"

View File

@@ -1409,8 +1409,6 @@ presets: "プリセット"
zeroPadding: "ゼロ埋め"
nothingToConfigure: "設定項目はありません"
viewRenotedChannel: "リノート先のチャンネルを見る"
previewingTheme: "テーマのプレビュー中"
previewingThemeRestore: "元に戻す"
_imageEditing:
_vars:
@@ -2124,7 +2122,6 @@ _role:
canSearchNotes: "ノート検索の利用"
canSearchUsers: "ユーザー検索の利用"
canUseTranslator: "翻訳機能の利用"
canCreateChannel: "チャンネルの作成"
avatarDecorationLimit: "アイコンデコレーションの最大取付個数"
canImportAntennas: "アンテナのインポートを許可"
canImportBlocking: "ブロックのインポートを許可"
@@ -3556,17 +3553,3 @@ _qr:
scanFile: "端末の画像をスキャン"
raw: "テキスト"
mfm: "MFM"
_room:
snapToGrid: "グリッドにスナップ"
gridScale: "グリッドサイズ"
thereAreUnsavedChanges: "未保存の変更があります"
revertAllChangesConfirmation: "全ての変更を取り消し、部屋を最後に保存した状態まで戻しますか?"
graphicsQuality: "グラフィックの品質"
frameRate: "フレームレート"
resolution: "解像度"
yourDeviceNotSupported_title: "お使いのデバイスはMisskeyRoomをサポートしていません。"
yourDeviceNotSupported_description: "MisskeyRoomを動作させるには、WebGPUをサポートするデバイスが必要です。"
failedToInitialize: "初期化に失敗しました"
crushed_description: "バグ、またはデバイスのリソース不足の可能性が考えられます。"
antialiasing: "アンチエイリアス"

View File

@@ -2098,7 +2098,6 @@ _role:
canSearchNotes: "노트 검색 이용 가능 여부"
canSearchUsers: "유저 검색 이용"
canUseTranslator: "번역 기능의 사용"
canCreateChannel: "패널 생성"
avatarDecorationLimit: "아바타 장식의 최대 붙임 개수"
canImportAntennas: "안테나 가져오기 허용"
canImportBlocking: "차단 목록 가져오기 허용"

View File

@@ -1332,9 +1332,7 @@ overrideByAccount: "Переопределить этим аккаунтом"
untitled: "Без названия"
noName: "Имя не указано"
skip: "Пропустить"
restore: "Восстановить"
syncBetweenDevices: "Синхронизировать между устройствами"
paste: "вставить"
postForm: "Форма отправки"
textCount: "Количество символов"
information: "Описание"
@@ -2397,8 +2395,6 @@ _imageEffector:
opacity: "Непрозрачность"
lightness: "Осветление"
drafts: "Черновик"
_drafts:
restore: "Восстановить"
_qr:
showTabTitle: "Отображение"
raw: "Текст"

View File

@@ -1335,7 +1335,7 @@ markAsSensitiveConfirm: "ต้องการตั้งค่าสื่อ
unmarkAsSensitiveConfirm: "ต้องการยกเลิกการระบุว่าสื่อนี้มีเนื้อหาละเอียดอ่อนหรือไม่?"
preferences: "การตั้งค่าสภาพแวดล้อม"
accessibility: "การช่วยการเข้าถึง"
preferencesProfile: "โปรไฟล์ของการตั้งค่า"
preferencesProfile: "โปรไฟล์การกำหนดค่า"
copyPreferenceId: "คัดลือก ID การตั้งค่า"
resetToDefaultValue: "คืนค่าเป็นค่าเริ่มต้น"
overrideByAccount: "เขียนทับด้วยบัญชี"
@@ -1345,10 +1345,10 @@ skip: "ข้าม"
restore: "กู้คืน"
syncBetweenDevices: "ซิงค์ระหว่างอุปกรณ์"
preferenceSyncConflictTitle: "การตั้งค่ามีอยู่บนเซิร์ฟเวอร์"
preferenceSyncConflictText: "รายการตั้งค่าที่เปิดการซิง์จะถูกบันทึกลงเซิร์ฟเวอร์ แต่รายการตั้งค่านี้ได้ถูกบันทึกลงเซิร์ฟเวอร์ไว้อยู่แล้ว ต้องการดำเนินการอย่างไร?"
preferenceSyncConflictText: "การตั้งค่าที่เปิดใช้งานการซิง์จะบันทึกค่าลงในเซิร์ฟเวอร์ อย่างไรก็ดี พบว่ามีค่าการตั้งค่านี้ที่เคยบันทึกไว้ในเซิร์ฟเวอร์แล้ว ต้องการดำเนินการอย่างไร?"
preferenceSyncConflictChoiceMerge: "รวมเข้าด้วยกัน"
preferenceSyncConflictChoiceServer: "เขียนทับด้วยค่าการตั้งค่าของเซิร์ฟเวอร์"
preferenceSyncConflictChoiceDevice: "เขียนทับด้วยค่าการตั้งค่าของอุปกรณ์"
preferenceSyncConflictChoiceServer: "เขียนทับด้วยค่าการตั้งค่าเซิร์ฟเวอร์"
preferenceSyncConflictChoiceDevice: "เขียนทับด้วยค่าการตั้งค่าอุปกรณ์"
preferenceSyncConflictChoiceCancel: "ยกเลิกการเปิดใช้งานการซิงค์"
paste: "วาง"
emojiPalette: "จานสีเอโมจิ"
@@ -1408,7 +1408,6 @@ frame: "เฟรม"
presets: "พรีเซ็ต"
zeroPadding: "ห่างเป็น 0"
nothingToConfigure: "ไม่มีอะไรให้ต้ังค่า"
viewRenotedChannel: "แสดงช่องที่ถูกรีโน้ต"
_imageEditing:
_vars:
caption: "แคปชั่นของไฟล์"
@@ -2098,7 +2097,6 @@ _role:
canSearchNotes: "การใช้การค้นหาโน้ต"
canSearchUsers: "ค้นหาผู้ใช้"
canUseTranslator: "การใช้งานแปล"
canCreateChannel: "สร้างช่องใหม่"
avatarDecorationLimit: "จำนวนของตกแต่งไอคอนสูงสุดที่สามารถติดตั้งได้"
canImportAntennas: "อนุญาตให้นำเข้าเสาอากาศ"
canImportBlocking: "อนุญาตให้นำเข้าการบล็อก"
@@ -2138,7 +2136,7 @@ _sensitiveMediaDetection:
sensitivityDescription: "เมื่อความไวต่ำ Misdetection (ผลบวกลวง) จะลดลง, เมื่อความไวสูง Missed detection (ผลลบลวง) จะลดลง"
setSensitiveFlagAutomatically: "ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน"
setSensitiveFlagAutomaticallyDescription: "ผลลัพธ์ของการตรวจจับภายในนั้นจะยังคงอยู่ ถึงแม้ว่าจะปิดตัวเลือกนี้"
analyzeVideos: "เปิดใช้งานวิเคราะห์วิดีโอ"
analyzeVideos: "เปิดใช้งานวิเคราะห์ของวิดีโอ"
analyzeVideosDescription: "การวิเคราะห์วิดีโอนอกเหนือจากรูปภาพนั้น การทำสิ่งนี้จะทำให้เพิ่มภาระบนเซิร์ฟเวอร์เล็กน้อย"
_emailUnavailable:
used: "ที่อยู่อีเมลนี้ได้ถูกใช้ไปแล้ว"
@@ -2588,7 +2586,7 @@ _widgetOptions:
period: "ระยะเวลา"
_cw:
hide: "ซ่อน"
show: "ดเพิ่มเติม"
show: "โหลดเพิ่มเติม"
chars: "{count} ตัวอักษร"
files: "{count} ไฟล์"
_poll:
@@ -3031,7 +3029,7 @@ _externalResourceInstaller:
description: "เกิดปัญหาระหว่างการติดตั้งธีม กรุณาลองอีกครั้ง. รายละเอียดข้อผิดพลาดสามารถดูได้ในคอนโซล Javascript"
_dataSaver:
_media:
title: "ปิดใช้งานการโหลดสื่ออัตโนมัติ"
title: "โหลดสื่อ"
description: "กันไม่ให้ภาพและวิดีโอโหลดโดยอัตโนมัติ แตะรูปภาพ/วิดีโอที่ซ่อนอยู่เพื่อโหลด"
_avatar:
title: "ปิดใช้งานภาพเคลื่อนไหวของไอคอนประจำตัว"
@@ -3113,7 +3111,7 @@ _urlPreviewSetting:
summaryProxyDescription: "สร้างการแสดงตัวอย่างด้วย summary Proxy แทนที่จะใช้เนื้อหา Misskey"
summaryProxyDescription2: "พารามิเตอร์ต่อไปนี้จะถูกใช้เป็นสตริงการสืบค้นเพื่อเชื่อมต่อกับพร็อกซี หากฝั่งพร็อกซีไม่รองรับการตั้งค่าเหล่านี้จะถูกละเว้น"
_mediaControls:
pip: "ภาพซ้อนภาพ (PiP)"
pip: "รูปภาพในรูปภาม"
playbackRate: "ความเร็วในการเล่น"
loop: "เล่นวนซ้ำ"
_contextMenu:

View File

@@ -1408,7 +1408,6 @@ frame: "Çerçeve"
presets: "Ön ayar"
zeroPadding: "Sıfır doldurma"
nothingToConfigure: "Ayarlar seçeneği bulunmamaktadır."
viewRenotedChannel: "Show renoted channel"
_imageEditing:
_vars:
caption: "Dosya başlığı"

View File

@@ -63,7 +63,7 @@ copyUserId: "复制用户 ID"
copyNoteId: "复制帖子 ID"
copyFileId: "复制文件ID"
copyFolderId: "复制文件夹ID"
copyProfileUrl: "复制个人资料链接"
copyProfileUrl: "复制个人资料URL"
searchUser: "搜索用户"
searchThisUsersNotes: "搜索用户帖子"
reply: "回复"
@@ -101,12 +101,12 @@ somethingHappened: "出错了"
retry: "重试"
pageLoadError: "页面加载失败。"
pageLoadErrorDescription: "这通常是由于网络或浏览器缓存的原因。请清除缓存或等待片刻后重试。"
serverIsDead: "服务器响应。 请稍后再试。"
serverIsDead: "没有服务器响应。 请稍后再试。"
youShouldUpgradeClient: "请重新加载并使用新版本的客户端查看此页面。"
enterListName: "输入列表名称"
privacy: "隐私"
makeFollowManuallyApprove: "关注请求需要批准"
defaultNoteVisibility: "默认可见范围"
defaultNoteVisibility: "默认可见"
follow: "关注"
followRequest: "申请关注"
followRequests: "关注请求"
@@ -137,10 +137,10 @@ pinnedEmojisForReactionSettingDescription: "可以设置发表回应时置顶显
pinnedEmojisSettingDescription: "可以设置输入表情符号时置顶显示的表情符号"
emojiPickerDisplay: "选择器显示设置"
overwriteFromPinnedEmojisForReaction: "使用「置顶(回应)」设置覆盖"
overwriteFromPinnedEmojis: "使用全局设置覆盖"
overwriteFromPinnedEmojis: "全局设置覆盖"
reactionSettingDescription2: "拖动重新排序,单击删除,点击 + 添加。"
rememberNoteVisibility: "保存上次设置的可见性"
attachCancel: "移除附件"
attachCancel: "取消添加附件"
deleteFile: "删除文件"
markAsSensitive: "标记为敏感内容"
unmarkAsSensitive: "取消标记为敏感内容"
@@ -149,12 +149,12 @@ mute: "屏蔽"
unmute: "取消隐藏"
renoteMute: "隐藏转帖"
renoteUnmute: "取消隐藏转帖"
block: "禁止对方与我互动"
unblock: "允许对方与我互动"
block: "屏蔽"
unblock: "取消屏蔽"
suspend: "冻结"
unsuspend: "解除冻结"
blockConfirm: "确定要禁止对方与我互动吗?"
unblockConfirm: "确定要允许对方与我互动吗?"
blockConfirm: "确定要屏蔽吗?"
unblockConfirm: "确定要取消屏蔽吗?"
suspendConfirm: "要冻结吗?"
unsuspendConfirm: "要解除冻结吗?"
selectList: "选择列表"
@@ -184,7 +184,7 @@ flagAsCat: "喵!!!!!!!!!!!!"
flagAsCatDescription: "喵喵喵??"
flagShowTimelineReplies: "在时间线上显示帖子的回复"
flagShowTimelineRepliesDescription: "启用时,时间线除了显示用户的帖子外,还会显示其他用户对帖子的回复。"
autoAcceptFollowed: "自动允许我关注的人的关注请求"
autoAcceptFollowed: "自动允许回关请求"
addAccount: "添加账户"
reloadAccountsList: "更新账户列表"
loginFailed: "登录失败"
@@ -207,7 +207,7 @@ selectSelf: "选择自己"
selectUser: "选择用户"
recipient: "收件人"
annotation: "注解"
federation: "联"
federation: "联"
instances: "服务器"
registeredAt: "初次观测"
latestRequestReceivedAt: "上次收到的请求"
@@ -244,11 +244,11 @@ silencedInstances: "被静音的服务器"
silencedInstancesDescription: "设置要静音的服务器,以换行分隔。被静音的服务器内所有的账户都被视为「静音」状态,且关注操作均需要被批准。已被屏蔽的实例不受影响。"
mediaSilencedInstances: "已隐藏媒体文件的服务器"
mediaSilencedInstancesDescription: "设置要隐藏媒体文件的服务器,以换行分隔。被设置的服务器内所有账号的文件均按照「敏感内容」处理,且将无法使用自定义表情符号。已被屏蔽的实例不受影响。"
federationAllowedHosts: "允许联邦交互的服务器"
federationAllowedHostsDescription: "设定允许联邦通信的服务器,以换行分隔。"
muteAndBlock: "屏蔽用户/禁止用户与我互动"
mutedUsers: "已屏蔽的用户"
blockedUsers: "禁止与我互动的用户"
federationAllowedHosts: "允许联的服务器"
federationAllowedHostsDescription: "设定允许联的服务器,以换行分隔。"
muteAndBlock: "隐藏/屏蔽"
mutedUsers: "已隐藏的用户"
blockedUsers: "已屏蔽的用户"
noUsers: "无用户"
editProfile: "编辑资料"
noteDeleteConfirm: "确定要删除该帖子吗?"
@@ -261,7 +261,7 @@ default: "默认"
defaultValueIs: "默认值: {value}"
noCustomEmojis: "没有自定义表情符号"
noJobs: "没有任务"
federating: "联邦通信中"
federating: "联中"
blocked: "已屏蔽"
suspended: "停止投递"
all: "全部"
@@ -277,7 +277,7 @@ retypedNotMatch: "两次输入不一致!"
currentPassword: "现在的密码"
newPassword: "新密码"
newPasswordRetype: "重新输入密码:"
attachFile: "添加附件"
attachFile: "插入附件"
more: "更多!"
featured: "热门"
usernameOrUserId: "用户名或用户 ID"
@@ -342,7 +342,7 @@ selectFolders: "选择多个文件夹"
fileNotSelected: "未选择文件"
renameFile: "重命名文件"
folderName: "文件夹名称"
createFolder: "建文件夹"
createFolder: "建文件夹"
renameFolder: "重命名文件夹"
deleteFolder: "删除文件夹"
folder: "文件夹"
@@ -353,7 +353,7 @@ emptyFolder: "此文件夹中无文件"
dropHereToUpload: "将文件拖动到这里来上传"
unableToDelete: "无法删除"
inputNewFileName: "请输入新文件名"
inputNewDescription: "请输入新的描述文本"
inputNewDescription: "请输入新标题"
inputNewFolderName: "请输入新文件夹名"
circularReferenceFolder: "目标文件夹是要移动的文件夹的子文件夹。"
hasChildFilesOrFolders: "此文件夹中有文件,无法删除。"
@@ -396,10 +396,10 @@ driveCapacityPerLocalAccount: "每个用户的网盘容量"
driveCapacityPerRemoteAccount: "每个远程用户的网盘容量"
inMb: "以兆字节(MegaByte)为单位"
bannerUrl: "横幅 URL"
backgroundImageUrl: "背景图片的链接"
backgroundImageUrl: "背景图 URL"
basicInfo: "基本信息"
pinnedUsers: "置顶用户"
pinnedUsersDescription: "在「发现页面中使用换行标记想要置顶的用户。"
pinnedUsersDescription: "输入您想要固定到“发现页面的用户,一行一个。"
pinnedPages: "固定页面"
pinnedPagesDescription: "输入您要固定到服务器首页的页面路径,以换行符分隔。"
pinnedClipId: "置顶的便签 ID"
@@ -432,7 +432,7 @@ antennaExcludeBots: "排除机器人账户"
antennaKeywordsDescription: "AND 条件用空格分隔OR 条件用换行符分隔。"
notifyAntenna: "开启通知"
withFileAntenna: "仅带有附件的帖子"
excludeNotesInSensitiveChannel: "排除敏感频道的帖子"
excludeNotesInSensitiveChannel: "排除敏感频道的帖子"
enableServiceworker: "启用 ServiceWorker"
antennaUsersDescription: "指定用户名,用换行符进行分隔"
caseSensitive: "区分大小写"
@@ -476,7 +476,7 @@ passwordLessLogin: "无密码登录"
passwordLessLoginDescription: "不使用密码,仅使用安全密钥或 Passkey 登录"
resetPassword: "重置密码"
newPasswordIs: "新的密码是「{password}」"
reduceUiAnimation: "减少 UI 动"
reduceUiAnimation: "减少 UI 动"
share: "分享"
notFound: "未找到"
notFoundDescription: "没有与指定 URL 对应的页面。"
@@ -543,7 +543,7 @@ regenerate: "重新生成"
fontSize: "字体大小"
mediaListWithOneImageAppearance: "仅一张图片的媒体列表高度"
limitTo: "上限为 {x}"
showMediaListByGridInWideArea: "在宽屏上并排显示媒体列表"
showMediaListByGridInWideArea: "在大屏幕上并排显示媒体列表"
noFollowRequests: "没有关注请求"
openImageInNewTab: "在新标签页中打开图片"
dashboard: "管理面板"
@@ -581,7 +581,7 @@ s3ForcePathStyleDesc: "启用 s3ForcePathStyle 会强制将存储桶名称指定
serverLogs: "服务器日志"
deleteAll: "全部删除"
showFixedPostForm: "在时间线顶部显示发帖框"
showFixedPostFormInChannel: "在时间线顶部显示发帖框(频道)"
showFixedPostFormInChannel: "在时间线顶部显示发帖对话框(频道)"
withRepliesByDefaultForNewlyFollowed: "在时间线中默认包含新关注用户的回复"
newNoteRecived: "有新的帖子"
newNote: "新帖子"
@@ -656,7 +656,7 @@ expandTweet: "展开帖子"
themeEditor: "主题编辑器"
description: "描述"
describeFile: "添加描述"
enterFileDescription: "输入描述文本"
enterFileDescription: "输入标题"
author: "作者"
leaveConfirm: "存在未保存的更改。要放弃更改吗?"
manage: "管理"
@@ -664,7 +664,7 @@ plugins: "插件"
preferencesBackups: "备份设置"
deck: "Deck"
undeck: "取消 Deck"
useBlurEffectForModal: "发帖背景使用模糊效果"
useBlurEffectForModal: "对话框使用模糊效果"
useFullReactionPicker: "使用全功能的回应工具栏"
width: "宽度"
height: "高度"
@@ -773,13 +773,13 @@ yes: "是"
no: "否"
driveFilesCount: "网盘的文件数"
driveUsage: "网盘的空间用量"
noCrawle: "拒绝搜索引擎索引"
noCrawleDescription: "拒绝搜索引擎收录(索引)您的个人资料,帖子,页面等。"
noCrawle: "要求搜索引擎索引该用户"
noCrawleDescription: "要求搜索引擎不要收录(索引)您的用户页面,帖子,页面等。"
lockedAccountInfo: "即使启用该功能,只要帖子可见范围不是「仅关注者」,任何人都可以看到您的帖子。"
alwaysMarkSensitive: "默认将媒体文件标记为敏感内容"
loadRawImages: "添加附件图像的缩略图时使用原始图像质量"
disableShowingAnimatedImages: "不播放动态图像"
disableShowingAnimatedImages_caption: "如果即使禁用了此设置,动态图像仍无法播放,可能是由于浏览器或操作系统的辅助功能设置、省电设置或其他因素所致。"
disableShowingAnimatedImages: "不播放动"
disableShowingAnimatedImages_caption: "如果即使关闭了此设置但动画仍无法播放,可能是浏览器或操作系统的辅助功能设置,又或者是省电设置等产生了干扰。"
highlightSensitiveMedia: "高亮显示敏感媒体"
verificationEmailSent: "已发送确认电子邮件。请访问电子邮件中的链接以完成设置。"
notSet: "未设置"
@@ -851,7 +851,7 @@ fullView: "全屏"
quitFullView: "退出全屏"
addDescription: "添加描述"
userPagePinTip: "在帖子的菜单中选择“置顶”,即可显示该条帖子。"
notSpecifiedMentionWarning: "有未添加到收件人的提及"
notSpecifiedMentionWarning: "有未指定的提及"
info: "关于"
userInfo: "用户信息"
unknown: "未知"
@@ -877,9 +877,9 @@ noMaintainerInformationWarning: "尚未设置管理员信息。"
noInquiryUrlWarning: "尚未设置联络地址。"
noBotProtectionWarning: "尚未设置 Bot 防御。"
configure: "设置"
postToGallery: "发表相册"
postToGallery: "创建新图集"
postToHashtag: "发布至该话题"
gallery: "相册"
gallery: "图集"
recentPosts: "最新发布"
popularPosts: "热门投稿"
shareWithNote: "分享到帖文"
@@ -1032,7 +1032,7 @@ browserPushNotificationDisabledDescription: "{serverName}无权限发送通知
windowMaximize: "最大化"
windowMinimize: "最小化"
windowRestore: "还原"
caption: "描述文本"
caption: "标题"
loggedInAsBot: "以 Bot 账户登录"
tools: "工具"
cannotLoad: "无法加载"
@@ -1073,7 +1073,7 @@ thisPostMayBeAnnoying: "这个帖子可能会让其他人感到困扰。"
thisPostMayBeAnnoyingHome: "发到首页"
thisPostMayBeAnnoyingCancel: "取消"
thisPostMayBeAnnoyingIgnore: "就这样发布"
collapseRenotes: "折叠已经看过的转"
collapseRenotes: "省略显示已经看过的转发内容"
collapseRenotesDescription: "折叠显示回应或转发过的帖文。"
internalServerError: "内部服务器错误"
internalServerErrorDescription: "内部服务器发生了预期外的错误"
@@ -1081,9 +1081,9 @@ copyErrorInfo: "复制错误信息"
joinThisServer: "在本服务器上注册"
exploreOtherServers: "探索其他服务器"
letsLookAtTimeline: "看看时间线"
disableFederationConfirm: "确定要禁用联邦交互"
disableFederationConfirmWarn: "即使禁用联邦交互,也不会将帖子设为私有。在大多数情况下,没有必要禁用联邦交互。"
disableFederationOk: "禁用联邦"
disableFederationConfirm: "确定要禁用联"
disableFederationConfirmWarn: "禁用联不会将帖子设为私有。在大多数情况下,不需要禁用联。"
disableFederationOk: "联合禁用"
invitationRequiredToRegister: "此服务器目前只允许拥有邀请码的人注册。"
emailNotSupported: "此服务器不支持发送邮件"
postToTheChannel: "发布到频道"
@@ -1093,7 +1093,7 @@ likeOnly: "仅点赞"
likeOnlyForRemote: "全部(远程仅点赞)"
nonSensitiveOnly: "仅限非敏感内容"
nonSensitiveOnlyForLocalLikeOnlyForRemote: "仅限非敏感内容(远程仅点赞)"
rolesAssignedToMe: "的角色"
rolesAssignedToMe: "指派给自己的角色"
resetPasswordConfirm: "确定重置密码?"
sensitiveWords: "敏感词"
sensitiveWordsDescription: "包含这些词的帖子将只在首页可见。可用换行来设定多个词。"
@@ -1148,7 +1148,7 @@ pleaseAgreeAllToContinue: "必须全部勾选「同意」才能够继续。"
continue: "继续"
preservedUsernames: "保留的用户名"
preservedUsernamesDescription: "列出需要保留的用户名,使用换行来作为分割。被指定的用户名在建立账户时无法使用,但由管理员所创建的账户不受该限制。此外,现有的账户也不会受到影响。"
createNoteFromTheFile: "使用该文件发帖"
createNoteFromTheFile: "从文件创建帖子"
archive: "归档"
archived: "已归档"
unarchive: "取消归档"
@@ -1158,7 +1158,7 @@ thisChannelArchived: "该频道已被归档。"
displayOfNote: "显示帖子"
initialAccountSetting: "初始设定"
youFollowing: "正在关注"
preventAiLearning: "拒绝用于训练生成式 AI"
preventAiLearning: "拒绝接受生成式 AI 的学习"
preventAiLearningDescription: "要求文章生成 AI 或图像生成 AI 不能够以发布的帖子和图像等内容作为学习对象。这是通过在 HTML 响应中包含 noai 标志来实现的,这不能完全阻止 AI 学习你的发布内容,并不是所有 AI 都会遵守这类请求。"
options: "选项"
specifyUser: "指定用户"
@@ -1226,8 +1226,8 @@ notificationRecieveConfig: "通知接收设置"
mutualFollow: "互相关注"
followingOrFollower: "关注中或关注者"
fileAttachedOnly: "仅限媒体"
showRepliesToOthersInTimeline: "在时间线中显示对他人的回复"
hideRepliesToOthersInTimeline: "在时间线中隐藏对他人的回复"
showRepliesToOthersInTimeline: "在时间线中包含给别人的回复"
hideRepliesToOthersInTimeline: "在时间线中隐藏给别人的回复"
showRepliesToOthersInTimelineAll: "在时间线中显示所有现在关注的人的回复"
hideRepliesToOthersInTimelineAll: "在时间线中隐藏所有现在关注的人的回复"
confirmShowRepliesAll: "此操作不可撤销。确认要在时间线中显示所有现在关注的人的回复吗?"
@@ -1325,22 +1325,22 @@ lockdown: "锁定"
pleaseSelectAccount: "请选择帐户"
availableRoles: "可用角色"
acknowledgeNotesAndEnable: "理解注意事项后再开启。"
federationSpecified: "此服务器已开启联白名单模式。只能与管理员指定的服务器通信。"
federationDisabled: "此服务器已禁用联邦功能。无法与其它服务器上的用户通信。"
federationSpecified: "此服务器已开启联白名单。只能与管理员指定的服务器通信。"
federationDisabled: "此服务器已禁用联。无法与其它服务器上的用户通信。"
draft: "草稿"
draftsAndScheduledNotes: "草稿和定时发送"
confirmOnReact: "发送回应前需要确认"
reactAreYouSure: "要用「{emoji}」进行回应吗?"
markAsSensitiveConfirm: "确定标记此媒体为敏感内容吗?"
unmarkAsSensitiveConfirm: "确定取消标记为敏感内容吗?"
markAsSensitiveConfirm: "要将此媒体标记为敏感吗?"
unmarkAsSensitiveConfirm: "要将此媒体解除敏感标记吗?"
preferences: "偏好设置"
accessibility: "辅助功能"
preferencesProfile: "设置的配置文件"
copyPreferenceId: "复制设置 ID"
resetToDefaultValue: "重置为默认值"
overrideByAccount: "使用账户设置"
overrideByAccount: "覆盖账号"
untitled: "未命名"
noName: "未命名"
noName: "没有名字"
skip: "跳过"
restore: "恢复"
syncBetweenDevices: "设备间同步"
@@ -1351,7 +1351,7 @@ preferenceSyncConflictChoiceServer: "服务器上的设定值"
preferenceSyncConflictChoiceDevice: "设备上的设定值"
preferenceSyncConflictChoiceCancel: "取消同步"
paste: "粘贴"
emojiPalette: "表情符号选择器"
emojiPalette: "表情符号调色板"
postForm: "发帖窗口"
textCount: "字数"
information: "关于"
@@ -1368,7 +1368,7 @@ embed: "嵌入"
settingsMigrating: "正在迁移设置,请稍候。(之后也可以在设置 → 其它 → 迁移旧设置来手动迁移)"
readonly: "只读"
goToDeck: "返回至 Deck"
federationJobs: "联作业"
federationJobs: "联作业"
driveAboutTip: "网盘可以显示以前上传的文件。<br>\n也可以在发布帖子时重复使用文件或在发布帖子前预先上传文件。<br>\n<b>删除文件时,其将从至今为止所有用到该文件的地方(如帖子、页面、头像、横幅)消失。</b><br>\n也可以新建文件夹来整理文件。"
scrollToClose: "滑动并关闭"
advice: "建议"
@@ -1382,7 +1382,7 @@ unmuteX: "取消对{x}的隐藏"
abort: "中止"
tip: "提示和技巧"
redisplayAllTips: "重新显示所有的提示和技巧"
hideAllTips: "隐藏所有的提示技巧"
hideAllTips: "隐藏所有的提示技巧"
defaultImageCompressionLevel: "默认图像压缩等级"
defaultImageCompressionLevel_description: "较低的等级可以保持画质,但会增加文件大小。<br>较高的等级可以减少文件大小,但相对应的画质将会降低。"
defaultCompressionLevel: "默认压缩等级"
@@ -1394,7 +1394,7 @@ pluginsAreDisabledBecauseSafeMode: "因启用了安全模式,所有插件均
customCssIsDisabledBecauseSafeMode: "因启用了安全模式,无法应用自定义 CSS。"
themeIsDefaultBecauseSafeMode: "启用安全模式时将使用默认主题。关闭安全模式后将还原。"
thankYouForTestingBeta: "感谢您协助测试 beta 版!"
createUserSpecifiedNote: "提及该用户并发帖"
createUserSpecifiedNote: "创建指定用户的帖子"
schedulePost: "定时发布"
scheduleToPostOnX: "预定在 {x} 发出"
scheduledToPostOnX: "已预定在 {x} 发出"
@@ -1511,10 +1511,10 @@ _chat:
mutual: "仅相互关注"
none: "没有人"
_emojiPalette:
palettes: "表情符号托盘"
enableSyncBetweenDevicesForPalettes: "在设备间同步表情符号托盘"
paletteForMain: "主表情符号托盘"
paletteForReaction: "回应时的表情符号托盘"
palettes: "调色板"
enableSyncBetweenDevicesForPalettes: "启用调色板的设备间同步"
paletteForMain: "主调色板"
paletteForReaction: "回应用调色板"
_settings:
driveBanner: "可在此管理和设置网盘、确认使用量及配置上传文件的设置。"
pluginBanner: "使用插件可以扩展客户端的功能。可以在此安装、单独管理插件。"
@@ -1535,9 +1535,9 @@ _settings:
timelineAndNote: "时间线和帖子"
makeEveryTextElementsSelectable: "使所有的文字均可选择"
makeEveryTextElementsSelectable_description: "若开启,在某些情况下可能降低用户体验。"
useStickyIcons: "用户头像跟随页面滚动"
useStickyIcons: "使图标跟随滚动"
enableHighQualityImagePlaceholders: "显示高质量图像的占位符"
uiAnimations: "UI 动"
uiAnimations: "UI 动"
showNavbarSubButtons: "在导航栏中显示副按钮"
ifOn: "启用时"
ifOff: "关闭时"
@@ -1552,7 +1552,7 @@ _settings:
showAvailableReactionsFirstInNote: "在顶部显示可用的回应"
showPageTabBarBottom: "在下方显示页面标签栏"
emojiPaletteBanner: "可以将固定显示在表情符号选择器中的预设注册为调色板,也可以自定义表情符号选择器的显示方式。"
enableAnimatedImages: "启用动图像"
enableAnimatedImages: "启用动图像"
settingsPersistence_title: "设置持久化"
settingsPersistence_description1: "启用设置持久化可防止设置信息丢失。"
settingsPersistence_description2: "根据环境不同,有可能无法开启。"
@@ -1580,12 +1580,12 @@ _accountSettings:
requireSigninToViewContents: "需要登录才能显示内容"
requireSigninToViewContentsDescription1: "您发布的所有帖子将变成需要登入后才会显示。有望防止爬虫收集各种信息。"
requireSigninToViewContentsDescription2: "没有 URL 预览OGP、内嵌网页、引用帖子的功能的服务器也将无法显示。"
requireSigninToViewContentsDescription3: "对于已通过联邦分发到远程服务器的内容,这些限制可能不适用。"
requireSigninToViewContentsDescription3: "这些限制可能不适用于联合到远程服务器的内容。"
makeNotesFollowersOnlyBefore: "可将过去的帖子设为仅关注者可见"
makeNotesFollowersOnlyBeforeDescription: "开启此设定时,超过设定的时间或日期后,帖子将变为仅关注者可见。关闭后帖子的公开状态将恢复成原本的设定。"
makeNotesHiddenBefore: "将过去的帖子设为私密"
makeNotesHiddenBeforeDescription: "开启此设定时,超过设定的时间或日期后,帖子将变为仅自己可见。关闭后帖子的公开状态将恢复成原本的设定。"
mayNotEffectForFederatedNotes: "对于已通过联邦投递到远程服务器的帖子,此操作在远端可能无法生效。"
mayNotEffectForFederatedNotes: "远程服务器联合的帖子在远端可能会没有效果。"
mayNotEffectSomeSituations: "此限制功能非常简单,在与远程服务器联合等情形时可能不适用。"
notesHavePassedSpecifiedPeriod: "超过指定时间的帖子"
notesOlderThanSpecifiedDateAndTime: "指定日期前的帖子"
@@ -1637,7 +1637,7 @@ _announcement:
_initialAccountSetting:
accountCreated: "账户创建完成了!"
letsStartAccountSetup: "马上来进行账户的初始设定吧。"
letsFillYourProfile: "首先,设置一下您的个人资料吧!"
letsFillYourProfile: "首先,来设定你的个人档案吧!"
profileSetting: "个人资料设置"
privacySetting: "隐私设置"
theseSettingsCanEditLater: "也可以在稍后修改这里的设置。"
@@ -1689,10 +1689,10 @@ _initialTutorial:
public: "向所有用户公开。\n"
home: "仅在首页时间线上发布。 关注者、从个人资料页查看过来的用户、以及通过转帖也能被别的用户看见。"
followers: "仅对关注者可见。 除了您自己之外,没有人可以转贴,并且只有您的关注者可以查看它。\n"
direct: "仅对指定用户公开,且收件人将收到通知。"
direct: "它将仅向指定用户公开,并且他们也会收到通知。 您可以使用它来代替私信。\n"
doNotSendConfidencialOnDirect1: "发送敏感信息时请注意。\n"
doNotSendConfidencialOnDirect2: "目标服务器的管理员可以看到发布的内容,因此如果您向不受信任的服务器上的用户发送私信,则在处理敏感信息时需要小心。"
localOnly: "不将帖子通过联邦推送到其它服务器。 无论上述公开范围如何,其它服务器的用户将无法看到附加了此设定的帖子。\n"
localOnly: "不将帖子推送到其它服务器。 无论上述公开范围如何,其它服务器的用户将无法看到附加了此设定的帖子。\n"
_cw:
title: "隐藏内容 (CW)\n"
description: "显示「注解」里的内容而不是正文。点击「查看更多」将会把正文显示出来。"
@@ -1701,7 +1701,7 @@ _initialTutorial:
note: "茨了带巧克力的甜甜圈🍩😋"
useCases: "用于服务器条款所规定的帖子,或对剧透内容和敏感内容进行自主规制。"
_howToMakeAttachmentsSensitive:
title: "如何标记附件为敏感内容?"
title: "如何将附件标注为敏感内容?"
description: "对于服务器方针所要求要求的,又或者不适合直接展示的附件,请添加「敏感」标记。\n"
tryThisFile: "试试看,将附加到此窗口的图像标注为敏感!"
_exampleNote:
@@ -1746,7 +1746,7 @@ _serverSettings:
singleUserMode: "单用户模式"
singleUserMode_description: "若此服务器只有自己使用,开启此模式将最佳化性能。"
signToActivityPubGet: "对 GET 请求签名"
signToActivityPubGet_description: "通常情况下请保持启用。若遇到联通信方面的问题,将其关闭可能会有所改善,但另一方面有可能会造成无法通信。"
signToActivityPubGet_description: "通常情况下请保持启用。若遇到联通信方面的问题,将其关闭可能会有所改善,但另一方面有可能会造成无法通信。"
proxyRemoteFiles: "代理远程文件"
proxyRemoteFiles_description: "如果启用,远程服务器的文件将由代理提供。可有效保护图像预览缩略图的生成与用户隐私。"
allowExternalApRedirect: "允许通过 ActivityPub 重定向查询"
@@ -1771,7 +1771,7 @@ _accountMigration:
moveTo: "把这个账户迁移到新的账户"
moveToLabel: "迁移后的账户"
moveCannotBeUndone: "一旦迁移账户,就无法撤销。"
moveAccountDescription: "\n迁移到新帐户。\n ・现有的关注者自动关注新帐户\n ・此帐户的所有关注者都将被删除\n ・您将无法再使用此帐户发帖。\n关注者迁移是自动的但关注中迁移必须手动完成。请在迁移前在此帐户上导出关注列表并在迁移后立即在目标帐户上执行导入。\n列表、屏蔽列表、禁止与我互动的列表也是如此,因此您必须手动迁移它。\n此描述适用于该服务器Misskey v13.12.0 或更高版本)。其他 ActivityPub 软件(例如 Mastodon的行为可能有所不同。"
moveAccountDescription: "\n迁移到新帐户。\n ・现有的关注者自动关注新帐户\n ・此帐户的所有关注者都将被删除\n ・您将无法再使用此帐户发帖。\n关注者迁移是自动的但关注中迁移必须手动完成。请在迁移前在此帐户上导出关注列表并在迁移后立即在目标帐户上执行导入。\n列表、隐藏、屏蔽也是如此,因此您必须手动迁移它。\n此描述适用于该服务器Misskey v13.12.0 或更高版本)。其他 ActivityPub 软件(例如 Mastodon的行为可能有所不同。"
moveAccountHowTo: "要进行账户迁移,请现在目标账户中为此账户建立一个别名。\n建立别名后请像这样输入目标账户@username@server.example.com"
startMigration: "迁移"
migrationConfirm: "确定要把此账户迁移到 {account} 吗?一旦确定后,此操作无法取消,此账户也无法以原来的状态使用。\n同时请确认迁移后的账户已创造别名。"
@@ -1889,7 +1889,7 @@ _achievements:
description: "第一次将帖子加入收藏"
_myNoteFavorited1:
title: "想要星星"
description: "自己的帖子被其他人收藏了"
description: "自己的帖子被其他人加入收藏了"
_profileFilled:
title: "整装待发"
description: "设置了个人资料"
@@ -2083,7 +2083,7 @@ _role:
maxFileSize: "可上传的最大文件大小"
maxFileSize_caption: "可能在反向代理或 CDN 等前端存在其它设定值。"
alwaysMarkNsfw: "总是将文件标记为 NSFW"
canUpdateBioMedia: "允许更新头像和横幅"
canUpdateBioMedia: "可以更新头像和横幅"
pinMax: "帖子置顶数量限制"
antennaMax: "可创建的最大天线数量"
wordMuteMax: "折叠词的字数限制"
@@ -2098,10 +2098,9 @@ _role:
canSearchNotes: "是否可以搜索帖子"
canSearchUsers: "使用用户检索"
canUseTranslator: "使用翻译功能"
canCreateChannel: "创建频道"
avatarDecorationLimit: "可添加头像挂件的最大个数"
canImportAntennas: "允许导入天线"
canImportBlocking: "允许导入禁止与我互动的列表"
canImportBlocking: "允许导入屏蔽列表"
canImportFollowing: "允许导入关注列表"
canImportMuting: "允许导入隐藏列表"
canImportUserLists: "允许导入用户列表"
@@ -2109,7 +2108,7 @@ _role:
uploadableFileTypes: "可上传的文件类型"
uploadableFileTypes_caption: "指定 MIME 类型。可用换行指定多个类型,也可以用星号(*)作为通配符。(如 image/*"
uploadableFileTypes_caption2: "文件根据文件的不同,可能无法判断其类型。若要允许此类文件,请在指定中添加 {x}。"
noteDraftLimit: "可在服务器上创建的草稿数量"
noteDraftLimit: "可在服务器上创建多少草稿"
scheduledNoteLimit: "可同时创建的定时帖子数量"
watermarkAvailable: "能否使用水印功能"
_condition:
@@ -2166,7 +2165,7 @@ _ad:
back: "返回"
reduceFrequencyOfThisAd: "减少此广告的频率"
hide: "不显示"
timezoneinfo: "星期几是根据服务器的时区定的。"
timezoneinfo: "星期几是服务器的时区所指定的。"
adsSettings: "广告设置"
notesPerOneAd: "在实时更新时间线中插入广告的间隔(帖子个数)"
setZeroToDisable: "设为 0 将不在实时更新时间线中投放广告"
@@ -2230,7 +2229,7 @@ _aboutMisskey:
_displayOfSensitiveMedia:
respect: "隐藏敏感媒体"
ignore: "显示敏感媒体"
force: "隐藏所有媒体"
force: "隐藏所有内容"
_instanceTicker:
none: "不显示"
remote: "仅远程用户"
@@ -2254,7 +2253,7 @@ _channel:
allowRenoteToExternal: "允许转发到频道外和引用"
_menuDisplay:
sideFull: "横向"
sideIcon: "横向图标"
sideIcon: "横向(图标)"
top: "顶部"
hide: "隐藏"
_wordMute:
@@ -2316,7 +2315,7 @@ _theme:
mention: "提及"
mentionMe: "提及"
renote: "转发"
modalBg: "发帖背景"
modalBg: "对话框背景"
divider: "分割线"
scrollbarHandle: "滚动条"
scrollbarHandleHover: "滚动条(悬停)"
@@ -2335,7 +2334,7 @@ _theme:
fgHighlighted: "高亮显示文本"
_sfx:
note: "帖子"
noteMy: "发帖"
noteMy: "我的帖子"
notification: "通知"
reaction: "选择回应时"
chatMessage: "私信"
@@ -2404,8 +2403,8 @@ _2fa:
_permissions:
"read:account": "查看账户信息"
"write:account": "更改帐户信息"
"read:blocks": "查看禁止与我互动的列表"
"write:blocks": "编辑禁止与我互动的列表"
"read:blocks": "查看屏蔽列表"
"write:blocks": "编辑屏蔽列表"
"read:drive": "查看网盘"
"write:drive": "管理网盘文件"
"read:favorites": "查看收藏夹"
@@ -2430,10 +2429,10 @@ _permissions:
"write:user-groups": "编辑用户组"
"read:channels": "查看频道"
"write:channels": "管理频道"
"read:gallery": "浏览相册"
"write:gallery": "管理相册"
"read:gallery-likes": "浏览喜欢的相册"
"write:gallery-likes": "管理喜欢的相册"
"read:gallery": "浏览图集"
"write:gallery": "编辑图集"
"read:gallery-likes": "浏览喜欢的图集"
"write:gallery-likes": "管理喜欢的图集"
"read:flash": "查看 Play"
"write:flash": "编辑 Play"
"read:flash-likes": "查看 Play 的点赞"
@@ -2457,7 +2456,7 @@ _permissions:
"write:admin:unsuspend-user": "解除用户冻结"
"write:admin:meta": "编辑实例元数据"
"write:admin:user-note": "编辑管理笔记"
"write:admin:roles": "管理角色"
"write:admin:roles": "编辑角色"
"read:admin:roles": "查看角色"
"write:admin:relays": "编辑中继"
"read:admin:relays": "查看中继"
@@ -2467,8 +2466,8 @@ _permissions:
"read:admin:announcements": "查看公告"
"write:admin:avatar-decorations": "编辑头像挂件"
"read:admin:avatar-decorations": "查看头像挂件"
"write:admin:federation": "编辑联相关信息"
"write:admin:account": "管理用户账户"
"write:admin:federation": "编辑联相关信息"
"write:admin:account": "编辑用户账户"
"read:admin:account": "查看用户相关情报"
"write:admin:emoji": "编辑表情符号"
"read:admin:emoji": "查看表情符号"
@@ -2484,7 +2483,7 @@ _permissions:
"read:invite-codes": "获取已发行的邀请码"
"write:clip-favorite": "管理喜欢的便签"
"read:clip-favorite": "查看便签的点赞"
"read:federation": "查看联相关信息"
"read:federation": "查看联相关信息"
"write:report-abuse": "举报用户"
"write:chat": "撰写或删除消息"
"read:chat": "查看私信"
@@ -2531,7 +2530,7 @@ _widgets:
photos: "照片"
digitalClock: "数字时钟"
unixClock: "UNIX 时钟"
federation: "联"
federation: "联"
instanceCloud: "服务器球状列表"
postForm: "发帖窗口"
slideshow: "幻灯片展示"
@@ -2564,7 +2563,7 @@ _widgetOptions:
graduationDots: "点"
graduationArabic: "阿拉伯数字"
fadeGraduations: "淡化表盘"
sAnimation: "秒针动"
sAnimation: "秒针动"
sAnimationElastic: "跳动"
sAnimationEaseOut: "平滑"
twentyFour: "24 小时制"
@@ -2622,11 +2621,11 @@ _visibility:
followersDescription: "仅发送至关注者"
specified: "指定用户"
specifiedDescription: "仅发送至指定用户"
disableFederation: "仅限本地"
disableFederation: "不参与联合"
disableFederationDescription: "不发送到其他服务器"
_postForm:
quitInspiteOfThereAreUnuploadedFilesConfirm: "还有未上传的文件,要丢弃并关闭窗口吗?"
uploaderTip: "文件未上传。可以在文件菜单中设置重命名、裁剪图片、添加水印以及是否压缩等功能。文件将在帖子发布时自动上传。"
uploaderTip: "文件未上传。可以在文件菜单中进行重命名、裁剪、添加水印、设置是否压缩等操作。文件将在帖时自动上传。"
replyPlaceholder: "回复这个帖子..."
quotePlaceholder: "引用这个帖子..."
channelPlaceholder: "发布到频道…"
@@ -2661,12 +2660,12 @@ _profile:
metadataDescription: "最多可以在个人资料中以表格形式显示四条其他信息。"
metadataLabel: "标签"
metadataContent: "内容"
changeAvatar: "更换头像"
changeBanner: "更换横幅"
changeAvatar: "修改头像"
changeBanner: "修改横幅"
verifiedLinkDescription: "如果将内容设置为 URL当链接所指向的网页内包含自己的个人资料链接时可以显示一个已验证图标。"
avatarDecorationMax: "最多可添加 {max} 个挂件"
followedMessage: "被关注时的信息"
followedMessageDescription: "被关注时,可设置关注显示的息。"
followedMessage: "被关注时显示的消息"
followedMessageDescription: "可设置关注时向对方显示的短消息。"
followedMessageDescriptionForLockedAccount: "需要批准才能关注的情况下,消息会在请求被批准后显示。"
_exportOrImport:
allNotes: "所有帖子"
@@ -2674,13 +2673,13 @@ _exportOrImport:
clips: "便签"
followingList: "关注中"
muteList: "隐藏"
blockingList: "禁止与我互动的列表"
blockingList: "屏蔽"
userLists: "列表"
excludeMutingUsers: "排除已隐藏用户"
excludeInactiveUsers: "排除不活跃用户"
withReplies: "在时间线中包含导入用户的回复"
_charts:
federation: "联"
federation: "联"
apRequest: "请求"
usersIncDec: "用户数量:增加/减少"
usersTotal: "用户总数"
@@ -2978,7 +2977,7 @@ _moderationLogTypes:
deleteAccount: "删除帐户"
deletePage: "删除页面"
deleteFlash: "删除 Play"
deleteGalleryPost: "删除相册内容"
deleteGalleryPost: "删除图集内容"
deleteChatRoom: "删除群聊"
updateProxyAccountDescription: "更新代理账户的简介"
_fileViewer:
@@ -3035,7 +3034,7 @@ _dataSaver:
description: "防止自动加载图像和视频。 点击隐藏的图像/视频即可加载它们。\n"
_avatar:
title: "头像"
description: "播放头像的动画。 由于动态图像的文件大小远大于一般图像,停止播放能够进一步节省数据流量。"
description: "停止播放头像的动画。 由于动画图片的文件大小可能比普通图像大,这可以进一步减少数据流量。"
_urlPreviewThumbnail:
title: "不显示 URL预览缩略图"
description: "将不再加载 URL 预览缩略图。"
@@ -3054,7 +3053,7 @@ _reversi:
gameSettings: "对局设置"
chooseBoard: "选择棋盘"
blackOrWhite: "先手/后手"
blackIs: "{name}执黑先手"
blackIs: "{name}执黑(先手)"
rules: "规则"
thisGameIsStartedSoon: "对局即将开始"
waitingForOther: "等待对手准备"
@@ -3070,7 +3069,7 @@ _reversi:
surrendered: "已认输"
timeout: "超时"
drawn: "平局"
won: "{name} 获胜"
won: "{name}获胜"
black: "黑"
white: "白"
total: "总计"
@@ -3079,7 +3078,7 @@ _reversi:
allGames: "所有对局"
ended: "结束"
playing: "对局中"
isLlotheo: "落子少的一方获胜(黑白棋规则"
isLlotheo: "落子少的一方获胜(又名奥赛罗"
loopedMap: "循环棋盘"
canPutEverywhere: "无限制放置模式"
timeLimitForEachTurn: "1回合的时间限制"
@@ -3089,8 +3088,8 @@ _reversi:
shareToTlTheGameWhenStart: "开始时在时间线发布对局"
iStartedAGame: "对局开始!#MisskeyReversi"
opponentHasSettingsChanged: "对手更改了设定"
allowIrregularRules: "允许特殊规则(完全自由)"
disallowIrregularRules: "禁止特殊规则"
allowIrregularRules: "允许非常规规则(完全自由)"
disallowIrregularRules: "禁止非常规规则"
showBoardLabels: "显示行号和列号"
useAvatarAsStone: "用头像作为棋子"
_offlineScreen:
@@ -3247,7 +3246,7 @@ _search:
searchScopeLocal: "本地"
searchScopeServer: "指定服务器"
searchScopeUser: "指定用户"
pleaseEnterServerHost: "请填写服务器主机名"
pleaseEnterServerHost: "请填写服务器主机名"
pleaseSelectUser: "请选择用户"
serverHostPlaceholder: "如misskey.example.com"
_serverSetupWizard:
@@ -3276,13 +3275,13 @@ _serverSetupWizard:
largeScaleServerAdvice: "运营大规模服务器可能需要高级基础设施知识,如负载均衡和数据库复制。"
doYouConnectToFediverse: "要加入 Fediverse 吗?"
doYouConnectToFediverse_description1: "若加入由分散性服务器所构成的网络Fediverse将能与其它服务器交换内容。"
doYouConnectToFediverse_description2: "加入 Fediverse 在这里被称为「联」。"
youCanConfigureMoreFederationSettingsLater: "可在之后进行如哪些服务器允许进行联邦交互等高级设置。"
doYouConnectToFediverse_description2: "加入 Fediverse 在这里被称为「联」。"
youCanConfigureMoreFederationSettingsLater: "可在之后进行如哪些服务器可以进行联等高级设置。"
remoteContentsCleaning: "自动清理传入内容"
remoteContentsCleaning_description: "开启联邦互通后,服务器将持续接收大量内容。打开自动清理后,将自动删除无法找到的旧内容,可节省存储空间。"
remoteContentsCleaning_description: "加入联合后,服务器将持续接收大量内容。打开自动清理后,将自动删除无法找到的旧内容,可节省存储空间。"
adminInfo: "管理员信息"
adminInfo_description: "设置用于接受询问的管理员信息。"
adminInfo_mustBeFilled: "开放服务器或启了联的情况下必须输入。"
adminInfo_mustBeFilled: "开放服务器或启了联的情况下必须输入。"
followingSettingsAreRecommended: "推荐以下设置"
applyTheseSettings: "使用此设置"
skipSettings: "跳过设置"
@@ -3302,7 +3301,7 @@ _uploader:
doneConfirm: "部分文件尚未上传,是否继续?"
maxFileSizeIsX: "可上传最大 {x} 的文件。"
allowedTypes: "可上传的文件类型"
tip: "文件尚未上传。在此对话框中,您可以进行上传前确认、重命名、压缩裁剪等操作。准备就绪后,点击上传”按钮即可开始上传。"
tip: "文件还没有被上传。在此对话框中进行上传前确认、重命名、压缩裁剪等操作。准备完成后,点击上传即可开始上传。"
_clientPerformanceIssueTip:
title: "如果觉得电池耗电过高"
makeSureDisabledAdBlocker: "请关闭广告拦截器"

View File

@@ -1,12 +1,12 @@
{
"name": "misskey",
"version": "2026.5.1",
"version": "2026.4.0-beta.0",
"codename": "nasubi",
"repository": {
"type": "git",
"url": "https://github.com/misskey-dev/misskey.git"
},
"packageManager": "pnpm@10.33.2",
"packageManager": "pnpm@10.33.0",
"workspaces": [
"packages/misskey-js",
"packages/i18n",
@@ -53,29 +53,29 @@
"cleanall": "pnpm clean-all"
},
"dependencies": {
"cssnano": "7.1.7",
"cssnano": "7.1.5",
"esbuild": "0.28.0",
"execa": "9.6.1",
"ignore-walk": "8.0.0",
"js-yaml": "4.1.1",
"postcss": "8.5.10",
"postcss": "8.5.9",
"tar": "7.5.13",
"terser": "5.46.2"
"terser": "5.46.1"
},
"devDependencies": {
"@eslint/js": "9.39.4",
"@misskey-dev/eslint-plugin": "2.1.0",
"@types/js-yaml": "4.0.9",
"@types/node": "24.12.2",
"@typescript-eslint/eslint-plugin": "8.59.0",
"@typescript-eslint/parser": "8.59.0",
"@typescript/native-preview": "7.0.0-dev.20260426.1",
"@typescript-eslint/eslint-plugin": "8.58.2",
"@typescript-eslint/parser": "8.58.2",
"@typescript/native-preview": "7.0.0-dev.20260421.2",
"cross-env": "10.1.0",
"cypress": "15.14.1",
"cypress": "15.13.1",
"eslint": "9.39.4",
"globals": "17.5.0",
"ncp": "2.0.0",
"pnpm": "10.33.2",
"pnpm": "10.33.0",
"start-server-and-test": "3.0.2",
"typescript": "5.9.3"
},

View File

@@ -53,8 +53,8 @@
"utf-8-validate": "6.0.6"
},
"dependencies": {
"@aws-sdk/client-s3": "3.1037.0",
"@aws-sdk/lib-storage": "3.1037.0",
"@aws-sdk/client-s3": "3.1030.0",
"@aws-sdk/lib-storage": "3.1030.0",
"@discordapp/twemoji": "16.0.1",
"@fastify/accepts": "5.0.4",
"@fastify/cors": "11.2.0",
@@ -64,27 +64,27 @@
"@fastify/static": "9.1.3",
"@kitajs/html": "4.2.13",
"@misskey-dev/sharp-read-bmp": "1.2.0",
"@misskey-dev/summaly": "5.3.0",
"@napi-rs/canvas": "0.1.100",
"@misskey-dev/summaly": "5.2.5",
"@napi-rs/canvas": "0.1.97",
"@nestjs/common": "11.1.19",
"@nestjs/core": "11.1.19",
"@nestjs/testing": "11.1.19",
"@oxc-project/runtime": "0.127.0",
"@oxc-project/runtime": "0.125.0",
"@peertube/http-signature": "1.7.0",
"@sentry/node": "10.50.0",
"@sentry/profiling-node": "10.50.0",
"@sentry/node": "10.48.0",
"@sentry/profiling-node": "10.48.0",
"@simplewebauthn/server": "13.3.0",
"@sinonjs/fake-timers": "15.3.2",
"@smithy/node-http-handler": "4.6.1",
"@smithy/node-http-handler": "4.5.2",
"@twemoji/parser": "16.0.0",
"accepts": "1.3.8",
"ajv": "8.20.0",
"ajv": "8.18.0",
"archiver": "7.0.1",
"async-mutex": "0.5.0",
"bcryptjs": "3.0.3",
"blurhash": "2.0.5",
"body-parser": "2.2.2",
"bullmq": "5.76.2",
"bullmq": "5.73.5",
"cacheable-lookup": "7.0.0",
"chalk": "5.6.2",
"chalk-template": "1.1.2",
@@ -92,14 +92,14 @@
"color-convert": "3.1.3",
"content-disposition": "1.1.0",
"date-fns": "4.1.0",
"deep-email-validator": "0.1.27",
"deep-email-validator": "0.1.21",
"fastify": "5.8.5",
"fastify-raw-body": "5.0.0",
"feed": "5.2.1",
"feed": "5.2.0",
"file-type": "22.0.1",
"fluent-ffmpeg": "2.1.3",
"form-data": "4.0.5",
"got": "15.0.3",
"got": "14.6.6",
"hpagent": "1.2.0",
"http-link-header": "1.1.3",
"i18n": "workspace:*",
@@ -116,16 +116,16 @@
"misskey-js": "workspace:*",
"misskey-reversi": "workspace:*",
"ms": "3.0.0-canary.202508261828",
"nanoid": "5.1.9",
"nanoid": "5.1.7",
"nested-property": "4.0.0",
"node-fetch": "3.3.2",
"node-html-parser": "7.1.0",
"nodemailer": "8.0.6",
"nodemailer": "8.0.5",
"nsfwjs": "4.3.0",
"oauth2orize": "1.12.0",
"oauth2orize-pkce": "0.1.2",
"os-utils": "0.0.14",
"otpauth": "9.5.1",
"otpauth": "9.5.0",
"pg": "8.20.0",
"pkce-challenge": "6.0.0",
"probe-image-size": "7.2.3",
@@ -160,7 +160,8 @@
"@kitajs/ts-html-plugin": "4.1.4",
"@nestjs/platform-express": "11.1.19",
"@rollup/plugin-esm-shim": "0.1.8",
"@sentry/vue": "10.50.0",
"@sentry/vue": "10.48.0",
"@simplewebauthn/types": "12.0.0",
"@types/accepts": "1.3.7",
"@types/archiver": "7.0.0",
"@types/body-parser": "1.19.6",
@@ -191,9 +192,9 @@
"@types/vary": "1.1.3",
"@types/web-push": "3.6.4",
"@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.59.0",
"@typescript-eslint/parser": "8.59.0",
"@vitest/coverage-v8": "4.1.5",
"@typescript-eslint/eslint-plugin": "8.58.2",
"@typescript-eslint/parser": "8.58.2",
"@vitest/coverage-v8": "4.1.4",
"aws-sdk-client-mock": "4.1.0",
"cbor": "10.0.12",
"cross-env": "10.1.0",
@@ -205,8 +206,8 @@
"rolldown": "1.0.0-rc.15",
"simple-oauth2": "5.1.0",
"supertest": "7.2.2",
"vite": "8.0.10",
"vitest": "4.1.5",
"vite": "8.0.8",
"vitest": "4.1.4",
"vitest-mock-extended": "4.0.0"
}
}

View File

@@ -63,21 +63,20 @@ type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
class NotificationManager {
private notifier: { id: MiUser['id']; };
private note: MiNote;
private queue: Map<MiLocalUser['id'], {
private queue: {
target: MiLocalUser['id'];
reason: NotificationType;
}>;
}[];
constructor(
private mutingsRepository: MutingsRepository,
private notificationService: NotificationService,
private followingsRepository: FollowingsRepository,
notifier: { id: MiUser['id']; },
note: MiNote,
) {
this.notifier = notifier;
this.note = note;
this.queue = new Map();
this.queue = [];
}
@bindThis
@@ -85,7 +84,7 @@ class NotificationManager {
// 自分自身へは通知しない
if (this.notifier.id === notifiee) return;
const exist = this.queue.get(notifiee);
const exist = this.queue.find(x => x.target === notifiee);
if (exist) {
// 「メンションされているかつ返信されている」場合は、メンションとしての通知ではなく返信としての通知にする
@@ -93,7 +92,7 @@ class NotificationManager {
exist.reason = reason;
}
} else {
this.queue.set(notifiee, {
this.queue.push({
reason: reason,
target: notifiee,
});
@@ -102,50 +101,7 @@ class NotificationManager {
@bindThis
public async notify() {
if (this.queue.size === 0) {
return;
}
let visibleUserIds: Set<MiUser['id']> | null;
switch (this.note.visibility) {
case 'public':
case 'home':
visibleUserIds = null;
break;
case 'specified':
visibleUserIds = new Set(this.note.visibleUserIds);
break;
case 'followers': {
// TODO: フォロワー限定ノートにフォロワーではない人がメンションされた場合通知されるのが正しい挙動なのか確認(一部に挙動の不一致がありそう)。現状は通知されるためフィルタしない
// const targetUserIds = this.queue.map(x => x.target);
// const followers = await this.followingsRepository.find({
// where: {
// followeeId: this.note.userId,
// followerId: In(targetUserIds),
// isFollowerHibernated: false,
// },
// select: ['followerId'],
// });
// visibleUserIds = new Set(followers.map(f => f.followerId));
visibleUserIds = null;
break;
}
default:
visibleUserIds = new Set();
break;
}
for (const x of this.queue.values()) {
const isVisibleToTarget = visibleUserIds === null || visibleUserIds.has(x.target);
if (!isVisibleToTarget) {
continue;
}
for (const x of this.queue) {
if (x.reason === 'renote') {
this.notificationService.createNotification(x.target, 'renote', {
noteId: this.note.id,
@@ -816,7 +772,7 @@ export class NoteCreateService implements OnApplicationShutdown {
this.webhookService.enqueueUserWebhook(user.id, 'note', { note: noteObj });
const nm = new NotificationManager(this.mutingsRepository, this.notificationService, this.followingsRepository, user, note);
const nm = new NotificationManager(this.mutingsRepository, this.notificationService, user, note);
await this.createMentionedEvents(mentionedUsers, note, nm);

View File

@@ -246,8 +246,7 @@ export class NotificationService implements OnApplicationShutdown {
private toXListId(id: string): string {
const { date, additional } = this.idService.parseFull(id);
// Redis Stream sequenceはunit64制約があるため、収まらない場合は下位64bitを取る
return date.toString() + '-' + BigInt.asUintN(64, additional).toString();
return date.toString() + '-' + additional.toString();
}
@bindThis

View File

@@ -47,7 +47,6 @@ export type RolePolicies = {
canSearchUsers: boolean;
canUseTranslator: boolean;
canHideAds: boolean;
canCreateChannel: boolean;
driveCapacityMb: number;
maxFileSizeMb: number;
alwaysMarkNsfw: boolean;
@@ -89,7 +88,6 @@ export const DEFAULT_POLICIES: RolePolicies = {
canSearchUsers: true,
canUseTranslator: true,
canHideAds: false,
canCreateChannel: true,
driveCapacityMb: 100,
maxFileSizeMb: 30,
alwaysMarkNsfw: false,
@@ -412,7 +410,6 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
canSearchUsers: calc('canSearchUsers', vs => vs.some(v => v === true)),
canUseTranslator: calc('canUseTranslator', vs => vs.some(v => v === true)),
canHideAds: calc('canHideAds', vs => vs.some(v => v === true)),
canCreateChannel: calc('canCreateChannel', vs => vs.some(v => v === true)),
driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)),
maxFileSizeMb: calc('maxFileSizeMb', vs => Math.max(...vs)),
alwaysMarkNsfw: calc('alwaysMarkNsfw', vs => vs.some(v => v === true)),
@@ -536,8 +533,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
roleId: In(administratorRoles.map(r => r.id)),
}) : [];
// TODO: isRootなアカウントも含める
// Setを経由して重複を除去ユーザIDは重複する可能性があるので
return [...new Set(assigns.map(a => a.userId))].sort((x, y) => x.localeCompare(y));
return assigns.map(a => a.userId);
}
@bindThis

View File

@@ -164,3 +164,4 @@ export class SignupService {
return { account, secret };
}
}

View File

@@ -24,7 +24,7 @@ import type {
PublicKeyCredentialCreationOptionsJSON,
PublicKeyCredentialRequestOptionsJSON,
RegistrationResponseJSON,
} from '@simplewebauthn/server';
} from '@simplewebauthn/types';
@Injectable()
export class WebAuthnService {

View File

@@ -259,7 +259,7 @@ export class ApInboxService {
@bindThis
private async add(actor: MiRemoteUser, activity: IAdd, resolver?: Resolver): Promise<string | void> {
if (actor.uri !== getApId(activity.actor)) {
if (actor.uri !== activity.actor) {
return 'invalid actor';
}
@@ -313,8 +313,7 @@ export class ApInboxService {
// アナウンス先が許可されているかチェック
if (!this.utilityService.isFederationAllowedUri(uri)) return;
const activityUri = getApId(activity);
const unlock = await acquireApObjectLock(this.redisClient, activityUri);
const unlock = await acquireApObjectLock(this.redisClient, uri);
try {
// 既に同じURIを持つものが登録されていないかチェック
@@ -470,7 +469,7 @@ export class ApInboxService {
@bindThis
private async delete(actor: MiRemoteUser, activity: IDelete): Promise<string> {
if (actor.uri !== getApId(activity.actor)) {
if (actor.uri !== activity.actor) {
return 'invalid actor';
}
@@ -624,7 +623,7 @@ export class ApInboxService {
@bindThis
private async remove(actor: MiRemoteUser, activity: IRemove, resolver?: Resolver): Promise<string | void> {
if (actor.uri !== getApId(activity.actor)) {
if (actor.uri !== activity.actor) {
return 'invalid actor';
}
@@ -644,7 +643,7 @@ export class ApInboxService {
@bindThis
private async undo(actor: MiRemoteUser, activity: IUndo, resolver?: Resolver): Promise<string> {
if (actor.uri !== getApId(activity.actor)) {
if (actor.uri !== activity.actor) {
return 'invalid actor';
}
@@ -778,7 +777,7 @@ export class ApInboxService {
@bindThis
private async update(actor: MiRemoteUser, activity: IUpdate, resolver?: Resolver): Promise<string> {
if (actor.uri !== getApId(activity.actor)) {
if (actor.uri !== activity.actor) {
return 'skip: invalid actor';
}

View File

@@ -132,7 +132,7 @@ export class MetaEntityService {
sentryForFrontend: this.config.sentryForFrontend ?? null,
mediaProxy: this.config.mediaProxy,
enableUrlPreview: instance.urlPreviewEnabled,
noteSearchableScope: (this.config.fulltextSearch?.provider === 'meilisearch' && this.config.meilisearch?.scope === 'local') ? 'local' : 'global',
noteSearchableScope: (this.config.meilisearch == null || this.config.meilisearch.scope !== 'local') ? 'global' : 'local',
maxFileSize: this.config.maxFileSize,
federation: this.meta.federation,
};

View File

@@ -224,10 +224,6 @@ export const packedRolePoliciesSchema = {
type: 'boolean',
optional: false, nullable: false,
},
canCreateChannel: {
type: 'boolean',
optional: false, nullable: false,
},
driveCapacityMb: {
type: 'integer',
optional: false, nullable: false,

View File

@@ -96,7 +96,7 @@ export class InboxProcessorService implements OnApplicationShutdown {
if (userExistenceCheckApId != null) {
const user = await this.apDbResolverService.getUserFromApId(userExistenceCheckApId);
if (user == null) {
return `skip: user not found for delete activity. ${getApId(userExistenceCheckApId)}`;
throw new Bull.UnrecoverableError(`skip: user not found for delete activity. ${getApId(userExistenceCheckApId)}`);
}
}
}
@@ -115,9 +115,9 @@ export class InboxProcessorService implements OnApplicationShutdown {
// 対象が4xxならスキップ
if (err instanceof StatusError) {
if (!err.isRetryable) {
throw new Bull.UnrecoverableError(`skip: Ignored deleted actors on both ends ${getApId(activity.actor)} - ${err.statusCode}`);
throw new Bull.UnrecoverableError(`skip: Ignored deleted actors on both ends ${activity.actor} - ${err.statusCode}`);
}
throw new Error(`Error in actor ${getApId(activity.actor)} - ${err.statusCode}`);
throw new Error(`Error in actor ${activity.actor} - ${err.statusCode}`);
}
}
}
@@ -136,7 +136,7 @@ export class InboxProcessorService implements OnApplicationShutdown {
const httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem);
// また、signatureのsignerは、activity.actorと一致する必要がある
if (!httpSignatureValidated || authUser.user.uri !== getApId(activity.actor)) {
if (!httpSignatureValidated || authUser.user.uri !== activity.actor) {
// 一致しなくても、でもLD-Signatureがありそうならそっちも見る
const ldSignature = activity.signature;
if (ldSignature) {
@@ -187,8 +187,8 @@ export class InboxProcessorService implements OnApplicationShutdown {
//#endregion
// もう一度actorチェック
if (authUser.user.uri !== getApId(activity.actor)) {
throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${getApId(activity.actor)})`);
if (authUser.user.uri !== activity.actor) {
throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${activity.actor})`);
}
const ldHost = this.utilityService.extractDbHost(authUser.user.uri);
@@ -243,17 +243,17 @@ export class InboxProcessorService implements OnApplicationShutdown {
}
} catch (e) {
if (e instanceof IdentifiableError) {
switch (e.id) {
case '689ee33f-f97c-479a-ac49-1b9f8140af99':
return 'blocked notes with prohibited words';
case '85ab9bd7-3a41-4530-959d-f07073900109':
return 'actor has been suspended';
case 'd450b8a9-48e4-4dab-ae36-f4db763fda7c': // invalid Note
return e.message;
case '9f466dab-c856-48cd-9e65-ff90ff750580':
return 'note contains too many mentions';
case '09d79f9e-64f1-4316-9cfa-e75c4d091574': // Instance is blocked
return 'skip: blocked instance';
if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') {
return 'blocked notes with prohibited words';
}
if (e.id === '85ab9bd7-3a41-4530-959d-f07073900109') {
return 'actor has been suspended';
}
if (e.id === 'd450b8a9-48e4-4dab-ae36-f4db763fda7c') { // invalid Note
return e.message;
}
if (e.id === '9f466dab-c856-48cd-9e65-ff90ff750580') {
return 'note contains too many mentions';
}
}
throw e;

View File

@@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import cors from '@fastify/cors';
import multipart from '@fastify/multipart';
import { ModuleRef } from '@nestjs/core';
import type { AuthenticationResponseJSON } from '@simplewebauthn/server';
import { AuthenticationResponseJSON } from '@simplewebauthn/types';
import type { Config } from '@/config.js';
import type { InstancesRepository, AccessTokensRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';

View File

@@ -28,7 +28,7 @@ import { LoggerService } from '@/core/LoggerService.js';
import { FastifyReplyError } from '@/misc/fastify-reply-error.js';
import { RateLimiterService } from './RateLimiterService.js';
import { SigninService } from './SigninService.js';
import type { AuthenticationResponseJSON } from '@simplewebauthn/server';
import type { AuthenticationResponseJSON } from '@simplewebauthn/types';
import type { FastifyReply, FastifyRequest } from 'fastify';
@Injectable()

View File

@@ -23,7 +23,7 @@ import { LoggerService } from '@/core/LoggerService.js';
import type { IdentifiableError } from '@/misc/identifiable-error.js';
import { RateLimiterService } from './RateLimiterService.js';
import { SigninService } from './SigninService.js';
import type { AuthenticationResponseJSON } from '@simplewebauthn/server';
import type { AuthenticationResponseJSON } from '@simplewebauthn/types';
import type { FastifyReply, FastifyRequest } from 'fastify';
@Injectable()

View File

@@ -22,8 +22,6 @@ export const meta = {
kind: 'write:channels',
requiredRolePolicy: 'canCreateChannel',
limit: {
duration: ms('1hour'),
max: 10,

View File

@@ -39,6 +39,137 @@ export const meta = {
res: {
type: 'object',
nullable: false,
optional: false,
properties: {
rp: {
type: 'object',
properties: {
id: {
type: 'string',
optional: true,
},
},
},
user: {
type: 'object',
properties: {
id: {
type: 'string',
},
name: {
type: 'string',
},
displayName: {
type: 'string',
},
},
},
challenge: {
type: 'string',
},
pubKeyCredParams: {
type: 'array',
items: {
type: 'object',
properties: {
type: {
type: 'string',
},
alg: {
type: 'number',
},
},
},
},
timeout: {
type: 'number',
nullable: true,
},
excludeCredentials: {
type: 'array',
nullable: true,
items: {
type: 'object',
properties: {
id: {
type: 'string',
},
type: {
type: 'string',
},
transports: {
type: 'array',
items: {
type: 'string',
enum: [
'ble',
'cable',
'hybrid',
'internal',
'nfc',
'smart-card',
'usb',
],
},
},
},
},
},
authenticatorSelection: {
type: 'object',
nullable: true,
properties: {
authenticatorAttachment: {
type: 'string',
enum: [
'cross-platform',
'platform',
],
},
requireResidentKey: {
type: 'boolean',
},
userVerification: {
type: 'string',
enum: [
'discouraged',
'preferred',
'required',
],
},
},
},
attestation: {
type: 'string',
nullable: true,
enum: [
'direct',
'enterprise',
'indirect',
'none',
null,
],
},
extensions: {
type: 'object',
nullable: true,
properties: {
appid: {
type: 'string',
nullable: true,
},
credProps: {
type: 'boolean',
nullable: true,
},
hmacCreateSecret: {
type: 'boolean',
nullable: true,
},
},
},
},
},
} as const;

View File

@@ -4,7 +4,7 @@
*/
import { Inject, Injectable } from '@nestjs/common';
import type { SummalyResult } from '@misskey-dev/summaly';
import type { SummalyResult } from '@misskey-dev/summaly/built/summary.js';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';

View File

@@ -1,6 +1,6 @@
services:
nginx:
image: nginx:1.30
image: nginx:1.29
volumes:
- type: bind
source: ./certificates/rootCA.crt

View File

@@ -18,7 +18,7 @@ import type {
PublicKeyCredentialCreationOptionsJSON,
PublicKeyCredentialRequestOptionsJSON,
RegistrationResponseJSON,
} from '@simplewebauthn/server';
} from '@simplewebauthn/types';
import type * as misskey from 'misskey-js';
import { describe, beforeAll, test } from 'vitest';

View File

@@ -696,19 +696,6 @@ describe('RoleService', () => {
expect(adminIds).toHaveLength(0);
});
test('should not include duplicate user IDs if a user has multiple administrator roles', async () => {
const adminUser = await createUser();
const adminRole1 = await createRole({ name: 'admin1', isAdministrator: true });
const adminRole2 = await createRole({ name: 'admin2', isAdministrator: true });
await roleService.assign(adminUser.id, adminRole1.id);
await roleService.assign(adminUser.id, adminRole2.id);
const adminIds = await roleService.getAdministratorIds();
expect(adminIds).toEqual([adminUser.id]);
});
// TODO: rootユーザーは現在実装に含まれていないため、テストもそれに倣う
test('should not include the root user', async () => {
const rootUser = await createUser();

View File

@@ -8,7 +8,7 @@ import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vites
import { mockDeep } from 'vitest-mock-extended';
import { Test, TestingModule } from '@nestjs/testing';
import { FastifyReply, FastifyRequest } from 'fastify';
import type { AuthenticationResponseJSON } from '@simplewebauthn/server';
import { AuthenticationResponseJSON } from '@simplewebauthn/types';
import { HttpHeader } from 'fastify/types/utils.js';
import { MiUser } from '@/models/User.js';
import { MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js';

View File

@@ -38,12 +38,4 @@ describe('misc:ulid', () => {
// id[16] = Z
expect(() => parseUlidFull('01KPS7S300ABCDEFZ000000000')).not.toThrow();
});
test('parseUlidFull - additional exceeds uint64 max (all-Z randomness)', () => {
// All 16 random chars = 'Z' (Crockford max) → 80-bit value > uint64 max
const { additional } = parseUlidFull('01ARZ3NDEKZZZZZZZZZZZZZZZZ');
const uint64Max = 2n ** 64n - 1n;
expect(additional > uint64Max).toBe(true);
expect(BigInt.asUintN(64, additional) <= uint64Max).toBe(true);
});
});

View File

@@ -12,15 +12,15 @@
"devDependencies": {
"@types/estree": "1.0.8",
"@types/node": "24.12.2",
"@typescript-eslint/eslint-plugin": "8.59.0",
"@typescript-eslint/parser": "8.59.0",
"rollup": "4.60.2"
"@typescript-eslint/eslint-plugin": "8.58.2",
"@typescript-eslint/parser": "8.58.2",
"rollup": "4.60.1"
},
"dependencies": {
"estree-walker": "3.0.3",
"i18n": "workspace:*",
"magic-string": "0.30.21",
"rolldown": "1.0.0-rc.15",
"vite": "8.0.10"
"vite": "8.0.8"
}
}

View File

@@ -24,14 +24,14 @@
"mfm-js": "0.25.0",
"misskey-js": "workspace:*",
"punycode.js": "2.3.1",
"rollup": "4.60.2",
"rollup": "4.60.1",
"shiki": "4.0.2",
"tinycolor2": "1.6.0",
"uuid": "14.0.0",
"vue": "3.5.33"
"uuid": "13.0.0",
"vue": "3.5.32"
},
"devDependencies": {
"@misskey-dev/summaly": "5.3.0",
"@misskey-dev/summaly": "5.2.5",
"@tabler/icons-webfont": "3.35.0",
"@testing-library/vue": "8.1.0",
"@types/estree": "1.0.8",
@@ -40,26 +40,26 @@
"@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.59.0",
"@typescript-eslint/parser": "8.59.0",
"@vitest/coverage-v8": "4.1.5",
"@vue/runtime-core": "3.5.33",
"@typescript-eslint/eslint-plugin": "8.58.2",
"@typescript-eslint/parser": "8.58.2",
"@vitest/coverage-v8": "4.1.4",
"@vue/runtime-core": "3.5.32",
"acorn": "8.16.0",
"cross-env": "10.1.0",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-vue": "10.9.0",
"eslint-plugin-vue": "10.8.0",
"happy-dom": "20.9.0",
"intersection-observer": "0.12.2",
"micromatch": "4.0.8",
"msw": "2.13.6",
"msw": "2.13.3",
"prettier": "3.8.3",
"sass-embedded": "1.99.0",
"start-server-and-test": "3.0.2",
"tsx": "4.21.0",
"vite": "8.0.10",
"vite": "8.0.8",
"vite-plugin-turbosnap": "1.0.3",
"vue-component-type-helpers": "3.2.7",
"vue-component-type-helpers": "3.2.6",
"vue-eslint-parser": "10.4.0",
"vue-tsc": "3.2.7"
"vue-tsc": "3.2.6"
}
}

View File

@@ -28,7 +28,7 @@ import { postMessageToParentWindow, setIframeId } from '@/post-message.js';
import { serverContext } from '@/server-context.js';
import { i18n } from '@/i18n.js';
import type { Theme } from '@@/js/theme.js';
import type { Theme } from '@/theme.js';
console.log('Misskey Embed');

View File

@@ -5,10 +5,26 @@
// TODO: (可能な部分を)sharedに抽出して frontend と共通化
import tinycolor from 'tinycolor2';
import lightTheme from '@@/themes/_light.json5';
import darkTheme from '@@/themes/_dark.json5';
import { compile } from '@@/js/theme.js';
import type { Theme } from '@@/js/theme.js';
import type { BundledTheme } from 'shiki/themes';
export type Theme = {
id: string;
name: string;
author: string;
desc?: string;
base?: 'dark' | 'light';
props: Record<string, string>;
codeHighlighter?: {
base: BundledTheme;
overrides?: Record<string, any>;
} | {
base: '_none_';
overrides: Record<string, any>;
};
};
let timeout: number | null = null;
@@ -16,7 +32,7 @@ export function assertIsTheme(theme: Record<string, unknown>): theme is Theme {
return typeof theme === 'object' && theme !== null && 'id' in theme && 'name' in theme && 'author' in theme && 'props' in theme;
}
export function applyTheme(theme: Theme) {
export function applyTheme(theme: Theme, persist = true) {
if (timeout) window.clearTimeout(timeout);
window.document.documentElement.classList.add('_themeChanging_');
@@ -52,3 +68,48 @@ export function applyTheme(theme: Theme) {
// iframeを正常に透過させるために、cssのcolor-schemeは `light dark;` 固定にしてある。style.scss参照
}
function compile(theme: Theme): Record<string, string> {
function getColor(val: string): tinycolor.Instance {
if (val[0] === '@') { // ref (prop)
return getColor(theme.props[val.substring(1)]);
} else if (val[0] === '$') { // ref (const)
return getColor(theme.props[val]);
} else if (val[0] === ':') { // func
const parts = val.split('<');
const funcTxt = parts.shift();
const argTxt = parts.shift();
if (funcTxt && argTxt) {
const func = funcTxt.substring(1);
const arg = parseFloat(argTxt);
const color = getColor(parts.join('<'));
switch (func) {
case 'darken': return color.darken(arg);
case 'lighten': return color.lighten(arg);
case 'alpha': return color.setAlpha(arg);
case 'hue': return color.spin(arg);
case 'saturate': return color.saturate(arg);
}
}
}
// other case
return tinycolor(val);
}
const props = {};
for (const [k, v] of Object.entries(theme.props)) {
if (k.startsWith('$')) continue; // ignore const
props[k] = v.startsWith('"') ? v.replace(/^"\s*/, '') : genValue(getColor(v));
}
return props;
}
function genValue(c: tinycolor.Instance): string {
return c.toRgbString();
}

View File

@@ -1,13 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
declare module '@@/themes/*.json5' {
import { Theme } from '@@/js/theme.js';
const theme: Theme;
// eslint-disable-next-line import/no-default-export
export default theme;
}

View File

@@ -0,0 +1,109 @@
import fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import * as esbuild from 'esbuild';
import { build } from 'esbuild';
import { execa } from 'execa';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const _package = JSON.parse(fs.readFileSync(_dirname + '/package.json', 'utf-8'));
const entryPoints = fs.globSync('./js/**/**.{ts,tsx}');
/** @type {import('esbuild').BuildOptions} */
const options = {
entryPoints,
minify: process.env.NODE_ENV === 'production',
outdir: './js-built',
target: 'es2022',
platform: 'browser',
format: 'esm',
sourcemap: 'linked',
};
const args = process.argv.slice(2).map(arg => arg.toLowerCase());
// js-built配下をすべて削除する
if (!args.includes('--no-clean')) {
fs.rmSync('./js-built', { recursive: true, force: true });
}
if (args.includes('--watch')) {
await watchSrc();
} else {
await buildSrc();
}
async function buildSrc() {
console.log(`[${_package.name}] start building...`);
await build(options)
.then(() => {
console.log(`[${_package.name}] build succeeded.`);
})
.catch((err) => {
process.stderr.write(err.stderr);
process.exit(1);
});
if (process.env.NODE_ENV === 'production') {
console.log(`[${_package.name}] skip building d.ts because NODE_ENV is production.`);
} else {
await buildDts();
}
fs.copyFileSync('./js/emojilist.json', './js-built/emojilist.json');
console.log(`[${_package.name}] finish building.`);
}
function buildDts() {
return execa(
'tsgo',
[
'--project', 'tsconfig.json',
'--outDir', 'js-built',
'--declaration', 'true',
'--emitDeclarationOnly', 'true',
],
{
stdout: process.stdout,
stderr: process.stderr,
},
);
}
async function watchSrc() {
const plugins = [{
name: 'gen-dts',
setup(build) {
build.onStart(() => {
console.log(`[${_package.name}] detect changed...`);
});
build.onEnd(async result => {
if (result.errors.length > 0) {
console.error(`[${_package.name}] watch build failed:`, result);
return;
}
await buildDts();
});
},
}];
console.log(`[${_package.name}] start watching...`);
const context = await esbuild.context({ ...options, plugins });
await context.watch();
await new Promise((resolve, reject) => {
process.on('SIGHUP', resolve);
process.on('SIGINT', resolve);
process.on('SIGTERM', resolve);
process.on('uncaughtException', reject);
process.on('exit', resolve);
}).finally(async () => {
await context.dispose();
console.log(`[${_package.name}] finish watching.`);
});
}

View File

@@ -1,126 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import tinycolor from 'tinycolor2';
import JSON5 from 'json5';
import lightTheme from '@@/themes/_light.json5';
import type { BundledTheme } from 'shiki/themes';
export type Theme = {
id: string;
name: string;
author: string;
desc?: string;
base?: 'dark' | 'light';
kind?: 'dark' | 'light'; // legacy
props: Record<string, string>;
codeHighlighter?: {
base: BundledTheme;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
overrides?: Record<string, any>;
} | {
base: '_none_';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
overrides: Record<string, any>;
};
};
export type CompiledTheme = Record<string, string>;
export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X'));
export const getBuiltinThemes = () => Promise.all(
[
'l-light',
'l-coffee',
'l-apricot',
'l-rainy',
'l-botanical',
'l-vivid',
'l-cherry',
'l-sushi',
'l-u0',
'd-dark',
'd-persimmon',
'd-astro',
'd-future',
'd-botanical',
'd-green-lime',
'd-green-orange',
'd-cherry',
'd-ice',
'd-u0',
].map(name => import(`@@/themes/${name}.json5`).then(({ default: _default }): Theme => _default)),
);
export function compile(theme: Theme): CompiledTheme {
function getColor(val: string): tinycolor.Instance {
if (val[0] === '@') { // ref (prop)
return getColor(theme.props[val.substring(1)]);
} else if (val[0] === '$') { // ref (const)
return getColor(theme.props[val]);
} else if (val[0] === ':') { // func
const parts = val.split('<');
const funcTxt = parts.shift();
const argTxt = parts.shift();
if (funcTxt && argTxt) {
const func = funcTxt.substring(1);
const arg = parseFloat(argTxt);
const color = getColor(parts.join('<'));
switch (func) {
case 'darken': return color.darken(arg);
case 'lighten': return color.lighten(arg);
case 'alpha': return color.setAlpha(arg);
case 'hue': return color.spin(arg);
case 'saturate': return color.saturate(arg);
}
}
}
// other case
return tinycolor(val);
}
const props = {} as CompiledTheme;
for (const [k, v] of Object.entries(theme.props)) {
if (k.startsWith('$')) continue; // ignore const
props[k] = v.startsWith('"') ? v.replace(/^"\s*/, '') : genValue(getColor(v));
}
return props;
}
function genValue(c: tinycolor.Instance): string {
return c.toRgbString();
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function validateTheme(theme: Record<string, any>): boolean {
if (theme.id == null || typeof theme.id !== 'string') return false;
if (theme.name == null || typeof theme.name !== 'string') return false;
if (theme.base == null || !['light', 'dark'].includes(theme.base)) return false;
if (theme.props == null || typeof theme.props !== 'object') return false;
return true;
}
export function parseThemeCode(code: string): Theme {
let theme;
try {
theme = JSON5.parse(code);
} catch (_) {
throw new Error('Failed to parse theme json');
}
if (!validateTheme(theme)) {
throw new Error('This theme is invaild');
}
return theme;
}

View File

@@ -1,18 +1,32 @@
{
"name": "frontend-shared",
"type": "module",
"private": true,
"main": "./js-built/index.js",
"types": "./js-built/index.d.ts",
"exports": {
".": {
"import": "./js-built/index.js",
"types": "./js-built/index.d.ts"
},
"./*": {
"import": "./js-built/*",
"types": "./js-built/*"
}
},
"scripts": {
"build": "node ./build.js",
"watch": "nodemon -w package.json -e json --exec \"node ./build.js --watch\"",
"eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
"typecheck": "tsgo --noEmit",
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
"@types/node": "24.12.2",
"@types/tinycolor2": "1.4.6",
"@typescript-eslint/eslint-plugin": "8.59.0",
"@typescript-eslint/parser": "8.59.0",
"eslint-plugin-vue": "10.9.0",
"@typescript-eslint/eslint-plugin": "8.58.2",
"@typescript-eslint/parser": "8.58.2",
"esbuild": "0.28.0",
"eslint-plugin-vue": "10.8.0",
"nodemon": "3.1.14",
"vue-eslint-parser": "10.4.0"
},
"files": [
@@ -20,10 +34,7 @@
],
"dependencies": {
"i18n": "workspace:*",
"json5": "2.2.3",
"misskey-js": "workspace:*",
"shiki": "4.0.2",
"tinycolor2": "1.6.0",
"vue": "3.5.33"
"vue": "3.5.32"
}
}

View File

@@ -20,13 +20,13 @@ let moduleInitialized = false;
let unobserve = () => {};
let misskeyOS = null;
function loadTheme(themeMaganer: typeof import('../src/theme')['themeManager']) {
function loadTheme(applyTheme: typeof import('../src/theme')['applyTheme']) {
unobserve();
const theme = themes[window.document.documentElement.dataset.misskeyTheme];
if (theme) {
themeMaganer.updateTheme(themes[window.document.documentElement.dataset.misskeyTheme]);
applyTheme(themes[window.document.documentElement.dataset.misskeyTheme]);
} else {
themeMaganer.updateTheme(themes['l-light']);
applyTheme(themes['l-light']);
}
const observer = new MutationObserver((entries) => {
for (const entry of entries) {
@@ -34,7 +34,7 @@ function loadTheme(themeMaganer: typeof import('../src/theme')['themeManager'])
const target = entry.target as HTMLElement;
const theme = themes[target.dataset.misskeyTheme];
if (theme) {
themeMaganer.updateTheme(themes[target.dataset.misskeyTheme]);
applyTheme(themes[target.dataset.misskeyTheme]);
} else {
target.removeAttribute('style');
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 879 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 626 KiB

View File

@@ -1 +0,0 @@
これらのサムネイルはdev buildでRoomのカタログダイアログを表示し家具を選択した状態でブラウザのコンソールで`takeScreenshot();`を叩くと生成・ダウンロードできます

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Some files were not shown because too many files have changed in this diff Show More