Merge branch 'develop' into feat-frontend-expand-profile-links

This commit is contained in:
Souma 2025-09-03 21:23:39 +09:00 committed by GitHub
commit b4acfd0c55
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
310 changed files with 3971 additions and 3340 deletions

View File

@ -16,7 +16,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.3.0
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.1.0 uses: pnpm/action-setup@v4.1.0

View File

@ -12,7 +12,7 @@ jobs:
steps: steps:
- name: Checkout head - name: Checkout head
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.3.0
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4.4.0 uses: actions/setup-node@v4.4.0
with: with:

View File

@ -18,7 +18,7 @@ jobs:
if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }} if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }}
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.3.0
with: with:
submodules: true submodules: true
persist-credentials: false persist-credentials: false
@ -66,7 +66,7 @@ jobs:
if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }} if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }}
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.3.0
with: with:
submodules: true submodules: true
persist-credentials: false persist-credentials: false

View File

@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.3.0
- name: Check version - name: Check version
run: | run: |
if [ "$(jq -r '.version' package.json)" != "$(jq -r '.version' packages/misskey-js/package.json)" ]; then if [ "$(jq -r '.version' package.json)" != "$(jq -r '.version' packages/misskey-js/package.json)" ]; then

View File

@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.3.0
- name: Check - name: Check
run: | run: |
counter=0 counter=0

View File

@ -10,7 +10,7 @@ jobs:
check_copyright_year: check_copyright_year:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
- run: | - run: |
if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then
echo "Please change copyright year!" echo "Please change copyright year!"

View File

@ -28,7 +28,7 @@ jobs:
wait_time: ${{ steps.get-wait-time.outputs.wait_time }} wait_time: ${{ steps.get-wait-time.outputs.wait_time }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.3.0
- name: Check allowed users - name: Check allowed users
id: check-allowed-users id: check-allowed-users

View File

@ -27,7 +27,7 @@ jobs:
platform=${{ matrix.platform }} platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Check out the repo - name: Check out the repo
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.3.0
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub - name: Log in to Docker Hub

View File

@ -32,7 +32,7 @@ jobs:
platform=${{ matrix.platform }} platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Check out the repo - name: Check out the repo
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.3.0
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: Docker meta - name: Docker meta

View File

@ -15,7 +15,7 @@ jobs:
DOCKER_CONTENT_TRUST: 1 DOCKER_CONTENT_TRUST: 1
DOCKLE_VERSION: 0.4.14 DOCKLE_VERSION: 0.4.14
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
- name: Download and install dockle v${{ env.DOCKLE_VERSION }} - name: Download and install dockle v${{ env.DOCKLE_VERSION }}
run: | run: |
curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v${DOCKLE_VERSION}/dockle_${DOCKLE_VERSION}_Linux-64bit.deb" curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v${DOCKLE_VERSION}/dockle_${DOCKLE_VERSION}_Linux-64bit.deb"

View File

@ -25,7 +25,7 @@ jobs:
ref: refs/pull/${{ github.event.number }}/merge ref: refs/pull/${{ github.event.number }}/merge
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
with: with:
ref: ${{ matrix.ref }} ref: ${{ matrix.ref }}
submodules: true submodules: true

View File

@ -36,7 +36,7 @@ jobs:
pnpm_install: pnpm_install:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
@ -69,7 +69,7 @@ jobs:
eslint-cache-version: v1 eslint-cache-version: v1
eslint-cache-path: ${{ github.workspace }}/node_modules/.cache/eslint-${{ matrix.workspace }} eslint-cache-path: ${{ github.workspace }}/node_modules/.cache/eslint-${{ matrix.workspace }}
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
@ -81,7 +81,7 @@ jobs:
cache: 'pnpm' cache: 'pnpm'
- run: pnpm i --frozen-lockfile - run: pnpm i --frozen-lockfile
- name: Restore eslint cache - name: Restore eslint cache
uses: actions/cache@v4.2.3 uses: actions/cache@v4.2.4
with: with:
path: ${{ env.eslint-cache-path }} path: ${{ env.eslint-cache-path }}
key: eslint-${{ env.eslint-cache-version }}-${{ matrix.workspace }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.ref_name }}-${{ github.sha }} key: eslint-${{ env.eslint-cache-version }}-${{ matrix.workspace }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.ref_name }}-${{ github.sha }}
@ -99,7 +99,7 @@ jobs:
- sw - sw
- misskey-js - misskey-js
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true

View File

@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
continue-on-error: true continue-on-error: true
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true

View File

@ -16,7 +16,7 @@ jobs:
id-token: write id-token: write
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
with: with:
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm

View File

@ -22,12 +22,12 @@ jobs:
NODE_OPTIONS: "--max_old_space_size=7168" NODE_OPTIONS: "--max_old_space_size=7168"
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
if: github.event_name != 'pull_request_target' if: github.event_name != 'pull_request_target'
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
if: github.event_name == 'pull_request_target' if: github.event_name == 'pull_request_target'
with: with:
fetch-depth: 0 fetch-depth: 0

View File

@ -50,7 +50,7 @@ jobs:
- 56312:6379 - 56312:6379
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
with: with:
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
@ -129,7 +129,7 @@ jobs:
- 56312:6379 - 56312:6379
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
with: with:
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
@ -173,7 +173,7 @@ jobs:
POSTGRES_HOST_AUTH_METHOD: trust POSTGRES_HOST_AUTH_METHOD: trust
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
with: with:
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm

View File

@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
with: with:
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm
@ -76,7 +76,7 @@ jobs:
- 56312:6379 - 56312:6379
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
with: with:
submodules: true submodules: true
# https://github.com/cypress-io/cypress-docker-images/issues/150 # https://github.com/cypress-io/cypress-docker-images/issues/150

View File

@ -22,7 +22,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.3.0
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v4.1.0 uses: pnpm/action-setup@v4.1.0

View File

@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
with: with:
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm

View File

@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4.2.2 - uses: actions/checkout@v4.3.0
with: with:
submodules: true submodules: true
- name: Setup pnpm - name: Setup pnpm

View File

@ -1,3 +1,18 @@
## Unreleased
### General
-
### Client
- Enhance: プロフィールへのリンクをユーザーポップアップのアバターに追加
- Enhance: ユーザーのノート、フォロー、フォロワーページへのリンクをユーザーポップアップに追加
- Fix: RSSティッカーウィジェットが正しく動作しない問題を修正
- Fix: エラー画像が横に引き伸ばされてしまう問題に対応
### Server
-
## 2025.8.0 ## 2025.8.0
### Note ### Note
@ -6,8 +21,8 @@
### General ### General
- ノートを削除した際、関連するノートが同時に削除されないようになりました - ノートを削除した際、関連するノートが同時に削除されないようになりました
- APIで、「replyIdが存在しているのにreplyがnull」や「renoteIdが存在しているのにrenoteがnull」であるという、今までにはなかったパターンが表れることになります - APIで、「replyIdが存在しているのにreplyがnull」や「renoteIdが存在しているのにrenoteがnull」であるという、今までにはなかったパターンが表れることになります
- 定期的に参照されていない古いリモートの投稿を削除する機能が実装されました(コントロールパネル→パフォーマンス→Remote Notes Cleaning) - 定期的に古いリモートの投稿を削除する機能が実装されました
- 既存のサーバーでは**デフォルトでオフ**、新規サーバーでは**デフォルトでオン**になります - コントロールパネル→パフォーマンス→Remote Notes Cleaning で有効化できます
- データベースの肥大化を防止することが可能です - データベースの肥大化を防止することが可能です
- 既存のサーバーで当機能を有効化した場合は、処理量が多くなるため、一時的にストレージ使用量が増加する可能性があります。 - 既存のサーバーで当機能を有効化した場合は、処理量が多くなるため、一時的にストレージ使用量が増加する可能性があります。
- 増加量を抑えるには、最大処理継続時間をデフォルトより短くしてください。 - 増加量を抑えるには、最大処理継続時間をデフォルトより短くしてください。
@ -15,7 +30,8 @@
- サーバーの初期設定が完了するまでは連合がオンにならないようになりました - サーバーの初期設定が完了するまでは連合がオンにならないようになりました
- 日本語における公開範囲名称の「ダイレクト」が「指名」に改称されました - 日本語における公開範囲名称の「ダイレクト」が「指名」に改称されました
- 実際の動作に即した名称になり、馴染みのない人でも理解しやすくなりました - 実際の動作に即した名称になり、馴染みのない人でも理解しやすくなりました
- 他サービスにおける「ダイレクトメッセージ」に相当するMisskeyの機能は「チャット」ですが、「ダイレクト投稿」という名称の機能が存在するとそちらがダイレクトメッセージ機能であるような誤解を生んでいました - 他サービスにおける「ダイレクトメッセージ」に相当するMisskeyの機能は「チャット」ですが(過去のバージョンのMisskeyでも、当該機能は「チャット」ではなく「ダイレクトメッセージ」でした)、「ダイレクト投稿」という名称の機能が存在するとそちらがダイレクトメッセージ機能であるような誤解を生んでいました
- 今後、「チャット」の名称を「ダイレクトメッセージ」に戻す可能性があります
- mfm.jsをアップデートしました - mfm.jsをアップデートしました
- Enhance: Unicode 15.1 および 16.0 に収録されている絵文字に対応 - Enhance: Unicode 15.1 および 16.0 に収録されている絵文字に対応
- Enhance: acctに `.` が入っているユーザーのメンションに対応 - Enhance: acctに `.` が入っているユーザーのメンションに対応
@ -27,6 +43,7 @@
- プラグインは1.xに対応したものが必要です - プラグインは1.xに対応したものが必要です
- Playはそのまま動作しますが、新規に作られるプリセットは1.xになります - Playはそのまま動作しますが、新規に作られるプリセットは1.xになります
- 以前のバージョンから無効化されていた note_view_interruptor が有効になりました - 以前のバージョンから無効化されていた note_view_interruptor が有効になりました
- ハンドラは同期的である必要があります
- Feat: セーフモード - Feat: セーフモード
- プラグイン・テーマ・カスタムCSSの使用でクライアントの起動に問題が発生した際に、これらを無効にして起動できます - プラグイン・テーマ・カスタムCSSの使用でクライアントの起動に問題が発生した際に、これらを無効にして起動できます
- 以下の方法でセーフモードを起動できます - 以下の方法でセーフモードを起動できます
@ -42,11 +59,11 @@
- Enhance: トルコ語 (tr-TR) に対応 - Enhance: トルコ語 (tr-TR) に対応
- Enhance: 不必要な翻訳データを読み込まなくなり、パフォーマンスが向上しました - Enhance: 不必要な翻訳データを読み込まなくなり、パフォーマンスが向上しました
- Enhance: 画像エフェクトのパラメータ名の多言語対応 - Enhance: 画像エフェクトのパラメータ名の多言語対応
- Enhance: 依存ソフトウェアの更新
- Enhance: ートを非表示にする相対期間を1ヶ月単位で自由に指定できるように - Enhance: ートを非表示にする相対期間を1ヶ月単位で自由に指定できるように
- Enhance: メールアドレス確認画面のUIを改善 - Enhance: メールアドレス確認画面のUIを改善
- Enhance: プロフィールへのリンクをユーザーポップアップのアバターに追加 - Enhance: アイコンのスクロール追従を無効化する際の適用範囲を強化
- Enhance: ユーザーのノート、フォロー、フォロワーページへのリンクをユーザーポップアップに追加 - Enhance: レンダリングパフォーマンスの向上
- Enhance: 依存ソフトウェアの更新
- Fix: 投稿フォームでファイルのアップロードが中止または失敗した際のハンドリングを修正 - Fix: 投稿フォームでファイルのアップロードが中止または失敗した際のハンドリングを修正
- Fix: 一部の設定検索結果が存在しないパスになる問題を修正 - Fix: 一部の設定検索結果が存在しないパスになる問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1171) (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1171)
@ -56,6 +73,11 @@
- Fix: 複数のメンションを1行に記述した場合に、サジェストが正しく表示されない問題を修正 - Fix: 複数のメンションを1行に記述した場合に、サジェストが正しく表示されない問題を修正
- Fix: メンションとしての条件を満たしていても、特定の条件(`-`が含まれる場合など)で正しくサジェストされない問題を一部修正 - Fix: メンションとしての条件を満たしていても、特定の条件(`-`が含まれる場合など)で正しくサジェストされない問題を一部修正
- Fix: ユーザーの前後ノートを閲覧する機能が動作しない問題を修正 - Fix: ユーザーの前後ノートを閲覧する機能が動作しない問題を修正
- Fix: 照会ダイアログでap/showでローカルユーザーを解決した際@username@nullに飛ばされる問題を修正
- Fix: アイコンのデコレーションを付ける際にデコレーションが表示されなくなる問題を修正
- Fix: タッチ操作時にマウスホバー時のユーザープレビューが開くことがある問題を修正
- Fix: 管理中アカウント一覧で正しい表示が行われない問題を修正
- Fix: lookupページでリモートURLを指定した際に正しく動作しない問題を修正
### Server ### Server
- Feat: サーバー管理コマンド - Feat: サーバー管理コマンド
@ -69,6 +91,8 @@
- Fix: `notes/mentions` で場合によっては並び順が正しく返されない問題を修正 - Fix: `notes/mentions` で場合によっては並び順が正しく返されない問題を修正
- Fix: SystemWebhook設定でsecretを空に出来ない問題を修正 - Fix: SystemWebhook設定でsecretを空に出来ない問題を修正
- Fix: 削除されたユーザーがチャットメッセージにリアクションしている場合`chat/history`などでエラーになる問題を修正 - Fix: 削除されたユーザーがチャットメッセージにリアクションしている場合`chat/history`などでエラーになる問題を修正
- Fix: Pageのアイキャッチ画像をドライブから消してもPageごと消えないように
- Fix: タイムラインAPIの withRenotes: false 時のレスポンスを修正
## 2025.7.0 ## 2025.7.0

View File

@ -4,8 +4,8 @@
*/ */
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import { StoryObj } from '@storybook/vue3'; import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw'; import { HttpResponse, http } from 'msw';
import { abuseUserReport } from '../packages/frontend/.storybook/fakes.js'; import { abuseUserReport } from '../packages/frontend/.storybook/fakes.js';
import { commonHandlers } from '../packages/frontend/.storybook/mocks.js'; import { commonHandlers } from '../packages/frontend/.storybook/mocks.js';

View File

@ -1668,7 +1668,7 @@ _serverSettings:
restartServerSetupWizardConfirm_text: "Algunes configuracions actuals seran restablertes." restartServerSetupWizardConfirm_text: "Algunes configuracions actuals seran restablertes."
entrancePageStyle: "Estil de la pàgina d'inici" entrancePageStyle: "Estil de la pàgina d'inici"
showTimelineForVisitor: "Mostrar la línia de temps" showTimelineForVisitor: "Mostrar la línia de temps"
showActivityiesForVisitor: "Mostrar les activitats" showActivitiesForVisitor: "Mostrar activitat"
_userGeneratedContentsVisibilityForVisitor: _userGeneratedContentsVisibilityForVisitor:
all: "Tot obert al públic " all: "Tot obert al públic "
localOnly: "Només es publiquen els continguts locals, el contingut remot es manté privat" localOnly: "Només es publiquen els continguts locals, el contingut remot es manté privat"

View File

