1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-06-24 18:44:45 +02:00

Compare commits

...

26 Commits

Author SHA1 Message Date
syuilo
c9b5e66cdb wip 2025-12-16 16:50:20 +09:00
かっこかり
0d52145b2b deps: update deps [ci skip] (#16997)
* update deps

* update chokidar to v5

* fix type error

* rollback serviceworker types to r74

* fix [ci skip]
2025-12-16 11:42:06 +09:00
かっこかり
467404d5bb fix(gh): thollander/actions-comment-pull-request@v3 の breaking change 対応漏れ 2025-12-16 09:45:26 +09:00
renovate[bot]
99e25784ad chore(deps): update [github actions] update dependencies (major) (#16869)
* chore(deps): update [github actions] update dependencies

* migrate

* bump download-artifact to v7

* bump upload-artifact to v6

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
2025-12-16 09:26:10 +09:00
github-actions[bot]
9e1e40d35a [skip ci] Update CHANGELOG.md (prepend template) 2025-12-14 07:27:11 +00:00
github-actions[bot]
8eb6e29d2c Release: 2025.12.1 2025-12-14 07:27:06 +00:00
Copilot
2d198a711b Update PostgreSQL volume mount paths for PostgreSQL 18 (#16986)
* Initial plan

* Update PostgreSQL volume mount paths for PostgreSQL 18 compatibility

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-12-14 11:32:42 +09:00
syuilo
e0b872dc09 Update CHANGELOG for version 2025.12.1
Removed empty section under General and retained fixes under Client.
2025-12-13 21:16:12 +09:00
github-actions[bot]
711b86ab7d Bump version to 2025.12.1-alpha.1 2025-12-13 10:14:03 +00:00
syuilo
e8b4dae553 New Crowdin updates (#16939)
* New translations ja-jp.yml (Spanish)

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

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (German)

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

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

* New translations ja-jp.yml (Chinese Simplified)
2025-12-13 19:08:25 +09:00
かっこかり
36d404818d fix(frontend/aiscript): nullを返すnote_view_intrruptorが動作しない問題を修正 (#16977)
* fix(frontend/aiscript): nullを返すnote_view_intrruptorが動作しない問題を修正

* Update Changelog
2025-12-13 19:08:02 +09:00
かっこかり
cb03f3f013 fix(frontend): follow-up of 16970 (#16975) 2025-12-13 18:22:40 +09:00
かっこかり
c109bec013 fix(frontend): ドライブファイルを日付以外で並び替える場合は月でグループ化して表示しないように (#16978)
* fix(frontend): ドライブファイルを日付以外で並び替える場合は月でグループ化して表示しないように

* Update Changelog
2025-12-13 18:17:09 +09:00
かっこかり
6fa4eb8c4f fix(frontend): 無限スクロールできる箇所の調整 (#16979) 2025-12-13 18:16:15 +09:00
github-actions[bot]
5fb4caa14b Bump version to 2025.12.1-alpha.0 2025-12-12 08:09:12 +00:00
まっちゃてぃー。
bc1f83664f fix(sw): Service Worker Auto Preloadをオプトアウトするように (#16971)
* Service Worker Auto Preloadをオプトアウトするように

* TypeErrorを修正

* コメントを追記
2025-12-12 17:08:15 +09:00
anatawa12
78435dc8d4 Fix: deckのタイムラインセレクタのデフォルトの値が現在のタイムラインではない問題 (#16008)
* fix: os.selectの型定義の上で default が文字列である問題を修正

* fix: deckのタイムライン選択画面で今指定されているタイムラインがデフォルトで選択されない問題

* fix lint

* fix: type error
2025-12-12 12:53:59 +09:00
かっこかり
d74aded35f fix: remove unused dependencies (#16969)
* fix(frontend): remove unused dependencies

* fix(backend): remove unused dependencies
2025-12-12 10:45:45 +09:00
FINEARCHS
d605680524 fix(frontend): オーナー不在のチャンネルをミュートできないのを修正 (#16906)
* オーナー不在のチャンネルをミュートできないのを修正

* update changelog

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-12-11 23:15:57 +09:00
かっこかり
1096ce8e4a fix(frontend): 削除されたノートのリノートが正しく表示されない問題を修正 (#16970)
* fix(frontend): 削除されたノートのリノートが正しく表示されない問題を修正

* Update Changelog

* 🎨

* fix
2025-12-11 23:10:23 +09:00
syuilo
8e6fffee68 enhance: use native glob (#16965)
* enhance: use native glob

* remove tiny-glob

* remove fast-glob

* refactor

* fix: use async glob if possible

---------

Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
2025-12-11 23:08:26 +09:00
まっちゃてぃー。
2cffd9f0fb fix(sw): オフライン時のfetch timeout処理を実装 (#16952)
* fix(sw): implement fetch timeout handling for navigation and offline content

* fix(sw): increase fetch timeout

* fix(sw): improve fetch timeout handling for i18n content

* fix(sw): 結局、fetchを通るかCacheがhitするはずなので、i18nのところはいらない

* fix(sw): 400番台のエラーを無条件でオフラインページにしていたのを修正

* 間違えた

* i18nもtimeoutが必要

* import sortingを修正

* import sortingを修正

* Fix: Frontend のsharedにはアクセスできないじゃん...

* SPDX

* Update CHANGELOG.md

* Update packages/sw/src/scripts/lang.ts

Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>

* Update packages/sw/src/sw.ts

Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>

* Update CHANGELOG.md

Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>

---------

Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
2025-12-10 17:26:30 +09:00
syuilo
988f5ab69f fix(backend): ジョブキューでSentryが有効にならない問題を修正 2025-12-08 15:44:37 +09:00
かっこかり
3afe7c5348 Update CHANGELOG.md [ci skip] 2025-12-08 10:20:07 +09:00
かっこかり
73cc30f50f fix(frontend): ロード時の言語判定結果が保存されない問題を修正 (#16956)
* fix(frontend): ロード時の言語判定結果が保存されない問題を修正

* Update Changelog
2025-12-08 10:17:13 +09:00
github-actions[bot]
da3b3af984 [skip ci] Update CHANGELOG.md (prepend template) 2025-12-06 12:23:00 +00:00
85 changed files with 2352 additions and 2389 deletions

View File

@@ -16,13 +16,13 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4.3.0
uses: actions/checkout@v6.0.1
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Setup Node.js
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

@@ -12,9 +12,9 @@ jobs:
steps:
- name: Checkout head
uses: actions/checkout@v4.3.0
uses: actions/checkout@v6.0.1
- name: Setup Node.js
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'

View File

@@ -18,7 +18,7 @@ jobs:
if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }}
steps:
- name: checkout
uses: actions/checkout@v4.3.0
uses: actions/checkout@v6.0.1
with:
submodules: true
persist-credentials: false
@@ -29,7 +29,7 @@ jobs:
- name: setup node
id: setup-node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: pnpm
@@ -53,7 +53,7 @@ jobs:
# packages/misskey-js/generator/built/autogen
- name: Upload Generated
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: generated-misskey-js
path: packages/misskey-js/generator/built/autogen
@@ -66,14 +66,14 @@ jobs:
if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }}
steps:
- name: checkout
uses: actions/checkout@v4.3.0
uses: actions/checkout@v6.0.1
with:
submodules: true
persist-credentials: false
ref: refs/pull/${{ github.event.pull_request.number }}/merge
- name: Upload From Merged
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: actual-misskey-js
path: packages/misskey-js/src/autogen
@@ -86,13 +86,13 @@ jobs:
pull-requests: write
steps:
- name: download generated-misskey-js
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: generated-misskey-js
path: misskey-js-generated
- name: download actual-misskey-js
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: actual-misskey-js
path: misskey-js-actual
@@ -113,9 +113,9 @@ jobs:
- name: send message
if: steps.check-changes.outputs.changes == 'true'
uses: thollander/actions-comment-pull-request@v2
uses: thollander/actions-comment-pull-request@v3
with:
comment_tag: check-misskey-js-autogen
comment-tag: check-misskey-js-autogen
message: |-
Thank you for sending us a great Pull Request! 👍
Please regenerate misskey-js type definitions! 🙏
@@ -127,9 +127,9 @@ jobs:
- name: send message
if: steps.check-changes.outputs.changes == 'false'
uses: thollander/actions-comment-pull-request@v2
uses: thollander/actions-comment-pull-request@v3
with:
comment_tag: check-misskey-js-autogen
comment-tag: check-misskey-js-autogen
mode: delete
message: "Thank you!"
create_if_not_exists: false

View File

@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4.3.0
uses: actions/checkout@v6.0.1
- name: Check version
run: |
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
steps:
- name: Checkout
uses: actions/checkout@v4.3.0
uses: actions/checkout@v6.0.1
- name: Check
run: |
counter=0

View File

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

View File

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

View File

@@ -27,7 +27,7 @@ jobs:
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Check out the repo
uses: actions/checkout@v4.3.0
uses: actions/checkout@v6.0.1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
@@ -53,7 +53,7 @@ jobs:
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/*
@@ -66,7 +66,7 @@ jobs:
- build
steps:
- name: Download digests
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
path: /tmp/digests
pattern: digests-*

View File

@@ -32,7 +32,7 @@ jobs:
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Check out the repo
uses: actions/checkout@v4.3.0
uses: actions/checkout@v6.0.1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
@@ -64,7 +64,7 @@ jobs:
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/*
@@ -77,7 +77,7 @@ jobs:
- build
steps:
- name: Download digests
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
path: /tmp/digests
pattern: digests-*

View File

@@ -16,7 +16,7 @@ jobs:
DOCKLE_VERSION: 0.4.15
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/checkout@v6.0.1
- name: Download and install dockle v${{ env.DOCKLE_VERSION }}
run: |

View File

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

View File

@@ -40,14 +40,14 @@ jobs:
- 56312:6379
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/checkout@v6.0.1
with:
ref: ${{ matrix.ref }}
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -67,7 +67,7 @@ jobs:
# Start the server and measure memory usage
node packages/backend/scripts/measure-memory.mjs > ${{ matrix.memory-json-name }}
- name: Upload Artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: memory-artifact-${{ matrix.memory-json-name }}
path: ${{ matrix.memory-json-name }}
@@ -81,7 +81,7 @@ jobs:
PR_NUMBER: ${{ github.event.number }}
run: |
echo "$PR_NUMBER" > ./pr_number
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
with:
name: memory-artifact-pr-number
path: pr_number

View File

@@ -11,6 +11,6 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5
- uses: actions/labeler@v6
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -36,13 +36,13 @@ jobs:
pnpm_install:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/checkout@v6.0.1
with:
fetch-depth: 0
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- uses: actions/setup-node@v4.4.0
- uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -69,13 +69,13 @@ jobs:
eslint-cache-version: v1
eslint-cache-path: ${{ github.workspace }}/node_modules/.cache/eslint-${{ matrix.workspace }}
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/checkout@v6.0.1
with:
fetch-depth: 0
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- uses: actions/setup-node@v4.4.0
- uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -100,13 +100,13 @@ jobs:
- sw
- misskey-js
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/checkout@v6.0.1
with:
fetch-depth: 0
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- uses: actions/setup-node@v4.4.0
- uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

@@ -16,13 +16,13 @@ jobs:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/checkout@v6.0.1
with:
fetch-depth: 0
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- uses: actions/setup-node@v4.4.0
- uses: actions/setup-node@v6.1.0
with:
node-version-file: ".node-version"
cache: "pnpm"

View File

@@ -16,13 +16,13 @@ jobs:
id-token: write
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/checkout@v6.0.1
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

@@ -19,7 +19,7 @@ jobs:
edit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
# headが$GITHUB_REF_NAME, baseが$STABLE_BRANCHかつopenのPRを1つ取得
- name: Get PR
run: |

View File

@@ -36,7 +36,7 @@ jobs:
outputs:
pr_number: ${{ steps.get_pr.outputs.pr_number }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
# headが$GITHUB_REF_NAME, baseが$STABLE_BRANCHかつopenのPRを1つ取得
- name: Get PRs
run: |

View File

@@ -16,7 +16,7 @@ jobs:
# api-artifact
steps:
- name: Download artifact
uses: actions/github-script@v7.1.0
uses: actions/github-script@v8.0.0
with:
script: |
const fs = require('fs');
@@ -60,7 +60,7 @@ jobs:
- name: Echo full diff
run: cat ./api-full.json.diff
- name: Upload full diff to Artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: api-artifact
path: |
@@ -73,9 +73,9 @@ jobs:
HEADER="このPRによるapi.jsonの差分"
FOOTER="[Get diff files from Workflow Page](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID})"
DIFF_BYTES="$(stat ./api.json.diff -c '%s' | tr -d '\n')"
echo "$HEADER" > ./output.md
if (( "$DIFF_BYTES" <= 1 )); then
echo '差分はありません。' >> ./output.md
else
@@ -87,18 +87,18 @@ jobs:
echo '```' >> ./output.md
echo '</details>' >> .output.md
fi
echo "$FOOTER" >> ./output.md
- uses: thollander/actions-comment-pull-request@v2
- uses: thollander/actions-comment-pull-request@v3
with:
pr_number: ${{ steps.load-pr-num.outputs.pr-number }}
comment_tag: show_diff
filePath: ./output.md
pr-number: ${{ steps.load-pr-num.outputs.pr-number }}
comment-tag: show_diff
file-path: ./output.md
- name: Tell error to PR
uses: thollander/actions-comment-pull-request@v2
uses: thollander/actions-comment-pull-request@v3
if: failure() && steps.load-pr-num.outputs.pr-number
with:
pr_number: ${{ steps.load-pr-num.outputs.pr-number }}
comment_tag: show_diff_error
pr-number: ${{ steps.load-pr-num.outputs.pr-number }}
comment-tag: show_diff_error
message: |
api.jsonの差分作成中にエラーが発生しました。詳細は[Workflowのログ](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})を確認してください。

View File

@@ -15,7 +15,7 @@ jobs:
steps:
- name: Download artifact
uses: actions/github-script@v7.1.0
uses: actions/github-script@v8.0.0
with:
script: |
const fs = require('fs');
@@ -107,16 +107,16 @@ jobs:
fi
echo "$FOOTER" >> ./output.md
- uses: thollander/actions-comment-pull-request@v2
- uses: thollander/actions-comment-pull-request@v3
with:
pr_number: ${{ steps.load-pr-num.outputs.pr-number }}
comment_tag: show_memory_diff
filePath: ./output.md
pr-number: ${{ steps.load-pr-num.outputs.pr-number }}
comment-tag: show_memory_diff
file-path: ./output.md
- name: Tell error to PR
uses: thollander/actions-comment-pull-request@v2
uses: thollander/actions-comment-pull-request@v3
if: failure() && steps.load-pr-num.outputs.pr-number
with:
pr_number: ${{ steps.load-pr-num.outputs.pr-number }}
comment_tag: show_memory_diff_error
pr-number: ${{ steps.load-pr-num.outputs.pr-number }}
comment-tag: show_memory_diff_error
message: |
An error occurred while comparing backend memory usage. See [workflow logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.

View File

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

View File

@@ -22,12 +22,12 @@ jobs:
NODE_OPTIONS: "--max_old_space_size=7168"
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/checkout@v6.0.1
if: github.event_name != 'pull_request_target'
with:
fetch-depth: 0
submodules: true
- uses: actions/checkout@v4.3.0
- uses: actions/checkout@v6.0.1
if: github.event_name == 'pull_request_target'
with:
fetch-depth: 0
@@ -39,7 +39,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -90,7 +90,7 @@ jobs:
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- name: Notify that Chromatic detects changes
uses: actions/github-script@v7.1.0
uses: actions/github-script@v8.0.0
if: github.event_name != 'pull_request_target' && steps.chromatic_push.outputs.success == 'false'
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
@@ -102,7 +102,7 @@ jobs:
body: 'Chromatic detects changes. Please [review the changes on Chromatic](https://www.chromatic.com/builds?appId=6428f7d7b962f0b79f97d6e4).'
})
- name: Upload Artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: storybook
path: packages/frontend/storybook-static

View File

@@ -50,7 +50,7 @@ jobs:
- 56312:6379
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/checkout@v6.0.1
with:
submodules: true
- name: Setup pnpm
@@ -86,7 +86,7 @@ jobs:
fi
done
- name: Use Node.js
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm'
@@ -129,13 +129,13 @@ jobs:
- 56312:6379
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/checkout@v6.0.1
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm'
@@ -173,7 +173,7 @@ jobs:
POSTGRES_HOST_AUTH_METHOD: trust
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/checkout@v6.0.1
with:
submodules: true
- name: Setup pnpm
@@ -182,7 +182,7 @@ jobs:
id: current-date
run: echo "today=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Use Node.js
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm'

View File

@@ -32,7 +32,7 @@ jobs:
- .node-version
- .github/min.node-version
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
submodules: true
- name: Setup pnpm
@@ -68,7 +68,7 @@ jobs:
fi
done
- name: Use Node.js
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm'

View File

@@ -28,13 +28,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/checkout@v6.0.1
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -76,7 +76,7 @@ jobs:
- 56312:6379
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/checkout@v6.0.1
with:
submodules: true
# https://github.com/cypress-io/cypress-docker-images/issues/150
@@ -88,7 +88,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -113,12 +113,12 @@ jobs:
wait-on: 'http://localhost:61812'
headed: true
browser: ${{ matrix.browser }}
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
if: failure()
with:
name: ${{ matrix.browser }}-cypress-screenshots
path: cypress/screenshots
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
if: always()
with:
name: ${{ matrix.browser }}-cypress-videos

View File

@@ -22,13 +22,13 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4.3.0
uses: actions/checkout@v6.0.1
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Setup Node.js
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

@@ -16,13 +16,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/checkout@v6.0.1
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

@@ -17,13 +17,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.3.0
- uses: actions/checkout@v6.0.1
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

@@ -1,3 +1,29 @@
## Unreleased
### General
-
### Client
-
### Server
-
## 2025.12.1
### Client
- Fix: 特定の条件下でMisskeyが起動せず空白のページが表示されることがある問題を軽減
- Fix: 初回読み込み時などに、言語設定で不整合が発生することがある問題を修正
- Fix: 削除されたノートのリノートが正しく動作されない問題を修正
- Fix: チャンネルオーナーが削除済みの時にチャンネルのヘッダーメニューが表示されない不具合を修正
- Fix: ドライブで登録日以外でソートする場合は月でグループ化して表示しないように
- Fix: `null` を返す note_view_intrruptor プラグインが動作しない問題を修正
### Server
- Fix: ジョブキューでSentryが有効にならない問題を修正
## 2025.12.0
### Note

View File

@@ -21,7 +21,7 @@ services:
env_file:
- .config/docker.env
volumes:
- ./db:/var/lib/postgresql/data
- ./db:/var/lib/postgresql
healthcheck:
test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
interval: 5s

View File

@@ -43,7 +43,7 @@ services:
env_file:
- .config/docker.env
volumes:
- ./db:/var/lib/postgresql/data
- ./db:/var/lib/postgresql
healthcheck:
test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
interval: 5s

View File

@@ -83,6 +83,7 @@ files: "Dateien"
download: "Herunterladen"
driveFileDeleteConfirm: "Möchtest du die Datei „{name}“ wirklich löschen? Einige Inhalte, die diese Datei verwenden, werden auch verschwinden."
unfollowConfirm: "Möchtest du {name} wirklich nicht mehr folgen?"
rejectFollowRequestConfirm: "Möchtest du die Follow-Anfrage von {name} ablehnen?"
exportRequested: "Du hast einen Export angefragt. Dies kann etwas Zeit in Anspruch nehmen. Sobald der Export abgeschlossen ist, wird er deiner Drive hinzugefügt."
importRequested: "Du hast einen Import angefragt. Dies kann etwas Zeit in Anspruch nehmen."
lists: "Listen"
@@ -1018,6 +1019,7 @@ pushNotificationAlreadySubscribed: "Push-Benachrichtigungen sind bereits aktivie
pushNotificationNotSupported: "Entweder dein Browser oder deine Instanz unterstützt Push-Benachrichtigungen nicht"
sendPushNotificationReadMessage: "Push-Benachrichtigungen löschen, sobald sie gelesen wurden"
sendPushNotificationReadMessageCaption: "Dies kann gegebenenfalls den Batterieverbrauch deines Gerätes erhöhen."
pleaseAllowPushNotification: "Bitte erlauben Sie Benachrichtigungen in Ihrem Browser."
windowMaximize: "Maximieren"
windowMinimize: "Minimieren"
windowRestore: "Wiederherstellen"
@@ -1054,6 +1056,7 @@ permissionDeniedError: "Aktion verweigert"
permissionDeniedErrorDescription: "Dieses Benutzerkonto besitzt nicht die Berechtigung, um diese Aktion auszuführen."
preset: "Vorlage"
selectFromPresets: "Aus Vorlagen wählen"
custom: "Benutzerdefiniert"
achievements: "Errungenschaften"
gotInvalidResponseError: "Ungültige Antwort des Servers"
gotInvalidResponseErrorDescription: "Eventuell ist der Server momentan nicht erreichbar oder untergeht Wartungsarbeiten. Bitte versuche es später noch einmal."
@@ -1243,6 +1246,7 @@ releaseToRefresh: "Zum Aktualisieren loslassen"
refreshing: "Wird aktualisiert..."
pullDownToRefresh: "Zum Aktualisieren ziehen"
useGroupedNotifications: "Benachrichtigungen gruppieren"
emailVerificationFailedError: "Es gab ein Problem bei der Überprüfung Ihrer E-Mail-Adresse. Der Link ist möglicherweise abgelaufen."
cwNotationRequired: "Ist \"Inhaltswarnung verwenden\" aktiviert, muss eine Beschreibung gegeben werden."
doReaction: "Reagieren"
code: "Code"
@@ -1370,7 +1374,12 @@ defaultImageCompressionLevel: "Standard-Bildkomprimierungsstufe"
defaultImageCompressionLevel_description: "Ein niedrigerer Wert erhält die Bildqualität, erhöht aber die Dateigröße. <br>Höhere Werte reduzieren die Dateigröße, verringern aber die Bildqualität."
inMinutes: "Minute(n)"
inDays: "Tag(en)"
safeModeEnabled: "Der abgesicherte Modus ist aktiviert."
schedule: "Planen"
scheduled: "Geplant"
widgets: "Widgets"
deviceInfo: "Geräteinformation"
youAreAdmin: "Sie sind ein Administrator"
presets: "Vorlage"
_imageEditing:
_vars:

View File

@@ -83,6 +83,8 @@ files: "Files"
download: "Download"
driveFileDeleteConfirm: "Are you sure you want to delete \"{name}\"? All notes with this file attached will also be deleted."
unfollowConfirm: "Are you sure you want to unfollow {name}?"
cancelFollowRequestConfirm: "Are you sure that you want to cancel your follow request to {name}?"
rejectFollowRequestConfirm: "Are you sure that you want to reject the follow request from {name}?"
exportRequested: "You've requested an export. This may take a while. It will be added to your Drive once completed."
importRequested: "You've requested an import. This may take a while."
lists: "Lists"

View File

@@ -1252,7 +1252,7 @@ detachAll: "Quitar todo"
angle: "Ángulo"
flip: "Echar de un capirotazo"
showAvatarDecorations: "Mostrar decoraciones de avatar"
releaseToRefresh: "Soltar para recargar"
releaseToRefresh: "Suelta para recargar"
refreshing: "Recargando..."
pullDownToRefresh: "Tira hacia abajo para recargar"
useGroupedNotifications: "Mostrar notificaciones agrupadas"

View File

@@ -53,7 +53,7 @@ copyRemoteLink: "复制远程链接"
copyLinkRenote: "复制转帖链接"
delete: "删除"
deleteAndEdit: "删除并编辑"
deleteAndEditConfirm: "要删除此帖并再次编辑吗?此帖所有回应、转发和回复也将被删除。"
deleteAndEditConfirm: "要删除此帖并再次编辑吗?此帖所有回应、转发和回复也将被删除。"
addToList: "添加至列表"
addToAntenna: "添加到天线"
sendMessage: "发送消息"
@@ -136,7 +136,7 @@ emojiPicker: "表情符号选择器"
pinnedEmojisForReactionSettingDescription: "可以设置发表回应时置顶显示的表情符号"
pinnedEmojisSettingDescription: "可以设置输入表情符号时置顶显示的表情符号"
emojiPickerDisplay: "选择器显示设置"
overwriteFromPinnedEmojisForReaction: "「置顶(回应)」设置覆盖"
overwriteFromPinnedEmojisForReaction: "使用「置顶(回应)」设置覆盖"
overwriteFromPinnedEmojis: "从全局设置覆盖"
reactionSettingDescription2: "拖动重新排序,单击删除,点击 + 添加。"
rememberNoteVisibility: "保存上次设置的可见性"
@@ -153,8 +153,8 @@ block: "拉黑"
unblock: "取消拉黑"
suspend: "冻结"
unsuspend: "解除冻结"
blockConfirm: "确定要拉黑吗?"
unblockConfirm: "确定要取消拉黑吗?"
blockConfirm: "确定要屏蔽吗?"
unblockConfirm: "确定要取消屏蔽吗?"
suspendConfirm: "要冻结吗?"
unsuspendConfirm: "要解除冻结吗?"
selectList: "选择列表"
@@ -174,7 +174,7 @@ emojiUrl: "emoji 地址"
addEmoji: "添加表情符号"
settingGuide: "推荐配置"
cacheRemoteFiles: "缓存远程文件"
cacheRemoteFilesDescription: "启用此设定时,将在此服务器上缓存远程文件。虽然可以加快图片显示的速度,但是相对的会消耗大量的服务器存储空间。用户角色内的网盘容量决定了这个远程用户能在服务器上保留多少缓存。当超出了这个限制时,旧的文件将从缓存中被删除,成为链接。当禁用此设定时,则是从一开始就将远程文件保留为链接。此时推荐将 default.yml 的 proxyRemoteFiles 设置为 true 以优化缩略图生成及保护用户隐私。"
cacheRemoteFilesDescription: "启用此设定时,将在此服务器上缓存远程文件。虽然可以加快图片显示的速度,但是相对的会消耗大量的服务器存储空间。用户角色内的网盘容量决定了这个远程用户能在服务器上保留多少缓存。当超出了这个限制时,旧的文件将从缓存中被删除,成为链接。当禁用此设定时,则是从一开始就将远程文件保留为链接。此时推荐将 的 proxyRemoteFiles 设置为 true 以优化缩略图生成及保护用户隐私。"
youCanCleanRemoteFilesCache: "可以使用文件管理的🗑️按钮来删除所有的缓存。"
cacheRemoteSensitiveFiles: "缓存远程敏感媒体文件"
cacheRemoteSensitiveFilesDescription: "如果禁用这项设定,远程服务器的敏感媒体将不会被缓存,而是直接链接。"
@@ -184,7 +184,7 @@ flagAsCat: "喵!!!!!!!!!!!!"
flagAsCatDescription: "喵喵喵??"
flagShowTimelineReplies: "在时间线上显示帖子的回复"
flagShowTimelineRepliesDescription: "启用时,时间线除了显示用户的帖子外,还会显示其他用户对帖子的回复。"
autoAcceptFollowed: "自动允许来自我关注的用户对我的关注请求"
autoAcceptFollowed: "自动允许回关请求"
addAccount: "添加账户"
reloadAccountsList: "更新账户列表"
loginFailed: "登录失败"
@@ -247,8 +247,8 @@ mediaSilencedInstancesDescription: "设置要隐藏媒体文件的服务器,
federationAllowedHosts: "允许联合的服务器"
federationAllowedHostsDescription: "设定允许联合的服务器,以换行分隔。"
muteAndBlock: "屏蔽/拉黑"
mutedUsers: "已屏蔽用户"
blockedUsers: "已拉黑的用户"
mutedUsers: "已静音的用户"
blockedUsers: "已屏蔽的用户"
noUsers: "无用户"
editProfile: "编辑资料"
noteDeleteConfirm: "确定要删除该帖子吗?"
@@ -1344,7 +1344,7 @@ skip: "跳过"
restore: "恢复"
syncBetweenDevices: "设备间同步"
preferenceSyncConflictTitle: "服务器上已存在设定值"
preferenceSyncConflictText: "服务器上已有此设置的设定值。要覆盖哪个设定值?"
preferenceSyncConflictText: "即将保存设定值到服务器,但检测到服务器上已有此设置的设定值。要使用哪个设定值?"
preferenceSyncConflictChoiceMerge: "合并"
preferenceSyncConflictChoiceServer: "服务器上的设定值"
preferenceSyncConflictChoiceDevice: "设备上的设定值"
@@ -3270,7 +3270,7 @@ _watermarkEditor:
driveFileTypeWarn: "不支持此文件"
driveFileTypeWarnDescription: "请选择图像文件"
title: "编辑水印"
cover: "覆盖全体"
cover: "覆盖所有"
repeat: "平铺"
preserveBoundingRect: "调整为旋转时不超出范围"
opacity: "不透明度"

View File

@@ -1,12 +1,12 @@
{
"name": "misskey",
"version": "2025.12.0",
"version": "2025.12.1",
"codename": "nasubi",
"repository": {
"type": "git",
"url": "https://github.com/misskey-dev/misskey.git"
},
"packageManager": "pnpm@10.24.0",
"packageManager": "pnpm@10.25.0",
"workspaces": [
"packages/misskey-js",
"packages/i18n",
@@ -53,15 +53,13 @@
"cleanall": "pnpm clean-all"
},
"resolutions": {
"chokidar": "4.0.3",
"chokidar": "5.0.0",
"lodash": "4.17.21"
},
"dependencies": {
"cssnano": "7.1.2",
"esbuild": "0.27.0",
"execa": "9.6.0",
"fast-glob": "3.3.3",
"glob": "13.0.0",
"esbuild": "0.27.1",
"execa": "9.6.1",
"ignore-walk": "8.0.0",
"js-yaml": "4.1.1",
"postcss": "8.5.6",
@@ -73,15 +71,15 @@
"@eslint/js": "9.39.1",
"@misskey-dev/eslint-plugin": "2.2.0",
"@types/js-yaml": "4.0.9",
"@types/node": "24.10.1",
"@typescript-eslint/eslint-plugin": "8.47.0",
"@typescript-eslint/parser": "8.47.0",
"@types/node": "24.10.2",
"@typescript-eslint/eslint-plugin": "8.49.0",
"@typescript-eslint/parser": "8.49.0",
"cross-env": "10.1.0",
"cypress": "15.7.0",
"cypress": "15.7.1",
"eslint": "9.39.1",
"globals": "16.5.0",
"ncp": "2.0.0",
"pnpm": "10.24.0",
"pnpm": "10.25.0",
"start-server-and-test": "2.1.3"
},
"optionalDependencies": {

View File

@@ -71,26 +71,25 @@
"utf-8-validate": "6.0.5"
},
"dependencies": {
"@aws-sdk/client-s3": "3.940.0",
"@aws-sdk/lib-storage": "3.940.0",
"@aws-sdk/client-s3": "3.947.0",
"@aws-sdk/lib-storage": "3.947.0",
"@discordapp/twemoji": "16.0.1",
"@fastify/accepts": "5.0.3",
"@fastify/cookie": "11.0.2",
"@fastify/accepts": "5.0.4",
"@fastify/cors": "11.1.0",
"@fastify/express": "4.0.2",
"@fastify/http-proxy": "11.3.0",
"@fastify/http-proxy": "11.4.1",
"@fastify/multipart": "9.3.0",
"@fastify/static": "8.3.0",
"@kitajs/html": "4.2.11",
"@misskey-dev/sharp-read-bmp": "1.2.0",
"@misskey-dev/summaly": "5.2.5",
"@napi-rs/canvas": "0.1.83",
"@napi-rs/canvas": "0.1.84",
"@nestjs/common": "11.1.9",
"@nestjs/core": "11.1.9",
"@nestjs/testing": "11.1.9",
"@peertube/http-signature": "1.7.0",
"@sentry/node": "10.27.0",
"@sentry/profiling-node": "10.27.0",
"@sentry/node": "10.29.0",
"@sentry/profiling-node": "10.29.0",
"@simplewebauthn/server": "13.2.2",
"@sinonjs/fake-timers": "15.0.0",
"@smithy/node-http-handler": "4.4.5",
@@ -105,12 +104,11 @@
"bcryptjs": "3.0.3",
"blurhash": "2.0.5",
"body-parser": "2.2.1",
"bullmq": "5.65.0",
"bullmq": "5.65.1",
"cacheable-lookup": "7.0.0",
"cbor": "10.0.11",
"chalk": "5.6.2",
"chalk-template": "1.1.2",
"chokidar": "4.0.3",
"chokidar": "5.0.0",
"color-convert": "3.1.3",
"content-disposition": "1.0.1",
"date-fns": "4.1.0",
@@ -131,7 +129,6 @@
"is-svg": "6.1.0",
"json5": "2.2.3",
"jsonld": "9.0.0",
"jsrsasign": "11.1.0",
"juice": "11.0.3",
"meilisearch": "0.54.0",
"mfm-js": "0.25.0",
@@ -145,7 +142,6 @@
"node-html-parser": "7.0.1",
"nodemailer": "7.0.11",
"nsfwjs": "4.2.0",
"oauth": "0.10.2",
"oauth2orize": "1.12.0",
"oauth2orize-pkce": "0.1.2",
"os-utils": "0.0.14",
@@ -170,14 +166,13 @@
"slacc": "0.0.10",
"strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0",
"systeminformation": "5.27.11",
"systeminformation": "5.27.12",
"tinycolor2": "1.6.0",
"tmp": "0.2.5",
"tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0",
"typeorm": "0.3.27",
"typeorm": "0.3.28",
"typescript": "5.9.3",
"ulid": "3.0.1",
"ulid": "3.0.2",
"vary": "1.1.2",
"web-push": "3.6.7",
"ws": "8.18.3",
@@ -187,7 +182,7 @@
"@jest/globals": "29.7.0",
"@kitajs/ts-html-plugin": "4.1.3",
"@nestjs/platform-express": "11.1.9",
"@sentry/vue": "10.27.0",
"@sentry/vue": "10.29.0",
"@simplewebauthn/types": "12.0.0",
"@swc/jest": "0.2.39",
"@types/accepts": "1.3.7",
@@ -199,12 +194,10 @@
"@types/http-link-header": "1.0.7",
"@types/jest": "29.5.14",
"@types/jsonld": "1.5.15",
"@types/jsrsasign": "10.5.15",
"@types/mime-types": "3.0.1",
"@types/ms": "2.1.0",
"@types/node": "24.10.1",
"@types/node": "24.10.2",
"@types/nodemailer": "7.0.4",
"@types/oauth": "0.9.6",
"@types/oauth2orize": "1.11.5",
"@types/oauth2orize-pkce": "0.1.2",
"@types/pg": "8.15.6",
@@ -222,21 +215,21 @@
"@types/vary": "1.1.3",
"@types/web-push": "3.6.4",
"@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.48.0",
"@typescript-eslint/parser": "8.48.0",
"@typescript-eslint/eslint-plugin": "8.49.0",
"@typescript-eslint/parser": "8.49.0",
"aws-sdk-client-mock": "4.1.0",
"cbor": "10.0.11",
"cross-env": "10.1.0",
"eslint-plugin-import": "2.32.0",
"execa": "9.6.0",
"execa": "9.6.1",
"fkill": "10.0.1",
"jest": "29.7.0",
"jest-mock": "29.7.0",
"jest-util": "29.7.0",
"js-yaml": "4.1.1",
"nodemon": "3.1.11",
"pid-port": "2.0.0",
"simple-oauth2": "5.1.0",
"supertest": "7.1.4",
"vite": "7.2.4"
"vite": "7.2.7"
}
}

View File

@@ -157,7 +157,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
}
let Sentry: typeof import('@sentry/node') | undefined;
if (Sentry != null) {
if (this.config.sentryForBackend) {
import('@sentry/node').then((mod) => {
Sentry = mod;
});

View File

@@ -50,7 +50,7 @@ services:
volumes:
- type: bind
source: ./volumes/db.a
target: /var/lib/postgresql/data
target: /var/lib/postgresql
bind:
create_host_path: true

View File

@@ -50,7 +50,7 @@ services:
volumes:
- type: bind
source: ./volumes/db.b
target: /var/lib/postgresql/data
target: /var/lib/postgresql
bind:
create_host_path: true

View File

@@ -11,9 +11,9 @@
},
"devDependencies": {
"@types/estree": "1.0.8",
"@types/node": "24.10.1",
"@typescript-eslint/eslint-plugin": "8.48.0",
"@typescript-eslint/parser": "8.48.0",
"@types/node": "24.10.2",
"@typescript-eslint/eslint-plugin": "8.49.0",
"@typescript-eslint/parser": "8.49.0",
"rollup": "4.53.3",
"typescript": "5.9.3"
},
@@ -21,6 +21,6 @@
"i18n": "workspace:*",
"estree-walker": "3.0.3",
"magic-string": "0.30.21",
"vite": "7.2.4"
"vite": "7.2.7"
}
}

View File

@@ -17,8 +17,6 @@
"@rollup/pluginutils": "5.3.0",
"@twemoji/parser": "16.0.0",
"@vitejs/plugin-vue": "6.0.2",
"@vue/compiler-sfc": "3.5.25",
"astring": "1.9.0",
"buraha": "0.0.1",
"estree-walker": "3.0.3",
"frontend-shared": "workspace:*",
@@ -28,14 +26,11 @@
"misskey-js": "workspace:*",
"punycode.js": "2.3.1",
"rollup": "4.53.3",
"sass": "1.94.2",
"shiki": "3.17.0",
"sass": "1.95.0",
"shiki": "3.19.0",
"tinycolor2": "1.6.0",
"tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0",
"typescript": "5.9.3",
"uuid": "13.0.0",
"vite": "7.2.4",
"vite": "7.2.7",
"vue": "3.5.25"
},
"devDependencies": {
@@ -44,30 +39,30 @@
"@testing-library/vue": "8.1.0",
"@types/estree": "1.0.8",
"@types/micromatch": "4.0.10",
"@types/node": "24.10.1",
"@types/node": "24.10.2",
"@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.48.0",
"@typescript-eslint/parser": "8.48.0",
"@vitest/coverage-v8": "4.0.14",
"@typescript-eslint/eslint-plugin": "8.49.0",
"@typescript-eslint/parser": "8.49.0",
"@vitest/coverage-v8": "4.0.15",
"@vue/runtime-core": "3.5.25",
"acorn": "8.15.0",
"cross-env": "10.1.0",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-vue": "10.6.2",
"fast-glob": "3.3.3",
"happy-dom": "20.0.11",
"intersection-observer": "0.12.2",
"micromatch": "4.0.8",
"msw": "2.12.3",
"msw": "2.12.4",
"nodemon": "3.1.11",
"prettier": "3.7.1",
"prettier": "3.7.4",
"start-server-and-test": "2.1.3",
"tsx": "4.20.6",
"tsx": "4.21.0",
"typescript": "5.9.3",
"vite-plugin-turbosnap": "1.0.3",
"vue-component-type-helpers": "3.1.5",
"vue-component-type-helpers": "3.1.7",
"vue-eslint-parser": "10.2.0",
"vue-tsc": "3.1.5"
"vue-tsc": "3.1.7"
}
}

View File

@@ -70,6 +70,8 @@
importAppScript();
});
}
localStorage.setItem('lang', lang);
//#endregion
async function addStyle(styleText) {

View File

@@ -3,14 +3,13 @@ import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import * as esbuild from 'esbuild';
import { build } from 'esbuild';
import { globSync } from 'glob';
import { execa } from 'execa';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const _package = JSON.parse(fs.readFileSync(_dirname + '/package.json', 'utf-8'));
const entryPoints = globSync('./js/**/**.{ts,tsx}');
const entryPoints = fs.globSync('./js/**/**.{ts,tsx}');
/** @type {import('esbuild').BuildOptions} */
const options = {

View File

@@ -21,10 +21,10 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
"@types/node": "24.10.1",
"@typescript-eslint/eslint-plugin": "8.48.0",
"@typescript-eslint/parser": "8.48.0",
"esbuild": "0.27.0",
"@types/node": "24.10.2",
"@typescript-eslint/eslint-plugin": "8.49.0",
"@typescript-eslint/parser": "8.49.0",
"esbuild": "0.27.1",
"eslint-plugin-vue": "10.6.2",
"nodemon": "3.1.11",
"typescript": "5.9.3",

View File

@@ -3,12 +3,11 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { existsSync, readFileSync } from 'node:fs';
import { existsSync, readFileSync, globSync } from 'node:fs';
import { writeFile } from 'node:fs/promises';
import { basename, dirname } from 'node:path/posix';
import { GENERATOR, type State, generate } from 'astring';
import type * as estree from 'estree';
import glob from 'fast-glob';
import { format } from 'prettier';
interface SatisfiesExpression extends estree.BaseExpression {
@@ -439,38 +438,37 @@ function toStories(component: string): Promise<string> {
// glob('src/{components,pages,ui,widgets}/**/*.vue')
(async () => {
const globs = await Promise.all([
glob('src/components/global/Mk*.vue'),
glob('src/components/global/RouterView.vue'),
glob('src/components/MkAbuseReportWindow.vue'),
glob('src/components/MkAccountMoved.vue'),
glob('src/components/MkAchievements.vue'),
glob('src/components/MkAnalogClock.vue'),
glob('src/components/MkAnimBg.vue'),
glob('src/components/MkAnnouncementDialog.vue'),
glob('src/components/MkAntennaEditor.vue'),
glob('src/components/MkAntennaEditorDialog.vue'),
glob('src/components/MkAsUi.vue'),
glob('src/components/MkAutocomplete.vue'),
glob('src/components/MkAvatars.vue'),
glob('src/components/Mk[B-E]*.vue'),
glob('src/components/MkFlashPreview.vue'),
glob('src/components/MkGalleryPostPreview.vue'),
glob('src/components/MkSignupServerRules.vue'),
glob('src/components/MkUserSetupDialog.vue'),
glob('src/components/MkUserSetupDialog.*.vue'),
glob('src/components/MkImgPreviewDialog.vue'),
glob('src/components/MkInstanceCardMini.vue'),
glob('src/components/MkInviteCode.vue'),
glob('src/components/MkTagItem.vue'),
glob('src/components/MkRoleSelectDialog.vue'),
glob('src/components/grid/MkGrid.vue'),
glob('src/pages/admin/custom-emojis-manager2.vue'),
glob('src/pages/admin/overview.ap-requests.vue'),
glob('src/pages/user/home.vue'),
glob('src/pages/search.vue'),
]);
const components = globs.flat();
const components = [
globSync('src/components/global/Mk*.vue'),
globSync('src/components/global/RouterView.vue'),
globSync('src/components/MkAbuseReportWindow.vue'),
globSync('src/components/MkAccountMoved.vue'),
globSync('src/components/MkAchievements.vue'),
globSync('src/components/MkAnalogClock.vue'),
globSync('src/components/MkAnimBg.vue'),
globSync('src/components/MkAnnouncementDialog.vue'),
globSync('src/components/MkAntennaEditor.vue'),
globSync('src/components/MkAntennaEditorDialog.vue'),
globSync('src/components/MkAsUi.vue'),
globSync('src/components/MkAutocomplete.vue'),
globSync('src/components/MkAvatars.vue'),
globSync('src/components/Mk[B-E]*.vue'),
globSync('src/components/MkFlashPreview.vue'),
globSync('src/components/MkGalleryPostPreview.vue'),
globSync('src/components/MkSignupServerRules.vue'),
globSync('src/components/MkUserSetupDialog.vue'),
globSync('src/components/MkUserSetupDialog.*.vue'),
globSync('src/components/MkImgPreviewDialog.vue'),
globSync('src/components/MkInstanceCardMini.vue'),
globSync('src/components/MkInviteCode.vue'),
globSync('src/components/MkTagItem.vue'),
globSync('src/components/MkRoleSelectDialog.vue'),
globSync('src/components/grid/MkGrid.vue'),
globSync('src/pages/admin/custom-emojis-manager2.vue'),
globSync('src/pages/admin/overview.ap-requests.vue'),
globSync('src/pages/user/home.vue'),
globSync('src/pages/search.vue'),
].flat();
await Promise.all(components.map(async (component) => {
const stories = component.replace(/\.vue$/, '.stories.ts');
await writeFile(stories, await toStories(component));

View File

@@ -16,7 +16,6 @@ import {
type PluginOption
} from 'vite';
import fs from 'node:fs';
import { glob } from 'glob';
import JSON5 from 'json5';
import MagicString, { SourceMap } from 'magic-string';
import path from 'node:path'
@@ -724,7 +723,7 @@ export function pluginCreateSearchIndexVirtualModule(options: Options, asigner:
async load(id) {
if (id == '\0' + allSearchIndexFile) {
const files = await Promise.all(options.targetFilePaths.map(async (filePathPattern) => await glob(filePathPattern))).then(paths => paths.flat());
const files = options.targetFilePaths.map((filePathPattern) => fs.globSync(filePathPattern)).flat();
let generatedFile = '';
let arrayElements = '';
for (let file of files) {

View File

@@ -19,21 +19,19 @@
"@analytics/google-analytics": "1.1.0",
"@discordapp/twemoji": "16.0.1",
"@github/webauthn-json": "2.1.1",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@mcaptcha/vanilla-glue": "0.1.0-rc2",
"i18n": "workspace:*",
"@misskey-dev/browser-image-resizer": "2024.1.0",
"@rollup/plugin-json": "6.1.0",
"@rollup/plugin-replace": "6.0.3",
"@rollup/pluginutils": "5.3.0",
"@sentry/vue": "10.27.0",
"@sentry/vue": "10.29.0",
"@syuilo/aiscript": "1.2.0",
"@syuilo/aiscript-0-19-0": "npm:@syuilo/aiscript@^0.19.0",
"@twemoji/parser": "16.0.0",
"@vitejs/plugin-vue": "6.0.2",
"@vue/compiler-sfc": "3.5.25",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.16",
"analytics": "0.8.19",
"astring": "1.9.0",
"broadcast-channel": "7.2.0",
"buraha": "0.0.1",
"canvas-confetti": "1.9.4",
@@ -46,10 +44,9 @@
"compare-versions": "6.1.1",
"cropperjs": "2.1.0",
"date-fns": "4.1.0",
"estree-walker": "3.0.3",
"eventemitter3": "5.0.1",
"execa": "9.6.0",
"exifreader": "4.32.0",
"execa": "9.6.1",
"exifreader": "4.33.1",
"frontend-shared": "workspace:*",
"icons-subsetter": "workspace:*",
"idb-keyval": "6.2.2",
@@ -57,9 +54,8 @@
"ios-haptics": "0.1.4",
"is-file-animated": "1.0.2",
"json5": "2.2.3",
"magic-string": "0.30.21",
"matter-js": "0.20.0",
"mediabunny": "1.25.3",
"mediabunny": "1.25.7",
"mfm-js": "0.25.0",
"misskey-bubble-game": "workspace:*",
"misskey-js": "workspace:*",
@@ -70,18 +66,14 @@
"qr-scanner": "1.4.2",
"rollup": "4.53.3",
"sanitize-html": "2.17.0",
"sass": "1.94.2",
"shiki": "3.17.0",
"strict-event-emitter-types": "2.0.0",
"sass": "1.95.0",
"shiki": "3.19.0",
"textarea-caret": "3.1.0",
"three": "0.181.2",
"throttle-debounce": "5.0.2",
"tinycolor2": "1.6.0",
"tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0",
"typescript": "5.9.3",
"v-code-diff": "1.13.1",
"vite": "7.2.4",
"vite": "7.2.7",
"vue": "3.5.25",
"vuedraggable": "next",
"wanakana": "5.3.1"
@@ -90,7 +82,7 @@
"@misskey-dev/summaly": "5.2.5",
"@storybook/addon-essentials": "8.6.14",
"@storybook/addon-interactions": "8.6.14",
"@storybook/addon-links": "10.1.0",
"@storybook/addon-links": "10.1.4",
"@storybook/addon-mdx-gfm": "8.6.14",
"@storybook/addon-storysource": "8.6.14",
"@storybook/blocks": "8.6.14",
@@ -98,58 +90,59 @@
"@storybook/core-events": "8.6.14",
"@storybook/manager-api": "8.6.14",
"@storybook/preview-api": "8.6.14",
"@storybook/react": "10.1.0",
"@storybook/react-vite": "10.1.0",
"@storybook/react": "10.1.4",
"@storybook/react-vite": "10.1.4",
"@storybook/test": "8.6.14",
"@storybook/theming": "8.6.14",
"@storybook/types": "8.6.14",
"@storybook/vue3": "10.1.0",
"@storybook/vue3-vite": "10.1.0",
"@storybook/vue3": "10.1.4",
"@storybook/vue3-vite": "10.1.4",
"@tabler/icons-webfont": "3.35.0",
"@testing-library/vue": "8.1.0",
"@types/canvas-confetti": "1.9.0",
"@types/estree": "1.0.8",
"@types/matter-js": "0.20.2",
"@types/micromatch": "4.0.10",
"@types/node": "24.10.1",
"@types/node": "24.10.2",
"@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/sanitize-html": "2.16.0",
"@types/seedrandom": "3.0.8",
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.48.0",
"@typescript-eslint/parser": "8.48.0",
"@vitest/coverage-v8": "4.0.14",
"@typescript-eslint/eslint-plugin": "8.49.0",
"@typescript-eslint/parser": "8.49.0",
"@vitest/coverage-v8": "4.0.15",
"@vue/compiler-core": "3.5.25",
"@vue/runtime-core": "3.5.25",
"acorn": "8.15.0",
"astring": "1.9.0",
"cross-env": "10.1.0",
"cypress": "15.7.0",
"cypress": "15.7.1",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-vue": "10.6.2",
"fast-glob": "3.3.3",
"estree-walker": "3.0.3",
"happy-dom": "20.0.11",
"intersection-observer": "0.12.2",
"magic-string": "0.30.21",
"micromatch": "4.0.8",
"minimatch": "10.1.1",
"msw": "2.12.3",
"msw": "2.12.4",
"msw-storybook-addon": "2.0.6",
"nodemon": "3.1.11",
"prettier": "3.7.1",
"react": "19.2.0",
"react-dom": "19.2.0",
"prettier": "3.7.4",
"react": "19.2.1",
"react-dom": "19.2.1",
"seedrandom": "3.0.5",
"start-server-and-test": "2.1.3",
"storybook": "10.1.0",
"storybook": "10.1.4",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"tsx": "4.20.6",
"vite-plugin-glsl": "1.5.4",
"tsx": "4.21.0",
"typescript": "5.9.3",
"vite-plugin-glsl": "1.5.5",
"vite-plugin-turbosnap": "1.0.3",
"vitest": "4.0.14",
"vitest": "4.0.15",
"vitest-fetch-mock": "0.4.5",
"vue-component-type-helpers": "3.1.5",
"vue-component-type-helpers": "3.1.7",
"vue-eslint-parser": "10.2.0",
"vue-tsc": "3.1.5"
"vue-tsc": "3.1.8"
}
}

View File

@@ -42,6 +42,8 @@
console.error('invalid lang value detected!!!', typeof lang, lang);
lang = 'en-US';
}
localStorage.setItem('lang', lang);
//#endregion
//#region Script

View File

@@ -83,34 +83,58 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<MkButton v-if="foldersPaginator.canFetchOlder.value" primary rounded @click="foldersPaginator.fetchOlder()">{{ i18n.ts.loadMore }}</MkButton>
<MkStickyContainer v-for="(item, i) in filesTimeline" :key="`${item.date.getFullYear()}/${item.date.getMonth() + 1}`">
<template #header>
<div :class="$style.date">
<span><i class="ti ti-chevron-down"></i> {{ item.date.getFullYear() }}/{{ item.date.getMonth() + 1 }}</span>
</div>
</template>
<template v-if="shouldBeGroupedByDate">
<MkStickyContainer v-for="(item, i) in filesTimeline" :key="`${item.date.getFullYear()}/${item.date.getMonth() + 1}`">
<template #header>
<div :class="$style.date">
<span><i class="ti ti-chevron-down"></i> {{ item.date.getFullYear() }}/{{ item.date.getMonth() + 1 }}</span>
</div>
</template>
<TransitionGroup
tag="div"
:enterActiveClass="prefer.s.animation ? $style.transition_files_enterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.transition_files_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_files_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_files_leaveTo : ''"
:moveClass="prefer.s.animation ? $style.transition_files_move : ''"
:class="$style.files"
>
<XFile
v-for="file in item.items" :key="file.id"
:class="$style.file"
:file="file"
:folder="folder"
:isSelected="selectedFiles.some(x => x.id === file.id)"
@click="onFileClick($event, file)"
@dragstart="onFileDragstart(file, $event)"
@dragend="isDragSource = false"
/>
</TransitionGroup>
</MkStickyContainer>
</template>
<TransitionGroup
v-else
tag="div"
:enterActiveClass="prefer.s.animation ? $style.transition_files_enterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.transition_files_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_files_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_files_leaveTo : ''"
:moveClass="prefer.s.animation ? $style.transition_files_move : ''"
:class="$style.files"
>
<XFile
v-for="file in filesPaginator.items.value" :key="file.id"
:class="$style.file"
:file="file"
:folder="folder"
:isSelected="selectedFiles.some(x => x.id === file.id)"
@click="onFileClick($event, file)"
@dragstart="onFileDragstart(file, $event)"
@dragend="isDragSource = false"
/>
</TransitionGroup>
<TransitionGroup
tag="div"
:enterActiveClass="prefer.s.animation ? $style.transition_files_enterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.transition_files_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_files_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_files_leaveTo : ''"
:moveClass="prefer.s.animation ? $style.transition_files_move : ''"
:class="$style.files"
>
<XFile
v-for="file in item.items" :key="file.id"
:class="$style.file"
:file="file"
:folder="folder"
:isSelected="selectedFiles.some(x => x.id === file.id)"
@click="onFileClick($event, file)"
@dragstart="onFileDragstart(file, $event)"
@dragend="isDragSource = false"
/>
</TransitionGroup>
</MkStickyContainer>
<MkButton v-show="filesPaginator.canFetchOlder.value" :class="$style.loadMore" primary rounded @click="filesPaginator.fetchOlder()">{{ i18n.ts.loadMore }}</MkButton>
<div v-if="filesPaginator.items.value.length == 0 && foldersPaginator.items.value.length == 0 && !fetching" :class="$style.empty">
@@ -217,6 +241,7 @@ const foldersPaginator = markRaw(new Paginator('drive/folders', {
}));
const filesTimeline = makeDateGroupedTimelineComputedRef(filesPaginator.items, 'month');
const shouldBeGroupedByDate = computed(() => ['+createdAt', '-createdAt'].includes(sortModeSelect.value));
watch(folder, () => emit('cd', folder.value));
watch(sortModeSelect, () => {

View File

@@ -31,6 +31,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div :class="$style.controls">
<div class="_spacer _gaps">
<MkButton inline rounded primary @click="chooseFile">{{ i18n.ts.selectFile }}</MkButton>
<MkRange v-model="params.borderThickness" :min="0" :max="0.2" :step="0.01" :continuousUpdate="true">
<template #label>{{ i18n.ts._imageFrameEditor.borderThickness }}</template>
</MkRange>
@@ -175,6 +177,7 @@ import { ensureSignin } from '@/i.js';
import { genId } from '@/utility/id.js';
import { useMkSelect } from '@/composables/use-mkselect.js';
import { prefer } from '@/preferences.js';
import { selectFile } from '@/utility/drive.js';
const $i = ensureSignin();
@@ -216,6 +219,7 @@ const params = reactive<ImageFrameParams>(deepClone(props.params) ?? {
bgColor: [1, 1, 1],
fgColor: [0, 0, 0],
font: 'sans-serif',
imageUrl: null,
});
const emit = defineEmits<{
@@ -409,6 +413,28 @@ function getRgb(hex: string | number): [number, number, number] | null {
if (m == null) return [0, 0, 0];
return m.map(x => parseInt(x, 16) / 255) as [number, number, number];
}
function chooseFile(ev: MouseEvent) {
selectFile({
anchorElement: ev.currentTarget ?? ev.target,
multiple: false,
label: i18n.ts.selectFile,
features: {
watermark: false,
},
}).then((file) => {
if (!file.type.startsWith('image')) {
os.alert({
type: 'warning',
title: i18n.ts._watermarkEditor.driveFileTypeWarn,
text: i18n.ts._watermarkEditor.driveFileTypeWarnDescription,
});
return;
}
params.imageUrl = file.url;
});
}
</script>
<style module>

View File

@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkModalWindow>
</template>
<script lang="ts" setup>
import { defineProps, ref } from 'vue';
import { ref } from 'vue';
import MkModalWindow from './MkModalWindow.vue';
import type * as Misskey from 'misskey-js';

View File

@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div
v-if="!hardMuted && muted === false"
v-if="!hardMuted && !hideByPlugin && muted === false"
ref="rootEl"
v-hotkey="keymap"
:class="[$style.root, { [$style.showActionsOnlyHover]: prefer.s.showNoteActionsOnlyHover, [$style.skipRender]: prefer.s.skipNoteRender }]"
@@ -38,7 +38,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ti ti-device-tv"></i></span>
</div>
</div>
<div v-if="renoteCollapsed" :class="$style.collapsedRenoteTarget">
<div v-if="isRenote && note.renote == null" :class="$style.deleted">
{{ i18n.ts.deletedNote }}
</div>
<div v-else-if="renoteCollapsed" :class="$style.collapsedRenoteTarget">
<MkAvatar :class="$style.collapsedRenoteTargetAvatar" :user="appearNote.user" link preview/>
<Mfm :text="getNoteSummary(appearNote)" :plain="true" :nowrap="true" :author="appearNote.user" :nyaize="'respect'" :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/>
</div>
@@ -158,7 +161,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</article>
</div>
<div v-else-if="!hardMuted" :class="$style.muted" @click="muted = false">
<div v-else-if="!hardMuted && !hideByPlugin" :class="$style.muted" @click="muted = false">
<I18n v-if="muted === 'sensitiveMute'" :src="i18n.ts.userSaysSomethingSensitive" tag="small">
<template #name>
<MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)">
@@ -267,6 +270,7 @@ let note = deepClone(props.note);
// plugin
const noteViewInterruptors = getPluginHandlers('note_view_interruptor');
const hideByPlugin = ref(false);
if (noteViewInterruptors.length > 0) {
let result: Misskey.entities.Note | null = deepClone(note);
for (const interruptor of noteViewInterruptors) {
@@ -276,7 +280,11 @@ if (noteViewInterruptors.length > 0) {
console.error(err);
}
}
note = result as Misskey.entities.Note;
if (result == null) {
hideByPlugin.value = true;
} else {
note = result as Misskey.entities.Note;
}
}
const isRenote = Misskey.note.isPureRenote(note);
@@ -1144,4 +1152,14 @@ function emitUpdReaction(emoji: string, delta: number) {
opacity: .8;
font-size: 95%;
}
.deleted {
text-align: center;
padding: 32px;
margin: 6px 32px 28px;
--color: light-dark(rgba(0, 0, 0, 0.05), rgba(0, 0, 0, 0.15));
background-size: auto auto;
background-image: repeating-linear-gradient(135deg, transparent, transparent 10px, var(--color) 4px, var(--color) 14px);
border-radius: 8px;
}
</style>

View File

@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div
v-if="!muted && !isDeleted"
v-if="!muted && !hideByPlugin && !isDeleted"
ref="rootEl"
v-hotkey="keymap"
:class="$style.root"
@@ -43,178 +43,183 @@ SPDX-License-Identifier: AGPL-3.0-only
<span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></i></span>
</div>
</div>
<article :class="$style.note" @contextmenu.stop="onContextmenu">
<header :class="$style.noteHeader">
<MkAvatar :class="$style.noteHeaderAvatar" :user="appearNote.user" indicator link preview/>
<div :class="$style.noteHeaderBody">
<div>
<MkA v-user-preview="appearNote.user.id" :class="$style.noteHeaderName" :to="userPage(appearNote.user)">
<MkUserName :nowrap="false" :user="appearNote.user"/>
<div v-if="isRenote && note.renote == null" :class="$style.deleted">
{{ i18n.ts.deletedNote }}
</div>
<template v-else>
<article :class="$style.note" @contextmenu.stop="onContextmenu">
<header :class="$style.noteHeader">
<MkAvatar :class="$style.noteHeaderAvatar" :user="appearNote.user" indicator link preview/>
<div :class="$style.noteHeaderBody">
<div>
<MkA v-user-preview="appearNote.user.id" :class="$style.noteHeaderName" :to="userPage(appearNote.user)">
<MkUserName :nowrap="false" :user="appearNote.user"/>
</MkA>
<span v-if="appearNote.user.isBot" :class="$style.isBot">bot</span>
<div :class="$style.noteHeaderInfo">
<span v-if="appearNote.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[appearNote.visibility]">
<i v-if="appearNote.visibility === 'home'" class="ti ti-home"></i>
<i v-else-if="appearNote.visibility === 'followers'" class="ti ti-lock"></i>
<i v-else-if="appearNote.visibility === 'specified'" ref="specified" class="ti ti-mail"></i>
</span>
<span v-if="appearNote.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></i></span>
</div>
</div>
<div :class="$style.noteHeaderUsernameAndBadgeRoles">
<div :class="$style.noteHeaderUsername">
<MkAcct :user="appearNote.user"/>
</div>
<div v-if="appearNote.user.badgeRoles" :class="$style.noteHeaderBadgeRoles">
<img v-for="(role, i) in appearNote.user.badgeRoles" :key="i" v-tooltip="role.name" :class="$style.noteHeaderBadgeRole" :src="role.iconUrl!"/>
</div>
</div>
<MkInstanceTicker v-if="showTicker" :host="appearNote.user.host" :instance="appearNote.user.instance"/>
</div>
</header>
<div :class="$style.noteContent">
<p v-if="appearNote.cw != null" :class="$style.cw">
<Mfm
v-if="appearNote.cw != ''"
:text="appearNote.cw"
:author="appearNote.user"
:nyaize="'respect'"
:enableEmojiMenu="true"
:enableEmojiMenuReaction="true"
/>
<MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll"/>
</p>
<div v-show="appearNote.cw == null || showContent">
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
<MkA v-if="appearNote.replyId" :class="$style.noteReplyTarget" :to="`/notes/${appearNote.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA>
<Mfm
v-if="appearNote.text"
:parsedNodes="parsed"
:text="appearNote.text"
:author="appearNote.user"
:nyaize="'respect'"
:emojiUrls="appearNote.emojis"
:enableEmojiMenu="true"
:enableEmojiMenuReaction="true"
class="_selectable"
/>
<a v-if="appearNote.renote != null" :class="$style.rn">RN:</a>
<div v-if="translating || translation" :class="$style.translation">
<MkLoading v-if="translating" mini/>
<div v-else-if="translation">
<b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b>
<Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis" class="_selectable"/>
</div>
</div>
<div v-if="appearNote.files && appearNote.files.length > 0">
<MkMediaList ref="galleryEl" :mediaList="appearNote.files"/>
</div>
<MkPoll
v-if="appearNote.poll"
:noteId="appearNote.id"
:multiple="appearNote.poll.multiple"
:expiresAt="appearNote.poll.expiresAt"
:choices="$appearNote.pollChoices"
:author="appearNote.user"
:emojiUrls="appearNote.emojis"
:class="$style.poll"
/>
<div v-if="isEnabledUrlPreview">
<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="true" style="margin-top: 6px;"/>
</div>
<div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div>
</div>
<MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA>
</div>
<footer>
<div :class="$style.noteFooterInfo">
<MkA :to="notePage(appearNote)">
<MkTime :time="appearNote.createdAt" mode="detail" colored/>
</MkA>
<span v-if="appearNote.user.isBot" :class="$style.isBot">bot</span>
<div :class="$style.noteHeaderInfo">
<span v-if="appearNote.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[appearNote.visibility]">
<i v-if="appearNote.visibility === 'home'" class="ti ti-home"></i>
<i v-else-if="appearNote.visibility === 'followers'" class="ti ti-lock"></i>
<i v-else-if="appearNote.visibility === 'specified'" ref="specified" class="ti ti-mail"></i>
</span>
<span v-if="appearNote.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></i></span>
</div>
</div>
<div :class="$style.noteHeaderUsernameAndBadgeRoles">
<div :class="$style.noteHeaderUsername">
<MkAcct :user="appearNote.user"/>
</div>
<div v-if="appearNote.user.badgeRoles" :class="$style.noteHeaderBadgeRoles">
<img v-for="(role, i) in appearNote.user.badgeRoles" :key="i" v-tooltip="role.name" :class="$style.noteHeaderBadgeRole" :src="role.iconUrl!"/>
</div>
</div>
<MkInstanceTicker v-if="showTicker" :host="appearNote.user.host" :instance="appearNote.user.instance"/>
</div>
</header>
<div :class="$style.noteContent">
<p v-if="appearNote.cw != null" :class="$style.cw">
<Mfm
v-if="appearNote.cw != ''"
:text="appearNote.cw"
:author="appearNote.user"
:nyaize="'respect'"
:enableEmojiMenu="true"
:enableEmojiMenuReaction="true"
/>
<MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll"/>
</p>
<div v-show="appearNote.cw == null || showContent">
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
<MkA v-if="appearNote.replyId" :class="$style.noteReplyTarget" :to="`/notes/${appearNote.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA>
<Mfm
v-if="appearNote.text"
:parsedNodes="parsed"
:text="appearNote.text"
:author="appearNote.user"
:nyaize="'respect'"
:emojiUrls="appearNote.emojis"
:enableEmojiMenu="true"
:enableEmojiMenuReaction="true"
class="_selectable"
/>
<a v-if="appearNote.renote != null" :class="$style.rn">RN:</a>
<div v-if="translating || translation" :class="$style.translation">
<MkLoading v-if="translating" mini/>
<div v-else-if="translation">
<b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b>
<Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis" class="_selectable"/>
</div>
</div>
<div v-if="appearNote.files && appearNote.files.length > 0">
<MkMediaList ref="galleryEl" :mediaList="appearNote.files"/>
</div>
<MkPoll
v-if="appearNote.poll"
<MkReactionsViewer
v-if="appearNote.reactionAcceptance !== 'likeOnly'"
style="margin-top: 6px;"
:reactions="$appearNote.reactions"
:reactionEmojis="$appearNote.reactionEmojis"
:myReaction="$appearNote.myReaction"
:noteId="appearNote.id"
:multiple="appearNote.poll.multiple"
:expiresAt="appearNote.poll.expiresAt"
:choices="$appearNote.pollChoices"
:author="appearNote.user"
:emojiUrls="appearNote.emojis"
:class="$style.poll"
:maxNumber="16"
@mockUpdateMyReaction="emitUpdReaction"
/>
<div v-if="isEnabledUrlPreview">
<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="true" style="margin-top: 6px;"/>
</div>
<div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div>
</div>
<MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA>
</div>
<footer>
<div :class="$style.noteFooterInfo">
<MkA :to="notePage(appearNote)">
<MkTime :time="appearNote.createdAt" mode="detail" colored/>
</MkA>
</div>
<MkReactionsViewer
v-if="appearNote.reactionAcceptance !== 'likeOnly'"
style="margin-top: 6px;"
:reactions="$appearNote.reactions"
:reactionEmojis="$appearNote.reactionEmojis"
:myReaction="$appearNote.myReaction"
:noteId="appearNote.id"
:maxNumber="16"
@mockUpdateMyReaction="emitUpdReaction"
/>
<button class="_button" :class="$style.noteFooterButton" @click="reply()">
<i class="ti ti-arrow-back-up"></i>
<p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.repliesCount) }}</p>
</button>
<button
v-if="canRenote"
ref="renoteButton"
class="_button"
:class="$style.noteFooterButton"
@mousedown.prevent="renote()"
>
<i class="ti ti-repeat"></i>
<p v-if="appearNote.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.renoteCount) }}</p>
</button>
<button v-else class="_button" :class="$style.noteFooterButton" disabled>
<i class="ti ti-ban"></i>
</button>
<button ref="reactButton" :class="$style.noteFooterButton" class="_button" @click="toggleReact()">
<i v-if="appearNote.reactionAcceptance === 'likeOnly' && $appearNote.myReaction != null" class="ti ti-heart-filled" style="color: var(--MI_THEME-love);"></i>
<i v-else-if="$appearNote.myReaction != null" class="ti ti-minus" style="color: var(--MI_THEME-accent);"></i>
<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
<i v-else class="ti ti-plus"></i>
<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || prefer.s.showReactionsCount) && $appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number($appearNote.reactionCount) }}</p>
</button>
<button v-if="prefer.s.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown.prevent="clip()">
<i class="ti ti-paperclip"></i>
</button>
<button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown.prevent="showMenu()">
<i class="ti ti-dots"></i>
</button>
</footer>
</article>
<div :class="$style.tabs">
<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'replies' }]" @click="tab = 'replies'"><i class="ti ti-arrow-back-up"></i> {{ i18n.ts.replies }}</button>
<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'renotes' }]" @click="tab = 'renotes'"><i class="ti ti-repeat"></i> {{ i18n.ts.renotes }}</button>
<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'reactions' }]" @click="tab = 'reactions'"><i class="ti ti-icons"></i> {{ i18n.ts.reactions }}</button>
</div>
<div>
<div v-if="tab === 'replies'">
<div v-if="!repliesLoaded" style="padding: 16px">
<MkButton style="margin: 0 auto;" primary rounded @click="loadReplies">{{ i18n.ts.loadReplies }}</MkButton>
</div>
<MkNoteSub v-for="note in replies" :key="note.id" :note="note" :class="$style.reply" :detail="true"/>
</div>
<div v-else-if="tab === 'renotes'" :class="$style.tab_renotes">
<MkPagination :paginator="renotesPaginator">
<template #default="{ items }">
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); grid-gap: 12px;">
<MkA v-for="item in items" :key="item.id" :to="userPage(item.user)">
<MkUserCardMini :user="item.user" :withChart="false"/>
</MkA>
</div>
</template>
</MkPagination>
</div>
<div v-else-if="tab === 'reactions'" :class="$style.tab_reactions">
<div :class="$style.reactionTabs">
<button v-for="reaction in Object.keys($appearNote.reactions)" :key="reaction" :class="[$style.reactionTab, { [$style.reactionTabActive]: reactionTabType === reaction }]" class="_button" @click="reactionTabType = reaction">
<MkReactionIcon :reaction="reaction"/>
<span style="margin-left: 4px;">{{ $appearNote.reactions[reaction] }}</span>
<button class="_button" :class="$style.noteFooterButton" @click="reply()">
<i class="ti ti-arrow-back-up"></i>
<p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.repliesCount) }}</p>
</button>
</div>
<MkPagination v-if="reactionTabType" :key="reactionTabType" :paginator="reactionsPaginator">
<template #default="{ items }">
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); grid-gap: 12px;">
<MkA v-for="item in items" :key="item.id" :to="userPage(item.user)">
<MkUserCardMini :user="item.user" :withChart="false"/>
</MkA>
</div>
</template>
</MkPagination>
<button
v-if="canRenote"
ref="renoteButton"
class="_button"
:class="$style.noteFooterButton"
@mousedown.prevent="renote()"
>
<i class="ti ti-repeat"></i>
<p v-if="appearNote.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.renoteCount) }}</p>
</button>
<button v-else class="_button" :class="$style.noteFooterButton" disabled>
<i class="ti ti-ban"></i>
</button>
<button ref="reactButton" :class="$style.noteFooterButton" class="_button" @click="toggleReact()">
<i v-if="appearNote.reactionAcceptance === 'likeOnly' && $appearNote.myReaction != null" class="ti ti-heart-filled" style="color: var(--MI_THEME-love);"></i>
<i v-else-if="$appearNote.myReaction != null" class="ti ti-minus" style="color: var(--MI_THEME-accent);"></i>
<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
<i v-else class="ti ti-plus"></i>
<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || prefer.s.showReactionsCount) && $appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number($appearNote.reactionCount) }}</p>
</button>
<button v-if="prefer.s.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown.prevent="clip()">
<i class="ti ti-paperclip"></i>
</button>
<button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown.prevent="showMenu()">
<i class="ti ti-dots"></i>
</button>
</footer>
</article>
<div :class="$style.tabs">
<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'replies' }]" @click="tab = 'replies'"><i class="ti ti-arrow-back-up"></i> {{ i18n.ts.replies }}</button>
<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'renotes' }]" @click="tab = 'renotes'"><i class="ti ti-repeat"></i> {{ i18n.ts.renotes }}</button>
<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'reactions' }]" @click="tab = 'reactions'"><i class="ti ti-icons"></i> {{ i18n.ts.reactions }}</button>
</div>
</div>
<div>
<div v-if="tab === 'replies'">
<div v-if="!repliesLoaded" style="padding: 16px">
<MkButton style="margin: 0 auto;" primary rounded @click="loadReplies">{{ i18n.ts.loadReplies }}</MkButton>
</div>
<MkNoteSub v-for="note in replies" :key="note.id" :note="note" :class="$style.reply" :detail="true"/>
</div>
<div v-else-if="tab === 'renotes'" :class="$style.tab_renotes">
<MkPagination :paginator="renotesPaginator" :forceDisableInfiniteScroll="true">
<template #default="{ items }">
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); grid-gap: 12px;">
<MkA v-for="item in items" :key="item.id" :to="userPage(item.user)">
<MkUserCardMini :user="item.user" :withChart="false"/>
</MkA>
</div>
</template>
</MkPagination>
</div>
<div v-else-if="tab === 'reactions'" :class="$style.tab_reactions">
<div :class="$style.reactionTabs">
<button v-for="reaction in Object.keys($appearNote.reactions)" :key="reaction" :class="[$style.reactionTab, { [$style.reactionTabActive]: reactionTabType === reaction }]" class="_button" @click="reactionTabType = reaction">
<MkReactionIcon :reaction="reaction"/>
<span style="margin-left: 4px;">{{ $appearNote.reactions[reaction] }}</span>
</button>
</div>
<MkPagination v-if="reactionTabType" :key="reactionTabType" :paginator="reactionsPaginator" :forceDisableInfiniteScroll="true">
<template #default="{ items }">
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); grid-gap: 12px;">
<MkA v-for="item in items" :key="item.id" :to="userPage(item.user)">
<MkUserCardMini :user="item.user" :withChart="false"/>
</MkA>
</div>
</template>
</MkPagination>
</div>
</div>
</template>
</div>
<div v-else-if="muted" class="_panel" :class="$style.muted" @click="muted = false">
<I18n :src="i18n.ts.userSaysSomething" tag="small">
@@ -289,6 +294,7 @@ let note = deepClone(props.note);
// plugin
const noteViewInterruptors = getPluginHandlers('note_view_interruptor');
const hideByPlugin = ref(false);
if (noteViewInterruptors.length > 0) {
let result: Misskey.entities.Note | null = deepClone(note);
for (const interruptor of noteViewInterruptors) {
@@ -298,7 +304,11 @@ if (noteViewInterruptors.length > 0) {
console.error(err);
}
}
note = result as Misskey.entities.Note;
if (result == null) {
hideByPlugin.value = true;
} else {
note = result as Misskey.entities.Note;
}
}
const isRenote = Misskey.note.isPureRenote(note);
@@ -944,4 +954,14 @@ function loadConversation() {
text-align: center;
opacity: 0.7;
}
.deleted {
text-align: center;
padding: 32px;
margin: 6px 32px 32px;
--color: light-dark(rgba(0, 0, 0, 0.05), rgba(0, 0, 0, 0.15));
background-size: auto auto;
background-image: repeating-linear-gradient(135deg, transparent, transparent 10px, var(--color) 4px, var(--color) 14px);
border-radius: 8px;
}
</style>

