mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-07-31 12:25:49 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 36c442a7b4 | |||
| 79340aec97 | |||
| e06878b7ec | |||
| 23de8030b1 | |||
| 8c28fd3a1b | |||
| cc2ed939b1 | |||
| 3f11a28003 | |||
| 13994163c4 | |||
| d0de75d33f | |||
| 95405e38df | |||
| f7c070cb09 | |||
| 3f28489fb8 | |||
| 9eca9b6f0d | |||
| 95de10ee65 | |||
| bdf1547a42 |
@@ -19,7 +19,7 @@
|
||||
|---|---|---|---|
|
||||
| `skills/context-budget/SKILL.md` | `skills/context-budget/SKILL.md` | ECC | description を日本語化、Misskey 固有メモを追記 |
|
||||
| `commands/harness-audit.md` | `commands/harness-audit.md` | ECC | scripts 依存の自動採点を、Claude が `pnpm`/`git`/`grep` で手動採点する版に書き換え。Misskey 固有の評価軸 (SPDX / endpoint-list / migration / locales) を組み込み |
|
||||
| `commands/quality-gate.md` | `commands/quality-gate.md` | ECC | 言語自動判定を排除し Misskey 固定 pipeline (`pnpm` + tsgo + ESLint + Vitest) に。Prettier/Biome フェーズを削除 |
|
||||
| `commands/quality-gate.md` | `commands/quality-gate.md` | ECC | 言語自動判定を排除し Misskey 固定 pipeline (`pnpm` + tsc + ESLint + Vitest) に。Prettier/Biome フェーズを削除 |
|
||||
|
||||
### MIT License (full text)
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ upstream path: commands/quality-gate.md
|
||||
upstream license: MIT — https://github.com/affaan-m/everything-claude-code/blob/main/LICENSE
|
||||
project-level notice: see .claude/THIRD_PARTY_LICENSES.md (Misskey 内サードパーティ一覧 + MIT 全文)
|
||||
|
||||
Imported into Misskey .claude/ on 2026-05-10. Pipeline 概念 (lint → typecheck → test) は upstream ECC 版から借用 (MIT)。実コマンド層は Misskey の pnpm + tsgo + ESLint + Vitest に固定し、formatter (Prettier/Biome) フェーズは削除した。
|
||||
Imported into Misskey .claude/ on 2026-05-10. Pipeline 概念 (lint → typecheck → test) は upstream ECC 版から借用 (MIT)。実コマンド層は Misskey の pnpm + tsc + ESLint + Vitest に固定し、formatter (Prettier/Biome) フェーズは削除した。
|
||||
|
||||
note: 元 ECC 版は言語自動判定 + format/lint/type のジェネリック版だったが、Misskey 専用に pnpm + tsgo + ESLint + Vitest の組み合わせに固定。重い test:e2e / test:fed は含まない (CI 側で実行される)。
|
||||
note: 元 ECC 版は言語自動判定 + format/lint/type のジェネリック版だったが、Misskey 専用に pnpm + tsc + ESLint + Vitest の組み合わせに固定。重い test:e2e / test:fed は含まない (CI 側で実行される)。
|
||||
-->
|
||||
|
||||
# /quality-gate — Misskey 軽量品質ゲート
|
||||
@@ -50,7 +50,7 @@ pnpm --filter frontend test
|
||||
lint がまとめて失敗していて typecheck の結果だけ単独で見たい場合は、以下を個別に回す。**通常は不要** (lint の出力を読めば足りる):
|
||||
|
||||
```bash
|
||||
pnpm --filter backend typecheck # tsgo 単体
|
||||
pnpm --filter backend typecheck # tsc 単体
|
||||
pnpm --filter frontend typecheck # vue-tsc 単体 (Vue SFC の型を見るため)
|
||||
```
|
||||
|
||||
@@ -63,7 +63,7 @@ pnpm --filter backend lint
|
||||
pnpm --filter backend test
|
||||
```
|
||||
|
||||
`tsgo` の出力を単独で見たい時のみ optional で `pnpm --filter backend typecheck` を別途回す。
|
||||
`tsc` の出力を単独で見たい時のみ optional で `pnpm --filter backend typecheck` を別途回す。
|
||||
|
||||
### Frontend scope
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Misskey の backend は NestJS 11 + Fastify 5 + TypeORM 1 (PostgreSQL) + Redis
|
||||
|
||||
- **DI コンテナ**: NestJS の `@Injectable()` サービス + Repository (TypeORM) パターン
|
||||
- **DI トークン**: [`@/di-symbols.js`](../../../../../packages/backend/src/di-symbols.ts) の `DI` から `@Inject(DI.xxx)` で注入
|
||||
- **ビルド**: `rolldown -c` で `built/` にバンドル。型チェックは `tsgo`
|
||||
- **ビルド**: `rolldown -c` で `built/` にバンドル。型チェックは `tsc`
|
||||
|
||||
## エンドポイント内での DI
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ pnpm --filter backend test:e2e
|
||||
```bash
|
||||
# 個別ファイルを高速にチェック
|
||||
pnpm exec eslint --fix packages/backend/src/server/api/endpoints/<category>/<name>.ts
|
||||
pnpm --filter backend typecheck # tsgo --noEmit (backend のみ)
|
||||
pnpm --filter backend typecheck # tsc --noEmit (backend のみ)
|
||||
|
||||
# 一括 (PR 提出前)
|
||||
pnpm --filter backend lint
|
||||
|
||||
@@ -9,3 +9,9 @@ reviews:
|
||||
drafts: false
|
||||
base_branches:
|
||||
- "develop"
|
||||
ignore_title_keywords:
|
||||
- "New Crowdin updates"
|
||||
ignore_usernames:
|
||||
- "dependabot[bot]"
|
||||
- "renovate[bot]"
|
||||
- "github-actions[bot]"
|
||||
|
||||
@@ -317,7 +317,7 @@ function renderVisualizerSummaryTable(before: ReturnType<typeof collectVisualize
|
||||
`</tr>`,
|
||||
`</tbody>`,
|
||||
`</table>`,
|
||||
];
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
function getChunkComparisonRows(keys: string[], before: Record<string, FileEntry>, after: Record<string, FileEntry>) {
|
||||
|
||||
@@ -71,6 +71,6 @@ jobs:
|
||||
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
|
||||
comment-tag: show_memory_diff
|
||||
message: |
|
||||
An error occurred while comparing backend memory usage. See [workflow logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
build:
|
||||
misskey:
|
||||
args:
|
||||
- NODE_ENV=development
|
||||
deploy:
|
||||
- helm upgrade --install misskey chart --set image=${OKTETO_BUILD_MISSKEY_IMAGE} --set url="https://misskey-$(kubectl config view --minify -o jsonpath='{..namespace}').cloud.okteto.net" --set environment=development
|
||||
+4
-4
@@ -17,8 +17,6 @@
|
||||
- Feat: コントロールパネルから二要素認証を解除できるように
|
||||
- Feat: 条件に一致したURLプレビューのサムネイルを隠すことができるように
|
||||
(Based on https://github.com/MisskeyIO/misskey/pull/214)
|
||||
- Feat: ユーザーミュートの適用範囲から通知を除外できるように
|
||||
- タイムラインや検索等でノートが見えないようにしつつ、通知は引き続き受け取れるように設定することができるようになりました
|
||||
|
||||
### Client
|
||||
- 2025.4.0 以前の設定情報の移行処理が削除されました
|
||||
@@ -32,14 +30,14 @@
|
||||
- Fix: 一部の画像のみセンシティブなとき、ビューワー内で画像を切り替えるとセンシティブな画像がそのまま表示される問題を修正
|
||||
- Fix: 一部の画像をビューワーで読み込んだ際に正しく表示されない問題を修正
|
||||
- Enhance: タイムラインの読み込みパフォーマンスを改善
|
||||
- Enhance: ミュートの付与期間を自由に設定できるように
|
||||
- Enhance: ロールの付与期間を自由に設定できるように
|
||||
- Fix: 「画像を新しいタブで開く」が機能しなくなっていた問題を修正
|
||||
- Fix: デバイスタイプをスマートフォンに固定している状態で画面幅が広いとき、画面左上のアイコンが表示されない問題を修正
|
||||
- Fix: チャットでIMEの変換を確定するEnterでメッセージが送信されてしまうことがある問題を修正
|
||||
- Fix: 自分へのメンションに対する色分けで、判定が大文字/小文字を区別していた問題を修正
|
||||
- Fix: いくつかのイベントリスナーが正しく解除されない問題を修正(メモリ使用量の改善)
|
||||
- Fix: 非ログイン時トップページをスクロール操作できないことがある問題を修正
|
||||
- Fix: ローカルユーザーへのホスト付きメンションが本文に含まれる指名ノートの作成時、投稿フォームにて、当該ユーザーが宛先に含まれていても正しく認識されない問題を修正
|
||||
- Fix: ドライブの「このファイルからノートを作成」やギャラリーの「ノートで共有」、誕生日ウィジェットからのノート作成において、通常投稿の下書きが表示される問題を修正
|
||||
|
||||
### Server
|
||||
- Feat: OpenTelemetryサポート
|
||||
@@ -63,6 +61,8 @@
|
||||
- Fix: ハッシュタグに関連するデータを更新する際のエラーハンドリングを修正
|
||||
- Fix: Sentry 使用環境下にて、Misskey が発行した SQL クエリが span に含まれない問題を修正
|
||||
- Fix: Sentry 使用環境下にて、外部送信リクエストへ `sentry-trace` / `baggage` ヘッダーが既定で付与されないように
|
||||
- Fix: フォロワー限定投稿へのリプライをホーム投稿に出来る問題を修正
|
||||
- Fix: ファイルをアップロードするAPIにて、処理終了後に一時ファイルが削除されないことがある問題を修正
|
||||
|
||||
## 2026.6.0
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
apiVersion: v2
|
||||
name: misskey
|
||||
version: 0.0.0
|
||||
description: This chart is created for the purpose of previewing Pull Requests. Do not use this for production use.
|
||||
@@ -1,231 +0,0 @@
|
||||
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# Misskey configuration
|
||||
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
# ┌─────┐
|
||||
#───┘ URL └─────────────────────────────────────────────────────
|
||||
|
||||
# Final accessible URL seen by a user.
|
||||
# url: https://example.tld/
|
||||
|
||||
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
|
||||
# URL SETTINGS AFTER THAT!
|
||||
|
||||
# ┌───────────────────────┐
|
||||
#───┘ Port and TLS settings └───────────────────────────────────
|
||||
|
||||
#
|
||||
# Misskey supports two deployment options for public.
|
||||
#
|
||||
|
||||
# Option 1: With Reverse Proxy
|
||||
#
|
||||
# +----- https://example.tld/ ------------+
|
||||
# +------+ |+-------------+ +----------------+|
|
||||
# | User | ---> || Proxy (443) | ---> | Misskey (3000) ||
|
||||
# +------+ |+-------------+ +----------------+|
|
||||
# +---------------------------------------+
|
||||
#
|
||||
# You need to setup reverse proxy. (eg. nginx)
|
||||
# You do not define 'https' section.
|
||||
|
||||
# Option 2: Standalone
|
||||
#
|
||||
# +- https://example.tld/ -+
|
||||
# +------+ | +---------------+ |
|
||||
# | User | ---> | | Misskey (443) | |
|
||||
# +------+ | +---------------+ |
|
||||
# +------------------------+
|
||||
#
|
||||
# You need to run Misskey as root.
|
||||
# You need to set Certificate in 'https' section.
|
||||
|
||||
# To use option 1, uncomment below line.
|
||||
port: 3000 # A port that your Misskey server should listen.
|
||||
|
||||
# To use option 2, uncomment below lines.
|
||||
#port: 443
|
||||
|
||||
#https:
|
||||
# # path for certification
|
||||
# key: /etc/letsencrypt/live/example.tld/privkey.pem
|
||||
# cert: /etc/letsencrypt/live/example.tld/fullchain.pem
|
||||
|
||||
# ┌──────────────────────────┐
|
||||
#───┘ PostgreSQL configuration └────────────────────────────────
|
||||
|
||||
db:
|
||||
host: localhost
|
||||
port: 5432
|
||||
|
||||
# Database name
|
||||
db: misskey
|
||||
|
||||
# Auth
|
||||
user: example-misskey-user
|
||||
pass: example-misskey-pass
|
||||
|
||||
# Whether disable Caching queries
|
||||
#disableCache: true
|
||||
|
||||
# Extra Connection options
|
||||
#extra:
|
||||
# ssl: true
|
||||
|
||||
dbReplications: false
|
||||
|
||||
# You can configure any number of replicas here
|
||||
#dbSlaves:
|
||||
# -
|
||||
# host:
|
||||
# port:
|
||||
# db:
|
||||
# user:
|
||||
# pass:
|
||||
# -
|
||||
# host:
|
||||
# port:
|
||||
# db:
|
||||
# user:
|
||||
# pass:
|
||||
|
||||
# ┌─────────────────────┐
|
||||
#───┘ Redis configuration └─────────────────────────────────────
|
||||
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
#family: 0 # 0=Both, 4=IPv4, 6=IPv6
|
||||
#pass: example-pass
|
||||
#prefix: example-prefix
|
||||
#db: 1
|
||||
|
||||
#redisForPubsub:
|
||||
# host: localhost
|
||||
# port: 6379
|
||||
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
|
||||
# #pass: example-pass
|
||||
# #prefix: example-prefix
|
||||
# #db: 1
|
||||
|
||||
#redisForJobQueue:
|
||||
# host: localhost
|
||||
# port: 6379
|
||||
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
|
||||
# #pass: example-pass
|
||||
# #prefix: example-prefix
|
||||
# #db: 1
|
||||
|
||||
#redisForTimelines:
|
||||
# host: redis
|
||||
# port: 6379
|
||||
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
|
||||
# #pass: example-pass
|
||||
# #prefix: example-prefix
|
||||
# #db: 1
|
||||
|
||||
#redisForReactions:
|
||||
# host: redis
|
||||
# port: 6379
|
||||
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
|
||||
# #pass: example-pass
|
||||
# #prefix: example-prefix
|
||||
# #db: 1
|
||||
|
||||
# ┌───────────────────────────┐
|
||||
#───┘ MeiliSearch configuration └─────────────────────────────
|
||||
|
||||
#meilisearch:
|
||||
# host: localhost
|
||||
# port: 7700
|
||||
# apiKey: ''
|
||||
# ssl: true
|
||||
# index: ''
|
||||
|
||||
# ┌───────────────┐
|
||||
#───┘ ID generation └───────────────────────────────────────────
|
||||
|
||||
# You can select the ID generation method.
|
||||
# You don't usually need to change this setting, but you can
|
||||
# change it according to your preferences.
|
||||
|
||||
# Available methods:
|
||||
# aid ... Short, Millisecond accuracy
|
||||
# aidx ... Millisecond accuracy
|
||||
# meid ... Similar to ObjectID, Millisecond accuracy
|
||||
# ulid ... Millisecond accuracy
|
||||
# objectid ... This is left for backward compatibility
|
||||
|
||||
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
|
||||
# ID SETTINGS AFTER THAT!
|
||||
|
||||
id: "aidx"
|
||||
|
||||
# ┌────────────────┐
|
||||
#───┘ Error tracking └──────────────────────────────────────────
|
||||
|
||||
# Sentry is available for error tracking.
|
||||
# See the Sentry documentation for more details on options.
|
||||
|
||||
#sentryForBackend:
|
||||
# enableNodeProfiling: true
|
||||
# options:
|
||||
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||
|
||||
#sentryForFrontend:
|
||||
# vueIntegration:
|
||||
# tracingOptions:
|
||||
# trackComponents: true
|
||||
# browserTracingIntegration:
|
||||
# replayIntegration:
|
||||
# options:
|
||||
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||
|
||||
# ┌─────────────────────┐
|
||||
#───┘ Other configuration └─────────────────────────────────────
|
||||
|
||||
# Whether disable HSTS
|
||||
#disableHsts: true
|
||||
|
||||
# Number of worker processes
|
||||
#clusterLimit: 1
|
||||
|
||||
# Number of threads of extra thread pool for CPU-intensive tasks (per worker)
|
||||
#threadPoolSize: 1
|
||||
|
||||
# Job concurrency per worker
|
||||
# deliverJobConcurrency: 128
|
||||
# inboxJobConcurrency: 16
|
||||
|
||||
# Job rate limiter
|
||||
# deliverJobPerSec: 128
|
||||
# inboxJobPerSec: 32
|
||||
|
||||
# Job attempts
|
||||
# deliverJobMaxAttempts: 12
|
||||
# inboxJobMaxAttempts: 8
|
||||
|
||||
# IP address family used for outgoing request (ipv4, ipv6 or dual)
|
||||
#outgoingAddressFamily: ipv4
|
||||
|
||||
# Proxy for HTTP/HTTPS
|
||||
#proxy: http://127.0.0.1:3128
|
||||
|
||||
#proxyBypassHosts: [
|
||||
# 'example.com',
|
||||
# '192.0.2.8'
|
||||
#]
|
||||
|
||||
# Proxy for SMTP/SMTPS
|
||||
#proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT
|
||||
#proxySmtp: socks4://127.0.0.1:1080 # use SOCKS4
|
||||
#proxySmtp: socks5://127.0.0.1:1080 # use SOCKS5
|
||||
|
||||
# Media Proxy
|
||||
#mediaProxy: https://example.com/proxy
|
||||
|
||||
allowedPrivateNetworks:
|
||||
- '127.0.0.1/32'
|
||||
|
||||
# Upload or download file size limits (bytes)
|
||||
#maxFileSize: 262144000
|
||||
@@ -1,8 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "misskey.fullname" . }}-configuration
|
||||
data:
|
||||
default.yml: |-
|
||||
{{ .Files.Get "files/default.yml"|nindent 4 }}
|
||||
url: {{ .Values.url }}
|
||||
@@ -1,47 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "misskey.fullname" . }}
|
||||
labels:
|
||||
{{- include "misskey.labels" . | nindent 4 }}
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "misskey.selectorLabels" . | nindent 6 }}
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "misskey.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
containers:
|
||||
- name: misskey
|
||||
image: {{ .Values.image }}
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: {{ .Values.environment }}
|
||||
volumeMounts:
|
||||
- name: {{ include "misskey.fullname" . }}-configuration
|
||||
mountPath: /misskey/.config
|
||||
readOnly: true
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
- name: postgres
|
||||
image: postgres:18-alpine
|
||||
env:
|
||||
- name: POSTGRES_USER
|
||||
value: "example-misskey-user"
|
||||
- name: POSTGRES_PASSWORD
|
||||
value: "example-misskey-pass"
|
||||
- name: POSTGRES_DB
|
||||
value: "misskey"
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
- name: redis
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
volumes:
|
||||
- name: {{ include "misskey.fullname" . }}-configuration
|
||||
configMap:
|
||||
name: {{ include "misskey.fullname" . }}-configuration
|
||||
@@ -1,14 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "misskey.fullname" . }}
|
||||
annotations:
|
||||
dev.okteto.com/auto-ingress: "true"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 3000
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
{{- include "misskey.selectorLabels" . | nindent 4 }}
|
||||
@@ -1,62 +0,0 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "misskey.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "misskey.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "misskey.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "misskey.labels" -}}
|
||||
helm.sh/chart: {{ include "misskey.chart" . }}
|
||||
{{ include "misskey.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "misskey.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "misskey.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "misskey.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "misskey.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -1,3 +0,0 @@
|
||||
url: https://example.tld/
|
||||
image: okteto.dev/misskey
|
||||
environment: production
|
||||
@@ -146,9 +146,6 @@ markAsSensitive: "センシティブとして設定"
|
||||
unmarkAsSensitive: "センシティブを解除する"
|
||||
enterFileName: "ファイル名を入力"
|
||||
mute: "ミュート"
|
||||
muteType: "ミュートする範囲"
|
||||
muteTypeDescription: "ミュートを適用する範囲を設定できます。「タイムラインのみ」に設定すると、タイムラインや検索結果上からは見えなくなりますが、通知は受け取ります。"
|
||||
muteTypeTimeline: "タイムラインのみ"
|
||||
unmute: "ミュート解除"
|
||||
renoteMute: "リノートをミュート"
|
||||
renoteUnmute: "リノートのミュートを解除"
|
||||
@@ -964,7 +961,6 @@ instanceDefaultLightTheme: "サーバーデフォルトのライトテーマ"
|
||||
instanceDefaultDarkTheme: "サーバーデフォルトのダークテーマ"
|
||||
instanceDefaultThemeDescription: "オブジェクト形式のテーマコードを記入します。"
|
||||
mutePeriod: "ミュートする期限"
|
||||
mutePeriodDescription: "期限はあくまで目安です。反映が数分遅れる場合があります。"
|
||||
period: "期限"
|
||||
indefinitely: "無期限"
|
||||
tenMinutes: "10分"
|
||||
@@ -1413,7 +1409,6 @@ youAreAdmin: "あなたは管理者です"
|
||||
frame: "フレーム"
|
||||
presets: "プリセット"
|
||||
zeroPadding: "ゼロ埋め"
|
||||
muteConfirm: "ミュートしますか?"
|
||||
nothingToConfigure: "設定項目はありません"
|
||||
viewRenotedChannel: "リノート先のチャンネルを見る"
|
||||
previewingTheme: "テーマのプレビュー中"
|
||||
|
||||
+12
-12
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "2026.7.0-alpha.6",
|
||||
"version": "2026.7.0-beta.1",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/misskey-dev/misskey.git"
|
||||
},
|
||||
"packageManager": "pnpm@11.9.0",
|
||||
"packageManager": "pnpm@11.11.0",
|
||||
"workspaces": [
|
||||
"packages/misskey-js",
|
||||
"packages/i18n",
|
||||
@@ -55,22 +55,22 @@
|
||||
"esbuild": "0.28.1",
|
||||
"execa": "9.6.1",
|
||||
"ignore-walk": "9.0.0",
|
||||
"js-yaml": "5.2.0",
|
||||
"tar": "7.5.19"
|
||||
"js-yaml": "5.2.1",
|
||||
"tar": "7.5.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "9.39.4",
|
||||
"@eslint/js": "9.39.5",
|
||||
"@misskey-dev/eslint-plugin": "2.1.0",
|
||||
"@types/node": "26.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.62.0",
|
||||
"@typescript/native-preview": "7.0.0-dev.20260426.1",
|
||||
"@types/node": "26.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.63.0",
|
||||
"@typescript-eslint/parser": "8.63.0",
|
||||
"@typescript/native": "npm:typescript@7.0.2",
|
||||
"cross-env": "10.1.0",
|
||||
"eslint": "9.39.4",
|
||||
"eslint": "9.39.5",
|
||||
"globals": "17.7.0",
|
||||
"ncp": "2.0.0",
|
||||
"pnpm": "11.9.0",
|
||||
"pnpm": "11.11.0",
|
||||
"start-server-and-test": "3.0.11",
|
||||
"typescript": "5.9.3"
|
||||
"typescript": "npm:@typescript/typescript6@6.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class AddMutingType1762516776421 {
|
||||
name = 'AddMutingType1762516776421'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "muting" ADD "mutingType" varchar(128) NOT NULL DEFAULT 'all'`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "muting" DROP COLUMN "mutingType"`);
|
||||
}
|
||||
}
|
||||
@@ -18,11 +18,11 @@
|
||||
"build": "rolldown -c",
|
||||
"build:unit": "rolldown -c --sourcemap",
|
||||
"build:e2e": "rolldown -c --e2e",
|
||||
"build:tsc": "tsgo -p tsconfig.json && tsc-alias -p tsconfig.json",
|
||||
"build:tsc": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
|
||||
"watch": "pnpm compile-config && node ./scripts/watch.mjs",
|
||||
"restart": "pnpm build && pnpm start",
|
||||
"dev": "pnpm compile-config && rolldown -c --watch",
|
||||
"typecheck": "tsgo --noEmit && tsgo -p test --noEmit && tsgo -p test-federation --noEmit",
|
||||
"typecheck": "tsc --noEmit && tsc -p test --noEmit && tsc -p test-federation --noEmit",
|
||||
"eslint": "eslint --quiet \"{src,test-federation}/**/*.ts\"",
|
||||
"lint": "pnpm typecheck && pnpm eslint",
|
||||
"test": "pnpm build:unit && cross-env NODE_ENV=test pnpm compile-config && vitest --config vitest.config.unit.ts",
|
||||
@@ -51,43 +51,43 @@
|
||||
"utf-8-validate": "6.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.1075.0",
|
||||
"@aws-sdk/lib-storage": "3.1075.0",
|
||||
"@aws-sdk/client-s3": "3.1085.0",
|
||||
"@aws-sdk/lib-storage": "3.1085.0",
|
||||
"@fastify/accepts": "5.0.4",
|
||||
"@fastify/cors": "11.2.0",
|
||||
"@fastify/cors": "11.3.0",
|
||||
"@fastify/http-proxy": "11.5.0",
|
||||
"@fastify/multipart": "10.0.0",
|
||||
"@fastify/otel": "0.19.0",
|
||||
"@fastify/static": "9.1.3",
|
||||
"@fastify/multipart": "10.1.0",
|
||||
"@fastify/otel": "0.20.1",
|
||||
"@fastify/static": "10.1.0",
|
||||
"@kitajs/html": "4.2.13",
|
||||
"@misskey-dev/emoji-assets": "17.0.3",
|
||||
"@misskey-dev/emoji-data": "17.0.3",
|
||||
"@misskey-dev/sharp-read-bmp": "1.3.1",
|
||||
"@misskey-dev/summaly": "5.5.1",
|
||||
"@napi-rs/canvas": "1.0.1",
|
||||
"@nestjs/common": "11.1.27",
|
||||
"@nestjs/core": "11.1.27",
|
||||
"@nestjs/testing": "11.1.27",
|
||||
"@napi-rs/canvas": "1.0.2",
|
||||
"@nestjs/common": "11.1.28",
|
||||
"@nestjs/core": "11.1.28",
|
||||
"@nestjs/testing": "11.1.28",
|
||||
"@opentelemetry/api": "1.9.1",
|
||||
"@opentelemetry/core": "2.8.0",
|
||||
"@opentelemetry/exporter-trace-otlp-proto": "0.214.0",
|
||||
"@opentelemetry/core": "2.9.0",
|
||||
"@opentelemetry/exporter-trace-otlp-proto": "0.220.0",
|
||||
"@opentelemetry/instrumentation-pg": "0.72.0",
|
||||
"@opentelemetry/resources": "2.7.1",
|
||||
"@opentelemetry/sdk-trace-base": "2.7.1",
|
||||
"@opentelemetry/sdk-trace-node": "2.7.1",
|
||||
"@opentelemetry/semantic-conventions": "1.40.0",
|
||||
"@oxc-project/runtime": "0.137.0",
|
||||
"@opentelemetry/resources": "2.9.0",
|
||||
"@opentelemetry/sdk-trace-base": "2.9.0",
|
||||
"@opentelemetry/sdk-trace-node": "2.9.0",
|
||||
"@opentelemetry/semantic-conventions": "1.43.0",
|
||||
"@oxc-project/runtime": "0.139.0",
|
||||
"@peertube/http-signature": "1.7.0",
|
||||
"@sentry/node": "10.62.0",
|
||||
"@sentry/profiling-node": "10.62.0",
|
||||
"@sentry/node": "10.65.0",
|
||||
"@sentry/profiling-node": "10.65.0",
|
||||
"@simplewebauthn/server": "13.3.2",
|
||||
"@smithy/node-http-handler": "4.8.2",
|
||||
"@smithy/node-http-handler": "4.9.5",
|
||||
"accepts": "1.3.8",
|
||||
"ajv": "8.20.0",
|
||||
"archiver": "8.0.0",
|
||||
"bcryptjs": "3.0.3",
|
||||
"blurhash": "2.0.5",
|
||||
"bullmq": "5.79.2",
|
||||
"bullmq": "5.80.2",
|
||||
"cacheable-lookup": "7.0.0",
|
||||
"chalk": "5.6.2",
|
||||
"chalk-template": "1.1.2",
|
||||
@@ -96,12 +96,12 @@
|
||||
"content-disposition": "2.0.1",
|
||||
"date-fns": "4.4.0",
|
||||
"deep-email-validator": "0.1.27",
|
||||
"fastify": "5.9.0",
|
||||
"fastify": "5.10.0",
|
||||
"fastify-raw-body": "5.0.0",
|
||||
"feed": "5.2.1",
|
||||
"feed": "6.0.0",
|
||||
"file-type": "22.0.1",
|
||||
"fluent-ffmpeg": "2.1.3",
|
||||
"got": "15.0.7",
|
||||
"got": "15.1.0",
|
||||
"hpagent": "1.2.0",
|
||||
"http-link-header": "1.1.3",
|
||||
"i18n": "workspace:*",
|
||||
@@ -112,17 +112,17 @@
|
||||
"json5": "2.2.3",
|
||||
"jsonld": "9.0.0",
|
||||
"juice": "12.1.1",
|
||||
"meilisearch": "0.58.0",
|
||||
"meilisearch": "0.59.0",
|
||||
"mfm-js": "0.26.0",
|
||||
"mime-types": "3.0.2",
|
||||
"misskey-js": "workspace:*",
|
||||
"misskey-reversi": "workspace:*",
|
||||
"ms": "3.0.0-canary.202508261828",
|
||||
"nanoid": "5.1.16",
|
||||
"nanoid": "6.0.0",
|
||||
"nested-property": "4.0.0",
|
||||
"node-fetch": "3.3.2",
|
||||
"node-html-parser": "8.0.3",
|
||||
"nodemailer": "9.0.1",
|
||||
"node-html-parser": "9.0.0",
|
||||
"nodemailer": "9.0.3",
|
||||
"os-utils": "0.0.14",
|
||||
"otpauth": "9.5.1",
|
||||
"pg": "8.22.0",
|
||||
@@ -132,21 +132,21 @@
|
||||
"qrcode": "1.5.4",
|
||||
"random-seed": "0.3.0",
|
||||
"ratelimiter": "3.4.1",
|
||||
"re2": "1.25.0",
|
||||
"re2": "1.26.0",
|
||||
"reflect-metadata": "0.2.2",
|
||||
"rename": "1.0.4",
|
||||
"rss-parser": "3.13.0",
|
||||
"sanitize-html": "2.17.5",
|
||||
"sanitize-html": "2.17.6",
|
||||
"secure-json-parse": "4.1.0",
|
||||
"semver": "7.8.5",
|
||||
"sharp": "0.35.2",
|
||||
"sharp": "0.35.3",
|
||||
"slacc": "0.1.5",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
"stringz": "2.1.0",
|
||||
"systeminformation": "5.31.11",
|
||||
"systeminformation": "5.31.16",
|
||||
"tinycolor2": "1.6.0",
|
||||
"tmp": "0.2.7",
|
||||
"tsc-alias": "1.8.17",
|
||||
"tsc-alias": "1.9.0",
|
||||
"typeorm": "1.0.0",
|
||||
"ulid": "3.0.2",
|
||||
"vary": "1.1.2",
|
||||
@@ -156,9 +156,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kitajs/ts-html-plugin": "4.1.4",
|
||||
"@nestjs/platform-express": "11.1.27",
|
||||
"@nestjs/platform-express": "11.1.28",
|
||||
"@rollup/plugin-esm-shim": "0.1.8",
|
||||
"@sentry/vue": "10.62.0",
|
||||
"@sentry/vue": "10.65.0",
|
||||
"@sinonjs/fake-timers": "15.4.0",
|
||||
"@types/accepts": "1.3.7",
|
||||
"@types/archiver": "8.0.0",
|
||||
@@ -167,7 +167,7 @@
|
||||
"@types/jsonld": "1.5.15",
|
||||
"@types/mime-types": "3.0.1",
|
||||
"@types/ms": "2.1.0",
|
||||
"@types/node": "26.0.1",
|
||||
"@types/node": "26.1.1",
|
||||
"@types/nodemailer": "8.0.1",
|
||||
"@types/pg": "8.20.0",
|
||||
"@types/qrcode": "1.5.6",
|
||||
@@ -183,21 +183,21 @@
|
||||
"@types/vary": "1.1.3",
|
||||
"@types/web-push": "3.6.4",
|
||||
"@types/ws": "8.18.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.62.0",
|
||||
"@vitest/coverage-v8": "4.1.9",
|
||||
"@typescript-eslint/eslint-plugin": "8.63.0",
|
||||
"@typescript-eslint/parser": "8.63.0",
|
||||
"@vitest/coverage-v8": "4.1.10",
|
||||
"aws-sdk-client-mock": "4.1.0",
|
||||
"cbor2": "2.3.0",
|
||||
"cross-env": "10.1.0",
|
||||
"eslint-plugin-import": "2.32.0",
|
||||
"execa": "9.6.1",
|
||||
"fkill": "10.0.3",
|
||||
"js-yaml": "5.2.0",
|
||||
"js-yaml": "5.2.1",
|
||||
"pid-port": "2.1.1",
|
||||
"rolldown": "1.1.3",
|
||||
"rolldown": "1.1.5",
|
||||
"simple-oauth2": "5.1.0",
|
||||
"vite": "8.1.0",
|
||||
"vitest": "4.1.9",
|
||||
"vite": "8.1.4",
|
||||
"vitest": "4.1.10",
|
||||
"vitest-mock-extended": "4.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ export default defineConfig((args) => {
|
||||
clearScreen: false,
|
||||
},
|
||||
// ビルドの高速化のために、watchモードのときは外部モジュールは全てバンドルしないようにする
|
||||
external: isWatchMode ? /^(?!@\/)[^.\/](?!:[\/\\])/ : externalModules,
|
||||
external: isWatchMode ? /^(?!@\/|\0)[^.\/](?!:[\/\\])/ : externalModules,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@ import { execa } from 'execa';
|
||||
});
|
||||
}, 3000);
|
||||
|
||||
execa('tsgo', ['-w', '-p', 'tsconfig.json'], {
|
||||
execa('tsc', ['-w', '-p', 'tsconfig.json'], {
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import * as Redis from 'ioredis';
|
||||
import type { BlockingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, MiUserProfile, UserProfilesRepository, UsersRepository, MiFollowing, MiMuting } from '@/models/_.js';
|
||||
import type { BlockingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, MiUserProfile, UserProfilesRepository, UsersRepository, MiFollowing } from '@/models/_.js';
|
||||
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
|
||||
import type { MiLocalUser, MiUser } from '@/models/User.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
@@ -21,7 +21,7 @@ export class CacheService implements OnApplicationShutdown {
|
||||
public localUserByIdCache: MemoryKVCache<MiLocalUser>;
|
||||
public uriPersonCache: MemoryKVCache<MiUser | null>;
|
||||
public userProfileCache: RedisKVCache<MiUserProfile>;
|
||||
public userMutingsCache: RedisKVCache<Map<string, Pick<MiMuting, 'mutingType'>>>;
|
||||
public userMutingsCache: RedisKVCache<Set<string>>;
|
||||
public userBlockingCache: RedisKVCache<Set<string>>;
|
||||
public userBlockedCache: RedisKVCache<Set<string>>; // NOTE: 「被」Blockキャッシュ
|
||||
public renoteMutingsCache: RedisKVCache<Set<string>>;
|
||||
@@ -69,15 +69,15 @@ export class CacheService implements OnApplicationShutdown {
|
||||
fromRedisConverter: (value) => JSON.parse(value), // TODO: date型の考慮
|
||||
});
|
||||
|
||||
this.userMutingsCache = new RedisKVCache<Map<string, Pick<MiMuting, 'mutingType'>>>(this.redisClient, 'userMutings', {
|
||||
this.userMutingsCache = new RedisKVCache<Set<string>>(this.redisClient, 'userMutings', {
|
||||
lifetime: 1000 * 60 * 30, // 30m
|
||||
memoryCacheLifetime: 1000 * 60, // 1m
|
||||
fetcher: (key) => this.mutingsRepository.find({
|
||||
where: { muterId: key },
|
||||
select: { muteeId: true, mutingType: true },
|
||||
}).then(xs => new Map(xs.map(x => [x.muteeId, { mutingType: x.mutingType }]))),
|
||||
toRedisConverter: (value) => JSON.stringify(Array.from(value.entries())),
|
||||
fromRedisConverter: (value) => new Map(JSON.parse(value)),
|
||||
select: { muteeId: true },
|
||||
}).then(xs => new Set(xs.map(x => x.muteeId))),
|
||||
toRedisConverter: (value) => JSON.stringify(Array.from(value)),
|
||||
fromRedisConverter: (value) => new Set(JSON.parse(value)),
|
||||
});
|
||||
|
||||
this.userBlockingCache = new RedisKVCache<Set<string>>(this.redisClient, 'userBlocking', {
|
||||
|
||||
@@ -111,7 +111,7 @@ export class FanoutTimelineEndpointService {
|
||||
if (ps.me) {
|
||||
const me = ps.me;
|
||||
const [
|
||||
userIdsWhoMeMutingMap,
|
||||
userIdsWhoMeMuting,
|
||||
userIdsWhoMeMutingRenotes,
|
||||
userIdsWhoBlockingMe,
|
||||
userMutedInstances,
|
||||
@@ -124,8 +124,6 @@ export class FanoutTimelineEndpointService {
|
||||
this.channelMutingService.mutingChannelsCache.fetch(me.id),
|
||||
]);
|
||||
|
||||
const userIdsWhoMeMuting = new Set(userIdsWhoMeMutingMap.keys());
|
||||
|
||||
const parentFilter = filter;
|
||||
filter = (note) => {
|
||||
if (isUserRelated(note, userIdsWhoBlockingMe, ps.ignoreAuthorFromBlock)) return false;
|
||||
|
||||
@@ -520,6 +520,11 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||
// specified / direct noteはreject
|
||||
throw new Error('Renote target is not public or home');
|
||||
}
|
||||
|
||||
// ローカルのみをRenoteしたらローカルのみにする
|
||||
if (data.renote.localOnly && data.channel == null) {
|
||||
data.localOnly = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check blocking
|
||||
@@ -534,19 +539,35 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||
}
|
||||
}
|
||||
|
||||
// 返信対象がpublicではないならhomeにする
|
||||
if (data.reply && data.reply.visibility !== 'public' && data.visibility === 'public') {
|
||||
data.visibility = 'home';
|
||||
}
|
||||
if (data.reply) {
|
||||
switch (data.reply.visibility) {
|
||||
case 'public':
|
||||
// public noteは無条件にreply可能
|
||||
break;
|
||||
case 'home':
|
||||
// home noteはhome以下にreply可能
|
||||
if (data.visibility === 'public') {
|
||||
data.visibility = 'home';
|
||||
}
|
||||
break;
|
||||
case 'followers':
|
||||
// followers noteはfollowers以下にreply可能
|
||||
if (data.visibility === 'public' || data.visibility === 'home') {
|
||||
data.visibility = 'followers';
|
||||
}
|
||||
break;
|
||||
case 'specified':
|
||||
// specified / direct noteはspecifiedのみreply可能
|
||||
if (data.visibility !== 'specified') {
|
||||
data.visibility = 'specified';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// ローカルのみをRenoteしたらローカルのみにする
|
||||
if (data.renote && data.renote.localOnly && data.channel == null) {
|
||||
data.localOnly = true;
|
||||
}
|
||||
|
||||
// ローカルのみにリプライしたらローカルのみにする
|
||||
if (data.reply && data.reply.localOnly && data.channel == null) {
|
||||
data.localOnly = true;
|
||||
// ローカルのみにリプライしたらローカルのみにする
|
||||
if (data.reply.localOnly && data.channel == null) {
|
||||
data.localOnly = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.text) {
|
||||
|
||||
@@ -108,8 +108,7 @@ export class NotificationService implements OnApplicationShutdown {
|
||||
}
|
||||
|
||||
const mutings = await this.cacheService.userMutingsCache.fetch(notifieeId);
|
||||
const muting = mutings.get(notifierId);
|
||||
if (muting != null && muting.mutingType === 'all') {
|
||||
if (mutings.has(notifierId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -307,16 +307,14 @@ export class SearchService {
|
||||
}
|
||||
|
||||
const [
|
||||
userIdsWhoMeMutingMap,
|
||||
userIdsWhoMeMuting,
|
||||
userIdsWhoBlockingMe,
|
||||
] = me
|
||||
? await Promise.all([
|
||||
this.cacheService.userMutingsCache.fetch(me.id),
|
||||
this.cacheService.userBlockedCache.fetch(me.id),
|
||||
])
|
||||
: [new Map<string, { mutingType: 'all' | 'timelineOnly' }>(), new Set<string>()];
|
||||
|
||||
const userIdsWhoMeMuting = new Set(userIdsWhoMeMutingMap.keys());
|
||||
: [new Set<string>(), new Set<string>()];
|
||||
|
||||
const query = this.notesRepository.createQueryBuilder('note')
|
||||
.innerJoinAndSelect('note.user', 'user')
|
||||
|
||||
@@ -24,13 +24,12 @@ export class UserMutingService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async mute(user: MiUser, target: MiUser, expiresAt: Date | null = null, mutingType: 'all' | 'timelineOnly' = 'all'): Promise<void> {
|
||||
public async mute(user: MiUser, target: MiUser, expiresAt: Date | null = null): Promise<void> {
|
||||
await this.mutingsRepository.insert({
|
||||
id: this.idService.gen(),
|
||||
expiresAt: expiresAt ?? null,
|
||||
muterId: user.id,
|
||||
muteeId: target.id,
|
||||
mutingType: mutingType,
|
||||
});
|
||||
|
||||
this.cacheService.userMutingsCache.refresh(user.id);
|
||||
|
||||
@@ -40,7 +40,6 @@ export class MutingEntityService {
|
||||
id: muting.id,
|
||||
createdAt: this.idService.parse(muting.id).date.toISOString(),
|
||||
expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null,
|
||||
mutingType: muting.mutingType,
|
||||
muteeId: muting.muteeId,
|
||||
mutee: hints?.packedMutee ?? this.userEntityService.pack(muting.muteeId, me, {
|
||||
schema: 'UserDetailedNotMe',
|
||||
|
||||
@@ -297,13 +297,12 @@ export class NotificationEntityService implements OnModuleInit {
|
||||
*/
|
||||
#validateNotifier <T extends MiNotification | MiGroupedNotification> (
|
||||
notification: T,
|
||||
userIdsWhoMeMutingMap: Map<MiUser['id'], { mutingType: 'all' | 'timelineOnly' }>,
|
||||
userIdsWhoMeMuting: Set<MiUser['id']>,
|
||||
userMutedInstances: Set<string>,
|
||||
notifiers: MiUser[],
|
||||
): boolean {
|
||||
if (!('notifierId' in notification)) return true;
|
||||
const muting = userIdsWhoMeMutingMap.get(notification.notifierId);
|
||||
if (muting != null && muting.mutingType === 'all') return false;
|
||||
if (userIdsWhoMeMuting.has(notification.notifierId)) return false;
|
||||
|
||||
const notifier = notifiers.find(x => x.id === notification.notifierId) ?? null;
|
||||
|
||||
@@ -333,7 +332,7 @@ export class NotificationEntityService implements OnModuleInit {
|
||||
meId: MiUser['id'],
|
||||
): Promise<T[]> {
|
||||
const [
|
||||
userIdsWhoMeMutingMap,
|
||||
userIdsWhoMeMuting,
|
||||
userMutedInstances,
|
||||
] = await Promise.all([
|
||||
this.cacheService.userMutingsCache.fetch(meId),
|
||||
@@ -346,7 +345,7 @@ export class NotificationEntityService implements OnModuleInit {
|
||||
}) : [];
|
||||
|
||||
const filteredNotifications = ((await Promise.all(notifications.map(async (notification) => {
|
||||
const isValid = this.#validateNotifier(notification, userIdsWhoMeMutingMap, userMutedInstances, notifiers);
|
||||
const isValid = this.#validateNotifier(notification, userIdsWhoMeMuting, userMutedInstances, notifiers);
|
||||
return isValid ? notification : null;
|
||||
}))) as [T | null] ).filter(x => x != null);
|
||||
|
||||
|
||||
@@ -19,12 +19,6 @@ export class MiMuting {
|
||||
})
|
||||
public expiresAt: Date | null;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 128,
|
||||
default: 'all',
|
||||
})
|
||||
public mutingType: 'all' | 'timelineOnly';
|
||||
|
||||
@Index()
|
||||
@Column({
|
||||
...id(),
|
||||
|
||||
@@ -22,11 +22,6 @@ export const packedMutingSchema = {
|
||||
optional: false, nullable: true,
|
||||
format: 'date-time',
|
||||
},
|
||||
mutingType: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
enum: ['all', 'timelineOnly'],
|
||||
},
|
||||
muteeId: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
|
||||
@@ -199,51 +199,54 @@ export class ApiCallService implements OnApplicationShutdown {
|
||||
reply.send();
|
||||
return;
|
||||
}
|
||||
|
||||
const [path, cleanup] = await createTemp();
|
||||
await stream.pipeline(multipartData.file, fs.createWriteStream(path));
|
||||
|
||||
// ファイルサイズが制限を超えていた場合
|
||||
// なお truncated はストリームを読み切ってからでないと機能しないため、stream.pipeline より後にある必要がある
|
||||
if (multipartData.file.truncated) {
|
||||
cleanup();
|
||||
reply.code(413);
|
||||
reply.send();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await stream.pipeline(multipartData.file, fs.createWriteStream(path));
|
||||
|
||||
const fields = {} as Record<string, unknown>;
|
||||
for (const [k, v] of Object.entries(multipartData.fields)) {
|
||||
fields[k] = typeof v === 'object' && 'value' in v ? v.value : undefined;
|
||||
}
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc6750.html#section-2.1 (case sensitive)
|
||||
const token = request.headers.authorization?.startsWith('Bearer ')
|
||||
? request.headers.authorization.slice(7)
|
||||
: fields['i'];
|
||||
if (token != null && typeof token !== 'string') {
|
||||
reply.code(400);
|
||||
return;
|
||||
}
|
||||
|
||||
return await this.telemetryService.startSpan('API: ' + endpoint.name, () => this.authenticateService.authenticate(token).then(([user, app]) => {
|
||||
const call = this.call(endpoint, user, app, fields, {
|
||||
name: multipartData.filename,
|
||||
path: path,
|
||||
}, request).then((res) => {
|
||||
this.send(reply, res);
|
||||
}).catch((err: ApiError) => {
|
||||
this.#sendApiError(reply, err);
|
||||
});
|
||||
|
||||
if (user) {
|
||||
this.logIp(request, user);
|
||||
// ファイルサイズが制限を超えていた場合
|
||||
// なお truncated はストリームを読み切ってからでないと機能しないため、stream.pipeline より後にある必要がある
|
||||
if (multipartData.file.truncated) {
|
||||
reply.code(413);
|
||||
reply.send();
|
||||
return;
|
||||
}
|
||||
|
||||
return call;
|
||||
}).catch(err => {
|
||||
this.#sendAuthenticationError(reply, err);
|
||||
}));
|
||||
const fields = {} as Record<string, unknown>;
|
||||
for (const [k, v] of Object.entries(multipartData.fields)) {
|
||||
fields[k] = typeof v === 'object' && 'value' in v ? v.value : undefined;
|
||||
}
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc6750.html#section-2.1 (case sensitive)
|
||||
const token = request.headers.authorization?.startsWith('Bearer ')
|
||||
? request.headers.authorization.slice(7)
|
||||
: fields['i'];
|
||||
if (token != null && typeof token !== 'string') {
|
||||
reply.code(400);
|
||||
return;
|
||||
}
|
||||
|
||||
return await this.telemetryService.startSpan('API: ' + endpoint.name, () => this.authenticateService.authenticate(token).then(([user, app]) => {
|
||||
const call = this.call(endpoint, user, app, fields, {
|
||||
name: multipartData.filename,
|
||||
path: path,
|
||||
}, request).then((res) => {
|
||||
this.send(reply, res);
|
||||
}).catch((err: ApiError) => {
|
||||
this.#sendApiError(reply, err);
|
||||
});
|
||||
|
||||
if (user) {
|
||||
this.logIp(request, user);
|
||||
}
|
||||
|
||||
return call;
|
||||
}).catch(err => {
|
||||
this.#sendAuthenticationError(reply, err);
|
||||
}));
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
||||
@@ -55,12 +55,6 @@ export const paramDef = {
|
||||
nullable: true,
|
||||
description: 'A Unix Epoch timestamp that must lie in the future. `null` means an indefinite mute.',
|
||||
},
|
||||
mutingType: {
|
||||
type: 'string',
|
||||
enum: ['all', 'timelineOnly'],
|
||||
default: 'all',
|
||||
description: 'Type of muting. `all` mutes everything including notifications. `timelineOnly` mutes only timeline and search, but allows notifications.',
|
||||
},
|
||||
},
|
||||
required: ['userId'],
|
||||
} as const;
|
||||
@@ -104,7 +98,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
return;
|
||||
}
|
||||
|
||||
await this.userMutingService.mute(muter, mutee, ps.expiresAt ? new Date(ps.expiresAt) : null, ps.mutingType ?? 'all');
|
||||
await this.userMutingService.mute(muter, mutee, ps.expiresAt ? new Date(ps.expiresAt) : null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,14 +80,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
|
||||
const [
|
||||
userIdsWhoMeMutingMap,
|
||||
userIdsWhoMeMuting,
|
||||
userIdsWhoBlockingMe,
|
||||
] = me ? await Promise.all([
|
||||
this.cacheService.userMutingsCache.fetch(me.id),
|
||||
this.cacheService.userBlockedCache.fetch(me.id),
|
||||
]) : [new Map<string, { mutingType: 'all' | 'timelineOnly' }>(), new Set<string>()];
|
||||
|
||||
const userIdsWhoMeMuting = new Set(userIdsWhoMeMutingMap.keys());
|
||||
]) : [new Set<string>(), new Set<string>()];
|
||||
|
||||
const query = this.notesRepository.createQueryBuilder('note')
|
||||
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
|
||||
|
||||
@@ -73,13 +73,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
|
||||
const [
|
||||
userIdsWhoMeMutingMap,
|
||||
userIdsWhoMeMuting,
|
||||
] = me ? await Promise.all([
|
||||
this.cacheService.userMutingsCache.fetch(me.id),
|
||||
]) : [new Map<string, { mutingType: 'all' | 'timelineOnly' }>()];
|
||||
|
||||
// Convert to Set for backward compatibility with isUserRelated
|
||||
const userIdsWhoMeMuting = new Set(userIdsWhoMeMutingMap.keys());
|
||||
]) : [new Set<string>()];
|
||||
|
||||
const query = this.notesRepository.createQueryBuilder('note')
|
||||
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
|
||||
|
||||
@@ -94,8 +94,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
}
|
||||
|
||||
const userIdsWhoMeMutingMap = me ? await this.cacheService.userMutingsCache.fetch(me.id) : new Map<string, { mutingType: 'all' | 'timelineOnly' }>();
|
||||
const userIdsWhoMeMuting = new Set(userIdsWhoMeMutingMap.keys());
|
||||
const userIdsWhoMeMuting = me ? await this.cacheService.userMutingsCache.fetch(me.id) : new Set<string>();
|
||||
|
||||
const query = this.queryService.makePaginationQuery(this.noteReactionsRepository.createQueryBuilder('reaction'),
|
||||
ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
|
||||
|
||||
@@ -59,7 +59,6 @@ export default class Connection {
|
||||
public followingChannels: Set<string> = new Set();
|
||||
public mutingChannels: Set<string> = new Set();
|
||||
public userIdsWhoMeMuting: Set<string> = new Set();
|
||||
public userIdsWhoMeMutingMap: Map<string, { mutingType: 'all' | 'timelineOnly' }> = new Map();
|
||||
public userIdsWhoBlockingMe: Set<string> = new Set();
|
||||
public userIdsWhoMeMutingRenotes: Set<string> = new Set();
|
||||
public userMutedInstances: Set<string> = new Set();
|
||||
@@ -86,7 +85,7 @@ export default class Connection {
|
||||
following,
|
||||
followingChannels,
|
||||
mutingChannels,
|
||||
userIdsWhoMeMutingMap,
|
||||
userIdsWhoMeMuting,
|
||||
userIdsWhoBlockingMe,
|
||||
userIdsWhoMeMutingRenotes,
|
||||
] = await Promise.all([
|
||||
@@ -102,8 +101,7 @@ export default class Connection {
|
||||
this.following = following;
|
||||
this.followingChannels = followingChannels;
|
||||
this.mutingChannels = mutingChannels;
|
||||
this.userIdsWhoMeMutingMap = userIdsWhoMeMutingMap;
|
||||
this.userIdsWhoMeMuting = new Set(userIdsWhoMeMutingMap.keys());
|
||||
this.userIdsWhoMeMuting = userIdsWhoMeMuting;
|
||||
this.userIdsWhoBlockingMe = userIdsWhoBlockingMe;
|
||||
this.userIdsWhoMeMutingRenotes = userIdsWhoMeMutingRenotes;
|
||||
this.userMutedInstances = new Set(userProfile.mutedInstances);
|
||||
|
||||
@@ -139,7 +139,7 @@ services:
|
||||
bash -c "
|
||||
npm install -g pnpm
|
||||
pnpm -F backend i --frozen-lockfile
|
||||
pnpm exec tsgo -p ./packages/backend/test-federation
|
||||
pnpm exec tsc -p ./packages/backend/test-federation
|
||||
node ./packages/backend/test-federation/built/daemon.js
|
||||
"
|
||||
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { describe, test, beforeAll } from 'vitest';
|
||||
import { api, post, react, signup, waitFire } from '../utils.js';
|
||||
import type * as misskey from 'misskey-js';
|
||||
|
||||
describe('Timeline-only Mute', () => {
|
||||
// alice timeline-only mutes carol
|
||||
let alice: misskey.entities.SignupResponse;
|
||||
let bob: misskey.entities.SignupResponse;
|
||||
let carol: misskey.entities.SignupResponse;
|
||||
|
||||
beforeAll(async () => {
|
||||
alice = await signup({ username: 'alice' });
|
||||
bob = await signup({ username: 'bob' });
|
||||
carol = await signup({ username: 'carol' });
|
||||
|
||||
// Timeline-only mute: alice ==> carol
|
||||
await api('mute/create', {
|
||||
userId: carol.id,
|
||||
mutingType: 'timelineOnly',
|
||||
}, alice);
|
||||
}, 1000 * 60 * 2);
|
||||
|
||||
test('タイムラインオンリーミュート作成', async () => {
|
||||
const res = await api('mute/create', {
|
||||
userId: bob.id,
|
||||
mutingType: 'timelineOnly',
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 204);
|
||||
|
||||
// Clean up side effects so tests can run independently
|
||||
await api('mute/delete', {
|
||||
userId: bob.id,
|
||||
}, alice);
|
||||
});
|
||||
|
||||
describe('Timeline', () => {
|
||||
test('タイムラインにtimelineOnlyミュートしているユーザーの投稿が含まれない', async () => {
|
||||
const aliceNote = await post(alice, { text: 'hi' });
|
||||
const bobNote = await post(bob, { text: 'hi' });
|
||||
const carolNote = await post(carol, { text: 'hi' });
|
||||
|
||||
const res = await api('notes/local-timeline', {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
|
||||
assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
|
||||
assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Notification', () => {
|
||||
test('timelineOnlyミュートしているユーザーからの通知は届く(リアクション)', async () => {
|
||||
const aliceNote = await post(alice, { text: 'hi' });
|
||||
await react(bob, aliceNote, 'like');
|
||||
await react(carol, aliceNote, 'like');
|
||||
|
||||
const res = await api('i/notifications', {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
|
||||
// carol is timelineOnly muted, so notifications should still come through
|
||||
assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), true);
|
||||
});
|
||||
|
||||
test('timelineOnlyミュートしているユーザーからのリプライ通知は届く', async () => {
|
||||
const aliceNote = await post(alice, { text: 'hi' });
|
||||
await post(bob, { text: '@alice hi', replyId: aliceNote.id });
|
||||
await post(carol, { text: '@alice hi', replyId: aliceNote.id });
|
||||
|
||||
const res = await api('i/notifications', {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(Array.isArray(res.body), true);
|
||||
|
||||
assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
|
||||
// carol is timelineOnly muted, so reply notifications should still come through
|
||||
assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), true);
|
||||
});
|
||||
|
||||
test('timelineOnlyミュートしているユーザーからのメンション通知は届く', async () => {
|
||||
await post(alice, { text: 'hi' });
|
||||
await post(bob, { text: '@alice hi' });
|
||||
await post(carol, { text: '@alice hi' });
|
||||
|
||||
const res = await api('i/notifications', {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(Array.isArray(res.body), true);
|
||||
|
||||
assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
|
||||
// carol is timelineOnly muted, so mention notifications should still come through
|
||||
assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), true);
|
||||
});
|
||||
|
||||
test('timelineOnlyミュートしているユーザーからの引用リノート通知は届く', async () => {
|
||||
const aliceNote = await post(alice, { text: 'hi' });
|
||||
await post(bob, { text: 'hi', renoteId: aliceNote.id });
|
||||
await post(carol, { text: 'hi', renoteId: aliceNote.id });
|
||||
|
||||
const res = await api('i/notifications', {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(Array.isArray(res.body), true);
|
||||
|
||||
assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
|
||||
// carol is timelineOnly muted, so quote notifications should still come through
|
||||
assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), true);
|
||||
});
|
||||
|
||||
test('timelineOnlyミュートしているユーザーからメンションされても、ストリームに unreadNotification イベントが流れてくる', async () => {
|
||||
// Reset state
|
||||
await api('notifications/mark-all-as-read', {}, alice);
|
||||
|
||||
const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadNotification');
|
||||
|
||||
// carol is timelineOnly muted, so notification stream should fire
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -3,7 +3,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
|
||||
"typecheck": "tsgo --noEmit",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "pnpm typecheck && pnpm eslint"
|
||||
},
|
||||
"exports": {
|
||||
@@ -11,16 +11,16 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/estree": "1.0.9",
|
||||
"@types/node": "26.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.62.0",
|
||||
"@types/node": "26.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.63.0",
|
||||
"@typescript-eslint/parser": "8.63.0",
|
||||
"rollup": "4.62.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"i18n": "workspace:*",
|
||||
"magic-string": "0.30.21",
|
||||
"oxc-walker": "1.0.0",
|
||||
"rolldown": "1.1.3",
|
||||
"vite": "8.1.0"
|
||||
"rolldown": "1.1.5",
|
||||
"vite": "8.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"mfm-js": "0.26.0",
|
||||
"misskey-js": "workspace:*",
|
||||
"punycode.js": "2.3.1",
|
||||
"shiki": "4.3.0",
|
||||
"shiki": "4.3.1",
|
||||
"tinycolor2": "1.6.0",
|
||||
"uuid": "14.0.1",
|
||||
"vue": "3.5.39"
|
||||
@@ -30,13 +30,13 @@
|
||||
"@rollup/pluginutils": "5.4.0",
|
||||
"@tabler/icons-webfont": "3.35.0",
|
||||
"@testing-library/vue": "8.1.0",
|
||||
"@types/node": "26.0.1",
|
||||
"@types/node": "26.1.1",
|
||||
"@types/punycode.js": "npm:@types/punycode@2.1.4",
|
||||
"@types/tinycolor2": "1.4.6",
|
||||
"@types/ws": "8.18.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.62.0",
|
||||
"@vitest/coverage-v8": "4.1.9",
|
||||
"@typescript-eslint/eslint-plugin": "8.63.0",
|
||||
"@typescript-eslint/parser": "8.63.0",
|
||||
"@vitest/coverage-v8": "4.1.10",
|
||||
"@vitejs/plugin-vue": "6.0.7",
|
||||
"@vue/runtime-core": "3.5.39",
|
||||
"eslint-plugin-import": "2.32.0",
|
||||
@@ -44,10 +44,11 @@
|
||||
"intersection-observer": "0.12.2",
|
||||
"lightningcss": "1.32.0",
|
||||
"sass-embedded": "1.100.0",
|
||||
"tsx": "4.22.4",
|
||||
"vite": "8.1.0",
|
||||
"vue-component-type-helpers": "3.3.5",
|
||||
"tsx": "4.23.0",
|
||||
"typescript": "6.0.2",
|
||||
"vite": "8.1.4",
|
||||
"vue-component-type-helpers": "3.3.7",
|
||||
"vue-eslint-parser": "10.4.1",
|
||||
"vue-tsc": "3.3.5"
|
||||
"vue-tsc": "3.3.7"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
|
||||
"typecheck": "tsgo --noEmit",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "pnpm typecheck && pnpm eslint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "26.0.1",
|
||||
"@types/node": "26.1.1",
|
||||
"@types/tinycolor2": "1.4.6",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.62.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.63.0",
|
||||
"@typescript-eslint/parser": "8.63.0",
|
||||
"eslint-plugin-vue": "10.9.2",
|
||||
"vue-eslint-parser": "10.4.1"
|
||||
},
|
||||
@@ -23,7 +23,7 @@
|
||||
"i18n": "workspace:*",
|
||||
"json5": "2.2.3",
|
||||
"misskey-js": "workspace:*",
|
||||
"shiki": "4.3.0",
|
||||
"shiki": "4.3.1",
|
||||
"tinycolor2": "1.6.0",
|
||||
"vue": "3.5.39"
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"watch": "vite",
|
||||
"build": "tsx build.ts",
|
||||
"storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"",
|
||||
"build-storybook-pre": "(tsgo -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
|
||||
"build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
|
||||
"build-storybook": "pnpm build-storybook-pre && storybook build --webpack-stats-json storybook-static",
|
||||
"chromatic": "chromatic",
|
||||
"test": "vitest --run --globals --config vitest.config.unit.ts",
|
||||
@@ -22,7 +22,7 @@
|
||||
"@mcaptcha/core-glue": "0.1.0-alpha-5",
|
||||
"@misskey-dev/browser-image-resizer": "2024.1.0",
|
||||
"@misskey-dev/emoji-data": "17.0.3",
|
||||
"@sentry/vue": "10.62.0",
|
||||
"@sentry/vue": "10.65.0",
|
||||
"@simplewebauthn/browser": "13.3.0",
|
||||
"@syuilo/aiscript": "1.2.1",
|
||||
"@syuilo/aiscript-0-19-0": "npm:@syuilo/aiscript@^0.19.0",
|
||||
@@ -34,7 +34,7 @@
|
||||
"canvas-confetti": "1.9.4",
|
||||
"chart.js": "4.5.1",
|
||||
"chartjs-adapter-date-fns": "3.0.0",
|
||||
"chartjs-chart-matrix": "3.0.4",
|
||||
"chartjs-chart-matrix": "3.0.5",
|
||||
"chartjs-plugin-gradient": "0.6.1",
|
||||
"chartjs-plugin-zoom": "2.2.0",
|
||||
"compare-versions": "6.1.1",
|
||||
@@ -45,13 +45,13 @@
|
||||
"frontend-shared": "workspace:*",
|
||||
"i18n": "workspace:*",
|
||||
"icons-subsetter": "workspace:*",
|
||||
"idb-keyval": "6.2.5",
|
||||
"idb-keyval": "6.3.0",
|
||||
"insert-text-at-cursor": "0.3.0",
|
||||
"ios-haptics": "0.1.5",
|
||||
"is-file-animated": "1.0.2",
|
||||
"json5": "2.2.3",
|
||||
"matter-js": "0.20.0",
|
||||
"mediabunny": "1.49.0",
|
||||
"mediabunny": "1.50.8",
|
||||
"mfm-js": "0.26.0",
|
||||
"misskey-bubble-game": "workspace:*",
|
||||
"misskey-js": "workspace:*",
|
||||
@@ -59,9 +59,9 @@
|
||||
"punycode.js": "2.3.1",
|
||||
"qr-code-styling": "1.9.2",
|
||||
"qr-scanner": "1.4.2",
|
||||
"sanitize-html": "2.17.5",
|
||||
"sanitize-html": "2.17.6",
|
||||
"seedrandom": "3.0.5",
|
||||
"shiki": "4.3.0",
|
||||
"shiki": "4.3.1",
|
||||
"textarea-caret": "3.1.0",
|
||||
"throttle-debounce": "5.0.2",
|
||||
"tinycolor2": "1.6.0",
|
||||
@@ -77,7 +77,7 @@
|
||||
"@rollup/pluginutils": "5.4.0",
|
||||
"@storybook/addon-essentials": "8.6.18",
|
||||
"@storybook/addon-interactions": "8.6.18",
|
||||
"@storybook/addon-links": "10.4.6",
|
||||
"@storybook/addon-links": "10.5.0",
|
||||
"@storybook/addon-mdx-gfm": "8.6.18",
|
||||
"@storybook/addon-storysource": "8.6.18",
|
||||
"@storybook/blocks": "8.6.18",
|
||||
@@ -88,8 +88,8 @@
|
||||
"@storybook/test": "8.6.18",
|
||||
"@storybook/theming": "8.6.18",
|
||||
"@storybook/types": "8.6.18",
|
||||
"@storybook/vue3": "10.4.6",
|
||||
"@storybook/vue3-vite": "10.4.6",
|
||||
"@storybook/vue3": "10.5.0",
|
||||
"@storybook/vue3-vite": "10.5.0",
|
||||
"@tabler/icons-webfont": "3.35.0",
|
||||
"@testing-library/vue": "8.1.0",
|
||||
"@types/canvas-confetti": "1.9.0",
|
||||
@@ -97,20 +97,20 @@
|
||||
"@types/insert-text-at-cursor": "0.3.2",
|
||||
"@types/matter-js": "0.20.2",
|
||||
"@types/micromatch": "4.0.10",
|
||||
"@types/node": "26.0.1",
|
||||
"@types/node": "26.1.1",
|
||||
"@types/punycode.js": "npm:@types/punycode@2.1.4",
|
||||
"@types/sanitize-html": "2.16.1",
|
||||
"@types/seedrandom": "3.0.8",
|
||||
"@types/textarea-caret": "3.0.4",
|
||||
"@types/throttle-debounce": "5.0.2",
|
||||
"@types/tinycolor2": "1.4.6",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.62.0",
|
||||
"@vitest/browser-playwright": "4.1.9",
|
||||
"@vitest/coverage-v8": "4.1.9",
|
||||
"@typescript-eslint/eslint-plugin": "8.63.0",
|
||||
"@typescript-eslint/parser": "8.63.0",
|
||||
"@vitest/browser-playwright": "4.1.10",
|
||||
"@vitest/coverage-v8": "4.1.10",
|
||||
"@vue/compiler-core": "3.5.39",
|
||||
"astring": "1.9.0",
|
||||
"chromatic": "17.7.2",
|
||||
"chromatic": "18.0.1",
|
||||
"eslint-plugin-import": "2.32.0",
|
||||
"eslint-plugin-vue": "10.9.2",
|
||||
"execa": "9.6.1",
|
||||
@@ -119,27 +119,28 @@
|
||||
"lightningcss": "1.32.0",
|
||||
"micromatch": "4.0.8",
|
||||
"minimatch": "10.2.5",
|
||||
"msw": "2.14.6",
|
||||
"msw": "2.15.0",
|
||||
"msw-storybook-addon": "2.0.7",
|
||||
"nodemon": "3.1.14",
|
||||
"oxc-walker": "1.0.0",
|
||||
"playwright": "1.61.1",
|
||||
"prettier": "3.9.1",
|
||||
"prettier": "3.9.5",
|
||||
"react": "19.2.7",
|
||||
"react-dom": "19.2.7",
|
||||
"rolldown": "1.1.3",
|
||||
"rolldown": "1.1.5",
|
||||
"rollup-plugin-visualizer": "7.0.1",
|
||||
"sass-embedded": "1.100.0",
|
||||
"storybook": "10.4.6",
|
||||
"storybook": "10.5.0",
|
||||
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
||||
"tsx": "4.22.4",
|
||||
"vite": "8.1.0",
|
||||
"tsx": "4.23.0",
|
||||
"typescript": "6.0.2",
|
||||
"vite": "8.1.4",
|
||||
"vite-plugin-glsl": "1.6.0",
|
||||
"vite-plugin-turbosnap": "1.0.3",
|
||||
"vitest": "4.1.9",
|
||||
"vitest": "4.1.10",
|
||||
"vitest-fetch-mock": "0.4.5",
|
||||
"vue-component-type-helpers": "3.3.5",
|
||||
"vue-component-type-helpers": "3.3.7",
|
||||
"vue-eslint-parser": "10.4.1",
|
||||
"vue-tsc": "3.3.5"
|
||||
"vue-tsc": "3.3.7"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts">
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkBlurhash from './MkBlurhash.vue';
|
||||
|
||||
type Size = {
|
||||
width: number;
|
||||
@@ -178,8 +177,9 @@ export function calculateSourceTransform({
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, ref, useTemplateRef, markRaw, watch, provide } from 'vue';
|
||||
import MkVideoControl from './MkVideoControl.vue';
|
||||
import { computed, nextTick, ref, useTemplateRef, markRaw, watch, provide, onBeforeUnmount } from 'vue';
|
||||
import MkVideoControl from '@/components/MkVideoControl.vue';
|
||||
import MkBlurhash from '@/components/MkBlurhash.vue';
|
||||
import XFileInfo from './MkLightbox.item.fileinfo.vue';
|
||||
import type { MenuItem } from '@/types/menu.js';
|
||||
import { DI } from '@/di.js';
|
||||
@@ -451,6 +451,17 @@ function onZoomGestureEnd() {
|
||||
transform.value = clampedTransform;
|
||||
}
|
||||
|
||||
/** 縦・横どちらのスワイプかを確定させるのに必要な、開始点からの移動量 (px) */
|
||||
const AXIS_SWIPE_HYSTERISIS = 10;
|
||||
/** スワイプが成立する速度 (px/ms) */
|
||||
const MIN_VELOCITY_TO_SWIPE = 0.5;
|
||||
/**縦スワイプで閉じるのに必要な移動量の、ビューポート高の1/3に対する比 */
|
||||
const MIN_RATIO_TO_CLOSE = 0.4;
|
||||
/** 横スワイプで前後のコンテンツに移動するのに必要な移動量 (px) */
|
||||
const HORIZONTAL_SWIPE_DISTANCE_THRESHOLD = 150;
|
||||
/** 速度を平均する時間窓 (ms) */
|
||||
const VELOCITY_WINDOW = 100;
|
||||
|
||||
let isDragging = false;
|
||||
let isClick = false;
|
||||
let clickAction: 'hidden' | 'video' | null = null;
|
||||
@@ -460,12 +471,43 @@ let currentPointerId: number | null = null;
|
||||
let currentPointerStartOffset = { x: 0, y: 0 };
|
||||
let isVerticalSwiping = false;
|
||||
let isHorizontalSwiping = false;
|
||||
let verticalSwipeDelta = 0;
|
||||
let horizontalSwipeDelta = 0;
|
||||
/** 軸が確定した時点のポインタ位置。ここを基準にスワイプ量を測ることで、確定時に描画が飛ぶのを防ぐ */
|
||||
let swipeOrigin = { x: 0, y: 0 };
|
||||
|
||||
const pointerEventCache = new Map<number, PointerEvent>();
|
||||
let pointerVec = { x: 0, y: 0 };
|
||||
|
||||
// 単発のpointermoveの増分から速度を求めると、表示のリフレッシュレートを超える頻度で
|
||||
// pointermoveを発火する環境では値が暴れるため、直近VELOCITY_WINDOW msの平均を取る
|
||||
let velocitySamples: { time: number; x: number; y: number }[] = [];
|
||||
|
||||
function pushVelocitySample(time: number, x: number, y: number) {
|
||||
velocitySamples.push({ time, x, y });
|
||||
while (velocitySamples.length > 2 && time - velocitySamples[0].time > VELOCITY_WINDOW) {
|
||||
velocitySamples.shift();
|
||||
}
|
||||
}
|
||||
|
||||
function getVelocity(now: number): { x: number; y: number } {
|
||||
if (velocitySamples.length < 2) return { x: 0, y: 0 };
|
||||
|
||||
const latest = velocitySamples[velocitySamples.length - 1];
|
||||
|
||||
// 指を止めたまま離した場合、pointermoveが発火しなくなる環境 (iOS・マウス) では直前のフリックの速度が
|
||||
// 残り続けてしまい、静止状態で離したのにスワイプが成立してしまう。最後のサンプルが古ければ速度なしとして扱う
|
||||
if (now - latest.time > VELOCITY_WINDOW) return { x: 0, y: 0 };
|
||||
|
||||
const oldest = velocitySamples[0];
|
||||
const duration = latest.time - oldest.time;
|
||||
if (duration <= 0) return { x: 0, y: 0 };
|
||||
|
||||
return {
|
||||
x: (latest.x - oldest.x) / duration,
|
||||
y: (latest.y - oldest.y) / duration,
|
||||
};
|
||||
}
|
||||
|
||||
function resolveClickAction(target: EventTarget | null): 'hidden' | 'video' | null {
|
||||
if (!(target instanceof Element)) return null;
|
||||
|
||||
@@ -488,22 +530,29 @@ function onPointerdown(ev: PointerEvent) {
|
||||
lastX = ev.clientX;
|
||||
lastY = ev.clientY;
|
||||
pointerVec = { x: 0, y: 0 };
|
||||
velocitySamples = [];
|
||||
horizontalSwipeDelta = 0;
|
||||
// スワイプで閉じた直後はitemがv-showで残ったままなので (MkLightbox.vue参照)、閉じるアニメーション中に
|
||||
// 触るとロック済みの軸が引き継がれてヒステリシスを経ずにスワイプが始まってしまう。ここで必ず解除する
|
||||
isVerticalSwiping = false;
|
||||
isHorizontalSwiping = false;
|
||||
swipeOrigin = { x: ev.clientX, y: ev.clientY };
|
||||
if (currentPointerId == null) {
|
||||
currentPointerId = ev.pointerId;
|
||||
currentPointerStartOffset = {
|
||||
x: ev.clientX,
|
||||
y: ev.clientY,
|
||||
};
|
||||
// pointerdown自体を最初のサンプルとして積む。これがないとpointermoveが1回しか発生しないような
|
||||
// 素早いフリックでサンプルが1つしか溜まらず、速度が常に0と評価されてスワイプが成立しない
|
||||
pushVelocitySample(ev.timeStamp, ev.clientX, ev.clientY);
|
||||
}
|
||||
}
|
||||
|
||||
let prevTwoTouchPointsDistance = 0;
|
||||
let lastMoveTimeStamp = 0;
|
||||
|
||||
function onPointermove(ev: PointerEvent) {
|
||||
const currentTime = performance.now();
|
||||
const dt = currentTime - lastMoveTimeStamp;
|
||||
lastMoveTimeStamp = currentTime;
|
||||
const currentTime = ev.timeStamp;
|
||||
|
||||
if (pointerEventCache.size === 0) {
|
||||
return;
|
||||
@@ -547,23 +596,35 @@ function onPointermove(ev: PointerEvent) {
|
||||
} else {
|
||||
if (isVerticalSwiping) {
|
||||
transform.value.y += deltaY;
|
||||
verticalSwipeDelta += deltaY;
|
||||
} else if (isHorizontalSwiping) {
|
||||
horizontalSwipeDelta = ev.clientX - currentPointerStartOffset.x;
|
||||
horizontalSwipeDelta = ev.clientX - swipeOrigin.x;
|
||||
emit('horizontalSwipe', horizontalSwipeDelta);
|
||||
} else {
|
||||
const isVerticalVector = Math.abs(deltaY) > Math.abs(deltaX);
|
||||
if (isVerticalVector) {
|
||||
isVerticalSwiping = true;
|
||||
} else {
|
||||
isHorizontalSwiping = true;
|
||||
// 軸を確定させるまでは、開始点からの累積移動量で毎回評価し直す。
|
||||
const totalX = ev.clientX - currentPointerStartOffset.x;
|
||||
const totalY = ev.clientY - currentPointerStartOffset.y;
|
||||
const diff = Math.abs(totalX) - Math.abs(totalY);
|
||||
|
||||
if (diff !== 0) {
|
||||
// 完全に同値の場合はどちらにもロックしない
|
||||
const isVerticalVector = diff < 0;
|
||||
const dominantDelta = isVerticalVector ? totalY : totalX;
|
||||
|
||||
// 意図が読み取れる程度に動いてからロックする
|
||||
if (Math.abs(dominantDelta) >= AXIS_SWIPE_HYSTERISIS) {
|
||||
swipeOrigin = { x: ev.clientX, y: ev.clientY };
|
||||
if (isVerticalVector) {
|
||||
isVerticalSwiping = true;
|
||||
} else {
|
||||
isHorizontalSwiping = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dt > 0) {
|
||||
pointerVec = { x: deltaX / dt, y: deltaY / dt };
|
||||
}
|
||||
pushVelocitySample(currentTime, ev.clientX, ev.clientY);
|
||||
pointerVec = getVelocity(currentTime);
|
||||
|
||||
lastX = ev.clientX;
|
||||
lastY = ev.clientY;
|
||||
@@ -581,9 +642,20 @@ function onPointerup(ev: PointerEvent) {
|
||||
if (currentPointerId === ev.pointerId) {
|
||||
currentPointerId = null;
|
||||
|
||||
// 離した時点で取り直す。指を止めたまま離した場合はここで速度が0になり、
|
||||
// 直前のフリックの速度で誤ってスワイプが成立するのを防ぐことができる
|
||||
pointerVec = getVelocity(ev.timeStamp);
|
||||
|
||||
// 判定にはジェスチャー開始点からの総移動量を使う。描画用のdelta (transform.y / horizontalSwipeDelta) は
|
||||
// 軸が確定した地点を基準にしており、確定までのヒステリシス分と確定したpointermove自体の移動量を含まないため、
|
||||
// pointermoveが1回しか発生しないような素早いフリックがそのまま握り潰されてしまう
|
||||
const totalSwipeX = ev.clientX - currentPointerStartOffset.x;
|
||||
const totalSwipeY = ev.clientY - currentPointerStartOffset.y;
|
||||
|
||||
if (isVerticalSwiping) {
|
||||
const shouldCloseByUpwardSwipe = verticalSwipeDelta < -200 || (verticalSwipeDelta < 0 && pointerVec.y < -3); // 上の方で離された、または上に向かって強めに弾かれた
|
||||
const shouldCloseByDownwardSwipe = verticalSwipeDelta > 200 || (verticalSwipeDelta > 0 && pointerVec.y > 3); // 下の方で離された、または下に向かって強めに弾かれた
|
||||
const closeThreshold = (window.innerHeight / 3) * MIN_RATIO_TO_CLOSE;
|
||||
const shouldCloseByUpwardSwipe = totalSwipeY < -closeThreshold || (totalSwipeY < 0 && pointerVec.y < -MIN_VELOCITY_TO_SWIPE); // 上の方で離された、または上に向かって強めに弾かれた
|
||||
const shouldCloseByDownwardSwipe = totalSwipeY > closeThreshold || (totalSwipeY > 0 && pointerVec.y > MIN_VELOCITY_TO_SWIPE); // 下の方で離された、または下に向かって強めに弾かれた
|
||||
if (shouldCloseByUpwardSwipe || shouldCloseByDownwardSwipe) {
|
||||
closeThis();
|
||||
return;
|
||||
@@ -591,8 +663,8 @@ function onPointerup(ev: PointerEvent) {
|
||||
|
||||
resetToNeutral();
|
||||
} else if (isHorizontalSwiping) {
|
||||
const shouldNext = horizontalSwipeDelta < -150 || (horizontalSwipeDelta < 0 && pointerVec.x < -1); // 左の方で離された、または左に向かって強めに弾かれた
|
||||
const shouldPrev = horizontalSwipeDelta > 150 || (horizontalSwipeDelta > 0 && pointerVec.x > 1); // 右の方で離された、または右に向かって強めに弾かれた
|
||||
const shouldNext = totalSwipeX < -HORIZONTAL_SWIPE_DISTANCE_THRESHOLD || (totalSwipeX < 0 && pointerVec.x < -MIN_VELOCITY_TO_SWIPE); // 左の方で離された、または左に向かって強めに弾かれた
|
||||
const shouldPrev = totalSwipeX > HORIZONTAL_SWIPE_DISTANCE_THRESHOLD || (totalSwipeX > 0 && pointerVec.x > MIN_VELOCITY_TO_SWIPE); // 右の方で離された、または右に向かって強めに弾かれた
|
||||
if (shouldNext) {
|
||||
emit('next');
|
||||
} else if (shouldPrev) {
|
||||
@@ -609,8 +681,6 @@ function onPointerup(ev: PointerEvent) {
|
||||
}
|
||||
|
||||
const doubleTapDetector = makeDoubleTapDetector((ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
pointerVec = { x: 0, y: 0 };
|
||||
|
||||
if (isZooming.value) {
|
||||
@@ -634,8 +704,9 @@ function cancelPointerGesture() {
|
||||
isClick = false;
|
||||
clickAction = null;
|
||||
pointerVec = { x: 0, y: 0 };
|
||||
verticalSwipeDelta = 0;
|
||||
velocitySamples = [];
|
||||
horizontalSwipeDelta = 0;
|
||||
swipeOrigin = { x: 0, y: 0 };
|
||||
isVerticalSwiping = false;
|
||||
isHorizontalSwiping = false;
|
||||
doubleTapDetector.reset();
|
||||
@@ -821,6 +892,12 @@ function onDeactive() {
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (rafHandle) {
|
||||
window.cancelAnimationFrame(rafHandle);
|
||||
}
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
onActive,
|
||||
onDeactive,
|
||||
|
||||
@@ -67,7 +67,7 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const activatedIndexes = ref(new Set<number>());
|
||||
const items = new Map<number, InstanceType<typeof XItem>>();
|
||||
const items = new Map<number, InstanceType<typeof XItem> | null>();
|
||||
const currentIndex = ref(props.defaultIndex ?? 0);
|
||||
|
||||
watch(currentIndex, (newIndex, oldIndex) => {
|
||||
@@ -75,10 +75,10 @@ watch(currentIndex, (newIndex, oldIndex) => {
|
||||
|
||||
nextTick(() => {
|
||||
if (oldIndex != null && items.has(oldIndex)) {
|
||||
items.get(oldIndex)!.onDeactive();
|
||||
items.get(oldIndex)?.onDeactive();
|
||||
}
|
||||
if (items.has(newIndex)) {
|
||||
items.get(newIndex)!.onActive();
|
||||
items.get(newIndex)?.onActive();
|
||||
}
|
||||
});
|
||||
}, { immediate: true });
|
||||
@@ -142,8 +142,9 @@ function closeGallery() {
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (items.has(currentIndex.value)) {
|
||||
items.get(currentIndex.value)!.closeThis();
|
||||
const item = items.get(currentIndex.value);
|
||||
if (item != null) {
|
||||
item.closeThis();
|
||||
} else {
|
||||
closeGallery();
|
||||
}
|
||||
@@ -209,6 +210,7 @@ const keymap = {
|
||||
} as const satisfies Keymap;
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
items.clear();
|
||||
window.removeEventListener('resize', onResize);
|
||||
window.removeEventListener('popstate', onPopState);
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div :class="[hide ? $style.hidden : $style.visible, (image.isSensitive && prefer.s.highlightSensitiveMedia) && $style.sensitive]" @click="onClick" @contextmenu.stop="onContextmenu">
|
||||
<div :class="[hide ? $style.hidden : $style.visible, (image.isSensitive && prefer.s.highlightSensitiveMedia) && $style.sensitive]" @pointerup="onClick" @contextmenu.stop="onContextmenu">
|
||||
<component
|
||||
:is="disableImageLink ? 'div' : 'a'"
|
||||
v-bind="disableImageLink ? {
|
||||
@@ -61,8 +61,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div v-if="image.comment" :class="$style.indicator">ALT</div>
|
||||
<div v-if="image.isSensitive" :class="$style.indicator" style="color: var(--MI_THEME-warn);" :title="i18n.ts.sensitive"><i class="ti ti-eye-exclamation"></i></div>
|
||||
</div>
|
||||
<button :class="$style.menu" class="_button" @click.stop="showMenu"><i class="ti ti-dots" style="vertical-align: middle;"></i></button>
|
||||
<i class="ti ti-eye-off" :class="$style.hide" @click.stop="hide = true"></i>
|
||||
<button :class="[$style.menu, $style.menuBottom]" class="_button" @click.stop="showMenu" @pointerup.stop><i class="ti ti-dots" style="vertical-align: middle;" aria-hidden="true"></i></button>
|
||||
<button :class="[$style.menu, $style.menuTop]" class="_button" @click.stop="hide = true" @pointerup.stop><i class="ti ti-eye-off" style="vertical-align: middle;" aria-hidden="true"></i></button>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@@ -174,23 +174,6 @@ function onContextmenu(ev: PointerEvent) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: block;
|
||||
position: absolute;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
-webkit-backdrop-filter: var(--MI-blur, blur(15px));
|
||||
backdrop-filter: var(--MI-blur, blur(15px));
|
||||
border-radius: 0 0 0 9px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
opacity: .5;
|
||||
padding: 5px 8px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.hiddenTextWrapper {
|
||||
display: table-cell;
|
||||
text-align: center;
|
||||
@@ -221,16 +204,25 @@ html[data-color-scheme=light] .visible {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
-webkit-backdrop-filter: var(--MI-blur, blur(15px));
|
||||
backdrop-filter: var(--MI-blur, blur(15px));
|
||||
border-radius: 9px 0 0 0;
|
||||
color: #fff;
|
||||
font-size: 0.8em;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.menuBottom {
|
||||
border-radius: 8px 0 8px 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.menuTop {
|
||||
border-radius: 0 8px 0 8px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.imageContainer {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -12,7 +12,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
(video.isSensitive && prefer.s.highlightSensitiveMedia) && $style.sensitive,
|
||||
]"
|
||||
@contextmenu.stop="onContextmenu"
|
||||
@keydown.stop
|
||||
>
|
||||
<button v-if="hide" :class="$style.hidden" @click="reveal">
|
||||
<div :class="$style.hiddenTextWrapper">
|
||||
@@ -22,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div v-else :class="$style.videoRoot" @click="emit('mediaClick', $event)">
|
||||
<div v-else :class="$style.videoRoot" @pointerup="emit('mediaClick', $event)">
|
||||
<img
|
||||
v-if="video.thumbnailUrl"
|
||||
:class="$style.video"
|
||||
@@ -42,8 +41,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<i class="ti ti-player-play"></i>
|
||||
</div>
|
||||
</div>
|
||||
<button :class="$style.menu" class="_button" @click.stop="showMenu"><i class="ti ti-dots" style="vertical-align: middle;"></i></button>
|
||||
<i class="ti ti-eye-off" :class="$style.hide" @click.stop="hide = true"></i>
|
||||
<button :class="[$style.menu, $style.menuBottom]" class="_button" @click.stop="showMenu" @pointerup.stop><i class="ti ti-dots" style="vertical-align: middle;" aria-hidden="true"></i></button>
|
||||
<button :class="[$style.menu, $style.menuTop]" class="_button" @click.stop="hide = true" @pointerup.stop><i class="ti ti-eye-off" style="vertical-align: middle;" aria-hidden="true"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -184,29 +183,21 @@ function onContextmenu(ev: PointerEvent) {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
-webkit-backdrop-filter: var(--MI-blur, blur(15px));
|
||||
backdrop-filter: var(--MI-blur, blur(15px));
|
||||
border-radius: 9px 0 0 0;
|
||||
color: #fff;
|
||||
font-size: 0.8em;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.menuBottom {
|
||||
border-radius: 8px 0 8px 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: block;
|
||||
position: absolute;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
-webkit-backdrop-filter: var(--MI-blur, blur(15px));
|
||||
backdrop-filter: var(--MI-blur, blur(15px));
|
||||
border-radius: 0 0 0 9px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
opacity: .5;
|
||||
padding: 5px 8px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
.menuTop {
|
||||
border-radius: 0 8px 0 8px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
@@ -1,217 +0,0 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<MkModal ref="modal" :preferType="'dialog'" :zPriority="'high'" @click="done(true)" @closed="emit('closed')">
|
||||
<div :class="$style.root" class="_gaps_m">
|
||||
<div class="_gaps_s">
|
||||
<div :class="$style.header">
|
||||
<div :class="$style.icon">
|
||||
<i class="ti ti-alert-triangle"></i>
|
||||
</div>
|
||||
<div :class="$style.title">{{ i18n.ts.muteConfirm }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_gaps">
|
||||
<FormSlot>
|
||||
<div class="_gaps_s">
|
||||
<MkSelect v-model="periodModel" :items="periodDef">
|
||||
<template #label>{{ i18n.ts.mutePeriod }}</template>
|
||||
</MkSelect>
|
||||
<MkInput
|
||||
v-if="periodModel === 'custom'"
|
||||
v-model="manualExpiresAt"
|
||||
type="datetime-local"
|
||||
></MkInput>
|
||||
</div>
|
||||
<template #caption>{{ i18n.ts.mutePeriodDescription }}</template>
|
||||
</FormSlot>
|
||||
<MkSelect v-if="withMuteType" v-model="muteTypeModel" :items="muteTypeDef">
|
||||
<template #label>{{ i18n.ts.muteType }}</template>
|
||||
<template #caption>{{ i18n.ts.muteTypeDescription }}</template>
|
||||
</MkSelect>
|
||||
</div>
|
||||
<div :class="$style.buttons">
|
||||
<MkButton inline rounded @click="cancel">{{ i18n.ts.cancel }}</MkButton>
|
||||
<MkButton inline primary rounded :disabled="!canSave" @click="ok">{{ i18n.ts.ok }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</MkModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { i18n } from '@/i18n.js';
|
||||
import type { MkSelectItem, GetMkSelectValueTypesFromDef } from '@/components/MkSelect.vue';
|
||||
import MkInput from './MkInput.vue';
|
||||
|
||||
const periodItems = [{
|
||||
value: 'indefinitely', label: i18n.ts.indefinitely,
|
||||
}, {
|
||||
value: 'tenMinutes', label: i18n.ts.tenMinutes,
|
||||
}, {
|
||||
value: 'oneHour', label: i18n.ts.oneHour,
|
||||
}, {
|
||||
value: 'oneDay', label: i18n.ts.oneDay,
|
||||
}, {
|
||||
value: 'oneWeek', label: i18n.ts.oneWeek,
|
||||
}, {
|
||||
value: 'custom', label: i18n.ts.custom,
|
||||
}] as const satisfies MkSelectItem[];
|
||||
|
||||
const muteTypeItems = [{
|
||||
value: 'all', label: i18n.ts.all,
|
||||
}, {
|
||||
value: 'timelineOnly', label: i18n.ts.muteTypeTimeline,
|
||||
}] as const satisfies MkSelectItem[];
|
||||
|
||||
export type MkMuteSettingDialogDoneEvent = { canceled: true } | { canceled: false, expiresAt: number | null, type: GetMkSelectValueTypesFromDef<typeof muteTypeItems> };
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, onMounted, useTemplateRef, ref, computed } from 'vue';
|
||||
import FormSlot from '@/components/form/slot.vue';
|
||||
import MkModal from '@/components/MkModal.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
import { useMkSelect } from '@/composables/use-mkselect.js';
|
||||
|
||||
withDefaults(defineProps<{
|
||||
withMuteType?: boolean;
|
||||
}>(), {
|
||||
withMuteType: false,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'done', v: MkMuteSettingDialogDoneEvent): void;
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
const modal = useTemplateRef('modal');
|
||||
|
||||
const {
|
||||
def: periodDef,
|
||||
model: periodModel,
|
||||
} = useMkSelect({
|
||||
items: periodItems,
|
||||
initialValue: 'indefinitely',
|
||||
});
|
||||
|
||||
const now = Date.now();
|
||||
const manualExpiresAt = ref<string | null>(null);
|
||||
const canSave = computed(() => {
|
||||
if (periodModel.value === 'custom') {
|
||||
return manualExpiresAt.value != null && new Date(manualExpiresAt.value).getTime() > now;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
const {
|
||||
def: muteTypeDef,
|
||||
model: muteTypeModel,
|
||||
} = useMkSelect({
|
||||
items: muteTypeItems,
|
||||
initialValue: 'all',
|
||||
});
|
||||
|
||||
// overload function を使いたいので lint エラーを無視する
|
||||
function done(canceled: true): void;
|
||||
function done(canceled: false, period: typeof periodModel.value, type: typeof muteTypeModel.value): void; // eslint-disable-line no-redeclare
|
||||
|
||||
function done(canceled: boolean, period?: typeof periodModel.value, type?: typeof muteTypeModel.value) { // eslint-disable-line no-redeclare
|
||||
const expiresAt = (() => {
|
||||
if (canceled) return null;
|
||||
if (period === 'custom' && manualExpiresAt.value != null) {
|
||||
return new Date(manualExpiresAt.value!).getTime();
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
switch (period) {
|
||||
case 'indefinitely':
|
||||
return null;
|
||||
case 'tenMinutes':
|
||||
return now + 10 * 60 * 1000;
|
||||
case 'oneHour':
|
||||
return now + 60 * 60 * 1000;
|
||||
case 'oneDay':
|
||||
return now + 24 * 60 * 60 * 1000;
|
||||
case 'oneWeek':
|
||||
return now + 7 * 24 * 60 * 60 * 1000;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
|
||||
if (canceled) {
|
||||
emit('done', { canceled: true });
|
||||
} else {
|
||||
emit('done', { canceled: false, expiresAt, type: type! });
|
||||
}
|
||||
|
||||
modal.value?.close();
|
||||
}
|
||||
|
||||
async function ok() {
|
||||
done(false, periodModel.value, muteTypeModel.value);
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
done(true);
|
||||
}
|
||||
|
||||
/*
|
||||
function onBgClick() {
|
||||
if (props.cancelableByBgClick) cancel();
|
||||
}
|
||||
*/
|
||||
function onKeydown(evt: KeyboardEvent) {
|
||||
if (evt.key === 'Escape') cancel();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.document.addEventListener('keydown', onKeydown);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.document.removeEventListener('keydown', onKeydown);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
padding: 32px;
|
||||
width: 100%;
|
||||
min-width: 320px;
|
||||
max-width: 480px;
|
||||
box-sizing: border-box;
|
||||
background: var(--MI_THEME-panel);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75em;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 18px;
|
||||
color: var(--MI_THEME-warn);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: right;
|
||||
}
|
||||
</style>
|
||||
@@ -1,187 +0,0 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<MkModal ref="modal" :preferType="'dialog'" :zPriority="'high'" @click="done(true)" @closed="emit('closed')">
|
||||
<div :class="$style.root">
|
||||
<div v-if="title" class="_selectable" :class="$style.header">
|
||||
<Mfm :text="title"/>
|
||||
</div>
|
||||
<div v-if="text" :class="$style.text" class="_selectable">
|
||||
<Mfm :text="text"/>
|
||||
</div>
|
||||
<div class="_gaps_s">
|
||||
<MkSelect v-model="periodModel" :items="periodDef"></MkSelect>
|
||||
<MkInput
|
||||
v-if="periodModel === 'custom'"
|
||||
v-model="manualExpiresAt"
|
||||
type="datetime-local"
|
||||
></MkInput>
|
||||
</div>
|
||||
<div :class="$style.buttons">
|
||||
<MkButton inline rounded @click="cancel">{{ i18n.ts.cancel }}</MkButton>
|
||||
<MkButton inline primary rounded :disabled="!canSave" @click="ok">{{ i18n.ts.ok }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</MkModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { i18n } from '@/i18n.js';
|
||||
import type { MkSelectItem } from '@/components/MkSelect.vue';
|
||||
import MkInput from './MkInput.vue';
|
||||
|
||||
const periodItems = [{
|
||||
value: 'indefinitely', label: i18n.ts.indefinitely,
|
||||
}, {
|
||||
value: 'tenMinutes', label: i18n.ts.tenMinutes,
|
||||
}, {
|
||||
value: 'oneHour', label: i18n.ts.oneHour,
|
||||
}, {
|
||||
value: 'oneDay', label: i18n.ts.oneDay,
|
||||
}, {
|
||||
value: 'oneWeek', label: i18n.ts.oneWeek,
|
||||
}, {
|
||||
value: 'custom', label: i18n.ts.custom,
|
||||
}] as const satisfies MkSelectItem[];
|
||||
|
||||
export type MkPeriodDialogDoneEvent = { canceled: true } | { canceled: false, expiresAt: number | null };
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, onMounted, useTemplateRef, ref, computed } from 'vue';
|
||||
import MkModal from '@/components/MkModal.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
import { useMkSelect } from '@/composables/use-mkselect.js';
|
||||
|
||||
defineProps<{
|
||||
title?: string;
|
||||
text?: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'done', v: MkPeriodDialogDoneEvent): void;
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
const modal = useTemplateRef('modal');
|
||||
|
||||
const {
|
||||
def: periodDef,
|
||||
model: periodModel,
|
||||
} = useMkSelect({
|
||||
items: periodItems,
|
||||
initialValue: 'indefinitely',
|
||||
});
|
||||
|
||||
const now = Date.now();
|
||||
const manualExpiresAt = ref<string | null>(null);
|
||||
const canSave = computed(() => {
|
||||
if (periodModel.value === 'custom') {
|
||||
return manualExpiresAt.value != null && new Date(manualExpiresAt.value).getTime() > now;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// overload function を使いたいので lint エラーを無視する
|
||||
function done(canceled: true): void;
|
||||
function done(canceled: false, period: typeof periodModel.value): void; // eslint-disable-line no-redeclare
|
||||
|
||||
function done(canceled: boolean, period?: typeof periodModel.value) { // eslint-disable-line no-redeclare
|
||||
const expiresAt = (() => {
|
||||
if (canceled) return null;
|
||||
if (period === 'custom' && manualExpiresAt.value != null) {
|
||||
return new Date(manualExpiresAt.value!).getTime();
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
switch (period) {
|
||||
case 'indefinitely':
|
||||
return null;
|
||||
case 'tenMinutes':
|
||||
return now + 10 * 60 * 1000;
|
||||
case 'oneHour':
|
||||
return now + 60 * 60 * 1000;
|
||||
case 'oneDay':
|
||||
return now + 24 * 60 * 60 * 1000;
|
||||
case 'oneWeek':
|
||||
return now + 7 * 24 * 60 * 60 * 1000;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
|
||||
if (canceled) {
|
||||
emit('done', { canceled: true });
|
||||
} else {
|
||||
emit('done', { canceled: false, expiresAt });
|
||||
}
|
||||
|
||||
modal.value?.close();
|
||||
}
|
||||
|
||||
async function ok() {
|
||||
done(false, periodModel.value);
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
done(true);
|
||||
}
|
||||
|
||||
/*
|
||||
function onBgClick() {
|
||||
if (props.cancelableByBgClick) cancel();
|
||||
}
|
||||
*/
|
||||
function onKeydown(evt: KeyboardEvent) {
|
||||
if (evt.key === 'Escape') cancel();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.document.addEventListener('keydown', onKeydown);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.document.removeEventListener('keydown', onKeydown);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
padding: 32px;
|
||||
min-width: 320px;
|
||||
max-width: 480px;
|
||||
box-sizing: border-box;
|
||||
background: var(--MI_THEME-panel);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin: 0 0 8px 0;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
|
||||
& + .text {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
margin: 16px 0 0 0;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
@@ -445,7 +445,7 @@ function checkMissingMention() {
|
||||
const ast = mfm.parse(text.value);
|
||||
|
||||
for (const x of extractMentions(ast)) {
|
||||
if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) {
|
||||
if (!visibleUsers.value.some(u => (u.username === x.username) && ((u.host === x.host) || (x.host === host && u.host == null)))) {
|
||||
hasNotSpecifiedMentions.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import type { Form, GetFormResultType } from '@/utility/form.js';
|
||||
import type { MenuItem } from '@/types/menu.js';
|
||||
import type { PostFormProps } from '@/types/post-form.js';
|
||||
import type { UploaderFeatures } from '@/composables/use-uploader.js';
|
||||
import type { MkPeriodDialogDoneEvent } from '@/components/MkPeriodDialog.vue';
|
||||
import type { MkSelectItem } from '@/components/MkSelect.vue';
|
||||
import type { OptionValue } from '@/types/option-value.js';
|
||||
import type { MkDialogReturnType } from '@/components/MkDialog.vue';
|
||||
@@ -494,19 +493,6 @@ export function select<C extends OptionValue, D extends C | null = null>(props:
|
||||
});
|
||||
}
|
||||
|
||||
export function selectPeriod(options: { title?: string } = {}): Promise<MkPeriodDialogDoneEvent> {
|
||||
return new Promise((resolve) => {
|
||||
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkPeriodDialog.vue')), {
|
||||
title: options.title,
|
||||
}, {
|
||||
done: result => {
|
||||
resolve(result ? result : { canceled: true });
|
||||
},
|
||||
closed: () => dispose(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function success(): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
const showing = ref(true);
|
||||
|
||||
@@ -462,13 +462,31 @@ async function assignRole() {
|
||||
});
|
||||
if (canceled || roleId == null) return;
|
||||
|
||||
const res = await os.selectPeriod({
|
||||
title: `${i18n.ts.period}: ${roles.find(r => r.id === roleId)!.name}`,
|
||||
const { canceled: canceled2, result: period } = await os.select({
|
||||
title: i18n.ts.period + ': ' + roles.find(r => r.id === roleId)!.name,
|
||||
items: [{
|
||||
value: 'indefinitely', label: i18n.ts.indefinitely,
|
||||
}, {
|
||||
value: 'oneHour', label: i18n.ts.oneHour,
|
||||
}, {
|
||||
value: 'oneDay', label: i18n.ts.oneDay,
|
||||
}, {
|
||||
value: 'oneWeek', label: i18n.ts.oneWeek,
|
||||
}, {
|
||||
value: 'oneMonth', label: i18n.ts.oneMonth,
|
||||
}],
|
||||
default: 'indefinitely',
|
||||
});
|
||||
if (canceled2) return;
|
||||
|
||||
if (res.canceled) return;
|
||||
const expiresAt = period === 'indefinitely' ? null
|
||||
: period === 'oneHour' ? Date.now() + (1000 * 60 * 60)
|
||||
: period === 'oneDay' ? Date.now() + (1000 * 60 * 60 * 24)
|
||||
: period === 'oneWeek' ? Date.now() + (1000 * 60 * 60 * 24 * 7)
|
||||
: period === 'oneMonth' ? Date.now() + (1000 * 60 * 60 * 24 * 30)
|
||||
: null;
|
||||
|
||||
await os.apiWithDialog('admin/roles/assign', { roleId, userId: user.value.id, expiresAt: res.expiresAt });
|
||||
await os.apiWithDialog('admin/roles/assign', { roleId, userId: user.value.id, expiresAt });
|
||||
refreshUser();
|
||||
}
|
||||
|
||||
|
||||
@@ -113,12 +113,31 @@ async function del() {
|
||||
async function assign() {
|
||||
const user = await os.selectUser({ includeSelf: true });
|
||||
|
||||
const res = await os.selectPeriod({
|
||||
title: `${i18n.ts.period}: ${role.name}`,
|
||||
const { canceled: canceled2, result: period } = await os.select({
|
||||
title: i18n.ts.period + ': ' + role.name,
|
||||
items: [{
|
||||
value: 'indefinitely', label: i18n.ts.indefinitely,
|
||||
}, {
|
||||
value: 'oneHour', label: i18n.ts.oneHour,
|
||||
}, {
|
||||
value: 'oneDay', label: i18n.ts.oneDay,
|
||||
}, {
|
||||
value: 'oneWeek', label: i18n.ts.oneWeek,
|
||||
}, {
|
||||
value: 'oneMonth', label: i18n.ts.oneMonth,
|
||||
}],
|
||||
default: 'indefinitely',
|
||||
});
|
||||
if (res.canceled) return;
|
||||
if (canceled2) return;
|
||||
|
||||
await os.apiWithDialog('admin/roles/assign', { roleId: role.id, userId: user.id, expiresAt: res.expiresAt });
|
||||
const expiresAt = period === 'indefinitely' ? null
|
||||
: period === 'oneHour' ? Date.now() + (1000 * 60 * 60)
|
||||
: period === 'oneDay' ? Date.now() + (1000 * 60 * 60 * 24)
|
||||
: period === 'oneWeek' ? Date.now() + (1000 * 60 * 60 * 24 * 7)
|
||||
: period === 'oneMonth' ? Date.now() + (1000 * 60 * 60 * 24 * 30)
|
||||
: null;
|
||||
|
||||
await os.apiWithDialog('admin/roles/assign', { roleId: role.id, userId: user.id, expiresAt });
|
||||
//role.users.push(user);
|
||||
usersPaginator.reload();
|
||||
}
|
||||
|
||||
@@ -81,7 +81,6 @@ import MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue'
|
||||
import XChannelFollowButton from '@/components/MkChannelFollowButton.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { openMuteSettingDialog } from '@/utility/mute-confirm.js';
|
||||
import { $i, iAmModerator } from '@/i.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
@@ -196,14 +195,33 @@ async function mute() {
|
||||
if (!channel.value) return;
|
||||
const _channel = channel.value;
|
||||
|
||||
const res = await openMuteSettingDialog({
|
||||
withMuteType: false,
|
||||
const { canceled, result: period } = await os.select({
|
||||
title: i18n.ts.mutePeriod,
|
||||
items: [{
|
||||
value: 'indefinitely', label: i18n.ts.indefinitely,
|
||||
}, {
|
||||
value: 'tenMinutes', label: i18n.ts.tenMinutes,
|
||||
}, {
|
||||
value: 'oneHour', label: i18n.ts.oneHour,
|
||||
}, {
|
||||
value: 'oneDay', label: i18n.ts.oneDay,
|
||||
}, {
|
||||
value: 'oneWeek', label: i18n.ts.oneWeek,
|
||||
}],
|
||||
default: 'indefinitely',
|
||||
});
|
||||
if (res.canceled) return;
|
||||
if (canceled) return;
|
||||
|
||||
const expiresAt = period === 'indefinitely' ? null
|
||||
: period === 'tenMinutes' ? Date.now() + (1000 * 60 * 10)
|
||||
: period === 'oneHour' ? Date.now() + (1000 * 60 * 60)
|
||||
: period === 'oneDay' ? Date.now() + (1000 * 60 * 60 * 24)
|
||||
: period === 'oneWeek' ? Date.now() + (1000 * 60 * 60 * 24 * 7)
|
||||
: null;
|
||||
|
||||
os.apiWithDialog('channels/mute/create', {
|
||||
channelId: _channel.id,
|
||||
expiresAt: res.expiresAt,
|
||||
expiresAt,
|
||||
}).then(() => {
|
||||
_channel.isMuting = true;
|
||||
});
|
||||
|
||||
@@ -123,6 +123,7 @@ function postThis() {
|
||||
|
||||
os.post({
|
||||
initialFiles: [file.value],
|
||||
instant: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -126,6 +126,7 @@ function shareWithNote() {
|
||||
if (!post.value) return;
|
||||
os.post({
|
||||
initialText: `${post.value.title} ${url}/gallery/${post.value.id}`,
|
||||
instant: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +131,7 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss
|
||||
icon: 'ti ti-pencil',
|
||||
action: () => os.post({
|
||||
initialFiles: [file],
|
||||
instant: true,
|
||||
}),
|
||||
}, {
|
||||
text: i18n.ts.copyUrl,
|
||||
|
||||
@@ -18,7 +18,6 @@ import { notesSearchAvailable, canSearchNonLocalNotes } from '@/utility/check-pe
|
||||
import { antennasCache, rolesCache, userListsCache } from '@/cache.js';
|
||||
import { mainRouter } from '@/router.js';
|
||||
import { genEmbedCode } from '@/utility/get-embed-code.js';
|
||||
import { openMuteSettingDialog } from '@/utility/mute-confirm.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { getPluginHandlers } from '@/plugin.js';
|
||||
|
||||
@@ -35,15 +34,33 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
|
||||
user.isMuted = false;
|
||||
});
|
||||
} else {
|
||||
const res = await openMuteSettingDialog({
|
||||
withMuteType: true,
|
||||
const { canceled, result: period } = await os.select({
|
||||
title: i18n.ts.mutePeriod,
|
||||
items: [{
|
||||
value: 'indefinitely', label: i18n.ts.indefinitely,
|
||||
}, {
|
||||
value: 'tenMinutes', label: i18n.ts.tenMinutes,
|
||||
}, {
|
||||
value: 'oneHour', label: i18n.ts.oneHour,
|
||||
}, {
|
||||
value: 'oneDay', label: i18n.ts.oneDay,
|
||||
}, {
|
||||
value: 'oneWeek', label: i18n.ts.oneWeek,
|
||||
}],
|
||||
default: 'indefinitely',
|
||||
});
|
||||
if (res.canceled) return;
|
||||
if (canceled) return;
|
||||
|
||||
const expiresAt = period === 'indefinitely' ? null
|
||||
: period === 'tenMinutes' ? Date.now() + (1000 * 60 * 10)
|
||||
: period === 'oneHour' ? Date.now() + (1000 * 60 * 60)
|
||||
: period === 'oneDay' ? Date.now() + (1000 * 60 * 60 * 24)
|
||||
: period === 'oneWeek' ? Date.now() + (1000 * 60 * 60 * 24 * 7)
|
||||
: null;
|
||||
|
||||
os.apiWithDialog('mute/create', {
|
||||
userId: user.id,
|
||||
expiresAt: res.expiresAt,
|
||||
mutingType: res.type,
|
||||
expiresAt,
|
||||
}).then(() => {
|
||||
user.isMuted = true;
|
||||
});
|
||||
@@ -307,13 +324,31 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
|
||||
return roles.filter(r => r.target === 'manual').map(r => ({
|
||||
text: r.name,
|
||||
action: async () => {
|
||||
const res = await os.selectPeriod({
|
||||
title: `${i18n.ts.period}: ${r.name}`,
|
||||
const { canceled, result: period } = await os.select({
|
||||
title: i18n.ts.period + ': ' + r.name,
|
||||
items: [{
|
||||
value: 'indefinitely', label: i18n.ts.indefinitely,
|
||||
}, {
|
||||
value: 'oneHour', label: i18n.ts.oneHour,
|
||||
}, {
|
||||
value: 'oneDay', label: i18n.ts.oneDay,
|
||||
}, {
|
||||
value: 'oneWeek', label: i18n.ts.oneWeek,
|
||||
}, {
|
||||
value: 'oneMonth', label: i18n.ts.oneMonth,
|
||||
}],
|
||||
default: 'indefinitely',
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
if (res.canceled) return;
|
||||
const expiresAt = period === 'indefinitely' ? null
|
||||
: period === 'oneHour' ? Date.now() + (1000 * 60 * 60)
|
||||
: period === 'oneDay' ? Date.now() + (1000 * 60 * 60 * 24)
|
||||
: period === 'oneWeek' ? Date.now() + (1000 * 60 * 60 * 24 * 7)
|
||||
: period === 'oneMonth' ? Date.now() + (1000 * 60 * 60 * 24 * 30)
|
||||
: null;
|
||||
|
||||
os.apiWithDialog('admin/roles/assign', { roleId: r.id, userId: user.id, expiresAt: res.expiresAt });
|
||||
os.apiWithDialog('admin/roles/assign', { roleId: r.id, userId: user.id, expiresAt });
|
||||
},
|
||||
}));
|
||||
},
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
import * as os from '@/os.js';
|
||||
import type { MkMuteSettingDialogDoneEvent } from '@/components/MkMuteSettingDialog.vue';
|
||||
|
||||
export function openMuteSettingDialog(opts?: { withMuteType?: boolean }): Promise<MkMuteSettingDialogDoneEvent> {
|
||||
return new Promise(resolve => {
|
||||
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkMuteSettingDialog.vue')), opts ?? {}, {
|
||||
done: result => {
|
||||
resolve(result ? result : { canceled: true });
|
||||
},
|
||||
closed: () => {
|
||||
dispose();
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
</MkUserCardMini>
|
||||
</MkA>
|
||||
<button v-tooltip.noDelay="i18n.ts.note" class="_button" :class="$style.post" @click="os.post({initialText: `@${item.user.username}${item.user.host ? `@${item.user.host}` : ''} `})">
|
||||
<button v-tooltip.noDelay="i18n.ts.note" class="_button" :class="$style.post" @click="os.post({initialText: `@${item.user.username}${item.user.host ? `@${item.user.host}` : ''} `, instant: true})">
|
||||
<i class="ti-fw ti ti-confetti" :class="$style.postIcon"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -100,7 +100,7 @@ async function buildSrc(): Promise<void> {
|
||||
|
||||
function buildDts(): Promise<unknown> {
|
||||
return execa(
|
||||
'tsgo',
|
||||
'tsc',
|
||||
[
|
||||
'--project', 'tsconfig.json',
|
||||
'--rootDir', 'src',
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"build": "tsx ./build.ts",
|
||||
"watch": "nodemon -w package.json -e json --exec \"tsx ./build.ts --watch\"",
|
||||
"tsd": "tsd",
|
||||
"typecheck": "tsgo --noEmit",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "pnpm typecheck && pnpm eslint",
|
||||
"lint:fix": "pnpm eslint --fix"
|
||||
},
|
||||
@@ -28,16 +28,16 @@
|
||||
"built"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/node": "26.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.62.0",
|
||||
"@types/node": "26.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.63.0",
|
||||
"@typescript-eslint/parser": "8.63.0",
|
||||
"chokidar": "5.0.0",
|
||||
"esbuild": "0.28.1",
|
||||
"execa": "9.6.1",
|
||||
"nodemon": "3.1.14",
|
||||
"tsx": "4.22.4"
|
||||
"tsx": "4.23.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"js-yaml": "5.2.0"
|
||||
"js-yaml": "5.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,18 +596,6 @@ export interface Locale extends ILocale {
|
||||
* ミュート
|
||||
*/
|
||||
"mute": string;
|
||||
/**
|
||||
* ミュートする範囲
|
||||
*/
|
||||
"muteType": string;
|
||||
/**
|
||||
* ミュートを適用する範囲を設定できます。「タイムラインのみ」に設定すると、タイムラインや検索結果上からは見えなくなりますが、通知は受け取ります。
|
||||
*/
|
||||
"muteTypeDescription": string;
|
||||
/**
|
||||
* タイムラインのみ
|
||||
*/
|
||||
"muteTypeTimeline": string;
|
||||
/**
|
||||
* ミュート解除
|
||||
*/
|
||||
@@ -3868,10 +3856,6 @@ export interface Locale extends ILocale {
|
||||
* ミュートする期限
|
||||
*/
|
||||
"mutePeriod": string;
|
||||
/**
|
||||
* 期限はあくまで目安です。反映が数分遅れる場合があります。
|
||||
*/
|
||||
"mutePeriodDescription": string;
|
||||
/**
|
||||
* 期限
|
||||
*/
|
||||
@@ -5667,10 +5651,6 @@ export interface Locale extends ILocale {
|
||||
* ゼロ埋め
|
||||
*/
|
||||
"zeroPadding": string;
|
||||
/**
|
||||
* ミュートしますか?
|
||||
*/
|
||||
"muteConfirm": string;
|
||||
/**
|
||||
* 設定項目はありません
|
||||
*/
|
||||
|
||||
@@ -7,19 +7,19 @@
|
||||
"scripts": {
|
||||
"build": "tsx src/generator.ts",
|
||||
"eslint": "eslint src/**/*.ts",
|
||||
"typecheck": "tsgo --noEmit",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "pnpm typecheck && pnpm eslint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "26.0.1",
|
||||
"@types/node": "26.1.1",
|
||||
"@types/wawoff2": "1.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.62.0"
|
||||
"@typescript-eslint/eslint-plugin": "8.63.0",
|
||||
"@typescript-eslint/parser": "8.63.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tabler/icons-webfont": "3.35.0",
|
||||
"harfbuzzjs": "1.4.0",
|
||||
"tsx": "4.22.4",
|
||||
"tsx": "4.23.0",
|
||||
"wawoff2": "2.0.1"
|
||||
},
|
||||
"files": [
|
||||
|
||||
@@ -58,7 +58,7 @@ async function buildSrc() {
|
||||
|
||||
function buildDts() {
|
||||
return execa(
|
||||
'tsgo',
|
||||
'tsc',
|
||||
[
|
||||
'--project', 'tsconfig.json',
|
||||
'--outDir', 'built',
|
||||
|
||||
@@ -20,15 +20,15 @@
|
||||
"build": "node ./build.js",
|
||||
"watch": "nodemon -w package.json -e json --exec \"node ./build.js --watch\"",
|
||||
"eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
|
||||
"typecheck": "tsgo --noEmit",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "pnpm typecheck && pnpm eslint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/matter-js": "0.20.2",
|
||||
"@types/node": "26.0.1",
|
||||
"@types/node": "26.1.1",
|
||||
"@types/seedrandom": "3.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.62.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.63.0",
|
||||
"@typescript-eslint/parser": "8.63.0",
|
||||
"esbuild": "0.28.1",
|
||||
"execa": "9.6.1",
|
||||
"nodemon": "3.1.14"
|
||||
|
||||
@@ -59,7 +59,7 @@ async function buildSrc() {
|
||||
|
||||
function buildDts() {
|
||||
return execa(
|
||||
'tsgo',
|
||||
'tsc',
|
||||
[
|
||||
'--project', 'tsconfig.json',
|
||||
'--outDir', 'built',
|
||||
|
||||
@@ -7,15 +7,15 @@
|
||||
"generate": "tsx src/generator.ts && eslint ./built/**/*.ts --fix"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@readme/openapi-parser": "6.1.3",
|
||||
"@types/node": "26.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.62.0",
|
||||
"@readme/openapi-parser": "6.2.1",
|
||||
"@types/node": "26.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.63.0",
|
||||
"@typescript-eslint/parser": "8.63.0",
|
||||
"openapi-types": "12.1.3",
|
||||
"openapi-typescript": "7.13.0",
|
||||
"ts-case-convert": "2.3.1",
|
||||
"tsx": "4.22.4",
|
||||
"eslint": "9.39.4"
|
||||
"tsx": "4.23.0",
|
||||
"eslint": "9.39.5"
|
||||
},
|
||||
"files": [
|
||||
"built"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "misskey-js",
|
||||
"version": "2026.7.0-alpha.6",
|
||||
"version": "2026.7.0-beta.1",
|
||||
"description": "Misskey SDK for JavaScript",
|
||||
"license": "MIT",
|
||||
"main": "./built/index.js",
|
||||
@@ -25,7 +25,7 @@
|
||||
"api": "pnpm api-extractor run --local --verbose",
|
||||
"api-prod": "pnpm api-extractor run --verbose",
|
||||
"eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
|
||||
"typecheck": "tsgo --noEmit",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "pnpm typecheck && pnpm eslint",
|
||||
"vitest": "vitest run --coverage",
|
||||
"test": "pnpm vitest && pnpm tsd",
|
||||
@@ -38,17 +38,17 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/api-extractor": "7.58.9",
|
||||
"@types/node": "26.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.62.0",
|
||||
"@vitest/coverage-v8": "4.1.9",
|
||||
"@types/node": "26.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.63.0",
|
||||
"@typescript-eslint/parser": "8.63.0",
|
||||
"@vitest/coverage-v8": "4.1.10",
|
||||
"esbuild": "0.28.1",
|
||||
"execa": "9.6.1",
|
||||
"ncp": "2.0.0",
|
||||
"nodemon": "3.1.14",
|
||||
"tsd": "0.33.0",
|
||||
"vitest": "4.1.9",
|
||||
"vitest-websocket-mock": "0.5.0"
|
||||
"vitest": "4.1.10",
|
||||
"vitest-websocket-mock": "0.7.0"
|
||||
},
|
||||
"files": [
|
||||
"built"
|
||||
|
||||
@@ -4905,8 +4905,6 @@ export type components = {
|
||||
createdAt: string;
|
||||
/** Format: date-time */
|
||||
expiresAt: string | null;
|
||||
/** @enum {string} */
|
||||
mutingType: 'all' | 'timelineOnly';
|
||||
/** Format: id */
|
||||
muteeId: string;
|
||||
mutee: components['schemas']['UserDetailedNotMe'];
|
||||
@@ -29011,12 +29009,6 @@ export interface operations {
|
||||
userId: string;
|
||||
/** @description A Unix Epoch timestamp that must lie in the future. `null` means an indefinite mute. */
|
||||
expiresAt?: number | null;
|
||||
/**
|
||||
* @description Type of muting. `all` mutes everything including notifications. `timelineOnly` mutes only timeline and search, but allows notifications.
|
||||
* @default all
|
||||
* @enum {string}
|
||||
*/
|
||||
mutingType?: 'all' | 'timelineOnly';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -58,7 +58,7 @@ async function buildSrc() {
|
||||
|
||||
function buildDts() {
|
||||
return execa(
|
||||
'tsgo',
|
||||
'tsc',
|
||||
[
|
||||
'--project', 'tsconfig.json',
|
||||
'--outDir', 'built',
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
"build": "node ./build.js",
|
||||
"watch": "nodemon -w package.json -e json --exec \"node ./build.js --watch\"",
|
||||
"eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
|
||||
"typecheck": "tsgo --noEmit",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "pnpm typecheck && pnpm eslint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "26.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.62.0",
|
||||
"@types/node": "26.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.63.0",
|
||||
"@typescript-eslint/parser": "8.63.0",
|
||||
"esbuild": "0.28.1",
|
||||
"execa": "9.6.1",
|
||||
"nodemon": "3.1.14"
|
||||
|
||||
@@ -4,18 +4,18 @@
|
||||
"scripts": {
|
||||
"watch": "nodemon -w ../../package.json -e json --exec \"node build.js watch\"",
|
||||
"build": "node build.js",
|
||||
"typecheck": "tsgo --noEmit",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"eslint": "eslint --quiet src/**/*.ts",
|
||||
"lint": "pnpm typecheck && pnpm eslint"
|
||||
},
|
||||
"dependencies": {
|
||||
"i18n": "workspace:*",
|
||||
"esbuild": "0.28.1",
|
||||
"idb-keyval": "6.2.5",
|
||||
"idb-keyval": "6.3.0",
|
||||
"misskey-js": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/parser": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.63.0",
|
||||
"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.74",
|
||||
"eslint-plugin-import": "2.32.0",
|
||||
"nodemon": "3.1.14"
|
||||
|
||||
Generated
+2054
-1863
File diff suppressed because it is too large
Load Diff
+511
-150
@@ -9,16 +9,16 @@
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@types/mdast": "4.0.4",
|
||||
"@types/node": "26.0.1",
|
||||
"@vitest/coverage-v8": "4.1.9",
|
||||
"@types/node": "26.1.1",
|
||||
"@vitest/coverage-v8": "4.1.10",
|
||||
"mdast-util-to-string": "4.0.0",
|
||||
"remark": "15.0.1",
|
||||
"remark-parse": "11.0.0",
|
||||
"typescript": "5.9.3",
|
||||
"typescript": "7.0.2",
|
||||
"unified": "11.0.5",
|
||||
"vite": "8.1.0",
|
||||
"vite": "8.1.4",
|
||||
"vite-node": "6.0.0",
|
||||
"vitest": "4.1.9"
|
||||
"vitest": "4.1.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
@@ -163,9 +163,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@oxc-project/types": {
|
||||
"version": "0.137.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz",
|
||||
"integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==",
|
||||
"version": "0.139.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz",
|
||||
"integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@@ -173,9 +173,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-android-arm64": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz",
|
||||
"integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz",
|
||||
"integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -190,9 +190,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz",
|
||||
"integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz",
|
||||
"integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -207,9 +207,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-x64": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz",
|
||||
"integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz",
|
||||
"integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -224,9 +224,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz",
|
||||
"integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz",
|
||||
"integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -241,9 +241,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz",
|
||||
"integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz",
|
||||
"integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -258,9 +258,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz",
|
||||
"integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz",
|
||||
"integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -275,9 +275,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz",
|
||||
"integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz",
|
||||
"integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -292,9 +292,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz",
|
||||
"integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz",
|
||||
"integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -309,9 +309,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz",
|
||||
"integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz",
|
||||
"integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -326,9 +326,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz",
|
||||
"integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz",
|
||||
"integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -343,9 +343,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz",
|
||||
"integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz",
|
||||
"integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -360,9 +360,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz",
|
||||
"integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz",
|
||||
"integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -377,9 +377,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz",
|
||||
"integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz",
|
||||
"integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
@@ -396,9 +396,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz",
|
||||
"integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz",
|
||||
"integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -413,9 +413,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz",
|
||||
"integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz",
|
||||
"integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -505,9 +505,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "26.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.1.tgz",
|
||||
"integrity": "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==",
|
||||
"version": "26.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz",
|
||||
"integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -520,15 +520,355 @@
|
||||
"integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript/typescript-aix-ppc64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz",
|
||||
"integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-darwin-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-darwin-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-freebsd-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-freebsd-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-arm": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz",
|
||||
"integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-loong64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz",
|
||||
"integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-mips64el": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz",
|
||||
"integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-ppc64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz",
|
||||
"integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-riscv64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz",
|
||||
"integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-s390x": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz",
|
||||
"integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-netbsd-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-netbsd-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-openbsd-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-openbsd-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-sunos-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-win32-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-win32-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/coverage-v8": {
|
||||
"version": "4.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.9.tgz",
|
||||
"integrity": "sha512-G9/lgqibheLVBDRuya45EbsEXTYcWoSG+TLg7i2axuzx0Eq62eXn+aWXyaVdV5vKvFSWd6ywcX8hA7la9Pvu8g==",
|
||||
"version": "4.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz",
|
||||
"integrity": "sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@bcoe/v8-coverage": "^1.0.2",
|
||||
"@vitest/utils": "4.1.9",
|
||||
"@vitest/utils": "4.1.10",
|
||||
"ast-v8-to-istanbul": "^1.0.0",
|
||||
"istanbul-lib-coverage": "^3.2.2",
|
||||
"istanbul-lib-report": "^3.0.1",
|
||||
@@ -542,8 +882,8 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vitest/browser": "4.1.9",
|
||||
"vitest": "4.1.9"
|
||||
"@vitest/browser": "4.1.10",
|
||||
"vitest": "4.1.10"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vitest/browser": {
|
||||
@@ -552,16 +892,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "4.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz",
|
||||
"integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==",
|
||||
"version": "4.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz",
|
||||
"integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@standard-schema/spec": "^1.1.0",
|
||||
"@types/chai": "^5.2.2",
|
||||
"@vitest/spy": "4.1.9",
|
||||
"@vitest/utils": "4.1.9",
|
||||
"@vitest/spy": "4.1.10",
|
||||
"@vitest/utils": "4.1.10",
|
||||
"chai": "^6.2.2",
|
||||
"tinyrainbow": "^3.1.0"
|
||||
},
|
||||
@@ -570,13 +910,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "4.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz",
|
||||
"integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==",
|
||||
"version": "4.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz",
|
||||
"integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "4.1.9",
|
||||
"@vitest/spy": "4.1.10",
|
||||
"estree-walker": "^3.0.3",
|
||||
"magic-string": "^0.30.21"
|
||||
},
|
||||
@@ -597,9 +937,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/pretty-format": {
|
||||
"version": "4.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz",
|
||||
"integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==",
|
||||
"version": "4.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz",
|
||||
"integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -610,13 +950,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "4.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz",
|
||||
"integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==",
|
||||
"version": "4.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz",
|
||||
"integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "4.1.9",
|
||||
"@vitest/utils": "4.1.10",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
"funding": {
|
||||
@@ -624,14 +964,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "4.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz",
|
||||
"integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==",
|
||||
"version": "4.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz",
|
||||
"integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "4.1.9",
|
||||
"@vitest/utils": "4.1.9",
|
||||
"@vitest/pretty-format": "4.1.10",
|
||||
"@vitest/utils": "4.1.10",
|
||||
"magic-string": "^0.30.21",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
@@ -640,9 +980,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "4.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz",
|
||||
"integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==",
|
||||
"version": "4.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz",
|
||||
"integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@@ -650,13 +990,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "4.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz",
|
||||
"integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==",
|
||||
"version": "4.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz",
|
||||
"integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "4.1.9",
|
||||
"@vitest/pretty-format": "4.1.10",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"tinyrainbow": "^3.1.0"
|
||||
},
|
||||
@@ -1774,9 +2114,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.12",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
|
||||
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
|
||||
"version": "3.3.16",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
|
||||
"integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -1818,9 +2158,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
|
||||
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -1831,9 +2171,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.15",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
||||
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
|
||||
"version": "8.5.20",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.20.tgz",
|
||||
"integrity": "sha512-lW616l85ucIQL+FocMmL7pQFPqBmwejrCMg+iPxyImlrANNJG9NHq/RkyCZopDhd8C3LA03PHRJDjkbGu8vvug==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -1851,7 +2191,7 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.12",
|
||||
"nanoid": "^3.3.16",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
@@ -1907,13 +2247,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rolldown": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz",
|
||||
"integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz",
|
||||
"integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@oxc-project/types": "=0.137.0",
|
||||
"@oxc-project/types": "=0.139.0",
|
||||
"@rolldown/pluginutils": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -1923,21 +2263,21 @@
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rolldown/binding-android-arm64": "1.1.3",
|
||||
"@rolldown/binding-darwin-arm64": "1.1.3",
|
||||
"@rolldown/binding-darwin-x64": "1.1.3",
|
||||
"@rolldown/binding-freebsd-x64": "1.1.3",
|
||||
"@rolldown/binding-linux-arm-gnueabihf": "1.1.3",
|
||||
"@rolldown/binding-linux-arm64-gnu": "1.1.3",
|
||||
"@rolldown/binding-linux-arm64-musl": "1.1.3",
|
||||
"@rolldown/binding-linux-ppc64-gnu": "1.1.3",
|
||||
"@rolldown/binding-linux-s390x-gnu": "1.1.3",
|
||||
"@rolldown/binding-linux-x64-gnu": "1.1.3",
|
||||
"@rolldown/binding-linux-x64-musl": "1.1.3",
|
||||
"@rolldown/binding-openharmony-arm64": "1.1.3",
|
||||
"@rolldown/binding-wasm32-wasi": "1.1.3",
|
||||
"@rolldown/binding-win32-arm64-msvc": "1.1.3",
|
||||
"@rolldown/binding-win32-x64-msvc": "1.1.3"
|
||||
"@rolldown/binding-android-arm64": "1.1.5",
|
||||
"@rolldown/binding-darwin-arm64": "1.1.5",
|
||||
"@rolldown/binding-darwin-x64": "1.1.5",
|
||||
"@rolldown/binding-freebsd-x64": "1.1.5",
|
||||
"@rolldown/binding-linux-arm-gnueabihf": "1.1.5",
|
||||
"@rolldown/binding-linux-arm64-gnu": "1.1.5",
|
||||
"@rolldown/binding-linux-arm64-musl": "1.1.5",
|
||||
"@rolldown/binding-linux-ppc64-gnu": "1.1.5",
|
||||
"@rolldown/binding-linux-s390x-gnu": "1.1.5",
|
||||
"@rolldown/binding-linux-x64-gnu": "1.1.5",
|
||||
"@rolldown/binding-linux-x64-musl": "1.1.5",
|
||||
"@rolldown/binding-openharmony-arm64": "1.1.5",
|
||||
"@rolldown/binding-wasm32-wasi": "1.1.5",
|
||||
"@rolldown/binding-win32-arm64-msvc": "1.1.5",
|
||||
"@rolldown/binding-win32-x64-msvc": "1.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
@@ -1979,9 +2319,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/std-env": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz",
|
||||
"integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz",
|
||||
"integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -2060,17 +2400,38 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz",
|
||||
"integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
"tsc": "bin/tsc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
"node": ">=16.20.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@typescript/typescript-aix-ppc64": "7.0.2",
|
||||
"@typescript/typescript-darwin-arm64": "7.0.2",
|
||||
"@typescript/typescript-darwin-x64": "7.0.2",
|
||||
"@typescript/typescript-freebsd-arm64": "7.0.2",
|
||||
"@typescript/typescript-freebsd-x64": "7.0.2",
|
||||
"@typescript/typescript-linux-arm": "7.0.2",
|
||||
"@typescript/typescript-linux-arm64": "7.0.2",
|
||||
"@typescript/typescript-linux-loong64": "7.0.2",
|
||||
"@typescript/typescript-linux-mips64el": "7.0.2",
|
||||
"@typescript/typescript-linux-ppc64": "7.0.2",
|
||||
"@typescript/typescript-linux-riscv64": "7.0.2",
|
||||
"@typescript/typescript-linux-s390x": "7.0.2",
|
||||
"@typescript/typescript-linux-x64": "7.0.2",
|
||||
"@typescript/typescript-netbsd-arm64": "7.0.2",
|
||||
"@typescript/typescript-netbsd-x64": "7.0.2",
|
||||
"@typescript/typescript-openbsd-arm64": "7.0.2",
|
||||
"@typescript/typescript-openbsd-x64": "7.0.2",
|
||||
"@typescript/typescript-sunos-x64": "7.0.2",
|
||||
"@typescript/typescript-win32-arm64": "7.0.2",
|
||||
"@typescript/typescript-win32-x64": "7.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
@@ -2185,16 +2546,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.1.0.tgz",
|
||||
"integrity": "sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==",
|
||||
"version": "8.1.4",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.1.4.tgz",
|
||||
"integrity": "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lightningcss": "^1.32.0",
|
||||
"picomatch": "^4.0.4",
|
||||
"postcss": "^8.5.15",
|
||||
"rolldown": "~1.1.2",
|
||||
"picomatch": "^4.0.5",
|
||||
"postcss": "^8.5.16",
|
||||
"rolldown": "~1.1.4",
|
||||
"tinyglobby": "^0.2.17"
|
||||
},
|
||||
"bin": {
|
||||
@@ -2286,19 +2647,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "4.1.9",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz",
|
||||
"integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==",
|
||||
"version": "4.1.10",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz",
|
||||
"integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/expect": "4.1.9",
|
||||
"@vitest/mocker": "4.1.9",
|
||||
"@vitest/pretty-format": "4.1.9",
|
||||
"@vitest/runner": "4.1.9",
|
||||
"@vitest/snapshot": "4.1.9",
|
||||
"@vitest/spy": "4.1.9",
|
||||
"@vitest/utils": "4.1.9",
|
||||
"@vitest/expect": "4.1.10",
|
||||
"@vitest/mocker": "4.1.10",
|
||||
"@vitest/pretty-format": "4.1.10",
|
||||
"@vitest/runner": "4.1.10",
|
||||
"@vitest/snapshot": "4.1.10",
|
||||
"@vitest/spy": "4.1.10",
|
||||
"@vitest/utils": "4.1.10",
|
||||
"es-module-lexer": "^2.0.0",
|
||||
"expect-type": "^1.3.0",
|
||||
"magic-string": "^0.30.21",
|
||||
@@ -2326,12 +2687,12 @@
|
||||
"@edge-runtime/vm": "*",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
|
||||
"@vitest/browser-playwright": "4.1.9",
|
||||
"@vitest/browser-preview": "4.1.9",
|
||||
"@vitest/browser-webdriverio": "4.1.9",
|
||||
"@vitest/coverage-istanbul": "4.1.9",
|
||||
"@vitest/coverage-v8": "4.1.9",
|
||||
"@vitest/ui": "4.1.9",
|
||||
"@vitest/browser-playwright": "4.1.10",
|
||||
"@vitest/browser-preview": "4.1.10",
|
||||
"@vitest/browser-webdriverio": "4.1.10",
|
||||
"@vitest/coverage-istanbul": "4.1.10",
|
||||
"@vitest/coverage-v8": "4.1.10",
|
||||
"@vitest/ui": "4.1.10",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*",
|
||||
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mdast": "4.0.4",
|
||||
"@types/node": "26.0.1",
|
||||
"@vitest/coverage-v8": "4.1.9",
|
||||
"@types/node": "26.1.1",
|
||||
"@vitest/coverage-v8": "4.1.10",
|
||||
"mdast-util-to-string": "4.0.0",
|
||||
"remark": "15.0.1",
|
||||
"remark-parse": "11.0.0",
|
||||
"typescript": "5.9.3",
|
||||
"typescript": "7.0.2",
|
||||
"unified": "11.0.5",
|
||||
"vite": "8.1.0",
|
||||
"vite": "8.1.4",
|
||||
"vite-node": "6.0.0",
|
||||
"vitest": "4.1.9"
|
||||
"vitest": "4.1.10"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user