@ -1218,8 +1218,8 @@ showRepliesToOthersInTimeline: "Show replies to others in timeline"
hideRepliesToOthersInTimeline: "Hide replies to others from timeline" hideRepliesToOthersInTimeline: "Hide replies to others from timeline"
showRepliesToOthersInTimelineAll: "Show replies to others from everyone you follow in timeline" showRepliesToOthersInTimelineAll: "Show replies to others from everyone you follow in timeline"
hideRepliesToOthersInTimelineAll: "Hide replies to others from everyone you follow in timeline" hideRepliesToOthersInTimelineAll: "Hide replies to others from everyone you follow in timeline"
confirmShowRepliesAll: "This operation is irreversible. Would you really like to show replies to others from everyone you follow in your timeline?" confirmShowRepliesAll: "Are you sure you want to show replies from everyone you follow in your timeline? This action is irreversible."
confirmHideRepliesAll: "This operation is irreversible. Would you really like to hide replies to others from everyone you follow in your timeline?" confirmHideRepliesAll: "Are you sure you want to hide replies from everyone you follow in your timeline? This action is irreversible."
externalServices: "External Services" externalServices: "External Services"
sourceCode: "Source code" sourceCode: "Source code"
sourceCodeIsNotYetProvided: "Source code is not yet available. Contact the administrator to fix this problem." sourceCodeIsNotYetProvided: "Source code is not yet available. Contact the administrator to fix this problem."
@ -1245,6 +1245,7 @@ releaseToRefresh: "Release to refresh"
refreshing: "Refreshing..." refreshing: "Refreshing..."
pullDownToRefresh: "Pull down to refresh" pullDownToRefresh: "Pull down to refresh"
useGroupedNotifications: "Display grouped notifications" useGroupedNotifications: "Display grouped notifications"
emailVerificationFailedError: "A problem occurred while verifying your email address. The link may have expired."
cwNotationRequired: "If \"Hide content\" is enabled, a description must be provided." cwNotationRequired: "If \"Hide content\" is enabled, a description must be provided."
doReaction: "Add reaction" doReaction: "Add reaction"
code: "Code" code: "Code"
@ -1375,6 +1376,7 @@ safeModeEnabled: "Safe mode is enabled"
pluginsAreDisabledBecauseSafeMode: "All plugins are disabled because safe mode is enabled." pluginsAreDisabledBecauseSafeMode: "All plugins are disabled because safe mode is enabled."
customCssIsDisabledBecauseSafeMode: "Custom CSS is not applied because safe mode is enabled." customCssIsDisabledBecauseSafeMode: "Custom CSS is not applied because safe mode is enabled."
themeIsDefaultBecauseSafeMode: "While safe mode is active, the default theme is used. Disabling safe mode will revert these changes." themeIsDefaultBecauseSafeMode: "While safe mode is active, the default theme is used. Disabling safe mode will revert these changes."
thankYouForTestingBeta: "Thank you for helping us test the beta version!"
_order: _order:
newest: "Newest First" newest: "Newest First"
oldest: "Oldest First" oldest: "Oldest First"
@ -1664,6 +1666,9 @@ _serverSettings:
userGeneratedContentsVisibilityForVisitor_description2: "Unconditionally publishing all content on the server to the Internet, including remote content received by the server is risky. This is especially important for guests who are unaware of the distributed nature of the content, as they may mistakenly believe that even remote content is content created by users on the server." userGeneratedContentsVisibilityForVisitor_description2: "Unconditionally publishing all content on the server to the Internet, including remote content received by the server is risky. This is especially important for guests who are unaware of the distributed nature of the content, as they may mistakenly believe that even remote content is content created by users on the server."
restartServerSetupWizardConfirm_title: "Restart server setup wizard?" restartServerSetupWizardConfirm_title: "Restart server setup wizard?"
restartServerSetupWizardConfirm_text: "Some current settings will be reset." restartServerSetupWizardConfirm_text: "Some current settings will be reset."
entrancePageStyle: "Entrance page style"
showTimelineForVisitor: "Show timeline"
showActivitiesForVisitor: "Show activities"
_userGeneratedContentsVisibilityForVisitor: _userGeneratedContentsVisibilityForVisitor:
all: "Everything is public" all: "Everything is public"
localOnly: "Only local content is published, remote content is kept private" localOnly: "Only local content is published, remote content is kept private"

View File

@ -1054,6 +1054,7 @@ permissionDeniedError: "Operación denegada"
permissionDeniedErrorDescription: "Esta cuenta no tiene permisos para hacer esa acción." permissionDeniedErrorDescription: "Esta cuenta no tiene permisos para hacer esa acción."
preset: "Predefinido" preset: "Predefinido"
selectFromPresets: "Escoger desde predefinidos" selectFromPresets: "Escoger desde predefinidos"
custom: "Personalizado"
achievements: "Logros" achievements: "Logros"
gotInvalidResponseError: "Respuesta del servidor inválida" gotInvalidResponseError: "Respuesta del servidor inválida"
gotInvalidResponseErrorDescription: "Puede que el servidor esté caído o en mantenimiento. Favor de intentar más tarde" gotInvalidResponseErrorDescription: "Puede que el servidor esté caído o en mantenimiento. Favor de intentar más tarde"
@ -1244,6 +1245,7 @@ releaseToRefresh: "Soltar para recargar"
refreshing: "Recargando..." refreshing: "Recargando..."
pullDownToRefresh: "Tira hacia abajo para recargar" pullDownToRefresh: "Tira hacia abajo para recargar"
useGroupedNotifications: "Mostrar notificaciones agrupadas" useGroupedNotifications: "Mostrar notificaciones agrupadas"
emailVerificationFailedError: "Se ha producido un error al confirmar tu dirección de correo electrónico. Es posible que el enlace haya caducado."
cwNotationRequired: "Si se ha activado \"ocultar contenido\", es necesario proporcionar una descripción." cwNotationRequired: "Si se ha activado \"ocultar contenido\", es necesario proporcionar una descripción."
doReaction: "Añadir reacción" doReaction: "Añadir reacción"
code: "Código" code: "Código"
@ -1374,6 +1376,7 @@ safeModeEnabled: "El modo seguro está activado"
pluginsAreDisabledBecauseSafeMode: "El modo seguro está activado, por lo que todos los plugins están desactivados." pluginsAreDisabledBecauseSafeMode: "El modo seguro está activado, por lo que todos los plugins están desactivados."
customCssIsDisabledBecauseSafeMode: "El modo seguro está activado, por lo que no se aplica el CSS personalizado." customCssIsDisabledBecauseSafeMode: "El modo seguro está activado, por lo que no se aplica el CSS personalizado."
themeIsDefaultBecauseSafeMode: "Mientras el modo seguro esté activado, se utilizará el tema predeterminado. Cuando se desactive el modo seguro, se volverá al tema original." themeIsDefaultBecauseSafeMode: "Mientras el modo seguro esté activado, se utilizará el tema predeterminado. Cuando se desactive el modo seguro, se volverá al tema original."
thankYouForTestingBeta: "¡Gracias por tu colaboración en la prueba de la versión beta!"
_order: _order:
newest: "Los más recientes primero" newest: "Los más recientes primero"
oldest: "Los más antiguos primero" oldest: "Los más antiguos primero"
@ -1663,6 +1666,9 @@ _serverSettings:
userGeneratedContentsVisibilityForVisitor_description2: "Publicar incondicionalmente todo el contenido del servidor en Internet, incluido el contenido remoto recibido por el servidor, es arriesgado. Esto es especialmente importante para los invitados que desconocen la naturaleza distribuida del contenido, ya que pueden creer erróneamente que incluso el contenido remoto es contenido creado por usuarios en el servidor." userGeneratedContentsVisibilityForVisitor_description2: "Publicar incondicionalmente todo el contenido del servidor en Internet, incluido el contenido remoto recibido por el servidor, es arriesgado. Esto es especialmente importante para los invitados que desconocen la naturaleza distribuida del contenido, ya que pueden creer erróneamente que incluso el contenido remoto es contenido creado por usuarios en el servidor."
restartServerSetupWizardConfirm_title: "¿Reiniciar el asistente de configuración del servidor?" restartServerSetupWizardConfirm_title: "¿Reiniciar el asistente de configuración del servidor?"
restartServerSetupWizardConfirm_text: "Algunas configuraciones actuales se restablecerán" restartServerSetupWizardConfirm_text: "Algunas configuraciones actuales se restablecerán"
entrancePageStyle: "Estilo de la página de inicio"
showTimelineForVisitor: "Mostrar la línea de tiempo"
showActivitiesForVisitor: "Mostrar actividades"
_userGeneratedContentsVisibilityForVisitor: _userGeneratedContentsVisibilityForVisitor:
all: "Todo es público." all: "Todo es público."
localOnly: "Sólo se publica el contenido local, el remoto se mantiene privado" localOnly: "Sólo se publica el contenido local, el remoto se mantiene privado"
@ -2272,6 +2278,7 @@ _time:
minute: "Minutos" minute: "Minutos"
hour: "Horas" hour: "Horas"
day: "Días" day: "Días"
month: "Mes(es)"
_2fa: _2fa:
alreadyRegistered: "Ya has completado la configuración." alreadyRegistered: "Ya has completado la configuración."
registerTOTP: "Registrar aplicación autenticadora" registerTOTP: "Registrar aplicación autenticadora"

8
locales/index.d.ts vendored
View File