View File

@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-if="showDecoration">
<img
v-for="decoration in decorations ?? user.avatarDecorations"
:class="[$style.decoration, { [$style.decorationBlink]: decoration.blink }]"
:class="[$style.decoration, { [$style.decorationBlink]: getDecorationIsBrink(decoration) }]"
:src="getDecorationUrl(decoration)"
:style="{
rotate: getDecorationAngle(decoration),
@@ -56,13 +56,16 @@ import { prefer } from '@/preferences.js';
const animation = ref(prefer.s.animation);
const squareAvatars = ref(prefer.s.squareAvatars);
type Decoration = Misskey.entities.UserDetailed['avatarDecorations'][number];
type DecorationEditorDecoration = Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'> & { blink?: boolean; };
const props = withDefaults(defineProps<{
user: Misskey.entities.User;
target?: string | null;
link?: boolean;
preview?: boolean;
indicator?: boolean;
decorations?: (Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'> & { blink?: boolean; })[];
decorations?: DecorationEditorDecoration[];
forceShowDecoration?: boolean;
}>(), {
target: null,
@@ -93,27 +96,31 @@ function onClick(ev: MouseEvent): void {
emit('click', ev);
}
function getDecorationUrl(decoration: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>) {
function getDecorationUrl(decoration: Decoration | DecorationEditorDecoration) {
if (prefer.s.disableShowingAnimatedImages || prefer.s.dataSaver.avatar) return getStaticImageUrl(decoration.url);
return decoration.url;
}
function getDecorationAngle(decoration: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>) {
function getDecorationAngle(decoration: Decoration | DecorationEditorDecoration) {
const angle = decoration.angle ?? 0;
return angle === 0 ? undefined : `${angle * 360}deg`;
}
function getDecorationScale(decoration: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>) {
function getDecorationScale(decoration: Decoration | DecorationEditorDecoration) {
const scaleX = decoration.flipH ? -1 : 1;
return scaleX === 1 ? undefined : `${scaleX} 1`;
}
function getDecorationOffset(decoration: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>) {
function getDecorationOffset(decoration: Decoration | DecorationEditorDecoration) {
const offsetX = decoration.offsetX ?? 0;
const offsetY = decoration.offsetY ?? 0;
return offsetX === 0 && offsetY === 0 ? undefined : `${offsetX * 100}% ${offsetY * 100}%`;
}
function getDecorationIsBrink(decoration: Decoration | DecorationEditorDecoration) {
return 'blink' in decoration && decoration.blink === true;
}
const color = ref<string | undefined>();
watch(() => props.user.avatarBlurhash, () => {

View File

@@ -77,7 +77,7 @@ export const components = {
SearchIcon: SearchIcon,
};
declare module '@vue/runtime-core' {
declare module 'vue' {
export interface GlobalComponents {
I18n: typeof I18n;
RouterView: typeof RouterView;

View File

@@ -257,7 +257,7 @@ async function search() {
}
const headerActions = computed(() => {
if (channel.value && channel.value.userId) {
if (channel.value) {
const headerItems: PageHeaderItem[] = [];
headerItems.push({

View File

@@ -63,7 +63,7 @@ async function setAntenna() {
})),
} : undefined),
],
default: props.column.antennaId,
default: antennas.find(x => x.id === props.column.antennaId)?.id,
});
if (canceled || antennaIdOrOperation == null) return;

View File

@@ -63,7 +63,7 @@ async function setChannel() {
items: channels.map(x => ({
value: x.id, label: x.name,
})),
default: props.column.channelId,
default: channels.find(x => x.id === props.column.channelId)?.id,
});
if (canceled || chosenChannelId == null) return;
const chosenChannel = channels.find(x => x.id === chosenChannelId)!;

View File

@@ -70,7 +70,7 @@ async function setList() {
})),
} : undefined),
],
default: props.column.listId,
default: lists.find(x => x.id === props.column.listId)?.id,
});
if (canceled || listIdOrOperation == null) return;

View File

@@ -54,7 +54,7 @@ async function setRole() {
items: roles.map(x => ({
value: x.id, label: x.name,
})),
default: props.column.roleId,
default: roles.find(x => x.id === props.column.roleId)?.id,
});
if (canceled || roleId == null) return;
const role = roles.find(x => x.id === roleId)!;

View File

@@ -104,6 +104,7 @@ async function setType() {
}, {
value: 'global', label: i18n.ts._timelines.global,
}],
default: props.column.tl,
});
if (canceled) {
if (props.column.tl == null) {

View File

@@ -30,6 +30,7 @@ export type ImageFrameParams = {
fgColor: [r: number, g: number, b: number];
font: 'serif' | 'sans-serif';
borderRadius: number; // TODO
imageUrl: string | null;
};
export type ImageFramePreset = {
@@ -241,6 +242,17 @@ export class ImageFrameRenderer {
this.compositor.registerTexture('bottomLabel', bottomLabelImage);
}
if (params.imageUrl != null) {
const frameImage = new Image();
frameImage.crossOrigin = 'anonymous';
await new Promise<void>((resolve, reject) => {
frameImage.onload = () => resolve();
frameImage.onerror = () => reject(new Error('Failed to load frame image'));
frameImage.src = params.imageUrl!;
});
this.compositor.registerTexture('frameImage', frameImage);
}
this.compositor.changeResolution(renderWidth, renderHeight);
this.compositor.render([{
@@ -248,6 +260,7 @@ export class ImageFrameRenderer {
id: 'a',
params: {
image: 'image',
frameImage: 'frameImage',
topLabel: 'topLabel',
bottomLabel: 'bottomLabel',
topLabelEnabled: params.labelTop.enabled,

View File

@@ -10,6 +10,7 @@ in vec2 in_uv;
uniform sampler2D in_texture;
uniform vec2 in_resolution;
uniform sampler2D u_image;
uniform sampler2D u_frameImage;
uniform sampler2D u_topLabel;
uniform sampler2D u_bottomLabel;
uniform bool u_topLabelEnabled;
@@ -37,6 +38,9 @@ void main() {
remap(in_uv.y, u_paddingTop, 1.0 - u_paddingBottom, 0.0, 1.0)
));
vec4 imageFrame_color = texture(u_frameImage, in_uv);
image_color = vec4(blendAlpha(image_color.rgb, imageFrame_color), 1.0);
vec4 topLabel_color = u_topLabelEnabled ? texture(u_topLabel, vec2(
in_uv.x,
remap(in_uv.y, 0.0, u_paddingTop, 0.0, 1.0)

View File

@@ -8,6 +8,7 @@ import { defineImageCompositorFunction } from '@/lib/ImageCompositor.js';
export const FN_frame = defineImageCompositorFunction<{
image: string | null;
frameImage: string | null;
topLabel: string | null;
bottomLabel: string | null;
topLabelEnabled: boolean;
@@ -36,21 +37,30 @@ export const FN_frame = defineImageCompositorFunction<{
gl.uniform1f(u.paddingRight, params.paddingRight);
gl.uniform3f(u.bg, params.bg[0], params.bg[1], params.bg[2]);
if (params.frameImage != null) {
const frameImage = textures.get(params.frameImage);
if (frameImage) {
gl.activeTexture(gl.TEXTURE2);
gl.bindTexture(gl.TEXTURE_2D, frameImage.texture);
gl.uniform1i(u.frameImage, 2);
}
}
if (params.topLabelEnabled && params.topLabel != null) {
const topLabel = textures.get(params.topLabel);
if (topLabel) {
gl.activeTexture(gl.TEXTURE2);
gl.activeTexture(gl.TEXTURE3);
gl.bindTexture(gl.TEXTURE_2D, topLabel.texture);
gl.uniform1i(u.topLabel, 2);
gl.uniform1i(u.topLabel, 3);
}
}
if (params.bottomLabelEnabled && params.bottomLabel != null) {
const bottomLabel = textures.get(params.bottomLabel);
if (bottomLabel) {
gl.activeTexture(gl.TEXTURE3);
gl.activeTexture(gl.TEXTURE4);
gl.bindTexture(gl.TEXTURE_2D, bottomLabel.texture);
gl.uniform1i(u.bottomLabel, 3);
gl.uniform1i(u.bottomLabel, 4);
}
}
},

View File

@@ -10,7 +10,6 @@ import { watch as chokidarWatch } from 'chokidar';
import * as esbuild from 'esbuild';
import { build } from 'esbuild';
import { execa } from 'execa';
import { globSync } from 'glob';
import { generateLocaleInterface } from './scripts/generateLocaleInterface.js';
import type { BuildOptions, BuildResult, Plugin, PluginBuild } from 'esbuild';
@@ -22,7 +21,7 @@ const _rootPackage = JSON.parse(fs.readFileSync(resolve(_rootPackageDir, 'packag
const _frontendLocalesDir = resolve(_dirname, '../../built/_frontend_dist_/locales');
const _localesDir = resolve(_rootPackageDir, 'locales');
const entryPoints = globSync('./src/**/**.{ts,tsx}');
const entryPoints = fs.globSync('./src/**/**.{ts,tsx}');
const options: BuildOptions = {
entryPoints,

View File

@@ -29,15 +29,14 @@
],
"devDependencies": {
"@types/js-yaml": "4.0.9",
"@types/node": "24.10.1",
"@typescript-eslint/eslint-plugin": "8.47.0",
"@typescript-eslint/parser": "8.47.0",
"chokidar": "4.0.3",
"esbuild": "0.27.0",
"execa": "9.6.0",
"glob": "11.1.0",
"@types/node": "24.10.2",
"@typescript-eslint/eslint-plugin": "8.49.0",
"@typescript-eslint/parser": "8.49.0",
"chokidar": "5.0.0",
"esbuild": "0.27.1",
"execa": "9.6.1",
"nodemon": "3.1.11",
"tsx": "4.20.6",
"tsx": "4.21.0",
"typescript": "5.9.3"
},
"dependencies": {

View File

@@ -11,16 +11,15 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
"@types/node": "24.10.1",
"@types/node": "24.10.2",
"@types/wawoff2": "1.0.2",
"@typescript-eslint/eslint-plugin": "8.48.0",
"@typescript-eslint/parser": "8.48.0"
"@typescript-eslint/eslint-plugin": "8.49.0",
"@typescript-eslint/parser": "8.49.0"
},
"dependencies": {
"@tabler/icons-webfont": "3.35.0",
"harfbuzzjs": "0.4.13",
"tiny-glob": "0.2.9",
"tsx": "4.20.6",
"tsx": "4.21.0",
"typescript": "5.9.3",
"wawoff2": "2.0.1"
},

View File

@@ -3,9 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { promises as fsp, existsSync } from 'fs';
import path from 'path';
import glob from 'tiny-glob';
import { promises as fsp, existsSync } from 'node:fs';
import path from 'node:path';
import { generateSubsettedFont } from './subsetter.js';
const filesToScan = {
@@ -47,8 +46,8 @@ async function main() {
const iconsToPack = new Set<string>();
const cwd = path.resolve(process.cwd(), '../../');
const files = await glob(dir, { cwd });
for (const file of files) {
const files = fsp.glob(dir, { cwd });
for await (const file of files) {
//console.log(`Scanning ${file}`);
const content = await fsp.readFile(path.resolve(cwd, file), 'utf-8');
const classRegex = /ti-[a-z0-9-]+/g;

View File

@@ -3,14 +3,13 @@ import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import * as esbuild from 'esbuild';
import { build } from 'esbuild';
import { globSync } from 'glob';
import { execa } from 'execa';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const _package = JSON.parse(fs.readFileSync(_dirname + '/package.json', 'utf-8'));
const entryPoints = globSync('./src/**/**.{ts,tsx}');
const entryPoints = fs.globSync('./src/**/**.{ts,tsx}');
/** @type {import('esbuild').BuildOptions} */
const options = {

View File

@@ -25,13 +25,12 @@
},
"devDependencies": {
"@types/matter-js": "0.20.2",
"@types/node": "24.10.1",
"@types/node": "24.10.2",
"@types/seedrandom": "3.0.8",
"@typescript-eslint/eslint-plugin": "8.48.0",
"@typescript-eslint/parser": "8.48.0",
"esbuild": "0.27.0",
"execa": "9.6.0",
"glob": "11.1.0",
"@typescript-eslint/eslint-plugin": "8.49.0",
"@typescript-eslint/parser": "8.49.0",
"esbuild": "0.27.1",
"execa": "9.6.1",
"nodemon": "3.1.11",
"typescript": "5.9.3"
},

View File

@@ -3,14 +3,13 @@ import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import * as esbuild from 'esbuild';
import { build } from 'esbuild';
import { globSync } from 'glob';
import { execa } from 'execa';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const _package = JSON.parse(fs.readFileSync(_dirname + '/package.json', 'utf-8'));
const entryPoints = globSync('./src/**/**.{ts,tsx}');
const entryPoints = fs.globSync('./src/**/**.{ts,tsx}');
/** @type {import('esbuild').BuildOptions} */
const options = {

View File

@@ -8,13 +8,13 @@
},
"devDependencies": {
"@readme/openapi-parser": "5.2.1",
"@types/node": "24.10.1",
"@typescript-eslint/eslint-plugin": "8.47.0",
"@typescript-eslint/parser": "8.47.0",
"@types/node": "24.10.2",
"@typescript-eslint/eslint-plugin": "8.49.0",
"@typescript-eslint/parser": "8.49.0",
"openapi-types": "12.1.3",
"openapi-typescript": "7.10.1",
"ts-case-convert": "2.1.0",
"tsx": "4.20.6",
"tsx": "4.21.0",
"typescript": "5.9.3",
"eslint": "9.39.1"
},

View File

@@ -1,7 +1,7 @@
{
"type": "module",
"name": "misskey-js",
"version": "2025.12.0",
"version": "2025.12.1",
"description": "Misskey SDK for JavaScript",
"license": "MIT",
"main": "./built/index.js",
@@ -37,19 +37,18 @@
"directory": "packages/misskey-js"
},
"devDependencies": {
"@microsoft/api-extractor": "7.55.1",
"@types/node": "24.10.1",
"@typescript-eslint/eslint-plugin": "8.47.0",
"@typescript-eslint/parser": "8.47.0",
"@vitest/coverage-v8": "4.0.13",
"esbuild": "0.27.0",
"execa": "9.6.0",
"glob": "13.0.0",
"@microsoft/api-extractor": "7.55.2",
"@types/node": "24.10.2",
"@typescript-eslint/eslint-plugin": "8.49.0",
"@typescript-eslint/parser": "8.49.0",
"@vitest/coverage-v8": "4.0.15",
"esbuild": "0.27.1",
"execa": "9.6.1",
"ncp": "2.0.0",
"nodemon": "3.1.11",
"tsd": "0.33.0",
"typescript": "5.9.3",
"vitest": "4.0.13",
"vitest": "4.0.15",
"vitest-websocket-mock": "0.5.0"
},
"files": [

View File

@@ -3,14 +3,13 @@ import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import * as esbuild from 'esbuild';
import { build } from 'esbuild';
import { globSync } from 'glob';
import { execa } from 'execa';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const _package = JSON.parse(fs.readFileSync(_dirname + '/package.json', 'utf-8'));
const entryPoints = globSync('./src/**/**.{ts,tsx}');
const entryPoints = fs.globSync('./src/**/**.{ts,tsx}');
/** @type {import('esbuild').BuildOptions} */
const options = {

View File

@@ -24,12 +24,11 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
"@types/node": "24.10.1",
"@typescript-eslint/eslint-plugin": "8.48.0",
"@typescript-eslint/parser": "8.48.0",
"esbuild": "0.27.0",
"execa": "9.6.0",
"glob": "11.1.0",
"@types/node": "24.10.2",
"@typescript-eslint/eslint-plugin": "8.49.0",
"@typescript-eslint/parser": "8.49.0",
"esbuild": "0.27.1",
"execa": "9.6.1",
"nodemon": "3.1.11",
"typescript": "5.9.3"
},

View File

@@ -10,12 +10,12 @@
},
"dependencies": {
"i18n": "workspace:*",
"esbuild": "0.27.0",
"esbuild": "0.27.1",
"idb-keyval": "6.2.2",
"misskey-js": "workspace:*"
},
"devDependencies": {
"@typescript-eslint/parser": "8.48.0",
"@typescript-eslint/parser": "8.49.0",
"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.74",
"eslint-plugin-import": "2.32.0",
"nodemon": "3.1.11",

6
packages/sw/src/const.ts Normal file
View File

@@ -0,0 +1,6 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export const FETCH_TIMEOUT_MS = 10000;

View File

@@ -8,6 +8,7 @@
*/
import { get, set } from 'idb-keyval';
import { I18n } from '@@/js/i18n.js';
import { FETCH_TIMEOUT_MS } from '@/const.js';
import type { Locale } from 'i18n';
class SwLang {
@@ -37,11 +38,21 @@ class SwLang {
// _DEV_がtrueの場合は常に最新化
if (!localeRes || _DEV_) {
localeRes = await fetch(localeUrl);
const clone = localeRes.clone();
if (!clone.clone().ok) throw new Error('locale fetching error');
const controller = new AbortController();
const timeout = globalThis.setTimeout(() => {
controller.abort('locale-fetch-timeout');
}, FETCH_TIMEOUT_MS);
caches.open(this.cacheName).then(cache => cache.put(localeUrl, clone));
try {
localeRes = await fetch(localeUrl, { signal: controller.signal });
const clone = localeRes.clone();
if (!clone.clone().ok) throw new Error('locale fetching error');
caches.open(this.cacheName).then(cache => cache.put(localeUrl, clone));
} finally {
globalThis.clearTimeout(timeout);
}
}
return new I18n<Locale>(await localeRes.json());

View File

@@ -5,6 +5,7 @@
import { get } from 'idb-keyval';
import * as Misskey from 'misskey-js';
import { FETCH_TIMEOUT_MS } from '@/const.js';
import type { PushNotificationDataMap } from '@/types.js';
import type { I18n } from '@@/js/i18n.js';
import type { Locale } from 'i18n';
@@ -12,8 +13,67 @@ import { createEmptyNotification, createNotification } from '@/scripts/create-no
import { swLang } from '@/scripts/lang.js';
import * as swos from '@/scripts/operations.js';
globalThis.addEventListener('install', () => {
// ev.waitUntil(globalThis.skipWaiting());
async function respondToNavigation(request: Request): Promise<Response> {
const controller = new AbortController();
const timeout = globalThis.setTimeout(() => {
controller.abort('navigation-timeout');
}, FETCH_TIMEOUT_MS);
try {
const response = await fetch(request, { signal: controller.signal });
if (response?.status && response.status < 500) return response;
if (response?.type === 'opaqueredirect') return response;
} catch (error) {
if (_DEV_) {
console.warn('navigation fetch failed; showing offline page', error);
}
} finally {
globalThis.clearTimeout(timeout);
}
// Only show offline page when network request actually fails
const html = await offlineContentHTML();
return new Response(html, {
status: 200,
headers: {
'content-type': 'text/html',
},
});
}
async function offlineContentHTML() {
let i18n: Partial<I18n<Locale>>;
try {
i18n = await (swLang.i18n ?? await swLang.fetchLocale()) as Partial<I18n<Locale>>;
} catch {
i18n = {};
}
const messages = {
title: i18n.ts?._offlineScreen.title ?? 'Offline - Could not connect to server',
header: i18n.ts?._offlineScreen.header ?? 'Could not connect to server',
reload: i18n.ts?.reload ?? 'Reload',
};
return `<!DOCTYPE html><html lang="ja"><head><meta charset="UTF-8"><meta content="width=device-width,initial-scale=1"name="viewport"><title>${messages.title}</title><style>body{background-color:#0c1210;color:#dee7e4;font-family:Hiragino Maru Gothic Pro,BIZ UDGothic,Roboto,HelveticaNeue,Arial,sans-serif;line-height:1.35;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;margin:0;padding:24px;box-sizing:border-box}.icon{max-width:120px;width:100%;height:auto;margin-bottom:20px;}.message{text-align:center;font-size:20px;font-weight:700;margin-bottom:20px}.version{text-align:center;font-size:90%;margin-bottom:20px}button{padding:7px 14px;min-width:100px;font-weight:700;font-family:Hiragino Maru Gothic Pro,BIZ UDGothic,Roboto,HelveticaNeue,Arial,sans-serif;line-height:1.35;border-radius:99rem;background-color:#b4e900;color:#192320;border:none;cursor:pointer;-webkit-tap-highlight-color:transparent}button:hover{background-color:#c6ff03}</style></head><body><svg class="icon"fill="none"height="24"stroke="currentColor"stroke-linecap="round"stroke-linejoin="round"stroke-width="2"viewBox="0 0 24 24"width="24"xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z"fill="none"stroke="none"/><path d="M9.58 5.548c.24 -.11 .492 -.207 .752 -.286c1.88 -.572 3.956 -.193 5.444 1c1.488 1.19 2.162 3.007 1.77 4.769h.99c1.913 0 3.464 1.56 3.464 3.486c0 .957 -.383 1.824 -1.003 2.454m-2.997 1.033h-11.343c-2.572 -.004 -4.657 -2.011 -4.657 -4.487c0 -2.475 2.085 -4.482 4.657 -4.482c.13 -.582 .37 -1.128 .7 -1.62"/><path d="M3 3l18 18"/></svg><div class="message">${messages.header}</div><div class="version">v${_VERSION_}</div><button onclick="reloadPage()">${messages.reload}</button><script>function reloadPage(){location.reload(!0)}</script></body></html>`;
}
globalThis.addEventListener('install', (ev) => {
// 次の問題が発生するため、ServiceWorkerAutoPreload をオプトアウトする必要がある
// https://issues.chromium.org/issues/466790291
if ('addRoutes' in ev) {
// doc: https://developer.mozilla.org/en-US/docs/Web/API/InstallEvent/addRoutes
// @ts-expect-error 実験的なAPIなので型定義がない
ev.addRoutes({
condition: {
// doc: https://developer.mozilla.org/ja/docs/Web/API/URLPattern
// @ts-expect-error 実験的なAPIなので型定義がない
urlPattern: new URLPattern({}),
},
source: 'fetch-event',
});
}
});
globalThis.addEventListener('activate', ev => {
@@ -28,17 +88,6 @@ globalThis.addEventListener('activate', ev => {
);
});
async function offlineContentHTML() {
const i18n = await (swLang.i18n ?? swLang.fetchLocale()) as Partial<I18n<Locale>>;
const messages = {
title: i18n.ts?._offlineScreen.title ?? 'Offline - Could not connect to server',
header: i18n.ts?._offlineScreen.header ?? 'Could not connect to server',
reload: i18n.ts?.reload ?? 'Reload',
};
return `<!DOCTYPE html><html lang="ja"><head><meta charset="UTF-8"><meta content="width=device-width,initial-scale=1"name="viewport"><title>${messages.title}</title><style>body{background-color:#0c1210;color:#dee7e4;font-family:Hiragino Maru Gothic Pro,BIZ UDGothic,Roboto,HelveticaNeue,Arial,sans-serif;line-height:1.35;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;margin:0;padding:24px;box-sizing:border-box}.icon{max-width:120px;width:100%;height:auto;margin-bottom:20px;}.message{text-align:center;font-size:20px;font-weight:700;margin-bottom:20px}.version{text-align:center;font-size:90%;margin-bottom:20px}button{padding:7px 14px;min-width:100px;font-weight:700;font-family:Hiragino Maru Gothic Pro,BIZ UDGothic,Roboto,HelveticaNeue,Arial,sans-serif;line-height:1.35;border-radius:99rem;background-color:#b4e900;color:#192320;border:none;cursor:pointer;-webkit-tap-highlight-color:transparent}button:hover{background-color:#c6ff03}</style></head><body><svg class="icon"fill="none"height="24"stroke="currentColor"stroke-linecap="round"stroke-linejoin="round"stroke-width="2"viewBox="0 0 24 24"width="24"xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z"fill="none"stroke="none"/><path d="M9.58 5.548c.24 -.11 .492 -.207 .752 -.286c1.88 -.572 3.956 -.193 5.444 1c1.488 1.19 2.162 3.007 1.77 4.769h.99c1.913 0 3.464 1.56 3.464 3.486c0 .957 -.383 1.824 -1.003 2.454m-2.997 1.033h-11.343c-2.572 -.004 -4.657 -2.011 -4.657 -4.487c0 -2.475 2.085 -4.482 4.657 -4.482c.13 -.582 .37 -1.128 .7 -1.62"/><path d="M3 3l18 18"/></svg><div class="message">${messages.header}</div><div class="version">v${_VERSION_}</div><button onclick="reloadPage()">${messages.reload}</button><script>function reloadPage(){location.reload(!0)}</script></body></html>`;
}
globalThis.addEventListener('fetch', ev => {
let isHTMLRequest = false;
if (ev.request.headers.get('sec-fetch-dest') === 'document') {
@@ -50,18 +99,7 @@ globalThis.addEventListener('fetch', ev => {
}
if (!isHTMLRequest) return;
ev.respondWith(
fetch(ev.request)
.catch(async () => {
const html = await offlineContentHTML();
return new Response(html, {
status: 200,
headers: {
'content-type': 'text/html',
},
});
}),
);
ev.respondWith(respondToNavigation(ev.request));
});
globalThis.addEventListener('push', ev => {

3274
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -35,3 +35,5 @@ ignorePatchFailures: false
minimumReleaseAge: 10080 # delay 7days to mitigate supply-chain attack
minimumReleaseAgeExclude:
- '@syuilo/aiscript'
- vue-tsc
- '@vue/language-core'

View File

@@ -9,16 +9,16 @@
"version": "1.0.0",
"devDependencies": {
"@types/mdast": "4.0.4",
"@types/node": "24.10.1",
"@vitest/coverage-v8": "4.0.14",
"@types/node": "24.10.2",
"@vitest/coverage-v8": "4.0.15",
"mdast-util-to-string": "4.0.0",
"remark": "15.0.1",
"remark-parse": "11.0.0",
"typescript": "5.9.3",
"unified": "11.0.5",
"vite": "7.2.4",
"vite": "7.2.7",
"vite-node": "5.2.0",
"vitest": "4.0.14"
"vitest": "4.0.15"
}
},
"node_modules/@babel/helper-string-parser": {
@@ -842,20 +842,21 @@
]
},
"node_modules/@standard-schema/spec": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz",
"integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/chai": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz",
"integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==",
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
"integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/deep-eql": "*"
"@types/deep-eql": "*",
"assertion-error": "^2.0.1"
}
},
"node_modules/@types/debug": {
@@ -898,12 +899,11 @@
"dev": true
},
"node_modules/@types/node": {
"version": "24.10.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
"version": "24.10.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.2.tgz",
"integrity": "sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~7.16.0"
}
@@ -915,14 +915,14 @@
"dev": true
},
"node_modules/@vitest/coverage-v8": {
"version": "4.0.14",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.14.tgz",
"integrity": "sha512-EYHLqN/BY6b47qHH7gtMxAg++saoGmsjWmAq9MlXxAz4M0NcHh9iOyKhBZyU4yxZqOd8Xnqp80/5saeitz4Cng==",
"version": "4.0.15",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.15.tgz",
"integrity": "sha512-FUJ+1RkpTFW7rQITdgTi93qOCWJobWhBirEPCeXh2SW2wsTlFxy51apDz5gzG+ZEYt/THvWeNmhdAoS9DTwpCw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@bcoe/v8-coverage": "^1.0.2",
"@vitest/utils": "4.0.14",
"@vitest/utils": "4.0.15",
"ast-v8-to-istanbul": "^0.3.8",
"istanbul-lib-coverage": "^3.2.2",
"istanbul-lib-report": "^3.0.1",
@@ -937,8 +937,8 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"@vitest/browser": "4.0.14",
"vitest": "4.0.14"
"@vitest/browser": "4.0.15",
"vitest": "4.0.15"
},
"peerDependenciesMeta": {
"@vitest/browser": {
@@ -947,16 +947,16 @@
}
},
"node_modules/@vitest/expect": {
"version": "4.0.14",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.14.tgz",
"integrity": "sha512-RHk63V3zvRiYOWAV0rGEBRO820ce17hz7cI2kDmEdfQsBjT2luEKB5tCOc91u1oSQoUOZkSv3ZyzkdkSLD7lKw==",
"version": "4.0.15",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.15.tgz",
"integrity": "sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"@types/chai": "^5.2.2",
"@vitest/spy": "4.0.14",
"@vitest/utils": "4.0.14",
"@vitest/spy": "4.0.15",
"@vitest/utils": "4.0.15",
"chai": "^6.2.1",
"tinyrainbow": "^3.0.3"
},
@@ -965,13 +965,13 @@
}
},
"node_modules/@vitest/mocker": {
"version": "4.0.14",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.14.tgz",
"integrity": "sha512-RzS5NujlCzeRPF1MK7MXLiEFpkIXeMdQ+rN3Kk3tDI9j0mtbr7Nmuq67tpkOJQpgyClbOltCXMjLZicJHsH5Cg==",
"version": "4.0.15",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.15.tgz",
"integrity": "sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "4.0.14",
"@vitest/spy": "4.0.15",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.21"
},
@@ -992,9 +992,9 @@
}
},
"node_modules/@vitest/pretty-format": {
"version": "4.0.14",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.14.tgz",
"integrity": "sha512-SOYPgujB6TITcJxgd3wmsLl+wZv+fy3av2PpiPpsWPZ6J1ySUYfScfpIt2Yv56ShJXR2MOA6q2KjKHN4EpdyRQ==",
"version": "4.0.15",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.15.tgz",
"integrity": "sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1005,13 +1005,13 @@
}
},
"node_modules/@vitest/runner": {
"version": "4.0.14",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.14.tgz",
"integrity": "sha512-BsAIk3FAqxICqREbX8SetIteT8PiaUL/tgJjmhxJhCsigmzzH8xeadtp7LRnTpCVzvf0ib9BgAfKJHuhNllKLw==",
"version": "4.0.15",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.15.tgz",
"integrity": "sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/utils": "4.0.14",
"@vitest/utils": "4.0.15",
"pathe": "^2.0.3"
},
"funding": {
@@ -1019,13 +1019,13 @@
}
},
"node_modules/@vitest/snapshot": {
"version": "4.0.14",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.14.tgz",
"integrity": "sha512-aQVBfT1PMzDSA16Y3Fp45a0q8nKexx6N5Amw3MX55BeTeZpoC08fGqEZqVmPcqN0ueZsuUQ9rriPMhZ3Mu19Ag==",
"version": "4.0.15",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.15.tgz",
"integrity": "sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "4.0.14",
"@vitest/pretty-format": "4.0.15",
"magic-string": "^0.30.21",
"pathe": "^2.0.3"
},
@@ -1034,9 +1034,9 @@
}
},
"node_modules/@vitest/spy": {
"version": "4.0.14",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.14.tgz",
"integrity": "sha512-JmAZT1UtZooO0tpY3GRyiC/8W7dCs05UOq9rfsUUgEZEdq+DuHLmWhPsrTt0TiW7WYeL/hXpaE07AZ2RCk44hg==",
"version": "4.0.15",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.15.tgz",
"integrity": "sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==",
"dev": true,
"license": "MIT",
"funding": {
@@ -1044,19 +1044,29 @@
}
},
"node_modules/@vitest/utils": {
"version": "4.0.14",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.14.tgz",
"integrity": "sha512-hLqXZKAWNg8pI+SQXyXxWCTOpA3MvsqcbVeNgSi8x/CSN2wi26dSzn1wrOhmCmFjEvN9p8/kLFRHa6PI8jHazw==",
"version": "4.0.15",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.15.tgz",
"integrity": "sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "4.0.14",
"@vitest/pretty-format": "4.0.15",
"tinyrainbow": "^3.0.3"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/assertion-error": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
}
},
"node_modules/ast-v8-to-istanbul": {
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.8.tgz",
@@ -1220,9 +1230,9 @@
}
},
"node_modules/expect-type": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz",
"integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
"integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -1983,7 +1993,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -2128,7 +2137,8 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
"integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
"dev": true
"dev": true,
"license": "ISC"
},
"node_modules/source-map-js": {
"version": "1.2.1",
@@ -2143,7 +2153,8 @@
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
"integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
"dev": true
"dev": true,
"license": "MIT"
},
"node_modules/std-env": {
"version": "3.10.0",
@@ -2172,11 +2183,14 @@
"license": "MIT"
},
"node_modules/tinyexec": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
"integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
"integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
"dev": true,
"license": "MIT"
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/tinyglobby": {
"version": "0.2.15",
@@ -2341,9 +2355,9 @@
}
},
"node_modules/vite": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz",
"integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==",
"version": "7.2.7",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.7.tgz",
"integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2439,20 +2453,19 @@
}
},
"node_modules/vitest": {
"version": "4.0.14",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.14.tgz",
"integrity": "sha512-d9B2J9Cm9dN9+6nxMnnNJKJCtcyKfnHj15N6YNJfaFHRLua/d3sRKU9RuKmO9mB0XdFtUizlxfz/VPbd3OxGhw==",
"version": "4.0.15",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.15.tgz",
"integrity": "sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vitest/expect": "4.0.14",
"@vitest/mocker": "4.0.14",
"@vitest/pretty-format": "4.0.14",
"@vitest/runner": "4.0.14",
"@vitest/snapshot": "4.0.14",
"@vitest/spy": "4.0.14",
"@vitest/utils": "4.0.14",
"@vitest/expect": "4.0.15",
"@vitest/mocker": "4.0.15",
"@vitest/pretty-format": "4.0.15",
"@vitest/runner": "4.0.15",
"@vitest/snapshot": "4.0.15",
"@vitest/spy": "4.0.15",
"@vitest/utils": "4.0.15",
"es-module-lexer": "^1.7.0",
"expect-type": "^1.2.2",
"magic-string": "^0.30.21",
@@ -2461,7 +2474,7 @@
"picomatch": "^4.0.3",
"std-env": "^3.10.0",
"tinybench": "^2.9.0",
"tinyexec": "^0.3.2",
"tinyexec": "^1.0.2",
"tinyglobby": "^0.2.15",
"tinyrainbow": "^3.0.3",
"vite": "^6.0.0 || ^7.0.0",
@@ -2480,10 +2493,10 @@
"@edge-runtime/vm": "*",
"@opentelemetry/api": "^1.9.0",
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
"@vitest/browser-playwright": "4.0.14",
"@vitest/browser-preview": "4.0.14",
"@vitest/browser-webdriverio": "4.0.14",
"@vitest/ui": "4.0.14",
"@vitest/browser-playwright": "4.0.15",
"@vitest/browser-preview": "4.0.15",
"@vitest/browser-webdriverio": "4.0.15",
"@vitest/ui": "4.0.15",
"happy-dom": "*",
"jsdom": "*"
},

View File

@@ -10,15 +10,15 @@
},
"devDependencies": {
"@types/mdast": "4.0.4",
"@types/node": "24.10.1",
"@vitest/coverage-v8": "4.0.14",
"@types/node": "24.10.2",
"@vitest/coverage-v8": "4.0.15",
"mdast-util-to-string": "4.0.0",
"remark": "15.0.1",
"remark-parse": "11.0.0",
"typescript": "5.9.3",
"unified": "11.0.5",
"vite": "7.2.4",
"vite": "7.2.7",
"vite-node": "5.2.0",
"vitest": "4.0.14"
"vitest": "4.0.15"
}
}

View File

@@ -3,11 +3,10 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { createWriteStream } from 'node:fs';
import { createWriteStream, promises as fsp } from 'node:fs';
import { mkdir } from 'node:fs/promises';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import glob from 'fast-glob';
import walk from 'ignore-walk';
import { Pack } from 'tar/pack';
import meta from '../package.json' with { type: "json" };
@@ -25,7 +24,7 @@ export default async function build() {
const pack = new Pack({ cwd, gzip: true });
const patterns = await walk({ path: cwd, ignoreFiles: ['.gitignore'] });
for await (const entry of glob.stream(patterns, { cwd, ignore, dot: true })) {
for await (const entry of fsp.glob(patterns, { cwd, ignore, dot: true })) {
pack.add(entry);
}