@ -6531,7 +6531,7 @@ export interface Locale extends ILocale {
*/ */
"remoteNotesCleaning": string; "remoteNotesCleaning": string;
/** /**
* 稿 * 稿
*/ */
"remoteNotesCleaning_description": string; "remoteNotesCleaning_description": string;
/** /**
@ -6633,7 +6633,7 @@ export interface Locale extends ILocale {
/** /**
* *
*/ */
"showActivityiesForVisitor": string; "showActivitiesForVisitor": string;
"_userGeneratedContentsVisibilityForVisitor": { "_userGeneratedContentsVisibilityForVisitor": {
/** /**
* *
@ -12032,11 +12032,11 @@ export interface Locale extends ILocale {
*/ */
"youCanConfigureMoreFederationSettingsLater": string; "youCanConfigureMoreFederationSettingsLater": string;
/** /**
* *
*/ */
"remoteContentsCleaning": string; "remoteContentsCleaning": string;
/** /**
* *
*/ */
"remoteContentsCleaning_description": string; "remoteContentsCleaning_description": string;
/** /**

View File

@ -139,7 +139,7 @@ overwriteFromPinnedEmojis: "Sovrascrivi con le impostazioni globali"
reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere." reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere."
rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note" rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note"
attachCancel: "Rimuovi allegato" attachCancel: "Rimuovi allegato"
deleteFile: "File da Drive eliminato" deleteFile: "Elimina un file dal Drive"
markAsSensitive: "Segna come esplicito" markAsSensitive: "Segna come esplicito"
unmarkAsSensitive: "Non segnare come esplicito " unmarkAsSensitive: "Non segnare come esplicito "
enterFileName: "Nome del file" enterFileName: "Nome del file"
@ -1668,7 +1668,7 @@ _serverSettings:
restartServerSetupWizardConfirm_text: "Verranno ripristinate alcune tue impostazioni personalizzate." restartServerSetupWizardConfirm_text: "Verranno ripristinate alcune tue impostazioni personalizzate."
entrancePageStyle: "Stile della pagina di ingresso" entrancePageStyle: "Stile della pagina di ingresso"
showTimelineForVisitor: "Mostra la Timeline a visitatori non autenticati" showTimelineForVisitor: "Mostra la Timeline a visitatori non autenticati"
showActivityiesForVisitor: "Mostra le attività a visitatori non autenticati" showActivitiesForVisitor: "Mostrare la propria attività"
_userGeneratedContentsVisibilityForVisitor: _userGeneratedContentsVisibilityForVisitor:
all: "Tutto pubblico" all: "Tutto pubblico"
localOnly: "Pubblica solo contenuti locali, mantieni privati i contenuti remoti" localOnly: "Pubblica solo contenuti locali, mantieni privati i contenuti remoti"
@ -2222,7 +2222,7 @@ _theme:
hashtag: "Hashtag" hashtag: "Hashtag"
mention: "Menzioni" mention: "Menzioni"
mentionMe: "Menzioni (di me)" mentionMe: "Menzioni (di me)"
renote: "Renota" renote: "Rinota"
modalBg: "Sfondo modale." modalBg: "Sfondo modale."
divider: "Interruzione di linea" divider: "Interruzione di linea"
scrollbarHandle: "Maniglie della barra di scorrimento" scrollbarHandle: "Maniglie della barra di scorrimento"
@ -2663,7 +2663,7 @@ _notification:
createToken: "È stato creato un token di accesso" createToken: "È stato creato un token di accesso"
createTokenDescription: "In caso contrario, eliminare il token di accesso tramite ({text})." createTokenDescription: "In caso contrario, eliminare il token di accesso tramite ({text})."
_types: _types:
all: "Tutto" all: "Tutte"
note: "Nuove Note" note: "Nuove Note"
follow: "Follower" follow: "Follower"
mention: "Menzioni" mention: "Menzioni"
@ -2671,7 +2671,7 @@ _notification:
renote: "Rinota" renote: "Rinota"
quote: "Cita" quote: "Cita"
reaction: "Reazioni" reaction: "Reazioni"
pollEnded: "Sondaggio chiuso." pollEnded: "Sondaggio terminato"
receiveFollowRequest: "Richieste di follow in arrivo" receiveFollowRequest: "Richieste di follow in arrivo"
followRequestAccepted: "Richieste di follow accettate" followRequestAccepted: "Richieste di follow accettate"
roleAssigned: "Ruolo concesso" roleAssigned: "Ruolo concesso"
@ -2679,7 +2679,7 @@ _notification:
achievementEarned: "Risultato raggiunto" achievementEarned: "Risultato raggiunto"
exportCompleted: "Esportazione completata" exportCompleted: "Esportazione completata"
login: "Accessi" login: "Accessi"
createToken: "Creare un token di accesso" createToken: "Aggiunto un token di accesso"
test: "Notifiche di test" test: "Notifiche di test"
app: "Notifiche da applicazioni" app: "Notifiche da applicazioni"
_actions: _actions:
@ -2771,56 +2771,56 @@ _abuseReport:
notifiedWebhook: "Webhook da usare" notifiedWebhook: "Webhook da usare"
deleteConfirm: "Vuoi davvero rimuovere il destinatario della notifica?" deleteConfirm: "Vuoi davvero rimuovere il destinatario della notifica?"
_moderationLogTypes: _moderationLogTypes:
createRole: "Ruolo creato" createRole: "Crea un Ruolo"
deleteRole: "Ruolo eliminato" deleteRole: "Elimina un Ruolo"
updateRole: "Ruolo aggiornato" updateRole: "Modifica un ruolo"
assignRole: "Ruolo assegnato" assignRole: "Assegna un Ruolo"
unassignRole: "Ruolo disassegnato" unassignRole: "Toglie un Ruolo al Profilo"
suspend: "Sospensione" suspend: "Sospende"
unsuspend: "Sospensione rimossa" unsuspend: "Solleva la sospensione"
addCustomEmoji: "Emoji personalizzata aggiunta" addCustomEmoji: "Aggiunge Emoji personalizzata"
updateCustomEmoji: "Emoji personalizzata aggiornata" updateCustomEmoji: "Modifica Emoji personalizzata"
deleteCustomEmoji: "Emoji personalizzata eliminata" deleteCustomEmoji: "Elimina Emoji personalizzata"
updateServerSettings: "Impostazioni del server aggiornate" updateServerSettings: "Modifica le impostazioni del server"
updateUserNote: "Promemoria di moderazione aggiornato" updateUserNote: "Modifica un promemoria di moderazione"
deleteDriveFile: "File da Drive eliminato" deleteDriveFile: "Elimina un file dal Drive"
deleteNote: "Nota eliminata" deleteNote: "Elimina una Nota"
createGlobalAnnouncement: "Annuncio globale creato" createGlobalAnnouncement: "Crea un annuncio globale"
createUserAnnouncement: "Annuncio ai profili iscritti creato" createUserAnnouncement: "Crea un annuncio ai profili già iscritti"
updateGlobalAnnouncement: "Annuncio globale aggiornato" updateGlobalAnnouncement: "Modifica un annuncio globale"
updateUserAnnouncement: "Annuncio ai profili iscritti aggiornato" updateUserAnnouncement: "Modifica un annuncio ai profili già iscritti"
deleteGlobalAnnouncement: "Annuncio globale eliminato" deleteGlobalAnnouncement: "Elimina un annuncio globale"
deleteUserAnnouncement: "Annuncio ai profili iscritti eliminato" deleteUserAnnouncement: "Elimina un annuncio ai profili già iscritti"
resetPassword: "Password azzerata" resetPassword: "Azzera la password"
suspendRemoteInstance: "Istanza remota sospesa" suspendRemoteInstance: "Sospende una istanza remota"
unsuspendRemoteInstance: "Istanza remota riattivata" unsuspendRemoteInstance: "Riattiva una istanza remota"
updateRemoteInstanceNote: "Aggiornamento del promemoria di moderazione per il server remoto" updateRemoteInstanceNote: "Modifica il promemoria di moderazione per il server remoto"
markSensitiveDriveFile: "File nel Drive segnato come esplicito" markSensitiveDriveFile: "Aggiunge NSFW a un file nel Drive"
unmarkSensitiveDriveFile: "File nel Drive segnato come non esplicito" unmarkSensitiveDriveFile: "Toglie NSFW da un file nel Drive"
resolveAbuseReport: "Segnalazione risolta" resolveAbuseReport: "Risolve una segnalazione"
forwardAbuseReport: "Segnalazione inoltrata" forwardAbuseReport: "Inoltra una segnalazione"
updateAbuseReportNote: "Ha aggiornato la segnalazione" updateAbuseReportNote: "Modifica una segnalazione"
createInvitation: "Genera codice di invito" createInvitation: "Genera un codice di invito"
createAd: "Banner creato" createAd: "Aggiunge un Banner"
deleteAd: "Banner eliminato" deleteAd: "Elimina un Banner"
updateAd: "Banner aggiornato" updateAd: "Modifica un Banner"
createAvatarDecoration: "Creazione decorazione della foto profilo" createAvatarDecoration: "Crea una decorazione della foto profilo"
updateAvatarDecoration: "Aggiornamento decorazione foto profilo" updateAvatarDecoration: "Modifica una decorazione della foto profilo"
deleteAvatarDecoration: "Eliminazione decorazione della foto profilo" deleteAvatarDecoration: "Elimina una decorazione della foto profilo"
unsetUserAvatar: "Rimossa foto profilo" unsetUserAvatar: "Toglie una foto profilo"
unsetUserBanner: "Rimossa intestazione profilo" unsetUserBanner: "Toglie una immagine di intestazione profilo"
createSystemWebhook: "Crea un SystemWebhook" createSystemWebhook: "Aggiunge un System Webhook"
updateSystemWebhook: "Modifica SystemWebhook" updateSystemWebhook: "Modifica un System Webhook"
deleteSystemWebhook: "Elimina SystemWebhook" deleteSystemWebhook: "Elimina un System Webhook"
createAbuseReportNotificationRecipient: "Crea destinatario per le notifiche di segnalazioni" createAbuseReportNotificationRecipient: "Crea destinatario per le notifiche di segnalazioni"
updateAbuseReportNotificationRecipient: "Aggiorna destinatario notifiche di segnalazioni" updateAbuseReportNotificationRecipient: "Modifica un destinatario per le notifiche di segnalazioni"
deleteAbuseReportNotificationRecipient: "Elimina destinatario notifiche di segnalazioni" deleteAbuseReportNotificationRecipient: "Elimina un destinatario per le notifiche di segnalazioni"
deleteAccount: "Quando viene eliminato un profilo" deleteAccount: "Elimina un profilo"
deletePage: "Pagina eliminata" deletePage: "Elimina una Pagina"
deleteFlash: "Play eliminato" deleteFlash: "Elimina un Play"
deleteGalleryPost: "Eliminazione pubblicazione nella Galleria" deleteGalleryPost: "Elimina pubblicazione nella Galleria"
deleteChatRoom: "Elimina chat" deleteChatRoom: "Elimina una Chat"
updateProxyAccountDescription: "Aggiornata la descrizione del profilo proxy" updateProxyAccountDescription: "Aggiorna la descrizione del profilo proxy"
_fileViewer: _fileViewer:
title: "Dettagli del file" title: "Dettagli del file"
type: "Tipo di file" type: "Tipo di file"

View File

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

View File

@ -1668,7 +1668,7 @@ _serverSettings:
restartServerSetupWizardConfirm_text: "현재 일부 설정은 리셋됩니다." restartServerSetupWizardConfirm_text: "현재 일부 설정은 리셋됩니다."
entrancePageStyle: "입구 페이지의 스타일" entrancePageStyle: "입구 페이지의 스타일"
showTimelineForVisitor: "타임라인 표시" showTimelineForVisitor: "타임라인 표시"
showActivityiesForVisitor: "활동 표시" showActivitiesForVisitor: "액티비티 표시하기"
_userGeneratedContentsVisibilityForVisitor: _userGeneratedContentsVisibilityForVisitor:
all: "모두 공개" all: "모두 공개"
localOnly: "로컬 콘텐츠만 공개하고 리모트 콘텐츠는 비공개" localOnly: "로컬 콘텐츠만 공개하고 리모트 콘텐츠는 비공개"

View File

@ -31,7 +31,7 @@ openInWindow: "Pencerede aç"
profile: "Profil" profile: "Profil"
timeline: "Pano" timeline: "Pano"
noAccountDescription: "Bu kullanıcı henüz biyografisini yazmamış." noAccountDescription: "Bu kullanıcı henüz biyografisini yazmamış."
login: "Giriş Yap" login: "Oturum Aç"
loggingIn: "Giriş Yapılıyor..." loggingIn: "Giriş Yapılıyor..."
logout: ıkış Yap" logout: ıkış Yap"
signup: "Kaydol" signup: "Kaydol"
@ -310,7 +310,7 @@ agreeBelow: "Aşağıdakileri kabul ediyorum"
basicNotesBeforeCreateAccount: "Önemli notlar" basicNotesBeforeCreateAccount: "Önemli notlar"
termsOfService: "Hizmet Şartları" termsOfService: "Hizmet Şartları"
start: "Başla" start: "Başla"
home: "Ana sayfa" home: "Pano"
remoteUserCaution: "Bu kullanıcı uzak bir sunucudan geldiği için, gösterilen bilgiler eksik olabilir." remoteUserCaution: "Bu kullanıcı uzak bir sunucudan geldiği için, gösterilen bilgiler eksik olabilir."
activity: "Etkinlik" activity: "Etkinlik"
images: "Görseller" images: "Görseller"
@ -1054,6 +1054,7 @@ permissionDeniedError: "İşlem reddedildi"
permissionDeniedErrorDescription: "Bu hesap bu işlemi gerçekleştirmek için gerekli izne sahip değildir." permissionDeniedErrorDescription: "Bu hesap bu işlemi gerçekleştirmek için gerekli izne sahip değildir."
preset: "Ön ayar" preset: "Ön ayar"
selectFromPresets: "Ön ayarlardan seçim yapın" selectFromPresets: "Ön ayarlardan seçim yapın"
custom: "Özel"
achievements: "Başarılar" achievements: "Başarılar"
gotInvalidResponseError: "Geçersiz sunucu yanıtı" gotInvalidResponseError: "Geçersiz sunucu yanıtı"
gotInvalidResponseErrorDescription: "Sunucu erişilemez durumda olabilir veya bakım çalışması yapılmaktadır. Lütfen daha sonra tekrar dene." gotInvalidResponseErrorDescription: "Sunucu erişilemez durumda olabilir veya bakım çalışması yapılmaktadır. Lütfen daha sonra tekrar dene."
@ -1066,8 +1067,8 @@ collapseRenotesDescription: "Zaten yanıtladığın veya renote aldığın notla
internalServerError: "İç Sunucu Hatası" internalServerError: "İç Sunucu Hatası"
internalServerErrorDescription: "Sunucu beklenmedik bir hatayla karşılaştı." internalServerErrorDescription: "Sunucu beklenmedik bir hatayla karşılaştı."
copyErrorInfo: "Hata ayrıntılarını kopyala" copyErrorInfo: "Hata ayrıntılarını kopyala"
joinThisServer: "Bu sunucuda kaydolun" joinThisServer: "Kaydol"
exploreOtherServers: "Başka bir sunucu arayın" exploreOtherServers: "Diğer sunucuları keşfet"
letsLookAtTimeline: "Pano'ya bir göz atın" letsLookAtTimeline: "Pano'ya bir göz atın"
disableFederationConfirm: "Federasyonu cidden devre dışı bırakmak istiyor musun?" disableFederationConfirm: "Federasyonu cidden devre dışı bırakmak istiyor musun?"
disableFederationConfirmWarn: "Federasyondan ayrılsa bile, aksi belirtilmedikçe gönderiler herkese açık olmaya devam edecek. Genellikle bunu yapmanız gerekmez." disableFederationConfirmWarn: "Federasyondan ayrılsa bile, aksi belirtilmedikçe gönderiler herkese açık olmaya devam edecek. Genellikle bunu yapmanız gerekmez."
@ -1244,6 +1245,7 @@ releaseToRefresh: "Yenilemek için serbest bırak"
refreshing: "Yenileniyor..." refreshing: "Yenileniyor..."
pullDownToRefresh: "Yenilemek için aşağı çekin" pullDownToRefresh: "Yenilemek için aşağı çekin"
useGroupedNotifications: "Gruplandırılmış bildirimleri göster" useGroupedNotifications: "Gruplandırılmış bildirimleri göster"
emailVerificationFailedError: "E-posta adresi doğrulanırken bir sorun oluştu. Bağlantının geçerlilik süresi dolmuş olabilir."
cwNotationRequired: "“İçeriği gizle” seçeneği etkinleştirilirse, bir açıklama sağlanmalı." cwNotationRequired: "“İçeriği gizle” seçeneği etkinleştirilirse, bir açıklama sağlanmalı."
doReaction: "Tepki ekle" doReaction: "Tepki ekle"
code: "Kod" code: "Kod"
@ -1374,6 +1376,7 @@ safeModeEnabled: "Güvenli mod etkinleştirildi"
pluginsAreDisabledBecauseSafeMode: "Güvenli mod etkinleştirildiği için tüm eklentiler devre dışı bırakılmıştır." pluginsAreDisabledBecauseSafeMode: "Güvenli mod etkinleştirildiği için tüm eklentiler devre dışı bırakılmıştır."
customCssIsDisabledBecauseSafeMode: "Güvenli mod etkin olduğu için özel CSS uygulanmıyor." customCssIsDisabledBecauseSafeMode: "Güvenli mod etkin olduğu için özel CSS uygulanmıyor."
themeIsDefaultBecauseSafeMode: "Güvenli mod etkinken, varsayılan tema kullanılır. Güvenli modu devre dışı bırakmak bu değişiklikleri geri alır." themeIsDefaultBecauseSafeMode: "Güvenli mod etkinken, varsayılan tema kullanılır. Güvenli modu devre dışı bırakmak bu değişiklikleri geri alır."
thankYouForTestingBeta: "Beta sürümünü test ettiğin için teşekkür ederiz!"
_order: _order:
newest: "Önce yeni" newest: "Önce yeni"
oldest: "Önce eski" oldest: "Önce eski"
@ -1622,7 +1625,7 @@ _initialTutorial:
_timelineDescription: _timelineDescription:
home: "Ana Pano'da, takip ettiğin hesapların notlarını görebilirsin." home: "Ana Pano'da, takip ettiğin hesapların notlarını görebilirsin."
local: "Yerel Pano'da, bu sunucudaki tüm kullanıcıların notlarını görebilirsin." local: "Yerel Pano'da, bu sunucudaki tüm kullanıcıların notlarını görebilirsin."
social: "Sosyal Pano, Ana Sayfa ve Yerel Pano'dan gelen notları görüntüler." social: "Pano, Sosyal Pano ve Yerel Pano'dan gelen notları görüntüler."
global: "Global Pano'da, bağlı tüm sunuculardan gelen notları görebilirsin." global: "Global Pano'da, bağlı tüm sunuculardan gelen notları görebilirsin."
_serverRules: _serverRules:
description: "Kayıt öncesinde gösterilecek bir dizi kural. Hizmet Şartlarının özetini belirlemen önerilir." description: "Kayıt öncesinde gösterilecek bir dizi kural. Hizmet Şartlarının özetini belirlemen önerilir."
@ -1663,6 +1666,8 @@ _serverSettings:
userGeneratedContentsVisibilityForVisitor_description2: "Sunucu tarafından alınan uzak içerik dahil olmak üzere sunucudaki tüm içeriği koşulsuz olarak İnternet'e yayınlamak risklidir. Bu, içeriğin dağıtılmış yapısından haberdar olmayan misafirler için özellikle önemlidir, çünkü onlar yanlışlıkla uzak içeriğin bile sunucudaki kullanıcılar tarafından oluşturulan içerik olduğunu düşünebilirler." userGeneratedContentsVisibilityForVisitor_description2: "Sunucu tarafından alınan uzak içerik dahil olmak üzere sunucudaki tüm içeriği koşulsuz olarak İnternet'e yayınlamak risklidir. Bu, içeriğin dağıtılmış yapısından haberdar olmayan misafirler için özellikle önemlidir, çünkü onlar yanlışlıkla uzak içeriğin bile sunucudaki kullanıcılar tarafından oluşturulan içerik olduğunu düşünebilirler."
restartServerSetupWizardConfirm_title: "Sunucu kurulum sihirbazını yeniden başlatmak ister misin?" restartServerSetupWizardConfirm_title: "Sunucu kurulum sihirbazını yeniden başlatmak ister misin?"
restartServerSetupWizardConfirm_text: "Bazı mevcut ayarlar sıfırlanacaktır." restartServerSetupWizardConfirm_text: "Bazı mevcut ayarlar sıfırlanacaktır."
entrancePageStyle: "Giriş sayfası stili"
showTimelineForVisitor: "Panoyu göster"
_userGeneratedContentsVisibilityForVisitor: _userGeneratedContentsVisibilityForVisitor:
all: "Her şey halka açıktır." all: "Her şey halka açıktır."
localOnly: "Yalnızca yerel içerik yayınlanır, uzak içerik gizli tutulur." localOnly: "Yalnızca yerel içerik yayınlanır, uzak içerik gizli tutulur."
@ -2272,6 +2277,7 @@ _time:
minute: "Dakika(lar)" minute: "Dakika(lar)"
hour: "Saat(ler)" hour: "Saat(ler)"
day: "Gün(ler)" day: "Gün(ler)"
month: "Ay"
_2fa: _2fa:
alreadyRegistered: "2fa kimlik doğrulama cihazını zaten kaydettin." alreadyRegistered: "2fa kimlik doğrulama cihazını zaten kaydettin."
registerTOTP: "Kimlik doğrulama uygulamasını kaydet" registerTOTP: "Kimlik doğrulama uygulamasını kaydet"
@ -2477,7 +2483,7 @@ _poll:
_visibility: _visibility:
public: "Halka açık" public: "Halka açık"
publicDescription: "Notunuz tüm kullanıcılar tarafından görülebilir olacaktır." publicDescription: "Notunuz tüm kullanıcılar tarafından görülebilir olacaktır."
home: "Ana sayfa" home: "Pano"
homeDescription: "Yalnızca ana zaman çizelgesine gönder" homeDescription: "Yalnızca ana zaman çizelgesine gönder"
followers: "Takipçiler" followers: "Takipçiler"
followersDescription: "Sadece takipçilerine görünür hale getir" followersDescription: "Sadece takipçilerine görünür hale getir"
@ -2553,7 +2559,7 @@ _instanceCharts:
files: "Dosya sayısındaki fark" files: "Dosya sayısındaki fark"
filesTotal: "Toplam dosya sayısı" filesTotal: "Toplam dosya sayısı"
_timelines: _timelines:
home: "Ana Sayfa" home: "Pano"
local: "Yerel" local: "Yerel"
social: "Sosyal" social: "Sosyal"
global: "Global" global: "Global"
@ -2671,7 +2677,7 @@ _notification:
chatRoomInvitationReceived: "Sohbet odasına davet edildi" chatRoomInvitationReceived: "Sohbet odasına davet edildi"
achievementEarned: "Başarı kilidi açıldı" achievementEarned: "Başarı kilidi açıldı"
exportCompleted: "İhracat işlemi tamamlandı." exportCompleted: "İhracat işlemi tamamlandı."
login: "Giriş Yap" login: "Oturum Aç"
createToken: "Erişim jetonu oluştur" createToken: "Erişim jetonu oluştur"
test: "Bildirim testi" test: "Bildirim testi"
app: "Bağlı uygulamalardan gelen bildirimler" app: "Bağlı uygulamalardan gelen bildirimler"
@ -2708,7 +2714,7 @@ _deck:
main: "Ana" main: "Ana"
widgets: "Widget'lar" widgets: "Widget'lar"
notifications: "Bildirimler" notifications: "Bildirimler"
tl: "Ana Sayfa" tl: "Pano"
antenna: "Antenler" antenna: "Antenler"
list: "Liste" list: "Liste"
channel: "Kanal" channel: "Kanal"

View File

@ -1668,7 +1668,7 @@ _serverSettings:
restartServerSetupWizardConfirm_text: "现有的部分设定将重置。" restartServerSetupWizardConfirm_text: "现有的部分设定将重置。"
entrancePageStyle: "入口页面样式" entrancePageStyle: "入口页面样式"
showTimelineForVisitor: "显示时间线" showTimelineForVisitor: "显示时间线"
showActivityiesForVisitor: "显示活动" showActivitiesForVisitor: "显示活动"
_userGeneratedContentsVisibilityForVisitor: _userGeneratedContentsVisibilityForVisitor:
all: "全部公开" all: "全部公开"
localOnly: "仅公开本地内容,隐藏远程内容" localOnly: "仅公开本地内容,隐藏远程内容"

View File

@ -1668,7 +1668,7 @@ _serverSettings:
restartServerSetupWizardConfirm_text: "當前的部分設定將會被重設。" restartServerSetupWizardConfirm_text: "當前的部分設定將會被重設。"
entrancePageStyle: "入口頁面的樣式" entrancePageStyle: "入口頁面的樣式"
showTimelineForVisitor: "顯示時間軸" showTimelineForVisitor: "顯示時間軸"
showActivityiesForVisitor: "顯示活動" showActivitiesForVisitor: "顯示活動"
_userGeneratedContentsVisibilityForVisitor: _userGeneratedContentsVisibilityForVisitor:
all: "全部公開\n" all: "全部公開\n"
localOnly: "僅公開本地內容,遠端內容則不公開\n" localOnly: "僅公開本地內容,遠端內容則不公開\n"

View File

@ -1,12 +1,12 @@
{ {
"name": "misskey", "name": "misskey",
"version": "2025.8.0-beta.3", "version": "2025.8.0",
"codename": "nasubi", "codename": "nasubi",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/misskey-dev/misskey.git" "url": "https://github.com/misskey-dev/misskey.git"
}, },
"packageManager": "pnpm@10.14.0", "packageManager": "pnpm@10.15.0",
"workspaces": [ "workspaces": [
"packages/frontend-shared", "packages/frontend-shared",
"packages/frontend", "packages/frontend",
@ -53,8 +53,8 @@
"lodash": "4.17.21" "lodash": "4.17.21"
}, },
"dependencies": { "dependencies": {
"cssnano": "7.1.0", "cssnano": "7.1.1",
"esbuild": "0.25.8", "esbuild": "0.25.9",
"execa": "9.6.0", "execa": "9.6.0",
"fast-glob": "3.3.3", "fast-glob": "3.3.3",
"glob": "11.0.3", "glob": "11.0.3",
@ -67,15 +67,16 @@
}, },
"devDependencies": { "devDependencies": {
"@misskey-dev/eslint-plugin": "2.1.0", "@misskey-dev/eslint-plugin": "2.1.0",
"@types/node": "22.17.1", "@types/js-yaml": "4.0.9",
"@typescript-eslint/eslint-plugin": "8.39.0", "@types/node": "22.17.2",
"@typescript-eslint/parser": "8.39.0", "@typescript-eslint/eslint-plugin": "8.40.0",
"@typescript-eslint/parser": "8.40.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "14.5.4", "cypress": "14.5.4",
"eslint": "9.33.0", "eslint": "9.34.0",
"globals": "16.3.0", "globals": "16.3.0",
"ncp": "2.0.0", "ncp": "2.0.0",
"pnpm": "10.14.0", "pnpm": "10.15.0",
"start-server-and-test": "2.0.13" "start-server-and-test": "2.0.13"
}, },
"optionalDependencies": { "optionalDependencies": {

View File

@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class NonCascadingPageEyeCatching1756062689648 {
name = 'NonCascadingPageEyeCatching1756062689648'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "page" DROP CONSTRAINT "FK_a9ca79ad939bf06066b81c9d3aa"`);
await queryRunner.query(`ALTER TABLE "page" ADD CONSTRAINT "FK_a9ca79ad939bf06066b81c9d3aa" FOREIGN KEY ("eyeCatchingImageId") REFERENCES "drive_file"("id") ON DELETE SET NULL ON UPDATE NO ACTION`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "page" DROP CONSTRAINT "FK_a9ca79ad939bf06066b81c9d3aa"`);
await queryRunner.query(`ALTER TABLE "page" ADD CONSTRAINT "FK_a9ca79ad939bf06066b81c9d3aa" FOREIGN KEY ("eyeCatchingImageId") REFERENCES "drive_file"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
}

View File

@ -39,17 +39,17 @@
}, },
"optionalDependencies": { "optionalDependencies": {
"@swc/core-android-arm64": "1.3.11", "@swc/core-android-arm64": "1.3.11",
"@swc/core-darwin-arm64": "1.13.3", "@swc/core-darwin-arm64": "1.13.4",
"@swc/core-darwin-x64": "1.13.3", "@swc/core-darwin-x64": "1.13.4",
"@swc/core-freebsd-x64": "1.3.11", "@swc/core-freebsd-x64": "1.3.11",
"@swc/core-linux-arm-gnueabihf": "1.13.3", "@swc/core-linux-arm-gnueabihf": "1.13.4",
"@swc/core-linux-arm64-gnu": "1.13.3", "@swc/core-linux-arm64-gnu": "1.13.4",
"@swc/core-linux-arm64-musl": "1.13.3", "@swc/core-linux-arm64-musl": "1.13.4",
"@swc/core-linux-x64-gnu": "1.13.3", "@swc/core-linux-x64-gnu": "1.13.4",
"@swc/core-linux-x64-musl": "1.13.3", "@swc/core-linux-x64-musl": "1.13.4",
"@swc/core-win32-arm64-msvc": "1.13.3", "@swc/core-win32-arm64-msvc": "1.13.4",
"@swc/core-win32-ia32-msvc": "1.13.3", "@swc/core-win32-ia32-msvc": "1.13.4",
"@swc/core-win32-x64-msvc": "1.13.3", "@swc/core-win32-x64-msvc": "1.13.4",
"@tensorflow/tfjs": "4.22.0", "@tensorflow/tfjs": "4.22.0",
"@tensorflow/tfjs-node": "4.22.0", "@tensorflow/tfjs-node": "4.22.0",
"bufferutil": "4.0.9", "bufferutil": "4.0.9",
@ -69,8 +69,8 @@
"utf-8-validate": "6.0.5" "utf-8-validate": "6.0.5"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "3.864.0", "@aws-sdk/client-s3": "3.873.0",
"@aws-sdk/lib-storage": "3.864.0", "@aws-sdk/lib-storage": "3.873.0",
"@discordapp/twemoji": "16.0.1", "@discordapp/twemoji": "16.0.1",
"@fastify/accepts": "5.0.2", "@fastify/accepts": "5.0.2",
"@fastify/cookie": "11.0.2", "@fastify/cookie": "11.0.2",
@ -93,7 +93,7 @@
"@sinonjs/fake-timers": "11.3.1", "@sinonjs/fake-timers": "11.3.1",
"@smithy/node-http-handler": "2.5.0", "@smithy/node-http-handler": "2.5.0",
"@swc/cli": "0.7.8", "@swc/cli": "0.7.8",
"@swc/core": "1.13.3", "@swc/core": "1.13.4",
"@twemoji/parser": "16.0.0", "@twemoji/parser": "16.0.0",
"@types/redis-info": "3.0.3", "@types/redis-info": "3.0.3",
"accepts": "1.3.8", "accepts": "1.3.8",
@ -103,10 +103,10 @@
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"blurhash": "2.0.5", "blurhash": "2.0.5",
"body-parser": "1.20.3", "body-parser": "1.20.3",
"bullmq": "5.56.9", "bullmq": "5.58.1",
"cacheable-lookup": "7.0.0", "cacheable-lookup": "7.0.0",
"cbor": "9.0.2", "cbor": "9.0.2",
"chalk": "5.5.0", "chalk": "5.6.0",
"chalk-template": "1.1.0", "chalk-template": "1.1.0",
"chokidar": "4.0.3", "chokidar": "4.0.3",
"cli-highlight": "2.1.11", "cli-highlight": "2.1.11",
@ -114,7 +114,7 @@
"content-disposition": "0.5.4", "content-disposition": "0.5.4",
"date-fns": "2.30.0", "date-fns": "2.30.0",
"deep-email-validator": "0.1.21", "deep-email-validator": "0.1.21",
"fastify": "5.4.0", "fastify": "5.5.0",
"fastify-raw-body": "5.0.0", "fastify-raw-body": "5.0.0",
"feed": "4.2.2", "feed": "4.2.2",
"file-type": "19.6.0", "file-type": "19.6.0",
@ -135,7 +135,7 @@
"jsonld": "8.3.3", "jsonld": "8.3.3",
"jsrsasign": "11.1.0", "jsrsasign": "11.1.0",
"juice": "11.0.1", "juice": "11.0.1",
"meilisearch": "0.51.0", "meilisearch": "0.52.0",
"mfm-js": "0.25.0", "mfm-js": "0.25.0",
"microformats-parser": "2.0.4", "microformats-parser": "2.0.4",
"mime-types": "2.1.35", "mime-types": "2.1.35",
@ -151,7 +151,7 @@
"oauth2orize": "1.12.0", "oauth2orize": "1.12.0",
"oauth2orize-pkce": "0.1.2", "oauth2orize-pkce": "0.1.2",
"os-utils": "0.0.14", "os-utils": "0.0.14",
"otpauth": "9.4.0", "otpauth": "9.4.1",
"parse5": "7.3.0", "parse5": "7.3.0",
"pg": "8.16.3", "pg": "8.16.3",
"pkce-challenge": "4.1.0", "pkce-challenge": "4.1.0",
@ -177,10 +177,10 @@
"stringz": "2.1.0", "stringz": "2.1.0",
"systeminformation": "5.27.7", "systeminformation": "5.27.7",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
"tmp": "0.2.4", "tmp": "0.2.5",
"tsc-alias": "1.8.16", "tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0", "tsconfig-paths": "4.2.0",
"typeorm": "0.3.25", "typeorm": "0.3.26",
"typescript": "5.9.2", "typescript": "5.9.2",
"ulid": "2.4.0", "ulid": "2.4.0",
"vary": "1.1.2", "vary": "1.1.2",
@ -191,7 +191,7 @@
"devDependencies": { "devDependencies": {
"@jest/globals": "29.7.0", "@jest/globals": "29.7.0",
"@nestjs/platform-express": "10.4.20", "@nestjs/platform-express": "10.4.20",
"@sentry/vue": "9.45.0", "@sentry/vue": "9.46.0",
"@simplewebauthn/types": "12.0.0", "@simplewebauthn/types": "12.0.0",
"@swc/jest": "0.2.39", "@swc/jest": "0.2.39",
"@types/accepts": "1.3.7", "@types/accepts": "1.3.7",
@ -210,8 +210,8 @@
"@types/jsrsasign": "10.5.15", "@types/jsrsasign": "10.5.15",
"@types/mime-types": "2.1.4", "@types/mime-types": "2.1.4",
"@types/ms": "0.7.34", "@types/ms": "0.7.34",
"@types/node": "22.17.1", "@types/node": "22.17.2",
"@types/nodemailer": "6.4.17", "@types/nodemailer": "6.4.19",
"@types/oauth": "0.9.6", "@types/oauth": "0.9.6",
"@types/oauth2orize": "1.11.5", "@types/oauth2orize": "1.11.5",
"@types/oauth2orize-pkce": "0.1.2", "@types/oauth2orize-pkce": "0.1.2",
@ -231,8 +231,8 @@
"@types/vary": "1.1.3", "@types/vary": "1.1.3",
"@types/web-push": "3.6.4", "@types/web-push": "3.6.4",
"@types/ws": "8.18.1", "@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.39.0", "@typescript-eslint/eslint-plugin": "8.40.0",
"@typescript-eslint/parser": "8.39.0", "@typescript-eslint/parser": "8.40.0",
"aws-sdk-client-mock": "4.1.0", "aws-sdk-client-mock": "4.1.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"eslint-plugin-import": "2.32.0", "eslint-plugin-import": "2.32.0",

View File

@ -31,6 +31,7 @@ import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
import { NotificationService } from '@/core/NotificationService.js'; import { NotificationService } from '@/core/NotificationService.js';
import type { OnApplicationShutdown, OnModuleInit } from '@nestjs/common'; import type { OnApplicationShutdown, OnModuleInit } from '@nestjs/common';
// misskey-js の rolePolicies と同期すべし
export type RolePolicies = { export type RolePolicies = {
gtlAvailable: boolean; gtlAvailable: boolean;
ltlAvailable: boolean; ltlAvailable: boolean;

View File

@ -244,7 +244,6 @@ export class WebhookTestService {
case 'reaction': case 'reaction':
return; return;
default: { default: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _exhaustiveAssertion: never = params.type; const _exhaustiveAssertion: never = params.type;
return; return;
} }
@ -327,7 +326,6 @@ export class WebhookTestService {
break; break;
} }
default: { default: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _exhaustiveAssertion: never = params.type; const _exhaustiveAssertion: never = params.type;
return; return;
} }
@ -412,7 +410,7 @@ export class WebhookTestService {
name: user.name, name: user.name,
username: user.username, username: user.username,
host: user.host, host: user.host,
avatarUrl: user.avatarId == null ? null : user.avatarUrl, avatarUrl: (user.avatarId == null ? null : user.avatarUrl) ?? '',
avatarBlurhash: user.avatarId == null ? null : user.avatarBlurhash, avatarBlurhash: user.avatarId == null ? null : user.avatarBlurhash,
avatarDecorations: user.avatarDecorations.map(it => ({ avatarDecorations: user.avatarDecorations.map(it => ({
id: it.id, id: it.id,

View File

@ -49,15 +49,12 @@ export class NoteReactionEntityService implements OnModuleInit {
public async pack( public async pack(
src: MiNoteReaction['id'] | MiNoteReaction, src: MiNoteReaction['id'] | MiNoteReaction,
me?: { id: MiUser['id'] } | null | undefined, me?: { id: MiUser['id'] } | null | undefined,
options?: { options?: object,
withNote: boolean;
},
hints?: { hints?: {
packedUser?: Packed<'UserLite'> packedUser?: Packed<'UserLite'>
}, },
): Promise<Packed<'NoteReaction'>> { ): Promise<Packed<'NoteReaction'>> {
const opts = Object.assign({ const opts = Object.assign({
withNote: false,
}, options); }, options);
const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src }); const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src });
@ -67,9 +64,6 @@ export class NoteReactionEntityService implements OnModuleInit {
createdAt: this.idService.parse(reaction.id).date.toISOString(), createdAt: this.idService.parse(reaction.id).date.toISOString(),
user: hints?.packedUser ?? await this.userEntityService.pack(reaction.user ?? reaction.userId, me), user: hints?.packedUser ?? await this.userEntityService.pack(reaction.user ?? reaction.userId, me),
type: this.reactionService.convertLegacyReaction(reaction.reaction), type: this.reactionService.convertLegacyReaction(reaction.reaction),
...(opts.withNote ? {
note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me),
} : {}),
}; };
} }
@ -77,16 +71,50 @@ export class NoteReactionEntityService implements OnModuleInit {
public async packMany( public async packMany(
reactions: MiNoteReaction[], reactions: MiNoteReaction[],
me?: { id: MiUser['id'] } | null | undefined, me?: { id: MiUser['id'] } | null | undefined,
options?: { options?: object,
withNote: boolean;
},
): Promise<Packed<'NoteReaction'>[]> { ): Promise<Packed<'NoteReaction'>[]> {
const opts = Object.assign({ const opts = Object.assign({
withNote: false,
}, options); }, options);
const _users = reactions.map(({ user, userId }) => user ?? userId); const _users = reactions.map(({ user, userId }) => user ?? userId);
const _userMap = await this.userEntityService.packMany(_users, me) const _userMap = await this.userEntityService.packMany(_users, me)
.then(users => new Map(users.map(u => [u.id, u]))); .then(users => new Map(users.map(u => [u.id, u])));
return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts, { packedUser: _userMap.get(reaction.userId) }))); return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts, { packedUser: _userMap.get(reaction.userId) })));
} }
@bindThis
public async packWithNote(
src: MiNoteReaction['id'] | MiNoteReaction,
me?: { id: MiUser['id'] } | null | undefined,
options?: object,
hints?: {
packedUser?: Packed<'UserLite'>
},
): Promise<Packed<'NoteReactionWithNote'>> {
const opts = Object.assign({
}, options);
const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src });
return {
id: reaction.id,
createdAt: this.idService.parse(reaction.id).date.toISOString(),
user: hints?.packedUser ?? await this.userEntityService.pack(reaction.user ?? reaction.userId, me),
type: this.reactionService.convertLegacyReaction(reaction.reaction),
note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me),
};
}
@bindThis
public async packManyWithNote(
reactions: MiNoteReaction[],
me?: { id: MiUser['id'] } | null | undefined,
options?: object,
): Promise<Packed<'NoteReactionWithNote'>[]> {
const opts = Object.assign({
}, options);
const _users = reactions.map(({ user, userId }) => user ?? userId);
const _userMap = await this.userEntityService.packMany(_users, me)
.then(users => new Map(users.map(u => [u.id, u])));
return Promise.all(reactions.map(reaction => this.packWithNote(reaction, me, opts, { packedUser: _userMap.get(reaction.userId) })));
}
} }

View File

@ -471,8 +471,8 @@ export class UserEntityService implements OnModuleInit {
(profile.followersVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount : (profile.followersVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount :
null; null;
const isModerator = isMe && isDetailed ? this.roleService.isModerator(user) : null; const isModerator = isMe && isDetailed ? this.roleService.isModerator(user) : undefined;
const isAdmin = isMe && isDetailed ? this.roleService.isAdministrator(user) : null; const isAdmin = isMe && isDetailed ? this.roleService.isAdministrator(user) : undefined;
const unreadAnnouncements = isMe && isDetailed ? const unreadAnnouncements = isMe && isDetailed ?
(await this.announcementService.getUnreadAnnouncements(user)).map((announcement) => ({ (await this.announcementService.getUnreadAnnouncements(user)).map((announcement) => ({
createdAt: this.idService.parse(announcement.id).date.toISOString(), createdAt: this.idService.parse(announcement.id).date.toISOString(),
@ -481,6 +481,7 @@ export class UserEntityService implements OnModuleInit {
const notificationsInfo = isMe && isDetailed ? await this.getNotificationsInfo(user.id) : null; const notificationsInfo = isMe && isDetailed ? await this.getNotificationsInfo(user.id) : null;
// TODO: 例えば avatarUrl: true など間違った型を設定しても型エラーにならないのをどうにかする(ジェネリクス使わない方法で実装するしかなさそう?)
const packed = { const packed = {
id: user.id, id: user.id,
name: user.name, name: user.name,

View File

@ -22,7 +22,7 @@ import { packedFollowingSchema } from '@/models/json-schema/following.js';
import { packedMutingSchema } from '@/models/json-schema/muting.js'; import { packedMutingSchema } from '@/models/json-schema/muting.js';
import { packedRenoteMutingSchema } from '@/models/json-schema/renote-muting.js'; import { packedRenoteMutingSchema } from '@/models/json-schema/renote-muting.js';
import { packedBlockingSchema } from '@/models/json-schema/blocking.js'; import { packedBlockingSchema } from '@/models/json-schema/blocking.js';
import { packedNoteReactionSchema } from '@/models/json-schema/note-reaction.js'; import { packedNoteReactionSchema, packedNoteReactionWithNoteSchema } from '@/models/json-schema/note-reaction.js';
import { packedHashtagSchema } from '@/models/json-schema/hashtag.js'; import { packedHashtagSchema } from '@/models/json-schema/hashtag.js';
import { packedInviteCodeSchema } from '@/models/json-schema/invite-code.js'; import { packedInviteCodeSchema } from '@/models/json-schema/invite-code.js';
import { packedPageBlockSchema, packedPageSchema } from '@/models/json-schema/page.js'; import { packedPageBlockSchema, packedPageSchema } from '@/models/json-schema/page.js';
@ -65,6 +65,7 @@ import {
packedMetaDetailedSchema, packedMetaDetailedSchema,
packedMetaLiteSchema, packedMetaLiteSchema,
} from '@/models/json-schema/meta.js'; } from '@/models/json-schema/meta.js';
import { packedUserWebhookSchema } from '@/models/json-schema/user-webhook.js';
import { packedSystemWebhookSchema } from '@/models/json-schema/system-webhook.js'; import { packedSystemWebhookSchema } from '@/models/json-schema/system-webhook.js';
import { packedAbuseReportNotificationRecipientSchema } from '@/models/json-schema/abuse-report-notification-recipient.js'; import { packedAbuseReportNotificationRecipientSchema } from '@/models/json-schema/abuse-report-notification-recipient.js';
import { packedChatMessageSchema, packedChatMessageLiteSchema, packedChatMessageLiteForRoomSchema, packedChatMessageLiteFor1on1Schema } from '@/models/json-schema/chat-message.js'; import { packedChatMessageSchema, packedChatMessageLiteSchema, packedChatMessageLiteForRoomSchema, packedChatMessageLiteFor1on1Schema } from '@/models/json-schema/chat-message.js';
@ -92,6 +93,7 @@ export const refs = {
Note: packedNoteSchema, Note: packedNoteSchema,
NoteDraft: packedNoteDraftSchema, NoteDraft: packedNoteDraftSchema,
NoteReaction: packedNoteReactionSchema, NoteReaction: packedNoteReactionSchema,
NoteReactionWithNote: packedNoteReactionWithNoteSchema,
NoteFavorite: packedNoteFavoriteSchema, NoteFavorite: packedNoteFavoriteSchema,
Notification: packedNotificationSchema, Notification: packedNotificationSchema,
DriveFile: packedDriveFileSchema, DriveFile: packedDriveFileSchema,
@ -133,6 +135,7 @@ export const refs = {
MetaLite: packedMetaLiteSchema, MetaLite: packedMetaLiteSchema,
MetaDetailedOnly: packedMetaDetailedOnlySchema, MetaDetailedOnly: packedMetaDetailedOnlySchema,
MetaDetailed: packedMetaDetailedSchema, MetaDetailed: packedMetaDetailedSchema,
UserWebhook: packedUserWebhookSchema,
SystemWebhook: packedSystemWebhookSchema, SystemWebhook: packedSystemWebhookSchema,
AbuseReportNotificationRecipient: packedAbuseReportNotificationRecipientSchema, AbuseReportNotificationRecipient: packedAbuseReportNotificationRecipientSchema,
ChatMessage: packedChatMessageSchema, ChatMessage: packedChatMessageSchema,

View File

@ -10,6 +10,7 @@ import { MiAccessToken } from './AccessToken.js';
import { MiRole } from './Role.js'; import { MiRole } from './Role.js';
import { MiDriveFile } from './DriveFile.js'; import { MiDriveFile } from './DriveFile.js';
// misskey-js の notificationTypes と同期すべし
export type MiNotification = { export type MiNotification = {
type: 'note'; type: 'note';
id: string; id: string;

View File

@ -69,7 +69,7 @@ export class MiPage {
public eyeCatchingImageId: MiDriveFile['id'] | null; public eyeCatchingImageId: MiDriveFile['id'] | null;
@ManyToOne(type => MiDriveFile, { @ManyToOne(type => MiDriveFile, {
onDelete: 'CASCADE', onDelete: 'SET NULL',
}) })
@JoinColumn() @JoinColumn()
public eyeCatchingImage: MiDriveFile | null; public eyeCatchingImage: MiDriveFile | null;

View File

@ -10,7 +10,6 @@ export const packedNoteReactionSchema = {
type: 'string', type: 'string',
optional: false, nullable: false, optional: false, nullable: false,
format: 'id', format: 'id',
example: 'xxxxxxxxxx',
}, },
createdAt: { createdAt: {
type: 'string', type: 'string',
@ -28,3 +27,33 @@ export const packedNoteReactionSchema = {
}, },
}, },
} as const; } as const;
export const packedNoteReactionWithNoteSchema = {
type: 'object',
properties: {
id: {
type: 'string',
optional: false, nullable: false,
format: 'id',
},
createdAt: {
type: 'string',
optional: false, nullable: false,
format: 'date-time',
},
user: {
type: 'object',
optional: false, nullable: false,
ref: 'UserLite',
},
type: {
type: 'string',
optional: false, nullable: false,
},
note: {
type: 'object',
optional: false, nullable: false,
ref: 'Note',
},
},
} as const;

View File

@ -0,0 +1,55 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { webhookEventTypes } from '@/models/Webhook.js';
export const packedUserWebhookSchema = {
type: 'object',
properties: {
id: {
type: 'string',
format: 'id',
optional: false, nullable: false,
},
userId: {
type: 'string',
format: 'id',
optional: false, nullable: false,
},
name: {
type: 'string',
optional: false, nullable: false,
},
on: {
type: 'array',
items: {
type: 'string',
optional: false, nullable: false,
enum: webhookEventTypes,
},
},
url: {
type: 'string',
optional: false, nullable: false,
},
secret: {
type: 'string',
optional: false, nullable: false,
},
active: {
type: 'boolean',
optional: false, nullable: false,
},
latestSentAt: {
type: 'string',
format: 'date-time',
optional: false, nullable: true,
},
latestStatus: {
type: 'integer',
optional: false, nullable: true,
},
},
} as const;

View File

@ -65,7 +65,7 @@ export const packedUserLiteSchema = {
avatarUrl: { avatarUrl: {
type: 'string', type: 'string',
format: 'url', format: 'url',
nullable: true, optional: false, nullable: false, optional: false,
}, },
avatarBlurhash: { avatarBlurhash: {
type: 'string', type: 'string',
@ -465,11 +465,11 @@ export const packedMeDetailedOnlySchema = {
}, },
isModerator: { isModerator: {
type: 'boolean', type: 'boolean',
nullable: true, optional: false, nullable: false, optional: false,
}, },
isAdmin: { isAdmin: {
type: 'boolean', type: 'boolean',
nullable: true, optional: false, nullable: false, optional: false,
}, },
injectFeaturedNote: { injectFeaturedNote: {
type: 'boolean', type: 'boolean',
@ -591,7 +591,7 @@ export const packedMeDetailedOnlySchema = {
}, },
mutedInstances: { mutedInstances: {
type: 'array', type: 'array',
nullable: true, optional: false, nullable: false, optional: false,
items: { items: {
type: 'string', type: 'string',
nullable: false, optional: false, nullable: false, optional: false,

View File

@ -49,6 +49,34 @@ export const meta = {
type: 'string', type: 'string',
optional: false, nullable: false, optional: false, nullable: false,
}, },
icon: {
type: 'string',
optional: false, nullable: true,
},
display: {
type: 'string',
optional: false, nullable: false,
},
isActive: {
type: 'boolean',
optional: false, nullable: false,
},
forExistingUsers: {
type: 'boolean',
optional: false, nullable: false,
},
silence: {
type: 'boolean',
optional: false, nullable: false,
},
needConfirmationToRead: {
type: 'boolean',
optional: false, nullable: false,
},
userId: {
type: 'string',
optional: false, nullable: true,
},
imageUrl: { imageUrl: {
type: 'string', type: 'string',
optional: false, nullable: true, optional: false, nullable: true,

View File

@ -157,6 +157,22 @@ export const meta = {
type: 'boolean', type: 'boolean',
optional: false, nullable: false, optional: false, nullable: false,
}, },
maybeSensitive: {
type: 'boolean',
optional: false, nullable: false,
},
maybePorn: {
type: 'boolean',
optional: false, nullable: false,
},
requestIp: {
type: 'string',
optional: false, nullable: true,
},
requestHeaders: {
type: 'object',
optional: false, nullable: true,
},
}, },
}, },
} as const; } as const;

View File

@ -223,10 +223,12 @@ export const meta = {
sensitiveMediaDetection: { sensitiveMediaDetection: {
type: 'string', type: 'string',
optional: false, nullable: false, optional: false, nullable: false,
enum: ['none', 'all', 'local', 'remote'],
}, },
sensitiveMediaDetectionSensitivity: { sensitiveMediaDetectionSensitivity: {
type: 'string', type: 'string',
optional: false, nullable: false, optional: false, nullable: false,
enum: ['medium', 'low', 'high', 'veryLow', 'veryHigh'],
}, },
setSensitiveFlagAutomatically: { setSensitiveFlagAutomatically: {
type: 'boolean', type: 'boolean',
@ -473,6 +475,10 @@ export const meta = {
type: 'string', type: 'string',
optional: false, nullable: true, optional: false, nullable: true,
}, },
feedbackUrl: {
type: 'string',
optional: false, nullable: true,
},
summalyProxy: { summalyProxy: {
type: 'string', type: 'string',
optional: false, nullable: true, optional: false, nullable: true,

View File

@ -73,8 +73,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
updatedAt: new Date(), updatedAt: new Date(),
...Object.fromEntries( ...Object.fromEntries(
Object.entries(ps).filter( Object.entries(ps).filter(
([key, val]) => (key !== 'flashId') && Object.hasOwn(paramDef.properties, key) ([key, val]) => (key !== 'flashId') && Object.hasOwn(paramDef.properties, key),
) ),
), ),
}); });
}); });

View File

@ -46,6 +46,14 @@ export const meta = {
type: 'string', type: 'string',
}, },
}, },
iconUrl: {
type: 'string',
optional: true, nullable: true,
},
description: {
type: 'string',
optional: true, nullable: true,
},
}, },
}, },
}, },
@ -88,6 +96,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
createdAt: this.idService.parse(token.id).date.toISOString(), createdAt: this.idService.parse(token.id).date.toISOString(),
lastUsedAt: token.lastUsedAt?.toISOString(), lastUsedAt: token.lastUsedAt?.toISOString(),
permission: token.app ? token.app.permission : token.permission, permission: token.app ? token.app.permission : token.permission,
iconUrl: token.iconUrl,
description: token.description ?? token.app?.description ?? null,
}))); })));
}); });
} }

View File

@ -21,29 +21,7 @@ export const meta = {
type: 'array', type: 'array',
items: { items: {
type: 'object', type: 'object',
properties: { ref: 'UserWebhook',
id: {
type: 'string',
format: 'misskey:id',
},
userId: {
type: 'string',
format: 'misskey:id',
},
name: { type: 'string' },
on: {
type: 'array',
items: {
type: 'string',
enum: webhookEventTypes,
},
},
url: { type: 'string' },
secret: { type: 'string' },
active: { type: 'boolean' },
latestSentAt: { type: 'string', format: 'date-time', nullable: true },
latestStatus: { type: 'integer', nullable: true },
},
}, },
}, },
} as const; } as const;
@ -65,8 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
userId: me.id, userId: me.id,
}); });
return webhooks.map(webhook => ( return webhooks.map(webhook => ({
{
id: webhook.id, id: webhook.id,
userId: webhook.userId, userId: webhook.userId,
name: webhook.name, name: webhook.name,
@ -76,8 +53,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
active: webhook.active, active: webhook.active,
latestSentAt: webhook.latestSentAt ? webhook.latestSentAt.toISOString() : null, latestSentAt: webhook.latestSentAt ? webhook.latestSentAt.toISOString() : null,
latestStatus: webhook.latestStatus, latestStatus: webhook.latestStatus,
} }));
));
}); });
} }
} }

View File

@ -28,29 +28,7 @@ export const meta = {
res: { res: {
type: 'object', type: 'object',
properties: { ref: 'UserWebhook',
id: {
type: 'string',
format: 'misskey:id',
},
userId: {
type: 'string',
format: 'misskey:id',
},
name: { type: 'string' },
on: {
type: 'array',
items: {
type: 'string',
enum: webhookEventTypes,
},
},
url: { type: 'string' },
secret: { type: 'string' },
active: { type: 'boolean' },
latestSentAt: { type: 'string', format: 'date-time', nullable: true },
latestStatus: { type: 'integer', nullable: true },
},
}, },
} as const; } as const;

View File

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

View File

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

View File

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

View File

@ -23,6 +23,16 @@ export const meta = {
type: 'object', type: 'object',
optional: false, nullable: false, optional: false, nullable: false,
ref: 'UserList', ref: 'UserList',
properties: {
likedCount: {
type: 'number',
optional: true, nullable: false,
},
isLiked: {
type: 'boolean',
optional: true, nullable: false,
},
},
}, },
errors: { errors: {

View File

@ -28,7 +28,7 @@ export const meta = {
items: { items: {
type: 'object', type: 'object',
optional: false, nullable: false, optional: false, nullable: false,
ref: 'NoteReaction', ref: 'NoteReactionWithNote',
}, },
}, },
@ -120,7 +120,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
return true; return true;
}); });
return await this.noteReactionEntityService.packMany(reactions, me, { withNote: true }); return await this.noteReactionEntityService.packManyWithNote(reactions, me);
}); });
} }
} }

View File

@ -16,7 +16,7 @@
"@rollup/pluginutils": "5.2.0", "@rollup/pluginutils": "5.2.0",
"@twemoji/parser": "16.0.0", "@twemoji/parser": "16.0.0",
"@vitejs/plugin-vue": "6.0.1", "@vitejs/plugin-vue": "6.0.1",
"@vue/compiler-sfc": "3.5.18", "@vue/compiler-sfc": "3.5.19",
"astring": "1.9.0", "astring": "1.9.0",
"buraha": "0.0.1", "buraha": "0.0.1",
"estree-walker": "3.0.3", "estree-walker": "3.0.3",
@ -26,16 +26,16 @@
"mfm-js": "0.25.0", "mfm-js": "0.25.0",
"misskey-js": "workspace:*", "misskey-js": "workspace:*",
"punycode.js": "2.3.1", "punycode.js": "2.3.1",
"rollup": "4.46.2", "rollup": "4.48.0",
"sass": "1.89.2", "sass": "1.90.0",
"shiki": "3.9.1", "shiki": "3.11.0",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
"tsc-alias": "1.8.16", "tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0", "tsconfig-paths": "4.2.0",
"typescript": "5.9.2", "typescript": "5.9.2",
"uuid": "11.1.0", "uuid": "11.1.0",
"vite": "7.0.6", "vite": "7.1.3",
"vue": "3.5.18" "vue": "3.5.19"
}, },
"devDependencies": { "devDependencies": {
"@misskey-dev/summaly": "5.2.3", "@misskey-dev/summaly": "5.2.3",
@ -43,14 +43,14 @@
"@testing-library/vue": "8.1.0", "@testing-library/vue": "8.1.0",
"@types/estree": "1.0.8", "@types/estree": "1.0.8",
"@types/micromatch": "4.0.9", "@types/micromatch": "4.0.9",
"@types/node": "22.17.0", "@types/node": "22.17.2",
"@types/punycode.js": "npm:@types/punycode@2.1.4", "@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/tinycolor2": "1.4.6", "@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1", "@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.38.0", "@typescript-eslint/eslint-plugin": "8.40.0",
"@typescript-eslint/parser": "8.38.0", "@typescript-eslint/parser": "8.40.0",
"@vitest/coverage-v8": "3.2.4", "@vitest/coverage-v8": "3.2.4",
"@vue/runtime-core": "3.5.18", "@vue/runtime-core": "3.5.19",
"acorn": "8.15.0", "acorn": "8.15.0",
"cross-env": "10.0.0", "cross-env": "10.0.0",
"eslint-plugin-import": "2.32.0", "eslint-plugin-import": "2.32.0",
@ -59,14 +59,14 @@
"happy-dom": "18.0.1", "happy-dom": "18.0.1",
"intersection-observer": "0.12.2", "intersection-observer": "0.12.2",
"micromatch": "4.0.8", "micromatch": "4.0.8",
"msw": "2.10.4", "msw": "2.10.5",
"nodemon": "3.1.10", "nodemon": "3.1.10",
"prettier": "3.6.2", "prettier": "3.6.2",
"start-server-and-test": "2.0.12", "start-server-and-test": "2.0.13",
"tsx": "4.20.3", "tsx": "4.20.4",
"vite-plugin-turbosnap": "1.0.3", "vite-plugin-turbosnap": "1.0.3",
"vue-component-type-helpers": "3.0.5", "vue-component-type-helpers": "3.0.6",
"vue-eslint-parser": "10.2.0", "vue-eslint-parser": "10.2.0",
"vue-tsc": "3.0.5" "vue-tsc": "3.0.6"
} }
} }

View File

@ -3,6 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
// TODO: (可能な部分を)sharedに抽出して frontend と共通化
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
import lightTheme from '@@/themes/_light.json5'; import lightTheme from '@@/themes/_light.json5';
import darkTheme from '@@/themes/_dark.json5'; import darkTheme from '@@/themes/_dark.json5';

View File

@ -54,68 +54,6 @@ https://github.com/sindresorhus/file-type/blob/main/core.js
https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
*/ */
export const notificationTypes = [
'note',
'follow',
'mention',
'reply',
'renote',
'quote',
'reaction',
'pollEnded',
'receiveFollowRequest',
'followRequestAccepted',
'roleAssigned',
'chatRoomInvitationReceived',
'achievementEarned',
'exportCompleted',
'login',
'createToken',
'test',
'app',
] as const;
export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const;
export const ROLE_POLICIES = [
'gtlAvailable',
'ltlAvailable',
'canPublicNote',
'mentionLimit',
'canInvite',
'inviteLimit',
'inviteLimitCycle',
'inviteExpirationTime',
'canManageCustomEmojis',
'canManageAvatarDecorations',
'canSearchNotes',
'canSearchUsers',
'canUseTranslator',
'canHideAds',
'driveCapacityMb',
'maxFileSizeMb',
'alwaysMarkNsfw',
'canUpdateBioMedia',
'pinLimit',
'antennaLimit',
'wordMuteLimit',
'webhookLimit',
'clipLimit',
'noteEachClipsLimit',
'userListLimit',
'userEachUserListsLimit',
'rateLimitFactor',
'avatarDecorationLimit',
'canImportAntennas',
'canImportBlocking',
'canImportFollowing',
'canImportMuting',
'canImportUserLists',
'chatAvailability',
'uploadableFileTypes',
'noteDraftLimit',
'watermarkAvailable',
] as const;
export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime']; export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime'];
export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = { export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = {
tada: ['speed=', 'delay='], tada: ['speed=', 'delay='],

View File

@ -21,10 +21,10 @@
"lint": "pnpm typecheck && pnpm eslint" "lint": "pnpm typecheck && pnpm eslint"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "22.17.0", "@types/node": "22.17.2",
"@typescript-eslint/eslint-plugin": "8.38.0", "@typescript-eslint/eslint-plugin": "8.40.0",
"@typescript-eslint/parser": "8.38.0", "@typescript-eslint/parser": "8.40.0",
"esbuild": "0.25.8", "esbuild": "0.25.9",
"eslint-plugin-vue": "10.4.0", "eslint-plugin-vue": "10.4.0",
"nodemon": "3.1.10", "nodemon": "3.1.10",
"typescript": "5.9.2", "typescript": "5.9.2",
@ -35,6 +35,6 @@
], ],
"dependencies": { "dependencies": {
"misskey-js": "workspace:*", "misskey-js": "workspace:*",
"vue": "3.5.18" "vue": "3.5.19"
} }
} }

View File

@ -6,7 +6,7 @@
import { HttpResponse, http } from 'msw'; import { HttpResponse, http } from 'msw';
import type { DefaultBodyType, HttpResponseResolver, JsonBodyType, PathParams } from 'msw'; import type { DefaultBodyType, HttpResponseResolver, JsonBodyType, PathParams } from 'msw';
import seedrandom from 'seedrandom'; import seedrandom from 'seedrandom';
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
function getChartArray(seed: string, limit: number, option?: { accumulate?: boolean, mul?: number }): number[] { function getChartArray(seed: string, limit: number, option?: { accumulate?: boolean, mul?: number }): number[] {
const rng = seedrandom(seed); const rng = seedrandom(seed);

View File

@ -127,7 +127,7 @@ export function galleryPost(isSensitive = false) {
} }
} }
export function file(isSensitive = false) { export function file(isSensitive = false): entities.DriveFile {
return { return {
id: 'somefileid', id: 'somefileid',
createdAt: '2016-12-28T22:49:51.000Z', createdAt: '2016-12-28T22:49:51.000Z',
@ -207,6 +207,7 @@ export function federationInstance(): entities.FederationInstance {
isSuspended: false, isSuspended: false,
suspensionState: 'none', suspensionState: 'none',
isBlocked: false, isBlocked: false,
isMediaSilenced: false,
softwareName: 'misskey', softwareName: 'misskey',
softwareVersion: '2024.5.0', softwareVersion: '2024.5.0',
openRegistrations: false, openRegistrations: false,
@ -311,6 +312,8 @@ export function userDetailed(id = 'someuserid', username = 'miskist', host: enti
alsoKnownAs: null, alsoKnownAs: null,
notify: 'none', notify: 'none',
memo: null, memo: null,
canChat: true,
chatScope: 'everyone',
}; };
} }
@ -378,6 +381,7 @@ export function role(params: {
asBadge: params.asBadge ?? true, asBadge: params.asBadge ?? true,
canEditMembersByModerator: params.canEditMembersByModerator ?? false, canEditMembersByModerator: params.canEditMembersByModerator ?? false,
usersCount: params.usersCount ?? 10, usersCount: params.usersCount ?? 10,
preserveAssignmentOnMoveAccount: false,
condFormula: { condFormula: {
id: '', id: '',
type: 'or', type: 'or',

View File

@ -42,7 +42,7 @@
"prefix": "storyimplevent", "prefix": "storyimplevent",
"body": [ "body": [
"/* eslint-disable @typescript-eslint/explicit-function-return-type */", "/* eslint-disable @typescript-eslint/explicit-function-return-type */",
"import { action } from '@storybook/addon-actions';", "import { action } from 'storybook/actions';",
"import { StoryObj } from '@storybook/vue3';", "import { StoryObj } from '@storybook/vue3';",
"import $1 from './$1.vue';", "import $1 from './$1.vue';",
"export const Default = {", "export const Default = {",

View File

@ -8,7 +8,7 @@
import { parse as vueSfcParse } from 'vue/compiler-sfc'; import { parse as vueSfcParse } from 'vue/compiler-sfc';
import { import {
createLogger, createLogger,
EnvironmentModuleGraph, type EnvironmentModuleGraph,
type LogErrorOptions, type LogErrorOptions,
type LogOptions, type LogOptions,
normalizePath, normalizePath,

View File

@ -24,14 +24,14 @@
"@rollup/plugin-json": "6.1.0", "@rollup/plugin-json": "6.1.0",
"@rollup/plugin-replace": "6.0.2", "@rollup/plugin-replace": "6.0.2",
"@rollup/pluginutils": "5.2.0", "@rollup/pluginutils": "5.2.0",
"@sentry/vue": "10.0.0", "@sentry/vue": "10.5.0",
"@syuilo/aiscript": "1.1.0", "@syuilo/aiscript": "1.1.0",
"@syuilo/aiscript-0-19-0": "npm:@syuilo/aiscript@^0.19.0", "@syuilo/aiscript-0-19-0": "npm:@syuilo/aiscript@^0.19.0",
"@twemoji/parser": "16.0.0", "@twemoji/parser": "16.0.0",
"@vitejs/plugin-vue": "6.0.1", "@vitejs/plugin-vue": "6.0.1",
"@vue/compiler-sfc": "3.5.18", "@vue/compiler-sfc": "3.5.19",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15", "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15",
"analytics": "0.8.16", "analytics": "0.8.19",
"astring": "1.9.0", "astring": "1.9.0",
"broadcast-channel": "7.1.0", "broadcast-channel": "7.1.0",
"buraha": "0.0.1", "buraha": "0.0.1",
@ -55,7 +55,7 @@
"ios-haptics": "0.1.0", "ios-haptics": "0.1.0",
"is-file-animated": "1.0.2", "is-file-animated": "1.0.2",
"json5": "2.2.3", "json5": "2.2.3",
"magic-string": "0.30.17", "magic-string": "0.30.18",
"matter-js": "0.20.0", "matter-js": "0.20.0",
"mfm-js": "0.25.0", "mfm-js": "0.25.0",
"misskey-bubble-game": "workspace:*", "misskey-bubble-game": "workspace:*",
@ -63,10 +63,10 @@
"misskey-reversi": "workspace:*", "misskey-reversi": "workspace:*",
"photoswipe": "5.4.4", "photoswipe": "5.4.4",
"punycode.js": "2.3.1", "punycode.js": "2.3.1",
"rollup": "4.46.2", "rollup": "4.48.0",
"sanitize-html": "2.17.0", "sanitize-html": "2.17.0",
"sass": "1.89.2", "sass": "1.90.0",
"shiki": "3.9.1", "shiki": "3.11.0",
"strict-event-emitter-types": "2.0.0", "strict-event-emitter-types": "2.0.0",
"textarea-caret": "3.1.0", "textarea-caret": "3.1.0",
"three": "0.179.1", "three": "0.179.1",
@ -76,17 +76,16 @@
"tsconfig-paths": "4.2.0", "tsconfig-paths": "4.2.0",
"typescript": "5.9.2", "typescript": "5.9.2",
"v-code-diff": "1.13.1", "v-code-diff": "1.13.1",
"vite": "7.0.6", "vite": "7.1.3",
"vue": "3.5.18", "vue": "3.5.19",
"vuedraggable": "next", "vuedraggable": "next",
"wanakana": "5.3.1" "wanakana": "5.3.1"
}, },
"devDependencies": { "devDependencies": {
"@misskey-dev/summaly": "5.2.3", "@misskey-dev/summaly": "5.2.3",
"@storybook/addon-actions": "9.0.8",
"@storybook/addon-essentials": "8.6.14", "@storybook/addon-essentials": "8.6.14",
"@storybook/addon-interactions": "8.6.14", "@storybook/addon-interactions": "8.6.14",
"@storybook/addon-links": "9.1.0", "@storybook/addon-links": "9.1.3",
"@storybook/addon-mdx-gfm": "8.6.14", "@storybook/addon-mdx-gfm": "8.6.14",
"@storybook/addon-storysource": "8.6.14", "@storybook/addon-storysource": "8.6.14",
"@storybook/blocks": "8.6.14", "@storybook/blocks": "8.6.14",
@ -94,34 +93,34 @@
"@storybook/core-events": "8.6.14", "@storybook/core-events": "8.6.14",
"@storybook/manager-api": "8.6.14", "@storybook/manager-api": "8.6.14",
"@storybook/preview-api": "8.6.14", "@storybook/preview-api": "8.6.14",
"@storybook/react": "9.1.0", "@storybook/react": "9.1.3",
"@storybook/react-vite": "9.1.0", "@storybook/react-vite": "9.1.3",
"@storybook/test": "8.6.14", "@storybook/test": "8.6.14",
"@storybook/theming": "8.6.14", "@storybook/theming": "8.6.14",
"@storybook/types": "8.6.14", "@storybook/types": "8.6.14",
"@storybook/vue3": "9.1.0", "@storybook/vue3": "9.1.3",
"@storybook/vue3-vite": "9.1.0", "@storybook/vue3-vite": "9.1.3",
"@tabler/icons-webfont": "3.34.1", "@tabler/icons-webfont": "3.34.1",
"@testing-library/vue": "8.1.0", "@testing-library/vue": "8.1.0",
"@types/canvas-confetti": "1.9.0", "@types/canvas-confetti": "1.9.0",
"@types/estree": "1.0.8", "@types/estree": "1.0.8",
"@types/matter-js": "0.19.8", "@types/matter-js": "0.20.0",
"@types/micromatch": "4.0.9", "@types/micromatch": "4.0.9",
"@types/node": "22.17.0", "@types/node": "22.17.2",
"@types/punycode.js": "npm:@types/punycode@2.1.4", "@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/sanitize-html": "2.16.0", "@types/sanitize-html": "2.16.0",
"@types/seedrandom": "3.0.8", "@types/seedrandom": "3.0.8",
"@types/throttle-debounce": "5.0.2", "@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6", "@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1", "@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.38.0", "@typescript-eslint/eslint-plugin": "8.40.0",
"@typescript-eslint/parser": "8.38.0", "@typescript-eslint/parser": "8.40.0",
"@vitest/coverage-v8": "3.2.4", "@vitest/coverage-v8": "3.2.4",
"@vue/compiler-core": "3.5.18", "@vue/compiler-core": "3.5.19",
"@vue/runtime-core": "3.5.18", "@vue/runtime-core": "3.5.19",
"acorn": "8.15.0", "acorn": "8.15.0",
"cross-env": "10.0.0", "cross-env": "10.0.0",
"cypress": "14.5.3", "cypress": "14.5.4",
"eslint-plugin-import": "2.32.0", "eslint-plugin-import": "2.32.0",
"eslint-plugin-vue": "10.4.0", "eslint-plugin-vue": "10.4.0",
"fast-glob": "3.3.3", "fast-glob": "3.3.3",
@ -129,22 +128,22 @@
"intersection-observer": "0.12.2", "intersection-observer": "0.12.2",
"micromatch": "4.0.8", "micromatch": "4.0.8",
"minimatch": "10.0.3", "minimatch": "10.0.3",
"msw": "2.10.4", "msw": "2.10.5",
"msw-storybook-addon": "2.0.5", "msw-storybook-addon": "2.0.5",
"nodemon": "3.1.10", "nodemon": "3.1.10",
"prettier": "3.6.2", "prettier": "3.6.2",
"react": "19.1.1", "react": "19.1.1",
"react-dom": "19.1.1", "react-dom": "19.1.1",
"seedrandom": "3.0.5", "seedrandom": "3.0.5",
"start-server-and-test": "2.0.12", "start-server-and-test": "2.0.13",
"storybook": "9.1.0", "storybook": "9.1.3",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"tsx": "4.20.3", "tsx": "4.20.4",
"vite-plugin-turbosnap": "1.0.3", "vite-plugin-turbosnap": "1.0.3",
"vitest": "3.2.4", "vitest": "3.2.4",
"vitest-fetch-mock": "0.4.5", "vitest-fetch-mock": "0.4.5",
"vue-component-type-helpers": "3.0.5", "vue-component-type-helpers": "3.0.6",
"vue-eslint-parser": "10.2.0", "vue-eslint-parser": "10.2.0",
"vue-tsc": "3.0.5" "vue-tsc": "3.0.6"
} }
} }

View File

@ -23,7 +23,7 @@ export async function getAccounts(): Promise<{
host: string; host: string;
id: Misskey.entities.User['id']; id: Misskey.entities.User['id'];
username: Misskey.entities.User['username']; username: Misskey.entities.User['username'];
user?: Misskey.entities.User | null; user?: Misskey.entities.MeDetailed | null;
token: string | null; token: string | null;
}[]> { }[]> {
const tokens = store.s.accountTokens; const tokens = store.s.accountTokens;
@ -38,7 +38,7 @@ export async function getAccounts(): Promise<{
})); }));
} }
async function addAccount(host: string, user: Misskey.entities.User, token: AccountWithToken['token']) { async function addAccount(host: string, user: Misskey.entities.MeDetailed, token: AccountWithToken['token']) {
if (!prefer.s.accounts.some(x => x[0] === host && x[1].id === user.id)) { if (!prefer.s.accounts.some(x => x[0] === host && x[1].id === user.id)) {
store.set('accountTokens', { ...store.s.accountTokens, [host + '/' + user.id]: token }); store.set('accountTokens', { ...store.s.accountTokens, [host + '/' + user.id]: token });
store.set('accountInfos', { ...store.s.accountInfos, [host + '/' + user.id]: user }); store.set('accountInfos', { ...store.s.accountInfos, [host + '/' + user.id]: user });
@ -149,9 +149,10 @@ export function updateCurrentAccountPartial(accountData: Partial<Misskey.entitie
export async function refreshCurrentAccount() { export async function refreshCurrentAccount() {
if (!$i) return; if (!$i) return;
const me = $i;
return fetchAccount($i.token, $i.id).then(updateCurrentAccount).catch(reason => { return fetchAccount($i.token, $i.id).then(updateCurrentAccount).catch(reason => {
if (reason === isAccountDeleted) { if (reason === isAccountDeleted) {
removeAccount(host, $i.id); removeAccount(host, me.id);
if (Object.keys(store.s.accountTokens).length > 0) { if (Object.keys(store.s.accountTokens).length > 0) {
login(Object.values(store.s.accountTokens)[0]); login(Object.values(store.s.accountTokens)[0]);
} else { } else {
@ -214,19 +215,37 @@ export async function openAccountMenu(opts: {
includeCurrentAccount?: boolean; includeCurrentAccount?: boolean;
withExtraOperation: boolean; withExtraOperation: boolean;
active?: Misskey.entities.User['id']; active?: Misskey.entities.User['id'];
onChoose?: (account: Misskey.entities.User) => void; onChoose?: (account: Misskey.entities.MeDetailed) => void;
}, ev: MouseEvent) { }, ev: MouseEvent) {
if (!$i) return; if (!$i) return;
const me = $i;
function createItem(host: string, id: Misskey.entities.User['id'], username: Misskey.entities.User['username'], account: Misskey.entities.User | null | undefined, token: string): MenuItem { const callback = opts.onChoose;
function createItem(host: string, id: Misskey.entities.User['id'], username: Misskey.entities.User['username'], account: Misskey.entities.MeDetailed | null | undefined, token: string | null): MenuItem {
if (account) { if (account) {
return { return {
type: 'user' as const, type: 'user' as const,
user: account, user: account,
active: opts.active != null ? opts.active === id : false, active: opts.active != null ? opts.active === id : false,
action: async () => { action: async () => {
if (opts.onChoose) { if (callback) {
opts.onChoose(account); callback(account);
} else {
switchAccount(host, id);
}
},
};
} else if (token != null) {
return {
type: 'button' as const,
text: username,
active: opts.active != null ? opts.active === id : false,
action: async () => {
if (callback) {
fetchAccount(token, id).then(account => {
callback(account);
});
} else { } else {
switchAccount(host, id); switchAccount(host, id);
} }
@ -238,13 +257,7 @@ export async function openAccountMenu(opts: {
text: username, text: username,
active: opts.active != null ? opts.active === id : false, active: opts.active != null ? opts.active === id : false,
action: async () => { action: async () => {
if (opts.onChoose) { // TODO
fetchAccount(token, id).then(account => {
opts.onChoose(account);
});
} else {
switchAccount(host, id);
}
}, },
}; };
} }
@ -253,7 +266,7 @@ export async function openAccountMenu(opts: {
const menuItems: MenuItem[] = []; const menuItems: MenuItem[] = [];
// TODO: $iのホストも比較したいけど通常null // TODO: $iのホストも比較したいけど通常null
const accountItems = (await getAccounts().then(accounts => accounts.filter(x => x.id !== $i.id))).map(a => createItem(a.host, a.id, a.username, a.user, a.token)); const accountItems = (await getAccounts().then(accounts => accounts.filter(x => x.id !== me.id))).map(a => createItem(a.host, a.id, a.username, a.user, a.token));
if (opts.withExtraOperation) { if (opts.withExtraOperation) {
menuItems.push({ menuItems.push({

View File

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

View File

@ -4,11 +4,11 @@
*/ */
import { utils, values } from '@syuilo/aiscript'; import { utils, values } from '@syuilo/aiscript';
import { genId } from '@/utility/id.js';
import { ref } from 'vue'; import { ref } from 'vue';
import type { Ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { assertStringAndIsIn } from './common.js'; import { assertStringAndIsIn } from './common.js';
import type { Ref } from 'vue';
import { genId } from '@/utility/id.js';
const ALIGNS = ['left', 'center', 'right'] as const; const ALIGNS = ['left', 'center', 'right'] as const;
const FONTS = ['serif', 'sans-serif', 'monospace'] as const; const FONTS = ['serif', 'sans-serif', 'monospace'] as const;
@ -21,16 +21,15 @@ type BorderStyle = (typeof BORDER_STYLES)[number];
export type AsUiComponentBase = { export type AsUiComponentBase = {
id: string; id: string;
hidden?: boolean; hidden?: boolean;
children?: AsUiComponent['id'][];
}; };
export type AsUiRoot = AsUiComponentBase & { export type AsUiRoot = AsUiComponentBase & {
type: 'root'; type: 'root';
children: AsUiComponent['id'][];
}; };
export type AsUiContainer = AsUiComponentBase & { export type AsUiContainer = AsUiComponentBase & {
type: 'container'; type: 'container';
children?: AsUiComponent['id'][];
align?: Align; align?: Align;
bgColor?: string; bgColor?: string;
fgColor?: string; fgColor?: string;
@ -123,7 +122,6 @@ export type AsUiSelect = AsUiComponentBase & {
export type AsUiFolder = AsUiComponentBase & { export type AsUiFolder = AsUiComponentBase & {
type: 'folder'; type: 'folder';
children?: AsUiComponent['id'][];
title?: string; title?: string;
opened?: boolean; opened?: boolean;
}; };

View File

@ -368,11 +368,6 @@ export async function mainBoot() {
}); });
}); });
main.on('unreadAntenna', () => {
updateCurrentAccountPartial({ hasUnreadAntenna: true });
sound.playMisskeySfx('antenna');
});
main.on('newChatMessage', () => { main.on('newChatMessage', () => {
updateCurrentAccountPartial({ hasUnreadChatMessages: true }); updateCurrentAccountPartial({ hasUnreadChatMessages: true });
sound.playMisskeySfx('chatMessage'); sound.playMisskeySfx('chatMessage');

View File

@ -3,13 +3,12 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
/* eslint-disable @typescript-eslint/explicit-function-return-type */ import { action } from 'storybook/actions';
import { action } from '@storybook/addon-actions';
import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw'; import { HttpResponse, http } from 'msw';
import { userDetailed } from '../../.storybook/fakes.js'; import { userDetailed } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js'; import { commonHandlers } from '../../.storybook/mocks.js';
import MkAbuseReportWindow from './MkAbuseReportWindow.vue'; import MkAbuseReportWindow from './MkAbuseReportWindow.vue';
import type { StoryObj } from '@storybook/vue3';
export const Default = { export const Default = {
render(args) { render(args) {
return { return {

View File

@ -4,7 +4,7 @@
*/ */
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3'; import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw'; import { HttpResponse, http } from 'msw';
import { commonHandlers } from '../../.storybook/mocks.js'; import { commonHandlers } from '../../.storybook/mocks.js';

View File

@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
[$style.iconFrame_platinum]: ACHIEVEMENT_BADGES[achievement.name].frame === 'platinum', [$style.iconFrame_platinum]: ACHIEVEMENT_BADGES[achievement.name].frame === 'platinum',
}]" }]"
> >
<div :class="[$style.iconInner]" :style="{ background: ACHIEVEMENT_BADGES[achievement.name].bg }"> <div :class="[$style.iconInner]" :style="{ background: ACHIEVEMENT_BADGES[achievement.name].bg ?? '' }">
<img :class="$style.iconImg" :src="ACHIEVEMENT_BADGES[achievement.name].img"> <img :class="$style.iconImg" :src="ACHIEVEMENT_BADGES[achievement.name].img">
</div> </div>
</div> </div>
@ -61,8 +61,8 @@ import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/utili
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
user: Misskey.entities.User; user: Misskey.entities.User;
withLocked: boolean; withLocked?: boolean;
withDescription: boolean; withDescription?: boolean;
}>(), { }>(), {
withLocked: true, withLocked: true,
withDescription: true, withDescription: true,
@ -71,7 +71,7 @@ const props = withDefaults(defineProps<{
const achievements = ref<Misskey.entities.UsersAchievementsResponse | null>(null); const achievements = ref<Misskey.entities.UsersAchievementsResponse | null>(null);
const lockedAchievements = computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements.value ?? []).some(a => a.name === x))); const lockedAchievements = computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements.value ?? []).some(a => a.name === x)));
function fetch() { function _fetch_() {
misskeyApi('users/achievements', { userId: props.user.id }).then(res => { misskeyApi('users/achievements', { userId: props.user.id }).then(res => {
achievements.value = []; achievements.value = [];
for (const t of ACHIEVEMENT_TYPES) { for (const t of ACHIEVEMENT_TYPES) {
@ -84,11 +84,11 @@ function fetch() {
function clickHere() { function clickHere() {
claimAchievement('clickedClickHere'); claimAchievement('clickedClickHere');
fetch(); _fetch_();
} }
onMounted(() => { onMounted(() => {
fetch(); _fetch_();
}); });
</script> </script>

View File

@ -44,7 +44,7 @@ function initShaderProgram(gl: WebGLRenderingContext, vsSource: string, fsSource
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
const shaderProgram = gl.createProgram(); const shaderProgram = gl.createProgram();
if (shaderProgram == null || vertexShader == null || fragmentShader == null) return null; if (vertexShader == null || fragmentShader == null) return null;
gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader); gl.attachShader(shaderProgram, fragmentShader);
@ -71,8 +71,10 @@ onMounted(() => {
canvas.width = width; canvas.width = width;
canvas.height = height; canvas.height = height;
const gl = canvas.getContext('webgl', { premultipliedAlpha: true }); const maybeGl = canvas.getContext('webgl', { premultipliedAlpha: true });
if (gl == null) return; if (maybeGl == null) return;
const gl = maybeGl;
gl.clearColor(0.0, 0.0, 0.0, 0.0); gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT); gl.clear(gl.COLOR_BUFFER_BIT);
@ -229,8 +231,8 @@ onMounted(() => {
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.DYNAMIC_DRAW); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.DYNAMIC_DRAW);
if (isChromatic()) { if (isChromatic()) {
gl!.uniform1f(u_time, 0); gl.uniform1f(u_time, 0);
gl!.drawArrays(gl!.TRIANGLE_STRIP, 0, 4); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
} else { } else {
function render(timeStamp: number) { function render(timeStamp: number) {
let sizeChanged = false; let sizeChanged = false;
@ -249,8 +251,8 @@ onMounted(() => {
gl.viewport(0, 0, width, height); gl.viewport(0, 0, width, height);
} }
gl!.uniform1f(u_time, timeStamp); gl.uniform1f(u_time, timeStamp);
gl!.drawArrays(gl!.TRIANGLE_STRIP, 0, 4); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
handle = window.requestAnimationFrame(render); handle = window.requestAnimationFrame(render);
} }
@ -263,6 +265,8 @@ onUnmounted(() => {
if (handle) { if (handle) {
window.cancelAnimationFrame(handle); window.cancelAnimationFrame(handle);
} }
// TODO: WebGL
}); });
</script> </script>

View File

@ -4,7 +4,7 @@
*/ */
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3'; import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw'; import { HttpResponse, http } from 'msw';
import { commonHandlers } from '../../.storybook/mocks.js'; import { commonHandlers } from '../../.storybook/mocks.js';

View File

@ -4,7 +4,7 @@
*/ */
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3'; import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw'; import { HttpResponse, http } from 'msw';
import { commonHandlers } from '../../.storybook/mocks.js'; import { commonHandlers } from '../../.storybook/mocks.js';

View File

@ -4,7 +4,7 @@
*/ */
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3'; import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw'; import { HttpResponse, http } from 'msw';
import { commonHandlers } from '../../.storybook/mocks.js'; import { commonHandlers } from '../../.storybook/mocks.js';

View File

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

View File

@ -4,7 +4,7 @@
*/ */
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import { expect, userEvent, waitFor, within } from '@storybook/test'; import { expect, userEvent, waitFor, within } from '@storybook/test';
import type { StoryObj } from '@storybook/vue3'; import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw'; import { HttpResponse, http } from 'msw';

View File

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

View File

@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable import/no-default-export */ /* eslint-disable import/no-default-export */
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3'; import type { StoryObj } from '@storybook/vue3';
import MkButton from './MkButton.vue'; import MkButton from './MkButton.vue';
export const Default = { export const Default = {

View File

@ -4,7 +4,7 @@
*/ */
import { HttpResponse, http } from 'msw'; import { HttpResponse, http } from 'msw';
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import { expect, userEvent, within } from '@storybook/test'; import { expect, userEvent, within } from '@storybook/test';
import { channel } from '../../.storybook/fakes.js'; import { channel } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js'; import { commonHandlers } from '../../.storybook/mocks.js';

View File

@ -3,14 +3,13 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable import/no-default-export */
import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw'; import { HttpResponse, http } from 'msw';
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import { channel } from '../../.storybook/fakes.js'; import { channel } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js'; import { commonHandlers } from '../../.storybook/mocks.js';
import MkChannelList from './MkChannelList.vue'; import MkChannelList from './MkChannelList.vue';
import type { StoryObj } from '@storybook/vue3';
import { Paginator } from '@/utility/paginator.js';
export const Default = { export const Default = {
render(args) { render(args) {
return { return {
@ -33,10 +32,7 @@ export const Default = {
}; };
}, },
args: { args: {
pagination: { paginator: new Paginator('channels/search', {}),
endpoint: 'channels/search',
limit: 10,
},
}, },
parameters: { parameters: {
chromatic: { chromatic: {

View File

@ -589,7 +589,10 @@ const fetchDriveFilesChart = async (): Promise<typeof chartData> => {
}; };
const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => { const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); const host = props.args?.host;
if (host == null) return { series: [] };
const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return { return {
series: [{ series: [{
name: 'In', name: 'In',
@ -611,7 +614,10 @@ const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
}; };
const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData> => { const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData> => {
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); const host = props.args?.host;
if (host == null) return { series: [] };
const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return { return {
series: [{ series: [{
name: 'Users', name: 'Users',
@ -626,7 +632,10 @@ const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData
}; };
const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData> => { const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData> => {
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); const host = props.args?.host;
if (host == null) return { series: [] };
const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return { return {
series: [{ series: [{
name: 'Notes', name: 'Notes',
@ -641,7 +650,10 @@ const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData
}; };
const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> => { const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> => {
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); const host = props.args?.host;
if (host == null) return { series: [] };
const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return { return {
series: [{ series: [{
name: 'Following', name: 'Following',
@ -664,7 +676,10 @@ const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> =
}; };
const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof chartData> => { const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof chartData> => {
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); const host = props.args?.host;
if (host == null) return { series: [] };
const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return { return {
bytes: true, bytes: true,
series: [{ series: [{
@ -680,7 +695,10 @@ const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof char
}; };
const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof chartData> => { const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof chartData> => {
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); const host = props.args?.host;
if (host == null) return { series: [] };
const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return { return {
series: [{ series: [{
name: 'Drive files', name: 'Drive files',
@ -695,7 +713,10 @@ const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof char
}; };
const fetchPerUserNotesChart = async (): Promise<typeof chartData> => { const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
const raw = await misskeyApiGet('charts/user/notes', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); const userId = props.args?.user?.id;
if (userId == null) return { series: [] };
const raw = await misskeyApiGet('charts/user/notes', { userId: userId, limit: props.limit, span: props.span });
return { return {
series: [...(props.args?.withoutAll ? [] : [{ series: [...(props.args?.withoutAll ? [] : [{
name: 'All', name: 'All',
@ -727,7 +748,10 @@ const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
}; };
const fetchPerUserPvChart = async (): Promise<typeof chartData> => { const fetchPerUserPvChart = async (): Promise<typeof chartData> => {
const raw = await misskeyApiGet('charts/user/pv', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); const userId = props.args?.user?.id;
if (userId == null) return { series: [] };
const raw = await misskeyApiGet('charts/user/pv', { userId: userId, limit: props.limit, span: props.span });
return { return {
series: [{ series: [{
name: 'Unique PV (user)', name: 'Unique PV (user)',
@ -754,7 +778,10 @@ const fetchPerUserPvChart = async (): Promise<typeof chartData> => {
}; };
const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => { const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); const userId = props.args?.user?.id;
if (userId == null) return { series: [] };
const raw = await misskeyApiGet('charts/user/following', { userId: userId, limit: props.limit, span: props.span });
return { return {
series: [{ series: [{
name: 'Local', name: 'Local',
@ -769,7 +796,10 @@ const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
}; };
const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => { const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); const userId = props.args?.user?.id;
if (userId == null) return { series: [] };
const raw = await misskeyApiGet('charts/user/following', { userId: userId, limit: props.limit, span: props.span });
return { return {
series: [{ series: [{
name: 'Local', name: 'Local',
@ -784,7 +814,10 @@ const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
}; };
const fetchPerUserDriveChart = async (): Promise<typeof chartData> => { const fetchPerUserDriveChart = async (): Promise<typeof chartData> => {
const raw = await misskeyApiGet('charts/user/drive', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); const userId = props.args?.user?.id;
if (userId == null) return { series: [] };
const raw = await misskeyApiGet('charts/user/drive', { userId: userId, limit: props.limit, span: props.span });
return { return {
bytes: true, bytes: true,
series: [{ series: [{

View File

@ -25,12 +25,12 @@ defineProps<{
showing: boolean; showing: boolean;
x: number; x: number;
y: number; y: number;
title?: string; title?: string | null;
series?: { series?: {
backgroundColor: string; backgroundColor: string;
borderColor: string; borderColor: string;
text: string; text: string;
}[]; }[] | null;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{

View File

@ -4,7 +4,7 @@
*/ */
import { http, HttpResponse } from 'msw'; import { http, HttpResponse } from 'msw';
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import { chatMessage } from '../../.storybook/fakes'; import { chatMessage } from '../../.storybook/fakes';
import MkChatHistories from './MkChatHistories.vue'; import MkChatHistories from './MkChatHistories.vue';
import type { StoryObj } from '@storybook/vue3'; import type { StoryObj } from '@storybook/vue3';

View File

@ -4,7 +4,7 @@
*/ */
import { HttpResponse, http } from 'msw'; import { HttpResponse, http } from 'msw';
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import { expect, userEvent, within } from '@storybook/test'; import { expect, userEvent, within } from '@storybook/test';
import { commonHandlers } from '../../.storybook/mocks.js'; import { commonHandlers } from '../../.storybook/mocks.js';
import MkClickerGame from './MkClickerGame.vue'; import MkClickerGame from './MkClickerGame.vue';

View File

@ -6,7 +6,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable import/no-default-export */ /* eslint-disable import/no-default-export */
import type { StoryObj } from '@storybook/vue3'; import type { StoryObj } from '@storybook/vue3';
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import MkCodeEditor from './MkCodeEditor.vue'; import MkCodeEditor from './MkCodeEditor.vue';
const code = `for (let i, 100) { const code = `for (let i, 100) {
<: if (i % 15 == 0) "FizzBuzz" <: if (i % 15 == 0) "FizzBuzz"

View File

@ -6,7 +6,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable import/no-default-export */ /* eslint-disable import/no-default-export */
import type { StoryObj } from '@storybook/vue3'; import type { StoryObj } from '@storybook/vue3';
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import MkColorInput from './MkColorInput.vue'; import MkColorInput from './MkColorInput.vue';
export const Default = { export const Default = {
render(args) { render(args) {

View File

@ -4,7 +4,7 @@
*/ */
import { HttpResponse, http } from 'msw'; import { HttpResponse, http } from 'msw';
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import { file } from '../../.storybook/fakes.js'; import { file } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js'; import { commonHandlers } from '../../.storybook/mocks.js';
import MkCropperDialog from './MkCropperDialog.vue'; import MkCropperDialog from './MkCropperDialog.vue';
@ -38,7 +38,7 @@ export const Default = {
}; };
}, },
args: { args: {
file: file(), imageFile: file(),
aspectRatio: NaN, aspectRatio: NaN,
}, },
parameters: { parameters: {

View File

@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, useTemplateRef, ref } from 'vue'; import { onMounted, useTemplateRef, ref, onUnmounted } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import Cropper from 'cropperjs'; import Cropper from 'cropperjs';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
@ -55,17 +55,19 @@ const imgEl = useTemplateRef('imgEl');
let cropper: Cropper | null = null; let cropper: Cropper | null = null;
const loading = ref(true); const loading = ref(true);
const ok = async () => { async function ok() {
const promise = new Promise<Misskey.entities.DriveFile>(async (res) => { const promise = new Promise<Blob>(async (res) => {
const croppedImage = await cropper?.getCropperImage(); if (cropper == null) throw new Error('Cropper is not initialized');
const croppedSection = await cropper?.getCropperSelection();
const croppedImage = await cropper.getCropperImage()!;
const croppedSection = await cropper.getCropperSelection()!;
// () // ()
const zoomedRate = croppedImage.getBoundingClientRect().width / croppedImage.clientWidth; const zoomedRate = croppedImage.getBoundingClientRect().width / croppedImage.clientWidth;
const widthToRender = croppedSection.getBoundingClientRect().width / zoomedRate; const widthToRender = croppedSection.getBoundingClientRect().width / zoomedRate;
const croppedCanvas = await croppedSection?.$toCanvas({ width: widthToRender }); const croppedCanvas = await croppedSection.$toCanvas({ width: widthToRender });
croppedCanvas?.toBlob(blob => { croppedCanvas.toBlob(blob => {
if (!blob) return; if (!blob) return;
res(blob); res(blob);
}); });
@ -74,25 +76,27 @@ const ok = async () => {
const f = await promise; const f = await promise;
emit('ok', f); emit('ok', f);
dialogEl.value!.close(); if (dialogEl.value != null) dialogEl.value.close();
}; }
const cancel = () => { function cancel() {
emit('cancel'); emit('cancel');
dialogEl.value!.close(); if (dialogEl.value != null) dialogEl.value.close();
}; }
const onImageLoad = () => { function onImageLoad() {
loading.value = false; loading.value = false;
if (cropper) { if (cropper) {
cropper.getCropperImage()!.$center('contain'); cropper.getCropperImage()!.$center('contain');
cropper.getCropperSelection()!.$center(); cropper.getCropperSelection()!.$center();
} }
}; }
onMounted(() => { onMounted(() => {
cropper = new Cropper(imgEl.value!, { if (imgEl.value == null) return; // TS
cropper = new Cropper(imgEl.value, {
}); });
const computedStyle = getComputedStyle(window.document.documentElement); const computedStyle = getComputedStyle(window.document.documentElement);
@ -104,16 +108,22 @@ onMounted(() => {
selection.outlined = true; selection.outlined = true;
window.setTimeout(() => { window.setTimeout(() => {
cropper!.getCropperImage()!.$center('contain'); if (cropper == null) return;
cropper.getCropperImage()!.$center('contain');
selection.$center(); selection.$center();
}, 100); }, 100);
// 調 // 調
window.setTimeout(() => { window.setTimeout(() => {
cropper!.getCropperImage()!.$center('contain'); if (cropper == null) return;
cropper.getCropperImage()!.$center('contain');
selection.$center(); selection.$center();
}, 500); }, 500);
}); });
onUnmounted(() => {
URL.revokeObjectURL(imgUrl);
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -6,7 +6,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable import/no-default-export */ /* eslint-disable import/no-default-export */
import type { StoryObj } from '@storybook/vue3'; import type { StoryObj } from '@storybook/vue3';
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import { expect, userEvent, within } from '@storybook/test'; import { expect, userEvent, within } from '@storybook/test';
import { file } from '../../.storybook/fakes.js'; import { file } from '../../.storybook/fakes.js';
import MkCwButton from './MkCwButton.vue'; import MkCwButton from './MkCwButton.vue';

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import { expect, userEvent, waitFor, within } from '@storybook/test'; import { expect, userEvent, waitFor, within } from '@storybook/test';
import type { StoryObj } from '@storybook/vue3'; import type { StoryObj } from '@storybook/vue3';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3'; import type { StoryObj } from '@storybook/vue3';
import { onBeforeUnmount } from 'vue'; import { onBeforeUnmount } from 'vue';
import MkDonation from './MkDonation.vue'; import MkDonation from './MkDonation.vue';

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3'; import type { StoryObj } from '@storybook/vue3';
import MkDrive_file from './MkDrive.file.vue'; import MkDrive_file from './MkDrive.file.vue';
import { file } from '../../.storybook/fakes.js'; import { file } from '../../.storybook/fakes.js';

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3'; import type { StoryObj } from '@storybook/vue3';
import { http, HttpResponse } from 'msw'; import { http, HttpResponse } from 'msw';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { action } from '@storybook/addon-actions'; import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3'; import type { StoryObj } from '@storybook/vue3';
import { http, HttpResponse } from 'msw'; import { http, HttpResponse } from 'msw';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';

View File

@ -152,11 +152,12 @@ import { getDriveFileMenu } from '@/utility/get-drive-file-menu.js';
import { Paginator } from '@/utility/paginator.js'; import { Paginator } from '@/utility/paginator.js';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
initialFolder?: Misskey.entities.DriveFolder['id'] | null; initialFolder?: Misskey.entities.DriveFolder | Misskey.entities.DriveFolder['id'] | null;
type?: string; type?: string;
multiple?: boolean; multiple?: boolean;
select?: 'file' | 'folder' | null; select?: 'file' | 'folder' | null;
}>(), { }>(), {
initialFolder: null,
multiple: false, multiple: false,
select: null, select: null,
}); });
@ -293,7 +294,7 @@ function onDragleave() {
draghover.value = false; draghover.value = false;
} }
function onDrop(ev: DragEvent) { function onDrop(ev: DragEvent): void | boolean {
draghover.value = false; draghover.value = false;
if (!ev.dataTransfer) return; if (!ev.dataTransfer) return;
@ -363,7 +364,7 @@ function onDrop(ev: DragEvent) {
//#endregion //#endregion
} }
function onUploadRequested(files: File[], folder: Misskey.entities.DriveFolder | null) { function onUploadRequested(files: File[], folder?: Misskey.entities.DriveFolder | null) {
os.launchUploader(files, { os.launchUploader(files, {
folderId: folder?.id ?? null, folderId: folder?.id ?? null,
}); });
@ -698,7 +699,7 @@ useGlobalEvent('driveFoldersDeleted', (folders) => {
} }
}); });
let connection: Misskey.ChannelConnection<Misskey.Channels['drive']> | null = null; let connection: Misskey.IChannelConnection<Misskey.Channels['drive']> | null = null;
onMounted(() => { onMounted(() => {
if (store.s.realtimeMode) { if (store.s.realtimeMode) {

View File

@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:forceBlurhash="forceBlurhash" :forceBlurhash="forceBlurhash"
/> />
<img <img
v-else-if="isThumbnailAvailable" v-else-if="isThumbnailAvailable && file.thumbnailUrl != null"
:src="file.thumbnailUrl" :src="file.thumbnailUrl"
:alt="file.name" :alt="file.name"
:title="file.name" :title="file.name"

View File

@ -39,13 +39,13 @@ withDefaults(defineProps<{
}); });
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'done', r?: Misskey.entities.DriveFolder[]): void; (ev: 'done', r?: (Misskey.entities.DriveFolder | null)[]): void;
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const dialog = useTemplateRef('dialog'); const dialog = useTemplateRef('dialog');
const selected = ref<Misskey.entities.DriveFolder[]>([]); const selected = ref<(Misskey.entities.DriveFolder | null)[]>([]);
function ok() { function ok() {
emit('done', selected.value); emit('done', selected.value);
@ -57,7 +57,7 @@ function cancel() {
dialog.value?.close(); dialog.value?.close();
} }
function onChangeSelection(v: Misskey.entities.DriveFolder[]) { function onChangeSelection(v: (Misskey.entities.DriveFolder | null)[]) {
selected.value = v; selected.value = v;
} }
</script> </script>

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