diff options
| author | misskey-release-bot[bot] <157398866+misskey-release-bot[bot]@users.noreply.github.com> | 2025-03-09 03:29:58 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-03-09 03:29:58 +0000 |
| commit | bef73ff530e84418c8257aff558b677a4aa71064 (patch) | |
| tree | e2a64f47e2c1c60b34fb240cb85aff308cc3bb59 | |
| parent | Merge pull request #15585 from misskey-dev/develop (diff) | |
| parent | Release: 2025.3.1 (diff) | |
| download | misskey-bef73ff530e84418c8257aff558b677a4aa71064.tar.gz misskey-bef73ff530e84418c8257aff558b677a4aa71064.tar.bz2 misskey-bef73ff530e84418c8257aff558b677a4aa71064.zip | |
Merge pull request #15615 from misskey-dev/develop
Release: 2025.3.1
75 files changed, 5839 insertions, 2652 deletions
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 8dd9d1c704..c506c36f6b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -7,8 +7,8 @@ "ghcr.io/devcontainers/features/node:1": { "version": "22.11.0" }, - "ghcr.io/devcontainers-extra/features/corepack:1": { - "version": "0.31.0" + "ghcr.io/devcontainers-extra/features/pnpm:2": { + "version": "10.6.1" } }, "forwardPorts": [3000], diff --git a/.devcontainer/init.sh b/.devcontainer/init.sh index e02a533c15..216292b082 100755 --- a/.devcontainer/init.sh +++ b/.devcontainer/init.sh @@ -7,8 +7,6 @@ sudo apt-get update sudo apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2 libxtst6 xauth xvfb git config --global --add safe.directory /workspace git submodule update --init -corepack install -corepack enable pnpm config set store-dir /home/node/.local/share/pnpm/store pnpm install --frozen-lockfile cp .devcontainer/devcontainer.yml .config/default.yml diff --git a/.github/workflows/api-misskey-js.yml b/.github/workflows/api-misskey-js.yml index fdd128be33..1c4bee2095 100644 --- a/.github/workflows/api-misskey-js.yml +++ b/.github/workflows/api-misskey-js.yml @@ -9,10 +9,6 @@ on: paths: - packages/misskey-js/** - .github/workflows/api-misskey-js.yml - -env: - COREPACK_DEFAULT_TO_LATEST: 0 - jobs: report: @@ -22,7 +18,8 @@ jobs: - name: Checkout uses: actions/checkout@v4.2.2 - - run: corepack enable + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 - name: Setup Node.js uses: actions/setup-node@v4.2.0 diff --git a/.github/workflows/get-api-diff.yml b/.github/workflows/get-api-diff.yml index 2da9647460..3244a39156 100644 --- a/.github/workflows/get-api-diff.yml +++ b/.github/workflows/get-api-diff.yml @@ -9,10 +9,6 @@ on: paths: - packages/backend/** - .github/workflows/get-api-diff.yml - -env: - COREPACK_DEFAULT_TO_LATEST: 0 - jobs: get-from-misskey: runs-on: ubuntu-latest @@ -34,14 +30,13 @@ jobs: with: ref: ${{ matrix.ref }} submodules: true - - name: Install pnpm - uses: pnpm/action-setup@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4.2.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' - - run: corepack enable - run: pnpm i --frozen-lockfile - name: Check pnpm-lock.yaml run: git diff --exit-code pnpm-lock.yaml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b1d52e8b3b..361bd697e5 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -28,10 +28,6 @@ on: - packages/misskey-reversi/** - packages/shared/eslint.config.js - .github/workflows/lint.yml - -env: - COREPACK_DEFAULT_TO_LATEST: 0 - jobs: pnpm_install: runs-on: ubuntu-latest @@ -40,12 +36,12 @@ jobs: with: fetch-depth: 0 submodules: true - - uses: pnpm/action-setup@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 - uses: actions/setup-node@v4.2.0 with: node-version-file: '.node-version' cache: 'pnpm' - - run: corepack enable - run: pnpm i --frozen-lockfile lint: @@ -71,12 +67,12 @@ jobs: with: fetch-depth: 0 submodules: true - - uses: pnpm/action-setup@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 - uses: actions/setup-node@v4.2.0 with: node-version-file: '.node-version' cache: 'pnpm' - - run: corepack enable - run: pnpm i --frozen-lockfile - name: Restore eslint cache uses: actions/cache@v4.2.2 @@ -101,12 +97,12 @@ jobs: with: fetch-depth: 0 submodules: true - - uses: pnpm/action-setup@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 - uses: actions/setup-node@v4.2.0 with: node-version-file: '.node-version' cache: 'pnpm' - - run: corepack enable - run: pnpm i --frozen-lockfile - run: pnpm --filter misskey-js run build if: ${{ matrix.workspace == 'backend' || matrix.workspace == 'sw' }} diff --git a/.github/workflows/locale.yml b/.github/workflows/locale.yml index 2daeaa3bd7..4c0de376d2 100644 --- a/.github/workflows/locale.yml +++ b/.github/workflows/locale.yml @@ -9,10 +9,6 @@ on: paths: - locales/** - .github/workflows/locale.yml - -env: - COREPACK_DEFAULT_TO_LATEST: 0 - jobs: locale_verify: runs-on: ubuntu-latest @@ -22,11 +18,11 @@ jobs: with: fetch-depth: 0 submodules: true - - uses: pnpm/action-setup@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 - uses: actions/setup-node@v4.2.0 with: node-version-file: '.node-version' cache: 'pnpm' - - run: corepack enable - run: pnpm i --frozen-lockfile - run: cd locales && node verify.js diff --git a/.github/workflows/on-release-created.yml b/.github/workflows/on-release-created.yml index 8e4ad4368b..aa32f2cb3b 100644 --- a/.github/workflows/on-release-created.yml +++ b/.github/workflows/on-release-created.yml @@ -6,9 +6,6 @@ on: workflow_dispatch: -env: - COREPACK_DEFAULT_TO_LATEST: 0 - jobs: publish-misskey-js: name: Publish misskey-js @@ -26,8 +23,8 @@ jobs: - uses: actions/checkout@v4.2.2 with: submodules: true - - name: Install pnpm - uses: pnpm/action-setup@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4.2.0 with: @@ -36,7 +33,6 @@ jobs: registry-url: 'https://registry.npmjs.org' - name: Publish package run: | - corepack enable pnpm i --frozen-lockfile pnpm build pnpm --filter misskey-js publish --access public --no-git-checks --provenance diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml index 9e5a79faac..9fdbeab913 100644 --- a/.github/workflows/storybook.yml +++ b/.github/workflows/storybook.yml @@ -13,9 +13,6 @@ on: # This is a waste of chromatic build quota, so we don't run storybook CI on pull requests targets master. - master -env: - COREPACK_DEFAULT_TO_LATEST: 0 - jobs: build: # chromatic is not likely to be available for fork repositories, so we disable for fork repositories. @@ -43,14 +40,13 @@ jobs: run: | echo "base=$(git rev-list --parents -n1 HEAD | cut -d" " -f2)" >> $GITHUB_OUTPUT git checkout $(git rev-list --parents -n1 HEAD | cut -d" " -f3) - - name: Install pnpm - uses: pnpm/action-setup@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 - name: Use Node.js 20.x uses: actions/setup-node@v4.2.0 with: node-version-file: '.node-version' cache: 'pnpm' - - run: corepack enable - run: pnpm i --frozen-lockfile - name: Check pnpm-lock.yaml run: git diff --exit-code pnpm-lock.yaml diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml index 2b8092cf45..69652621ca 100644 --- a/.github/workflows/test-backend.yml +++ b/.github/workflows/test-backend.yml @@ -18,10 +18,6 @@ on: - packages/misskey-js/** - .github/workflows/test-backend.yml - .github/misskey/test.yml - -env: - COREPACK_DEFAULT_TO_LATEST: 0 - jobs: unit: name: Unit tests (backend) @@ -48,8 +44,8 @@ jobs: - uses: actions/checkout@v4.2.2 with: submodules: true - - name: Install pnpm - uses: pnpm/action-setup@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 - name: Install FFmpeg run: | for i in {1..3}; do @@ -70,7 +66,6 @@ jobs: with: node-version: ${{ matrix.node-version }} cache: 'pnpm' - - run: corepack enable - run: pnpm i --frozen-lockfile - name: Check pnpm-lock.yaml run: git diff --exit-code pnpm-lock.yaml @@ -111,14 +106,13 @@ jobs: - uses: actions/checkout@v4.2.2 with: submodules: true - - name: Install pnpm - uses: pnpm/action-setup@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4.2.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' - - run: corepack enable - run: pnpm i --frozen-lockfile - name: Check pnpm-lock.yaml run: git diff --exit-code pnpm-lock.yaml diff --git a/.github/workflows/test-federation.yml b/.github/workflows/test-federation.yml index 0b71325de3..93588b54b9 100644 --- a/.github/workflows/test-federation.yml +++ b/.github/workflows/test-federation.yml @@ -15,9 +15,6 @@ on: - packages/misskey-js/** - .github/workflows/test-federation.yml -env: - COREPACK_DEFAULT_TO_LATEST: 0 - jobs: test: name: Federation test @@ -29,8 +26,8 @@ jobs: - uses: actions/checkout@v4 with: submodules: true - - name: Install pnpm - uses: pnpm/action-setup@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 - name: Install FFmpeg run: | for i in {1..3}; do @@ -53,7 +50,6 @@ jobs: cache: 'pnpm' - name: Build Misskey run: | - corepack enable && corepack prepare pnpm i --frozen-lockfile pnpm build - name: Setup diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml index e489ebf07c..14a754c190 100644 --- a/.github/workflows/test-frontend.yml +++ b/.github/workflows/test-frontend.yml @@ -22,10 +22,6 @@ on: - packages/backend/** - .github/workflows/test-frontend.yml - .github/misskey/test.yml - -env: - COREPACK_DEFAULT_TO_LATEST: 0 - jobs: vitest: name: Unit tests (frontend) @@ -39,14 +35,13 @@ jobs: - uses: actions/checkout@v4.2.2 with: submodules: true - - name: Install pnpm - uses: pnpm/action-setup@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4.2.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' - - run: corepack enable - run: pnpm i --frozen-lockfile - name: Check pnpm-lock.yaml run: git diff --exit-code pnpm-lock.yaml @@ -95,14 +90,13 @@ jobs: # if: ${{ matrix.browser == 'firefox' }} #- uses: browser-actions/setup-firefox@latest # if: ${{ matrix.browser == 'firefox' }} - - name: Install pnpm - uses: pnpm/action-setup@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4.2.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' - - run: corepack enable - run: pnpm i --frozen-lockfile - name: Copy Configure run: cp .github/misskey/test.yml .config diff --git a/.github/workflows/test-misskey-js.yml b/.github/workflows/test-misskey-js.yml index 05f757acc1..29b6c6172b 100644 --- a/.github/workflows/test-misskey-js.yml +++ b/.github/workflows/test-misskey-js.yml @@ -14,10 +14,6 @@ on: paths: - packages/misskey-js/** - .github/workflows/test-misskey-js.yml - -env: - COREPACK_DEFAULT_TO_LATEST: 0 - jobs: test: name: Unit tests (misskey.js) @@ -33,7 +29,8 @@ jobs: - name: Checkout uses: actions/checkout@v4.2.2 - - run: corepack enable + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4.2.0 diff --git a/.github/workflows/test-production.yml b/.github/workflows/test-production.yml index 56e42213ff..205eae2399 100644 --- a/.github/workflows/test-production.yml +++ b/.github/workflows/test-production.yml @@ -9,7 +9,6 @@ on: env: NODE_ENV: production - COREPACK_DEFAULT_TO_LATEST: 0 jobs: production: @@ -24,14 +23,13 @@ jobs: - uses: actions/checkout@v4.2.2 with: submodules: true - - name: Install pnpm - uses: pnpm/action-setup@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4.2.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' - - run: corepack enable - run: pnpm i --frozen-lockfile - name: Check pnpm-lock.yaml run: git diff --exit-code pnpm-lock.yaml diff --git a/.github/workflows/validate-api-json.yml b/.github/workflows/validate-api-json.yml index a8b2402988..f84efa4821 100644 --- a/.github/workflows/validate-api-json.yml +++ b/.github/workflows/validate-api-json.yml @@ -12,10 +12,6 @@ on: paths: - packages/backend/** - .github/workflows/validate-api-json.yml - -env: - COREPACK_DEFAULT_TO_LATEST: 0 - jobs: validate-api-json: runs-on: ubuntu-latest @@ -28,8 +24,8 @@ jobs: - uses: actions/checkout@v4.2.2 with: submodules: true - - name: Install pnpm - uses: pnpm/action-setup@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4.2.0 with: @@ -37,7 +33,6 @@ jobs: cache: 'pnpm' - name: Install Redocly CLI run: npm i -g @redocly/cli - - run: corepack enable - run: pnpm i --frozen-lockfile - name: Check pnpm-lock.yaml run: git diff --exit-code pnpm-lock.yaml @@ -1 +1,3 @@ engine-strict = true +save-exact = true +shell-emulator = true diff --git a/CHANGELOG.md b/CHANGELOG.md index 197de5aec7..e5df6de4e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +## 2025.3.1 + +### General +- pnpmをv10に更新 +- Corepackを削除 + +### Client +- Feat: 設定の検索を追加(実験的) +- Enhance: 設定項目の再配置 + +### Server +- Fix: DBマイグレーション際にシステムアカウントのユーザーID判定が正しくない問題を修正 +- Fix: user.featured列が状況によってJSON文字列になっていたのを修正 + + ## 2025.3.0 ### General diff --git a/Dockerfile b/Dockerfile index 3bc2044396..9d5596f1f1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,8 +6,6 @@ ARG NODE_VERSION=22.11.0-bookworm FROM --platform=$BUILDPLATFORM node:${NODE_VERSION} AS native-builder -ENV COREPACK_DEFAULT_TO_LATEST=0 - RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ rm -f /etc/apt/apt.conf.d/docker-clean \ @@ -16,8 +14,6 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ && apt-get install -yqq --no-install-recommends \ build-essential -RUN corepack enable - WORKDIR /misskey COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"] @@ -33,6 +29,8 @@ COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bu ARG NODE_ENV=production +RUN node -e "console.log(JSON.parse(require('node:fs').readFileSync('./package.json')).packageManager)" | xargs npm install -g + RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \ pnpm i --frozen-lockfile --aggregate-output @@ -46,14 +44,10 @@ RUN rm -rf .git/ FROM --platform=$TARGETPLATFORM node:${NODE_VERSION} AS target-builder -ENV COREPACK_DEFAULT_TO_LATEST=0 - RUN apt-get update \ && apt-get install -yqq --no-install-recommends \ build-essential -RUN corepack enable - WORKDIR /misskey COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"] @@ -65,6 +59,8 @@ COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bu ARG NODE_ENV=production +RUN node -e "console.log(JSON.parse(require('node:fs').readFileSync('./package.json')).packageManager)" | xargs npm install -g + RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \ pnpm i --frozen-lockfile --aggregate-output @@ -72,13 +68,11 @@ FROM --platform=$TARGETPLATFORM node:${NODE_VERSION}-slim AS runner ARG UID="991" ARG GID="991" -ENV COREPACK_DEFAULT_TO_LATEST=0 RUN apt-get update \ && apt-get install -y --no-install-recommends \ ffmpeg tini curl libjemalloc-dev libjemalloc2 \ && ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so \ - && corepack enable \ && groupadd -g "${GID}" misskey \ && useradd -l -u "${UID}" -g "${GID}" -m -d /misskey misskey \ && find / -type d -path /sys -prune -o -type d -path /proc -prune -o -type f -perm /u+s -ignore_readdir_race -exec chmod u-s {} \; \ @@ -86,13 +80,13 @@ RUN apt-get update \ && apt-get clean \ && rm -rf /var/lib/apt/lists +# add package.json to add pnpm +COPY ./package.json ./package.json +RUN node -e "console.log(JSON.parse(require('node:fs').readFileSync('./package.json')).packageManager)" | xargs npm install -g + USER misskey WORKDIR /misskey -# add package.json to add pnpm -COPY --chown=misskey:misskey ./package.json ./package.json -RUN corepack install - COPY --chown=misskey:misskey --from=target-builder /misskey/node_modules ./node_modules COPY --chown=misskey:misskey --from=target-builder /misskey/packages/backend/node_modules ./packages/backend/node_modules COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-js/node_modules ./packages/misskey-js/node_modules diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index 8b52450e95..377d16b147 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -111,7 +111,7 @@ followRequests: "Peticions de seguiment" unfollow: "Deixar de seguir" followRequestPending: "Sol·licituds de seguiment pendents" enterEmoji: "Introduir un emoji" -renote: "Impulsos" +renote: "Impulsar" unrenote: "Anul·la l'impuls" renoted: "S'ha impulsat" renotedToX: "Impulsat per {name}." @@ -1114,7 +1114,7 @@ forceShowAds: "Mostra els anuncis sempre " addMemo: "Afegir recordatori" editMemo: "Editar recordatori" reactionsList: "Reaccions" -renotesList: "Impulsos" +renotesList: "Llistat d'impulsos " notificationDisplay: "Notificacions" leftTop: "Dalt a l'esquerra " rightTop: "Dalt a la dreta " @@ -1190,7 +1190,7 @@ pastAnnouncements: "Informes passats" youHaveUnreadAnnouncements: "Tens informes per llegir." useSecurityKey: "Segueix les instruccions del teu navegador O dispositiu per fer servir el teu passkey." replies: "Respostes" -renotes: "Impulsos" +renotes: "Impulsar" loadReplies: "Mostrar les respostes" loadConversation: "Mostrar la conversació " pinnedList: "Llista fixada" @@ -2452,7 +2452,7 @@ _notification: follow: "Segueix-me" mention: "Menció" reply: "Respostes" - renote: "Renotar" + renote: "Impulsar" quote: "Citar" reaction: "Reaccions" pollEnded: "Enquesta terminada" @@ -2467,7 +2467,7 @@ _notification: _actions: followBack: "També et segueix" reply: "Respondre" - renote: "Renotar" + renote: "Impulsos" _deck: alwaysShowMainColumn: "Mostrar sempre la columna principal" columnAlign: "Alinea les columnes" diff --git a/locales/en-US.yml b/locales/en-US.yml index 04ddd2966a..f4c3323691 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1311,6 +1311,8 @@ federationSpecified: "This server is operated in a whitelist federation. Interac federationDisabled: "Federation is disabled on this server. You cannot interact with users on other servers." confirmOnReact: "Confirm when reacting" reactAreYouSure: "Would you like to add a \"{emoji}\" reaction?" +markAsSensitiveConfirm: "Do you want to set this media as sensitive?" +unmarkAsSensitiveConfirm: "Do you want to remove the sensitive designation for this media?" _accountSettings: requireSigninToViewContents: "Require sign-in to view contents" requireSigninToViewContentsDescription1: "Require login to view all notes and other content you have created. This will have the effect of preventing crawlers from collecting your information." @@ -2594,6 +2596,7 @@ _moderationLogTypes: deletePage: "Page deleted" deleteFlash: "Play deleted" deleteGalleryPost: "Gallery post deleted" + updateProxyAccountDescription: "Update the description of the proxy account" _fileViewer: title: "File details" type: "File type" @@ -2649,7 +2652,7 @@ _dataSaver: description: "Prevents images/videos from being loaded automatically. Hidden images/videos will be loaded when tapped." _avatar: title: "Avatar image" - description: "Stop avatar image animation. Animated images can be larger in file size than normal images, potentially leading to further reductions in data traffic." + description: "Stop avatar image animation. Animated images can be larger in file size than normal images, potentially leading to further reductions in data traffic." _urlPreview: title: "URL preview thumbnails" description: "URL preview thumbnail images will no longer be loaded." @@ -2857,4 +2860,8 @@ _bootErrors: _search: searchScopeAll: "All" searchScopeLocal: "Local" + searchScopeServer: "Specific server" searchScopeUser: "Specific user" + pleaseEnterServerHost: "Enter the server host" + pleaseSelectUser: "Select user" + serverHostPlaceholder: "Example: misskey.example.com" diff --git a/locales/index.d.ts b/locales/index.d.ts index 947b577792..6810d204cb 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -4971,7 +4971,7 @@ export interface Locale extends ILocale { */ "disableStreamingTimeline": string; /** - * 通知をグルーピングして表示する + * 通知をグルーピング */ "useGroupedNotifications": string; /** @@ -5270,6 +5270,14 @@ export interface Locale extends ILocale { * このメディアのセンシティブ指定を解除しますか? */ "unmarkAsSensitiveConfirm": string; + /** + * 環境設定 + */ + "preferences": string; + /** + * アクセシビリティ + */ + "accessibility": string; "_accountSettings": { /** * コンテンツの表示にログインを必須にする diff --git a/locales/it-IT.yml b/locales/it-IT.yml index c3a33139be..5248927035 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -1313,6 +1313,8 @@ confirmOnReact: "Confermare le reazioni" reactAreYouSure: "Vuoi davvero reagire con {emoji} ?" markAsSensitiveConfirm: "Vuoi davvero indicare questo contenuto multimediale come esplicito?" unmarkAsSensitiveConfirm: "Vuoi davvero indicare come non esplicito il contenuto multimediale?" +preferences: "Preferenze" +accessibility: "Accessibilità" _accountSettings: requireSigninToViewContents: "Per vedere il contenuto, è necessaria l'iscrizione" requireSigninToViewContentsDescription1: "Richiedere l'iscrizione per visualizzare tutte le Note e gli altri contenuti che hai creato. Probabilmente l'effetto è impedire la raccolta di informazioni da parte dei bot crawler." @@ -1475,7 +1477,7 @@ _serverSettings: _accountMigration: moveFrom: "Migra un altro profilo dentro a questo" moveFromSub: "Crea un alias verso un altro profilo remoto" - moveFromLabel: "Profilo da cui migrare #{n}" + moveFromLabel: "Profilo da cui migrare n. {n}" moveFromDescription: "Se desideri spostare i Follower da un altro profilo a questo, devi prima creare un alias qui. Assicurati averlo creato PRIMA di eseguire l'attività! Inserisci l'indirizzo del profilo mittente in questo modo: @persona@vecchia.istanza.it" moveTo: "Migrare questo profilo verso un un altro" moveToLabel: "Profilo verso cui migrare" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index fbe4d98896..7a5d2f795e 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1238,7 +1238,7 @@ releaseToRefresh: "離してリロード" refreshing: "リロード中" pullDownToRefresh: "引っ張ってリロード" disableStreamingTimeline: "タイムラインのリアルタイム更新を無効にする" -useGroupedNotifications: "通知をグルーピングして表示する" +useGroupedNotifications: "通知をグルーピング" signupPendingError: "メールアドレスの確認中に問題が発生しました。リンクの有効期限が切れている可能性があります。" cwNotationRequired: "「内容を隠す」がオンの場合は注釈の記述が必要です。" doReaction: "リアクションする" @@ -1313,6 +1313,8 @@ confirmOnReact: "リアクションする際に確認する" reactAreYouSure: "\" {emoji} \" をリアクションしますか?" markAsSensitiveConfirm: "このメディアをセンシティブとして設定しますか?" unmarkAsSensitiveConfirm: "このメディアのセンシティブ指定を解除しますか?" +preferences: "環境設定" +accessibility: "アクセシビリティ" _accountSettings: requireSigninToViewContents: "コンテンツの表示にログインを必須にする" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index f0df9deee0..f5a9786063 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -1313,6 +1313,8 @@ confirmOnReact: "发送回应前需要确认" reactAreYouSure: "要用「{emoji}」进行回应吗?" markAsSensitiveConfirm: "要将此媒体标记为敏感吗?" unmarkAsSensitiveConfirm: "要将此媒体解除敏感标记吗?" +preferences: "设置" +accessibility: "辅助功能" _accountSettings: requireSigninToViewContents: "需要登录才能显示内容" requireSigninToViewContentsDescription1: "您发布的所有帖子将变成需要登入后才会显示。有望防止爬虫收集各种信息。" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 7c7e290545..ea4ef49e7f 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1313,6 +1313,8 @@ confirmOnReact: "反應時確認" reactAreYouSure: "用「 {emoji} 」反應嗎?" markAsSensitiveConfirm: "要將這個媒體設定為敏感嗎?" unmarkAsSensitiveConfirm: "要解除這個媒體的敏感設定嗎?" +preferences: "環境設定" +accessibility: "輔助工具" _accountSettings: requireSigninToViewContents: "須登入以顯示內容" requireSigninToViewContentsDescription1: "必須登入才會顯示您建立的貼文等內容。可望有效防止資訊被爬蟲蒐集。" diff --git a/package.json b/package.json index a3a5924aff..267dd8adaa 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "misskey", - "version": "2025.3.0", + "version": "2025.3.1", "codename": "nasubi", "repository": { "type": "git", "url": "https://github.com/misskey-dev/misskey.git" }, - "packageManager": "pnpm@9.15.4", + "packageManager": "pnpm@10.6.1", "workspaces": [ "packages/frontend-shared", "packages/frontend", @@ -47,35 +47,44 @@ "cleanall": "pnpm clean-all" }, "resolutions": { - "chokidar": "3.6.0", + "chokidar": "4.0.3", "lodash": "4.17.21" }, "dependencies": { "cssnano": "7.0.6", - "execa": "8.0.1", + "execa": "9.5.2", "fast-glob": "3.3.3", - "ignore-walk": "6.0.5", + "ignore-walk": "7.0.0", "js-yaml": "4.1.0", - "postcss": "8.5.2", - "tar": "6.2.1", + "postcss": "8.5.3", + "tar": "7.4.3", "terser": "5.39.0", - "typescript": "5.7.3", + "typescript": "5.8.2", "esbuild": "0.25.0", "glob": "11.0.1" }, "devDependencies": { "@misskey-dev/eslint-plugin": "2.1.0", - "@types/node": "22.13.4", - "@typescript-eslint/eslint-plugin": "8.24.0", - "@typescript-eslint/parser": "8.24.0", + "@types/node": "22.13.9", + "@typescript-eslint/eslint-plugin": "8.26.0", + "@typescript-eslint/parser": "8.26.0", "cross-env": "7.0.3", - "cypress": "14.0.3", - "eslint": "9.20.1", - "globals": "15.15.0", + "cypress": "14.1.0", + "eslint": "9.21.0", + "globals": "16.0.0", "ncp": "2.0.0", + "pnpm": "10.6.1", "start-server-and-test": "2.0.10" }, "optionalDependencies": { "@tensorflow/tfjs-core": "4.22.0" + }, + "pnpm": { + "overrides": { + "@aiscript-dev/aiscript-languageserver": "-" + }, + "patchedDependencies": { + "re2": "scripts/dependency-patches/re2.patch" + } } } diff --git a/packages/backend/migration/1741279404074-system-accounts-fixup.js b/packages/backend/migration/1741279404074-system-accounts-fixup.js new file mode 100644 index 0000000000..31cab7f5ae --- /dev/null +++ b/packages/backend/migration/1741279404074-system-accounts-fixup.js @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class SystemAccounts1741279404074 { + name = 'SystemAccounts1741279404074' + + async up(queryRunner) { + const instanceActor = await queryRunner.query(`SELECT "id" FROM "user" WHERE "username" = 'instance.actor' AND "host" IS NULL AND "id" NOT IN (SELECT "userId" FROM "system_account" WHERE "type" = 'actor')`); + if (instanceActor.length > 0) { + console.warn('instance.actor was incorrect, updating...'); + await queryRunner.query(`UPDATE "system_account" SET "id" = '${instanceActor[0].id}', "userId" = '${instanceActor[0].id}' WHERE "type" = 'actor'`); + } + + const relayActor = await queryRunner.query(`SELECT "id" FROM "user" WHERE "username" = 'relay.actor' AND "host" IS NULL AND "id" NOT IN (SELECT "userId" FROM "system_account" WHERE "type" = 'relay')`); + if (relayActor.length > 0) { + console.warn('relay.actor was incorrect, updating...'); + await queryRunner.query(`UPDATE "system_account" SET "id" = '${relayActor[0].id}', "userId" = '${relayActor[0].id}' WHERE "type" = 'relay'`); + } + } + + async down(queryRunner) { + // fixup migration, no down migration + } +} diff --git a/packages/backend/migration/1741424411879-user-featured-fixup.js b/packages/backend/migration/1741424411879-user-featured-fixup.js new file mode 100644 index 0000000000..5643a328f0 --- /dev/null +++ b/packages/backend/migration/1741424411879-user-featured-fixup.js @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class UserFeaturedFixup1741424411879 { + name = 'UserFeaturedFixup1741424411879' + + async up(queryRunner) { + await queryRunner.query(`CREATE OR REPLACE FUNCTION pg_temp.extract_ap_id(text) RETURNS text AS $$ + SELECT + CASE + WHEN $1 ~ '^https?://' THEN $1 + WHEN $1 LIKE '{%' THEN COALESCE(jsonb_extract_path_text($1::jsonb, 'id'), null) + ELSE null + END; + $$ LANGUAGE sql IMMUTABLE;`); + + // "host" is NOT NULL is not needed but just in case add it to prevent overwriting irreplaceable data + await queryRunner.query(`UPDATE "user" SET "featured" = pg_temp.extract_ap_id("featured") WHERE "host" IS NOT NULL`); + } + + async down(queryRunner) { + // fixup migration, no down migration + } +} diff --git a/packages/backend/src/core/FileInfoService.ts b/packages/backend/src/core/FileInfoService.ts index fc68eb4836..a295e81920 100644 --- a/packages/backend/src/core/FileInfoService.ts +++ b/packages/backend/src/core/FileInfoService.ts @@ -268,7 +268,6 @@ export class FileInfoService { private async *asyncIterateFrames(cwd: string, command: FFmpeg.FfmpegCommand): AsyncGenerator<string, void> { const watcher = new FSWatcher({ cwd, - disableGlobbing: true, }); let finished = false; command.once('end', () => { diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 879f1922ca..e52078ed0f 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -560,7 +560,7 @@ export class ApPersonService implements OnModuleInit { inbox: person.inbox, sharedInbox: person.sharedInbox ?? person.endpoints?.sharedInbox ?? null, followersUri: person.followers ? getApId(person.followers) : undefined, - featured: person.featured, + featured: person.featured ? getApId(person.featured) : undefined, emojis: emojiNames, name: truncate(person.name, nameLength), tags, diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 8c4b13a40a..20e985aaf2 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -751,7 +751,7 @@ export class ActivityPubServerService { }); // follow - fastify.get<{ Params: { followRequestId: string ; } }>('/follows/:followRequestId', async (request, reply) => { + fastify.get<{ Params: { followRequestId: string; } }>('/follows/:followRequestId', async (request, reply) => { // This may be used before the follow is completed, so we do not // check if the following exists and only check if the follow request exists. diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index bf0a011699..772c37094c 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -497,7 +497,7 @@ export class FileServerService { @bindThis private async downloadAndDetectTypeFromUrl(url: string): Promise< - { state: 'remote' ; mime: string; ext: string | null; path: string; cleanup: () => void; filename: string; } + { state: 'remote'; mime: string; ext: string | null; path: string; cleanup: () => void; filename: string; } > { const [path, cleanup] = await createTemp(); try { diff --git a/packages/backend/test-federation/compose.tpl.yml b/packages/backend/test-federation/compose.tpl.yml index 8b270e58f7..25770063d3 100644 --- a/packages/backend/test-federation/compose.tpl.yml +++ b/packages/backend/test-federation/compose.tpl.yml @@ -17,7 +17,6 @@ services: - ./.config/docker.env environment: - NODE_ENV=production - - COREPACK_DEFAULT_TO_LATEST=0 volumes: - type: bind source: ../../../built @@ -76,13 +75,17 @@ services: target: /misskey/pnpm-workspace.yaml read_only: true - type: bind + source: ../../../scripts/dependency-patches + target: /misskey/scripts/dependency-patches + read_only: true + - type: bind source: ./certificates/rootCA.crt target: /usr/local/share/ca-certificates/rootCA.crt read_only: true working_dir: /misskey command: > bash -c " - corepack enable && corepack prepare + npm install -g pnpm pnpm -F backend migrate pnpm -F backend start " diff --git a/packages/backend/test-federation/compose.yml b/packages/backend/test-federation/compose.yml index ed39109aab..dfa51b940a 100644 --- a/packages/backend/test-federation/compose.yml +++ b/packages/backend/test-federation/compose.yml @@ -9,7 +9,7 @@ services: service: misskey command: > bash -c " - corepack enable && corepack prepare + npm install -g pnpm pnpm -F backend i pnpm -F misskey-js i pnpm -F misskey-reversi i @@ -29,7 +29,6 @@ services: environment: - NODE_ENV=development - NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/rootCA.crt - - COREPACK_DEFAULT_TO_LATEST=0 volumes: - type: bind source: ../package.json @@ -72,13 +71,17 @@ services: target: /misskey/pnpm-workspace.yaml read_only: true - type: bind + source: ../../../scripts/dependency-patches + target: /misskey/scripts/dependency-patches + read_only: true + - type: bind source: ./certificates/rootCA.crt target: /usr/local/share/ca-certificates/rootCA.crt read_only: true working_dir: /misskey entrypoint: > bash -c ' - corepack enable && corepack prepare + npm install -g pnpm pnpm -F misskey-js i --frozen-lockfile pnpm -F backend i --frozen-lockfile exec "$0" "$@" @@ -90,8 +93,6 @@ services: depends_on: redis.test: condition: service_healthy - environment: - - COREPACK_DEFAULT_TO_LATEST=0 volumes: - type: bind source: ../package.json @@ -117,10 +118,14 @@ services: source: ../../../pnpm-workspace.yaml target: /misskey/pnpm-workspace.yaml read_only: true + - type: bind + source: ../../../scripts/dependency-patches + target: /misskey/scripts/dependency-patches + read_only: true working_dir: /misskey command: > bash -c " - corepack enable && corepack prepare + npm install -g pnpm pnpm -F backend i --frozen-lockfile pnpm exec tsc -p ./packages/backend/test-federation node ./packages/backend/test-federation/built/daemon.js diff --git a/packages/frontend-embed/package.json b/packages/frontend-embed/package.json index 1ee4fc2c28..21247e32ab 100644 --- a/packages/frontend-embed/package.json +++ b/packages/frontend-embed/package.json @@ -34,7 +34,7 @@ "typescript": "5.8.2", "uuid": "11.1.0", "json5": "2.2.3", - "vite": "6.2.0", + "vite": "6.2.1", "vue": "3.5.13" }, "devDependencies": { @@ -48,14 +48,14 @@ "@types/ws": "8.18.0", "@typescript-eslint/eslint-plugin": "8.26.0", "@typescript-eslint/parser": "8.26.0", - "@vitest/coverage-v8": "3.0.7", + "@vitest/coverage-v8": "3.0.8", "@vue/runtime-core": "3.5.13", - "acorn": "8.14.0", + "acorn": "8.14.1", "cross-env": "7.0.3", "eslint-plugin-import": "2.31.0", - "eslint-plugin-vue": "9.33.0", + "eslint-plugin-vue": "10.0.0", "fast-glob": "3.3.3", - "happy-dom": "17.2.2", + "happy-dom": "17.3.0", "intersection-observer": "0.12.2", "micromatch": "4.0.8", "msw": "2.7.3", @@ -64,7 +64,7 @@ "start-server-and-test": "2.0.10", "vite-plugin-turbosnap": "1.0.3", "vue-component-type-helpers": "2.2.8", - "vue-eslint-parser": "9.4.3", + "vue-eslint-parser": "10.1.1", "vue-tsc": "2.2.8" } } diff --git a/packages/frontend-shared/package.json b/packages/frontend-shared/package.json index ad9a0bafb6..7a05771ea8 100644 --- a/packages/frontend-shared/package.json +++ b/packages/frontend-shared/package.json @@ -25,10 +25,10 @@ "@typescript-eslint/eslint-plugin": "8.26.0", "@typescript-eslint/parser": "8.26.0", "esbuild": "0.25.0", - "eslint-plugin-vue": "9.33.0", + "eslint-plugin-vue": "10.0.0", "nodemon": "3.1.9", "typescript": "5.8.2", - "vue-eslint-parser": "9.4.3" + "vue-eslint-parser": "10.1.1" }, "files": [ "js-built" diff --git a/packages/frontend/.storybook/main.ts b/packages/frontend/.storybook/main.ts index 9f318cf449..c1119c2523 100644 --- a/packages/frontend/.storybook/main.ts +++ b/packages/frontend/.storybook/main.ts @@ -39,6 +39,10 @@ const config = { if (~replacePluginForIsChromatic) { config.plugins?.splice(replacePluginForIsChromatic, 1); } + + //pluginsからcreateSearchIndexを削除、複数あるかもしれないので全て削除 + config.plugins = config.plugins?.filter((plugin: Plugin) => plugin && plugin.name !== 'createSearchIndex') ?? []; + return mergeConfig(config, { plugins: [ { diff --git a/packages/frontend/lib/vite-plugin-create-search-index.ts b/packages/frontend/lib/vite-plugin-create-search-index.ts new file mode 100644 index 0000000000..509eb804cb --- /dev/null +++ b/packages/frontend/lib/vite-plugin-create-search-index.ts @@ -0,0 +1,1496 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { parse as vueSfcParse } from 'vue/compiler-sfc'; +import type { LogOptions, Plugin } from 'vite'; +import fs from 'node:fs'; +import { glob } from 'glob'; +import JSON5 from 'json5'; +import MagicString from 'magic-string'; +import path from 'node:path' +import { hash, toBase62 } from '../vite.config'; +import { createLogger } from 'vite'; + +interface VueAstNode { + type: number; + tag?: string; + loc?: { + start: { offset: number, line: number, column: number }, + end: { offset: number, line: number, column: number }, + source?: string + }; + props?: Array<{ + name: string; + type: number; + value?: { content?: string }; + arg?: { content?: string }; + exp?: { content?: string; loc?: any }; + }>; + children?: VueAstNode[]; + content?: any; + __markerId?: string; + __children?: string[]; +} + +export type AnalysisResult = { + filePath: string; + usage: SearchIndexItem[]; +} + +export type SearchIndexItem = { + id: string; + path?: string; + label: string; + keywords: string | string[]; + icon?: string; + inlining?: string[]; + children?: SearchIndexItem[]; +}; + +export type Options = { + targetFilePaths: string[], + exportFilePath: string, + verbose?: boolean, +}; + +// 関連するノードタイプの定数化 +const NODE_TYPES = { + ELEMENT: 1, + EXPRESSION: 2, + TEXT: 3, + INTERPOLATION: 5, // Mustache +}; + +// マーカー関係を表す型 +interface MarkerRelation { + parentId?: string; + markerId: string; + node: VueAstNode; +} + +// ロガー +let logger = { + info: (msg: string, options?: LogOptions) => { }, + warn: (msg: string, options?: LogOptions) => { }, + error: (msg: string, options?: LogOptions) => { }, +}; +let loggerInitialized = false; + +function initLogger(options: Options) { + if (loggerInitialized) return; + loggerInitialized = true; + const viteLogger = createLogger(options.verbose ? 'info' : 'warn'); + + logger.info = (msg, options) => { + msg = `[create-search-index] ${msg}`; + viteLogger.info(msg, options); + } + + logger.warn = (msg, options) => { + msg = `[create-search-index] ${msg}`; + viteLogger.warn(msg, options); + } + + logger.error = (msg, options) => { + msg = `[create-search-index] ${msg}`; + viteLogger.error(msg, options); + } +} + +/** + * 解析結果をTypeScriptファイルとして出力する + */ +function outputAnalysisResultAsTS(outputPath: string, analysisResults: AnalysisResult[]): void { + logger.info(`Processing ${analysisResults.length} files for output`); + + // 新しいツリー構造を構築 + const allMarkers = new Map<string, SearchIndexItem>(); + + // 1. すべてのマーカーを一旦フラットに収集 + for (const file of analysisResults) { + logger.info(`Processing file: ${file.filePath} with ${file.usage.length} markers`); + + for (const marker of file.usage) { + if (marker.id) { + // キーワードとchildren処理を共通化 + const processedMarker = { + ...marker, + keywords: processMarkerProperty(marker.keywords, 'keywords'), + children: processMarkerProperty(marker.children || [], 'children') + }; + + allMarkers.set(marker.id, processedMarker); + } + } + } + + logger.info(`Collected total ${allMarkers.size} unique markers`); + + // 2. 子マーカーIDの収集 + const childIds = collectChildIds(allMarkers); + logger.info(`Found ${childIds.size} child markers`); + + // 3. ルートマーカーの特定(他の誰かの子でないマーカー) + const rootMarkers = identifyRootMarkers(allMarkers, childIds); + logger.info(`Found ${rootMarkers.length} root markers`); + + // 4. 子マーカーの参照を解決 + const resolvedRootMarkers = resolveChildReferences(rootMarkers, allMarkers); + + // 5. デバッグ情報を生成 + const { totalMarkers, totalChildren } = countMarkers(resolvedRootMarkers); + logger.info(`Total markers in tree: ${totalMarkers} (${resolvedRootMarkers.length} roots + ${totalChildren} nested children)`); + + // 6. 結果をTS形式で出力 + writeOutputFile(outputPath, resolvedRootMarkers); +} + +/** + * マーカーのプロパティ(keywordsやchildren)を処理する + */ +function processMarkerProperty(propValue: any, propType: 'keywords' | 'children'): any { + // 文字列の配列表現を解析 + if (typeof propValue === 'string' && propValue.startsWith('[') && propValue.endsWith(']')) { + try { + // JSON5解析を試みる + return JSON5.parse(propValue.replace(/'/g, '"')); + } catch (e) { + // 解析に失敗した場合 + logger.warn(`Could not parse ${propType}: ${propValue}, using ${propType === 'children' ? 'empty array' : 'as is'}`); + return propType === 'children' ? [] : propValue; + } + } + + return propValue; +} + +/** + * 全マーカーから子IDを収集する + */ +function collectChildIds(allMarkers: Map<string, SearchIndexItem>): Set<string> { + const childIds = new Set<string>(); + + allMarkers.forEach((marker, id) => { + // 通常のchildren処理 + const children = marker.children; + if (Array.isArray(children)) { + children.forEach(childId => { + if (typeof childId === 'string') { + if (!allMarkers.has(childId)) { + logger.warn(`Warning: Child marker ID ${childId} referenced but not found`); + } else { + childIds.add(childId); + } + } + }); + } + + // inlining処理を追加 + if (marker.inlining) { + let inliningIds: string[] = []; + + // 文字列の場合は配列に変換 + if (typeof marker.inlining === 'string') { + try { + const inliningStr = (marker.inlining as string).trim(); + if (inliningStr.startsWith('[') && inliningStr.endsWith(']')) { + inliningIds = JSON5.parse(inliningStr.replace(/'/g, '"')); + logger.info(`Parsed inlining string to array: ${inliningStr} -> ${JSON.stringify(inliningIds)}`); + } else { + inliningIds = [inliningStr]; + } + } catch (e) { + logger.error(`Failed to parse inlining string: ${marker.inlining}`, e); + } + } + // 既に配列の場合 + else if (Array.isArray(marker.inlining)) { + inliningIds = marker.inlining; + } + + // inliningで指定されたIDを子セットに追加 + for (const inlineId of inliningIds) { + if (typeof inlineId === 'string') { + if (!allMarkers.has(inlineId)) { + logger.warn(`Warning: Inlining marker ID ${inlineId} referenced but not found`); + } else { + // inliningで参照されているマーカーも子として扱う + childIds.add(inlineId); + logger.info(`Added inlined marker ${inlineId} as child in collectChildIds`); + } + } + } + } + }); + + return childIds; +} + +/** + * ルートマーカー(他の子でないマーカー)を特定する + */ +function identifyRootMarkers( + allMarkers: Map<string, SearchIndexItem>, + childIds: Set<string> +): SearchIndexItem[] { + const rootMarkers: SearchIndexItem[] = []; + + allMarkers.forEach((marker, id) => { + if (!childIds.has(id)) { + rootMarkers.push(marker); + logger.info(`Added root marker to output: ${id} with label ${marker.label}`); + } + }); + + return rootMarkers; +} + +/** + * 子マーカーの参照をIDから実際のオブジェクトに解決する + */ +function resolveChildReferences( + rootMarkers: SearchIndexItem[], + allMarkers: Map<string, SearchIndexItem> +): SearchIndexItem[] { + function resolveChildrenForMarker(marker: SearchIndexItem): SearchIndexItem { + // マーカーのディープコピーを作成 + const resolvedMarker = { ...marker }; + // 明示的に子マーカー配列を作成 + const resolvedChildren: SearchIndexItem[] = []; + + // 通常のchildren処理 + if (Array.isArray(marker.children)) { + for (const childId of marker.children) { + if (typeof childId === 'string') { + const childMarker = allMarkers.get(childId); + if (childMarker) { + // 子マーカーの子も再帰的に解決 + const resolvedChild = resolveChildrenForMarker(childMarker); + resolvedChildren.push(resolvedChild); + logger.info(`Resolved regular child ${childId} for parent ${marker.id}`); + } + } + } + } + + // inlining属性の処理 + let inliningIds: string[] = []; + + // 文字列の場合は配列に変換。例: "['2fa']" -> ['2fa'] + if (typeof marker.inlining === 'string') { + try { + // 文字列形式の配列を実際の配列に変換 + const inliningStr = (marker.inlining as string).trim(); + if (inliningStr.startsWith('[') && inliningStr.endsWith(']')) { + inliningIds = JSON5.parse(inliningStr.replace(/'/g, '"')); + logger.info(`Converted string inlining to array: ${inliningStr} -> ${JSON.stringify(inliningIds)}`); + } else { + // 単一値の場合は配列に + inliningIds = [inliningStr]; + logger.info(`Converted single string inlining to array: ${inliningStr}`); + } + } catch (e) { + logger.error(`Failed to parse inlining string: ${marker.inlining}`, e); + } + } + // 既に配列の場合はそのまま使用 + else if (Array.isArray(marker.inlining)) { + inliningIds = marker.inlining; + } + + // インライン指定されたマーカーを子として追加 + for (const inlineId of inliningIds) { + if (typeof inlineId === 'string') { + const inlineMarker = allMarkers.get(inlineId); + if (inlineMarker) { + // インライン指定されたマーカーを再帰的に解決 + const resolvedInline = resolveChildrenForMarker(inlineMarker); + delete resolvedInline.path + resolvedChildren.push(resolvedInline); + logger.info(`Added inlined marker ${inlineId} as child to ${marker.id}`); + } else { + logger.warn(`Inlining target not found: ${inlineId} referenced by ${marker.id}`); + } + } + } + + // 解決した子が存在する場合のみchildrenプロパティを設定 + if (resolvedChildren.length > 0) { + resolvedMarker.children = resolvedChildren; + } else { + delete resolvedMarker.children; + } + + return resolvedMarker; + } + + // すべてのルートマーカーの子を解決 + return rootMarkers.map(marker => resolveChildrenForMarker(marker)); +} + +/** + * マーカー数を数える(デバッグ用) + */ +function countMarkers(markers: SearchIndexItem[]): { totalMarkers: number, totalChildren: number } { + let totalMarkers = markers.length; + let totalChildren = 0; + + function countNested(items: SearchIndexItem[]): void { + for (const marker of items) { + if (marker.children && Array.isArray(marker.children)) { + totalChildren += marker.children.length; + totalMarkers += marker.children.length; + countNested(marker.children as SearchIndexItem[]); + } + } + } + + countNested(markers); + return { totalMarkers, totalChildren }; +} + +/** + * 最終的なTypeScriptファイルを出力 + */ +function writeOutputFile(outputPath: string, resolvedRootMarkers: SearchIndexItem[]): void { + try { + const tsOutput = generateTypeScriptCode(resolvedRootMarkers); + fs.writeFileSync(outputPath, tsOutput, 'utf-8'); + // 強制的に出力させるためにViteロガーを使わない + console.log(`Successfully wrote search index to ${outputPath} with ${resolvedRootMarkers.length} root entries`); + } catch (error) { + logger.error('[create-search-index]: error writing output: ', error); + } +} + +/** + * TypeScriptコード生成 + */ +function generateTypeScriptCode(resolvedRootMarkers: SearchIndexItem[]): string { + return ` +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +// This file was automatically generated by create-search-index. +// Do not edit this file. + +import { i18n } from '@/i18n.js'; + +export type SearchIndexItem = { + id: string; + path?: string; + label: string; + keywords: string[]; + icon?: string; + children?: SearchIndexItem[]; +}; + +export const searchIndexes: SearchIndexItem[] = ${customStringify(resolvedRootMarkers)} as const; + +export type SearchIndex = typeof searchIndexes; +`; +} + +/** + * オブジェクトを特殊な形式の文字列に変換する + * i18n参照を保持しつつ適切な形式に変換 + */ +function customStringify(obj: any, depth = 0): string { + const INDENT_STR = '\t'; + + // 配列の処理 + if (Array.isArray(obj)) { + if (obj.length === 0) return '[]'; + const indent = INDENT_STR.repeat(depth); + const childIndent = INDENT_STR.repeat(depth + 1); + + // 配列要素の処理 + const items = obj.map(item => { + // オブジェクト要素 + if (typeof item === 'object' && item !== null) { + return `${childIndent}${customStringify(item, depth + 1)}`; + } + + // i18n参照を含む文字列要素 + if (typeof item === 'string' && item.includes('i18n.ts.')) { + return `${childIndent}${item}`; // クォートなしでそのまま出力 + } + + // その他の要素 + return `${childIndent}${JSON5.stringify(item)}`; + }).join(',\n'); + + return `[\n${items},\n${indent}]`; + } + + // null または非オブジェクト + if (obj === null || typeof obj !== 'object') { + return JSON5.stringify(obj); + } + + // オブジェクトの処理 + const indent = INDENT_STR.repeat(depth); + const childIndent = INDENT_STR.repeat(depth + 1); + + const entries = Object.entries(obj) + // 不要なプロパティを除去 + .filter(([key, value]) => { + if (value === undefined) return false; + if (key === 'children' && Array.isArray(value) && value.length === 0) return false; + if (key === 'inlining') return false; + return true; + }) + // 各プロパティを変換 + .map(([key, value]) => { + // 子要素配列の特殊処理 + if (key === 'children' && Array.isArray(value) && value.length > 0) { + return `${childIndent}${key}: ${customStringify(value, depth + 1)}`; + } + + // ラベルやその他プロパティを処理 + return `${childIndent}${key}: ${formatSpecialProperty(key, value)}`; + }); + + if (entries.length === 0) return '{}'; + return `{\n${entries.join(',\n')},\n${indent}}`; +} + +/** + * 特殊プロパティの書式設定 + */ +function formatSpecialProperty(key: string, value: any): string { + // 値がundefinedの場合は空文字列を返す + if (value === undefined) { + return '""'; + } + + // childrenが配列の場合は特別に処理 + if (key === 'children' && Array.isArray(value)) { + return customStringify(value); + } + + // keywordsが配列の場合、特別に処理 + if (key === 'keywords' && Array.isArray(value)) { + return `[${formatArrayForOutput(value)}]`; + } + + // 文字列値の場合の特別処理 + if (typeof value === 'string') { + // i18n.ts 参照を含む場合 - クォートなしでそのまま出力 + if (isI18nReference(value)) { + logger.info(`Preserving i18n reference in output: ${value}`); + return value; + } + + // keywords が配列リテラルの形式の場合 + if (key === 'keywords' && value.startsWith('[') && value.endsWith(']')) { + return value; + } + } + + // 上記以外は通常の JSON5 文字列として返す + return JSON5.stringify(value); +} + +/** + * 配列式の文字列表現を生成 + */ +function formatArrayForOutput(items: any[]): string { + return items.map(item => { + // i18n.ts. 参照の文字列はそのままJavaScript式として出力 + if (typeof item === 'string' && isI18nReference(item)) { + logger.info(`Preserving i18n reference in array: ${item}`); + return item; // クォートなしでそのまま + } + + // その他の値はJSON5形式で文字列化 + return JSON5.stringify(item); + }).join(', '); +} + +/** + * 要素ノードからテキスト内容を抽出する + * 各抽出方法を分離して可読性を向上 + */ +function extractElementText(node: VueAstNode): string | null { + if (!node) return null; + + logger.info(`Extracting text from node type=${node.type}, tag=${node.tag || 'unknown'}`); + + // 1. 直接コンテンツの抽出を試行 + const directContent = extractDirectContent(node); + if (directContent) return directContent; + + // 子要素がない場合は終了 + if (!node.children || !Array.isArray(node.children)) { + return null; + } + + // 2. インターポレーションノードを検索 + const interpolationContent = extractInterpolationContent(node.children); + if (interpolationContent) return interpolationContent; + + // 3. 式ノードを検索 + const expressionContent = extractExpressionContent(node.children); + if (expressionContent) return expressionContent; + + // 4. テキストノードを検索 + const textContent = extractTextContent(node.children); + if (textContent) return textContent; + + // 5. 再帰的に子ノードを探索 + return extractNestedContent(node.children); +} +/** + * ノードから直接コンテンツを抽出 + */ +function extractDirectContent(node: VueAstNode): string | null { + if (!node.content) return null; + + const content = typeof node.content === 'string' + ? node.content.trim() + : (node.content.content ? node.content.content.trim() : null); + + if (!content) return null; + + logger.info(`Direct node content found: ${content}`); + + // Mustache構文のチェック + const mustachePattern = /^\s*{{\s*(.*?)\s*}}\s*$/; + const mustacheMatch = content.match(mustachePattern); + + if (mustacheMatch && mustacheMatch[1] && isI18nReference(mustacheMatch[1])) { + const extractedContent = mustacheMatch[1].trim(); + logger.info(`Extracted i18n reference from mustache: ${extractedContent}`); + return extractedContent; + } + + // 直接i18n参照を含む場合 + if (isI18nReference(content)) { + logger.info(`Direct i18n reference found: ${content}`); + return content; + } + + // その他のコンテンツ + return content; +} + +/** + * インターポレーションノード(Mustache)からコンテンツを抽出 + */ +function extractInterpolationContent(children: VueAstNode[]): string | null { + for (const child of children) { + if (child.type === NODE_TYPES.INTERPOLATION) { + logger.info(`Found interpolation node (Mustache): ${JSON.stringify(child.content).substring(0, 100)}...`); + + if (child.content && child.content.type === 4 && child.content.content) { + const content = child.content.content.trim(); + logger.info(`Interpolation content: ${content}`); + + if (isI18nReference(content)) { + return content; + } + } else if (child.content && typeof child.content === 'object') { + // オブジェクト形式のcontentを探索 + logger.info(`Complex interpolation node: ${JSON.stringify(child.content).substring(0, 100)}...`); + + if (child.content.content) { + const content = child.content.content.trim(); + + if (isI18nReference(content)) { + logger.info(`Found i18n reference in complex interpolation: ${content}`); + return content; + } + } + } + } + } + + return null; +} + +/** + * 式ノードからコンテンツを抽出 + */ +function extractExpressionContent(children: VueAstNode[]): string | null { + // i18n.ts. 参照パターンを持つものを優先 + for (const child of children) { + if (child.type === NODE_TYPES.EXPRESSION && child.content) { + const expr = child.content.trim(); + + if (isI18nReference(expr)) { + logger.info(`Found i18n reference in expression node: ${expr}`); + return expr; + } + } + } + + // その他の式 + for (const child of children) { + if (child.type === NODE_TYPES.EXPRESSION && child.content) { + const expr = child.content.trim(); + logger.info(`Found expression: ${expr}`); + return expr; + } + } + + return null; +} + +/** + * テキストノードからコンテンツを抽出 + */ +function extractTextContent(children: VueAstNode[]): string | null { + for (const child of children) { + if (child.type === NODE_TYPES.TEXT && child.content) { + const text = child.content.trim(); + + if (text) { + logger.info(`Found text node: ${text}`); + + // Mustache構文のチェック + const mustachePattern = /^\s*{{\s*(.*?)\s*}}\s*$/; + const mustacheMatch = text.match(mustachePattern); + + if (mustacheMatch && mustacheMatch[1] && isI18nReference(mustacheMatch[1])) { + logger.info(`Extracted i18n ref from text mustache: ${mustacheMatch[1]}`); + return mustacheMatch[1].trim(); + } + + return text; + } + } + } + + return null; +} + +/** + * 子ノードを再帰的に探索してコンテンツを抽出 + */ +function extractNestedContent(children: VueAstNode[]): string | null { + for (const child of children) { + if (child.children && Array.isArray(child.children) && child.children.length > 0) { + const nestedContent = extractElementText(child); + + if (nestedContent) { + logger.info(`Found nested content: ${nestedContent}`); + return nestedContent; + } + } else if (child.type === NODE_TYPES.ELEMENT) { + // childrenがなくても内部を調査 + const nestedContent = extractElementText(child); + + if (nestedContent) { + logger.info(`Found content in childless element: ${nestedContent}`); + return nestedContent; + } + } + } + + return null; +} + + +/** + * SearchLabelとSearchKeywordを探して抽出する関数 + */ +function extractLabelsAndKeywords(nodes: VueAstNode[]): { label: string | null, keywords: any[] } { + let label: string | null = null; + const keywords: any[] = []; + + logger.info(`Extracting labels and keywords from ${nodes.length} nodes`); + + // 再帰的にSearchLabelとSearchKeywordを探索(ネストされたSearchMarkerは処理しない) + function findComponents(nodes: VueAstNode[]) { + for (const node of nodes) { + if (node.type === NODE_TYPES.ELEMENT) { + logger.info(`Checking element: ${node.tag}`); + + // SearchMarkerの場合は、その子要素は別スコープなのでスキップ + if (node.tag === 'SearchMarker') { + logger.info(`Found nested SearchMarker - skipping its content to maintain scope isolation`); + continue; // このSearchMarkerの中身は処理しない (スコープ分離) + } + + // SearchLabelの処理 + if (node.tag === 'SearchLabel') { + logger.info(`Found SearchLabel node, structure: ${JSON.stringify(node).substring(0, 200)}...`); + + // まず完全なノード内容の抽出を試みる + const content = extractElementText(node); + if (content) { + label = content; + logger.info(`SearchLabel content extracted: ${content}`); + } else { + logger.info(`SearchLabel found but extraction failed, trying direct children inspection`); + + // バックアップ: 子直接確認 - type=5のMustacheインターポレーションを重点的に確認 + if (node.children && Array.isArray(node.children)) { + for (const child of node.children) { + // Mustacheインターポレーション + if (child.type === NODE_TYPES.INTERPOLATION && child.content) { + // content内の式を取り出す + const expression = child.content.content || + (child.content.type === 4 ? child.content.content : null) || + JSON.stringify(child.content); + + logger.info(`Interpolation expression: ${expression}`); + if (typeof expression === 'string' && isI18nReference(expression)) { + label = expression.trim(); + logger.info(`Found i18n in interpolation: ${label}`); + break; + } + } + // 式ノード + else if (child.type === NODE_TYPES.EXPRESSION && child.content && isI18nReference(child.content)) { + label = child.content.trim(); + logger.info(`Found i18n in expression: ${label}`); + break; + } + // テキストノードでもMustache構文を探す + else if (child.type === NODE_TYPES.TEXT && child.content) { + const mustacheMatch = child.content.trim().match(/^\s*{{\s*(.*?)\s*}}\s*$/); + if (mustacheMatch && mustacheMatch[1] && isI18nReference(mustacheMatch[1])) { + label = mustacheMatch[1].trim(); + logger.info(`Found i18n in text mustache: ${label}`); + break; + } + } + } + } + } + } + // SearchKeywordの処理 + else if (node.tag === 'SearchKeyword') { + logger.info(`Found SearchKeyword node`); + + // まず完全なノード内容の抽出を試みる + const content = extractElementText(node); + if (content) { + keywords.push(content); + logger.info(`SearchKeyword content extracted: ${content}`); + } else { + logger.info(`SearchKeyword found but extraction failed, trying direct children inspection`); + + // バックアップ: 子直接確認 - type=5のMustacheインターポレーションを重点的に確認 + if (node.children && Array.isArray(node.children)) { + for (const child of node.children) { + // Mustacheインターポレーション + if (child.type === NODE_TYPES.INTERPOLATION && child.content) { + // content内の式を取り出す + const expression = child.content.content || + (child.content.type === 4 ? child.content.content : null) || + JSON.stringify(child.content); + + logger.info(`Keyword interpolation: ${expression}`); + if (typeof expression === 'string' && isI18nReference(expression)) { + const keyword = expression.trim(); + keywords.push(keyword); + logger.info(`Found i18n keyword in interpolation: ${keyword}`); + break; + } + } + // 式ノード + else if (child.type === NODE_TYPES.EXPRESSION && child.content && isI18nReference(child.content)) { + const keyword = child.content.trim(); + keywords.push(keyword); + logger.info(`Found i18n keyword in expression: ${keyword}`); + break; + } + // テキストノードでもMustache構文を探す + else if (child.type === NODE_TYPES.TEXT && child.content) { + const mustacheMatch = child.content.trim().match(/^\s*{{\s*(.*?)\s*}}\s*$/); + if (mustacheMatch && mustacheMatch[1] && isI18nReference(mustacheMatch[1])) { + const keyword = mustacheMatch[1].trim(); + keywords.push(keyword); + logger.info(`Found i18n keyword in text mustache: ${keyword}`); + break; + } + } + } + } + } + } + + // 子要素を再帰的に調査(ただしSearchMarkerは除外) + if (node.children && Array.isArray(node.children)) { + findComponents(node.children); + } + } + } + } + + findComponents(nodes); + + // デバッグ情報 + logger.info(`Extraction completed: label=${label}, keywords=[${keywords.join(', ')}]`); + return { label, keywords }; +} + + +function extractUsageInfoFromTemplateAst( + templateAst: any, + id: string, +): SearchIndexItem[] { + const allMarkers: SearchIndexItem[] = []; + const markerMap = new Map<string, SearchIndexItem>(); + const childrenIds = new Set<string>(); + const normalizedId = id.replace(/\\/g, '/'); + + if (!templateAst) return allMarkers; + + // マーカーの基本情報を収集 + function collectMarkers(node: VueAstNode, parentId: string | null = null) { + if (node.type === 1 && node.tag === 'SearchMarker') { + // マーカーID取得 + const markerIdProp = node.props?.find((p: any) => p.name === 'markerId'); + const markerId = markerIdProp?.value?.content || + node.__markerId; + + // SearchMarkerにマーカーIDがない場合はエラー + if (markerId == null) { + logger.error(`Marker ID not found for node: ${JSON.stringify(node)}`); + throw new Error(`Marker ID not found in file ${id}`); + } + + // マーカー基本情報 + const markerInfo: SearchIndexItem = { + id: markerId, + children: [], + label: '', // デフォルト値 + keywords: [], + }; + + // 静的プロパティを取得 + if (node.props && Array.isArray(node.props)) { + for (const prop of node.props) { + if (prop.type === 6 && prop.name && prop.name !== 'markerId') { + if (prop.name === 'path') markerInfo.path = prop.value?.content || ''; + else if (prop.name === 'icon') markerInfo.icon = prop.value?.content || ''; + else if (prop.name === 'label') markerInfo.label = prop.value?.content || ''; + } + } + } + + // バインドプロパティを取得 + const bindings = extractNodeBindings(node); + if (bindings.path) markerInfo.path = bindings.path; + if (bindings.icon) markerInfo.icon = bindings.icon; + if (bindings.label) markerInfo.label = bindings.label; + if (bindings.children) markerInfo.children = bindings.children; + if (bindings.inlining) { + markerInfo.inlining = bindings.inlining; + logger.info(`Added inlining ${JSON.stringify(bindings.inlining)} to marker ${markerId}`); + } + if (bindings.keywords) { + if (Array.isArray(bindings.keywords)) { + markerInfo.keywords = bindings.keywords; + } else { + markerInfo.keywords = bindings.keywords || []; + } + } + + //pathがない場合はファイルパスを設定 + if (markerInfo.path == null && parentId == null) { + markerInfo.path = normalizedId.match(/.*(\/(admin|settings)\/[^\/]+)\.vue$/)?.[1]; + } + + // SearchLabelとSearchKeywordを抽出 (AST全体を探索) + if (node.children && Array.isArray(node.children)) { + logger.info(`Processing marker ${markerId} for labels and keywords`); + const extracted = extractLabelsAndKeywords(node.children); + + // SearchLabelからのラベル取得は最優先で適用 + if (extracted.label) { + markerInfo.label = extracted.label; + logger.info(`Using extracted label for ${markerId}: ${extracted.label}`); + } else if (markerInfo.label) { + logger.info(`Using existing label for ${markerId}: ${markerInfo.label}`); + } else { + markerInfo.label = 'Unnamed marker'; + logger.info(`No label found for ${markerId}, using default`); + } + + // SearchKeywordからのキーワード取得を追加 + if (extracted.keywords.length > 0) { + const existingKeywords = Array.isArray(markerInfo.keywords) ? + [...markerInfo.keywords] : + (markerInfo.keywords ? [markerInfo.keywords] : []); + + // i18n参照のキーワードは最優先で追加 + const combinedKeywords = [...existingKeywords]; + for (const kw of extracted.keywords) { + combinedKeywords.push(kw); + logger.info(`Added extracted keyword to ${markerId}: ${kw}`); + } + + markerInfo.keywords = combinedKeywords; + } + } + + // マーカーを登録 + markerMap.set(markerId, markerInfo); + allMarkers.push(markerInfo); + + // 親子関係を記録 + if (parentId) { + const parent = markerMap.get(parentId); + if (parent) { + childrenIds.add(markerId); + } + } + + // 子ノードを処理 + if (node.children && Array.isArray(node.children)) { + node.children.forEach((child: VueAstNode) => { + collectMarkers(child, markerId); + }); + } + + return markerId; + } + // SearchMarkerでない場合は再帰的に子ノードを処理 + else if (node.children && Array.isArray(node.children)) { + node.children.forEach((child: VueAstNode) => { + collectMarkers(child, parentId); + }); + } + + return null; + } + + // AST解析開始 + collectMarkers(templateAst); + return allMarkers; +} + +// バインドプロパティの処理を修正する関数 +function extractNodeBindings(node: VueAstNode): Record<keyof SearchIndexItem, any> { + const bindings: Record<string, any> = {}; + + if (!node.props || !Array.isArray(node.props)) return bindings; + + // バインド式を収集 + for (const prop of node.props) { + if (prop.type === 7 && prop.name === 'bind' && prop.arg?.content) { + const propName = prop.arg.content; + const propContent = prop.exp?.content || ''; + + logger.info(`Processing bind prop ${propName}: ${propContent}`); + + // inliningプロパティの処理を追加 + if (propName === 'inlining') { + try { + const content = propContent.trim(); + + // 配列式の場合 + if (content.startsWith('[') && content.endsWith(']')) { + // 配列要素を解析 + const elements = parseArrayExpression(content); + if (elements.length > 0) { + bindings.inlining = elements; + logger.info(`Parsed inlining array: ${JSON5.stringify(elements)}`); + } else { + bindings.inlining = []; + } + } + // 文字列の場合は配列に変換 + else if (content) { + bindings.inlining = [content]; // 単一の値を配列に + logger.info(`Converting inlining to array: [${content}]`); + } + } catch (e) { + logger.error(`Failed to parse inlining binding: ${propContent}`, e); + } + } + // keywordsの特殊処理 + if (propName === 'keywords') { + try { + const content = propContent.trim(); + + // 配列式の場合 + if (content.startsWith('[') && content.endsWith(']')) { + // i18n参照や特殊な式を保持するため、各要素を個別に解析 + const elements = parseArrayExpression(content); + if (elements.length > 0) { + bindings.keywords = elements; + logger.info(`Parsed keywords array: ${JSON5.stringify(elements)}`); + } else { + bindings.keywords = []; + logger.info('Empty keywords array'); + } + } + // その他の式(非配列) + else if (content) { + bindings.keywords = content; // 式をそのまま保持 + logger.info(`Keeping keywords as expression: ${content}`); + } else { + bindings.keywords = []; + logger.info('No keywords provided'); + } + } catch (e) { + logger.error(`Failed to parse keywords binding: ${propContent}`, e); + // エラーが起きても何らかの値を設定 + bindings.keywords = propContent || []; + } + } + // その他のプロパティ + else if (propName === 'label') { + // ラベルの場合も式として保持 + bindings[propName] = propContent; + logger.info(`Set label from bind expression: ${propContent}`); + } + else { + bindings[propName] = propContent; + } + } + } + + return bindings; +} + +// 配列式をパースする補助関数(文字列リテラル処理を改善) +function parseArrayExpression(expr: string): any[] { + try { + // 単純なケースはJSON5でパースを試みる + return JSON5.parse(expr.replace(/'/g, '"')); + } catch (e) { + // 複雑なケース(i18n.ts.xxx などの式を含む場合)は手動パース + logger.info(`Complex array expression, trying manual parsing: ${expr}`); + + // "["と"]"を取り除く + const content = expr.substring(1, expr.length - 1).trim(); + if (!content) return []; + + const result: any[] = []; + let currentItem = ''; + let depth = 0; + let inString = false; + let stringChar = ''; + + // カンマで区切る(ただし文字列内や入れ子の配列内のカンマは無視) + for (let i = 0; i < content.length; i++) { + const char = content[i]; + + if (inString) { + if (char === stringChar && content[i - 1] !== '\\') { + inString = false; + } + currentItem += char; + } else if (char === '"' || char === "'") { + inString = true; + stringChar = char; + currentItem += char; + } else if (char === '[') { + depth++; + currentItem += char; + } else if (char === ']') { + depth--; + currentItem += char; + } else if (char === ',' && depth === 0) { + // 項目の区切りを検出 + const trimmed = currentItem.trim(); + + // 純粋な文字列リテラルの場合、実際の値に変換 + if ((trimmed.startsWith("'") && trimmed.endsWith("'")) || + (trimmed.startsWith('"') && trimmed.endsWith('"'))) { + try { + result.push(JSON5.parse(trimmed)); + } catch (err) { + result.push(trimmed); + } + } else { + // それ以外の式はそのまま(i18n.ts.xxx など) + result.push(trimmed); + } + + currentItem = ''; + } else { + currentItem += char; + } + } + + // 最後の項目を処理 + if (currentItem.trim()) { + const trimmed = currentItem.trim(); + + // 純粋な文字列リテラルの場合、実際の値に変換 + if ((trimmed.startsWith("'") && trimmed.endsWith("'")) || + (trimmed.startsWith('"') && trimmed.endsWith('"'))) { + try { + result.push(JSON5.parse(trimmed)); + } catch (err) { + result.push(trimmed); + } + } else { + // それ以外の式はそのまま(i18n.ts.xxx など) + result.push(trimmed); + } + } + + logger.info(`Parsed complex array expression: ${expr} -> ${JSON.stringify(result)}`); + return result; + } +} + +export async function analyzeVueProps(options: Options & { + transformedCodeCache: Record<string, string>, +}): Promise<void> { + initLogger(options); + + const allMarkers: SearchIndexItem[] = []; + + // 対象ファイルパスを glob で展開 + const filePaths = options.targetFilePaths.reduce<string[]>((acc, filePathPattern) => { + const matchedFiles = glob.sync(filePathPattern); + return [...acc, ...matchedFiles]; + }, []); + + logger.info(`Found ${filePaths.length} matching files to analyze`); + + for (const filePath of filePaths) { + const absolutePath = path.join(process.cwd(), filePath); + const id = absolutePath.replace(/\\/g, '/'); // 絶対パスに変換 + const code = options.transformedCodeCache[id]; // options 経由でキャッシュ参照 + if (!code) { // キャッシュミスの場合 + logger.error(`Error: No cached code found for: ${id}.`); // エラーログ + throw new Error(`No cached code found for: ${id}.`); // エラーを投げる + } + + try { + const { descriptor, errors } = vueSfcParse(options.transformedCodeCache[id], { + filename: filePath, + }); + + if (errors.length > 0) { + logger.error(`Compile Error: ${filePath}, ${errors}`); + continue; // エラーが発生したファイルはスキップ + } + + const fileMarkers = extractUsageInfoFromTemplateAst(descriptor.template?.ast, id); + + if (fileMarkers && fileMarkers.length > 0) { + allMarkers.push(...fileMarkers); // すべてのマーカーを収集 + logger.info(`Successfully extracted ${fileMarkers.length} markers from ${filePath}`); + } else { + logger.info(`No markers found in ${filePath}`); + } + } catch (error) { + logger.error(`Error analyzing file ${filePath}:`, error); + } + } + + // 収集したすべてのマーカー情報を使用 + const analysisResult: AnalysisResult[] = [ + { + filePath: "combined-markers", // すべてのファイルのマーカーを1つのエントリとして扱う + usage: allMarkers, + } + ]; + + outputAnalysisResultAsTS(options.exportFilePath, analysisResult); // すべてのマーカー情報を渡す +} + +interface MarkerRelation { + parentId?: string; + markerId: string; + node: VueAstNode; +} + +async function processVueFile( + code: string, + id: string, + options: Options, + transformedCodeCache: Record<string, string> +): Promise<{ + code: string, + map: any, + transformedCodeCache: Record<string, string> +}> { + const normalizedId = id.replace(/\\/g, '/'); // ファイルパスを正規化 + // すでにキャッシュに存在する場合は、そのまま返す + if (transformedCodeCache[normalizedId] && transformedCodeCache[normalizedId].includes('markerId=')) { + logger.info(`Using cached version for ${id}`); + return { + code: transformedCodeCache[normalizedId], + map: null, + transformedCodeCache + }; + } + + const s = new MagicString(code); // magic-string のインスタンスを作成 + const parsed = vueSfcParse(code, { filename: id }); + if (!parsed.descriptor.template) { + return { + code, + map: null, + transformedCodeCache + }; + } + const ast = parsed.descriptor.template.ast; // テンプレート AST を取得 + const markerRelations: MarkerRelation[] = []; // MarkerRelation 配列を初期化 + + if (ast) { + function traverse(node: any, currentParent?: any) { + if (node.type === 1 && node.tag === 'SearchMarker') { + // 行番号はコード先頭からの改行数で取得 + const lineNumber = code.slice(0, node.loc.start.offset).split('\n').length; + // ファイルパスと行番号からハッシュ値を生成 + // この際実行環境で差が出ないようにファイルパスを正規化 + const idKey = id.replace(/\\/g, '/').split('packages/frontend/')[1] + const generatedMarkerId = toBase62(hash(`${idKey}:${lineNumber}`)); + + const props = node.props || []; + const hasMarkerIdProp = props.some((prop: any) => prop.type === 6 && prop.name === 'markerId'); + const nodeMarkerId = hasMarkerIdProp + ? props.find((prop: any) => prop.type === 6 && prop.name === 'markerId')?.value?.content as string + : generatedMarkerId; + node.__markerId = nodeMarkerId; + + // 子マーカーの場合、親ノードに __children を設定しておく + if (currentParent && currentParent.type === 1 && currentParent.tag === 'SearchMarker') { + currentParent.__children = currentParent.__children || []; + currentParent.__children.push(nodeMarkerId); + } + + const parentMarkerId = currentParent && currentParent.__markerId; + markerRelations.push({ + parentId: parentMarkerId, + markerId: nodeMarkerId, + node: node, + }); + + if (!hasMarkerIdProp) { + const nodeStart = node.loc.start.offset; + let endOfStartTag; + + if (node.children && node.children.length > 0) { + // 子要素がある場合、最初の子要素の開始位置を基準にする + endOfStartTag = code.lastIndexOf('>', node.children[0].loc.start.offset); + } else if (node.loc.end.offset > nodeStart) { + // 子要素がない場合、自身の終了位置から逆算 + const nodeSource = code.substring(nodeStart, node.loc.end.offset); + // 自己終了タグか通常の終了タグかを判断 + if (nodeSource.includes('/>')) { + endOfStartTag = code.indexOf('/>', nodeStart) - 1; + } else { + endOfStartTag = code.indexOf('>', nodeStart); + } + } + + if (endOfStartTag !== undefined && endOfStartTag !== -1) { + // markerId が既に存在しないことを確認 + const tagText = code.substring(nodeStart, endOfStartTag + 1); + const markerIdRegex = /\s+markerId\s*=\s*["'][^"']*["']/; + + if (!markerIdRegex.test(tagText)) { + s.appendRight(endOfStartTag, ` markerId="${generatedMarkerId}" data-in-app-search-marker-id="${generatedMarkerId}"`); + logger.info(`Adding markerId="${generatedMarkerId}" to ${id}:${lineNumber}`); + } else { + logger.info(`markerId already exists in ${id}:${lineNumber}`); + } + } + } + } + + const newParent = node.type === 1 && node.tag === 'SearchMarker' ? node : currentParent; + if (node.children && Array.isArray(node.children)) { + node.children.forEach(child => traverse(child, newParent)); + } + } + + traverse(ast); // AST を traverse (1段階目: ID 生成と親子関係記録) + + // 2段階目: :children 属性の追加 + // 最初に親マーカーごとに子マーカーIDを集約する処理を追加 + const parentChildrenMap = new Map<string, string[]>(); + + // 1. まず親ごとのすべての子マーカーIDを収集 + markerRelations.forEach(relation => { + if (relation.parentId) { + if (!parentChildrenMap.has(relation.parentId)) { + parentChildrenMap.set(relation.parentId, []); + } + parentChildrenMap.get(relation.parentId)?.push(relation.markerId); + } + }); + + // 2. 親ごとにまとめて :children 属性を処理 + for (const [parentId, childIds] of parentChildrenMap.entries()) { + const parentRelation = markerRelations.find(r => r.markerId === parentId); + if (!parentRelation || !parentRelation.node) continue; + + const parentNode = parentRelation.node; + const childrenProp = parentNode.props?.find((prop: any) => prop.type === 7 && prop.name === 'bind' && prop.arg?.content === 'children'); + + // 親ノードの開始位置を特定 + const parentNodeStart = parentNode.loc!.start.offset; + const endOfParentStartTag = parentNode.children && parentNode.children.length > 0 + ? code.lastIndexOf('>', parentNode.children[0].loc!.start.offset) + : code.indexOf('>', parentNodeStart); + + if (endOfParentStartTag === -1) continue; + + // 親タグのテキストを取得 + const parentTagText = code.substring(parentNodeStart, endOfParentStartTag + 1); + + if (childrenProp) { + // AST で :children 属性が検出された場合、それを更新 + try { + const childrenStart = code.indexOf('[', childrenProp.exp!.loc.start.offset); + const childrenEnd = code.indexOf(']', childrenProp.exp!.loc.start.offset); + if (childrenStart !== -1 && childrenEnd !== -1) { + const childrenArrayStr = code.slice(childrenStart, childrenEnd + 1); + let childrenArray = JSON5.parse(childrenArrayStr.replace(/'/g, '"')); + + // 新しいIDを追加(重複は除外) + const newIds = childIds.filter(id => !childrenArray.includes(id)); + if (newIds.length > 0) { + childrenArray = [...childrenArray, ...newIds]; + const updatedChildrenArrayStr = JSON5.stringify(childrenArray).replace(/"/g, "'"); + s.overwrite(childrenStart, childrenEnd + 1, updatedChildrenArrayStr); + logger.info(`Added ${newIds.length} child markerIds to existing :children in ${id}`); + } + } + } catch (e) { + logger.error('Error updating :children attribute:', e); + } + } else { + // AST では検出されなかった場合、タグテキストを調べる + const childrenRegex = /:children\s*=\s*["']\[(.*?)\]["']/; + const childrenMatch = parentTagText.match(childrenRegex); + + if (childrenMatch) { + // テキストから :children 属性値を解析して更新 + try { + const childrenContent = childrenMatch[1]; + const childrenArrayStr = `[${childrenContent}]`; + const childrenArray = JSON5.parse(childrenArrayStr.replace(/'/g, '"')); + + // 新しいIDを追加(重複は除外) + const newIds = childIds.filter(id => !childrenArray.includes(id)); + if (newIds.length > 0) { + childrenArray.push(...newIds); + + // :children="[...]" の位置を特定して上書き + const attrStart = parentTagText.indexOf(':children='); + if (attrStart > -1) { + const attrValueStart = parentTagText.indexOf('[', attrStart); + const attrValueEnd = parentTagText.indexOf(']', attrValueStart) + 1; + if (attrValueStart > -1 && attrValueEnd > -1) { + const absoluteStart = parentNodeStart + attrValueStart; + const absoluteEnd = parentNodeStart + attrValueEnd; + const updatedArrayStr = JSON5.stringify(childrenArray).replace(/"/g, "'"); + s.overwrite(absoluteStart, absoluteEnd, updatedArrayStr); + logger.info(`Updated existing :children in tag text for ${id}`); + } + } + } + } catch (e) { + logger.error('Error updating :children in tag text:', e); + } + } else { + // :children 属性がまだない場合、新規作成 + s.appendRight(endOfParentStartTag, ` :children="${JSON5.stringify(childIds).replace(/"/g, "'")}"`); + logger.info(`Created new :children attribute with ${childIds.length} markerIds in ${id}`); + } + } + } + } + + const transformedCode = s.toString(); // 変換後のコードを取得 + transformedCodeCache[normalizedId] = transformedCode; // 変換後のコードをキャッシュに保存 + + return { + code: transformedCode, // 変更後のコードを返す + map: s.generateMap({ source: id, includeContent: true }), // ソースマップも生成 (sourceMap: true が必要) + transformedCodeCache // キャッシュも返す + }; +} + + +// Rollup プラグインとして export +export default function pluginCreateSearchIndex(options: Options): Plugin { + let transformedCodeCache: Record<string, string> = {}; // キャッシュオブジェクトをプラグインスコープで定義 + const isDevServer = process.env.NODE_ENV === 'development'; // 開発サーバーかどうか + + initLogger(options); // ロガーを初期化 + + return { + name: 'createSearchIndex', + enforce: 'pre', + + async buildStart() { + if (!isDevServer) { + return; + } + + const filePaths = options.targetFilePaths.reduce<string[]>((acc, filePathPattern) => { + const matchedFiles = glob.sync(filePathPattern); + return [...acc, ...matchedFiles]; + }, []); + + for (const filePath of filePaths) { + const id = path.resolve(filePath); // 絶対パスに変換 + const code = fs.readFileSync(filePath, 'utf-8'); // ファイル内容を読み込む + const { transformedCodeCache: newCache } = await processVueFile(code, id, options, transformedCodeCache); // processVueFile 関数を呼び出す + transformedCodeCache = newCache; // キャッシュを更新 + } + + await analyzeVueProps({ ...options, transformedCodeCache }); // 開発サーバー起動時にも analyzeVueProps を実行 + }, + + async transform(code, id) { + if (!id.endsWith('.vue')) { + return; + } + + // targetFilePaths にマッチするファイルのみ処理を行う + // glob パターンでマッチング + let isMatch = false; // isMatch の初期値を false に設定 + for (const pattern of options.targetFilePaths) { // パターンごとにマッチング確認 + const globbedFiles = glob.sync(pattern); + for (const globbedFile of globbedFiles) { + const normalizedGlobbedFile = path.resolve(globbedFile); // glob 結果を絶対パスに + const normalizedId = path.resolve(id); // id を絶対パスに + if (normalizedGlobbedFile === normalizedId) { // 絶対パス同士で比較 + isMatch = true; + break; // マッチしたらループを抜ける + } + } + if (isMatch) break; // いずれかのパターンでマッチしたら、outer loop も抜ける + } + + + if (!isMatch) { + return; + } + + const transformed = await processVueFile(code, id, options, transformedCodeCache); + transformedCodeCache = transformed.transformedCodeCache; // キャッシュを更新 + if (isDevServer) { + await analyzeVueProps({ ...options, transformedCodeCache }); // analyzeVueProps を呼び出す + } + return transformed; + }, + + async writeBundle() { + await analyzeVueProps({ ...options, transformedCodeCache }); // ビルド時にも analyzeVueProps を実行 + }, + }; +} + +// i18n参照を検出するためのヘルパー関数を追加 +function isI18nReference(text: string | null | undefined): boolean { + if (!text) return false; + // ドット記法(i18n.ts.something) + const dotPattern = /i18n\.ts\.\w+/; + // ブラケット記法(i18n.ts['something']) + const bracketPattern = /i18n\.ts\[['"][^'"]+['"]\]/; + return dotPattern.test(text) || bracketPattern.test(text); +} diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 2bf7728d0a..88b57e57ab 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -51,6 +51,7 @@ "insert-text-at-cursor": "0.3.0", "is-file-animated": "1.0.2", "json5": "2.2.3", + "magic-string": "0.30.17", "matter-js": "0.20.0", "mfm-js": "0.24.0", "misskey-bubble-game": "workspace:*", @@ -72,30 +73,30 @@ "typescript": "5.8.2", "uuid": "11.1.0", "v-code-diff": "1.13.1", - "vite": "6.2.0", + "vite": "6.2.1", "vue": "3.5.13", "vuedraggable": "next" }, "devDependencies": { "@misskey-dev/summaly": "5.2.0", - "@storybook/addon-actions": "8.6.3", - "@storybook/addon-essentials": "8.6.3", - "@storybook/addon-interactions": "8.6.3", - "@storybook/addon-links": "8.6.3", - "@storybook/addon-mdx-gfm": "8.6.3", - "@storybook/addon-storysource": "8.6.3", - "@storybook/blocks": "8.6.3", - "@storybook/components": "8.6.3", - "@storybook/core-events": "8.6.3", - "@storybook/manager-api": "8.6.3", - "@storybook/preview-api": "8.6.3", - "@storybook/react": "8.6.3", - "@storybook/react-vite": "8.6.3", - "@storybook/test": "8.6.3", - "@storybook/theming": "8.6.3", - "@storybook/types": "8.6.3", - "@storybook/vue3": "8.6.3", - "@storybook/vue3-vite": "8.6.3", + "@storybook/addon-actions": "8.6.4", + "@storybook/addon-essentials": "8.6.4", + "@storybook/addon-interactions": "8.6.4", + "@storybook/addon-links": "8.6.4", + "@storybook/addon-mdx-gfm": "8.6.4", + "@storybook/addon-storysource": "8.6.4", + "@storybook/blocks": "8.6.4", + "@storybook/components": "8.6.4", + "@storybook/core-events": "8.6.4", + "@storybook/manager-api": "8.6.4", + "@storybook/preview-api": "8.6.4", + "@storybook/react": "8.6.4", + "@storybook/react-vite": "8.6.4", + "@storybook/test": "8.6.4", + "@storybook/theming": "8.6.4", + "@storybook/types": "8.6.4", + "@storybook/vue3": "8.6.4", + "@storybook/vue3-vite": "8.6.4", "@testing-library/vue": "8.1.0", "@types/canvas-confetti": "1.9.0", "@types/estree": "1.0.6", @@ -110,15 +111,15 @@ "@types/ws": "8.18.0", "@typescript-eslint/eslint-plugin": "8.26.0", "@typescript-eslint/parser": "8.26.0", - "@vitest/coverage-v8": "3.0.7", + "@vitest/coverage-v8": "3.0.8", "@vue/runtime-core": "3.5.13", - "acorn": "8.14.0", + "acorn": "8.14.1", "cross-env": "7.0.3", "cypress": "14.1.0", "eslint-plugin-import": "2.31.0", - "eslint-plugin-vue": "9.33.0", + "eslint-plugin-vue": "10.0.0", "fast-glob": "3.3.3", - "happy-dom": "17.2.2", + "happy-dom": "17.3.0", "intersection-observer": "0.12.2", "micromatch": "4.0.8", "msw": "2.7.3", @@ -129,13 +130,13 @@ "react-dom": "19.0.0", "seedrandom": "3.0.5", "start-server-and-test": "2.0.10", - "storybook": "8.6.3", + "storybook": "8.6.4", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "vite-plugin-turbosnap": "1.0.3", - "vitest": "3.0.7", + "vitest": "3.0.8", "vitest-fetch-mock": "0.4.5", "vue-component-type-helpers": "2.2.8", - "vue-eslint-parser": "9.4.3", + "vue-eslint-parser": "10.1.1", "vue-tsc": "2.2.8" } } diff --git a/idea/MkDisableSection.vue b/packages/frontend/src/components/MkDisableSection.vue index 360705071b..bd7ecf225d 100644 --- a/idea/MkDisableSection.vue +++ b/packages/frontend/src/components/MkDisableSection.vue @@ -24,7 +24,8 @@ defineProps<{ } .disabled { - opacity: 0.7; + opacity: 0.3; + filter: saturate(0.5); } .cover { @@ -34,7 +35,7 @@ defineProps<{ width: 100%; height: 100%; cursor: not-allowed; - --color: color(from var(--MI_THEME-error) srgb r g b / 0.25); + --color: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05)); background-size: auto auto; background-image: repeating-linear-gradient(135deg, transparent, transparent 10px, var(--color) 4px, var(--color) 14px); } diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue index e725d2a15d..c3fc1961eb 100644 --- a/packages/frontend/src/components/MkPageWindow.vue +++ b/packages/frontend/src/components/MkPageWindow.vue @@ -91,6 +91,14 @@ const buttonsRight = computed(() => { }); const reloadCount = ref(0); +function getSearchMarker(path: string) { + const hash = path.split('#')[1]; + if (hash == null) return null; + return hash; +} + +const searchMarkerId = ref<string | null>(getSearchMarker(props.initialPath)); + windowRouter.addListener('push', ctx => { history.value.push({ path: ctx.path, key: ctx.key }); }); @@ -101,7 +109,8 @@ windowRouter.addListener('replace', ctx => { }); windowRouter.addListener('change', ctx => { - console.log('windowRouter: change', ctx.path); + if (_DEV_) console.log('windowRouter: change', ctx.path); + searchMarkerId.value = getSearchMarker(ctx.path); analytics.page({ path: ctx.path, title: ctx.path, @@ -111,6 +120,7 @@ windowRouter.addListener('change', ctx => { windowRouter.init(); provide('router', windowRouter); +provide('inAppSearchMarkerId', searchMarkerId); provideMetadataReceiver((metadataGetter) => { const info = metadataGetter(); pageMetadata.value = info; diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue index 397aa68ed6..d8dec3aa2f 100644 --- a/packages/frontend/src/components/MkSuperMenu.vue +++ b/packages/frontend/src/components/MkSuperMenu.vue @@ -4,27 +4,60 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="rrevdjwu" :class="{ grid }"> - <div v-for="group in def" class="group"> - <div v-if="group.title" class="title">{{ group.title }}</div> +<div ref="rootEl" class="rrevdjwu" :class="{ grid }"> + <MkInput + v-model="search" + :placeholder="i18n.ts.search" + type="search" + style="margin-bottom: 16px;" + @keydown="searchOnKeyDown" + > + <template #prefix><i class="ti ti-search"></i></template> + </MkInput> - <div class="items"> - <template v-for="(item, i) in group.items"> - <a v-if="item.type === 'a'" :href="item.href" :target="item.target" class="_button item" :class="{ danger: item.danger, active: item.active }"> - <span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> - <span class="text">{{ item.text }}</span> - </a> - <button v-else-if="item.type === 'button'" class="_button item" :class="{ danger: item.danger, active: item.active }" :disabled="item.active" @click="ev => item.action(ev)"> - <span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> - <span class="text">{{ item.text }}</span> - </button> - <MkA v-else :to="item.to" class="_button item" :class="{ danger: item.danger, active: item.active }"> - <span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> - <span class="text">{{ item.text }}</span> - </MkA> - </template> + <template v-if="search == ''"> + <div v-for="group in def" class="group"> + <div v-if="group.title" class="title">{{ group.title }}</div> + + <div class="items"> + <template v-for="(item, i) in group.items"> + <a v-if="item.type === 'a'" :href="item.href" :target="item.target" class="_button item" :class="{ danger: item.danger, active: item.active }"> + <span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> + <span class="text">{{ item.text }}</span> + </a> + <button v-else-if="item.type === 'button'" class="_button item" :class="{ danger: item.danger, active: item.active }" :disabled="item.active" @click="ev => item.action(ev)"> + <span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> + <span class="text">{{ item.text }}</span> + </button> + <MkA v-else :to="item.to" class="_button item" :class="{ danger: item.danger, active: item.active }"> + <span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> + <span class="text">{{ item.text }}</span> + </MkA> + </template> + </div> + </div> + </template> + <template v-else> + <div v-for="item, index in searchResult"> + <MkA + :to="item.path + '#' + item.id" + class="_button searchResultItem" + :class="{ selected: searchSelectedIndex !== null && searchSelectedIndex === index }" + > + <span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> + <span class="text"> + <template v-if="item.isRoot"> + {{ item.label }} + </template> + <template v-else> + <span style="opacity: 0.7; font-size: 90%;">{{ item.parentLabels.join(' > ') }}</span> + <br> + <span>{{ item.label }}</span> + </template> + </span> + </MkA> </div> - </div> + </template> </div> </template> @@ -58,10 +91,98 @@ export type SuperMenuDef = { </script> <script lang="ts" setup> -defineProps<{ +import { useTemplateRef, ref, watch, nextTick } from 'vue'; +import type { SearchIndexItem } from '@/scripts/autogen/settings-search-index.js'; +import MkInput from '@/components/MkInput.vue'; +import { i18n } from '@/i18n.js'; +import { getScrollContainer } from '@@/js/scroll.js'; +import { useRouter } from '@/router/supplier.js'; + +const props = defineProps<{ def: SuperMenuDef[]; grid?: boolean; + searchIndex: SearchIndexItem[]; }>(); + +const router = useRouter(); +const rootEl = useTemplateRef('rootEl'); + +const search = ref(''); +const searchSelectedIndex = ref<null | number>(null); +const searchResult = ref<{ + id: string; + path: string; + label: string; + icon?: string; + isRoot: boolean; + parentLabels: string[]; +}[]>([]); + +watch(search, (value) => { + searchResult.value = []; + searchSelectedIndex.value = null; + + if (value === '') { + return; + } + + const dive = (items: SearchIndexItem[], parents: SearchIndexItem[] = []) => { + for (const item of items) { + const matched = + item.label.includes(value.toLowerCase()) || + item.keywords.some((x) => x.toLowerCase().includes(value.toLowerCase())); + + if (matched) { + searchResult.value.push({ + id: item.id, + path: item.path ?? parents.find((x) => x.path != null)?.path, + label: item.label, + parentLabels: parents.map((x) => x.label).toReversed(), + icon: item.icon ?? parents.find((x) => x.icon != null)?.icon, + isRoot: parents.length === 0, + }); + } + + if (item.children) { + dive(item.children, [item, ...parents]); + } + } + }; + + dive(props.searchIndex); +}); + +function searchOnKeyDown(ev: KeyboardEvent) { + if (ev.isComposing) return; + + if (ev.key === 'Enter' && searchSelectedIndex.value != null) { + ev.preventDefault(); + router.push(searchResult.value[searchSelectedIndex.value].path + '#' + searchResult.value[searchSelectedIndex.value].id); + } else if (ev.key === 'ArrowDown') { + ev.preventDefault(); + const current = searchSelectedIndex.value ?? -1; + searchSelectedIndex.value = current + 1 >= searchResult.value.length ? 0 : current + 1; + } else if (ev.key === 'ArrowUp') { + ev.preventDefault(); + const current = searchSelectedIndex.value ?? 0; + searchSelectedIndex.value = current - 1 < 0 ? searchResult.value.length - 1 : current - 1; + } + + if (ev.key === 'ArrowDown' || ev.key === 'ArrowUp') { + nextTick(() => { + if (!rootEl.value) return; + const selectedEl = rootEl.value.querySelector<HTMLElement>('.searchResultItem.selected'); + if (selectedEl != null) { + const scrollContainer = getScrollContainer(selectedEl); + if (!scrollContainer) return; + scrollContainer.scrollTo({ + top: selectedEl.offsetTop - scrollContainer.clientHeight / 2 + selectedEl.clientHeight / 2, + behavior: 'instant', + }); + } + }); + } +} </script> <style lang="scss" scoped> @@ -184,5 +305,52 @@ defineProps<{ } } } + + .searchResultItem { + display: flex; + align-items: center; + width: 100%; + box-sizing: border-box; + padding: 9px 16px 9px 8px; + border-radius: 9px; + font-size: 0.9em; + + &:hover { + text-decoration: none; + background: var(--MI_THEME-panelHighlight); + } + + &.selected { + outline: 2px solid var(--MI_THEME-focus); + } + + &:focus-visible, + &.selected { + outline-offset: -2px; + } + + &.active { + color: var(--MI_THEME-accent); + background: var(--MI_THEME-accentedBg); + } + + &.danger { + color: var(--MI_THEME-error); + } + + > .icon { + width: 32px; + margin-right: 2px; + flex-shrink: 0; + text-align: center; + opacity: 0.8; + } + + > .text { + white-space: normal; + padding-right: 12px; + flex-shrink: 1; + } + } } </style> diff --git a/packages/frontend/src/components/global/SearchKeyword.vue b/packages/frontend/src/components/global/SearchKeyword.vue new file mode 100644 index 0000000000..27a284faf0 --- /dev/null +++ b/packages/frontend/src/components/global/SearchKeyword.vue @@ -0,0 +1,14 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<slot></slot> +</template> + +<script lang="ts" setup> +</script> + +<style lang="scss" module> +</style> diff --git a/packages/frontend/src/components/global/SearchLabel.vue b/packages/frontend/src/components/global/SearchLabel.vue new file mode 100644 index 0000000000..27a284faf0 --- /dev/null +++ b/packages/frontend/src/components/global/SearchLabel.vue @@ -0,0 +1,14 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<slot></slot> +</template> + +<script lang="ts" setup> +</script> + +<style lang="scss" module> +</style> diff --git a/packages/frontend/src/components/global/SearchMarker.vue b/packages/frontend/src/components/global/SearchMarker.vue new file mode 100644 index 0000000000..c5ec626cf4 --- /dev/null +++ b/packages/frontend/src/components/global/SearchMarker.vue @@ -0,0 +1,116 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div ref="root" :class="[$style.root, { [$style.highlighted]: highlighted }]"> + <slot></slot> +</div> +</template> + +<script lang="ts" setup> +import { + onActivated, + onDeactivated, + onMounted, + onBeforeUnmount, + watch, + computed, + ref, + useTemplateRef, + inject, +} from 'vue'; +import type { Ref } from 'vue'; + +const props = defineProps<{ + markerId?: string; + label?: string; + icon?: string; + keywords?: string[]; + children?: string[]; + inlining?: string[]; +}>(); + +const rootEl = useTemplateRef('root'); +const rootElMutationObserver = new MutationObserver(() => { + checkChildren(); +}); +const injectedSearchMarkerId = inject<Ref<string | null>>('inAppSearchMarkerId'); +const searchMarkerId = computed(() => injectedSearchMarkerId?.value ?? window.location.hash.slice(1)); +const highlighted = ref(props.markerId === searchMarkerId.value); + +function checkChildren() { + if (props.children?.includes(searchMarkerId.value)) { + const el = document.querySelector(`[data-in-app-search-marker-id="${searchMarkerId.value}"]`); + highlighted.value = el == null; + } +} + +watch([ + searchMarkerId, + () => props.children, +], () => { + if (props.children != null && props.children.length > 0) { + checkChildren(); + } +}, { flush: 'post' }); + +function init() { + checkChildren(); + + if (highlighted.value) { + rootEl.value?.scrollIntoView({ + behavior: 'smooth', + block: 'center', + }); + } + + if (rootEl.value != null) { + rootElMutationObserver.observe(rootEl.value, { + childList: true, + subtree: true, + }); + } +} + +function dispose() { + rootElMutationObserver.disconnect(); +} + +onMounted(init); +onActivated(init); +onDeactivated(dispose); +onBeforeUnmount(dispose); +</script> + +<style lang="scss" module> +.root { + position: relative; +} + +.highlighted { + &::after { + content: ''; + position: absolute; + top: -8px; + left: -8px; + width: calc(100% + 16px); + height: calc(100% + 16px); + border-radius: 6px; + animation: blink 1s 3.5; + pointer-events: none; + } +} + +@keyframes blink { + 0%, 100% { + background: color(from var(--MI_THEME-accent) srgb r g b / 0.05); + border: 1px solid color(from var(--MI_THEME-accent) srgb r g b / 0.7); + } + 50% { + background: transparent; + border: 1px solid transparent; + } +} +</style> diff --git a/packages/frontend/src/components/index.ts b/packages/frontend/src/components/index.ts index 0252bf0252..ebbad3e5b8 100644 --- a/packages/frontend/src/components/index.ts +++ b/packages/frontend/src/components/index.ts @@ -3,8 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import type { App } from 'vue'; - import Mfm from './global/MkMfm.js'; import MkA from './global/MkA.vue'; import MkAcct from './global/MkAcct.vue'; @@ -26,6 +24,11 @@ import MkSpacer from './global/MkSpacer.vue'; import MkFooterSpacer from './global/MkFooterSpacer.vue'; import MkStickyContainer from './global/MkStickyContainer.vue'; import MkLazy from './global/MkLazy.vue'; +import SearchMarker from './global/SearchMarker.vue'; +import SearchLabel from './global/SearchLabel.vue'; +import SearchKeyword from './global/SearchKeyword.vue'; + +import type { App } from 'vue'; export default function(app: App) { for (const [key, value] of Object.entries(components)) { @@ -55,6 +58,9 @@ export const components = { MkFooterSpacer: MkFooterSpacer, MkStickyContainer: MkStickyContainer, MkLazy: MkLazy, + SearchMarker: SearchMarker, + SearchLabel: SearchLabel, + SearchKeyword: SearchKeyword, }; declare module '@vue/runtime-core' { @@ -80,5 +86,8 @@ declare module '@vue/runtime-core' { MkFooterSpacer: typeof MkFooterSpacer; MkStickyContainer: typeof MkStickyContainer; MkLazy: typeof MkLazy; + SearchMarker: typeof SearchMarker; + SearchLabel: typeof SearchLabel; + SearchKeyword: typeof SearchKeyword; } } diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue index 776f59dda3..806599e801 100644 --- a/packages/frontend/src/pages/settings/2fa.vue +++ b/packages/frontend/src/pages/settings/2fa.vue @@ -4,74 +4,82 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<FormSection :first="first"> - <template #label>{{ i18n.ts['2fa'] }}</template> +<SearchMarker markerId="2fa" :keywords="['2fa']"> + <FormSection :first="first"> + <template #label><SearchLabel>{{ i18n.ts['2fa'] }}</SearchLabel></template> - <div v-if="$i" class="_gaps_s"> - <MkInfo v-if="$i.twoFactorEnabled && $i.twoFactorBackupCodesStock === 'partial'" warn> - {{ i18n.ts._2fa.backupCodeUsedWarning }} - </MkInfo> - <MkInfo v-if="$i.twoFactorEnabled && $i.twoFactorBackupCodesStock === 'none'" warn> - {{ i18n.ts._2fa.backupCodesExhaustedWarning }} - </MkInfo> + <div v-if="$i" class="_gaps_s"> + <MkInfo v-if="$i.twoFactorEnabled && $i.twoFactorBackupCodesStock === 'partial'" warn> + {{ i18n.ts._2fa.backupCodeUsedWarning }} + </MkInfo> + <MkInfo v-if="$i.twoFactorEnabled && $i.twoFactorBackupCodesStock === 'none'" warn> + {{ i18n.ts._2fa.backupCodesExhaustedWarning }} + </MkInfo> - <MkFolder :defaultOpen="true"> - <template #icon><i class="ti ti-shield-lock"></i></template> - <template #label>{{ i18n.ts.totp }}</template> - <template #caption>{{ i18n.ts.totpDescription }}</template> - <template #suffix><i v-if="$i.twoFactorEnabled" class="ti ti-check" style="color: var(--MI_THEME-success)"></i></template> + <SearchMarker :keywords="['totp', 'app']"> + <MkFolder :defaultOpen="true"> + <template #icon><i class="ti ti-shield-lock"></i></template> + <template #label><SearchLabel>{{ i18n.ts.totp }}</SearchLabel></template> + <template #caption><SearchKeyword>{{ i18n.ts.totpDescription }}</SearchKeyword></template> + <template #suffix><i v-if="$i.twoFactorEnabled" class="ti ti-check" style="color: var(--MI_THEME-success)"></i></template> - <div v-if="$i.twoFactorEnabled" class="_gaps_s"> - <div v-text="i18n.ts._2fa.alreadyRegistered"/> - <template v-if="$i.securityKeysList.length > 0"> - <MkButton @click="renewTOTP">{{ i18n.ts._2fa.renewTOTP }}</MkButton> - <MkInfo>{{ i18n.ts._2fa.whyTOTPOnlyRenew }}</MkInfo> - </template> - <MkButton v-else danger @click="unregisterTOTP">{{ i18n.ts.unregister }}</MkButton> - </div> + <div v-if="$i.twoFactorEnabled" class="_gaps_s"> + <div v-text="i18n.ts._2fa.alreadyRegistered"/> + <template v-if="$i.securityKeysList.length > 0"> + <MkButton @click="renewTOTP">{{ i18n.ts._2fa.renewTOTP }}</MkButton> + <MkInfo>{{ i18n.ts._2fa.whyTOTPOnlyRenew }}</MkInfo> + </template> + <MkButton v-else danger @click="unregisterTOTP">{{ i18n.ts.unregister }}</MkButton> + </div> - <div v-else-if="!$i.twoFactorEnabled" class="_gaps_s"> - <MkButton primary gradate @click="registerTOTP">{{ i18n.ts._2fa.registerTOTP }}</MkButton> - <MkLink url="https://misskey-hub.net/docs/for-users/stepped-guides/how-to-enable-2fa/" target="_blank"><i class="ti ti-help-circle"></i> {{ i18n.ts.learnMore }}</MkLink> - </div> - </MkFolder> + <div v-else-if="!$i.twoFactorEnabled" class="_gaps_s"> + <MkButton primary gradate @click="registerTOTP">{{ i18n.ts._2fa.registerTOTP }}</MkButton> + <MkLink url="https://misskey-hub.net/docs/for-users/stepped-guides/how-to-enable-2fa/" target="_blank"><i class="ti ti-help-circle"></i> {{ i18n.ts.learnMore }}</MkLink> + </div> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-key"></i></template> - <template #label>{{ i18n.ts.securityKeyAndPasskey }}</template> - <div class="_gaps_s"> - <MkInfo> - {{ i18n.ts._2fa.securityKeyInfo }} - </MkInfo> + <SearchMarker :keywords="['security', 'key', 'passkey']"> + <MkFolder> + <template #icon><i class="ti ti-key"></i></template> + <template #label><SearchLabel>{{ i18n.ts.securityKeyAndPasskey }}</SearchLabel></template> + <div class="_gaps_s"> + <MkInfo> + {{ i18n.ts._2fa.securityKeyInfo }} + </MkInfo> - <MkInfo v-if="!webAuthnSupported()" warn> - {{ i18n.ts._2fa.securityKeyNotSupported }} - </MkInfo> + <MkInfo v-if="!webAuthnSupported()" warn> + {{ i18n.ts._2fa.securityKeyNotSupported }} + </MkInfo> - <MkInfo v-else-if="webAuthnSupported() && !$i.twoFactorEnabled" warn> - {{ i18n.ts._2fa.registerTOTPBeforeKey }} - </MkInfo> + <MkInfo v-else-if="webAuthnSupported() && !$i.twoFactorEnabled" warn> + {{ i18n.ts._2fa.registerTOTPBeforeKey }} + </MkInfo> - <template v-else> - <MkButton primary @click="addSecurityKey">{{ i18n.ts._2fa.registerSecurityKey }}</MkButton> - <MkFolder v-for="key in $i.securityKeysList" :key="key.id"> - <template #label>{{ key.name }}</template> - <template #suffix><I18n :src="i18n.ts.lastUsedAt"><template #t><MkTime :time="key.lastUsed"/></template></I18n></template> - <div class="_buttons"> - <MkButton @click="renameKey(key)"><i class="ti ti-forms"></i> {{ i18n.ts.rename }}</MkButton> - <MkButton danger @click="unregisterKey(key)"><i class="ti ti-trash"></i> {{ i18n.ts.unregister }}</MkButton> - </div> - </MkFolder> - </template> - </div> - </MkFolder> + <template v-else> + <MkButton primary @click="addSecurityKey">{{ i18n.ts._2fa.registerSecurityKey }}</MkButton> + <MkFolder v-for="key in $i.securityKeysList" :key="key.id"> + <template #label>{{ key.name }}</template> + <template #suffix><I18n :src="i18n.ts.lastUsedAt"><template #t><MkTime :time="key.lastUsed"/></template></I18n></template> + <div class="_buttons"> + <MkButton @click="renameKey(key)"><i class="ti ti-forms"></i> {{ i18n.ts.rename }}</MkButton> + <MkButton danger @click="unregisterKey(key)"><i class="ti ti-trash"></i> {{ i18n.ts.unregister }}</MkButton> + </div> + </MkFolder> + </template> + </div> + </MkFolder> + </SearchMarker> - <MkSwitch :disabled="!$i.twoFactorEnabled || $i.securityKeysList.length === 0" :modelValue="usePasswordLessLogin" @update:modelValue="v => updatePasswordLessLogin(v)"> - <template #label>{{ i18n.ts.passwordLessLogin }}</template> - <template #caption>{{ i18n.ts.passwordLessLoginDescription }}</template> - </MkSwitch> - </div> -</FormSection> + <SearchMarker :keywords="['password', 'less', 'key', 'passkey', 'login', 'signin']"> + <MkSwitch :disabled="!$i.twoFactorEnabled || $i.securityKeysList.length === 0" :modelValue="usePasswordLessLogin" @update:modelValue="v => updatePasswordLessLogin(v)"> + <template #label><SearchLabel>{{ i18n.ts.passwordLessLogin }}</SearchLabel></template> + <template #caption><SearchKeyword>{{ i18n.ts.passwordLessLoginDescription }}</SearchKeyword></template> + </MkSwitch> + </SearchMarker> + </div> + </FormSection> +</SearchMarker> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/settings/accessibility.vue b/packages/frontend/src/pages/settings/accessibility.vue new file mode 100644 index 0000000000..b703be1fe1 --- /dev/null +++ b/packages/frontend/src/pages/settings/accessibility.vue @@ -0,0 +1,91 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<SearchMarker path="/settings/accessibility" :label="i18n.ts.accessibility" :keywords="['accessibility']" icon="ti ti-accessible"> + <div class="_gaps_m"> + <div class="_gaps_s"> + <SearchMarker :keywords="['animation', 'motion', 'reduce']"> + <MkSwitch v-model="reduceAnimation"> + <template #label><SearchLabel>{{ i18n.ts.reduceUiAnimation }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['disable', 'animation', 'image', 'photo', 'picture', 'media', 'thumbnail', 'gif']"> + <MkSwitch v-model="disableShowingAnimatedImages"> + <template #label><SearchLabel>{{ i18n.ts.disableShowingAnimatedImages }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['mfm', 'enable', 'show', 'animated']"> + <MkSwitch v-model="animatedMfm"> + <template #label><SearchLabel>{{ i18n.ts.enableAnimatedMfm }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['swipe', 'horizontal', 'tab']"> + <MkSwitch v-model="enableHorizontalSwipe"> + <template #label><SearchLabel>{{ i18n.ts.enableHorizontalSwipe }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['keep', 'screen', 'display', 'on']"> + <MkSwitch v-model="keepScreenOn"> + <template #label><SearchLabel>{{ i18n.ts.keepScreenOn }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['native', 'system', 'video', 'audio', 'player', 'media']"> + <MkSwitch v-model="useNativeUIForVideoAudioPlayer"> + <template #label><SearchLabel>{{ i18n.ts.useNativeUIForVideoAudioPlayer }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + </div> + + <SearchMarker :keywords="['contextmenu', 'system', 'native']"> + <MkSelect v-model="contextMenu"> + <template #label><SearchLabel>{{ i18n.ts._contextMenu.title }}</SearchLabel></template> + <option value="app">{{ i18n.ts._contextMenu.app }}</option> + <option value="appWithShift">{{ i18n.ts._contextMenu.appWithShift }}</option> + <option value="native">{{ i18n.ts._contextMenu.native }}</option> + </MkSelect> + </SearchMarker> + </div> +</SearchMarker> +</template> + +<script lang="ts" setup> +import { computed, ref, watch } from 'vue'; +import MkSwitch from '@/components/MkSwitch.vue'; +import MkSelect from '@/components/MkSelect.vue'; +import { defaultStore } from '@/store.js'; +import { reloadAsk } from '@/scripts/reload-ask.js'; +import { i18n } from '@/i18n.js'; +import { definePageMetadata } from '@/scripts/page-metadata.js'; + +const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v)); +const animatedMfm = computed(defaultStore.makeGetterSetter('animatedMfm')); +const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages')); +const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn')); +const enableHorizontalSwipe = computed(defaultStore.makeGetterSetter('enableHorizontalSwipe')); +const useNativeUIForVideoAudioPlayer = computed(defaultStore.makeGetterSetter('useNativeUIForVideoAudioPlayer')); +const contextMenu = computed(defaultStore.makeGetterSetter('contextMenu')); + +watch([ + keepScreenOn, + contextMenu, +], async () => { + await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true }); +}); + +const headerActions = computed(() => []); + +const headerTabs = computed(() => []); + +definePageMetadata(() => ({ + title: i18n.ts.accessibility, + icon: 'ti ti-accessible', +})); +</script> diff --git a/packages/frontend/src/pages/settings/appearance.vue b/packages/frontend/src/pages/settings/appearance.vue new file mode 100644 index 0000000000..465c2a38c2 --- /dev/null +++ b/packages/frontend/src/pages/settings/appearance.vue @@ -0,0 +1,287 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<SearchMarker path="/settings/appearance" :label="i18n.ts.appearance" :keywords="['appearance']" icon="ti ti-device-desktop"> + <div class="_gaps_m"> + <FormSection first> + <div class="_gaps_m"> + <div class="_gaps_s"> + <SearchMarker :keywords="['blur']"> + <MkSwitch v-model="useBlurEffect"> + <template #label><SearchLabel>{{ i18n.ts.useBlurEffect }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['blur', 'modal']"> + <MkSwitch v-model="useBlurEffectForModal"> + <template #label><SearchLabel>{{ i18n.ts.useBlurEffectForModal }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['highlight', 'sensitive', 'nsfw', 'image', 'photo', 'picture', 'media', 'thumbnail']"> + <MkSwitch v-model="highlightSensitiveMedia"> + <template #label><SearchLabel>{{ i18n.ts.highlightSensitiveMedia }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['avatar', 'icon', 'square']"> + <MkSwitch v-model="squareAvatars"> + <template #label><SearchLabel>{{ i18n.ts.squareAvatars }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['avatar', 'icon', 'decoration', 'show']"> + <MkSwitch v-model="showAvatarDecorations"> + <template #label><SearchLabel>{{ i18n.ts.showAvatarDecorations }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['note', 'timeline', 'gap']"> + <MkSwitch v-model="showGapBetweenNotesInTimeline"> + <template #label><SearchLabel>{{ i18n.ts.showGapBetweenNotesInTimeline }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['font', 'system', 'native']"> + <MkSwitch v-model="useSystemFont"> + <template #label><SearchLabel>{{ i18n.ts.useSystemFont }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['effect', 'show']"> + <MkSwitch v-model="enableSeasonalScreenEffect"> + <template #label><SearchLabel>{{ i18n.ts.seasonalScreenEffect }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + </div> + + <SearchMarker :keywords="['menu', 'style', 'popup', 'drawer']"> + <MkSelect v-model="menuStyle"> + <template #label><SearchLabel>{{ i18n.ts.menuStyle }}</SearchLabel></template> + <option value="auto">{{ i18n.ts.auto }}</option> + <option value="popup">{{ i18n.ts.popup }}</option> + <option value="drawer">{{ i18n.ts.drawer }}</option> + </MkSelect> + </SearchMarker> + + <SearchMarker :keywords="['emoji', 'style', 'native', 'system', 'fluent', 'twemoji']"> + <div> + <MkRadios v-model="emojiStyle"> + <template #label><SearchLabel>{{ i18n.ts.emojiStyle }}</SearchLabel></template> + <option value="native">{{ i18n.ts.native }}</option> + <option value="fluentEmoji">Fluent Emoji</option> + <option value="twemoji">Twemoji</option> + </MkRadios> + <div style="margin: 8px 0 0 0; font-size: 1.5em;"><Mfm :key="emojiStyle" text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></div> + </div> + </SearchMarker> + + <SearchMarker :keywords="['font', 'size']"> + <MkRadios v-model="fontSize"> + <template #label><SearchLabel>{{ i18n.ts.fontSize }}</SearchLabel></template> + <option :value="null"><span style="font-size: 14px;">Aa</span></option> + <option value="1"><span style="font-size: 15px;">Aa</span></option> + <option value="2"><span style="font-size: 16px;">Aa</span></option> + <option value="3"><span style="font-size: 17px;">Aa</span></option> + </MkRadios> + </SearchMarker> + </div> + </FormSection> + + <SearchMarker :keywords="['note', 'display']"> + <FormSection> + <template #label><SearchLabel>{{ i18n.ts.displayOfNote }}</SearchLabel></template> + + <div class="_gaps_m"> + <SearchMarker :keywords="['reaction', 'size', 'scale', 'display']"> + <MkRadios v-model="reactionsDisplaySize"> + <template #label><SearchLabel>{{ i18n.ts.reactionsDisplaySize }}</SearchLabel></template> + <option value="small">{{ i18n.ts.small }}</option> + <option value="medium">{{ i18n.ts.medium }}</option> + <option value="large">{{ i18n.ts.large }}</option> + </MkRadios> + </SearchMarker> + + <SearchMarker :keywords="['reaction', 'size', 'scale', 'display', 'width', 'limit']"> + <MkSwitch v-model="limitWidthOfReaction"> + <template #label><SearchLabel>{{ i18n.ts.limitWidthOfReaction }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'list', 'size', 'height']"> + <MkRadios v-model="mediaListWithOneImageAppearance"> + <template #label><SearchLabel>{{ i18n.ts.mediaListWithOneImageAppearance }}</SearchLabel></template> + <option value="expand">{{ i18n.ts.default }}</option> + <option value="16_9">{{ i18n.tsx.limitTo({ x: '16:9' }) }}</option> + <option value="1_1">{{ i18n.tsx.limitTo({ x: '1:1' }) }}</option> + <option value="2_3">{{ i18n.tsx.limitTo({ x: '2:3' }) }}</option> + </MkRadios> + </SearchMarker> + + <SearchMarker :keywords="['ticker', 'information', 'label', 'instance', 'server', 'host', 'federation']"> + <MkSelect v-if="instance.federation !== 'none'" v-model="instanceTicker"> + <template #label><SearchLabel>{{ i18n.ts.instanceTicker }}</SearchLabel></template> + <option value="none">{{ i18n.ts._instanceTicker.none }}</option> + <option value="remote">{{ i18n.ts._instanceTicker.remote }}</option> + <option value="always">{{ i18n.ts._instanceTicker.always }}</option> + </MkSelect> + </SearchMarker> + + <SearchMarker :keywords="['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'nsfw', 'sensitive', 'display', 'show', 'hide', 'visibility']"> + <MkSelect v-model="nsfw"> + <template #label><SearchLabel>{{ i18n.ts.displayOfSensitiveMedia }}</SearchLabel></template> + <option value="respect">{{ i18n.ts._displayOfSensitiveMedia.respect }}</option> + <option value="ignore">{{ i18n.ts._displayOfSensitiveMedia.ignore }}</option> + <option value="force">{{ i18n.ts._displayOfSensitiveMedia.force }}</option> + </MkSelect> + </SearchMarker> + </div> + </FormSection> + </SearchMarker> + + <SearchMarker :keywords="['notification', 'display']"> + <FormSection> + <template #label><SearchLabel>{{ i18n.ts.notificationDisplay }}</SearchLabel></template> + + <div class="_gaps_m"> + <SearchMarker :keywords="['position']"> + <MkRadios v-model="notificationPosition"> + <template #label><SearchLabel>{{ i18n.ts.position }}</SearchLabel></template> + <option value="leftTop"><i class="ti ti-align-box-left-top"></i> {{ i18n.ts.leftTop }}</option> + <option value="rightTop"><i class="ti ti-align-box-right-top"></i> {{ i18n.ts.rightTop }}</option> + <option value="leftBottom"><i class="ti ti-align-box-left-bottom"></i> {{ i18n.ts.leftBottom }}</option> + <option value="rightBottom"><i class="ti ti-align-box-right-bottom"></i> {{ i18n.ts.rightBottom }}</option> + </MkRadios> + </SearchMarker> + + <SearchMarker :keywords="['stack', 'axis', 'direction']"> + <MkRadios v-model="notificationStackAxis"> + <template #label><SearchLabel>{{ i18n.ts.stackAxis }}</SearchLabel></template> + <option value="vertical"><i class="ti ti-carousel-vertical"></i> {{ i18n.ts.vertical }}</option> + <option value="horizontal"><i class="ti ti-carousel-horizontal"></i> {{ i18n.ts.horizontal }}</option> + </MkRadios> + </SearchMarker> + + <MkButton @click="testNotification">{{ i18n.ts._notification.checkNotificationBehavior }}</MkButton> + </div> + </FormSection> + </SearchMarker> + + <FormSection> + <FormLink to="/settings/custom-css"><template #icon><i class="ti ti-code"></i></template>{{ i18n.ts.customCss }}</FormLink> + </FormSection> + </div> +</SearchMarker> +</template> + +<script lang="ts" setup> +import { computed, ref, watch } from 'vue'; +import * as Misskey from 'misskey-js'; +import MkSwitch from '@/components/MkSwitch.vue'; +import MkSelect from '@/components/MkSelect.vue'; +import MkRadios from '@/components/MkRadios.vue'; +import { defaultStore } from '@/store.js'; +import { reloadAsk } from '@/scripts/reload-ask.js'; +import { i18n } from '@/i18n.js'; +import { definePageMetadata } from '@/scripts/page-metadata.js'; +import { miLocalStorage } from '@/local-storage.js'; +import FormLink from '@/components/form/link.vue'; +import { globalEvents } from '@/events.js'; +import { claimAchievement } from '@/scripts/achievements.js'; +import MkButton from '@/components/MkButton.vue'; +import FormSection from '@/components/form/section.vue'; +import { instance } from '@/instance.js'; + +const fontSize = ref(miLocalStorage.getItem('fontSize')); +const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null); + +const showAvatarDecorations = computed(defaultStore.makeGetterSetter('showAvatarDecorations')); +const emojiStyle = computed(defaultStore.makeGetterSetter('emojiStyle')); +const menuStyle = computed(defaultStore.makeGetterSetter('menuStyle')); +const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal')); +const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect')); +const highlightSensitiveMedia = computed(defaultStore.makeGetterSetter('highlightSensitiveMedia')); +const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars')); +const enableSeasonalScreenEffect = computed(defaultStore.makeGetterSetter('enableSeasonalScreenEffect')); +const showGapBetweenNotesInTimeline = computed(defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline')); +const mediaListWithOneImageAppearance = computed(defaultStore.makeGetterSetter('mediaListWithOneImageAppearance')); +const reactionsDisplaySize = computed(defaultStore.makeGetterSetter('reactionsDisplaySize')); +const limitWidthOfReaction = computed(defaultStore.makeGetterSetter('limitWidthOfReaction')); +const notificationPosition = computed(defaultStore.makeGetterSetter('notificationPosition')); +const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis')); +const nsfw = computed(defaultStore.makeGetterSetter('nsfw')); +const instanceTicker = computed(defaultStore.makeGetterSetter('instanceTicker')); + +watch(fontSize, () => { + if (fontSize.value == null) { + miLocalStorage.removeItem('fontSize'); + } else { + miLocalStorage.setItem('fontSize', fontSize.value); + } +}); + +watch(useSystemFont, () => { + if (useSystemFont.value) { + miLocalStorage.setItem('useSystemFont', 't'); + } else { + miLocalStorage.removeItem('useSystemFont'); + } +}); + +watch([ + fontSize, + useSystemFont, + squareAvatars, + highlightSensitiveMedia, + enableSeasonalScreenEffect, + showGapBetweenNotesInTimeline, + mediaListWithOneImageAppearance, + reactionsDisplaySize, + limitWidthOfReaction, + mediaListWithOneImageAppearance, + reactionsDisplaySize, + limitWidthOfReaction, + instanceTicker, +], async () => { + await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true }); +}); + +let smashCount = 0; +let smashTimer: number | null = null; + +function testNotification(): void { + const notification: Misskey.entities.Notification = { + id: Math.random().toString(), + createdAt: new Date().toUTCString(), + isRead: false, + type: 'test', + }; + + globalEvents.emit('clientNotification', notification); + + // セルフ通知破壊 実績関連 + smashCount++; + if (smashCount >= 10) { + claimAchievement('smashTestNotificationButton'); + smashCount = 0; + } + if (smashTimer) { + clearTimeout(smashTimer); + } + smashTimer = window.setTimeout(() => { + smashCount = 0; + }, 300); +} + +const headerActions = computed(() => []); + +const headerTabs = computed(() => []); + +definePageMetadata(() => ({ + title: i18n.ts.appearance, + icon: 'ti ti-device-desktop', +})); +</script> diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue index 9fca306f9f..79be2b9b1e 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.vue @@ -4,44 +4,46 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <div v-if="!loading" class="_gaps"> - <MkInfo>{{ i18n.tsx._profile.avatarDecorationMax({ max: $i.policies.avatarDecorationLimit }) }} ({{ i18n.tsx.remainingN({ n: $i.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo> +<SearchMarker path="/settings/avatar-decoration" :label="i18n.ts.avatarDecorations" :keywords="['avatar', 'icon', 'decoration']" icon="ti ti-sparkles"> + <div> + <div v-if="!loading" class="_gaps"> + <MkInfo>{{ i18n.tsx._profile.avatarDecorationMax({ max: $i.policies.avatarDecorationLimit }) }} ({{ i18n.tsx.remainingN({ n: $i.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo> - <MkAvatar :class="$style.avatar" :user="$i" forceShowDecoration/> + <MkAvatar :class="$style.avatar" :user="$i" forceShowDecoration/> - <div v-if="$i.avatarDecorations.length > 0" v-panel :class="$style.current" class="_gaps_s"> - <div>{{ i18n.ts.inUse }}</div> + <div v-if="$i.avatarDecorations.length > 0" v-panel :class="$style.current" class="_gaps_s"> + <div>{{ i18n.ts.inUse }}</div> + + <div :class="$style.decorations"> + <XDecoration + v-for="(avatarDecoration, i) in $i.avatarDecorations" + :decoration="avatarDecorations.find(d => d.id === avatarDecoration.id)" + :angle="avatarDecoration.angle" + :flipH="avatarDecoration.flipH" + :offsetX="avatarDecoration.offsetX" + :offsetY="avatarDecoration.offsetY" + :active="true" + @click="openDecoration(avatarDecoration, i)" + /> + </div> + + <MkButton danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton> + </div> <div :class="$style.decorations"> <XDecoration - v-for="(avatarDecoration, i) in $i.avatarDecorations" - :decoration="avatarDecorations.find(d => d.id === avatarDecoration.id)" - :angle="avatarDecoration.angle" - :flipH="avatarDecoration.flipH" - :offsetX="avatarDecoration.offsetX" - :offsetY="avatarDecoration.offsetY" - :active="true" - @click="openDecoration(avatarDecoration, i)" + v-for="avatarDecoration in avatarDecorations" + :key="avatarDecoration.id" + :decoration="avatarDecoration" + @click="openDecoration(avatarDecoration)" /> </div> - - <MkButton danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton> </div> - - <div :class="$style.decorations"> - <XDecoration - v-for="avatarDecoration in avatarDecorations" - :key="avatarDecoration.id" - :decoration="avatarDecoration" - @click="openDecoration(avatarDecoration)" - /> + <div v-else> + <MkLoading/> </div> </div> - <div v-else> - <MkLoading/> - </div> -</div> +</SearchMarker> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue index 0e66b93f1c..0138aac1c5 100644 --- a/packages/frontend/src/pages/settings/drive.vue +++ b/packages/frontend/src/pages/settings/drive.vue @@ -4,60 +4,81 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="_gaps_m"> - <FormSection v-if="!fetching" first> - <template #label>{{ i18n.ts.usageAmount }}</template> +<SearchMarker path="/settings/drive" :label="i18n.ts.drive" :keywords="['drive']" icon="ti ti-cloud"> + <div class="_gaps_m"> + <SearchMarker :keywords="['capacity', 'usage']"> + <FormSection first> + <template #label><SearchLabel>{{ i18n.ts.usageAmount }}</SearchLabel></template> - <div class="_gaps_m"> - <div> - <div :class="$style.meter"><div :class="$style.meterValue" :style="meterStyle"></div></div> - </div> - <FormSplit> - <MkKeyValue> - <template #key>{{ i18n.ts.capacity }}</template> - <template #value>{{ bytes(capacity, 1) }}</template> - </MkKeyValue> - <MkKeyValue> - <template #key>{{ i18n.ts.inUse }}</template> - <template #value>{{ bytes(usage, 1) }}</template> - </MkKeyValue> - </FormSplit> - </div> - </FormSection> + <div v-if="!fetching" class="_gaps_m"> + <div> + <div :class="$style.meter"><div :class="$style.meterValue" :style="meterStyle"></div></div> + </div> + <FormSplit> + <MkKeyValue> + <template #key>{{ i18n.ts.capacity }}</template> + <template #value>{{ bytes(capacity, 1) }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>{{ i18n.ts.inUse }}</template> + <template #value>{{ bytes(usage, 1) }}</template> + </MkKeyValue> + </FormSplit> + </div> + </FormSection> + </SearchMarker> + + <SearchMarker :keywords="['statistics', 'usage']"> + <FormSection> + <template #label><SearchLabel>{{ i18n.ts.statistics }}</SearchLabel></template> + <MkChart src="per-user-drive" :args="{ user: $i }" span="day" :limit="7 * 5" :bar="true" :stacked="true" :detailed="false" :aspectRatio="6"/> + </FormSection> + </SearchMarker> + + <FormSection> + <div class="_gaps_m"> + <SearchMarker :keywords="['default', 'upload', 'folder']"> + <FormLink @click="chooseUploadFolder()"> + <SearchLabel>{{ i18n.ts.uploadFolder }}</SearchLabel> + <template #suffix>{{ uploadFolder ? uploadFolder.name : '-' }}</template> + <template #suffixIcon><i class="ti ti-folder"></i></template> + </FormLink> + </SearchMarker> + + <FormLink to="/settings/drive/cleaner"> + {{ i18n.ts.drivecleaner }} + </FormLink> - <FormSection> - <template #label>{{ i18n.ts.statistics }}</template> - <MkChart src="per-user-drive" :args="{ user: $i }" span="day" :limit="7 * 5" :bar="true" :stacked="true" :detailed="false" :aspectRatio="6"/> - </FormSection> + <SearchMarker :keywords="['keep', 'original', 'raw', 'upload']"> + <MkSwitch v-model="keepOriginalUploading"> + <template #label><SearchLabel>{{ i18n.ts.keepOriginalUploading }}</SearchLabel></template> + <template #caption><SearchKeyword>{{ i18n.ts.keepOriginalUploadingDescription }}</SearchKeyword></template> + </MkSwitch> + </SearchMarker> - <FormSection> - <div class="_gaps_m"> - <FormLink @click="chooseUploadFolder()"> - {{ i18n.ts.uploadFolder }} - <template #suffix>{{ uploadFolder ? uploadFolder.name : '-' }}</template> - <template #suffixIcon><i class="ti ti-folder"></i></template> - </FormLink> - <FormLink to="/settings/drive/cleaner"> - {{ i18n.ts.drivecleaner }} - </FormLink> - <MkSwitch v-model="keepOriginalUploading"> - <template #label>{{ i18n.ts.keepOriginalUploading }}</template> - <template #caption>{{ i18n.ts.keepOriginalUploadingDescription }}</template> - </MkSwitch> - <MkSwitch v-model="keepOriginalFilename"> - <template #label>{{ i18n.ts.keepOriginalFilename }}</template> - <template #caption>{{ i18n.ts.keepOriginalFilenameDescription }}</template> - </MkSwitch> - <MkSwitch v-model="alwaysMarkNsfw" @update:modelValue="saveProfile()"> - <template #label>{{ i18n.ts.alwaysMarkSensitive }}</template> - </MkSwitch> - <MkSwitch v-model="autoSensitive" @update:modelValue="saveProfile()"> - <template #label>{{ i18n.ts.enableAutoSensitive }}<span class="_beta">{{ i18n.ts.beta }}</span></template> - <template #caption>{{ i18n.ts.enableAutoSensitiveDescription }}</template> - </MkSwitch> - </div> - </FormSection> -</div> + <SearchMarker :keywords="['keep', 'original', 'filename']"> + <MkSwitch v-model="keepOriginalFilename"> + <template #label><SearchLabel>{{ i18n.ts.keepOriginalFilename }}</SearchLabel></template> + <template #caption><SearchKeyword>{{ i18n.ts.keepOriginalFilenameDescription }}</SearchKeyword></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['always', 'default', 'mark', 'nsfw', 'sensitive', 'media', 'file']"> + <MkSwitch v-model="alwaysMarkNsfw" @update:modelValue="saveProfile()"> + <template #label><SearchLabel>{{ i18n.ts.alwaysMarkSensitive }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['auto', 'nsfw', 'sensitive', 'media', 'file']"> + <MkSwitch v-model="autoSensitive" @update:modelValue="saveProfile()"> + <template #label><SearchLabel>{{ i18n.ts.enableAutoSensitive }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template> + <template #caption><SearchKeyword>{{ i18n.ts.enableAutoSensitiveDescription }}</SearchKeyword></template> + </MkSwitch> + </SearchMarker> + </div> + </FormSection> + </div> +</SearchMarker> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue index d452f249b6..e7a8fc5634 100644 --- a/packages/frontend/src/pages/settings/email.vue +++ b/packages/frontend/src/pages/settings/email.vue @@ -4,47 +4,58 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div v-if="instance.enableEmail" class="_gaps_m"> - <FormSection first> - <template #label>{{ i18n.ts.emailAddress }}</template> - <MkInput v-model="emailAddress" type="email" manualSave> - <template #prefix><i class="ti ti-mail"></i></template> - <template v-if="$i.email && !$i.emailVerified" #caption>{{ i18n.ts.verificationEmailSent }}</template> - <template v-else-if="emailAddress === $i.email && $i.emailVerified" #caption><i class="ti ti-check" style="color: var(--MI_THEME-success);"></i> {{ i18n.ts.emailVerified }}</template> - </MkInput> - </FormSection> +<SearchMarker path="/settings/email" :label="i18n.ts.email" :keywords="['email']" icon="ti ti-mail"> + <div class="_gaps_m"> + <MkInfo v-if="!instance.enableEmail">{{ i18n.ts.emailNotSupported }}</MkInfo> - <FormSection> - <MkSwitch :modelValue="$i.receiveAnnouncementEmail" @update:modelValue="onChangeReceiveAnnouncementEmail"> - {{ i18n.ts.receiveAnnouncementFromInstance }} - </MkSwitch> - </FormSection> + <MkDisableSection :disabled="!instance.enableEmail"> + <div class="_gaps_m"> + <SearchMarker :keywords="['email', 'address']"> + <FormSection first> + <template #label><SearchLabel>{{ i18n.ts.emailAddress }}</SearchLabel></template> + <MkInput v-model="emailAddress" type="email" manualSave> + <template #prefix><i class="ti ti-mail"></i></template> + <template v-if="$i.email && !$i.emailVerified" #caption>{{ i18n.ts.verificationEmailSent }}</template> + <template v-else-if="emailAddress === $i.email && $i.emailVerified" #caption><i class="ti ti-check" style="color: var(--MI_THEME-success);"></i> {{ i18n.ts.emailVerified }}</template> + </MkInput> + </FormSection> + </SearchMarker> - <FormSection> - <template #label>{{ i18n.ts.emailNotification }}</template> + <FormSection> + <SearchMarker :keywords="['announcement', 'email']"> + <MkSwitch :modelValue="$i.receiveAnnouncementEmail" @update:modelValue="onChangeReceiveAnnouncementEmail"> + <template #label><SearchLabel>{{ i18n.ts.receiveAnnouncementFromInstance }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + </FormSection> - <div class="_gaps_s"> - <MkSwitch v-model="emailNotification_mention"> - {{ i18n.ts._notification._types.mention }} - </MkSwitch> - <MkSwitch v-model="emailNotification_reply"> - {{ i18n.ts._notification._types.reply }} - </MkSwitch> - <MkSwitch v-model="emailNotification_quote"> - {{ i18n.ts._notification._types.quote }} - </MkSwitch> - <MkSwitch v-model="emailNotification_follow"> - {{ i18n.ts._notification._types.follow }} - </MkSwitch> - <MkSwitch v-model="emailNotification_receiveFollowRequest"> - {{ i18n.ts._notification._types.receiveFollowRequest }} - </MkSwitch> - </div> - </FormSection> -</div> -<div v-if="!instance.enableEmail" class="_gaps_m"> - <MkInfo>{{ i18n.ts.emailNotSupported }}</MkInfo> -</div> + <SearchMarker :keywords="['notification', 'email']"> + <FormSection> + <template #label><SearchLabel>{{ i18n.ts.emailNotification }}</SearchLabel></template> + + <div class="_gaps_s"> + <MkSwitch v-model="emailNotification_mention"> + {{ i18n.ts._notification._types.mention }} + </MkSwitch> + <MkSwitch v-model="emailNotification_reply"> + {{ i18n.ts._notification._types.reply }} + </MkSwitch> + <MkSwitch v-model="emailNotification_quote"> + {{ i18n.ts._notification._types.quote }} + </MkSwitch> + <MkSwitch v-model="emailNotification_follow"> + {{ i18n.ts._notification._types.follow }} + </MkSwitch> + <MkSwitch v-model="emailNotification_receiveFollowRequest"> + {{ i18n.ts._notification._types.receiveFollowRequest }} + </MkSwitch> + </div> + </FormSection> + </SearchMarker> + </div> + </MkDisableSection> + </div> +</SearchMarker> </template> <script lang="ts" setup> @@ -53,6 +64,7 @@ import FormSection from '@/components/form/section.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkInput from '@/components/MkInput.vue'; import MkSwitch from '@/components/MkSwitch.vue'; +import MkDisableSection from '@/components/MkDisableSection.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { signinRequired } from '@/account.js'; diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue deleted file mode 100644 index 4449d6169f..0000000000 --- a/packages/frontend/src/pages/settings/general.vue +++ /dev/null @@ -1,492 +0,0 @@ -<!-- -SPDX-FileCopyrightText: syuilo and misskey-project -SPDX-License-Identifier: AGPL-3.0-only ---> - -<template> -<div class="_gaps_m"> - <MkSelect v-model="lang"> - <template #label>{{ i18n.ts.uiLanguage }}</template> - <option v-for="x in langs" :key="x[0]" :value="x[0]">{{ x[1] }}</option> - <template #caption> - <I18n :src="i18n.ts.i18nInfo" tag="span"> - <template #link> - <MkLink url="https://crowdin.com/project/misskey">Crowdin</MkLink> - </template> - </I18n> - </template> - </MkSelect> - - <MkRadios v-model="overridedDeviceKind"> - <template #label>{{ i18n.ts.overridedDeviceKind }}</template> - <option :value="null">{{ i18n.ts.auto }}</option> - <option value="smartphone"><i class="ti ti-device-mobile"/> {{ i18n.ts.smartphone }}</option> - <option value="tablet"><i class="ti ti-device-tablet"/> {{ i18n.ts.tablet }}</option> - <option value="desktop"><i class="ti ti-device-desktop"/> {{ i18n.ts.desktop }}</option> - </MkRadios> - - <FormSection> - <div class="_gaps_s"> - <MkSwitch v-model="showFixedPostForm">{{ i18n.ts.showFixedPostForm }}</MkSwitch> - <MkSwitch v-model="showFixedPostFormInChannel">{{ i18n.ts.showFixedPostFormInChannel }}</MkSwitch> - <MkFolder> - <template #label>{{ i18n.ts.pinnedList }}</template> - <!-- 複数ピン止め管理できるようにしたいけどめんどいので一旦ひとつのみ --> - <MkButton v-if="defaultStore.reactiveState.pinnedUserLists.value.length === 0" @click="setPinnedList()">{{ i18n.ts.add }}</MkButton> - <MkButton v-else danger @click="removePinnedList()"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> - </MkFolder> - </div> - </FormSection> - - <FormSection> - <template #label>{{ i18n.ts.displayOfNote }}</template> - - <div class="_gaps_m"> - <div class="_gaps_s"> - <MkSwitch v-model="collapseRenotes"> - <template #label>{{ i18n.ts.collapseRenotes }}</template> - <template #caption>{{ i18n.ts.collapseRenotesDescription }}</template> - </MkSwitch> - <MkSwitch v-model="showNoteActionsOnlyHover">{{ i18n.ts.showNoteActionsOnlyHover }}</MkSwitch> - <MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch> - <MkSwitch v-model="advancedMfm">{{ i18n.ts.enableAdvancedMfm }}</MkSwitch> - <MkSwitch v-if="advancedMfm" v-model="animatedMfm">{{ i18n.ts.enableAnimatedMfm }}</MkSwitch> - <MkSwitch v-if="advancedMfm" v-model="enableQuickAddMfmFunction">{{ i18n.ts.enableQuickAddMfmFunction }}</MkSwitch> - <MkSwitch v-model="showReactionsCount">{{ i18n.ts.showReactionsCount }}</MkSwitch> - <MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch> - <MkSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</MkSwitch> - <MkRadios v-model="reactionsDisplaySize"> - <template #label>{{ i18n.ts.reactionsDisplaySize }}</template> - <option value="small">{{ i18n.ts.small }}</option> - <option value="medium">{{ i18n.ts.medium }}</option> - <option value="large">{{ i18n.ts.large }}</option> - </MkRadios> - <MkSwitch v-model="limitWidthOfReaction">{{ i18n.ts.limitWidthOfReaction }}</MkSwitch> - </div> - - <MkSelect v-if="instance.federation !== 'none'" v-model="instanceTicker"> - <template #label>{{ i18n.ts.instanceTicker }}</template> - <option value="none">{{ i18n.ts._instanceTicker.none }}</option> - <option value="remote">{{ i18n.ts._instanceTicker.remote }}</option> - <option value="always">{{ i18n.ts._instanceTicker.always }}</option> - </MkSelect> - - <MkSelect v-model="nsfw"> - <template #label>{{ i18n.ts.displayOfSensitiveMedia }}</template> - <option value="respect">{{ i18n.ts._displayOfSensitiveMedia.respect }}</option> - <option value="ignore">{{ i18n.ts._displayOfSensitiveMedia.ignore }}</option> - <option value="force">{{ i18n.ts._displayOfSensitiveMedia.force }}</option> - </MkSelect> - - <MkRadios v-model="mediaListWithOneImageAppearance"> - <template #label>{{ i18n.ts.mediaListWithOneImageAppearance }}</template> - <option value="expand">{{ i18n.ts.default }}</option> - <option value="16_9">{{ i18n.tsx.limitTo({ x: '16:9' }) }}</option> - <option value="1_1">{{ i18n.tsx.limitTo({ x: '1:1' }) }}</option> - <option value="2_3">{{ i18n.tsx.limitTo({ x: '2:3' }) }}</option> - </MkRadios> - </div> - </FormSection> - - <FormSection> - <template #label>{{ i18n.ts.notificationDisplay }}</template> - - <div class="_gaps_m"> - <MkSwitch v-model="useGroupedNotifications">{{ i18n.ts.useGroupedNotifications }}</MkSwitch> - - <MkRadios v-model="notificationPosition"> - <template #label>{{ i18n.ts.position }}</template> - <option value="leftTop"><i class="ti ti-align-box-left-top"></i> {{ i18n.ts.leftTop }}</option> - <option value="rightTop"><i class="ti ti-align-box-right-top"></i> {{ i18n.ts.rightTop }}</option> - <option value="leftBottom"><i class="ti ti-align-box-left-bottom"></i> {{ i18n.ts.leftBottom }}</option> - <option value="rightBottom"><i class="ti ti-align-box-right-bottom"></i> {{ i18n.ts.rightBottom }}</option> - </MkRadios> - - <MkRadios v-model="notificationStackAxis"> - <template #label>{{ i18n.ts.stackAxis }}</template> - <option value="vertical"><i class="ti ti-carousel-vertical"></i> {{ i18n.ts.vertical }}</option> - <option value="horizontal"><i class="ti ti-carousel-horizontal"></i> {{ i18n.ts.horizontal }}</option> - </MkRadios> - - <MkButton @click="testNotification">{{ i18n.ts._notification.checkNotificationBehavior }}</MkButton> - </div> - </FormSection> - - <FormSection> - <template #label>{{ i18n.ts.appearance }}</template> - - <div class="_gaps_m"> - <div class="_gaps_s"> - <MkSwitch v-model="reduceAnimation">{{ i18n.ts.reduceUiAnimation }}</MkSwitch> - <MkSwitch v-model="useBlurEffect">{{ i18n.ts.useBlurEffect }}</MkSwitch> - <MkSwitch v-model="useBlurEffectForModal">{{ i18n.ts.useBlurEffectForModal }}</MkSwitch> - <MkSwitch v-model="disableShowingAnimatedImages">{{ i18n.ts.disableShowingAnimatedImages }}</MkSwitch> - <MkSwitch v-model="highlightSensitiveMedia">{{ i18n.ts.highlightSensitiveMedia }}</MkSwitch> - <MkSwitch v-model="squareAvatars">{{ i18n.ts.squareAvatars }}</MkSwitch> - <MkSwitch v-model="showAvatarDecorations">{{ i18n.ts.showAvatarDecorations }}</MkSwitch> - <MkSwitch v-model="useSystemFont">{{ i18n.ts.useSystemFont }}</MkSwitch> - <MkSwitch v-model="forceShowAds">{{ i18n.ts.forceShowAds }}</MkSwitch> - <MkSwitch v-model="enableSeasonalScreenEffect">{{ i18n.ts.seasonalScreenEffect }}</MkSwitch> - <MkSwitch v-model="useNativeUIForVideoAudioPlayer">{{ i18n.ts.useNativeUIForVideoAudioPlayer }}</MkSwitch> - </div> - - <MkSelect v-model="menuStyle"> - <template #label>{{ i18n.ts.menuStyle }}</template> - <option value="auto">{{ i18n.ts.auto }}</option> - <option value="popup">{{ i18n.ts.popup }}</option> - <option value="drawer">{{ i18n.ts.drawer }}</option> - </MkSelect> - - <div> - <MkRadios v-model="emojiStyle"> - <template #label>{{ i18n.ts.emojiStyle }}</template> - <option value="native">{{ i18n.ts.native }}</option> - <option value="fluentEmoji">Fluent Emoji</option> - <option value="twemoji">Twemoji</option> - </MkRadios> - <div style="margin: 8px 0 0 0; font-size: 1.5em;"><Mfm :key="emojiStyle" text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></div> - </div> - - <MkRadios v-model="fontSize"> - <template #label>{{ i18n.ts.fontSize }}</template> - <option :value="null"><span style="font-size: 14px;">Aa</span></option> - <option value="1"><span style="font-size: 15px;">Aa</span></option> - <option value="2"><span style="font-size: 16px;">Aa</span></option> - <option value="3"><span style="font-size: 17px;">Aa</span></option> - </MkRadios> - </div> - </FormSection> - - <FormSection> - <template #label>{{ i18n.ts.behavior }}</template> - - <div class="_gaps_m"> - <div class="_gaps_s"> - <MkSwitch v-model="imageNewTab">{{ i18n.ts.openImageInNewTab }}</MkSwitch> - <MkSwitch v-model="useReactionPickerForContextMenu">{{ i18n.ts.useReactionPickerForContextMenu }}</MkSwitch> - <MkSwitch v-model="enableInfiniteScroll">{{ i18n.ts.enableInfiniteScroll }}</MkSwitch> - <MkSwitch v-model="keepScreenOn">{{ i18n.ts.keepScreenOn }}</MkSwitch> - <MkSwitch v-model="disableStreamingTimeline">{{ i18n.ts.disableStreamingTimeline }}</MkSwitch> - <MkSwitch v-model="enableHorizontalSwipe">{{ i18n.ts.enableHorizontalSwipe }}</MkSwitch> - <MkSwitch v-model="alwaysConfirmFollow">{{ i18n.ts.alwaysConfirmFollow }}</MkSwitch> - <MkSwitch v-model="confirmWhenRevealingSensitiveMedia">{{ i18n.ts.confirmWhenRevealingSensitiveMedia }}</MkSwitch> - <MkSwitch v-model="confirmOnReact">{{ i18n.ts.confirmOnReact }}</MkSwitch> - </div> - <MkSelect v-model="serverDisconnectedBehavior"> - <template #label>{{ i18n.ts.whenServerDisconnected }}</template> - <option value="reload">{{ i18n.ts._serverDisconnectedBehavior.reload }}</option> - <option value="dialog">{{ i18n.ts._serverDisconnectedBehavior.dialog }}</option> - <option value="quiet">{{ i18n.ts._serverDisconnectedBehavior.quiet }}</option> - </MkSelect> - <MkSelect v-model="contextMenu"> - <template #label>{{ i18n.ts._contextMenu.title }}</template> - <option value="app">{{ i18n.ts._contextMenu.app }}</option> - <option value="appWithShift">{{ i18n.ts._contextMenu.appWithShift }}</option> - <option value="native">{{ i18n.ts._contextMenu.native }}</option> - </MkSelect> - <MkRange v-model="numberOfPageCache" :min="1" :max="10" :step="1" easing> - <template #label>{{ i18n.ts.numberOfPageCache }}</template> - <template #caption>{{ i18n.ts.numberOfPageCacheDescription }}</template> - </MkRange> - - <MkFolder> - <template #label>{{ i18n.ts.dataSaver }}</template> - - <div class="_gaps_m"> - <MkInfo>{{ i18n.ts.reloadRequiredToApplySettings }}</MkInfo> - - <div class="_buttons"> - <MkButton inline @click="enableAllDataSaver">{{ i18n.ts.enableAll }}</MkButton> - <MkButton inline @click="disableAllDataSaver">{{ i18n.ts.disableAll }}</MkButton> - </div> - <div class="_gaps_m"> - <MkSwitch v-model="dataSaver.media"> - {{ i18n.ts._dataSaver._media.title }} - <template #caption>{{ i18n.ts._dataSaver._media.description }}</template> - </MkSwitch> - <MkSwitch v-model="dataSaver.avatar"> - {{ i18n.ts._dataSaver._avatar.title }} - <template #caption>{{ i18n.ts._dataSaver._avatar.description }}</template> - </MkSwitch> - <MkSwitch v-model="dataSaver.urlPreview"> - {{ i18n.ts._dataSaver._urlPreview.title }} - <template #caption>{{ i18n.ts._dataSaver._urlPreview.description }}</template> - </MkSwitch> - <MkSwitch v-model="dataSaver.code"> - {{ i18n.ts._dataSaver._code.title }} - <template #caption>{{ i18n.ts._dataSaver._code.description }}</template> - </MkSwitch> - </div> - </div> - </MkFolder> - </div> - </FormSection> - - <FormSection> - <template #label>{{ i18n.ts.other }}</template> - - <div class="_gaps"> - <MkRadios v-model="hemisphere"> - <template #label>{{ i18n.ts.hemisphere }}</template> - <option value="N">{{ i18n.ts._hemisphere.N }}</option> - <option value="S">{{ i18n.ts._hemisphere.S }}</option> - <template #caption>{{ i18n.ts._hemisphere.caption }}</template> - </MkRadios> - <MkFolder> - <template #label>{{ i18n.ts.additionalEmojiDictionary }}</template> - <div class="_buttons"> - <template v-for="lang in emojiIndexLangs" :key="lang"> - <MkButton v-if="defaultStore.reactiveState.additionalUnicodeEmojiIndexes.value[lang]" danger @click="removeEmojiIndex(lang)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }} ({{ getEmojiIndexLangName(lang) }})</MkButton> - <MkButton v-else @click="downloadEmojiIndex(lang)"><i class="ti ti-download"></i> {{ getEmojiIndexLangName(lang) }}{{ defaultStore.reactiveState.additionalUnicodeEmojiIndexes.value[lang] ? ` (${ i18n.ts.installed })` : '' }}</MkButton> - </template> - </div> - </MkFolder> - <FormLink to="/settings/deck">{{ i18n.ts.deck }}</FormLink> - <FormLink to="/settings/custom-css"><template #icon><i class="ti ti-code"></i></template>{{ i18n.ts.customCss }}</FormLink> - </div> - </FormSection> -</div> -</template> - -<script lang="ts" setup> -import { computed, ref, watch } from 'vue'; -import * as Misskey from 'misskey-js'; -import { langs } from '@@/js/config.js'; -import MkSwitch from '@/components/MkSwitch.vue'; -import MkSelect from '@/components/MkSelect.vue'; -import MkRadios from '@/components/MkRadios.vue'; -import MkRange from '@/components/MkRange.vue'; -import MkFolder from '@/components/MkFolder.vue'; -import MkButton from '@/components/MkButton.vue'; -import FormSection from '@/components/form/section.vue'; -import FormLink from '@/components/form/link.vue'; -import MkLink from '@/components/MkLink.vue'; -import MkInfo from '@/components/MkInfo.vue'; -import { defaultStore } from '@/store.js'; -import * as os from '@/os.js'; -import { instance } from '@/instance.js'; -import { misskeyApi } from '@/scripts/misskey-api.js'; -import { reloadAsk } from '@/scripts/reload-ask.js'; -import { i18n } from '@/i18n.js'; -import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { miLocalStorage } from '@/local-storage.js'; -import { globalEvents } from '@/events.js'; -import { claimAchievement } from '@/scripts/achievements.js'; - -const lang = ref(miLocalStorage.getItem('lang')); -const fontSize = ref(miLocalStorage.getItem('fontSize')); -const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null); -const dataSaver = ref(defaultStore.state.dataSaver); - -const hemisphere = computed(defaultStore.makeGetterSetter('hemisphere')); -const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind')); -const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serverDisconnectedBehavior')); -const showNoteActionsOnlyHover = computed(defaultStore.makeGetterSetter('showNoteActionsOnlyHover')); -const showClipButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showClipButtonInNoteFooter')); -const reactionsDisplaySize = computed(defaultStore.makeGetterSetter('reactionsDisplaySize')); -const limitWidthOfReaction = computed(defaultStore.makeGetterSetter('limitWidthOfReaction')); -const collapseRenotes = computed(defaultStore.makeGetterSetter('collapseRenotes')); -const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v)); -const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal')); -const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect')); -const showGapBetweenNotesInTimeline = computed(defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline')); -const animatedMfm = computed(defaultStore.makeGetterSetter('animatedMfm')); -const advancedMfm = computed(defaultStore.makeGetterSetter('advancedMfm')); -const showReactionsCount = computed(defaultStore.makeGetterSetter('showReactionsCount')); -const enableQuickAddMfmFunction = computed(defaultStore.makeGetterSetter('enableQuickAddMfmFunction')); -const emojiStyle = computed(defaultStore.makeGetterSetter('emojiStyle')); -const menuStyle = computed(defaultStore.makeGetterSetter('menuStyle')); -const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages')); -const forceShowAds = computed(defaultStore.makeGetterSetter('forceShowAds')); -const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages')); -const highlightSensitiveMedia = computed(defaultStore.makeGetterSetter('highlightSensitiveMedia')); -const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab')); -const nsfw = computed(defaultStore.makeGetterSetter('nsfw')); -const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm')); -const showFixedPostFormInChannel = computed(defaultStore.makeGetterSetter('showFixedPostFormInChannel')); -const numberOfPageCache = computed(defaultStore.makeGetterSetter('numberOfPageCache')); -const instanceTicker = computed(defaultStore.makeGetterSetter('instanceTicker')); -const enableInfiniteScroll = computed(defaultStore.makeGetterSetter('enableInfiniteScroll')); -const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('useReactionPickerForContextMenu')); -const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars')); -const showAvatarDecorations = computed(defaultStore.makeGetterSetter('showAvatarDecorations')); -const mediaListWithOneImageAppearance = computed(defaultStore.makeGetterSetter('mediaListWithOneImageAppearance')); -const notificationPosition = computed(defaultStore.makeGetterSetter('notificationPosition')); -const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis')); -const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn')); -const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disableStreamingTimeline')); -const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications')); -const enableSeasonalScreenEffect = computed(defaultStore.makeGetterSetter('enableSeasonalScreenEffect')); -const enableHorizontalSwipe = computed(defaultStore.makeGetterSetter('enableHorizontalSwipe')); -const useNativeUIForVideoAudioPlayer = computed(defaultStore.makeGetterSetter('useNativeUIForVideoAudioPlayer')); -const alwaysConfirmFollow = computed(defaultStore.makeGetterSetter('alwaysConfirmFollow')); -const confirmWhenRevealingSensitiveMedia = computed(defaultStore.makeGetterSetter('confirmWhenRevealingSensitiveMedia')); -const confirmOnReact = computed(defaultStore.makeGetterSetter('confirmOnReact')); -const contextMenu = computed(defaultStore.makeGetterSetter('contextMenu')); - -watch(lang, () => { - miLocalStorage.setItem('lang', lang.value as string); - miLocalStorage.removeItem('locale'); - miLocalStorage.removeItem('localeVersion'); -}); - -watch(fontSize, () => { - if (fontSize.value == null) { - miLocalStorage.removeItem('fontSize'); - } else { - miLocalStorage.setItem('fontSize', fontSize.value); - } -}); - -watch(useSystemFont, () => { - if (useSystemFont.value) { - miLocalStorage.setItem('useSystemFont', 't'); - } else { - miLocalStorage.removeItem('useSystemFont'); - } -}); - -watch([ - hemisphere, - lang, - fontSize, - useSystemFont, - enableInfiniteScroll, - squareAvatars, - showNoteActionsOnlyHover, - showGapBetweenNotesInTimeline, - instanceTicker, - overridedDeviceKind, - mediaListWithOneImageAppearance, - reactionsDisplaySize, - limitWidthOfReaction, - highlightSensitiveMedia, - keepScreenOn, - disableStreamingTimeline, - enableSeasonalScreenEffect, - alwaysConfirmFollow, - confirmWhenRevealingSensitiveMedia, - contextMenu, -], async () => { - await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true }); -}); - -const emojiIndexLangs = ['en-US', 'ja-JP', 'ja-JP_hira'] as const; - -function getEmojiIndexLangName(targetLang: typeof emojiIndexLangs[number]) { - if (langs.find(x => x[0] === targetLang)) { - return langs.find(x => x[0] === targetLang)![1]; - } else { - // 絵文字辞書限定の言語定義 - switch (targetLang) { - case 'ja-JP_hira': return 'ひらがな'; - default: return targetLang; - } - } -} - -function downloadEmojiIndex(lang: typeof emojiIndexLangs[number]) { - async function main() { - const currentIndexes = defaultStore.state.additionalUnicodeEmojiIndexes; - - function download() { - switch (lang) { - case 'en-US': return import('../../unicode-emoji-indexes/en-US.json').then(x => x.default); - case 'ja-JP': return import('../../unicode-emoji-indexes/ja-JP.json').then(x => x.default); - case 'ja-JP_hira': return import('../../unicode-emoji-indexes/ja-JP_hira.json').then(x => x.default); - default: throw new Error('unrecognized lang: ' + lang); - } - } - - currentIndexes[lang] = await download(); - await defaultStore.set('additionalUnicodeEmojiIndexes', currentIndexes); - } - - os.promiseDialog(main()); -} - -function removeEmojiIndex(lang: string) { - async function main() { - const currentIndexes = defaultStore.state.additionalUnicodeEmojiIndexes; - delete currentIndexes[lang]; - await defaultStore.set('additionalUnicodeEmojiIndexes', currentIndexes); - } - - os.promiseDialog(main()); -} - -async function setPinnedList() { - const lists = await misskeyApi('users/lists/list'); - const { canceled, result: list } = await os.select({ - title: i18n.ts.selectList, - items: lists.map(x => ({ - value: x, text: x.name, - })), - }); - if (canceled) return; - - defaultStore.set('pinnedUserLists', [list]); -} - -function removePinnedList() { - defaultStore.set('pinnedUserLists', []); -} - -let smashCount = 0; -let smashTimer: number | null = null; - -function testNotification(): void { - const notification: Misskey.entities.Notification = { - id: Math.random().toString(), - createdAt: new Date().toUTCString(), - isRead: false, - type: 'test', - }; - - globalEvents.emit('clientNotification', notification); - - // セルフ通知破壊 実績関連 - smashCount++; - if (smashCount >= 10) { - claimAchievement('smashTestNotificationButton'); - smashCount = 0; - } - if (smashTimer) { - clearTimeout(smashTimer); - } - smashTimer = window.setTimeout(() => { - smashCount = 0; - }, 300); -} - -function enableAllDataSaver() { - const g = { ...defaultStore.state.dataSaver }; - - Object.keys(g).forEach((key) => { g[key] = true; }); - - dataSaver.value = g; -} - -function disableAllDataSaver() { - const g = { ...defaultStore.state.dataSaver }; - - Object.keys(g).forEach((key) => { g[key] = false; }); - - dataSaver.value = g; -} - -watch(dataSaver, (to) => { - defaultStore.set('dataSaver', to); -}, { - deep: true, -}); - -const headerActions = computed(() => []); - -const headerTabs = computed(() => []); - -definePageMetadata(() => ({ - title: i18n.ts.general, - icon: 'ti ti-adjustments', -})); -</script> diff --git a/packages/frontend/src/pages/settings/import-export.vue b/packages/frontend/src/pages/settings/import-export.vue index 5acbc50756..6b67a9a1a8 100644 --- a/packages/frontend/src/pages/settings/import-export.vue +++ b/packages/frontend/src/pages/settings/import-export.vue @@ -4,118 +4,143 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="_gaps_m"> - <FormSection first> - <template #label><i class="ti ti-pencil"></i> {{ i18n.ts._exportOrImport.allNotes }}</template> - <MkFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportNotes()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </MkFolder> - </FormSection> - <FormSection> - <template #label><i class="ti ti-star"></i> {{ i18n.ts._exportOrImport.favoritedNotes }}</template> - <MkFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportFavorites()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </MkFolder> - </FormSection> - <FormSection> - <template #label><i class="ti ti-star"></i> {{ i18n.ts._exportOrImport.clips }}</template> - <MkFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportClips()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </MkFolder> - </FormSection> - <FormSection> - <template #label><i class="ti ti-users"></i> {{ i18n.ts._exportOrImport.followingList }}</template> - <div class="_gaps_s"> - <MkFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> +<SearchMarker path="/settings/import-export" :label="i18n.ts.importAndExport" :keywords="['import', 'export', 'data']" icon="ti ti-package"> + <div class="_gaps_m"> + <SearchMarker :keywords="['notes']"> + <FormSection first> + <template #label><i class="ti ti-pencil"></i> <SearchLabel>{{ i18n.ts._exportOrImport.allNotes }}</SearchLabel></template> + <MkFolder> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportNotes()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </MkFolder> + </FormSection> + </SearchMarker> + + <SearchMarker :keywords="['favorite', 'notes']"> + <FormSection> + <template #label><i class="ti ti-star"></i> <SearchLabel>{{ i18n.ts._exportOrImport.favoritedNotes }}</SearchLabel></template> + <MkFolder> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportFavorites()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </MkFolder> + </FormSection> + </SearchMarker> + + <SearchMarker :keywords="['clip', 'notes']"> + <FormSection> + <template #label><i class="ti ti-star"></i> <SearchLabel>{{ i18n.ts._exportOrImport.clips }}</SearchLabel></template> + <MkFolder> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportClips()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </MkFolder> + </FormSection> + </SearchMarker> + + <SearchMarker :keywords="['following', 'users']"> + <FormSection> + <template #label><i class="ti ti-users"></i> <SearchLabel>{{ i18n.ts._exportOrImport.followingList }}</SearchLabel></template> + <div class="_gaps_s"> + <MkFolder> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <div class="_gaps_s"> + <MkSwitch v-model="excludeMutingUsers"> + {{ i18n.ts._exportOrImport.excludeMutingUsers }} + </MkSwitch> + <MkSwitch v-model="excludeInactiveUsers"> + {{ i18n.ts._exportOrImport.excludeInactiveUsers }} + </MkSwitch> + <MkButton primary :class="$style.button" inline @click="exportFollowing()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </div> + </MkFolder> + <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportFollowing"> + <template #label>{{ i18n.ts.import }}</template> + <template #icon><i class="ti ti-upload"></i></template> + <MkSwitch v-model="withReplies"> + {{ i18n.ts._exportOrImport.withReplies }} + </MkSwitch> + <MkButton primary :class="$style.button" inline @click="importFollowing($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> + </MkFolder> + </div> + </FormSection> + </SearchMarker> + + <SearchMarker :keywords="['user', 'lists']"> + <FormSection> + <template #label><i class="ti ti-users"></i> <SearchLabel>{{ i18n.ts._exportOrImport.userLists }}</SearchLabel></template> + <div class="_gaps_s"> + <MkFolder> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportUserLists()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </MkFolder> + <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportUserLists"> + <template #label>{{ i18n.ts.import }}</template> + <template #icon><i class="ti ti-upload"></i></template> + <MkButton primary :class="$style.button" inline @click="importUserLists($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> + </MkFolder> + </div> + </FormSection> + </SearchMarker> + + <SearchMarker :keywords="['mute', 'users']"> + <FormSection> + <template #label><i class="ti ti-user-off"></i> <SearchLabel>{{ i18n.ts._exportOrImport.muteList }}</SearchLabel></template> + <div class="_gaps_s"> + <MkFolder> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportMuting()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </MkFolder> + <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportMuting"> + <template #label>{{ i18n.ts.import }}</template> + <template #icon><i class="ti ti-upload"></i></template> + <MkButton primary :class="$style.button" inline @click="importMuting($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> + </MkFolder> + </div> + </FormSection> + </SearchMarker> + + <SearchMarker :keywords="['block', 'users']"> + <FormSection> + <template #label><i class="ti ti-user-off"></i> <SearchLabel>{{ i18n.ts._exportOrImport.blockingList }}</SearchLabel></template> + <div class="_gaps_s"> + <MkFolder> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportBlocking()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </MkFolder> + <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportBlocking"> + <template #label>{{ i18n.ts.import }}</template> + <template #icon><i class="ti ti-upload"></i></template> + <MkButton primary :class="$style.button" inline @click="importBlocking($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> + </MkFolder> + </div> + </FormSection> + </SearchMarker> + + <SearchMarker :keywords="['antennas']"> + <FormSection> + <template #label><i class="ti ti-antenna"></i> <SearchLabel>{{ i18n.ts.antennas }}</SearchLabel></template> <div class="_gaps_s"> - <MkSwitch v-model="excludeMutingUsers"> - {{ i18n.ts._exportOrImport.excludeMutingUsers }} - </MkSwitch> - <MkSwitch v-model="excludeInactiveUsers"> - {{ i18n.ts._exportOrImport.excludeInactiveUsers }} - </MkSwitch> - <MkButton primary :class="$style.button" inline @click="exportFollowing()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + <MkFolder> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportAntennas()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </MkFolder> + <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportAntennas"> + <template #label>{{ i18n.ts.import }}</template> + <template #icon><i class="ti ti-upload"></i></template> + <MkButton primary :class="$style.button" inline @click="importAntennas($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> + </MkFolder> </div> - </MkFolder> - <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportFollowing"> - <template #label>{{ i18n.ts.import }}</template> - <template #icon><i class="ti ti-upload"></i></template> - <MkSwitch v-model="withReplies"> - {{ i18n.ts._exportOrImport.withReplies }} - </MkSwitch> - <MkButton primary :class="$style.button" inline @click="importFollowing($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> - </MkFolder> - </div> - </FormSection> - <FormSection> - <template #label><i class="ti ti-users"></i> {{ i18n.ts._exportOrImport.userLists }}</template> - <div class="_gaps_s"> - <MkFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportUserLists()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </MkFolder> - <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportUserLists"> - <template #label>{{ i18n.ts.import }}</template> - <template #icon><i class="ti ti-upload"></i></template> - <MkButton primary :class="$style.button" inline @click="importUserLists($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> - </MkFolder> - </div> - </FormSection> - <FormSection> - <template #label><i class="ti ti-user-off"></i> {{ i18n.ts._exportOrImport.muteList }}</template> - <div class="_gaps_s"> - <MkFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportMuting()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </MkFolder> - <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportMuting"> - <template #label>{{ i18n.ts.import }}</template> - <template #icon><i class="ti ti-upload"></i></template> - <MkButton primary :class="$style.button" inline @click="importMuting($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> - </MkFolder> - </div> - </FormSection> - <FormSection> - <template #label><i class="ti ti-user-off"></i> {{ i18n.ts._exportOrImport.blockingList }}</template> - <div class="_gaps_s"> - <MkFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportBlocking()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </MkFolder> - <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportBlocking"> - <template #label>{{ i18n.ts.import }}</template> - <template #icon><i class="ti ti-upload"></i></template> - <MkButton primary :class="$style.button" inline @click="importBlocking($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> - </MkFolder> - </div> - </FormSection> - <FormSection> - <template #label><i class="ti ti-antenna"></i> {{ i18n.ts.antennas }}</template> - <div class="_gaps_s"> - <MkFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportAntennas()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </MkFolder> - <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportAntennas"> - <template #label>{{ i18n.ts.import }}</template> - <template #icon><i class="ti ti-upload"></i></template> - <MkButton primary :class="$style.button" inline @click="importAntennas($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> - </MkFolder> - </div> - </FormSection> -</div> + </FormSection> + </SearchMarker> + </div> +</SearchMarker> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index bc6d6d0261..458605d545 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="!narrow || currentPage?.route.name == null" class="nav"> <div class="baaadecd"> <MkInfo v-if="emailNotConfigured" warn class="info">{{ i18n.ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo> - <MkSuperMenu :def="menuDef" :grid="narrow"></MkSuperMenu> + <MkSuperMenu :def="menuDef" :grid="narrow" :searchIndex="SETTING_INDEX"></MkSuperMenu> </div> </div> <div v-if="!(narrow && currentPage?.route.name == null)" class="main"> @@ -29,6 +29,8 @@ SPDX-License-Identifier: AGPL-3.0-only <script setup lang="ts"> import { computed, onActivated, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'; +import type { PageMetadata } from '@/scripts/page-metadata.js'; +import type { SuperMenuDef } from '@/components/MkSuperMenu.vue'; import { i18n } from '@/i18n.js'; import MkInfo from '@/components/MkInfo.vue'; import MkSuperMenu from '@/components/MkSuperMenu.vue'; @@ -38,8 +40,9 @@ import { instance } from '@/instance.js'; import { definePageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; import * as os from '@/os.js'; import { useRouter } from '@/router/supplier.js'; -import type { PageMetadata } from '@/scripts/page-metadata.js'; -import type { SuperMenuDef } from '@/components/MkSuperMenu.vue'; +import { searchIndexes } from '@/scripts/autogen/settings-search-index.js'; + +const SETTING_INDEX = searchIndexes; // TODO: lazy load const indexInfo = { title: i18n.ts.settings, @@ -63,7 +66,6 @@ const ro = new ResizeObserver((entries, observer) => { }); const menuDef = computed<SuperMenuDef[]>(() => [{ - title: i18n.ts.basicSettings, items: [{ icon: 'ti ti-user', text: i18n.ts.profile, @@ -101,40 +103,38 @@ const menuDef = computed<SuperMenuDef[]>(() => [{ active: currentPage.value?.route.name === 'security', }], }, { - title: i18n.ts.clientSettings, items: [{ icon: 'ti ti-adjustments', - text: i18n.ts.general, - to: '/settings/general', - active: currentPage.value?.route.name === 'general', + text: i18n.ts.preferences, + to: '/settings/preferences', + active: currentPage.value?.route.name === 'preferences', }, { icon: 'ti ti-palette', text: i18n.ts.theme, to: '/settings/theme', active: currentPage.value?.route.name === 'theme', }, { - icon: 'ti ti-menu-2', - text: i18n.ts.navbar, - to: '/settings/navbar', - active: currentPage.value?.route.name === 'navbar', - }, { - icon: 'ti ti-equal-double', - text: i18n.ts.statusbar, - to: '/settings/statusbar', - active: currentPage.value?.route.name === 'statusbar', + icon: 'ti ti-device-desktop', + text: i18n.ts.appearance, + to: '/settings/appearance', + active: currentPage.value?.route.name === 'appearance', }, { icon: 'ti ti-music', text: i18n.ts.sounds, to: '/settings/sounds', active: currentPage.value?.route.name === 'sounds', }, { + icon: 'ti ti-accessible', + text: i18n.ts.accessibility, + to: '/settings/accessibility', + active: currentPage.value?.route.name === 'accessibility', + }, { icon: 'ti ti-plug', text: i18n.ts.plugins, to: '/settings/plugin', active: currentPage.value?.route.name === 'plugin', }], }, { - title: i18n.ts.otherSettings, items: [{ icon: 'ti ti-badges', text: i18n.ts.roles, @@ -161,11 +161,6 @@ const menuDef = computed<SuperMenuDef[]>(() => [{ to: '/settings/import-export', active: currentPage.value?.route.name === 'import-export', }, { - icon: 'ti ti-plane', - text: `${i18n.ts.accountMigration}`, - to: '/settings/migration', - active: currentPage.value?.route.name === 'migration', - }, { icon: 'ti ti-dots', text: i18n.ts.other, to: '/settings/other', diff --git a/packages/frontend/src/pages/settings/migration.vue b/packages/frontend/src/pages/settings/migration.vue index ddc23945dd..1c00d64d73 100644 --- a/packages/frontend/src/pages/settings/migration.vue +++ b/packages/frontend/src/pages/settings/migration.vue @@ -68,7 +68,6 @@ import MkUserInfo from '@/components/MkUserInfo.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; -import { definePageMetadata } from '@/scripts/page-metadata.js'; import { signinRequired } from '@/account.js'; import { unisonReload } from '@/scripts/unison-reload.js'; @@ -120,11 +119,6 @@ async function save(): Promise<void> { } init(); - -definePageMetadata(() => ({ - title: i18n.ts.accountMigration, - icon: 'ti ti-plane', -})); </script> <style lang="scss"> diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue index 491676e0d0..4aac2a25bd 100644 --- a/packages/frontend/src/pages/settings/mute-block.vue +++ b/packages/frontend/src/pages/settings/mute-block.vue @@ -4,132 +4,171 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="_gaps_m"> - <MkFolder> - <template #icon><i class="ti ti-message-off"></i></template> - <template #label>{{ i18n.ts.wordMute }}</template> +<SearchMarker path="/settings/mute-block" :label="i18n.ts.muteAndBlock" icon="ti ti-ban" :keywords="['mute', 'block']"> + <div class="_gaps_m"> + <SearchMarker + :label="i18n.ts.wordMute" + :keywords="['note', 'word', 'soft', 'mute', 'hide']" + > + <MkFolder> + <template #icon><i class="ti ti-message-off"></i></template> + <template #label>{{ i18n.ts.wordMute }}</template> - <div class="_gaps_m"> - <MkInfo>{{ i18n.ts.wordMuteDescription }}</MkInfo> - <MkSwitch v-model="showSoftWordMutedWord">{{ i18n.ts.showMutedWord }}</MkSwitch> - <XWordMute :muted="$i.mutedWords" @save="saveMutedWords"/> - </div> - </MkFolder> + <div class="_gaps_m"> + <MkInfo>{{ i18n.ts.wordMuteDescription }}</MkInfo> - <MkFolder> - <template #icon><i class="ti ti-message-off"></i></template> - <template #label>{{ i18n.ts.hardWordMute }}</template> + <SearchMarker + :label="i18n.ts.showMutedWord" + :keywords="['show']" + > + <MkSwitch v-model="showSoftWordMutedWord">{{ i18n.ts.showMutedWord }}</MkSwitch> + </SearchMarker> - <div class="_gaps_m"> - <MkInfo>{{ i18n.ts.hardWordMuteDescription }}</MkInfo> - <XWordMute :muted="$i.hardMutedWords" @save="saveHardMutedWords"/> - </div> - </MkFolder> + <XWordMute :muted="$i.mutedWords" @save="saveMutedWords"/> + </div> + </MkFolder> + </SearchMarker> - <MkFolder v-if="instance.federation !== 'none'"> - <template #icon><i class="ti ti-planet-off"></i></template> - <template #label>{{ i18n.ts.instanceMute }}</template> + <SearchMarker + :label="i18n.ts.hardWordMute" + :keywords="['note', 'word', 'hard', 'mute', 'hide']" + > + <MkFolder> + <template #icon><i class="ti ti-message-off"></i></template> + <template #label>{{ i18n.ts.hardWordMute }}</template> - <XInstanceMute/> - </MkFolder> + <div class="_gaps_m"> + <MkInfo>{{ i18n.ts.hardWordMuteDescription }}</MkInfo> + <XWordMute :muted="$i.hardMutedWords" @save="saveHardMutedWords"/> + </div> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-repeat-off"></i></template> - <template #label>{{ i18n.ts.mutedUsers }} ({{ i18n.ts.renote }})</template> + <SearchMarker + :label="i18n.ts.instanceMute" + :keywords="['note', 'server', 'instance', 'host', 'federation', 'mute', 'hide']" + > + <MkFolder v-if="instance.federation !== 'none'"> + <template #icon><i class="ti ti-planet-off"></i></template> + <template #label>{{ i18n.ts.instanceMute }}</template> - <MkPagination :pagination="renoteMutingPagination"> - <template #empty> - <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> - <div>{{ i18n.ts.noUsers }}</div> - </div> - </template> + <XInstanceMute/> + </MkFolder> + </SearchMarker> - <template #default="{ items }"> - <div class="_gaps_s"> - <div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedRenoteMuteItems.includes(item.id) }]"> - <div :class="$style.userItemMain"> - <MkA :class="$style.userItemMainBody" :to="userPage(item.mutee)"> - <MkUserCardMini :user="item.mutee"/> - </MkA> - <button class="_button" :class="$style.userToggle" @click="toggleRenoteMuteItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> - <button class="_button" :class="$style.remove" @click="unrenoteMute(item.mutee, $event)"><i class="ti ti-x"></i></button> - </div> - <div v-if="expandedRenoteMuteItems.includes(item.id)" :class="$style.userItemSub"> - <div>Muted at: <MkTime :time="item.createdAt" mode="detail"/></div> + <SearchMarker + :label="`${i18n.ts.mutedUsers} (${ i18n.ts.renote })`" + :keywords="['renote', 'mute', 'hide', 'user']" + > + <MkFolder> + <template #icon><i class="ti ti-repeat-off"></i></template> + <template #label>{{ i18n.ts.mutedUsers }} ({{ i18n.ts.renote }})</template> + + <MkPagination :pagination="renoteMutingPagination"> + <template #empty> + <div class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.noUsers }}</div> </div> - </div> - </div> - </template> - </MkPagination> - </MkFolder> + </template> - <MkFolder> - <template #icon><i class="ti ti-eye-off"></i></template> - <template #label>{{ i18n.ts.mutedUsers }}</template> + <template #default="{ items }"> + <div class="_gaps_s"> + <div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedRenoteMuteItems.includes(item.id) }]"> + <div :class="$style.userItemMain"> + <MkA :class="$style.userItemMainBody" :to="userPage(item.mutee)"> + <MkUserCardMini :user="item.mutee"/> + </MkA> + <button class="_button" :class="$style.userToggle" @click="toggleRenoteMuteItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> + <button class="_button" :class="$style.remove" @click="unrenoteMute(item.mutee, $event)"><i class="ti ti-x"></i></button> + </div> + <div v-if="expandedRenoteMuteItems.includes(item.id)" :class="$style.userItemSub"> + <div>Muted at: <MkTime :time="item.createdAt" mode="detail"/></div> + </div> + </div> + </div> + </template> + </MkPagination> + </MkFolder> + </SearchMarker> - <MkPagination :pagination="mutingPagination"> - <template #empty> - <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> - <div>{{ i18n.ts.noUsers }}</div> - </div> - </template> + <SearchMarker + :label="i18n.ts.mutedUsers" + :keywords="['note', 'mute', 'hide', 'user']" + > + <MkFolder> + <template #icon><i class="ti ti-eye-off"></i></template> + <template #label>{{ i18n.ts.mutedUsers }}</template> - <template #default="{ items }"> - <div class="_gaps_s"> - <div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedMuteItems.includes(item.id) }]"> - <div :class="$style.userItemMain"> - <MkA :class="$style.userItemMainBody" :to="userPage(item.mutee)"> - <MkUserCardMini :user="item.mutee"/> - </MkA> - <button class="_button" :class="$style.userToggle" @click="toggleMuteItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> - <button class="_button" :class="$style.remove" @click="unmute(item.mutee, $event)"><i class="ti ti-x"></i></button> + <MkPagination :pagination="mutingPagination"> + <template #empty> + <div class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.noUsers }}</div> </div> - <div v-if="expandedMuteItems.includes(item.id)" :class="$style.userItemSub"> - <div>Muted at: <MkTime :time="item.createdAt" mode="detail"/></div> - <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div> - <div v-else>Period: {{ i18n.ts.indefinitely }}</div> + </template> + + <template #default="{ items }"> + <div class="_gaps_s"> + <div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedMuteItems.includes(item.id) }]"> + <div :class="$style.userItemMain"> + <MkA :class="$style.userItemMainBody" :to="userPage(item.mutee)"> + <MkUserCardMini :user="item.mutee"/> + </MkA> + <button class="_button" :class="$style.userToggle" @click="toggleMuteItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> + <button class="_button" :class="$style.remove" @click="unmute(item.mutee, $event)"><i class="ti ti-x"></i></button> + </div> + <div v-if="expandedMuteItems.includes(item.id)" :class="$style.userItemSub"> + <div>Muted at: <MkTime :time="item.createdAt" mode="detail"/></div> + <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div> + <div v-else>Period: {{ i18n.ts.indefinitely }}</div> + </div> + </div> </div> - </div> - </div> - </template> - </MkPagination> - </MkFolder> + </template> + </MkPagination> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-ban"></i></template> - <template #label>{{ i18n.ts.blockedUsers }}</template> + <SearchMarker + :label="i18n.ts.blockedUsers" + :keywords="['block', 'user']" + > + <MkFolder> + <template #icon><i class="ti ti-ban"></i></template> + <template #label>{{ i18n.ts.blockedUsers }}</template> - <MkPagination :pagination="blockingPagination"> - <template #empty> - <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> - <div>{{ i18n.ts.noUsers }}</div> - </div> - </template> - - <template #default="{ items }"> - <div class="_gaps_s"> - <div v-for="item in items" :key="item.blockee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedBlockItems.includes(item.id) }]"> - <div :class="$style.userItemMain"> - <MkA :class="$style.userItemMainBody" :to="userPage(item.blockee)"> - <MkUserCardMini :user="item.blockee"/> - </MkA> - <button class="_button" :class="$style.userToggle" @click="toggleBlockItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> - <button class="_button" :class="$style.remove" @click="unblock(item.blockee, $event)"><i class="ti ti-x"></i></button> + <MkPagination :pagination="blockingPagination"> + <template #empty> + <div class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.noUsers }}</div> </div> - <div v-if="expandedBlockItems.includes(item.id)" :class="$style.userItemSub"> - <div>Blocked at: <MkTime :time="item.createdAt" mode="detail"/></div> - <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div> - <div v-else>Period: {{ i18n.ts.indefinitely }}</div> + </template> + + <template #default="{ items }"> + <div class="_gaps_s"> + <div v-for="item in items" :key="item.blockee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedBlockItems.includes(item.id) }]"> + <div :class="$style.userItemMain"> + <MkA :class="$style.userItemMainBody" :to="userPage(item.blockee)"> + <MkUserCardMini :user="item.blockee"/> + </MkA> + <button class="_button" :class="$style.userToggle" @click="toggleBlockItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> + <button class="_button" :class="$style.remove" @click="unblock(item.blockee, $event)"><i class="ti ti-x"></i></button> + </div> + <div v-if="expandedBlockItems.includes(item.id)" :class="$style.userItemSub"> + <div>Blocked at: <MkTime :time="item.createdAt" mode="detail"/></div> + <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div> + <div v-else>Period: {{ i18n.ts.indefinitely }}</div> + </div> + </div> </div> - </div> - </div> - </template> - </MkPagination> - </MkFolder> -</div> + </template> + </MkPagination> + </MkFolder> + </SearchMarker> + </div> +</SearchMarker> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue index 4a52e59d02..9742c548e7 100644 --- a/packages/frontend/src/pages/settings/other.vue +++ b/packages/frontend/src/pages/settings/other.vue @@ -4,91 +4,111 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="_gaps_m"> - <!-- - <MkSwitch v-model="$i.injectFeaturedNote" @update:model-value="onChangeInjectFeaturedNote"> - <template #label>{{ i18n.ts.showFeaturedNotesInTimeline }}</template> - </MkSwitch> - --> +<SearchMarker path="/settings/other" :label="i18n.ts.other" :keywords="['other']" icon="ti ti-dots"> + <div class="_gaps_m"> + <!-- + <MkSwitch v-model="$i.injectFeaturedNote" @update:model-value="onChangeInjectFeaturedNote"> + <template #label>{{ i18n.ts.showFeaturedNotesInTimeline }}</template> + </MkSwitch> + --> - <!-- - <MkSwitch v-model="reportError">{{ i18n.ts.sendErrorReports }}<template #caption>{{ i18n.ts.sendErrorReportsDescription }}</template></MkSwitch> - --> + <!-- + <MkSwitch v-model="reportError">{{ i18n.ts.sendErrorReports }}<template #caption>{{ i18n.ts.sendErrorReportsDescription }}</template></MkSwitch> + --> - <FormSection first> - <div class="_gaps_s"> - <MkFolder> - <template #icon><i class="ti ti-info-circle"></i></template> - <template #label>{{ i18n.ts.accountInfo }}</template> + <FormSection first> + <div class="_gaps_s"> + <SearchMarker :keywords="['account', 'info']"> + <MkFolder> + <template #icon><i class="ti ti-info-circle"></i></template> + <template #label><SearchLabel>{{ i18n.ts.accountInfo }}</SearchLabel></template> - <div class="_gaps_m"> - <MkKeyValue> - <template #key>ID</template> - <template #value><span class="_monospace">{{ $i.id }}</span></template> - </MkKeyValue> + <div class="_gaps_m"> + <MkKeyValue> + <template #key>ID</template> + <template #value><span class="_monospace">{{ $i.id }}</span></template> + </MkKeyValue> - <MkKeyValue> - <template #key>{{ i18n.ts.registeredDate }}</template> - <template #value><MkTime :time="$i.createdAt" mode="detail"/></template> - </MkKeyValue> - </div> - </MkFolder> + <MkKeyValue> + <template #key>{{ i18n.ts.registeredDate }}</template> + <template #value><MkTime :time="$i.createdAt" mode="detail"/></template> + </MkKeyValue> + </div> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-alert-triangle"></i></template> - <template #label>{{ i18n.ts.closeAccount }}</template> + <SearchMarker :keywords="['account', 'move', 'migration']"> + <MkFolder> + <template #icon><i class="ti ti-plane"></i></template> + <template #label><SearchLabel>{{ i18n.ts.accountMigration }}</SearchLabel></template> - <div class="_gaps_m"> - <FormInfo warn>{{ i18n.ts._accountDelete.mayTakeTime }}</FormInfo> - <FormInfo>{{ i18n.ts._accountDelete.sendEmail }}</FormInfo> - <MkButton v-if="!$i.isDeleted" danger @click="deleteAccount">{{ i18n.ts._accountDelete.requestAccountDelete }}</MkButton> - <MkButton v-else disabled>{{ i18n.ts._accountDelete.inProgress }}</MkButton> - </div> - </MkFolder> + <XMigration/> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-flask"></i></template> - <template #label>{{ i18n.ts.experimentalFeatures }}</template> + <SearchMarker :keywords="['account', 'close', 'delete']"> + <MkFolder> + <template #icon><i class="ti ti-alert-triangle"></i></template> + <template #label><SearchLabel>{{ i18n.ts.closeAccount }}</SearchLabel></template> - <div class="_gaps_m"> - <MkSwitch v-model="enableCondensedLine"> - <template #label>Enable condensed line</template> - </MkSwitch> - <MkSwitch v-model="skipNoteRender"> - <template #label>Enable note render skipping</template> - </MkSwitch> - </div> - </MkFolder> + <div class="_gaps_m"> + <FormInfo warn>{{ i18n.ts._accountDelete.mayTakeTime }}</FormInfo> + <FormInfo>{{ i18n.ts._accountDelete.sendEmail }}</FormInfo> + <MkButton v-if="!$i.isDeleted" danger @click="deleteAccount"><SearchKeyword>{{ i18n.ts._accountDelete.requestAccountDelete }}</SearchKeyword></MkButton> + <MkButton v-else disabled>{{ i18n.ts._accountDelete.inProgress }}</MkButton> + </div> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-code"></i></template> - <template #label>{{ i18n.ts.developer }}</template> + <SearchMarker :keywords="['experimental', 'feature', 'flags']"> + <MkFolder> + <template #icon><i class="ti ti-flask"></i></template> + <template #label><SearchLabel>{{ i18n.ts.experimentalFeatures }}</SearchLabel></template> - <div class="_gaps_m"> - <MkSwitch v-model="devMode"> - <template #label>{{ i18n.ts.devMode }}</template> - </MkSwitch> - </div> - </MkFolder> - </div> - </FormSection> + <div class="_gaps_m"> + <MkSwitch v-model="enableCondensedLine"> + <template #label>Enable condensed line</template> + </MkSwitch> + <MkSwitch v-model="skipNoteRender"> + <template #label>Enable note render skipping</template> + </MkSwitch> + </div> + </MkFolder> + </SearchMarker> - <FormSection> - <FormLink to="/registry"><template #icon><i class="ti ti-adjustments"></i></template>{{ i18n.ts.registry }}</FormLink> - </FormSection> + <SearchMarker :keywords="['developer', 'mode', 'debug']"> + <MkFolder> + <template #icon><i class="ti ti-code"></i></template> + <template #label><SearchLabel>{{ i18n.ts.developer }}</SearchLabel></template> - <FormSection> - <div class="_gaps_s"> - <MkSwitch v-model="defaultWithReplies">{{ i18n.ts.withRepliesByDefaultForNewlyFollowed }}</MkSwitch> - <MkButton danger @click="updateRepliesAll(true)"><i class="ti ti-messages"></i> {{ i18n.ts.showRepliesToOthersInTimelineAll }}</MkButton> - <MkButton danger @click="updateRepliesAll(false)"><i class="ti ti-messages-off"></i> {{ i18n.ts.hideRepliesToOthersInTimelineAll }}</MkButton> - </div> - </FormSection> -</div> + <div class="_gaps_m"> + <MkSwitch v-model="devMode"> + <template #label>{{ i18n.ts.devMode }}</template> + </MkSwitch> + </div> + </MkFolder> + </SearchMarker> + </div> + </FormSection> + + <FormSection> + <FormLink to="/registry"><template #icon><i class="ti ti-adjustments"></i></template>{{ i18n.ts.registry }}</FormLink> + </FormSection> + + <FormSection> + <div class="_gaps_s"> + <MkSwitch v-model="defaultWithReplies">{{ i18n.ts.withRepliesByDefaultForNewlyFollowed }}</MkSwitch> + <MkButton danger @click="updateRepliesAll(true)"><i class="ti ti-messages"></i> {{ i18n.ts.showRepliesToOthersInTimelineAll }}</MkButton> + <MkButton danger @click="updateRepliesAll(false)"><i class="ti ti-messages-off"></i> {{ i18n.ts.hideRepliesToOthersInTimelineAll }}</MkButton> + </div> + </FormSection> + </div> +</SearchMarker> </template> <script lang="ts" setup> import { computed, watch } from 'vue'; +import XMigration from './migration.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import FormLink from '@/components/form/link.vue'; import MkFolder from '@/components/MkFolder.vue'; diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue new file mode 100644 index 0000000000..fe718bfa69 --- /dev/null +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -0,0 +1,434 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<SearchMarker path="/settings/preferences" :label="i18n.ts.preferences" :keywords="['general', 'preferences']" icon="ti ti-adjustments"> + <div class="_gaps_m"> + <SearchMarker :keywords="['language']"> + <MkSelect v-model="lang"> + <template #label><SearchLabel>{{ i18n.ts.uiLanguage }}</SearchLabel></template> + <option v-for="x in langs" :key="x[0]" :value="x[0]">{{ x[1] }}</option> + <template #caption> + <I18n :src="i18n.ts.i18nInfo" tag="span"> + <template #link> + <MkLink url="https://crowdin.com/project/misskey">Crowdin</MkLink> + </template> + </I18n> + </template> + </MkSelect> + </SearchMarker> + + <SearchMarker :keywords="['device', 'type', 'kind', 'smartphone', 'tablet', 'desktop']"> + <MkRadios v-model="overridedDeviceKind"> + <template #label><SearchLabel>{{ i18n.ts.overridedDeviceKind }}</SearchLabel></template> + <option :value="null">{{ i18n.ts.auto }}</option> + <option value="smartphone"><i class="ti ti-device-mobile"/> {{ i18n.ts.smartphone }}</option> + <option value="tablet"><i class="ti ti-device-tablet"/> {{ i18n.ts.tablet }}</option> + <option value="desktop"><i class="ti ti-device-desktop"/> {{ i18n.ts.desktop }}</option> + </MkRadios> + </SearchMarker> + + <FormSection> + <div class="_gaps_s"> + <SearchMarker :keywords="['post', 'form', 'timeline']"> + <MkSwitch v-model="showFixedPostForm"> + <template #label><SearchLabel>{{ i18n.ts.showFixedPostForm }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['post', 'form', 'timeline', 'channel']"> + <MkSwitch v-model="showFixedPostFormInChannel"> + <template #label><SearchLabel>{{ i18n.ts.showFixedPostFormInChannel }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['pinned', 'list']"> + <MkFolder> + <template #label><SearchLabel>{{ i18n.ts.pinnedList }}</SearchLabel></template> + <!-- 複数ピン止め管理できるようにしたいけどめんどいので一旦ひとつのみ --> + <MkButton v-if="defaultStore.reactiveState.pinnedUserLists.value.length === 0" @click="setPinnedList()">{{ i18n.ts.add }}</MkButton> + <MkButton v-else danger @click="removePinnedList()"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> + </MkFolder> + </SearchMarker> + + <SearchMarker :keywords="['mfm', 'enable', 'show', 'advanced', 'picker', 'form', 'function', 'fn']"> + <MkSwitch v-model="enableQuickAddMfmFunction"> + <template #label><SearchLabel>{{ i18n.ts.enableQuickAddMfmFunction }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + </div> + </FormSection> + + <SearchMarker :keywords="['note']"> + <FormSection> + <template #label><SearchLabel>{{ i18n.ts.note }}</SearchLabel></template> + + <div class="_gaps_m"> + <div class="_gaps_s"> + <SearchMarker :keywords="['renote']"> + <MkSwitch v-model="collapseRenotes"> + <template #label><SearchLabel>{{ i18n.ts.collapseRenotes }}</SearchLabel></template> + <template #caption><SearchKeyword>{{ i18n.ts.collapseRenotesDescription }}</SearchKeyword></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['hover', 'show', 'footer', 'action']"> + <MkSwitch v-model="showNoteActionsOnlyHover"> + <template #label><SearchLabel>{{ i18n.ts.showNoteActionsOnlyHover }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['footer', 'action', 'clip', 'show']"> + <MkSwitch v-model="showClipButtonInNoteFooter"> + <template #label><SearchLabel>{{ i18n.ts.showClipButtonInNoteFooter }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['mfm', 'enable', 'show', 'advanced']"> + <MkSwitch v-model="advancedMfm"> + <template #label><SearchLabel>{{ i18n.ts.enableAdvancedMfm }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['reaction', 'count', 'show']"> + <MkSwitch v-model="showReactionsCount"> + <template #label><SearchLabel>{{ i18n.ts.showReactionsCount }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['image', 'photo', 'picture', 'media', 'thumbnail', 'quality', 'raw', 'attachment']"> + <MkSwitch v-model="loadRawImages"> + <template #label><SearchLabel>{{ i18n.ts.loadRawImages }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + </div> + </div> + </FormSection> + </SearchMarker> + + <SearchMarker :keywords="['notification']"> + <FormSection> + <template #label><SearchLabel>{{ i18n.ts.notifications }}</SearchLabel></template> + + <div class="_gaps_m"> + <SearchMarker :keywords="['group']"> + <MkSwitch v-model="useGroupedNotifications"> + <template #label><SearchLabel>{{ i18n.ts.useGroupedNotifications }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + </div> + </FormSection> + </SearchMarker> + + <SearchMarker :keywords="['behavior']"> + <FormSection> + <template #label><SearchLabel>{{ i18n.ts.behavior }}</SearchLabel></template> + + <div class="_gaps_m"> + <div class="_gaps_s"> + <SearchMarker :keywords="['image', 'photo', 'picture', 'media', 'thumbnail', 'new', 'tab']"> + <MkSwitch v-model="imageNewTab"> + <template #label><SearchLabel>{{ i18n.ts.openImageInNewTab }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['reaction', 'picker', 'contextmenu', 'open']"> + <MkSwitch v-model="useReactionPickerForContextMenu"> + <template #label><SearchLabel>{{ i18n.ts.useReactionPickerForContextMenu }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['load', 'auto', 'more']"> + <MkSwitch v-model="enableInfiniteScroll"> + <template #label><SearchLabel>{{ i18n.ts.enableInfiniteScroll }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['disable', 'streaming', 'timeline']"> + <MkSwitch v-model="disableStreamingTimeline"> + <template #label><SearchLabel>{{ i18n.ts.disableStreamingTimeline }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['follow', 'confirm', 'always']"> + <MkSwitch v-model="alwaysConfirmFollow"> + <template #label><SearchLabel>{{ i18n.ts.alwaysConfirmFollow }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['sensitive', 'nsfw', 'media', 'image', 'photo', 'picture', 'attachment', 'confirm']"> + <MkSwitch v-model="confirmWhenRevealingSensitiveMedia"> + <template #label><SearchLabel>{{ i18n.ts.confirmWhenRevealingSensitiveMedia }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['reaction', 'confirm']"> + <MkSwitch v-model="confirmOnReact"> + <template #label><SearchLabel>{{ i18n.ts.confirmOnReact }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + </div> + + <SearchMarker :keywords="['server', 'disconnect', 'reconnect', 'reload', 'streaming']"> + <MkSelect v-model="serverDisconnectedBehavior"> + <template #label><SearchLabel>{{ i18n.ts.whenServerDisconnected }}</SearchLabel></template> + <option value="reload">{{ i18n.ts._serverDisconnectedBehavior.reload }}</option> + <option value="dialog">{{ i18n.ts._serverDisconnectedBehavior.dialog }}</option> + <option value="quiet">{{ i18n.ts._serverDisconnectedBehavior.quiet }}</option> + </MkSelect> + </SearchMarker> + + <SearchMarker :keywords="['cache', 'page']"> + <MkRange v-model="numberOfPageCache" :min="1" :max="10" :step="1" easing> + <template #label><SearchLabel>{{ i18n.ts.numberOfPageCache }}</SearchLabel></template> + <template #caption>{{ i18n.ts.numberOfPageCacheDescription }}</template> + </MkRange> + </SearchMarker> + + <SearchMarker :label="i18n.ts.dataSaver" :keywords="['datasaver']"> + <MkFolder> + <template #label><SearchLabel>{{ i18n.ts.dataSaver }}</SearchLabel></template> + + <div class="_gaps_m"> + <MkInfo>{{ i18n.ts.reloadRequiredToApplySettings }}</MkInfo> + + <div class="_buttons"> + <MkButton inline @click="enableAllDataSaver">{{ i18n.ts.enableAll }}</MkButton> + <MkButton inline @click="disableAllDataSaver">{{ i18n.ts.disableAll }}</MkButton> + </div> + <div class="_gaps_m"> + <MkSwitch v-model="dataSaver.media"> + {{ i18n.ts._dataSaver._media.title }} + <template #caption>{{ i18n.ts._dataSaver._media.description }}</template> + </MkSwitch> + <MkSwitch v-model="dataSaver.avatar"> + {{ i18n.ts._dataSaver._avatar.title }} + <template #caption>{{ i18n.ts._dataSaver._avatar.description }}</template> + </MkSwitch> + <MkSwitch v-model="dataSaver.urlPreview"> + {{ i18n.ts._dataSaver._urlPreview.title }} + <template #caption>{{ i18n.ts._dataSaver._urlPreview.description }}</template> + </MkSwitch> + <MkSwitch v-model="dataSaver.code"> + {{ i18n.ts._dataSaver._code.title }} + <template #caption>{{ i18n.ts._dataSaver._code.description }}</template> + </MkSwitch> + </div> + </div> + </MkFolder> + </SearchMarker> + </div> + </FormSection> + </SearchMarker> + + <SearchMarker> + <FormSection> + <template #label><SearchLabel>{{ i18n.ts.other }}</SearchLabel></template> + + <div class="_gaps"> + <SearchMarker :keywords="['ad', 'show']"> + <MkSwitch v-model="forceShowAds"> + <template #label><SearchLabel>{{ i18n.ts.forceShowAds }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker> + <MkRadios v-model="hemisphere"> + <template #label><SearchLabel>{{ i18n.ts.hemisphere }}</SearchLabel></template> + <option value="N">{{ i18n.ts._hemisphere.N }}</option> + <option value="S">{{ i18n.ts._hemisphere.S }}</option> + <template #caption>{{ i18n.ts._hemisphere.caption }}</template> + </MkRadios> + </SearchMarker> + + <SearchMarker :keywords="['emoji', 'dictionary', 'additional', 'extra']"> + <MkFolder> + <template #label><SearchLabel>{{ i18n.ts.additionalEmojiDictionary }}</SearchLabel></template> + <div class="_buttons"> + <template v-for="lang in emojiIndexLangs" :key="lang"> + <MkButton v-if="defaultStore.reactiveState.additionalUnicodeEmojiIndexes.value[lang]" danger @click="removeEmojiIndex(lang)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }} ({{ getEmojiIndexLangName(lang) }})</MkButton> + <MkButton v-else @click="downloadEmojiIndex(lang)"><i class="ti ti-download"></i> {{ getEmojiIndexLangName(lang) }}{{ defaultStore.reactiveState.additionalUnicodeEmojiIndexes.value[lang] ? ` (${ i18n.ts.installed })` : '' }}</MkButton> + </template> + </div> + </MkFolder> + </SearchMarker> + + <FormLink to="/settings/navbar">{{ i18n.ts.navbar }}</FormLink> + <FormLink to="/settings/statusbar">{{ i18n.ts.statusbar }}</FormLink> + </div> + </FormSection> + </SearchMarker> + + <FormSection> + <div class="_gaps"> + <FormLink to="/settings/deck">{{ i18n.ts.deck }}</FormLink> + </div> + </FormSection> + </div> +</SearchMarker> +</template> + +<script lang="ts" setup> +import { computed, ref, watch } from 'vue'; +import * as Misskey from 'misskey-js'; +import { langs } from '@@/js/config.js'; +import MkSwitch from '@/components/MkSwitch.vue'; +import MkSelect from '@/components/MkSelect.vue'; +import MkRadios from '@/components/MkRadios.vue'; +import MkRange from '@/components/MkRange.vue'; +import MkFolder from '@/components/MkFolder.vue'; +import MkButton from '@/components/MkButton.vue'; +import FormSection from '@/components/form/section.vue'; +import FormLink from '@/components/form/link.vue'; +import MkLink from '@/components/MkLink.vue'; +import MkInfo from '@/components/MkInfo.vue'; +import { defaultStore } from '@/store.js'; +import * as os from '@/os.js'; +import { instance } from '@/instance.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; +import { reloadAsk } from '@/scripts/reload-ask.js'; +import { i18n } from '@/i18n.js'; +import { definePageMetadata } from '@/scripts/page-metadata.js'; +import { miLocalStorage } from '@/local-storage.js'; + +const lang = ref(miLocalStorage.getItem('lang')); +const dataSaver = ref(defaultStore.state.dataSaver); + +const hemisphere = computed(defaultStore.makeGetterSetter('hemisphere')); +const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind')); +const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serverDisconnectedBehavior')); +const showNoteActionsOnlyHover = computed(defaultStore.makeGetterSetter('showNoteActionsOnlyHover')); +const showClipButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showClipButtonInNoteFooter')); +const collapseRenotes = computed(defaultStore.makeGetterSetter('collapseRenotes')); +const advancedMfm = computed(defaultStore.makeGetterSetter('advancedMfm')); +const showReactionsCount = computed(defaultStore.makeGetterSetter('showReactionsCount')); +const enableQuickAddMfmFunction = computed(defaultStore.makeGetterSetter('enableQuickAddMfmFunction')); +const forceShowAds = computed(defaultStore.makeGetterSetter('forceShowAds')); +const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages')); +const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab')); +const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm')); +const showFixedPostFormInChannel = computed(defaultStore.makeGetterSetter('showFixedPostFormInChannel')); +const numberOfPageCache = computed(defaultStore.makeGetterSetter('numberOfPageCache')); +const enableInfiniteScroll = computed(defaultStore.makeGetterSetter('enableInfiniteScroll')); +const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('useReactionPickerForContextMenu')); +const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disableStreamingTimeline')); +const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications')); +const alwaysConfirmFollow = computed(defaultStore.makeGetterSetter('alwaysConfirmFollow')); +const confirmWhenRevealingSensitiveMedia = computed(defaultStore.makeGetterSetter('confirmWhenRevealingSensitiveMedia')); +const confirmOnReact = computed(defaultStore.makeGetterSetter('confirmOnReact')); +const contextMenu = computed(defaultStore.makeGetterSetter('contextMenu')); + +watch(lang, () => { + miLocalStorage.setItem('lang', lang.value as string); + miLocalStorage.removeItem('locale'); + miLocalStorage.removeItem('localeVersion'); +}); + +watch([ + hemisphere, + lang, + enableInfiniteScroll, + showNoteActionsOnlyHover, + overridedDeviceKind, + disableStreamingTimeline, + alwaysConfirmFollow, + confirmWhenRevealingSensitiveMedia, + contextMenu, +], async () => { + await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true }); +}); + +const emojiIndexLangs = ['en-US', 'ja-JP', 'ja-JP_hira'] as const; + +function getEmojiIndexLangName(targetLang: typeof emojiIndexLangs[number]) { + if (langs.find(x => x[0] === targetLang)) { + return langs.find(x => x[0] === targetLang)![1]; + } else { + // 絵文字辞書限定の言語定義 + switch (targetLang) { + case 'ja-JP_hira': return 'ひらがな'; + default: return targetLang; + } + } +} + +function downloadEmojiIndex(lang: typeof emojiIndexLangs[number]) { + async function main() { + const currentIndexes = defaultStore.state.additionalUnicodeEmojiIndexes; + + function download() { + switch (lang) { + case 'en-US': return import('../../unicode-emoji-indexes/en-US.json').then(x => x.default); + case 'ja-JP': return import('../../unicode-emoji-indexes/ja-JP.json').then(x => x.default); + case 'ja-JP_hira': return import('../../unicode-emoji-indexes/ja-JP_hira.json').then(x => x.default); + default: throw new Error('unrecognized lang: ' + lang); + } + } + + currentIndexes[lang] = await download(); + await defaultStore.set('additionalUnicodeEmojiIndexes', currentIndexes); + } + + os.promiseDialog(main()); +} + +function removeEmojiIndex(lang: string) { + async function main() { + const currentIndexes = defaultStore.state.additionalUnicodeEmojiIndexes; + delete currentIndexes[lang]; + await defaultStore.set('additionalUnicodeEmojiIndexes', currentIndexes); + } + + os.promiseDialog(main()); +} + +async function setPinnedList() { + const lists = await misskeyApi('users/lists/list'); + const { canceled, result: list } = await os.select({ + title: i18n.ts.selectList, + items: lists.map(x => ({ + value: x, text: x.name, + })), + }); + if (canceled) return; + + defaultStore.set('pinnedUserLists', [list]); +} + +function removePinnedList() { + defaultStore.set('pinnedUserLists', []); +} + +function enableAllDataSaver() { + const g = { ...defaultStore.state.dataSaver }; + + Object.keys(g).forEach((key) => { g[key] = true; }); + + dataSaver.value = g; +} + +function disableAllDataSaver() { + const g = { ...defaultStore.state.dataSaver }; + + Object.keys(g).forEach((key) => { g[key] = false; }); + + dataSaver.value = g; +} + +watch(dataSaver, (to) => { + defaultStore.set('dataSaver', to); +}, { + deep: true, +}); + +const headerActions = computed(() => []); + +const headerTabs = computed(() => []); + +definePageMetadata(() => ({ + title: i18n.ts.general, + icon: 'ti ti-adjustments', +})); +</script> diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue index 54a5aeb6c1..cd0d54a73b 100644 --- a/packages/frontend/src/pages/settings/privacy.vue +++ b/packages/frontend/src/pages/settings/privacy.vue @@ -4,158 +4,208 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="_gaps_m"> - <MkSwitch v-model="isLocked" @update:modelValue="save()">{{ i18n.ts.makeFollowManuallyApprove }}<template #caption>{{ i18n.ts.lockedAccountInfo }}</template></MkSwitch> - <MkSwitch v-if="isLocked" v-model="autoAcceptFollowed" @update:modelValue="save()">{{ i18n.ts.autoAcceptFollowed }}</MkSwitch> +<SearchMarker path="/settings/privacy" :label="i18n.ts.privacy" :keywords="['privacy']" icon="ti ti-lock-open"> + <div class="_gaps_m"> + <SearchMarker :keywords="['follow', 'lock']"> + <MkSwitch v-model="isLocked" @update:modelValue="save()"> + <template #label><SearchLabel>{{ i18n.ts.makeFollowManuallyApprove }}</SearchLabel></template> + <template #caption><SearchKeyword>{{ i18n.ts.lockedAccountInfo }}</SearchKeyword></template> + </MkSwitch> + </SearchMarker> - <MkSwitch v-model="publicReactions" @update:modelValue="save()"> - {{ i18n.ts.makeReactionsPublic }} - <template #caption>{{ i18n.ts.makeReactionsPublicDescription }}</template> - </MkSwitch> + <MkDisableSection :disabled="!isLocked"> + <SearchMarker :keywords="['follow', 'auto', 'accept']"> + <MkSwitch v-model="autoAcceptFollowed" @update:modelValue="save()"> + <template #label><SearchLabel>{{ i18n.ts.autoAcceptFollowed }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + </MkDisableSection> - <MkSelect v-model="followingVisibility" @update:modelValue="save()"> - <template #label>{{ i18n.ts.followingVisibility }}</template> - <option value="public">{{ i18n.ts._ffVisibility.public }}</option> - <option value="followers">{{ i18n.ts._ffVisibility.followers }}</option> - <option value="private">{{ i18n.ts._ffVisibility.private }}</option> - </MkSelect> + <SearchMarker :keywords="['reaction', 'public']"> + <MkSwitch v-model="publicReactions" @update:modelValue="save()"> + <template #label><SearchLabel>{{ i18n.ts.makeReactionsPublic }}</SearchLabel></template> + <template #caption><SearchKeyword>{{ i18n.ts.makeReactionsPublicDescription }}</SearchKeyword></template> + </MkSwitch> + </SearchMarker> - <MkSelect v-model="followersVisibility" @update:modelValue="save()"> - <template #label>{{ i18n.ts.followersVisibility }}</template> - <option value="public">{{ i18n.ts._ffVisibility.public }}</option> - <option value="followers">{{ i18n.ts._ffVisibility.followers }}</option> - <option value="private">{{ i18n.ts._ffVisibility.private }}</option> - </MkSelect> + <SearchMarker :keywords="['following', 'visibility']"> + <MkSelect v-model="followingVisibility" @update:modelValue="save()"> + <template #label><SearchLabel>{{ i18n.ts.followingVisibility }}</SearchLabel></template> + <option value="public">{{ i18n.ts._ffVisibility.public }}</option> + <option value="followers">{{ i18n.ts._ffVisibility.followers }}</option> + <option value="private">{{ i18n.ts._ffVisibility.private }}</option> + </MkSelect> + </SearchMarker> - <MkSwitch v-model="hideOnlineStatus" @update:modelValue="save()"> - {{ i18n.ts.hideOnlineStatus }} - <template #caption>{{ i18n.ts.hideOnlineStatusDescription }}</template> - </MkSwitch> - <MkSwitch v-model="noCrawle" @update:modelValue="save()"> - {{ i18n.ts.noCrawle }} - <template #caption>{{ i18n.ts.noCrawleDescription }}</template> - </MkSwitch> - <MkSwitch v-model="preventAiLearning" @update:modelValue="save()"> - {{ i18n.ts.preventAiLearning }} - <template #caption>{{ i18n.ts.preventAiLearningDescription }}</template> - </MkSwitch> - <MkSwitch v-model="isExplorable" @update:modelValue="save()"> - {{ i18n.ts.makeExplorable }} - <template #caption>{{ i18n.ts.makeExplorableDescription }}</template> - </MkSwitch> + <SearchMarker :keywords="['follower', 'visibility']"> + <MkSelect v-model="followersVisibility" @update:modelValue="save()"> + <template #label><SearchLabel>{{ i18n.ts.followersVisibility }}</SearchLabel></template> + <option value="public">{{ i18n.ts._ffVisibility.public }}</option> + <option value="followers">{{ i18n.ts._ffVisibility.followers }}</option> + <option value="private">{{ i18n.ts._ffVisibility.private }}</option> + </MkSelect> + </SearchMarker> - <FormSection> - <template #label>{{ i18n.ts.lockdown }}<span class="_beta">{{ i18n.ts.beta }}</span></template> + <SearchMarker :keywords="['online', 'status']"> + <MkSwitch v-model="hideOnlineStatus" @update:modelValue="save()"> + <template #label><SearchLabel>{{ i18n.ts.hideOnlineStatus }}</SearchLabel></template> + <template #caption><SearchKeyword>{{ i18n.ts.hideOnlineStatusDescription }}</SearchKeyword></template> + </MkSwitch> + </SearchMarker> - <div class="_gaps_m"> - <MkSwitch :modelValue="requireSigninToViewContents" @update:modelValue="update_requireSigninToViewContents"> - {{ i18n.ts._accountSettings.requireSigninToViewContents }} - <template #caption> - <div>{{ i18n.ts._accountSettings.requireSigninToViewContentsDescription1 }}</div> - <div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.requireSigninToViewContentsDescription2 }}</div> - <div v-if="instance.federation !== 'none'"><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.requireSigninToViewContentsDescription3 }}</div> - </template> + <SearchMarker :keywords="['crawle', 'index', 'search']"> + <MkSwitch v-model="noCrawle" @update:modelValue="save()"> + <template #label><SearchLabel>{{ i18n.ts.noCrawle }}</SearchLabel></template> + <template #caption><SearchKeyword>{{ i18n.ts.noCrawleDescription }}</SearchKeyword></template> </MkSwitch> + </SearchMarker> - <FormSlot> - <template #label>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBefore }}</template> + <SearchMarker :keywords="['crawle', 'ai']"> + <MkSwitch v-model="preventAiLearning" @update:modelValue="save()"> + <template #label><SearchLabel>{{ i18n.ts.preventAiLearning }}</SearchLabel></template> + <template #caption><SearchKeyword>{{ i18n.ts.preventAiLearningDescription }}</SearchKeyword></template> + </MkSwitch> + </SearchMarker> - <div class="_gaps_s"> - <MkSelect :modelValue="makeNotesFollowersOnlyBefore_type" @update:modelValue="makeNotesFollowersOnlyBefore = $event === 'relative' ? -604800 : $event === 'absolute' ? Math.floor(Date.now() / 1000) : null"> - <option :value="null">{{ i18n.ts.none }}</option> - <option value="relative">{{ i18n.ts._accountSettings.notesHavePassedSpecifiedPeriod }}</option> - <option value="absolute">{{ i18n.ts._accountSettings.notesOlderThanSpecifiedDateAndTime }}</option> - </MkSelect> + <SearchMarker :keywords="['explore']"> + <MkSwitch v-model="isExplorable" @update:modelValue="save()"> + <template #label><SearchLabel>{{ i18n.ts.makeExplorable }}</SearchLabel></template> + <template #caption><SearchKeyword>{{ i18n.ts.makeExplorableDescription }}</SearchKeyword></template> + </MkSwitch> + </SearchMarker> - <MkSelect v-if="makeNotesFollowersOnlyBefore_type === 'relative'" v-model="makeNotesFollowersOnlyBefore"> - <option :value="-3600">{{ i18n.ts.oneHour }}</option> - <option :value="-86400">{{ i18n.ts.oneDay }}</option> - <option :value="-259200">{{ i18n.ts.threeDays }}</option> - <option :value="-604800">{{ i18n.ts.oneWeek }}</option> - <option :value="-2592000">{{ i18n.ts.oneMonth }}</option> - <option :value="-7776000">{{ i18n.ts.threeMonths }}</option> - <option :value="-31104000">{{ i18n.ts.oneYear }}</option> - </MkSelect> + <SearchMarker :keywords="['lockdown']"> + <FormSection> + <template #label><SearchLabel>{{ i18n.ts.lockdown }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template> - <MkInput - v-if="makeNotesFollowersOnlyBefore_type === 'absolute'" - :modelValue="formatDateTimeString(new Date(makeNotesFollowersOnlyBefore * 1000), 'yyyy-MM-dd')" - type="date" - :manualSave="true" - @update:modelValue="makeNotesFollowersOnlyBefore = Math.floor(new Date($event).getTime() / 1000)" - > - </MkInput> - </div> + <div class="_gaps_m"> + <SearchMarker :keywords="['login', 'signin']"> + <MkSwitch :modelValue="requireSigninToViewContents" @update:modelValue="update_requireSigninToViewContents"> + <template #label><SearchLabel>{{ i18n.ts._accountSettings.requireSigninToViewContents }}</SearchLabel></template> + <template #caption> + <div>{{ i18n.ts._accountSettings.requireSigninToViewContentsDescription1 }}</div> + <div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.requireSigninToViewContentsDescription2 }}</div> + <div v-if="instance.federation !== 'none'"><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.requireSigninToViewContentsDescription3 }}</div> + </template> + </MkSwitch> + </SearchMarker> - <template #caption> - <div>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBeforeDescription }}</div> - <div v-if="instance.federation !== 'none'"><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.mayNotEffectForFederatedNotes }}</div> - </template> - </FormSlot> + <SearchMarker :keywords="['follower']"> + <FormSlot> + <template #label><SearchLabel>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBefore }}</SearchLabel></template> - <FormSlot> - <template #label>{{ i18n.ts._accountSettings.makeNotesHiddenBefore }}</template> + <div class="_gaps_s"> + <MkSelect :modelValue="makeNotesFollowersOnlyBefore_type" @update:modelValue="makeNotesFollowersOnlyBefore = $event === 'relative' ? -604800 : $event === 'absolute' ? Math.floor(Date.now() / 1000) : null"> + <option :value="null">{{ i18n.ts.none }}</option> + <option value="relative">{{ i18n.ts._accountSettings.notesHavePassedSpecifiedPeriod }}</option> + <option value="absolute">{{ i18n.ts._accountSettings.notesOlderThanSpecifiedDateAndTime }}</option> + </MkSelect> - <div class="_gaps_s"> - <MkSelect :modelValue="makeNotesHiddenBefore_type" @update:modelValue="makeNotesHiddenBefore = $event === 'relative' ? -604800 : $event === 'absolute' ? Math.floor(Date.now() / 1000) : null"> - <option :value="null">{{ i18n.ts.none }}</option> - <option value="relative">{{ i18n.ts._accountSettings.notesHavePassedSpecifiedPeriod }}</option> - <option value="absolute">{{ i18n.ts._accountSettings.notesOlderThanSpecifiedDateAndTime }}</option> - </MkSelect> + <MkSelect v-if="makeNotesFollowersOnlyBefore_type === 'relative'" v-model="makeNotesFollowersOnlyBefore"> + <option :value="-3600">{{ i18n.ts.oneHour }}</option> + <option :value="-86400">{{ i18n.ts.oneDay }}</option> + <option :value="-259200">{{ i18n.ts.threeDays }}</option> + <option :value="-604800">{{ i18n.ts.oneWeek }}</option> + <option :value="-2592000">{{ i18n.ts.oneMonth }}</option> + <option :value="-7776000">{{ i18n.ts.threeMonths }}</option> + <option :value="-31104000">{{ i18n.ts.oneYear }}</option> + </MkSelect> - <MkSelect v-if="makeNotesHiddenBefore_type === 'relative'" v-model="makeNotesHiddenBefore"> - <option :value="-3600">{{ i18n.ts.oneHour }}</option> - <option :value="-86400">{{ i18n.ts.oneDay }}</option> - <option :value="-259200">{{ i18n.ts.threeDays }}</option> - <option :value="-604800">{{ i18n.ts.oneWeek }}</option> - <option :value="-2592000">{{ i18n.ts.oneMonth }}</option> - <option :value="-7776000">{{ i18n.ts.threeMonths }}</option> - <option :value="-31104000">{{ i18n.ts.oneYear }}</option> - </MkSelect> + <MkInput + v-if="makeNotesFollowersOnlyBefore_type === 'absolute'" + :modelValue="formatDateTimeString(new Date(makeNotesFollowersOnlyBefore * 1000), 'yyyy-MM-dd')" + type="date" + :manualSave="true" + @update:modelValue="makeNotesFollowersOnlyBefore = Math.floor(new Date($event).getTime() / 1000)" + > + </MkInput> + </div> - <MkInput - v-if="makeNotesHiddenBefore_type === 'absolute'" - :modelValue="formatDateTimeString(new Date(makeNotesHiddenBefore * 1000), 'yyyy-MM-dd')" - type="date" - :manualSave="true" - @update:modelValue="makeNotesHiddenBefore = Math.floor(new Date($event).getTime() / 1000)" - > - </MkInput> - </div> + <template #caption> + <div><SearchKeyword>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBeforeDescription }}</SearchKeyword></div> + <div v-if="instance.federation !== 'none'"><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.mayNotEffectForFederatedNotes }}</div> + </template> + </FormSlot> + </SearchMarker> - <template #caption> - <div>{{ i18n.ts._accountSettings.makeNotesHiddenBeforeDescription }}</div> - <div v-if="instance.federation !== 'none'"><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.mayNotEffectForFederatedNotes }}</div> - </template> - </FormSlot> - </div> - </FormSection> + <SearchMarker :keywords="['hidden']"> + <FormSlot> + <template #label><SearchLabel>{{ i18n.ts._accountSettings.makeNotesHiddenBefore }}</SearchLabel></template> - <FormSection> - <div class="_gaps_m"> - <MkSwitch v-model="rememberNoteVisibility" @update:modelValue="save()">{{ i18n.ts.rememberNoteVisibility }}</MkSwitch> - <MkFolder v-if="!rememberNoteVisibility"> - <template #label>{{ i18n.ts.defaultNoteVisibility }}</template> - <template v-if="defaultNoteVisibility === 'public'" #suffix>{{ i18n.ts._visibility.public }}</template> - <template v-else-if="defaultNoteVisibility === 'home'" #suffix>{{ i18n.ts._visibility.home }}</template> - <template v-else-if="defaultNoteVisibility === 'followers'" #suffix>{{ i18n.ts._visibility.followers }}</template> - <template v-else-if="defaultNoteVisibility === 'specified'" #suffix>{{ i18n.ts._visibility.specified }}</template> + <div class="_gaps_s"> + <MkSelect :modelValue="makeNotesHiddenBefore_type" @update:modelValue="makeNotesHiddenBefore = $event === 'relative' ? -604800 : $event === 'absolute' ? Math.floor(Date.now() / 1000) : null"> + <option :value="null">{{ i18n.ts.none }}</option> + <option value="relative">{{ i18n.ts._accountSettings.notesHavePassedSpecifiedPeriod }}</option> + <option value="absolute">{{ i18n.ts._accountSettings.notesOlderThanSpecifiedDateAndTime }}</option> + </MkSelect> - <div class="_gaps_m"> - <MkSelect v-model="defaultNoteVisibility"> - <option value="public">{{ i18n.ts._visibility.public }}</option> - <option value="home">{{ i18n.ts._visibility.home }}</option> - <option value="followers">{{ i18n.ts._visibility.followers }}</option> - <option value="specified">{{ i18n.ts._visibility.specified }}</option> - </MkSelect> - <MkSwitch v-model="defaultNoteLocalOnly">{{ i18n.ts._visibility.disableFederation }}</MkSwitch> + <MkSelect v-if="makeNotesHiddenBefore_type === 'relative'" v-model="makeNotesHiddenBefore"> + <option :value="-3600">{{ i18n.ts.oneHour }}</option> + <option :value="-86400">{{ i18n.ts.oneDay }}</option> + <option :value="-259200">{{ i18n.ts.threeDays }}</option> + <option :value="-604800">{{ i18n.ts.oneWeek }}</option> + <option :value="-2592000">{{ i18n.ts.oneMonth }}</option> + <option :value="-7776000">{{ i18n.ts.threeMonths }}</option> + <option :value="-31104000">{{ i18n.ts.oneYear }}</option> + </MkSelect> + + <MkInput + v-if="makeNotesHiddenBefore_type === 'absolute'" + :modelValue="formatDateTimeString(new Date(makeNotesHiddenBefore * 1000), 'yyyy-MM-dd')" + type="date" + :manualSave="true" + @update:modelValue="makeNotesHiddenBefore = Math.floor(new Date($event).getTime() / 1000)" + > + </MkInput> + </div> + + <template #caption> + <div><SearchKeyword>{{ i18n.ts._accountSettings.makeNotesHiddenBeforeDescription }}</SearchKeyword></div> + <div v-if="instance.federation !== 'none'"><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.mayNotEffectForFederatedNotes }}</div> + </template> + </FormSlot> + </SearchMarker> </div> - </MkFolder> - </div> - </FormSection> + </FormSection> + </SearchMarker> + + <FormSection> + <div class="_gaps_m"> + <SearchMarker :keywords="['remember', 'keep', 'note', 'visibility']"> + <MkSwitch v-model="rememberNoteVisibility" @update:modelValue="save()"> + <template #label><SearchLabel>{{ i18n.ts.rememberNoteVisibility }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> - <MkSwitch v-model="keepCw" @update:modelValue="save()">{{ i18n.ts.keepCw }}</MkSwitch> -</div> + <SearchMarker :keywords="['default', 'note', 'visibility']"> + <MkFolder v-if="!rememberNoteVisibility"> + <template #label><SearchLabel>{{ i18n.ts.defaultNoteVisibility }}</SearchLabel></template> + <template v-if="defaultNoteVisibility === 'public'" #suffix>{{ i18n.ts._visibility.public }}</template> + <template v-else-if="defaultNoteVisibility === 'home'" #suffix>{{ i18n.ts._visibility.home }}</template> + <template v-else-if="defaultNoteVisibility === 'followers'" #suffix>{{ i18n.ts._visibility.followers }}</template> + <template v-else-if="defaultNoteVisibility === 'specified'" #suffix>{{ i18n.ts._visibility.specified }}</template> + + <div class="_gaps_m"> + <MkSelect v-model="defaultNoteVisibility"> + <option value="public">{{ i18n.ts._visibility.public }}</option> + <option value="home">{{ i18n.ts._visibility.home }}</option> + <option value="followers">{{ i18n.ts._visibility.followers }}</option> + <option value="specified">{{ i18n.ts._visibility.specified }}</option> + </MkSelect> + <MkSwitch v-model="defaultNoteLocalOnly">{{ i18n.ts._visibility.disableFederation }}</MkSwitch> + </div> + </MkFolder> + </SearchMarker> + </div> + </FormSection> + + <SearchMarker :keywords="['remember', 'keep', 'note', 'cw']"> + <MkSwitch v-model="keepCw" @update:modelValue="save()"> + <template #label><SearchLabel>{{ i18n.ts.keepCw }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> + </div> +</SearchMarker> </template> <script lang="ts" setup> @@ -174,6 +224,7 @@ import FormSlot from '@/components/form/slot.vue'; import { formatDateTimeString } from '@/scripts/format-time-string.js'; import MkInput from '@/components/MkInput.vue'; import * as os from '@/os.js'; +import MkDisableSection from '@/components/MkDisableSection.vue'; const $i = signinRequired(); diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index 561894d2b7..51148a1f72 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -4,115 +4,152 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="_gaps_m"> - <div class="_panel"> - <div :class="$style.banner" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }"> - <MkButton primary rounded :class="$style.bannerEdit" @click="changeBanner">{{ i18n.ts._profile.changeBanner }}</MkButton> - </div> - <div :class="$style.avatarContainer"> - <MkAvatar :class="$style.avatar" :user="$i" forceShowDecoration @click="changeAvatar"/> - <div class="_buttonsCenter"> - <MkButton primary rounded @click="changeAvatar">{{ i18n.ts._profile.changeAvatar }}</MkButton> - <MkButton primary rounded link to="/settings/avatar-decoration">{{ i18n.ts.decorate }} <i class="ti ti-sparkles"></i></MkButton> +<SearchMarker path="/settings/profile" :label="i18n.ts.profile" :keywords="['profile']" icon="ti ti-user"> + <div class="_gaps_m"> + <div class="_panel"> + <div :class="$style.banner" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }"> + <div :class="$style.bannerEdit"> + <SearchMarker :keywords="['banner', 'change']"> + <MkButton primary rounded @click="changeBanner"><SearchLabel>{{ i18n.ts._profile.changeBanner }}</SearchLabel></MkButton> + </SearchMarker> + </div> + </div> + <div :class="$style.avatarContainer"> + <MkAvatar :class="$style.avatar" :user="$i" forceShowDecoration @click="changeAvatar"/> + <div class="_buttonsCenter"> + <SearchMarker :keywords="['avatar', 'icon', 'change']"> + <MkButton primary rounded @click="changeAvatar"><SearchLabel>{{ i18n.ts._profile.changeAvatar }}</SearchLabel></MkButton> + </SearchMarker> + <MkButton primary rounded link to="/settings/avatar-decoration">{{ i18n.ts.decorate }} <i class="ti ti-sparkles"></i></MkButton> + </div> </div> </div> - </div> - - <MkInput v-model="profile.name" :max="30" manualSave :mfmAutocomplete="['emoji']"> - <template #label>{{ i18n.ts._profile.name }}</template> - </MkInput> - <MkTextarea v-model="profile.description" :max="500" tall manualSave mfmAutocomplete :mfmPreview="true"> - <template #label>{{ i18n.ts._profile.description }}</template> - <template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template> - </MkTextarea> + <SearchMarker :keywords="['name']"> + <MkInput v-model="profile.name" :max="30" manualSave :mfmAutocomplete="['emoji']"> + <template #label><SearchLabel>{{ i18n.ts._profile.name }}</SearchLabel></template> + </MkInput> + </SearchMarker> - <MkInput v-model="profile.location" manualSave> - <template #label>{{ i18n.ts.location }}</template> - <template #prefix><i class="ti ti-map-pin"></i></template> - </MkInput> + <SearchMarker :keywords="['description', 'bio']"> + <MkTextarea v-model="profile.description" :max="500" tall manualSave mfmAutocomplete :mfmPreview="true"> + <template #label><SearchLabel>{{ i18n.ts._profile.description }}</SearchLabel></template> + <template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template> + </MkTextarea> + </SearchMarker> - <MkInput v-model="profile.birthday" type="date" manualSave> - <template #label>{{ i18n.ts.birthday }}</template> - <template #prefix><i class="ti ti-cake"></i></template> - </MkInput> + <SearchMarker :keywords="['location', 'locale']"> + <MkInput v-model="profile.location" manualSave> + <template #label><SearchLabel>{{ i18n.ts.location }}</SearchLabel></template> + <template #prefix><i class="ti ti-map-pin"></i></template> + </MkInput> + </SearchMarker> - <MkSelect v-model="profile.lang"> - <template #label>{{ i18n.ts.language }}</template> - <option v-for="x in Object.keys(langmap)" :key="x" :value="x">{{ langmap[x].nativeName }}</option> - </MkSelect> - - <FormSlot> - <MkFolder> - <template #icon><i class="ti ti-list"></i></template> - <template #label>{{ i18n.ts._profile.metadataEdit }}</template> - <template #footer> - <div class="_buttons"> - <MkButton primary @click="saveFields"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> - <MkButton :disabled="fields.length >= 16" @click="addField"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> - <MkButton v-if="!fieldEditMode" :disabled="fields.length <= 1" danger @click="fieldEditMode = !fieldEditMode"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> - <MkButton v-else @click="fieldEditMode = !fieldEditMode"><i class="ti ti-arrows-sort"></i> {{ i18n.ts.rearrange }}</MkButton> - </div> - </template> + <SearchMarker :keywords="['birthday', 'birthdate', 'age']"> + <MkInput v-model="profile.birthday" type="date" manualSave> + <template #label><SearchLabel>{{ i18n.ts.birthday }}</SearchLabel></template> + <template #prefix><i class="ti ti-cake"></i></template> + </MkInput> + </SearchMarker> - <div :class="$style.metadataRoot" class="_gaps_s"> - <MkInfo>{{ i18n.ts._profile.verifiedLinkDescription }}</MkInfo> + <SearchMarker :keywords="['language', 'locale']"> + <MkSelect v-model="profile.lang"> + <template #label><SearchLabel>{{ i18n.ts.language }}</SearchLabel></template> + <option v-for="x in Object.keys(langmap)" :key="x" :value="x">{{ langmap[x].nativeName }}</option> + </MkSelect> + </SearchMarker> - <Sortable - v-model="fields" - class="_gaps_s" - itemKey="id" - :animation="150" - :handle="'.' + $style.dragItemHandle" - @start="e => e.item.classList.add('active')" - @end="e => e.item.classList.remove('active')" - > - <template #item="{element, index}"> - <div v-panel :class="$style.fieldDragItem"> - <button v-if="!fieldEditMode" class="_button" :class="$style.dragItemHandle" tabindex="-1"><i class="ti ti-menu"></i></button> - <button v-if="fieldEditMode" :disabled="fields.length <= 1" class="_button" :class="$style.dragItemRemove" @click="deleteField(index)"><i class="ti ti-x"></i></button> - <div :class="$style.dragItemForm"> - <FormSplit :minWidth="200"> - <MkInput v-model="element.name" small :placeholder="i18n.ts._profile.metadataLabel"> - </MkInput> - <MkInput v-model="element.value" small :placeholder="i18n.ts._profile.metadataContent"> - </MkInput> - </FormSplit> - </div> + <SearchMarker :keywords="['metadata']"> + <FormSlot> + <MkFolder> + <template #icon><i class="ti ti-list"></i></template> + <template #label><SearchLabel>{{ i18n.ts._profile.metadataEdit }}</SearchLabel></template> + <template #footer> + <div class="_buttons"> + <MkButton primary @click="saveFields"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> + <MkButton :disabled="fields.length >= 16" @click="addField"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> + <MkButton v-if="!fieldEditMode" :disabled="fields.length <= 1" danger @click="fieldEditMode = !fieldEditMode"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> + <MkButton v-else @click="fieldEditMode = !fieldEditMode"><i class="ti ti-arrows-sort"></i> {{ i18n.ts.rearrange }}</MkButton> </div> </template> - </Sortable> - </div> - </MkFolder> - <template #caption>{{ i18n.ts._profile.metadataDescription }}</template> - </FormSlot> - <MkInput v-model="profile.followedMessage" :max="200" manualSave :mfmPreview="false"> - <template #label>{{ i18n.ts._profile.followedMessage }}<span class="_beta">{{ i18n.ts.beta }}</span></template> - <template #caption> - <div>{{ i18n.ts._profile.followedMessageDescription }}</div> - <div>{{ i18n.ts._profile.followedMessageDescriptionForLockedAccount }}</div> - </template> - </MkInput> + <div :class="$style.metadataRoot" class="_gaps_s"> + <MkInfo>{{ i18n.ts._profile.verifiedLinkDescription }}</MkInfo> - <MkSelect v-model="reactionAcceptance"> - <template #label>{{ i18n.ts.reactionAcceptance }}</template> - <option :value="null">{{ i18n.ts.all }}</option> - <option value="likeOnlyForRemote">{{ i18n.ts.likeOnlyForRemote }}</option> - <option value="nonSensitiveOnly">{{ i18n.ts.nonSensitiveOnly }}</option> - <option value="nonSensitiveOnlyForLocalLikeOnlyForRemote">{{ i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote }}</option> - <option value="likeOnly">{{ i18n.ts.likeOnly }}</option> - </MkSelect> + <Sortable + v-model="fields" + class="_gaps_s" + itemKey="id" + :animation="150" + :handle="'.' + $style.dragItemHandle" + @start="e => e.item.classList.add('active')" + @end="e => e.item.classList.remove('active')" + > + <template #item="{element, index}"> + <div v-panel :class="$style.fieldDragItem"> + <button v-if="!fieldEditMode" class="_button" :class="$style.dragItemHandle" tabindex="-1"><i class="ti ti-menu"></i></button> + <button v-if="fieldEditMode" :disabled="fields.length <= 1" class="_button" :class="$style.dragItemRemove" @click="deleteField(index)"><i class="ti ti-x"></i></button> + <div :class="$style.dragItemForm"> + <FormSplit :minWidth="200"> + <MkInput v-model="element.name" small :placeholder="i18n.ts._profile.metadataLabel"> + </MkInput> + <MkInput v-model="element.value" small :placeholder="i18n.ts._profile.metadataContent"> + </MkInput> + </FormSplit> + </div> + </div> + </template> + </Sortable> + </div> + </MkFolder> + <template #caption>{{ i18n.ts._profile.metadataDescription }}</template> + </FormSlot> + </SearchMarker> - <MkFolder> - <template #label>{{ i18n.ts.advancedSettings }}</template> + <SearchMarker :keywords="['follow', 'message']"> + <MkInput v-model="profile.followedMessage" :max="200" manualSave :mfmPreview="false"> + <template #label><SearchLabel>{{ i18n.ts._profile.followedMessage }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template> + <template #caption> + <div><SearchKeyword>{{ i18n.ts._profile.followedMessageDescription }}</SearchKeyword></div> + <div>{{ i18n.ts._profile.followedMessageDescriptionForLockedAccount }}</div> + </template> + </MkInput> + </SearchMarker> - <div class="_gaps_m"> - <MkSwitch v-model="profile.isCat">{{ i18n.ts.flagAsCat }}<template #caption>{{ i18n.ts.flagAsCatDescription }}</template></MkSwitch> - <MkSwitch v-model="profile.isBot">{{ i18n.ts.flagAsBot }}<template #caption>{{ i18n.ts.flagAsBotDescription }}</template></MkSwitch> - </div> - </MkFolder> -</div> + <SearchMarker :keywords="['reaction']"> + <MkSelect v-model="reactionAcceptance"> + <template #label><SearchLabel>{{ i18n.ts.reactionAcceptance }}</SearchLabel></template> + <option :value="null">{{ i18n.ts.all }}</option> + <option value="likeOnlyForRemote">{{ i18n.ts.likeOnlyForRemote }}</option> + <option value="nonSensitiveOnly">{{ i18n.ts.nonSensitiveOnly }}</option> + <option value="nonSensitiveOnlyForLocalLikeOnlyForRemote">{{ i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote }}</option> + <option value="likeOnly">{{ i18n.ts.likeOnly }}</option> + </MkSelect> + </SearchMarker> + + <SearchMarker> + <MkFolder> + <template #label><SearchLabel>{{ i18n.ts.advancedSettings }}</SearchLabel></template> + + <div class="_gaps_m"> + <SearchMarker :keywords="['cat']"> + <MkSwitch v-model="profile.isCat"> + <template #label><SearchLabel>{{ i18n.ts.flagAsCat }}</SearchLabel></template> + <template #caption>{{ i18n.ts.flagAsCatDescription }}</template> + </MkSwitch> + </SearchMarker> + + <SearchMarker :keywords="['bot']"> + <MkSwitch v-model="profile.isBot"> + <template #label><SearchLabel>{{ i18n.ts.flagAsBot }}</SearchLabel></template> + <template #caption>{{ i18n.ts.flagAsBotDescription }}</template> + </MkSwitch> + </SearchMarker> + </div> + </MkFolder> + </SearchMarker> + </div> +</SearchMarker> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue index 8f9d4f858b..f365146e0a 100644 --- a/packages/frontend/src/pages/settings/security.vue +++ b/packages/frontend/src/pages/settings/security.vue @@ -4,39 +4,48 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="_gaps_m"> - <FormSection first> - <template #label>{{ i18n.ts.password }}</template> - <MkButton primary @click="change()">{{ i18n.ts.changePassword }}</MkButton> - </FormSection> +<SearchMarker path="/settings/security" :label="i18n.ts.security" :keywords="['security']" icon="ti ti-lock" :inlining="['2fa']"> + <div class="_gaps_m"> + <SearchMarker :keywords="['password']"> + <FormSection first> + <template #label><SearchLabel>{{ i18n.ts.password }}</SearchLabel></template> - <X2fa/> + <SearchMarker> + <MkButton primary @click="change()"> + <SearchLabel>{{ i18n.ts.changePassword }}</SearchLabel> + </MkButton> + </SearchMarker> + </FormSection> + </SearchMarker> - <FormSection> - <template #label>{{ i18n.ts.signinHistory }}</template> - <MkPagination :pagination="pagination" disableAutoLoad> - <template #default="{items}"> - <div> - <div v-for="item in items" :key="item.id" v-panel class="timnmucd"> - <header> - <i v-if="item.success" class="ti ti-check icon succ"></i> - <i v-else class="ti ti-circle-x icon fail"></i> - <code class="ip _monospace">{{ item.ip }}</code> - <MkTime :time="item.createdAt" class="time"/> - </header> + <X2fa/> + + <FormSection> + <template #label>{{ i18n.ts.signinHistory }}</template> + <MkPagination :pagination="pagination" disableAutoLoad> + <template #default="{items}"> + <div> + <div v-for="item in items" :key="item.id" v-panel class="timnmucd"> + <header> + <i v-if="item.success" class="ti ti-check icon succ"></i> + <i v-else class="ti ti-circle-x icon fail"></i> + <code class="ip _monospace">{{ item.ip }}</code> + <MkTime :time="item.createdAt" class="time"/> + </header> + </div> </div> - </div> - </template> - </MkPagination> - </FormSection> + </template> + </MkPagination> + </FormSection> - <FormSection> - <FormSlot> - <MkButton danger @click="regenerateToken"><i class="ti ti-refresh"></i> {{ i18n.ts.regenerateLoginToken }}</MkButton> - <template #caption>{{ i18n.ts.regenerateLoginTokenDescription }}</template> - </FormSlot> - </FormSection> -</div> + <FormSection> + <FormSlot> + <MkButton danger @click="regenerateToken"><i class="ti ti-refresh"></i> {{ i18n.ts.regenerateLoginToken }}</MkButton> + <template #caption>{{ i18n.ts.regenerateLoginTokenDescription }}</template> + </FormSlot> + </FormSection> + </div> +</SearchMarker> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue index bf461f173b..1df2d89277 100644 --- a/packages/frontend/src/pages/settings/sounds.vue +++ b/packages/frontend/src/pages/settings/sounds.vue @@ -4,43 +4,53 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="_gaps_m"> - <MkSwitch v-model="notUseSound"> - <template #label>{{ i18n.ts.notUseSound }}</template> - </MkSwitch> - <MkSwitch v-model="useSoundOnlyWhenActive"> - <template #label>{{ i18n.ts.useSoundOnlyWhenActive }}</template> - </MkSwitch> - <MkRange v-model="masterVolume" :min="0" :max="1" :step="0.05" :textConverter="(v) => `${Math.floor(v * 100)}%`"> - <template #label>{{ i18n.ts.masterVolume }}</template> - </MkRange> +<SearchMarker path="/settings/sounds" :label="i18n.ts.sounds" :keywords="['sounds']" icon="ti ti-music"> + <div class="_gaps_m"> + <SearchMarker :keywords="['mute']"> + <MkSwitch v-model="notUseSound"> + <template #label><SearchLabel>{{ i18n.ts.notUseSound }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> - <FormSection> - <template #label>{{ i18n.ts.sounds }}</template> - <div class="_gaps_s"> - <MkFolder v-for="type in operationTypes" :key="type"> - <template #label>{{ i18n.ts._sfx[type] }}</template> - <template #suffix>{{ getSoundTypeName(sounds[type].type) }}</template> - <Suspense> - <template #default> - <XSound :type="sounds[type].type" :volume="sounds[type].volume" :fileId="sounds[type].fileId" :fileUrl="sounds[type].fileUrl" @update="(res) => updated(type, res)"/> - </template> - <template #fallback> - <MkLoading/> - </template> - </Suspense> - </MkFolder> - </div> - </FormSection> + <SearchMarker :keywords="['active', 'mute']"> + <MkSwitch v-model="useSoundOnlyWhenActive"> + <template #label><SearchLabel>{{ i18n.ts.useSoundOnlyWhenActive }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> - <MkButton danger @click="reset()"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</MkButton> -</div> + <SearchMarker :keywords="['volume', 'master']"> + <MkRange v-model="masterVolume" :min="0" :max="1" :step="0.05" :textConverter="(v) => `${Math.floor(v * 100)}%`"> + <template #label><SearchLabel>{{ i18n.ts.masterVolume }}</SearchLabel></template> + </MkRange> + </SearchMarker> + + <FormSection> + <template #label>{{ i18n.ts.sounds }}</template> + <div class="_gaps_s"> + <MkFolder v-for="type in operationTypes" :key="type"> + <template #label>{{ i18n.ts._sfx[type] }}</template> + <template #suffix>{{ getSoundTypeName(sounds[type].type) }}</template> + <Suspense> + <template #default> + <XSound :type="sounds[type].type" :volume="sounds[type].volume" :fileId="sounds[type].fileId" :fileUrl="sounds[type].fileUrl" @update="(res) => updated(type, res)"/> + </template> + <template #fallback> + <MkLoading/> + </template> + </Suspense> + </MkFolder> + </div> + </FormSection> + + <MkButton danger @click="reset()"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</MkButton> + </div> +</SearchMarker> </template> <script lang="ts" setup> import { computed, ref } from 'vue'; -import type { Ref } from 'vue'; import XSound from './sounds.sound.vue'; +import type { Ref } from 'vue'; import type { SoundType, OperationType } from '@/scripts/sound.js'; import type { SoundStore } from '@/store.js'; import MkRange from '@/components/MkRange.vue'; diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue index fcf5b3cd9b..b0e4ce13d5 100644 --- a/packages/frontend/src/pages/settings/theme.vue +++ b/packages/frontend/src/pages/settings/theme.vue @@ -4,56 +4,72 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="_gaps_m rsljpzjq"> - <div v-adaptive-border class="rfqxtzch _panel"> - <div class="toggle"> - <div class="toggleWrapper"> - <input id="dn" v-model="darkMode" type="checkbox" class="dn"/> - <label for="dn" class="toggle"> - <span class="before">{{ i18n.ts.light }}</span> - <span class="after">{{ i18n.ts.dark }}</span> - <span class="toggle__handler"> - <span class="crater crater--1"></span> - <span class="crater crater--2"></span> - <span class="crater crater--3"></span> - </span> - <span class="star star--1"></span> - <span class="star star--2"></span> - <span class="star star--3"></span> - <span class="star star--4"></span> - <span class="star star--5"></span> - <span class="star star--6"></span> - </label> +<SearchMarker path="/settings/theme" :label="i18n.ts.theme" :keywords="['theme']" icon="ti ti-palette"> + <div class="_gaps_m rsljpzjq"> + <div v-adaptive-border class="rfqxtzch _panel"> + <div class="toggle"> + <div class="toggleWrapper"> + <input id="dn" v-model="darkMode" type="checkbox" class="dn"/> + <label for="dn" class="toggle"> + <span class="before">{{ i18n.ts.light }}</span> + <span class="after">{{ i18n.ts.dark }}</span> + <span class="toggle__handler"> + <span class="crater crater--1"></span> + <span class="crater crater--2"></span> + <span class="crater crater--3"></span> + </span> + <span class="star star--1"></span> + <span class="star star--2"></span> + <span class="star star--3"></span> + <span class="star star--4"></span> + <span class="star star--5"></span> + <span class="star star--6"></span> + </label> + </div> + </div> + <div class="sync"> + <SearchMarker :keywords="['sync', 'device', 'dark', 'light', 'mode']"> + <MkSwitch v-model="syncDeviceDarkMode"> + <template #label><SearchLabel>{{ i18n.ts.syncDeviceDarkMode }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> </div> </div> - <div class="sync"> - <MkSwitch v-model="syncDeviceDarkMode">{{ i18n.ts.syncDeviceDarkMode }}</MkSwitch> - </div> - </div> - - <div class="selects"> - <MkSelect v-model="lightThemeId" large class="select" :items="lightThemeSelectorItems"> - <template #label>{{ i18n.ts.themeForLightMode }}</template> - <template #prefix><i class="ti ti-sun"></i></template> - </MkSelect> - <MkSelect v-model="darkThemeId" large class="select" :items="darkThemeSelectorItems"> - <template #label>{{ i18n.ts.themeForDarkMode }}</template> - <template #prefix><i class="ti ti-moon"></i></template> - </MkSelect> - </div> - <FormSection> - <div class="_formLinksGrid"> - <FormLink to="/settings/theme/manage"><template #icon><i class="ti ti-tool"></i></template>{{ i18n.ts._theme.manage }}<template #suffix>{{ themesCount }}</template></FormLink> - <FormLink to="https://assets.misskey.io/theme/list" external><template #icon><i class="ti ti-world"></i></template>{{ i18n.ts._theme.explore }}</FormLink> - <FormLink to="/settings/theme/install"><template #icon><i class="ti ti-download"></i></template>{{ i18n.ts._theme.install }}</FormLink> - <FormLink to="/theme-editor"><template #icon><i class="ti ti-paint"></i></template>{{ i18n.ts._theme.make }}</FormLink> + <div class="selects"> + <div class="select"> + <SearchMarker :keywords="['light', 'theme']"> + <MkSelect v-model="lightThemeId" large :items="lightThemeSelectorItems"> + <template #label><SearchLabel>{{ i18n.ts.themeForLightMode }}</SearchLabel></template> + <template #prefix><i class="ti ti-sun"></i></template> + </MkSelect> + </SearchMarker> + </div> + <div class="select"> + <SearchMarker :keywords="['dark', 'theme']"> + <MkSelect v-model="darkThemeId" large :items="darkThemeSelectorItems"> + <template #label><SearchLabel>{{ i18n.ts.themeForDarkMode }}</SearchLabel></template> + <template #prefix><i class="ti ti-moon"></i></template> + </MkSelect> + </SearchMarker> + </div> </div> - </FormSection> - <MkButton v-if="wallpaper == null" @click="setWallpaper">{{ i18n.ts.setWallpaper }}</MkButton> - <MkButton v-else @click="wallpaper = null">{{ i18n.ts.removeWallpaper }}</MkButton> -</div> + <FormSection> + <div class="_formLinksGrid"> + <FormLink to="/settings/theme/manage"><template #icon><i class="ti ti-tool"></i></template>{{ i18n.ts._theme.manage }}<template #suffix>{{ themesCount }}</template></FormLink> + <FormLink to="https://assets.misskey.io/theme/list" external><template #icon><i class="ti ti-world"></i></template>{{ i18n.ts._theme.explore }}</FormLink> + <FormLink to="/settings/theme/install"><template #icon><i class="ti ti-download"></i></template>{{ i18n.ts._theme.install }}</FormLink> + <FormLink to="/theme-editor"><template #icon><i class="ti ti-paint"></i></template>{{ i18n.ts._theme.make }}</FormLink> + </div> + </FormSection> + + <SearchMarker :keywords="['wallpaper']"> + <MkButton v-if="wallpaper == null" @click="setWallpaper"><SearchLabel>{{ i18n.ts.setWallpaper }}</SearchLabel></MkButton> + <MkButton v-else @click="wallpaper = null">{{ i18n.ts.removeWallpaper }}</MkButton> + </SearchMarker> + </div> +</SearchMarker> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts index d2a4484c45..c6ee128f5f 100644 --- a/packages/frontend/src/router/definition.ts +++ b/packages/frontend/src/router/definition.ts @@ -90,9 +90,9 @@ const routes: RouteDef[] = [{ name: 'security', component: page(() => import('@/pages/settings/security.vue')), }, { - path: '/general', - name: 'general', - component: page(() => import('@/pages/settings/general.vue')), + path: '/preferences', + name: 'preferences', + component: page(() => import('@/pages/settings/preferences.vue')), }, { path: '/theme/install', name: 'theme', @@ -106,6 +106,10 @@ const routes: RouteDef[] = [{ name: 'theme', component: page(() => import('@/pages/settings/theme.vue')), }, { + path: '/appearance', + name: 'appearance', + component: page(() => import('@/pages/settings/appearance.vue')), + }, { path: '/navbar', name: 'navbar', component: page(() => import('@/pages/settings/navbar.vue')), @@ -118,6 +122,10 @@ const routes: RouteDef[] = [{ name: 'sounds', component: page(() => import('@/pages/settings/sounds.vue')), }, { + path: '/accessibility', + name: 'accessibility', + component: page(() => import('@/pages/settings/accessibility.vue')), + }, { path: '/plugin/install', name: 'plugin', component: page(() => import('@/pages/settings/plugin.install.vue')), @@ -162,12 +170,8 @@ const routes: RouteDef[] = [{ name: 'preferences-backups', component: page(() => import('@/pages/settings/preferences-backups.vue')), }, { - path: '/migration', - name: 'migration', - component: page(() => import('@/pages/settings/migration.vue')), - }, { path: '/custom-css', - name: 'general', + name: 'preferences', component: page(() => import('@/pages/settings/custom-css.vue')), }, { path: '/accounts', diff --git a/packages/frontend/src/scripts/autogen/settings-search-index.ts b/packages/frontend/src/scripts/autogen/settings-search-index.ts new file mode 100644 index 0000000000..c62272b271 --- /dev/null +++ b/packages/frontend/src/scripts/autogen/settings-search-index.ts @@ -0,0 +1,815 @@ + +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +// This file was automatically generated by create-search-index. +// Do not edit this file. + +import { i18n } from '@/i18n.js'; + +export type SearchIndexItem = { + id: string; + path?: string; + label: string; + keywords: string[]; + icon?: string; + children?: SearchIndexItem[]; +}; + +export const searchIndexes: SearchIndexItem[] = [ + { + id: 'flXd1LC7r', + children: [ + { + id: 'hB11H5oul', + label: i18n.ts.syncDeviceDarkMode, + keywords: ['sync', 'device', 'dark', 'light', 'mode'], + }, + { + id: 'fDbLtIKeo', + label: i18n.ts.themeForLightMode, + keywords: ['light', 'theme'], + }, + { + id: 'eLOwK5Ia2', + label: i18n.ts.themeForDarkMode, + keywords: ['dark', 'theme'], + }, + { + id: 'ujvMfyzUr', + label: i18n.ts.setWallpaper, + keywords: ['wallpaper'], + }, + ], + label: i18n.ts.theme, + keywords: ['theme'], + path: '/settings/theme', + icon: 'ti ti-palette', + }, + { + id: '6fFIRXUww', + children: [ + { + id: 'nO7NnzqiC', + label: i18n.ts.notUseSound, + keywords: ['mute'], + }, + { + id: 'xy5OOBB4A', + label: i18n.ts.useSoundOnlyWhenActive, + keywords: ['active', 'mute'], + }, + { + id: '9MxYVIf7k', + label: i18n.ts.masterVolume, + keywords: ['volume', 'master'], + }, + ], + label: i18n.ts.sounds, + keywords: ['sounds'], + path: '/settings/sounds', + icon: 'ti ti-music', + }, + { + id: '5BjnxMfYV', + children: [ + { + id: '3UqdSCaFw', + children: [ + { + id: '75QPEg57v', + label: i18n.ts.changePassword, + keywords: [], + }, + ], + label: i18n.ts.password, + keywords: ['password'], + }, + { + id: '2fa', + children: [ + { + id: 'qCXM0HtJ7', + label: i18n.ts.totp, + keywords: ['totp', 'app', i18n.ts.totpDescription], + }, + { + id: '3g1RePuD9', + label: i18n.ts.securityKeyAndPasskey, + keywords: ['security', 'key', 'passkey'], + }, + { + id: 'pFRud5u8k', + label: i18n.ts.passwordLessLogin, + keywords: ['password', 'less', 'key', 'passkey', 'login', 'signin', i18n.ts.passwordLessLoginDescription], + }, + ], + label: i18n.ts['2fa'], + keywords: ['2fa'], + }, + ], + label: i18n.ts.security, + keywords: ['security'], + path: '/settings/security', + icon: 'ti ti-lock', + }, + { + id: 'w4L6myH61', + children: [ + { + id: 'ru8DrOn3J', + label: i18n.ts._profile.changeBanner, + keywords: ['banner', 'change'], + }, + { + id: 'CCnD8Apnu', + label: i18n.ts._profile.changeAvatar, + keywords: ['avatar', 'icon', 'change'], + }, + { + id: 'yFEVCJxFX', + label: i18n.ts._profile.name, + keywords: ['name'], + }, + { + id: '2O1S5reaB', + label: i18n.ts._profile.description, + keywords: ['description', 'bio'], + }, + { + id: 'pWi4OLS8g', + label: i18n.ts.location, + keywords: ['location', 'locale'], + }, + { + id: 'oLO5X6Wtw', + label: i18n.ts.birthday, + keywords: ['birthday', 'birthdate', 'age'], + }, + { + id: 'm2trKwPgq', + label: i18n.ts.language, + keywords: ['language', 'locale'], + }, + { + id: 'kfDZxCDp9', + label: i18n.ts._profile.metadataEdit, + keywords: ['metadata'], + }, + { + id: 'uPt3MFymp', + label: i18n.ts._profile.followedMessage, + keywords: ['follow', 'message', i18n.ts._profile.followedMessageDescription], + }, + { + id: 'wuGg0tBjw', + label: i18n.ts.reactionAcceptance, + keywords: ['reaction'], + }, + { + id: 'EezPpmMnf', + children: [ + { + id: 'f2cRLh8ad', + label: i18n.ts.flagAsCat, + keywords: ['cat'], + }, + { + id: 'eVoViiF3h', + label: i18n.ts.flagAsBot, + keywords: ['bot'], + }, + ], + label: i18n.ts.advancedSettings, + keywords: [], + }, + ], + label: i18n.ts.profile, + keywords: ['profile'], + path: '/settings/profile', + icon: 'ti ti-user', + }, + { + id: '2rp9ka5Ht', + children: [ + { + id: 'qBUSKPxLW', + label: i18n.ts.makeFollowManuallyApprove, + keywords: ['follow', 'lock', i18n.ts.lockedAccountInfo], + }, + { + id: '3LZBlZCej', + label: i18n.ts.autoAcceptFollowed, + keywords: ['follow', 'auto', 'accept'], + }, + { + id: '9gOp28wKG', + label: i18n.ts.makeReactionsPublic, + keywords: ['reaction', 'public', i18n.ts.makeReactionsPublicDescription], + }, + { + id: 'CjAkqMhct', + label: i18n.ts.followingVisibility, + keywords: ['following', 'visibility'], + }, + { + id: '4nEwI6LYt', + label: i18n.ts.followersVisibility, + keywords: ['follower', 'visibility'], + }, + { + id: 'naMp37wTL', + label: i18n.ts.hideOnlineStatus, + keywords: ['online', 'status', i18n.ts.hideOnlineStatusDescription], + }, + { + id: 'p0dCVR0UP', + label: i18n.ts.noCrawle, + keywords: ['crawle', 'index', 'search', i18n.ts.noCrawleDescription], + }, + { + id: 'aceURmNPq', + label: i18n.ts.preventAiLearning, + keywords: ['crawle', 'ai', i18n.ts.preventAiLearningDescription], + }, + { + id: 'ahABA0j7u', + label: i18n.ts.makeExplorable, + keywords: ['explore', i18n.ts.makeExplorableDescription], + }, + { + id: 'cyeDbLN8N', + children: [ + { + id: 'xEYlOghao', + label: i18n.ts._accountSettings.requireSigninToViewContents, + keywords: ['login', 'signin'], + }, + { + id: 'sMmYFCS60', + label: i18n.ts._accountSettings.makeNotesFollowersOnlyBefore, + keywords: ['follower', i18n.ts._accountSettings.makeNotesFollowersOnlyBeforeDescription], + }, + { + id: '2prkeWRSd', + label: i18n.ts._accountSettings.makeNotesHiddenBefore, + keywords: ['hidden', i18n.ts._accountSettings.makeNotesHiddenBeforeDescription], + }, + ], + label: i18n.ts.lockdown, + keywords: ['lockdown'], + }, + { + id: '37QLEyrtk', + label: i18n.ts.rememberNoteVisibility, + keywords: ['remember', 'keep', 'note', 'visibility'], + }, + { + id: 'rhKwScbVS', + label: i18n.ts.defaultNoteVisibility, + keywords: ['default', 'note', 'visibility'], + }, + { + id: '3EmXVyevo', + label: i18n.ts.keepCw, + keywords: ['remember', 'keep', 'note', 'cw'], + }, + ], + label: i18n.ts.privacy, + keywords: ['privacy'], + path: '/settings/privacy', + icon: 'ti ti-lock-open', + }, + { + id: '3yCAv0IsZ', + children: [ + { + id: 'x1GWSQnPw', + label: i18n.ts.uiLanguage, + keywords: ['language'], + }, + { + id: 'EOSa4rtt3', + label: i18n.ts.overridedDeviceKind, + keywords: ['device', 'type', 'kind', 'smartphone', 'tablet', 'desktop'], + }, + { + id: 'm9LhX8BG8', + label: i18n.ts.showFixedPostForm, + keywords: ['post', 'form', 'timeline'], + }, + { + id: '9ra14w32V', + label: i18n.ts.showFixedPostFormInChannel, + keywords: ['post', 'form', 'timeline', 'channel'], + }, + { + id: '84MdeDWL1', + label: i18n.ts.pinnedList, + keywords: ['pinned', 'list'], + }, + { + id: 'fYdWhBbrN', + label: i18n.ts.enableQuickAddMfmFunction, + keywords: ['mfm', 'enable', 'show', 'advanced', 'picker', 'form', 'function', 'fn'], + }, + { + id: '4huRldNp5', + children: [ + { + id: 'puIqj1a8b', + label: i18n.ts.collapseRenotes, + keywords: ['renote', i18n.ts.collapseRenotesDescription], + }, + { + id: 'wqpOC22Zm', + label: i18n.ts.showNoteActionsOnlyHover, + keywords: ['hover', 'show', 'footer', 'action'], + }, + { + id: 'cjfAtxMzP', + label: i18n.ts.showClipButtonInNoteFooter, + keywords: ['footer', 'action', 'clip', 'show'], + }, + { + id: 'khzxoCjtp', + label: i18n.ts.enableAdvancedMfm, + keywords: ['mfm', 'enable', 'show', 'advanced'], + }, + { + id: 'uJkoVjTmF', + label: i18n.ts.showReactionsCount, + keywords: ['reaction', 'count', 'show'], + }, + { + id: '9gTCaLkIf', + label: i18n.ts.loadRawImages, + keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'quality', 'raw', 'attachment'], + }, + ], + label: i18n.ts.note, + keywords: ['note'], + }, + { + id: '5G6O6qdis', + children: [ + { + id: 'sYTvqUbhP', + label: i18n.ts.useGroupedNotifications, + keywords: ['group'], + }, + ], + label: i18n.ts.notifications, + keywords: ['notification'], + }, + { + id: 'c3xhLyXZ5', + children: [ + { + id: 'FbhoeuRAD', + label: i18n.ts.openImageInNewTab, + keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'new', 'tab'], + }, + { + id: 'qixh85g2N', + label: i18n.ts.useReactionPickerForContextMenu, + keywords: ['reaction', 'picker', 'contextmenu', 'open'], + }, + { + id: 'd2H4E5ys6', + label: i18n.ts.enableInfiniteScroll, + keywords: ['load', 'auto', 'more'], + }, + { + id: 'jC7LtTnmc', + label: i18n.ts.disableStreamingTimeline, + keywords: ['disable', 'streaming', 'timeline'], + }, + { + id: '8xazEqlgZ', + label: i18n.ts.alwaysConfirmFollow, + keywords: ['follow', 'confirm', 'always'], + }, + { + id: 'wZqrDQZar', + label: i18n.ts.confirmWhenRevealingSensitiveMedia, + keywords: ['sensitive', 'nsfw', 'media', 'image', 'photo', 'picture', 'attachment', 'confirm'], + }, + { + id: '5QTUzrpT3', + label: i18n.ts.confirmOnReact, + keywords: ['reaction', 'confirm'], + }, + { + id: 'nygexkaUk', + label: i18n.ts.whenServerDisconnected, + keywords: ['server', 'disconnect', 'reconnect', 'reload', 'streaming'], + }, + { + id: 'whKYKvaQB', + label: i18n.ts.numberOfPageCache, + keywords: ['cache', 'page'], + }, + { + id: 'lBbtAg0Hm', + label: i18n.ts.dataSaver, + keywords: ['datasaver'], + }, + ], + label: i18n.ts.behavior, + keywords: ['behavior'], + }, + { + id: 'y2v7CV9zs', + children: [ + { + id: 'k1qTdyfzM', + label: i18n.ts.forceShowAds, + keywords: ['ad', 'show'], + }, + { + id: 'e9As4Us48', + label: i18n.ts.hemisphere, + keywords: [], + }, + { + id: 'zvM13vl26', + label: i18n.ts.additionalEmojiDictionary, + keywords: ['emoji', 'dictionary', 'additional', 'extra'], + }, + ], + label: i18n.ts.other, + keywords: [], + }, + ], + label: i18n.ts.preferences, + keywords: ['general', 'preferences'], + path: '/settings/preferences', + icon: 'ti ti-adjustments', + }, + { + id: 'F1uK9ssiY', + children: [ + { + id: 'msAcN6u3S', + label: i18n.ts.accountInfo, + keywords: ['account', 'info'], + }, + { + id: 'ts8DgdnZV', + label: i18n.ts.accountMigration, + keywords: ['account', 'move', 'migration'], + }, + { + id: '4BG7nBECm', + label: i18n.ts.closeAccount, + keywords: ['account', 'close', 'delete', i18n.ts._accountDelete.requestAccountDelete], + }, + { + id: '2qI6ruPgi', + label: i18n.ts.experimentalFeatures, + keywords: ['experimental', 'feature', 'flags'], + }, + { + id: 'cIeaax47o', + label: i18n.ts.developer, + keywords: ['developer', 'mode', 'debug'], + }, + ], + label: i18n.ts.other, + keywords: ['other'], + path: '/settings/other', + icon: 'ti ti-dots', + }, + { + id: '3icEvyv2D', + children: [ + { + id: 'Tyt3gZTy', + children: [ + { + id: '9b7ZURyAt', + label: i18n.ts.showMutedWord, + keywords: ['show'], + }, + ], + label: i18n.ts.wordMute, + keywords: ['note', 'word', 'soft', 'mute', 'hide'], + }, + { + id: 'kdMk41II0', + label: i18n.ts.hardWordMute, + keywords: ['note', 'word', 'hard', 'mute', 'hide'], + }, + { + id: 'mjORQamAK', + label: i18n.ts.instanceMute, + keywords: ['note', 'server', 'instance', 'host', 'federation', 'mute', 'hide'], + }, + { + id: '1ZT7S9FZd', + label: `${i18n.ts.mutedUsers} (${ i18n.ts.renote })`, + keywords: ['renote', 'mute', 'hide', 'user'], + }, + { + id: 'ANrPit3kQ', + label: i18n.ts.mutedUsers, + keywords: ['note', 'mute', 'hide', 'user'], + }, + { + id: 'bPAE4lfno', + label: i18n.ts.blockedUsers, + keywords: ['block', 'user'], + }, + ], + label: i18n.ts.muteAndBlock, + keywords: ['mute', 'block'], + path: '/settings/mute-block', + icon: 'ti ti-ban', + }, + { + id: 'qE2vLlMkF', + children: [ + { + id: 'hPPEzjvZC', + label: i18n.ts._exportOrImport.allNotes, + keywords: ['notes'], + }, + { + id: 'AFaeHsCUB', + label: i18n.ts._exportOrImport.favoritedNotes, + keywords: ['favorite', 'notes'], + }, + { + id: 'xyCPmQiRo', + label: i18n.ts._exportOrImport.clips, + keywords: ['clip', 'notes'], + }, + { + id: 'Ch7hWAGUy', + label: i18n.ts._exportOrImport.followingList, + keywords: ['following', 'users'], + }, + { + id: 'AwPgFboEx', + label: i18n.ts._exportOrImport.userLists, + keywords: ['user', 'lists'], + }, + { + id: 'nporiHshC', + label: i18n.ts._exportOrImport.muteList, + keywords: ['mute', 'users'], + }, + { + id: 'BsCzR7vNw', + label: i18n.ts._exportOrImport.blockingList, + keywords: ['block', 'users'], + }, + { + id: 'dvf4IgYrQ', + label: i18n.ts.antennas, + keywords: ['antennas'], + }, + ], + label: i18n.ts.importAndExport, + keywords: ['import', 'export', 'data'], + path: '/settings/import-export', + icon: 'ti ti-package', + }, + { + id: '3Tcxw4Fwl', + children: [ + { + id: 'iIai9O65I', + label: i18n.ts.emailAddress, + keywords: ['email', 'address'], + }, + { + id: 'i6cC6oi0m', + label: i18n.ts.receiveAnnouncementFromInstance, + keywords: ['announcement', 'email'], + }, + { + id: 'C1YTinP11', + label: i18n.ts.emailNotification, + keywords: ['notification', 'email'], + }, + ], + label: i18n.ts.email, + keywords: ['email'], + path: '/settings/email', + icon: 'ti ti-mail', + }, + { + id: 'tnYoppRiv', + children: [ + { + id: 'ncIq6TAR2', + label: i18n.ts.usageAmount, + keywords: ['capacity', 'usage'], + }, + { + id: '2c4CQSvSr', + label: i18n.ts.statistics, + keywords: ['statistics', 'usage'], + }, + { + id: 'pepHELHMt', + label: i18n.ts.uploadFolder, + keywords: ['default', 'upload', 'folder'], + }, + { + id: 'xqOWrABxV', + label: i18n.ts.keepOriginalUploading, + keywords: ['keep', 'original', 'raw', 'upload', i18n.ts.keepOriginalUploadingDescription], + }, + { + id: 'oqUiI5w0s', + label: i18n.ts.keepOriginalFilename, + keywords: ['keep', 'original', 'filename', i18n.ts.keepOriginalFilenameDescription], + }, + { + id: 'Aszkikq9n', + label: i18n.ts.alwaysMarkSensitive, + keywords: ['always', 'default', 'mark', 'nsfw', 'sensitive', 'media', 'file'], + }, + { + id: 'iGlVjsfVj', + label: i18n.ts.enableAutoSensitive, + keywords: ['auto', 'nsfw', 'sensitive', 'media', 'file', i18n.ts.enableAutoSensitiveDescription], + }, + ], + label: i18n.ts.drive, + keywords: ['drive'], + path: '/settings/drive', + icon: 'ti ti-cloud', + }, + { + id: 'gtaOSdIJB', + label: i18n.ts.avatarDecorations, + keywords: ['avatar', 'icon', 'decoration'], + path: '/settings/avatar-decoration', + icon: 'ti ti-sparkles', + }, + { + id: 'AqPvMgn3A', + children: [ + { + id: 'j5gTtuMWP', + label: i18n.ts.useBlurEffect, + keywords: ['blur'], + }, + { + id: 'vbZvyLDC1', + label: i18n.ts.useBlurEffectForModal, + keywords: ['blur', 'modal'], + }, + { + id: '6fLNMTwNt', + label: i18n.ts.highlightSensitiveMedia, + keywords: ['highlight', 'sensitive', 'nsfw', 'image', 'photo', 'picture', 'media', 'thumbnail'], + }, + { + id: 'hhvF8Z4pF', + label: i18n.ts.squareAvatars, + keywords: ['avatar', 'icon', 'square'], + }, + { + id: 'DsS2CwjYE', + label: i18n.ts.showAvatarDecorations, + keywords: ['avatar', 'icon', 'decoration', 'show'], + }, + { + id: 'pWZ0ypy2g', + label: i18n.ts.showGapBetweenNotesInTimeline, + keywords: ['note', 'timeline', 'gap'], + }, + { + id: 'AfRMcC6IM', + label: i18n.ts.useSystemFont, + keywords: ['font', 'system', 'native'], + }, + { + id: 'jD0qbxlzN', + label: i18n.ts.seasonalScreenEffect, + keywords: ['effect', 'show'], + }, + { + id: 'EdYo3hOK', + label: i18n.ts.menuStyle, + keywords: ['menu', 'style', 'popup', 'drawer'], + }, + { + id: '9mSlX0EkD', + label: i18n.ts.emojiStyle, + keywords: ['emoji', 'style', 'native', 'system', 'fluent', 'twemoji'], + }, + { + id: '44UmMwmUe', + label: i18n.ts.fontSize, + keywords: ['font', 'size'], + }, + { + id: 'vFB0pLzck', + children: [ + { + id: 'pc7IpPEU4', + label: i18n.ts.reactionsDisplaySize, + keywords: ['reaction', 'size', 'scale', 'display'], + }, + { + id: 'siOW5aSwp', + label: i18n.ts.limitWidthOfReaction, + keywords: ['reaction', 'size', 'scale', 'display', 'width', 'limit'], + }, + { + id: 'dDUvhk13F', + label: i18n.ts.mediaListWithOneImageAppearance, + keywords: ['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'list', 'size', 'height'], + }, + { + id: 'CLxNL1Rp0', + label: i18n.ts.instanceTicker, + keywords: ['ticker', 'information', 'label', 'instance', 'server', 'host', 'federation'], + }, + { + id: 'dP2KWDYzD', + label: i18n.ts.displayOfSensitiveMedia, + keywords: ['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'nsfw', 'sensitive', 'display', 'show', 'hide', 'visibility'], + }, + ], + label: i18n.ts.displayOfNote, + keywords: ['note', 'display'], + }, + { + id: 'dVOzi22IW', + children: [ + { + id: 'aoF4ufUwn', + label: i18n.ts.position, + keywords: ['position'], + }, + { + id: 'sKK2XSS69', + label: i18n.ts.stackAxis, + keywords: ['stack', 'axis', 'direction'], + }, + ], + label: i18n.ts.notificationDisplay, + keywords: ['notification', 'display'], + }, + ], + label: i18n.ts.appearance, + keywords: ['appearance'], + path: '/settings/appearance', + icon: 'ti ti-device-desktop', + }, + { + id: 'f08Mi1Uwn', + children: [ + { + id: '7ov7ceoij', + label: i18n.ts.reduceUiAnimation, + keywords: ['animation', 'motion', 'reduce'], + }, + { + id: 'RhYwm8At', + label: i18n.ts.disableShowingAnimatedImages, + keywords: ['disable', 'animation', 'image', 'photo', 'picture', 'media', 'thumbnail', 'gif'], + }, + { + id: '5mZxz2cru', + label: i18n.ts.enableAnimatedMfm, + keywords: ['mfm', 'enable', 'show', 'animated'], + }, + { + id: 'bgjamYEis', + label: i18n.ts.enableHorizontalSwipe, + keywords: ['swipe', 'horizontal', 'tab'], + }, + { + id: 'yPEpJigqY', + label: i18n.ts.keepScreenOn, + keywords: ['keep', 'screen', 'display', 'on'], + }, + { + id: 'oxwiGKMu0', + label: i18n.ts.useNativeUIForVideoAudioPlayer, + keywords: ['native', 'system', 'video', 'audio', 'player', 'media'], + }, + { + id: 'n90tffyiU', + label: i18n.ts._contextMenu.title, + keywords: ['contextmenu', 'system', 'native'], + }, + ], + label: i18n.ts.accessibility, + keywords: ['accessibility'], + path: '/settings/accessibility', + icon: 'ti ti-accessible', + }, +] as const; + +export type SearchIndex = typeof searchIndexes; diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 9724905e02..fec8666dc1 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -56,21 +56,35 @@ SPDX-License-Identifier: AGPL-3.0-only </button> </div> </div> - <button v-if="!forceIconOnly" class="_button" :class="$style.toggleButton" @click="toggleIconOnly"> - <!-- - <svg viewBox="0 0 16 48" :class="$style.toggleButtonShape"> - <g transform="matrix(0.333333,0,0,0.222222,0.000895785,13.3333)"> - <path d="M23.935,-24C37.223,-24 47.995,-7.842 47.995,12.09C47.995,34.077 47.995,62.07 47.995,84.034C47.995,93.573 45.469,102.721 40.972,109.466C36.475,116.211 30.377,120 24.018,120L23.997,120C10.743,120 -0.003,136.118 -0.003,156C-0.003,156 -0.003,156 -0.003,156L-0.003,-60L-0.003,-59.901C-0.003,-50.379 2.519,-41.248 7.007,-34.515C11.496,-27.782 17.584,-24 23.931,-24C23.932,-24 23.934,-24 23.935,-24Z" style="fill:var(--MI_THEME-navBg);"/> - </g> - </svg> - --> - <svg viewBox="0 0 16 64" :class="$style.toggleButtonShape"> - <g transform="matrix(0.333333,0,0,0.222222,0.000895785,21.3333)"> - <path d="M47.488,7.995C47.79,10.11 47.943,12.266 47.943,14.429C47.997,26.989 47.997,84 47.997,84C47.997,84 44.018,118.246 23.997,133.5C-0.374,152.07 -0.003,192 -0.003,192L-0.003,-96C-0.003,-96 0.151,-56.216 23.997,-37.5C40.861,-24.265 46.043,-1.243 47.488,7.995Z" style="fill:var(--MI_THEME-navBg);"/> - </g> - </svg> - <i :class="'ti ' + `ti-chevron-${ iconOnly ? 'right' : 'left' }`" style="font-size: 12px; margin-left: -8px;"></i> - </button> + + <!-- + <svg viewBox="0 0 16 48" :class="$style.subButtonShape"> + <g transform="matrix(0.333333,0,0,0.222222,0.000895785,13.3333)"> + <path d="M23.935,-24C37.223,-24 47.995,-7.842 47.995,12.09C47.995,34.077 47.995,62.07 47.995,84.034C47.995,93.573 45.469,102.721 40.972,109.466C36.475,116.211 30.377,120 24.018,120L23.997,120C10.743,120 -0.003,136.118 -0.003,156C-0.003,156 -0.003,156 -0.003,156L-0.003,-60L-0.003,-59.901C-0.003,-50.379 2.519,-41.248 7.007,-34.515C11.496,-27.782 17.584,-24 23.931,-24C23.932,-24 23.934,-24 23.935,-24Z" style="fill:var(--MI_THEME-navBg);"/> + </g> + </svg> + --> + + <div :class="$style.subButtons"> + <div :class="[$style.subButton, $style.menuEditButton]"> + <svg viewBox="0 0 16 64" :class="$style.subButtonShape"> + <g transform="matrix(0.333333,0,0,0.222222,0.000895785,21.3333)"> + <path d="M47.488,7.995C47.79,10.11 47.943,12.266 47.943,14.429C47.997,26.989 47.997,84 47.997,84C47.997,84 44.018,118.246 23.997,133.5C-0.374,152.07 -0.003,192 -0.003,192L-0.003,-96C-0.003,-96 0.151,-56.216 23.997,-37.5C40.861,-24.265 46.043,-1.243 47.488,7.995Z" style="fill:var(--MI_THEME-navBg);"/> + </g> + </svg> + <button class="_button" :class="$style.subButtonClickable" @click="menuEdit"><i :class="$style.subButtonIcon" class="ti ti-settings-2"></i></button> + </div> + <div v-if="!forceIconOnly" :class="$style.subButtonGapFill"></div> + <div v-if="!forceIconOnly" :class="$style.subButtonGapFillDivider"></div> + <div v-if="!forceIconOnly" :class="[$style.subButton, $style.toggleButton]"> + <svg viewBox="0 0 16 64" :class="$style.subButtonShape"> + <g transform="matrix(0.333333,0,0,0.222222,0.000895785,21.3333)"> + <path d="M47.488,7.995C47.79,10.11 47.943,12.266 47.943,14.429C47.997,26.989 47.997,84 47.997,84C47.997,84 44.018,118.246 23.997,133.5C-0.374,152.07 -0.003,192 -0.003,192L-0.003,-96C-0.003,-96 0.151,-56.216 23.997,-37.5C40.861,-24.265 46.043,-1.243 47.488,7.995Z" style="fill:var(--MI_THEME-navBg);"/> + </g> + </svg> + <button class="_button" :class="$style.subButtonClickable" @click="toggleIconOnly"><i v-if="iconOnly" class="ti ti-chevron-right" :class="$style.subButtonIcon"></i><i v-else class="ti ti-chevron-left" :class="$style.subButtonIcon"></i></button> + </div> + </div> </div> </template> @@ -84,6 +98,9 @@ import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import { instance } from '@/instance.js'; import { getHTMLElementOrNull } from '@/scripts/get-dom-node-or-null.js'; +import { useRouter } from '@/router/supplier.js'; + +const router = useRouter(); const forceIconOnly = ref(window.innerWidth <= 1279); const iconOnly = computed(() => { @@ -128,6 +145,10 @@ function more(ev: MouseEvent) { closed: () => dispose(), }); } + +function menuEdit() { + router.push('/settings/navbar'); +} </script> <style lang="scss" module> @@ -136,6 +157,8 @@ function more(ev: MouseEvent) { --nav-icon-only-width: 80px; --nav-bg-transparent: color(from var(--MI_THEME-navBg) srgb r g b / 0.5); + --subButtonWidth: 20px; + flex: 0 0 var(--nav-width); width: var(--nav-width); box-sizing: border-box; @@ -171,23 +194,80 @@ function more(ev: MouseEvent) { direction: ltr; } -.toggleButton { +.subButtons { position: fixed; - bottom: 20px; left: var(--nav-width); + bottom: 80px; z-index: 1001; - width: 16px; - height: 64px; box-sizing: border-box; } -.toggleButtonShape { +.subButton { + display: block; + position: relative; + z-index: 1002; + width: var(--subButtonWidth); + height: 50px; + box-sizing: border-box; + align-content: center; +} + +.subButtonShape { position: absolute; z-index: -1; top: 0; + bottom: 0; left: 0; - width: 16px; + margin: auto; + width: var(--subButtonWidth); + height: calc(var(--subButtonWidth) * 4); +} + +.subButtonClickable { + position: absolute; + display: block; + max-width: unset; + width: 24px; + height: 42px; + top: 0; + bottom: 0; + left: -4px; + margin: auto; + font-size: 10px; + + &:hover { + color: var(--MI_THEME-fgHighlighted); + + .subButtonIcon { + opacity: 1; + } + } +} + +.subButtonIcon { + margin-left: -4px; + opacity: 0.7; +} + +.subButtonGapFill { + position: relative; + z-index: 1001; + width: var(--subButtonWidth); height: 64px; + margin-top: -32px; + margin-bottom: -32px; + pointer-events: none; + background: var(--MI_THEME-navBg); +} + +.subButtonGapFillDivider { + position: relative; + z-index: 1010; + margin-left: -2px; + width: 14px; + height: 1px; + background: var(--MI_THEME-divider); + pointer-events: none; } .root:not(.iconOnly) { @@ -419,7 +499,7 @@ function more(ev: MouseEvent) { font-size: 0.9em; } - .toggleButton { + .subButtons { left: var(--nav-width); } } @@ -623,7 +703,7 @@ function more(ev: MouseEvent) { } } - .toggleButton { + .subButtons { left: var(--nav-icon-only-width); } } diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index 9fff5efe51..25f47a2d55 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -96,12 +96,13 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { defineAsyncComponent, provide, onMounted, computed, ref, watch, shallowRef } from 'vue'; -import type { Ref } from 'vue'; import { instanceName } from '@@/js/config.js'; import { CURRENT_STICKY_BOTTOM } from '@@/js/const.js'; import { isLink } from '@@/js/is-link.js'; import XCommon from './_common_/common.vue'; +import type { Ref } from 'vue'; import type MkStickyContainer from '@/components/global/MkStickyContainer.vue'; +import type { PageMetadata } from '@/scripts/page-metadata.js'; import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue'; import * as os from '@/os.js'; import { defaultStore } from '@/store.js'; @@ -109,7 +110,6 @@ import { navbarItemDef } from '@/navbar.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; import { provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; -import type { PageMetadata } from '@/scripts/page-metadata.js'; import { deviceKind } from '@/scripts/device-kind.js'; import { miLocalStorage } from '@/local-storage.js'; import { useScrollPositionManager } from '@/nirax.js'; @@ -331,6 +331,8 @@ $widgets-hide-threshold: 1090px; overflow-y: scroll; overscroll-behavior: contain; background: var(--MI_THEME-bg); + scroll-padding-top: 60px; // TODO: ちゃんと計算する + scroll-padding-bottom: 60px; // TODO: ちゃんと計算する } .widgets { diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index d1b7c410dc..1c094e272b 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -10,6 +10,7 @@ import meta from '../../package.json'; import packageInfo from './package.json' with { type: 'json' }; import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-module-class-name.js'; import pluginJson5 from './vite.json5.js'; +import pluginCreateSearchIndex from './lib/vite-plugin-create-search-index.js'; const url = process.env.NODE_ENV === 'development' ? yaml.load(await fsp.readFile('../../.config/default.yml', 'utf-8')).url : null; const host = url ? (new URL(url)).hostname : undefined; @@ -34,7 +35,7 @@ const externalPackages = [ }, ]; -const hash = (str: string, seed = 0): number => { +export const hash = (str: string, seed = 0): number => { let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed; for (let i = 0, ch; i < str.length; i++) { @@ -49,9 +50,9 @@ const hash = (str: string, seed = 0): number => { return 4294967296 * (2097151 & h2) + (h1 >>> 0); }; -const BASE62_DIGITS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; +export const BASE62_DIGITS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; -function toBase62(n: number): string { +export function toBase62(n: number): string { if (n === 0) { return '0'; } @@ -83,6 +84,11 @@ export function getConfig(): UserConfig { }, plugins: [ + pluginCreateSearchIndex({ + targetFilePaths: ['src/pages/settings/*.vue'], + exportFilePath: './src/scripts/autogen/settings-search-index.ts', + verbose: process.env.FRONTEND_SEARCH_INDEX_VERBOSE === 'true', + }), pluginVue(), pluginUnwindCssModuleClassName(), pluginJson5(), diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 8a91f8ea38..eb8e7c479b 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2025.3.0", + "version": "2025.3.1", "description": "Misskey SDK for JavaScript", "license": "MIT", "main": "./built/index.js", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5096d59e7..4d11b5ac49 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,8 +5,14 @@ settings: excludeLinksFromLockfile: false overrides: - chokidar: 3.6.0 + chokidar: 4.0.3 lodash: 4.17.21 + '@aiscript-dev/aiscript-languageserver': '-' + +patchedDependencies: + re2: + hash: 018babd22b7ce951bcd10d6246f1e541a7ac7ba212f7fa8985e774ece67d08e1 + path: scripts/dependency-patches/re2.patch importers: @@ -14,13 +20,13 @@ importers: dependencies: cssnano: specifier: 7.0.6 - version: 7.0.6(postcss@8.5.2) + version: 7.0.6(postcss@8.5.3) esbuild: specifier: 0.25.0 version: 0.25.0 execa: - specifier: 8.0.1 - version: 8.0.1 + specifier: 9.5.2 + version: 9.5.2 fast-glob: specifier: 3.3.3 version: 3.3.3 @@ -28,58 +34,61 @@ importers: specifier: 11.0.1 version: 11.0.1 ignore-walk: - specifier: 6.0.5 - version: 6.0.5 + specifier: 7.0.0 + version: 7.0.0 js-yaml: specifier: 4.1.0 version: 4.1.0 postcss: - specifier: 8.5.2 - version: 8.5.2 + specifier: 8.5.3 + version: 8.5.3 tar: - specifier: 6.2.1 - version: 6.2.1 + specifier: 7.4.3 + version: 7.4.3 terser: specifier: 5.39.0 version: 5.39.0 typescript: - specifier: 5.7.3 - version: 5.7.3 - optionalDependencies: - '@tensorflow/tfjs-core': - specifier: 4.22.0 - version: 4.22.0(encoding@0.1.13) + specifier: 5.8.2 + version: 5.8.2 devDependencies: '@misskey-dev/eslint-plugin': specifier: 2.1.0 - version: 2.1.0(@eslint/compat@1.1.1)(@stylistic/eslint-plugin@2.13.0(eslint@9.20.1)(typescript@5.7.3))(@typescript-eslint/eslint-plugin@8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1)(typescript@5.7.3))(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1))(eslint@9.20.1)(globals@15.15.0) + version: 2.1.0(@eslint/compat@1.1.1)(@stylistic/eslint-plugin@2.13.0(eslint@9.21.0)(typescript@5.8.2))(@typescript-eslint/eslint-plugin@8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0)(typescript@5.8.2))(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0))(eslint@9.21.0)(globals@16.0.0) '@types/node': - specifier: 22.13.4 - version: 22.13.4 + specifier: 22.13.9 + version: 22.13.9 '@typescript-eslint/eslint-plugin': - specifier: 8.24.0 - version: 8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1)(typescript@5.7.3) + specifier: 8.26.0 + version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0)(typescript@5.8.2) '@typescript-eslint/parser': - specifier: 8.24.0 - version: 8.24.0(eslint@9.20.1)(typescript@5.7.3) + specifier: 8.26.0 + version: 8.26.0(eslint@9.21.0)(typescript@5.8.2) cross-env: specifier: 7.0.3 version: 7.0.3 cypress: - specifier: 14.0.3 - version: 14.0.3 + specifier: 14.1.0 + version: 14.1.0 eslint: - specifier: 9.20.1 - version: 9.20.1 + specifier: 9.21.0 + version: 9.21.0 globals: - specifier: 15.15.0 - version: 15.15.0 + specifier: 16.0.0 + version: 16.0.0 ncp: specifier: 2.0.0 version: 2.0.0 + pnpm: + specifier: 10.6.1 + version: 10.6.1 start-server-and-test: specifier: 2.0.10 version: 2.0.10 + optionalDependencies: + '@tensorflow/tfjs-core': + specifier: 4.22.0 + version: 4.22.0(encoding@0.1.13) packages/backend: dependencies: @@ -163,7 +172,7 @@ importers: version: 2.5.0 '@swc/cli': specifier: 0.6.0 - version: 0.6.0(@swc/core@1.10.16)(chokidar@3.6.0) + version: 0.6.0(@swc/core@1.10.16)(chokidar@4.0.3) '@swc/core': specifier: 1.10.16 version: 1.10.16 @@ -207,8 +216,8 @@ importers: specifier: 1.1.0 version: 1.1.0 chokidar: - specifier: 3.6.0 - version: 3.6.0 + specifier: 4.0.3 + version: 4.0.3 cli-highlight: specifier: 2.1.11 version: 2.1.11 @@ -367,7 +376,7 @@ importers: version: 3.4.1 re2: specifier: 1.21.4 - version: 1.21.4 + version: 1.21.4(patch_hash=018babd22b7ce951bcd10d6246f1e541a7ac7ba212f7fa8985e774ece67d08e1) redis-lock: specifier: 0.1.4 version: 0.1.4 @@ -437,94 +446,6 @@ importers: xev: specifier: 3.0.2 version: 3.0.2 - optionalDependencies: - '@swc/core-android-arm64': - specifier: 1.3.11 - version: 1.3.11 - '@swc/core-darwin-arm64': - specifier: 1.10.16 - version: 1.10.16 - '@swc/core-darwin-x64': - specifier: 1.10.16 - version: 1.10.16 - '@swc/core-freebsd-x64': - specifier: 1.3.11 - version: 1.3.11 - '@swc/core-linux-arm-gnueabihf': - specifier: 1.10.16 - version: 1.10.16 - '@swc/core-linux-arm64-gnu': - specifier: 1.10.16 - version: 1.10.16 - '@swc/core-linux-arm64-musl': - specifier: 1.10.16 - version: 1.10.16 - '@swc/core-linux-x64-gnu': - specifier: 1.10.16 - version: 1.10.16 - '@swc/core-linux-x64-musl': - specifier: 1.10.16 - version: 1.10.16 - '@swc/core-win32-arm64-msvc': - specifier: 1.10.16 - version: 1.10.16 - '@swc/core-win32-ia32-msvc': - specifier: 1.10.16 - version: 1.10.16 - '@swc/core-win32-x64-msvc': - specifier: 1.10.16 - version: 1.10.16 - '@tensorflow/tfjs': - specifier: 4.22.0 - version: 4.22.0(encoding@0.1.13)(seedrandom@3.0.5) - '@tensorflow/tfjs-node': - specifier: 4.22.0 - version: 4.22.0(encoding@0.1.13)(seedrandom@3.0.5) - bufferutil: - specifier: 4.0.9 - version: 4.0.9 - slacc-android-arm-eabi: - specifier: 0.0.10 - version: 0.0.10 - slacc-android-arm64: - specifier: 0.0.10 - version: 0.0.10 - slacc-darwin-arm64: - specifier: 0.0.10 - version: 0.0.10 - slacc-darwin-universal: - specifier: 0.0.10 - version: 0.0.10 - slacc-darwin-x64: - specifier: 0.0.10 - version: 0.0.10 - slacc-freebsd-x64: - specifier: 0.0.10 - version: 0.0.10 - slacc-linux-arm-gnueabihf: - specifier: 0.0.10 - version: 0.0.10 - slacc-linux-arm64-gnu: - specifier: 0.0.10 - version: 0.0.10 - slacc-linux-arm64-musl: - specifier: 0.0.10 - version: 0.0.10 - slacc-linux-x64-gnu: - specifier: 0.0.10 - version: 0.0.10 - slacc-linux-x64-musl: - specifier: 0.0.10 - version: 0.0.10 - slacc-win32-arm64-msvc: - specifier: 0.0.10 - version: 0.0.10 - slacc-win32-x64-msvc: - specifier: 0.0.10 - version: 0.0.10 - utf-8-validate: - specifier: 6.0.5 - version: 6.0.5 devDependencies: '@jest/globals': specifier: 29.7.0 @@ -648,10 +569,10 @@ importers: version: 8.5.14 '@typescript-eslint/eslint-plugin': specifier: 8.24.0 - version: 8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1)(typescript@5.7.3) + version: 8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.21.0)(typescript@5.7.3))(eslint@9.21.0)(typescript@5.7.3) '@typescript-eslint/parser': specifier: 8.24.0 - version: 8.24.0(eslint@9.20.1)(typescript@5.7.3) + version: 8.24.0(eslint@9.21.0)(typescript@5.7.3) aws-sdk-client-mock: specifier: 4.1.0 version: 4.1.0 @@ -660,7 +581,7 @@ importers: version: 7.0.3 eslint-plugin-import: specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1) + version: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.21.0)(typescript@5.7.3))(eslint@9.21.0) execa: specifier: 8.0.1 version: 8.0.1 @@ -682,6 +603,94 @@ importers: simple-oauth2: specifier: 5.1.0 version: 5.1.0 + optionalDependencies: + '@swc/core-android-arm64': + specifier: 1.3.11 + version: 1.3.11 + '@swc/core-darwin-arm64': + specifier: 1.10.16 + version: 1.10.16 + '@swc/core-darwin-x64': + specifier: 1.10.16 + version: 1.10.16 + '@swc/core-freebsd-x64': + specifier: 1.3.11 + version: 1.3.11 + '@swc/core-linux-arm-gnueabihf': + specifier: 1.10.16 + version: 1.10.16 + '@swc/core-linux-arm64-gnu': + specifier: 1.10.16 + version: 1.10.16 + '@swc/core-linux-arm64-musl': + specifier: 1.10.16 + version: 1.10.16 + '@swc/core-linux-x64-gnu': + specifier: 1.10.16 + version: 1.10.16 + '@swc/core-linux-x64-musl': + specifier: 1.10.16 + version: 1.10.16 + '@swc/core-win32-arm64-msvc': + specifier: 1.10.16 + version: 1.10.16 + '@swc/core-win32-ia32-msvc': + specifier: 1.10.16 + version: 1.10.16 + '@swc/core-win32-x64-msvc': + specifier: 1.10.16 + version: 1.10.16 + '@tensorflow/tfjs': + specifier: 4.22.0 + version: 4.22.0(encoding@0.1.13)(seedrandom@3.0.5) + '@tensorflow/tfjs-node': + specifier: 4.22.0 + version: 4.22.0(encoding@0.1.13)(seedrandom@3.0.5) + bufferutil: + specifier: 4.0.9 + version: 4.0.9 + slacc-android-arm-eabi: + specifier: 0.0.10 + version: 0.0.10 + slacc-android-arm64: + specifier: 0.0.10 + version: 0.0.10 + slacc-darwin-arm64: + specifier: 0.0.10 + version: 0.0.10 + slacc-darwin-universal: + specifier: 0.0.10 + version: 0.0.10 + slacc-darwin-x64: + specifier: 0.0.10 + version: 0.0.10 + slacc-freebsd-x64: + specifier: 0.0.10 + version: 0.0.10 + slacc-linux-arm-gnueabihf: + specifier: 0.0.10 + version: 0.0.10 + slacc-linux-arm64-gnu: + specifier: 0.0.10 + version: 0.0.10 + slacc-linux-arm64-musl: + specifier: 0.0.10 + version: 0.0.10 + slacc-linux-x64-gnu: + specifier: 0.0.10 + version: 0.0.10 + slacc-linux-x64-musl: + specifier: 0.0.10 + version: 0.0.10 + slacc-win32-arm64-msvc: + specifier: 0.0.10 + version: 0.0.10 + slacc-win32-x64-msvc: + specifier: 0.0.10 + version: 0.0.10 + utf-8-validate: + specifier: 6.0.5 + version: 6.0.5 packages/frontend: dependencies: @@ -720,7 +729,7 @@ importers: version: 15.1.1 '@vitejs/plugin-vue': specifier: 5.2.1 - version: 5.2.1(vite@6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2)) + version: 5.2.1(vite@6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2)) '@vue/compiler-sfc': specifier: 3.5.13 version: 3.5.13 @@ -790,6 +799,9 @@ importers: json5: specifier: 2.2.3 version: 2.2.3 + magic-string: + specifier: 0.30.17 + version: 0.30.17 matter-js: specifier: 0.20.0 version: 0.20.0 @@ -854,8 +866,8 @@ importers: specifier: 1.13.1 version: 1.13.1(vue@3.5.13(typescript@5.8.2)) vite: - specifier: 6.2.0 - version: 6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) + specifier: 6.2.1 + version: 6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) vue: specifier: 3.5.13 version: 3.5.13(typescript@5.8.2) @@ -867,59 +879,59 @@ importers: specifier: 5.2.0 version: 5.2.0 '@storybook/addon-actions': - specifier: 8.6.3 - version: 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.4 + version: 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/addon-essentials': - specifier: 8.6.3 - version: 8.6.3(@types/react@18.0.28)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.4 + version: 8.6.4(@types/react@18.0.28)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/addon-interactions': - specifier: 8.6.3 - version: 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.4 + version: 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/addon-links': - specifier: 8.6.3 - version: 8.6.3(react@19.0.0)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.4 + version: 8.6.4(react@19.0.0)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/addon-mdx-gfm': - specifier: 8.6.3 - version: 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.4 + version: 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/addon-storysource': - specifier: 8.6.3 - version: 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.4 + version: 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/blocks': - specifier: 8.6.3 - version: 8.6.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.4 + version: 8.6.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/components': - specifier: 8.6.3 - version: 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.4 + version: 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/core-events': - specifier: 8.6.3 - version: 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.4 + version: 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/manager-api': - specifier: 8.6.3 - version: 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.4 + version: 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/preview-api': - specifier: 8.6.3 - version: 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.4 + version: 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/react': - specifier: 8.6.3 - version: 8.6.3(@storybook/test@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2) + specifier: 8.6.4 + version: 8.6.4(@storybook/test@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2) '@storybook/react-vite': - specifier: 8.6.3 - version: 8.6.3(@storybook/test@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.34.9)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2)(vite@6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)) + specifier: 8.6.4 + version: 8.6.4(@storybook/test@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.34.9)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2)(vite@6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)) '@storybook/test': - specifier: 8.6.3 - version: 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.4 + version: 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/theming': - specifier: 8.6.3 - version: 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.4 + version: 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/types': - specifier: 8.6.3 - version: 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + specifier: 8.6.4 + version: 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/vue3': - specifier: 8.6.3 - version: 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.13(typescript@5.8.2)) + specifier: 8.6.4 + version: 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.13(typescript@5.8.2)) '@storybook/vue3-vite': - specifier: 8.6.3 - version: 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2)) + specifier: 8.6.4 + version: 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2)) '@testing-library/vue': specifier: 8.1.0 version: 8.1.0(@vue/compiler-sfc@3.5.13)(@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2)) @@ -958,19 +970,19 @@ importers: version: 8.18.0 '@typescript-eslint/eslint-plugin': specifier: 8.26.0 - version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.20.1)(typescript@5.8.2))(eslint@9.20.1)(typescript@5.8.2) + version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0)(typescript@5.8.2) '@typescript-eslint/parser': specifier: 8.26.0 - version: 8.26.0(eslint@9.20.1)(typescript@5.8.2) + version: 8.26.0(eslint@9.21.0)(typescript@5.8.2) '@vitest/coverage-v8': - specifier: 3.0.7 - version: 3.0.7(vitest@3.0.7(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.2.2)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)) + specifier: 3.0.8 + version: 3.0.8(vitest@3.0.8(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.3.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)) '@vue/runtime-core': specifier: 3.5.13 version: 3.5.13 acorn: - specifier: 8.14.0 - version: 8.14.0 + specifier: 8.14.1 + version: 8.14.1 cross-env: specifier: 7.0.3 version: 7.0.3 @@ -979,16 +991,16 @@ importers: version: 14.1.0 eslint-plugin-import: specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.20.1)(typescript@5.8.2))(eslint@9.20.1) + version: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0) eslint-plugin-vue: - specifier: 9.33.0 - version: 9.33.0(eslint@9.20.1) + specifier: 10.0.0 + version: 10.0.0(eslint@9.21.0)(vue-eslint-parser@10.1.1(eslint@9.21.0)) fast-glob: specifier: 3.3.3 version: 3.3.3 happy-dom: - specifier: 17.2.2 - version: 17.2.2 + specifier: 17.3.0 + version: 17.3.0 intersection-observer: specifier: 0.12.2 version: 0.12.2 @@ -1020,26 +1032,26 @@ importers: specifier: 2.0.10 version: 2.0.10 storybook: - specifier: 8.6.3 - version: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + specifier: 8.6.4 + version: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) storybook-addon-misskey-theme: specifier: github:misskey-dev/storybook-addon-misskey-theme - version: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.6.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/components@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/core-events@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/manager-api@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/preview-api@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/theming@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/types@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.6.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/components@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/core-events@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/manager-api@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/preview-api@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/theming@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/types@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) vite-plugin-turbosnap: specifier: 1.0.3 version: 1.0.3 vitest: - specifier: 3.0.7 - version: 3.0.7(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.2.2)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) + specifier: 3.0.8 + version: 3.0.8(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.3.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) vitest-fetch-mock: specifier: 0.4.5 - version: 0.4.5(vitest@3.0.7(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.2.2)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)) + version: 0.4.5(vitest@3.0.8(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.3.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)) vue-component-type-helpers: specifier: 2.2.8 version: 2.2.8 vue-eslint-parser: - specifier: 9.4.3 - version: 9.4.3(eslint@9.20.1) + specifier: 10.1.1 + version: 10.1.1(eslint@9.21.0) vue-tsc: specifier: 2.2.8 version: 2.2.8(typescript@5.8.2) @@ -1066,7 +1078,7 @@ importers: version: 15.1.1 '@vitejs/plugin-vue': specifier: 5.2.1 - version: 5.2.1(vite@6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2)) + version: 5.2.1(vite@6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2)) '@vue/compiler-sfc': specifier: 3.5.13 version: 3.5.13 @@ -1119,8 +1131,8 @@ importers: specifier: 11.1.0 version: 11.1.0 vite: - specifier: 6.2.0 - version: 6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) + specifier: 6.2.1 + version: 6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) vue: specifier: 3.5.13 version: 3.5.13(typescript@5.8.2) @@ -1151,34 +1163,34 @@ importers: version: 8.18.0 '@typescript-eslint/eslint-plugin': specifier: 8.26.0 - version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.20.1)(typescript@5.8.2))(eslint@9.20.1)(typescript@5.8.2) + version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0)(typescript@5.8.2) '@typescript-eslint/parser': specifier: 8.26.0 - version: 8.26.0(eslint@9.20.1)(typescript@5.8.2) + version: 8.26.0(eslint@9.21.0)(typescript@5.8.2) '@vitest/coverage-v8': - specifier: 3.0.7 - version: 3.0.7(vitest@3.0.7(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.2.2)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)) + specifier: 3.0.8 + version: 3.0.8(vitest@3.0.8(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.3.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)) '@vue/runtime-core': specifier: 3.5.13 version: 3.5.13 acorn: - specifier: 8.14.0 - version: 8.14.0 + specifier: 8.14.1 + version: 8.14.1 cross-env: specifier: 7.0.3 version: 7.0.3 eslint-plugin-import: specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.20.1)(typescript@5.8.2))(eslint@9.20.1) + version: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0) eslint-plugin-vue: - specifier: 9.33.0 - version: 9.33.0(eslint@9.20.1) + specifier: 10.0.0 + version: 10.0.0(eslint@9.21.0)(vue-eslint-parser@10.1.1(eslint@9.21.0)) fast-glob: specifier: 3.3.3 version: 3.3.3 happy-dom: - specifier: 17.2.2 - version: 17.2.2 + specifier: 17.3.0 + version: 17.3.0 intersection-observer: specifier: 0.12.2 version: 0.12.2 @@ -1204,8 +1216,8 @@ importers: specifier: 2.2.8 version: 2.2.8 vue-eslint-parser: - specifier: 9.4.3 - version: 9.4.3(eslint@9.20.1) + specifier: 10.1.1 + version: 10.1.1(eslint@9.21.0) vue-tsc: specifier: 2.2.8 version: 2.2.8(typescript@5.8.2) @@ -1224,16 +1236,16 @@ importers: version: 22.13.9 '@typescript-eslint/eslint-plugin': specifier: 8.26.0 - version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.20.1)(typescript@5.8.2))(eslint@9.20.1)(typescript@5.8.2) + version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0)(typescript@5.8.2) '@typescript-eslint/parser': specifier: 8.26.0 - version: 8.26.0(eslint@9.20.1)(typescript@5.8.2) + version: 8.26.0(eslint@9.21.0)(typescript@5.8.2) esbuild: specifier: 0.25.0 version: 0.25.0 eslint-plugin-vue: - specifier: 9.33.0 - version: 9.33.0(eslint@9.20.1) + specifier: 10.0.0 + version: 10.0.0(eslint@9.21.0)(vue-eslint-parser@10.1.1(eslint@9.21.0)) nodemon: specifier: 3.1.9 version: 3.1.9 @@ -1241,8 +1253,8 @@ importers: specifier: 5.8.2 version: 5.8.2 vue-eslint-parser: - specifier: 9.4.3 - version: 9.4.3(eslint@9.20.1) + specifier: 10.1.1 + version: 10.1.1(eslint@9.21.0) packages/misskey-bubble-game: dependencies: @@ -1267,10 +1279,10 @@ importers: version: 3.0.8 '@typescript-eslint/eslint-plugin': specifier: 8.26.0 - version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.20.1)(typescript@5.8.2))(eslint@9.20.1)(typescript@5.8.2) + version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0)(typescript@5.8.2) '@typescript-eslint/parser': specifier: 8.26.0 - version: 8.26.0(eslint@9.20.1)(typescript@5.8.2) + version: 8.26.0(eslint@9.21.0)(typescript@5.8.2) esbuild: specifier: 0.25.0 version: 0.25.0 @@ -1313,10 +1325,10 @@ importers: version: 22.13.9 '@typescript-eslint/eslint-plugin': specifier: 8.26.0 - version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.20.1)(typescript@5.8.2))(eslint@9.20.1)(typescript@5.8.2) + version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0)(typescript@5.8.2) '@typescript-eslint/parser': specifier: 8.26.0 - version: 8.26.0(eslint@9.20.1)(typescript@5.8.2) + version: 8.26.0(eslint@9.21.0)(typescript@5.8.2) esbuild: specifier: 0.25.0 version: 0.25.0 @@ -1361,10 +1373,10 @@ importers: version: 22.13.9 '@typescript-eslint/eslint-plugin': specifier: 8.26.0 - version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.20.1)(typescript@5.8.2))(eslint@9.20.1)(typescript@5.8.2) + version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0)(typescript@5.8.2) '@typescript-eslint/parser': specifier: 8.26.0 - version: 8.26.0(eslint@9.20.1)(typescript@5.8.2) + version: 8.26.0(eslint@9.21.0)(typescript@5.8.2) openapi-types: specifier: 12.1.3 version: 12.1.3 @@ -1392,10 +1404,10 @@ importers: version: 22.13.9 '@typescript-eslint/eslint-plugin': specifier: 8.26.0 - version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.20.1)(typescript@5.8.2))(eslint@9.20.1)(typescript@5.8.2) + version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0)(typescript@5.8.2) '@typescript-eslint/parser': specifier: 8.26.0 - version: 8.26.0(eslint@9.20.1)(typescript@5.8.2) + version: 8.26.0(eslint@9.21.0)(typescript@5.8.2) esbuild: specifier: 0.25.0 version: 0.25.0 @@ -1426,13 +1438,13 @@ importers: devDependencies: '@typescript-eslint/parser': specifier: 8.26.0 - version: 8.26.0(eslint@9.20.1)(typescript@5.8.2) + version: 8.26.0(eslint@9.21.0)(typescript@5.8.2) '@typescript/lib-webworker': specifier: npm:@types/serviceworker@0.0.74 version: '@types/serviceworker@0.0.74' eslint-plugin-import: specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.20.1)(typescript@5.8.2))(eslint@9.20.1) + version: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0) nodemon: specifier: 3.1.9 version: 3.1.9 @@ -1445,11 +1457,6 @@ packages: '@adobe/css-tools@4.4.0': resolution: {integrity: sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==} - '@aiscript-dev/aiscript-languageserver@https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz': - resolution: {tarball: https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz} - version: 0.1.6 - hasBin: true - '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} @@ -1645,10 +1652,6 @@ packages: resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.23.5': - resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} - engines: {node: '>=6.9.0'} - '@babel/compat-data@7.24.7': resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} engines: {node: '>=6.9.0'} @@ -1689,10 +1692,6 @@ packages: resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.22.15': - resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} - engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.24.7': resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} engines: {node: '>=6.9.0'} @@ -1713,10 +1712,6 @@ packages: resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} engines: {node: '>=6.9.0'} - '@babel/helper-simple-access@7.22.5': - resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} - engines: {node: '>=6.9.0'} - '@babel/helper-simple-access@7.24.7': resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} engines: {node: '>=6.9.0'} @@ -1737,10 +1732,6 @@ packages: resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.23.5': - resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.24.7': resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} engines: {node: '>=6.9.0'} @@ -2143,32 +2134,28 @@ packages: resolution: {integrity: sha512-lpHyRyplhGPL5mGEh6M9O5nnKk0Gz4bFI+Zu6tKlPpDUN7XshWvH9C/px4UVm87IAANE0W81CEsNGbS1KlzXpA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-array@0.19.1': - resolution: {integrity: sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/core@0.10.0': - resolution: {integrity: sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==} + '@eslint/config-array@0.19.2': + resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.11.0': - resolution: {integrity: sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==} + '@eslint/core@0.12.0': + resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.2.0': - resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} + '@eslint/eslintrc@3.3.0': + resolution: {integrity: sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.20.0': - resolution: {integrity: sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==} + '@eslint/js@9.21.0': + resolution: {integrity: sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.5': - resolution: {integrity: sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==} + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.2.5': - resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==} + '@eslint/plugin-kit@0.2.7': + resolution: {integrity: sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@fastify/accept-negotiator@2.0.0': @@ -2277,8 +2264,8 @@ packages: resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} engines: {node: '>=18.18'} - '@humanwhocodes/retry@0.4.1': - resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} + '@humanwhocodes/retry@0.4.2': + resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} engines: {node: '>=18.18'} '@img/sharp-darwin-arm64@0.33.5': @@ -2413,6 +2400,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@istanbuljs/load-nyc-config@1.1.0': resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} @@ -3619,120 +3610,120 @@ packages: '@sqltools/formatter@1.2.5': resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} - '@storybook/addon-actions@8.6.3': - resolution: {integrity: sha512-0UrVqRoZFRFCqjtR8ODacpJNqi47qDUnsnB5F7e93U9ihSrH2edOBBX6frl11XKYA23rzq7jtnviFTVOpWpG7Q==} + '@storybook/addon-actions@8.6.4': + resolution: {integrity: sha512-mCcyfkeb19fJX0dpQqqZCnWBwjVn0/27xcpR0mbm/KW2wTByU6bKFFujgrHsX3ONl97IcIaUnmwwUwBr1ebZXw==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/addon-backgrounds@8.6.3': - resolution: {integrity: sha512-2mmMpMyUsS8rti2guMR4rk4h5YBLNHidxUqTm+U4nITZFfCXNP76To9hfTczpLTvUEpPxSbPG0sCIeHFaw4NRQ==} + '@storybook/addon-backgrounds@8.6.4': + resolution: {integrity: sha512-lRYGumlYdd1RptQJvOTRMx/q2pDmg2MO5GX4la7VfI8KrUyeuC1ZOSRDEcXeTuAZWJztqmtymg6bB7cAAoxCFA==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/addon-controls@8.6.3': - resolution: {integrity: sha512-j4Oof3nwjyiO6oNP1bJ98Sz1iZlYhdcgHX284yd0wBO91Q5B2GoCeqyCE+yRCh752ZnnYG1gazJrHmiG6gKxVg==} + '@storybook/addon-controls@8.6.4': + resolution: {integrity: sha512-oMMP9Bj0RMfYmaitjFt6oBSjKH4titUqP+wE6PrZ3v+Om56f4buqfNKXRf80As2OrsZn0pjj95muWzVVHqIhyQ==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/addon-docs@8.6.3': - resolution: {integrity: sha512-FRABH+r2huMpAK8iUQiFlYZtYenbqtudX3fNKFK9b38eV1R14kWggVG02lsa6upXbzxWVbMLUdOqaZJHxNbO/A==} + '@storybook/addon-docs@8.6.4': + resolution: {integrity: sha512-+kbcjvEAH0Xs+k+raAwfC0WmJilWhxBYnLLeazP3m5AkVI3sIjbzuuZ78NR0DCdRkw9BpuuXMHv5o4tIvLIUlw==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/addon-essentials@8.6.3': - resolution: {integrity: sha512-tH+MwkZ6UwRWyhGdq8izVZAZHGWdeiBY1wpIwdceP1Rl2j9s11Gbddb/JlmiXrC+f/Oiylxghaf7EIksVVqLQQ==} + '@storybook/addon-essentials@8.6.4': + resolution: {integrity: sha512-3pF0ZDl5EICqe0eOupPQq6PxeupwkLsfTWANuuJUYTJur82kvJd3Chb7P9vqw0A0QBx6106mL6PIyjrFJJMhLg==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/addon-highlight@8.6.3': - resolution: {integrity: sha512-LYZsgZt5q3EZBkZjUEELh/5+TDnUP0njuQ5g6skyKil6vj9+2RI4/Vjodp+ni5+xct5aDhXavRyUnPRfclX/Cg==} + '@storybook/addon-highlight@8.6.4': + resolution: {integrity: sha512-jFREXnSE/7VuBR8kbluN+DBVkMXEV7MGuCe8Ytb1/D2Q0ohgJe395dfVgEgSMXErOwsn//NV/NgJp6JNXH2DrA==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/addon-interactions@8.6.3': - resolution: {integrity: sha512-cDvxuMcjoQdtimNrT4BM9AK0qZJhA0Ep/CWPcVK1bAFzqlzBbe//UZa5It/AeC4EMYAr5rFY+LWEli3YPeOnjQ==} + '@storybook/addon-interactions@8.6.4': + resolution: {integrity: sha512-MZAAZjyvmJXCvM35zEiPpXz7vK+fimovt+WZKAMayAbXy5fT+7El0c9dDyTQ2norNKNj9QU/8hiU/1zARSUELQ==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/addon-links@8.6.3': - resolution: {integrity: sha512-3wGiMZxWbgdjEgymUrCVG5PwU0vAYF9EiSHsGxiSxje69l08GLD6s7FTLx0HwvuyiNFcigLcuF45XZnB252RtA==} + '@storybook/addon-links@8.6.4': + resolution: {integrity: sha512-TaSIteYLJ12+dVBk7fW96ZvNIFizKs+Vo/YuNAe4xTzFJRrjLkFj9htLVi/dusMfn7lYo5DHIns08LuM+po1Dg==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.6.3 + storybook: ^8.6.4 peerDependenciesMeta: react: optional: true - '@storybook/addon-mdx-gfm@8.6.3': - resolution: {integrity: sha512-SNHrYeGLqV1Ym1tDWQpDYU83o0R/cDRsFT4O1kiHTkb/QFZ3XZw1oq2bn02Bvt3LZTJ8KYiT3Iqf1mYWjcDkMQ==} + '@storybook/addon-mdx-gfm@8.6.4': + resolution: {integrity: sha512-9ipDU8pNnf0PiCTYaPtm7QLfhskYRE7S9vzZ0PDGwdlIDd2TdkAUZt8dys7zg9t4/Rc5UGWpGDEJ5O7v1H/OGw==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/addon-measure@8.6.3': - resolution: {integrity: sha512-FC/3pqM2adSnwyPOd9AxEoZD5XWCMKAk16urQFQ0M4+IzRUdf2OV8cc7aM/oZiBX36+q/UCcUWm2SbQ5nzNJpg==} + '@storybook/addon-measure@8.6.4': + resolution: {integrity: sha512-IpVL1rTy1tO8sy140eU3GdVB1QJ6J62+V6GSstcmqTLxDJQk5jFfg7hVbPEAZZ2sPFmeyceP9AMoBBo0EB355A==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/addon-outline@8.6.3': - resolution: {integrity: sha512-YklKHRkoDLSWawIIBrEI69RAWEdvhkYCOv+fMLu9zBeVPnkwbtIjXN/I+UJwPCm6jlxeEwEUAvbPWZMMf+BkPQ==} + '@storybook/addon-outline@8.6.4': + resolution: {integrity: sha512-28nAslKTy0zWMdxAZcipMDYrEp1TkXVooAsqMGY5AMXMiORi1ObjhmjTLhVt1dXp+aDg0X+M3B6PqoingmHhqQ==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/addon-storysource@8.6.3': - resolution: {integrity: sha512-Pwp3Xy/HIMLhaK4Qf+ZgZSnMWvmpvn7bKbbCjuyMHmGFA81lT4NlF+HaBp2IRio85Nk9zOF2wduuH1xaoBuSfQ==} + '@storybook/addon-storysource@8.6.4': + resolution: {integrity: sha512-b9azhM6ur+W2QZI+xp1NY7sTVJqWqGR1bgi8gFC/4vA//sXD17+S03GX84Wb8HWGGZheDvRKh41Ob2ebMUxHdg==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/addon-toolbars@8.6.3': - resolution: {integrity: sha512-GTC1GPrFNfWvvBaQQnGuL7ZfGK5Q+3ZovwQA9tnPu7QZEwea/4CXvUyQh1u0NwqrFZkrabOad1XvYfpRuCPGSA==} + '@storybook/addon-toolbars@8.6.4': + resolution: {integrity: sha512-PU2lvgwCKDn93zpp5MEog103UUmSSugcxDf18xaoa9D15Qtr+YuQHd2hXbxA7+dnYL9lA7MLYsstfxE91ieM4Q==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/addon-viewport@8.6.3': - resolution: {integrity: sha512-AixZKiQdBVs7ePj5iV0U1IY2jvH0G7wQJwBRTOq4qC1FKiOsZEYmrwc3wLUBUlVqyenXFKN+H40r4VhPzzSfLw==} + '@storybook/addon-viewport@8.6.4': + resolution: {integrity: sha512-O5Ij+SRVg6grY6JOL5lOpsFyopZxuZEl2GHfh2SUf9hfowNS0QAgFpJupqXkwZzRSrlf9uKrLkjB6ulLgN2gOQ==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/blocks@8.6.3': - resolution: {integrity: sha512-Ieu6kwqdeAcrLzcX2QIqnCd0XWZi46i4eem8W54JRiOMQMYUpZ7onbciRAP58qxEWrZWqgxPS+tiCTaJe48VVQ==} + '@storybook/blocks@8.6.4': + resolution: {integrity: sha512-+oPXwT3KzJzsdkQuGEzBqOKTIFlb6qmlCWWbDwAnP0SEqYHoTVRTAIa44icFP0EZeIe+ypFVAm1E7kWTLmw1hQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^8.6.3 + storybook: ^8.6.4 peerDependenciesMeta: react: optional: true react-dom: optional: true - '@storybook/builder-vite@8.6.3': - resolution: {integrity: sha512-v/nlBeT7Avn1ld2GHY5dtm1+TKREvtQ+DEcKK5iOWfv2259WqUp0dGnF4fbHcsNCtFurkA/P2uqJ9vc0xOIVUg==} + '@storybook/builder-vite@8.6.4': + resolution: {integrity: sha512-FuSP2GhWVVTt6NdX0UJHhPOqhu09X4apSk+KWUf3aITRIJg9gbPYtJDBmxv1vXQEgvfCDdYBYbeG1khiO/Ghfw==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 vite: ^4.0.0 || ^5.0.0 || ^6.0.0 - '@storybook/components@8.6.3': - resolution: {integrity: sha512-q5DQkV+E/j0KfF818RywgqEHjaZTg71q5YY4z0UO8CRSzDQ/VYF6L76oc69corbkJtYAk/GqaYJllzrWykS4sg==} + '@storybook/components@8.6.4': + resolution: {integrity: sha512-91VEVFWOgHkEFoNFMk6gs1AuOE9Yp7N283BXQOW+AgP+atpzED6t/fIBPGqJ2ewAuzLJ+cFOrasSzoNwVfg3Jg==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/core-events@8.6.3': - resolution: {integrity: sha512-+UCkypJKcrITCuA84IITcn1RI1gxXGl72e0KszfFYgsSkJpDMvxSU2RwVqJSORBe5GU3XOIR3FaNngbkO3nXMg==} + '@storybook/core-events@8.6.4': + resolution: {integrity: sha512-m2fux7Z/XZXS3Hpq0PHOSj6U8hpTrvmXs2OmH4CtKoDdTy8iFVBW+Q2057Klwvp37y8dF01jPn1lSAG1nkSpYQ==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/core@8.6.3': - resolution: {integrity: sha512-0iMTfmo3UFCa1hFJLtThnRIppkIpGPyTL3MElhORP1t5l9lCUq5am0ymbi/TeCbsJPjE86FjeO0NinokL9iQiw==} + '@storybook/core@8.6.4': + resolution: {integrity: sha512-glDbjEBi3wokw1T+KQtl93irHO9N0LCwgylWfWVXYDdQjUJ7pGRQGnw73gPX7Ds9tg3myXFC83GjmY94UYSMbA==} peerDependencies: prettier: ^2 || ^3 peerDependenciesMeta: prettier: optional: true - '@storybook/csf-plugin@8.6.3': - resolution: {integrity: sha512-0QDLBcMOxSEt1yH28cvIsoiaIokIxDDShMnxVJHWk/7+KZ3xe4lZBfKCWZspZoJmrxgz10gLRifj1b3ysIFlyA==} + '@storybook/csf-plugin@8.6.4': + resolution: {integrity: sha512-7UpEp4PFTy1iKjZiRaYMG7zvnpLIRPyD0+lUJUlLYG4UIemV3onvnIi1Je1tSZ4hfTup+ulom7JLztVSHZGRMg==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 '@storybook/global@5.0.0': resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} @@ -3744,49 +3735,49 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - '@storybook/instrumenter@8.6.3': - resolution: {integrity: sha512-Y5n6JWCWdOqok08Hgklsc98TBoqROhAhBRSzNWuIaLsRhz8EziXQtuEkWqmVbyYOys25iTZiK3S8+QQkOzGrBw==} + '@storybook/instrumenter@8.6.4': + resolution: {integrity: sha512-8OtIWLhayTUdqJEeXiPm6l3LTdSkWgQzzV2l2HIe4Adedeot+Rkwu6XHmyRDpnb0+Ish6zmMDqtJBxC2PQsy6Q==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/manager-api@8.6.3': - resolution: {integrity: sha512-7m9MQELc6XpuKIuliqMiQWzl8yVWpUDwTcpr+rTT7l3OfRzw7Y00UFct2tI03YG6EXsxsykw8EmueMQhe0lG5Q==} + '@storybook/manager-api@8.6.4': + resolution: {integrity: sha512-w/Nn/VznfbIg2oezDfzZNwSTDY5kBZbzxVBHLCnIcyu2AKt2Yto3pfGi60SikFcTrsClaAKT7D92kMQ9qdQNQQ==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/preview-api@8.6.3': - resolution: {integrity: sha512-y2Ic6eHBQD/AwaCHctKOJ4tOM1r7/mPXfhGh0I+Qf8kZPlDTgQcJ6Z7/Ruma1L+ijXPBWouDaPw51gipcX+t9Q==} + '@storybook/preview-api@8.6.4': + resolution: {integrity: sha512-5HBfxggzxGz0dg2c61NpPiQJav7UAmzsQlzmI5SzWOS6lkaylcDG8giwKzASVCXVWBxNji9qIDFM++UH090aDg==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/react-dom-shim@8.6.3': - resolution: {integrity: sha512-vE3LA2TxbzDF1Fso2IgvUtoHc+8a6laKhuJdx8frP5A8M1KGOBfuEPFCCcE49Q90HUlDgwb/zQl1GNq/QjLgWQ==} + '@storybook/react-dom-shim@8.6.4': + resolution: {integrity: sha512-kTGJ3aFdmfCFzYaDFGmZWfTXr9xhbUaf0tJ6+nEjc4tME6mFwMI+tTUT6U/J6mJhZuc2DjvIRA7bM0x77dIDqw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/react-vite@8.6.3': - resolution: {integrity: sha512-A/cA0wM/mMfFcJH7dxhWSbVg9aE2zZKNDioyEbiB042CgrLW3zQ6dvQvA5ohFhsPWZ6GVAyc+r3x0JE55aXxWQ==} + '@storybook/react-vite@8.6.4': + resolution: {integrity: sha512-MEmD6sP2tUI/SYCXCeWGTs8umZj+N0e3DHXCQUz0nCsJH7kuCTTipOTBQvr/GuEstNd7BNG5k8aLIRrXLjAvdA==} engines: {node: '>=18.0.0'} peerDependencies: - '@storybook/test': 8.6.3 + '@storybook/test': 8.6.4 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.6.3 + storybook: ^8.6.4 vite: ^4.0.0 || ^5.0.0 || ^6.0.0 peerDependenciesMeta: '@storybook/test': optional: true - '@storybook/react@8.6.3': - resolution: {integrity: sha512-B4WYRWU2Y71UWl4CG3+mcB7duNln9finJyDB8Y1o2CYWUxgEo+3Bnp3k7NUr++tYVkZI1H+28UWeX0rpCkvReQ==} + '@storybook/react@8.6.4': + resolution: {integrity: sha512-pfv4hMhu3AScOh0l86uIzmXLSQ0XA/e0reIVwQcxKht6miaKArhx9GkS4mMp6SO23ZoV5G/nfLgUaMVPVE0ZPg==} engines: {node: '>=18.0.0'} peerDependencies: - '@storybook/test': 8.6.3 + '@storybook/test': 8.6.4 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.6.3 + storybook: ^8.6.4 typescript: '>= 4.2.x' peerDependenciesMeta: '@storybook/test': @@ -3794,38 +3785,38 @@ packages: typescript: optional: true - '@storybook/source-loader@8.6.3': - resolution: {integrity: sha512-I6vDK5mwLLHgBqh0OVn2zAZbZ8Xa3L/+w1tNN4v6HuAjW1DU6rdBtxvjTFjxTDqyvSptxL1AvVsFF62hN7GsuQ==} + '@storybook/source-loader@8.6.4': + resolution: {integrity: sha512-1lwX+X4dH/KNt5hsOjSGuARIaQzwMnRBdZCqIfVFwjcP0qUAIBFVTsD6R4YemJ1HkV8zjX6Zs8Ubf99M84VtPQ==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/test@8.6.3': - resolution: {integrity: sha512-UimvhV/PmYoXCwIbGpkyqQfMhjdH2GaHJbV6BWr7M7BHA3kUS6zYJAm2V2CC5SYcmyj7FejLB4tgL7FmLXB6hA==} + '@storybook/test@8.6.4': + resolution: {integrity: sha512-JPjfbaMMuCBT47pg3/MDD9vYFF5OGPAOWEB9nJWJ9IjYAb2Nd8OYJQIDoYJQNT+aLkTVLtvzGnVNwdxpouAJcQ==} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 - '@storybook/theming@8.6.3': - resolution: {integrity: sha512-sDcWnnko73KOCIc9stQyec9KvTmGOuMswqeKtWh0ha/wsgYB6G2/2j1xOheFmWKPitOsbwgvqtjCP7bRE68uIA==} + '@storybook/theming@8.6.4': + resolution: {integrity: sha512-g9Ns4uenC9oAWETaJ/tEKEIPMdS+CqjNWZz5Wbw1bLNhXwADZgKrVqawzZi64+bYYtQ+i8VCTjPoFa6s2eHiDQ==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/types@8.6.3': - resolution: {integrity: sha512-lU2PO4SBhgtDru7TH/18jFNY7I5aFPdmd7BpZk1dkHuOgklOKUU2yC6VtRoRu8cJUmhzuUXET/K/FHOx95c4dw==} + '@storybook/types@8.6.4': + resolution: {integrity: sha512-6VJat4/2Ne2tuz3s0iOHpMpW3/zdHGDAxGTSgJCxcgElNQgceI8dlhTwB0b3B6WyoG2tSt9DgbGa/TdfZhSAPg==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/vue3-vite@8.6.3': - resolution: {integrity: sha512-7LVIDjujXsML5KqDrj03u96C78aYkk4zw+y/enYMZGtc0mZ0mJEYKEJy8GhraqZGEU21Ni+ckeCttgt31S0UPQ==} + '@storybook/vue3-vite@8.6.4': + resolution: {integrity: sha512-zmWfSc3DfJzh1DkdiKrQW9XHfBnnlauCroBfvKjo7v91eLXi2GTH2U6CUJR0d7jJ7TJh5dKN6JOQINtk/PKxKg==} engines: {node: '>=18.0.0'} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 vite: ^4.0.0 || ^5.0.0 || ^6.0.0 - '@storybook/vue3@8.6.3': - resolution: {integrity: sha512-QMvAlX20+Yq37vkcR7VP21+f1yj+HoS5j6nvQZnfp36rs08KVnXBTF8KtnbjAqBA1KFdZgSXMtB4fWxvCHGLbA==} + '@storybook/vue3@8.6.4': + resolution: {integrity: sha512-Vrgnonk3sSZZHciJ/0Ey+cnOEDlqucnLks1iJ1QKrka//Y+rtIzfHhcGs7HnF0VxtFpajNPSNM4MYj59USjiXQ==} engines: {node: '>=18.0.0'} peerDependencies: - storybook: ^8.6.3 + storybook: ^8.6.4 vue: ^3.0.0 '@stylistic/eslint-plugin@2.13.0': @@ -3840,7 +3831,7 @@ packages: hasBin: true peerDependencies: '@swc/core': ^1.2.66 - chokidar: 3.6.0 + chokidar: 4.0.3 peerDependenciesMeta: chokidar: optional: true @@ -4470,11 +4461,11 @@ packages: vite: ^5.0.0 || ^6.0.0 vue: ^3.2.25 - '@vitest/coverage-v8@3.0.7': - resolution: {integrity: sha512-Av8WgBJLTrfLOer0uy3CxjlVuWK4CzcLBndW1Nm2vI+3hZ2ozHututkfc7Blu1u6waeQ7J8gzPK/AsBRnWA5mQ==} + '@vitest/coverage-v8@3.0.8': + resolution: {integrity: sha512-y7SAKsQirsEJ2F8bulBck4DoluhI2EEgTimHd6EEUgJBGKy9tC25cpywh1MH4FvDGoG2Unt7+asVd1kj4qOSAw==} peerDependencies: - '@vitest/browser': 3.0.7 - vitest: 3.0.7 + '@vitest/browser': 3.0.8 + vitest: 3.0.8 peerDependenciesMeta: '@vitest/browser': optional: true @@ -4482,11 +4473,11 @@ packages: '@vitest/expect@2.0.5': resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} - '@vitest/expect@3.0.7': - resolution: {integrity: sha512-QP25f+YJhzPfHrHfYHtvRn+uvkCFCqFtW9CktfBxmB+25QqWsx7VB2As6f4GmwllHLDhXNHvqedwhvMmSnNmjw==} + '@vitest/expect@3.0.8': + resolution: {integrity: sha512-Xu6TTIavTvSSS6LZaA3EebWFr6tsoXPetOWNMOlc7LO88QVVBwq2oQWBoDiLCN6YTvNYsGSjqOO8CAdjom5DCQ==} - '@vitest/mocker@3.0.7': - resolution: {integrity: sha512-qui+3BLz9Eonx4EAuR/i+QlCX6AUZ35taDQgwGkK/Tw6/WgwodSrjN1X2xf69IA/643ZX5zNKIn2svvtZDrs4w==} + '@vitest/mocker@3.0.8': + resolution: {integrity: sha512-n3LjS7fcW1BCoF+zWZxG7/5XvuYH+lsFg+BDwwAz0arIwHQJFUEsKBQ0BLU49fCxuM/2HSeBPHQD8WjgrxMfow==} peerDependencies: msw: ^2.4.9 vite: ^5.0.0 || ^6.0.0 @@ -4502,20 +4493,20 @@ packages: '@vitest/pretty-format@2.1.1': resolution: {integrity: sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==} - '@vitest/pretty-format@3.0.7': - resolution: {integrity: sha512-CiRY0BViD/V8uwuEzz9Yapyao+M9M008/9oMOSQydwbwb+CMokEq3XVaF3XK/VWaOK0Jm9z7ENhybg70Gtxsmg==} + '@vitest/pretty-format@3.0.8': + resolution: {integrity: sha512-BNqwbEyitFhzYMYHUVbIvepOyeQOSFA/NeJMIP9enMntkkxLgOcgABH6fjyXG85ipTgvero6noreavGIqfJcIg==} - '@vitest/runner@3.0.7': - resolution: {integrity: sha512-WeEl38Z0S2ZcuRTeyYqaZtm4e26tq6ZFqh5y8YD9YxfWuu0OFiGFUbnxNynwLjNRHPsXyee2M9tV7YxOTPZl2g==} + '@vitest/runner@3.0.8': + resolution: {integrity: sha512-c7UUw6gEcOzI8fih+uaAXS5DwjlBaCJUo7KJ4VvJcjL95+DSR1kova2hFuRt3w41KZEFcOEiq098KkyrjXeM5w==} - '@vitest/snapshot@3.0.7': - resolution: {integrity: sha512-eqTUryJWQN0Rtf5yqCGTQWsCFOQe4eNz5Twsu21xYEcnFJtMU5XvmG0vgebhdLlrHQTSq5p8vWHJIeJQV8ovsA==} + '@vitest/snapshot@3.0.8': + resolution: {integrity: sha512-x8IlMGSEMugakInj44nUrLSILh/zy1f2/BgH0UeHpNyOocG18M9CWVIFBaXPt8TrqVZWmcPjwfG/ht5tnpba8A==} '@vitest/spy@2.0.5': resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==} - '@vitest/spy@3.0.7': - resolution: {integrity: sha512-4T4WcsibB0B6hrKdAZTM37ekuyFZt2cGbEGd2+L0P8ov15J1/HUsUaqkXEQPNAWr4BtPPe1gI+FYfMHhEKfR8w==} + '@vitest/spy@3.0.8': + resolution: {integrity: sha512-MR+PzJa+22vFKYb934CejhR4BeRpMSoxkvNoDit68GQxRLSf11aT6CTj3XaqUU9rxgWJFnqicN/wxw6yBRkI1Q==} '@vitest/utils@2.0.5': resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==} @@ -4523,8 +4514,8 @@ packages: '@vitest/utils@2.1.1': resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==} - '@vitest/utils@3.0.7': - resolution: {integrity: sha512-xePVpCRfooFX3rANQjwoditoXgWb1MaFbzmGuPP59MK6i13mrnDw/yEIyJudLeW6/38mCNcwCiJIGmpDPibAIg==} + '@vitest/utils@3.0.8': + resolution: {integrity: sha512-nkBC3aEhfX2PdtQI/QwAWp8qZWwzASsU4Npbcd5RdMPBSSLCpkZp52P3xku3s3uA0HIEhGvEcF8rNkBsz9dQ4Q==} '@volar/language-core@2.2.0': resolution: {integrity: sha512-a8WG9+4OdeNDW4ywABZIM6S6UN7em8uIlM/BZ2pWQUYrVmX+m8sj/X+QadvO+Li/t/LjAqbWJQtVgxdpEWLALQ==} @@ -4677,8 +4668,8 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - acorn@8.14.0: - resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + acorn@8.14.1: + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} engines: {node: '>=0.4.0'} hasBin: true @@ -5020,10 +5011,6 @@ packages: resolution: {integrity: sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==} engines: {node: '>=12'} - binary-extensions@2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} - engines: {node: '>=8'} - bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -5065,11 +5052,6 @@ packages: browser-assert@1.2.1: resolution: {integrity: sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==} - browserslist@4.23.0: - resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - browserslist@4.24.4: resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -5146,9 +5128,6 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - call-bind@1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} - call-bind@1.0.7: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} @@ -5281,9 +5260,9 @@ packages: resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==} engines: {node: '>=18.17'} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} @@ -5292,6 +5271,10 @@ packages: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + chromatic@11.27.0: resolution: {integrity: sha512-jQ2ufjS+ePpg+NtcPI9B2eOi+pAzlRd2nhd1LgNMsVCC9Bzf5t8mJtyd8v2AUuJS0LdX0QVBgkOnlNv9xviHzA==} hasBin: true @@ -5604,11 +5587,6 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - cypress@14.0.3: - resolution: {integrity: sha512-yIdvobANw3kS+KF/t5vwjjPNufBA8ux7iQHaWxPTkUw2yCKI72m9mKM24eOwE84Wk4ALPsSvEcGbDrwgmhr4RA==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - hasBin: true - cypress@14.1.0: resolution: {integrity: sha512-pPPj8Uu9NwjaaiXAEcjYZZmgsq6v9Zs1Nw6a+zRF+ANgYSNhH4S32SjFRsvMcuOHR/8dp4GBJhBPqIPSs+TxaA==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -5925,9 +5903,6 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.4.686: - resolution: {integrity: sha512-3avY1B+vUzNxEgkBDpKOP8WarvUAEwpRaiCL0He5OKWEFxzaOFiq4WoZEZe7qh0ReS7DiWoHMnYoQCKxNZNzSg==} - electron-to-chromium@1.5.83: resolution: {integrity: sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==} @@ -6121,19 +6096,16 @@ packages: '@typescript-eslint/parser': optional: true - eslint-plugin-vue@9.33.0: - resolution: {integrity: sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==} - engines: {node: ^14.17.0 || >=16.0.0} + eslint-plugin-vue@10.0.0: + resolution: {integrity: sha512-XKckedtajqwmaX6u1VnECmZ6xJt+YvlmMzBPZd+/sI3ub2lpYZyFnsyWo7c3nMOQKJQudeyk1lw/JxdgeKT64w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 + vue-eslint-parser: ^10.0.0 eslint-rule-docs@1.1.235: resolution: {integrity: sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A==} - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-scope@8.2.0: resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6146,8 +6118,8 @@ packages: resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.20.1: - resolution: {integrity: sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==} + eslint@9.21.0: + resolution: {integrity: sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -6160,10 +6132,6 @@ packages: resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} @@ -6641,16 +6609,12 @@ packages: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} - globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@15.15.0: - resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + globals@16.0.0: + resolution: {integrity: sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==} engines: {node: '>=18'} globalthis@1.0.3: @@ -6697,8 +6661,8 @@ packages: resolution: {integrity: sha512-n0QrmT9lD81rbpKsyhnlz3DgnMZlaOkJPpgi746doA+HvaMC79bdWkwjrNnGJRvDrWTI8iOcJiVTJ5CdT/AZRw==} engines: {node: '>=18.0.0'} - happy-dom@17.2.2: - resolution: {integrity: sha512-3I1/CrNi780sdOhuhUnFtgTWhloSc3quSZwsylI41jycx8o97M6Y4aQAu0phSexGusT7+59BxATh4L4xiY0HcA==} + happy-dom@17.3.0: + resolution: {integrity: sha512-dTwlpUHrhE0usQOd1Df9k461SOYQUWNl0G31mXCDj+N9//oPcDb+cchrSJzrXN6qxZ5sZSrLf5AfY702Zvddfw==} engines: {node: '>=18.0.0'} hard-rejection@2.1.0: @@ -6901,9 +6865,9 @@ packages: ignore-by-default@1.0.1: resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} - ignore-walk@6.0.5: - resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ignore-walk@7.0.0: + resolution: {integrity: sha512-T4gbf83A4NH95zvhVYZc+qWocBBGlpzUXLPGurJggw/WIOwicfXJChLDP/iBZnN5WqROSu5Bm3hhle4z8a8YGQ==} + engines: {node: ^18.17.0 || >=20.5.0} ignore@5.3.1: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} @@ -7026,10 +6990,6 @@ packages: is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - is-boolean-object@1.1.2: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} @@ -7686,9 +7646,6 @@ packages: resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} engines: {node: '>=12'} - magic-string@0.30.11: - resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} - magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} @@ -8008,10 +7965,6 @@ packages: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} - minipass@7.0.4: - resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} - engines: {node: '>=16 || 14 >=14.17'} - minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} @@ -8020,6 +7973,10 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} + minizlib@3.0.1: + resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==} + engines: {node: '>= 18'} + mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} @@ -8037,6 +7994,11 @@ packages: engines: {node: '>=10'} hasBin: true + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + mnemonist@0.39.8: resolution: {integrity: sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==} @@ -8201,9 +8163,6 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-releases@2.0.14: - resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} - node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} @@ -8645,6 +8604,11 @@ packages: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} + pnpm@10.6.1: + resolution: {integrity: sha512-QO4Jr0B/qfu1+/uOHLQPu3TArww+EOkiTXtTx2WFKGFbLJJFDnTPrZHjotyv485AUNgL2nHXV3VtLOK2YhPpow==} + engines: {node: '>=18.12'} + hasBin: true + polished@4.2.2: resolution: {integrity: sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==} engines: {node: '>=10'} @@ -8826,10 +8790,6 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.2: - resolution: {integrity: sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.5.3: resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} @@ -9151,9 +9111,9 @@ packages: readdir-glob@1.1.2: resolution: {integrity: sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA==} - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} real-require@0.2.0: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} @@ -9303,6 +9263,10 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + rimraf@5.0.10: + resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} + hasBin: true + rollup@4.34.9: resolution: {integrity: sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -9403,11 +9367,6 @@ packages: engines: {node: '>=10'} hasBin: true - semver@7.6.0: - resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} - engines: {node: '>=10'} - hasBin: true - semver@7.6.3: resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} engines: {node: '>=10'} @@ -9726,8 +9685,8 @@ packages: react-dom: optional: true - storybook@8.6.3: - resolution: {integrity: sha512-Vbmd8/FXp6X0AOMak6arcg3WdkHj+2AYJTNHbCPVHsCEbnREyRZIG+Eq5/Ffmy6byiz+4OAX5HwsHGSMR6Xmow==} + storybook@8.6.4: + resolution: {integrity: sha512-XXh1Acvf1r3BQX0BDLQw6yhZ7yUGvYxIcKOBuMdetnX7iXtczipJTfw0uyFwk0ltkKEE9PpJvivYmARF3u64VQ==} hasBin: true peerDependencies: prettier: ^2 || ^3 @@ -9921,6 +9880,10 @@ packages: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + taskkill@5.0.0: resolution: {integrity: sha512-+HRtZ40Vc+6YfCDWCeAsixwxJgMbPY4HHuTgzPYH3JXvqHWUlsCfy+ylXlAKhFNcuLp4xVeWeFBUhDk+7KYUvQ==} engines: {node: '>=14.16'} @@ -10130,10 +10093,6 @@ packages: resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} engines: {node: '>=10'} - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} @@ -10351,12 +10310,6 @@ packages: resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} engines: {node: '>=8'} - update-browserslist-db@1.0.13: - resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - update-browserslist-db@1.1.2: resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} hasBin: true @@ -10429,16 +10382,16 @@ packages: vfile@6.0.1: resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==} - vite-node@3.0.7: - resolution: {integrity: sha512-2fX0QwX4GkkkpULXdT1Pf4q0tC1i1lFOyseKoonavXUNlQ77KpW2XqBGGNIm/J4Ows4KxgGJzDguYVPKwG/n5A==} + vite-node@3.0.8: + resolution: {integrity: sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true vite-plugin-turbosnap@1.0.3: resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==} - vite@6.2.0: - resolution: {integrity: sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==} + vite@6.2.1: + resolution: {integrity: sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -10483,16 +10436,16 @@ packages: peerDependencies: vitest: '>=2.0.0' - vitest@3.0.7: - resolution: {integrity: sha512-IP7gPK3LS3Fvn44x30X1dM9vtawm0aesAa2yBIZ9vQf+qB69NXC5776+Qmcr7ohUXIQuLhk7xQR0aSUIDPqavg==} + vitest@3.0.8: + resolution: {integrity: sha512-dfqAsNqRGUc8hB9OVR2P0w8PZPEckti2+5rdZip0WIz9WW0MnImJ8XiR61QhqLa92EQzKP2uPkzenKOAHyEIbA==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/debug': ^4.1.12 '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.0.7 - '@vitest/ui': 3.0.7 + '@vitest/browser': 3.0.8 + '@vitest/ui': 3.0.8 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -10526,16 +10479,9 @@ packages: vscode-languageserver-protocol@3.17.5: resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} - vscode-languageserver-textdocument@1.0.11: - resolution: {integrity: sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==} - vscode-languageserver-types@3.17.5: resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} - vscode-languageserver@9.0.1: - resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} - hasBin: true - vscode-uri@3.0.8: resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} @@ -10572,11 +10518,11 @@ packages: peerDependencies: vue: '>=2' - vue-eslint-parser@9.4.3: - resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} - engines: {node: ^14.17.0 || >=16.0.0} + vue-eslint-parser@10.1.1: + resolution: {integrity: sha512-bh2Z/Au5slro9QJ3neFYLanZtb1jH+W2bKqGHXAoYD4vZgNG3KeotL7JpPv5xzY4UXUXJl7TrIsnzECH63kd3Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: '>=6.0.0' + eslint: ^8.57.0 || ^9.0.0 vue-inbrowser-compiler-independent-utils@4.71.1: resolution: {integrity: sha512-K3wt3iVmNGaFEOUR4JIThQRWfqokxLfnPslD41FDZB2ajXp789+wCqJyGYlIFsvEQ2P61PInw6/ph5iiqg51gg==} @@ -10788,6 +10734,10 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} @@ -10842,14 +10792,6 @@ snapshots: '@adobe/css-tools@4.4.0': {} - '@aiscript-dev/aiscript-languageserver@https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz': - dependencies: - seedrandom: 3.0.5 - stringz: 2.1.0 - uuid: 9.0.1 - vscode-languageserver: 9.0.1 - vscode-languageserver-textdocument: 1.0.11 - '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.5 @@ -11375,8 +11317,6 @@ snapshots: '@babel/highlight': 7.24.7 picocolors: 1.1.1 - '@babel/compat-data@7.23.5': {} - '@babel/compat-data@7.24.7': {} '@babel/core@7.23.5': @@ -11435,9 +11375,9 @@ snapshots: '@babel/helper-compilation-targets@7.22.15': dependencies: - '@babel/compat-data': 7.23.5 - '@babel/helper-validator-option': 7.23.5 - browserslist: 4.23.0 + '@babel/compat-data': 7.24.7 + '@babel/helper-validator-option': 7.24.7 + browserslist: 4.24.4 lru-cache: 5.1.1 semver: 6.3.1 @@ -11462,10 +11402,6 @@ snapshots: dependencies: '@babel/types': 7.25.6 - '@babel/helper-module-imports@7.22.15': - dependencies: - '@babel/types': 7.25.6 - '@babel/helper-module-imports@7.24.7': dependencies: '@babel/traverse': 7.24.7 @@ -11477,10 +11413,12 @@ snapshots: dependencies: '@babel/core': 7.23.5 '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-module-imports': 7.22.15 - '@babel/helper-simple-access': 7.22.5 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 '@babel/helper-split-export-declaration': 7.24.7 '@babel/helper-validator-identifier': 7.24.7 + transitivePeerDependencies: + - supports-color '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)': dependencies: @@ -11495,10 +11433,6 @@ snapshots: '@babel/helper-plugin-utils@7.22.5': {} - '@babel/helper-simple-access@7.22.5': - dependencies: - '@babel/types': 7.25.6 - '@babel/helper-simple-access@7.24.7': dependencies: '@babel/traverse': 7.24.7 @@ -11516,8 +11450,6 @@ snapshots: '@babel/helper-validator-identifier@7.24.7': {} - '@babel/helper-validator-option@7.23.5': {} - '@babel/helper-validator-option@7.24.7': {} '@babel/helpers@7.23.5': @@ -11911,32 +11843,28 @@ snapshots: '@esbuild/win32-x64@0.25.0': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@9.20.1)': + '@eslint-community/eslint-utils@4.4.0(eslint@9.21.0)': dependencies: - eslint: 9.20.1 + eslint: 9.21.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} '@eslint/compat@1.1.1': {} - '@eslint/config-array@0.19.1': + '@eslint/config-array@0.19.2': dependencies: - '@eslint/object-schema': 2.1.5 + '@eslint/object-schema': 2.1.6 debug: 4.4.0(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color - '@eslint/core@0.10.0': + '@eslint/core@0.12.0': dependencies: '@types/json-schema': 7.0.15 - '@eslint/core@0.11.0': - dependencies: - '@types/json-schema': 7.0.15 - - '@eslint/eslintrc@3.2.0': + '@eslint/eslintrc@3.3.0': dependencies: ajv: 6.12.6 debug: 4.4.0(supports-color@8.1.1) @@ -11950,13 +11878,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.20.0': {} + '@eslint/js@9.21.0': {} - '@eslint/object-schema@2.1.5': {} + '@eslint/object-schema@2.1.6': {} - '@eslint/plugin-kit@0.2.5': + '@eslint/plugin-kit@0.2.7': dependencies: - '@eslint/core': 0.10.0 + '@eslint/core': 0.12.0 levn: 0.4.1 '@fastify/accept-negotiator@2.0.0': {} @@ -12099,7 +12027,7 @@ snapshots: '@humanwhocodes/retry@0.3.0': {} - '@humanwhocodes/retry@0.4.1': {} + '@humanwhocodes/retry@0.4.2': {} '@img/sharp-darwin-arm64@0.33.5': optionalDependencies: @@ -12213,6 +12141,10 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + '@istanbuljs/load-nyc-config@1.1.0': dependencies: camelcase: 5.3.1 @@ -12389,12 +12321,12 @@ snapshots: '@types/yargs': 17.0.19 chalk: 4.1.2 - '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.8.2)(vite@6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.8.2)(vite@6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))': dependencies: glob: 10.4.5 magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.8.2) - vite: 6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) + vite: 6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) optionalDependencies: typescript: 5.8.2 @@ -12495,15 +12427,15 @@ snapshots: '@misskey-dev/browser-image-resizer@2024.1.0': {} - '@misskey-dev/eslint-plugin@2.1.0(@eslint/compat@1.1.1)(@stylistic/eslint-plugin@2.13.0(eslint@9.20.1)(typescript@5.7.3))(@typescript-eslint/eslint-plugin@8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1)(typescript@5.7.3))(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1))(eslint@9.20.1)(globals@15.15.0)': + '@misskey-dev/eslint-plugin@2.1.0(@eslint/compat@1.1.1)(@stylistic/eslint-plugin@2.13.0(eslint@9.21.0)(typescript@5.8.2))(@typescript-eslint/eslint-plugin@8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0)(typescript@5.8.2))(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0))(eslint@9.21.0)(globals@16.0.0)': dependencies: '@eslint/compat': 1.1.1 - '@stylistic/eslint-plugin': 2.13.0(eslint@9.20.1)(typescript@5.7.3) - '@typescript-eslint/eslint-plugin': 8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1)(typescript@5.7.3) - '@typescript-eslint/parser': 8.24.0(eslint@9.20.1)(typescript@5.7.3) - eslint: 9.20.1 - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1) - globals: 15.15.0 + '@stylistic/eslint-plugin': 2.13.0(eslint@9.21.0)(typescript@5.8.2) + '@typescript-eslint/eslint-plugin': 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0)(typescript@5.8.2) + '@typescript-eslint/parser': 8.26.0(eslint@9.21.0)(typescript@5.8.2) + eslint: 9.21.0 + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0) + globals: 16.0.0 '@misskey-dev/sharp-read-bmp@1.2.0': dependencies: @@ -13119,7 +13051,7 @@ snapshots: '@rollup/plugin-replace@6.0.2(rollup@4.34.9)': dependencies: '@rollup/pluginutils': 5.1.4(rollup@4.34.9) - magic-string: 0.30.11 + magic-string: 0.30.17 optionalDependencies: rollup: 4.34.9 @@ -13757,144 +13689,144 @@ snapshots: '@sqltools/formatter@1.2.5': {} - '@storybook/addon-actions@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-actions@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 '@types/uuid': 9.0.8 dequal: 2.0.3 polished: 4.2.2 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) uuid: 9.0.1 - '@storybook/addon-backgrounds@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-backgrounds@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 memoizerific: 1.11.3 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - '@storybook/addon-controls@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-controls@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 dequal: 2.0.3 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - '@storybook/addon-docs@8.6.3(@types/react@18.0.28)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-docs@8.6.4(@types/react@18.0.28)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@mdx-js/react': 3.0.1(@types/react@18.0.28)(react@19.0.0) - '@storybook/blocks': 8.6.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/csf-plugin': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/react-dom-shim': 8.6.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/blocks': 8.6.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/csf-plugin': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/react-dom-shim': 8.6.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-essentials@8.6.3(@types/react@18.0.28)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-essentials@8.6.4(@types/react@18.0.28)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - '@storybook/addon-actions': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-backgrounds': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-controls': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-docs': 8.6.3(@types/react@18.0.28)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-highlight': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-measure': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-outline': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-toolbars': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-viewport': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + '@storybook/addon-actions': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/addon-backgrounds': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/addon-controls': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/addon-docs': 8.6.4(@types/react@18.0.28)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/addon-highlight': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/addon-measure': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/addon-outline': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/addon-toolbars': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/addon-viewport': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-highlight@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-highlight@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/addon-interactions@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-interactions@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - '@storybook/instrumenter': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/test': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/instrumenter': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/test': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) polished: 4.2.2 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - '@storybook/addon-links@8.6.3(react@19.0.0)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-links@8.6.4(react@19.0.0)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 optionalDependencies: react: 19.0.0 - '@storybook/addon-mdx-gfm@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-mdx-gfm@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: remark-gfm: 4.0.0 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 transitivePeerDependencies: - supports-color - '@storybook/addon-measure@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-measure@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) tiny-invariant: 1.3.3 - '@storybook/addon-outline@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-outline@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - '@storybook/addon-storysource@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-storysource@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - '@storybook/source-loader': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/source-loader': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) estraverse: 5.3.0 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) tiny-invariant: 1.3.3 - '@storybook/addon-toolbars@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-toolbars@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/addon-viewport@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-viewport@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: memoizerific: 1.11.3 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/blocks@8.6.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/blocks@8.6.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/icons': 1.2.12(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 optionalDependencies: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - '@storybook/builder-vite@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))': + '@storybook/builder-vite@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))': dependencies: - '@storybook/csf-plugin': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/csf-plugin': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) browser-assert: 1.2.1 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - vite: 6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) + vite: 6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) - '@storybook/components@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/components@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/core-events@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/core-events@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/core@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(utf-8-validate@6.0.5)': + '@storybook/core@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(utf-8-validate@6.0.5)': dependencies: - '@storybook/theming': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/theming': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) better-opn: 3.0.2 browser-assert: 1.2.1 esbuild: 0.25.0 @@ -13913,9 +13845,9 @@ snapshots: - supports-color - utf-8-validate - '@storybook/csf-plugin@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/csf-plugin@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) unplugin: 1.4.0 '@storybook/global@5.0.0': {} @@ -13925,121 +13857,121 @@ snapshots: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - '@storybook/instrumenter@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/instrumenter@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 '@vitest/utils': 2.1.1 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/manager-api@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/manager-api@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/preview-api@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/preview-api@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/react-dom-shim@8.6.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/react-dom-shim@8.6.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/react-vite@8.6.3(@storybook/test@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.34.9)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2)(vite@6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))': + '@storybook/react-vite@8.6.4(@storybook/test@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.34.9)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2)(vite@6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.8.2)(vite@6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.8.2)(vite@6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)) '@rollup/pluginutils': 5.1.4(rollup@4.34.9) - '@storybook/builder-vite': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)) - '@storybook/react': 8.6.3(@storybook/test@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2) + '@storybook/builder-vite': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)) + '@storybook/react': 8.6.4(@storybook/test@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2) find-up: 5.0.0 magic-string: 0.30.17 react: 19.0.0 react-docgen: 7.0.1 react-dom: 19.0.0(react@19.0.0) resolve: 1.22.8 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) tsconfig-paths: 4.2.0 - vite: 6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) + vite: 6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) optionalDependencies: - '@storybook/test': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/test': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) transitivePeerDependencies: - rollup - supports-color - typescript - '@storybook/react@8.6.3(@storybook/test@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2)': + '@storybook/react@8.6.4(@storybook/test@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2)': dependencies: - '@storybook/components': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/components': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/global': 5.0.0 - '@storybook/manager-api': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/preview-api': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/react-dom-shim': 8.6.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/theming': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/manager-api': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/preview-api': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/react-dom-shim': 8.6.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/theming': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) optionalDependencies: - '@storybook/test': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/test': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) typescript: 5.8.2 - '@storybook/source-loader@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/source-loader@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: es-toolkit: 1.27.0 estraverse: 5.3.0 prettier: 3.5.3 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/test@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/test@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - '@storybook/instrumenter': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/instrumenter': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@testing-library/dom': 10.4.0 '@testing-library/jest-dom': 6.5.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) '@vitest/expect': 2.0.5 '@vitest/spy': 2.0.5 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/theming@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/theming@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/types@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/types@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/vue3-vite@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2))': + '@storybook/vue3-vite@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2))': dependencies: - '@storybook/builder-vite': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)) - '@storybook/vue3': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.13(typescript@5.8.2)) + '@storybook/builder-vite': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)) + '@storybook/vue3': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.13(typescript@5.8.2)) find-package-json: 1.2.0 magic-string: 0.30.17 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) typescript: 5.8.2 - vite: 6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) + vite: 6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) vue-component-meta: 2.0.16(typescript@5.8.2) vue-docgen-api: 4.75.1(vue@3.5.13(typescript@5.8.2)) transitivePeerDependencies: - vue - '@storybook/vue3@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.13(typescript@5.8.2))': + '@storybook/vue3@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.13(typescript@5.8.2))': dependencies: - '@storybook/components': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/components': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/global': 5.0.0 - '@storybook/manager-api': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/preview-api': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/theming': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/manager-api': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/preview-api': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/theming': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@vue/compiler-core': 3.5.13 - storybook: 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 type-fest: 2.19.0 vue: 3.5.13(typescript@5.8.2) vue-component-type-helpers: 2.2.8 - '@stylistic/eslint-plugin@2.13.0(eslint@9.20.1)(typescript@5.7.3)': + '@stylistic/eslint-plugin@2.13.0(eslint@9.21.0)(typescript@5.8.2)': dependencies: - '@typescript-eslint/utils': 8.26.0(eslint@9.20.1)(typescript@5.7.3) - eslint: 9.20.1 + '@typescript-eslint/utils': 8.26.0(eslint@9.21.0)(typescript@5.8.2) + eslint: 9.21.0 eslint-visitor-keys: 4.2.0 espree: 10.3.0 estraverse: 5.3.0 @@ -14048,7 +13980,7 @@ snapshots: - supports-color - typescript - '@swc/cli@0.6.0(@swc/core@1.10.16)(chokidar@3.6.0)': + '@swc/cli@0.6.0(@swc/core@1.10.16)(chokidar@4.0.3)': dependencies: '@swc/core': 1.10.16 '@swc/counter': 0.1.3 @@ -14061,7 +13993,7 @@ snapshots: slash: 3.0.0 source-map: 0.7.4 optionalDependencies: - chokidar: 3.6.0 + chokidar: 4.0.3 '@swc/core-android-arm64@1.3.11': dependencies: @@ -14624,15 +14556,15 @@ snapshots: '@types/node': 22.13.9 optional: true - '@typescript-eslint/eslint-plugin@8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1)(typescript@5.7.3)': + '@typescript-eslint/eslint-plugin@8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.21.0)(typescript@5.7.3))(eslint@9.21.0)(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.24.0(eslint@9.20.1)(typescript@5.7.3) + '@typescript-eslint/parser': 8.24.0(eslint@9.21.0)(typescript@5.7.3) '@typescript-eslint/scope-manager': 8.24.0 - '@typescript-eslint/type-utils': 8.24.0(eslint@9.20.1)(typescript@5.7.3) - '@typescript-eslint/utils': 8.24.0(eslint@9.20.1)(typescript@5.7.3) + '@typescript-eslint/type-utils': 8.24.0(eslint@9.21.0)(typescript@5.7.3) + '@typescript-eslint/utils': 8.24.0(eslint@9.21.0)(typescript@5.7.3) '@typescript-eslint/visitor-keys': 8.24.0 - eslint: 9.20.1 + eslint: 9.21.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 @@ -14641,15 +14573,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.20.1)(typescript@5.8.2))(eslint@9.20.1)(typescript@5.8.2)': + '@typescript-eslint/eslint-plugin@8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0)(typescript@5.8.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.26.0(eslint@9.20.1)(typescript@5.8.2) + '@typescript-eslint/parser': 8.26.0(eslint@9.21.0)(typescript@5.8.2) '@typescript-eslint/scope-manager': 8.26.0 - '@typescript-eslint/type-utils': 8.26.0(eslint@9.20.1)(typescript@5.8.2) - '@typescript-eslint/utils': 8.26.0(eslint@9.20.1)(typescript@5.8.2) + '@typescript-eslint/type-utils': 8.26.0(eslint@9.21.0)(typescript@5.8.2) + '@typescript-eslint/utils': 8.26.0(eslint@9.21.0)(typescript@5.8.2) '@typescript-eslint/visitor-keys': 8.26.0 - eslint: 9.20.1 + eslint: 9.21.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 @@ -14658,26 +14590,26 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3)': + '@typescript-eslint/parser@8.24.0(eslint@9.21.0)(typescript@5.7.3)': dependencies: '@typescript-eslint/scope-manager': 8.24.0 '@typescript-eslint/types': 8.24.0 '@typescript-eslint/typescript-estree': 8.24.0(typescript@5.7.3) '@typescript-eslint/visitor-keys': 8.24.0 debug: 4.4.0(supports-color@8.1.1) - eslint: 9.20.1 + eslint: 9.21.0 typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.26.0(eslint@9.20.1)(typescript@5.8.2)': + '@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2)': dependencies: '@typescript-eslint/scope-manager': 8.26.0 '@typescript-eslint/types': 8.26.0 '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.8.2) '@typescript-eslint/visitor-keys': 8.26.0 debug: 4.4.0(supports-color@8.1.1) - eslint: 9.20.1 + eslint: 9.21.0 typescript: 5.8.2 transitivePeerDependencies: - supports-color @@ -14692,23 +14624,23 @@ snapshots: '@typescript-eslint/types': 8.26.0 '@typescript-eslint/visitor-keys': 8.26.0 - '@typescript-eslint/type-utils@8.24.0(eslint@9.20.1)(typescript@5.7.3)': + '@typescript-eslint/type-utils@8.24.0(eslint@9.21.0)(typescript@5.7.3)': dependencies: '@typescript-eslint/typescript-estree': 8.24.0(typescript@5.7.3) - '@typescript-eslint/utils': 8.24.0(eslint@9.20.1)(typescript@5.7.3) + '@typescript-eslint/utils': 8.24.0(eslint@9.21.0)(typescript@5.7.3) debug: 4.4.0(supports-color@8.1.1) - eslint: 9.20.1 + eslint: 9.21.0 ts-api-utils: 2.0.1(typescript@5.7.3) typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.26.0(eslint@9.20.1)(typescript@5.8.2)': + '@typescript-eslint/type-utils@8.26.0(eslint@9.21.0)(typescript@5.8.2)': dependencies: '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.8.2) - '@typescript-eslint/utils': 8.26.0(eslint@9.20.1)(typescript@5.8.2) + '@typescript-eslint/utils': 8.26.0(eslint@9.21.0)(typescript@5.8.2) debug: 4.4.0(supports-color@8.1.1) - eslint: 9.20.1 + eslint: 9.21.0 ts-api-utils: 2.0.1(typescript@5.8.2) typescript: 5.8.2 transitivePeerDependencies: @@ -14732,20 +14664,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.26.0(typescript@5.7.3)': - dependencies: - '@typescript-eslint/types': 8.26.0 - '@typescript-eslint/visitor-keys': 8.26.0 - debug: 4.4.0(supports-color@8.1.1) - fast-glob: 3.3.3 - is-glob: 4.0.3 - minimatch: 9.0.4 - semver: 7.6.3 - ts-api-utils: 2.0.1(typescript@5.7.3) - typescript: 5.7.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/typescript-estree@8.26.0(typescript@5.8.2)': dependencies: '@typescript-eslint/types': 8.26.0 @@ -14760,35 +14678,24 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.24.0(eslint@9.20.1)(typescript@5.7.3)': + '@typescript-eslint/utils@8.24.0(eslint@9.21.0)(typescript@5.7.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.20.1) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.21.0) '@typescript-eslint/scope-manager': 8.24.0 '@typescript-eslint/types': 8.24.0 '@typescript-eslint/typescript-estree': 8.24.0(typescript@5.7.3) - eslint: 9.20.1 + eslint: 9.21.0 typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.26.0(eslint@9.20.1)(typescript@5.7.3)': + '@typescript-eslint/utils@8.26.0(eslint@9.21.0)(typescript@5.8.2)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.20.1) - '@typescript-eslint/scope-manager': 8.26.0 - '@typescript-eslint/types': 8.26.0 - '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.7.3) - eslint: 9.20.1 - typescript: 5.7.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.26.0(eslint@9.20.1)(typescript@5.8.2)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.20.1) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.21.0) '@typescript-eslint/scope-manager': 8.26.0 '@typescript-eslint/types': 8.26.0 '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.8.2) - eslint: 9.20.1 + eslint: 9.21.0 typescript: 5.8.2 transitivePeerDependencies: - supports-color @@ -14805,12 +14712,12 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-vue@5.2.1(vite@6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2))': + '@vitejs/plugin-vue@5.2.1(vite@6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2))': dependencies: - vite: 6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) + vite: 6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) vue: 3.5.13(typescript@5.8.2) - '@vitest/coverage-v8@3.0.7(vitest@3.0.7(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.2.2)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))': + '@vitest/coverage-v8@3.0.8(vitest@3.0.8(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.3.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 @@ -14824,7 +14731,7 @@ snapshots: std-env: 3.8.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.0.7(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.2.2)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) + vitest: 3.0.8(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.3.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) transitivePeerDependencies: - supports-color @@ -14835,21 +14742,21 @@ snapshots: chai: 5.2.0 tinyrainbow: 1.2.0 - '@vitest/expect@3.0.7': + '@vitest/expect@3.0.8': dependencies: - '@vitest/spy': 3.0.7 - '@vitest/utils': 3.0.7 + '@vitest/spy': 3.0.8 + '@vitest/utils': 3.0.8 chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.0.7(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(vite@6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))': + '@vitest/mocker@3.0.8(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(vite@6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3))': dependencies: - '@vitest/spy': 3.0.7 + '@vitest/spy': 3.0.8 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: msw: 2.7.3(@types/node@22.13.9)(typescript@5.8.2) - vite: 6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) + vite: 6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) '@vitest/pretty-format@2.0.5': dependencies: @@ -14859,18 +14766,18 @@ snapshots: dependencies: tinyrainbow: 1.2.0 - '@vitest/pretty-format@3.0.7': + '@vitest/pretty-format@3.0.8': dependencies: tinyrainbow: 2.0.0 - '@vitest/runner@3.0.7': + '@vitest/runner@3.0.8': dependencies: - '@vitest/utils': 3.0.7 + '@vitest/utils': 3.0.8 pathe: 2.0.3 - '@vitest/snapshot@3.0.7': + '@vitest/snapshot@3.0.8': dependencies: - '@vitest/pretty-format': 3.0.7 + '@vitest/pretty-format': 3.0.8 magic-string: 0.30.17 pathe: 2.0.3 @@ -14878,7 +14785,7 @@ snapshots: dependencies: tinyspy: 3.0.2 - '@vitest/spy@3.0.7': + '@vitest/spy@3.0.8': dependencies: tinyspy: 3.0.2 @@ -14895,9 +14802,9 @@ snapshots: loupe: 3.1.3 tinyrainbow: 1.2.0 - '@vitest/utils@3.0.7': + '@vitest/utils@3.0.8': dependencies: - '@vitest/pretty-format': 3.0.7 + '@vitest/pretty-format': 3.0.8 loupe: 3.1.3 tinyrainbow: 2.0.0 @@ -14947,8 +14854,8 @@ snapshots: '@vue/compiler-ssr': 3.5.13 '@vue/shared': 3.5.13 estree-walker: 2.0.2 - magic-string: 0.30.11 - postcss: 8.5.2 + magic-string: 0.30.17 + postcss: 8.5.3 source-map-js: 1.2.1 '@vue/compiler-ssr@3.5.13': @@ -15103,17 +15010,17 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 - acorn-import-attributes@1.9.5(acorn@8.14.0): + acorn-import-attributes@1.9.5(acorn@8.14.1): dependencies: - acorn: 8.14.0 + acorn: 8.14.1 - acorn-jsx@5.3.2(acorn@8.14.0): + acorn-jsx@5.3.2(acorn@8.14.1): dependencies: - acorn: 8.14.0 + acorn: 8.14.1 acorn@7.4.1: {} - acorn@8.14.0: {} + acorn@8.14.1: {} adm-zip@0.5.10: optional: true @@ -15150,7 +15057,6 @@ snapshots: aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/c3cde89e79a41d93540cf8a48cd619c3f2dcb1b7: dependencies: - '@aiscript-dev/aiscript-languageserver': https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz vscode-languageclient: 9.0.1 ajv-draft-04@1.0.0(ajv@8.13.0): @@ -15529,8 +15435,6 @@ snapshots: execa: 5.1.1 find-versions: 5.1.0 - binary-extensions@2.2.0: {} - bl@4.1.0: dependencies: buffer: 5.7.1 @@ -15589,13 +15493,6 @@ snapshots: browser-assert@1.2.1: {} - browserslist@4.23.0: - dependencies: - caniuse-lite: 1.0.30001591 - electron-to-chromium: 1.4.686 - node-releases: 2.0.14 - update-browserslist-db: 1.0.13(browserslist@4.23.0) - browserslist@4.24.4: dependencies: caniuse-lite: 1.0.30001695 @@ -15701,11 +15598,6 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 - call-bind@1.0.2: - dependencies: - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - call-bind@1.0.7: dependencies: es-define-property: 1.0.0 @@ -15849,23 +15741,17 @@ snapshots: undici: 6.19.8 whatwg-mimetype: 4.0.0 - chokidar@3.6.0: + chokidar@4.0.3: dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 + readdirp: 4.1.2 chownr@1.1.4: optional: true chownr@2.0.0: {} + chownr@3.0.0: {} + chromatic@11.27.0: {} ci-info@3.7.1: {} @@ -16120,9 +16006,9 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - css-declaration-sorter@7.2.0(postcss@8.5.2): + css-declaration-sorter@7.2.0(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 css-select@5.1.0: dependencies: @@ -16148,49 +16034,49 @@ snapshots: cssesc@3.0.0: {} - cssnano-preset-default@7.0.6(postcss@8.5.2): + cssnano-preset-default@7.0.6(postcss@8.5.3): dependencies: browserslist: 4.24.4 - css-declaration-sorter: 7.2.0(postcss@8.5.2) - cssnano-utils: 5.0.0(postcss@8.5.2) - postcss: 8.5.2 - postcss-calc: 10.1.0(postcss@8.5.2) - postcss-colormin: 7.0.2(postcss@8.5.2) - postcss-convert-values: 7.0.4(postcss@8.5.2) - postcss-discard-comments: 7.0.3(postcss@8.5.2) - postcss-discard-duplicates: 7.0.1(postcss@8.5.2) - postcss-discard-empty: 7.0.0(postcss@8.5.2) - postcss-discard-overridden: 7.0.0(postcss@8.5.2) - postcss-merge-longhand: 7.0.4(postcss@8.5.2) - postcss-merge-rules: 7.0.4(postcss@8.5.2) - postcss-minify-font-values: 7.0.0(postcss@8.5.2) - postcss-minify-gradients: 7.0.0(postcss@8.5.2) - postcss-minify-params: 7.0.2(postcss@8.5.2) - postcss-minify-selectors: 7.0.4(postcss@8.5.2) - postcss-normalize-charset: 7.0.0(postcss@8.5.2) - postcss-normalize-display-values: 7.0.0(postcss@8.5.2) - postcss-normalize-positions: 7.0.0(postcss@8.5.2) - postcss-normalize-repeat-style: 7.0.0(postcss@8.5.2) - postcss-normalize-string: 7.0.0(postcss@8.5.2) - postcss-normalize-timing-functions: 7.0.0(postcss@8.5.2) - postcss-normalize-unicode: 7.0.2(postcss@8.5.2) - postcss-normalize-url: 7.0.0(postcss@8.5.2) - postcss-normalize-whitespace: 7.0.0(postcss@8.5.2) - postcss-ordered-values: 7.0.1(postcss@8.5.2) - postcss-reduce-initial: 7.0.2(postcss@8.5.2) - postcss-reduce-transforms: 7.0.0(postcss@8.5.2) - postcss-svgo: 7.0.1(postcss@8.5.2) - postcss-unique-selectors: 7.0.3(postcss@8.5.2) + css-declaration-sorter: 7.2.0(postcss@8.5.3) + cssnano-utils: 5.0.0(postcss@8.5.3) + postcss: 8.5.3 + postcss-calc: 10.1.0(postcss@8.5.3) + postcss-colormin: 7.0.2(postcss@8.5.3) + postcss-convert-values: 7.0.4(postcss@8.5.3) + postcss-discard-comments: 7.0.3(postcss@8.5.3) + postcss-discard-duplicates: 7.0.1(postcss@8.5.3) + postcss-discard-empty: 7.0.0(postcss@8.5.3) + postcss-discard-overridden: 7.0.0(postcss@8.5.3) + postcss-merge-longhand: 7.0.4(postcss@8.5.3) + postcss-merge-rules: 7.0.4(postcss@8.5.3) + postcss-minify-font-values: 7.0.0(postcss@8.5.3) + postcss-minify-gradients: 7.0.0(postcss@8.5.3) + postcss-minify-params: 7.0.2(postcss@8.5.3) + postcss-minify-selectors: 7.0.4(postcss@8.5.3) + postcss-normalize-charset: 7.0.0(postcss@8.5.3) + postcss-normalize-display-values: 7.0.0(postcss@8.5.3) + postcss-normalize-positions: 7.0.0(postcss@8.5.3) + postcss-normalize-repeat-style: 7.0.0(postcss@8.5.3) + postcss-normalize-string: 7.0.0(postcss@8.5.3) + postcss-normalize-timing-functions: 7.0.0(postcss@8.5.3) + postcss-normalize-unicode: 7.0.2(postcss@8.5.3) + postcss-normalize-url: 7.0.0(postcss@8.5.3) + postcss-normalize-whitespace: 7.0.0(postcss@8.5.3) + postcss-ordered-values: 7.0.1(postcss@8.5.3) + postcss-reduce-initial: 7.0.2(postcss@8.5.3) + postcss-reduce-transforms: 7.0.0(postcss@8.5.3) + postcss-svgo: 7.0.1(postcss@8.5.3) + postcss-unique-selectors: 7.0.3(postcss@8.5.3) - cssnano-utils@5.0.0(postcss@8.5.2): + cssnano-utils@5.0.0(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 - cssnano@7.0.6(postcss@8.5.2): + cssnano@7.0.6(postcss@8.5.3): dependencies: - cssnano-preset-default: 7.0.6(postcss@8.5.2) + cssnano-preset-default: 7.0.6(postcss@8.5.3) lilconfig: 3.1.3 - postcss: 8.5.2 + postcss: 8.5.3 csso@5.0.5: dependencies: @@ -16203,52 +16089,6 @@ snapshots: csstype@3.1.3: {} - cypress@14.0.3: - dependencies: - '@cypress/request': 3.0.7 - '@cypress/xvfb': 1.2.4(supports-color@8.1.1) - '@types/sinonjs__fake-timers': 8.1.1 - '@types/sizzle': 2.3.3 - arch: 2.2.0 - blob-util: 2.0.2 - bluebird: 3.7.2 - buffer: 5.7.1 - cachedir: 2.3.0 - chalk: 4.1.2 - check-more-types: 2.24.0 - ci-info: 4.1.0 - cli-cursor: 3.1.0 - cli-table3: 0.6.3 - commander: 6.2.1 - common-tags: 1.8.2 - dayjs: 1.11.10 - debug: 4.4.0(supports-color@8.1.1) - enquirer: 2.3.6 - eventemitter2: 6.4.7 - execa: 4.1.0 - executable: 4.1.1 - extract-zip: 2.0.1(supports-color@8.1.1) - figures: 3.2.0 - fs-extra: 9.1.0 - getos: 3.2.1 - is-installed-globally: 0.4.0 - lazy-ass: 1.6.0 - listr2: 3.14.0(enquirer@2.3.6) - lodash: 4.17.21 - log-symbols: 4.1.0 - minimist: 1.2.8 - ospath: 1.2.2 - pretty-bytes: 5.6.0 - process: 0.11.10 - proxy-from-env: 1.0.0 - request-progress: 3.0.0 - semver: 7.6.3 - supports-color: 8.1.1 - tmp: 0.2.3 - tree-kill: 1.2.2 - untildify: 4.0.0 - yauzl: 2.10.0 - cypress@14.1.0: dependencies: '@cypress/request': 3.0.7 @@ -16407,7 +16247,7 @@ snapshots: deep-equal@2.2.0: dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 es-get-iterator: 1.1.3 get-intrinsic: 1.2.4 is-arguments: 1.1.1 @@ -16423,7 +16263,7 @@ snapshots: side-channel: 1.0.6 which-boxed-primitive: 1.0.2 which-collection: 1.0.1 - which-typed-array: 1.1.11 + which-typed-array: 1.1.15 deep-extend@0.6.0: optional: true @@ -16440,7 +16280,7 @@ snapshots: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 - gopd: 1.0.1 + gopd: 1.2.0 define-lazy-prop@2.0.0: {} @@ -16587,8 +16427,6 @@ snapshots: dependencies: jake: 10.8.5 - electron-to-chromium@1.4.686: {} - electron-to-chromium@1.5.83: {} emittery@0.13.1: {} @@ -16859,27 +16697,27 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint@9.20.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.21.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint@9.21.0): dependencies: debug: 3.2.7(supports-color@8.1.1) optionalDependencies: - '@typescript-eslint/parser': 8.24.0(eslint@9.20.1)(typescript@5.7.3) - eslint: 9.20.1 + '@typescript-eslint/parser': 8.24.0(eslint@9.21.0)(typescript@5.7.3) + eslint: 9.21.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.26.0(eslint@9.20.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@9.20.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@9.21.0): dependencies: debug: 3.2.7(supports-color@8.1.1) optionalDependencies: - '@typescript-eslint/parser': 8.26.0(eslint@9.20.1)(typescript@5.8.2) - eslint: 9.20.1 + '@typescript-eslint/parser': 8.26.0(eslint@9.21.0)(typescript@5.8.2) + eslint: 9.21.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.21.0)(typescript@5.7.3))(eslint@9.21.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -16888,9 +16726,9 @@ snapshots: array.prototype.flatmap: 1.3.2 debug: 3.2.7(supports-color@8.1.1) doctrine: 2.1.0 - eslint: 9.20.1 + eslint: 9.21.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.20.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint@9.20.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.21.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint@9.21.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -16902,13 +16740,13 @@ snapshots: string.prototype.trimend: 1.0.8 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.24.0(eslint@9.20.1)(typescript@5.7.3) + '@typescript-eslint/parser': 8.24.0(eslint@9.21.0)(typescript@5.7.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.20.1)(typescript@5.8.2))(eslint@9.20.1): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -16917,9 +16755,9 @@ snapshots: array.prototype.flatmap: 1.3.2 debug: 3.2.7(supports-color@8.1.1) doctrine: 2.1.0 - eslint: 9.20.1 + eslint: 9.21.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.26.0(eslint@9.20.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@9.20.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@9.21.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -16931,33 +16769,25 @@ snapshots: string.prototype.trimend: 1.0.8 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.26.0(eslint@9.20.1)(typescript@5.8.2) + '@typescript-eslint/parser': 8.26.0(eslint@9.21.0)(typescript@5.8.2) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-vue@9.33.0(eslint@9.20.1): + eslint-plugin-vue@10.0.0(eslint@9.21.0)(vue-eslint-parser@10.1.1(eslint@9.21.0)): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.20.1) - eslint: 9.20.1 - globals: 13.24.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.21.0) + eslint: 9.21.0 natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 6.1.2 semver: 7.6.3 - vue-eslint-parser: 9.4.3(eslint@9.20.1) + vue-eslint-parser: 10.1.1(eslint@9.21.0) xml-name-validator: 4.0.0 - transitivePeerDependencies: - - supports-color eslint-rule-docs@1.1.235: {} - eslint-scope@7.2.2: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - eslint-scope@8.2.0: dependencies: esrecurse: 4.3.0 @@ -16967,18 +16797,18 @@ snapshots: eslint-visitor-keys@4.2.0: {} - eslint@9.20.1: + eslint@9.21.0: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.20.1) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.21.0) '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.19.1 - '@eslint/core': 0.11.0 - '@eslint/eslintrc': 3.2.0 - '@eslint/js': 9.20.0 - '@eslint/plugin-kit': 0.2.5 + '@eslint/config-array': 0.19.2 + '@eslint/core': 0.12.0 + '@eslint/eslintrc': 3.3.0 + '@eslint/js': 9.21.0 + '@eslint/plugin-kit': 0.2.7 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.1 + '@humanwhocodes/retry': 0.4.2 '@types/estree': 1.0.6 '@types/json-schema': 7.0.15 ajv: 6.12.6 @@ -17008,16 +16838,10 @@ snapshots: espree@10.3.0: dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) + acorn: 8.14.1 + acorn-jsx: 5.3.2(acorn@8.14.1) eslint-visitor-keys: 4.2.0 - espree@9.6.1: - dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) - eslint-visitor-keys: 3.4.3 - esprima@4.0.1: {} esquery@1.6.0: @@ -17074,7 +16898,7 @@ snapshots: execa@5.1.1: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 get-stream: 6.0.1 human-signals: 2.1.0 is-stream: 2.0.1 @@ -17438,7 +17262,7 @@ snapshots: foreground-child@3.1.1: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 signal-exit: 4.1.0 forever-agent@0.6.1: {} @@ -17616,7 +17440,7 @@ snapshots: foreground-child: 3.1.1 jackspeak: 2.3.6 minimatch: 9.0.4 - minipass: 7.0.4 + minipass: 7.1.2 path-scurry: 1.10.1 glob@10.4.5: @@ -17660,13 +17484,9 @@ snapshots: globals@11.12.0: {} - globals@13.24.0: - dependencies: - type-fest: 0.20.2 - globals@14.0.0: {} - globals@15.15.0: {} + globals@16.0.0: {} globalthis@1.0.3: dependencies: @@ -17731,7 +17551,7 @@ snapshots: webidl-conversions: 7.0.0 whatwg-mimetype: 3.0.0 - happy-dom@17.2.2: + happy-dom@17.3.0: dependencies: webidl-conversions: 7.0.0 whatwg-mimetype: 3.0.0 @@ -17938,7 +17758,7 @@ snapshots: ignore-by-default@1.0.1: {} - ignore-walk@6.0.5: + ignore-walk@7.0.0: dependencies: minimatch: 9.0.4 @@ -17953,8 +17773,8 @@ snapshots: import-in-the-middle@1.11.2: dependencies: - acorn: 8.14.0 - acorn-import-attributes: 1.9.5(acorn@8.14.0) + acorn: 8.14.1 + acorn-import-attributes: 1.9.5(acorn@8.14.1) cjs-module-lexer: 1.2.2 module-details-from-path: 1.0.3 @@ -18044,9 +17864,9 @@ snapshots: is-array-buffer@3.0.2: dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 get-intrinsic: 1.2.4 - is-typed-array: 1.1.10 + is-typed-array: 1.1.13 is-array-buffer@3.0.4: dependencies: @@ -18061,10 +17881,6 @@ snapshots: dependencies: has-bigints: 1.0.2 - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.2.0 - is-boolean-object@1.1.2: dependencies: call-bind: 1.0.7 @@ -18142,14 +17958,14 @@ snapshots: is-regex@1.1.4: dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 has-tostringtag: 1.0.2 is-set@2.0.2: {} is-shared-array-buffer@1.0.2: dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 is-shared-array-buffer@1.0.3: dependencies: @@ -18175,10 +17991,10 @@ snapshots: is-typed-array@1.1.10: dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 + gopd: 1.2.0 has-tostringtag: 1.0.2 is-typed-array@1.1.13: @@ -18968,10 +18784,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 - magic-string@0.30.11: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -19467,8 +19279,6 @@ snapshots: minipass@5.0.0: {} - minipass@7.0.4: {} - minipass@7.1.2: {} minizlib@2.1.2: @@ -19476,6 +19286,11 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 + minizlib@3.0.1: + dependencies: + minipass: 7.1.2 + rimraf: 5.0.10 + mkdirp-classic@0.5.3: optional: true @@ -19487,6 +19302,8 @@ snapshots: mkdirp@2.1.6: {} + mkdirp@3.0.1: {} + mnemonist@0.39.8: dependencies: obliterator: 2.0.4 @@ -19667,15 +19484,13 @@ snapshots: node-int64@0.4.0: {} - node-releases@2.0.14: {} - node-releases@2.0.19: {} nodemailer@6.10.0: {} nodemon@3.1.9: dependencies: - chokidar: 3.6.0 + chokidar: 4.0.3 debug: 4.3.7(supports-color@5.5.0) ignore-by-default: 1.0.1 minimatch: 3.1.2 @@ -19780,7 +19595,7 @@ snapshots: object.assign@4.1.4: dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 define-properties: 1.2.0 has-symbols: 1.0.3 object-keys: 1.1.1 @@ -20111,148 +19926,150 @@ snapshots: pngjs@5.0.0: {} + pnpm@10.6.1: {} + polished@4.2.2: dependencies: '@babel/runtime': 7.23.4 possible-typed-array-names@1.0.0: {} - postcss-calc@10.1.0(postcss@8.5.2): + postcss-calc@10.1.0(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 postcss-selector-parser: 7.0.0 postcss-value-parser: 4.2.0 - postcss-colormin@7.0.2(postcss@8.5.2): + postcss-colormin@7.0.2(postcss@8.5.3): dependencies: browserslist: 4.24.4 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.5.2 + postcss: 8.5.3 postcss-value-parser: 4.2.0 - postcss-convert-values@7.0.4(postcss@8.5.2): + postcss-convert-values@7.0.4(postcss@8.5.3): dependencies: browserslist: 4.24.4 - postcss: 8.5.2 + postcss: 8.5.3 postcss-value-parser: 4.2.0 - postcss-discard-comments@7.0.3(postcss@8.5.2): + postcss-discard-comments@7.0.3(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 postcss-selector-parser: 6.1.2 - postcss-discard-duplicates@7.0.1(postcss@8.5.2): + postcss-discard-duplicates@7.0.1(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 - postcss-discard-empty@7.0.0(postcss@8.5.2): + postcss-discard-empty@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 - postcss-discard-overridden@7.0.0(postcss@8.5.2): + postcss-discard-overridden@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 - postcss-merge-longhand@7.0.4(postcss@8.5.2): + postcss-merge-longhand@7.0.4(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 postcss-value-parser: 4.2.0 - stylehacks: 7.0.4(postcss@8.5.2) + stylehacks: 7.0.4(postcss@8.5.3) - postcss-merge-rules@7.0.4(postcss@8.5.2): + postcss-merge-rules@7.0.4(postcss@8.5.3): dependencies: browserslist: 4.24.4 caniuse-api: 3.0.0 - cssnano-utils: 5.0.0(postcss@8.5.2) - postcss: 8.5.2 + cssnano-utils: 5.0.0(postcss@8.5.3) + postcss: 8.5.3 postcss-selector-parser: 6.1.2 - postcss-minify-font-values@7.0.0(postcss@8.5.2): + postcss-minify-font-values@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 postcss-value-parser: 4.2.0 - postcss-minify-gradients@7.0.0(postcss@8.5.2): + postcss-minify-gradients@7.0.0(postcss@8.5.3): dependencies: colord: 2.9.3 - cssnano-utils: 5.0.0(postcss@8.5.2) - postcss: 8.5.2 + cssnano-utils: 5.0.0(postcss@8.5.3) + postcss: 8.5.3 postcss-value-parser: 4.2.0 - postcss-minify-params@7.0.2(postcss@8.5.2): + postcss-minify-params@7.0.2(postcss@8.5.3): dependencies: browserslist: 4.24.4 - cssnano-utils: 5.0.0(postcss@8.5.2) - postcss: 8.5.2 + cssnano-utils: 5.0.0(postcss@8.5.3) + postcss: 8.5.3 postcss-value-parser: 4.2.0 - postcss-minify-selectors@7.0.4(postcss@8.5.2): + postcss-minify-selectors@7.0.4(postcss@8.5.3): dependencies: cssesc: 3.0.0 - postcss: 8.5.2 + postcss: 8.5.3 postcss-selector-parser: 6.1.2 - postcss-normalize-charset@7.0.0(postcss@8.5.2): + postcss-normalize-charset@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 - postcss-normalize-display-values@7.0.0(postcss@8.5.2): + postcss-normalize-display-values@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 postcss-value-parser: 4.2.0 - postcss-normalize-positions@7.0.0(postcss@8.5.2): + postcss-normalize-positions@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 postcss-value-parser: 4.2.0 - postcss-normalize-repeat-style@7.0.0(postcss@8.5.2): + postcss-normalize-repeat-style@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 postcss-value-parser: 4.2.0 - postcss-normalize-string@7.0.0(postcss@8.5.2): + postcss-normalize-string@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 postcss-value-parser: 4.2.0 - postcss-normalize-timing-functions@7.0.0(postcss@8.5.2): + postcss-normalize-timing-functions@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 postcss-value-parser: 4.2.0 - postcss-normalize-unicode@7.0.2(postcss@8.5.2): + postcss-normalize-unicode@7.0.2(postcss@8.5.3): dependencies: browserslist: 4.24.4 - postcss: 8.5.2 + postcss: 8.5.3 postcss-value-parser: 4.2.0 - postcss-normalize-url@7.0.0(postcss@8.5.2): + postcss-normalize-url@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 postcss-value-parser: 4.2.0 - postcss-normalize-whitespace@7.0.0(postcss@8.5.2): + postcss-normalize-whitespace@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 postcss-value-parser: 4.2.0 - postcss-ordered-values@7.0.1(postcss@8.5.2): + postcss-ordered-values@7.0.1(postcss@8.5.3): dependencies: - cssnano-utils: 5.0.0(postcss@8.5.2) - postcss: 8.5.2 + cssnano-utils: 5.0.0(postcss@8.5.3) + postcss: 8.5.3 postcss-value-parser: 4.2.0 - postcss-reduce-initial@7.0.2(postcss@8.5.2): + postcss-reduce-initial@7.0.2(postcss@8.5.3): dependencies: browserslist: 4.24.4 caniuse-api: 3.0.0 - postcss: 8.5.2 + postcss: 8.5.3 - postcss-reduce-transforms@7.0.0(postcss@8.5.2): + postcss-reduce-transforms@7.0.0(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 postcss-value-parser: 4.2.0 postcss-selector-parser@6.1.2: @@ -20265,25 +20082,19 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-svgo@7.0.1(postcss@8.5.2): + postcss-svgo@7.0.1(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 postcss-value-parser: 4.2.0 svgo: 3.3.2 - postcss-unique-selectors@7.0.3(postcss@8.5.2): + postcss-unique-selectors@7.0.3(postcss@8.5.3): dependencies: - postcss: 8.5.2 + postcss: 8.5.3 postcss-selector-parser: 6.1.2 postcss-value-parser@4.2.0: {} - postcss@8.5.2: - dependencies: - nanoid: 3.3.8 - picocolors: 1.1.1 - source-map-js: 1.2.1 - postcss@8.5.3: dependencies: nanoid: 3.3.8 @@ -20573,7 +20384,7 @@ snapshots: dependencies: setimmediate: 1.0.5 - re2@1.21.4: + re2@1.21.4(patch_hash=018babd22b7ce951bcd10d6246f1e541a7ac7ba212f7fa8985e774ece67d08e1): dependencies: install-artifact-from-github: 1.3.5 nan: 2.20.0 @@ -20651,9 +20462,7 @@ snapshots: dependencies: minimatch: 5.1.2 - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 + readdirp@4.1.2: {} real-require@0.2.0: {} @@ -20702,7 +20511,7 @@ snapshots: regexp.prototype.flags@1.5.0: dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 define-properties: 1.2.0 functions-have-names: 1.2.3 @@ -20812,6 +20621,10 @@ snapshots: glob: 7.2.3 optional: true + rimraf@5.0.10: + dependencies: + glob: 10.4.5 + rollup@4.34.9: dependencies: '@types/estree': 1.0.6 @@ -20897,11 +20710,11 @@ snapshots: htmlparser2: 8.0.1 is-plain-object: 5.0.0 parse-srcset: 1.0.2 - postcss: 8.5.2 + postcss: 8.5.3 sass@1.85.1: dependencies: - chokidar: 3.6.0 + chokidar: 4.0.3 immutable: 5.0.3 source-map-js: 1.2.1 optionalDependencies: @@ -20939,10 +20752,6 @@ snapshots: dependencies: lru-cache: 6.0.0 - semver@7.6.0: - dependencies: - lru-cache: 6.0.0 - semver@7.6.3: {} send@0.19.0: @@ -20982,7 +20791,7 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 get-intrinsic: 1.2.4 - gopd: 1.0.1 + gopd: 1.2.0 has-property-descriptors: 1.0.2 set-function-name@2.0.2: @@ -21296,22 +21105,22 @@ snapshots: dependencies: internal-slot: 1.0.5 - storybook-addon-misskey-theme@https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.6.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/components@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/core-events@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/manager-api@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/preview-api@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/theming@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/types@8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + storybook-addon-misskey-theme@https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.6.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/components@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/core-events@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/manager-api@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/preview-api@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/theming@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/types@8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: - '@storybook/blocks': 8.6.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/components': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/core-events': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/manager-api': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/preview-api': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/theming': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/types': 8.6.3(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/blocks': 8.6.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/components': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/core-events': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/manager-api': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/preview-api': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/theming': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/types': 8.6.4(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) optionalDependencies: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5): + storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5): dependencies: - '@storybook/core': 8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(storybook@8.6.3(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(utf-8-validate@6.0.5) + '@storybook/core': 8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(storybook@8.6.4(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(utf-8-validate@6.0.5) optionalDependencies: prettier: 3.5.3 transitivePeerDependencies: @@ -21461,10 +21270,10 @@ snapshots: '@tokenizer/token': 0.3.0 peek-readable: 5.3.1 - stylehacks@7.0.4(postcss@8.5.2): + stylehacks@7.0.4(postcss@8.5.3): dependencies: browserslist: 4.24.4 - postcss: 8.5.2 + postcss: 8.5.3 postcss-selector-parser: 6.1.2 supports-color@5.5.0: @@ -21540,6 +21349,15 @@ snapshots: mkdirp: 1.0.4 yallist: 4.0.0 + tar@7.4.3: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.1 + mkdirp: 3.0.1 + yallist: 5.0.0 + taskkill@5.0.0: dependencies: execa: 6.1.0 @@ -21547,7 +21365,7 @@ snapshots: terser@5.39.0: dependencies: '@jridgewell/source-map': 0.3.6 - acorn: 8.14.0 + acorn: 8.14.1 commander: 2.20.3 source-map-support: 0.5.21 @@ -21675,7 +21493,7 @@ snapshots: tsc-alias@1.8.10: dependencies: - chokidar: 3.6.0 + chokidar: 4.0.3 commander: 9.5.0 globby: 11.1.0 mylas: 2.1.13 @@ -21684,7 +21502,7 @@ snapshots: tsc-alias@1.8.11: dependencies: - chokidar: 3.6.0 + chokidar: 4.0.3 commander: 9.5.0 globby: 11.1.0 mylas: 2.1.13 @@ -21739,8 +21557,6 @@ snapshots: type-fest@0.18.1: {} - type-fest@0.20.2: {} - type-fest@0.21.3: {} type-fest@0.6.0: {} @@ -21760,7 +21576,7 @@ snapshots: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 - is-typed-array: 1.1.10 + is-typed-array: 1.1.13 typed-array-buffer@1.0.2: dependencies: @@ -21773,30 +21589,30 @@ snapshots: call-bind: 1.0.7 for-each: 0.3.3 has-proto: 1.0.1 - is-typed-array: 1.1.10 + is-typed-array: 1.1.13 typed-array-byte-length@1.0.1: dependencies: call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 + gopd: 1.2.0 has-proto: 1.0.3 is-typed-array: 1.1.13 typed-array-byte-offset@1.0.0: dependencies: - available-typed-arrays: 1.0.5 + available-typed-arrays: 1.0.7 call-bind: 1.0.7 for-each: 0.3.3 has-proto: 1.0.1 - is-typed-array: 1.1.10 + is-typed-array: 1.1.13 typed-array-byte-offset@1.0.2: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 + gopd: 1.2.0 has-proto: 1.0.3 is-typed-array: 1.1.13 @@ -21804,13 +21620,13 @@ snapshots: dependencies: call-bind: 1.0.7 for-each: 0.3.3 - is-typed-array: 1.1.10 + is-typed-array: 1.1.13 typed-array-length@1.0.6: dependencies: call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 + gopd: 1.2.0 has-proto: 1.0.3 is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 @@ -21931,19 +21747,13 @@ snapshots: unplugin@1.4.0: dependencies: - acorn: 8.14.0 - chokidar: 3.6.0 + acorn: 8.14.1 + chokidar: 4.0.3 webpack-sources: 3.2.3 webpack-virtual-modules: 0.5.0 untildify@4.0.0: {} - update-browserslist-db@1.0.13(browserslist@4.23.0): - dependencies: - browserslist: 4.23.0 - escalade: 3.1.1 - picocolors: 1.1.1 - update-browserslist-db@1.1.2(browserslist@4.24.4): dependencies: browserslist: 4.24.4 @@ -22022,13 +21832,13 @@ snapshots: unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 - vite-node@3.0.7(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3): + vite-node@3.0.8(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3): dependencies: cac: 6.7.14 debug: 4.4.0(supports-color@8.1.1) es-module-lexer: 1.6.0 pathe: 2.0.3 - vite: 6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) + vite: 6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) transitivePeerDependencies: - '@types/node' - jiti @@ -22045,7 +21855,7 @@ snapshots: vite-plugin-turbosnap@1.0.3: {} - vite@6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3): + vite@6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3): dependencies: esbuild: 0.25.0 postcss: 8.5.3 @@ -22057,19 +21867,19 @@ snapshots: terser: 5.39.0 tsx: 4.19.3 - vitest-fetch-mock@0.4.5(vitest@3.0.7(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.2.2)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)): + vitest-fetch-mock@0.4.5(vitest@3.0.8(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.3.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)): dependencies: - vitest: 3.0.7(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.2.2)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) + vitest: 3.0.8(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.3.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) - vitest@3.0.7(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.2.2)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3): + vitest@3.0.8(@types/debug@4.1.12)(@types/node@22.13.9)(happy-dom@17.3.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3): dependencies: - '@vitest/expect': 3.0.7 - '@vitest/mocker': 3.0.7(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(vite@6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)) - '@vitest/pretty-format': 3.0.7 - '@vitest/runner': 3.0.7 - '@vitest/snapshot': 3.0.7 - '@vitest/spy': 3.0.7 - '@vitest/utils': 3.0.7 + '@vitest/expect': 3.0.8 + '@vitest/mocker': 3.0.8(msw@2.7.3(@types/node@22.13.9)(typescript@5.8.2))(vite@6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3)) + '@vitest/pretty-format': 3.0.8 + '@vitest/runner': 3.0.8 + '@vitest/snapshot': 3.0.8 + '@vitest/spy': 3.0.8 + '@vitest/utils': 3.0.8 chai: 5.2.0 debug: 4.4.0(supports-color@8.1.1) expect-type: 1.1.0 @@ -22080,13 +21890,13 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 6.2.0(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) - vite-node: 3.0.7(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) + vite: 6.2.1(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) + vite-node: 3.0.8(@types/node@22.13.9)(sass@1.85.1)(terser@5.39.0)(tsx@4.19.3) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 '@types/node': 22.13.9 - happy-dom: 17.2.2 + happy-dom: 17.3.0 jsdom: 26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5) transitivePeerDependencies: - jiti @@ -22117,14 +21927,8 @@ snapshots: vscode-jsonrpc: 8.2.0 vscode-languageserver-types: 3.17.5 - vscode-languageserver-textdocument@1.0.11: {} - vscode-languageserver-types@3.17.5: {} - vscode-languageserver@9.0.1: - dependencies: - vscode-languageserver-protocol: 3.17.5 - vscode-uri@3.0.8: {} vue-component-meta@2.0.16(typescript@5.8.2): @@ -22161,16 +21965,16 @@ snapshots: vue: 3.5.13(typescript@5.8.2) vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.5.13(typescript@5.8.2)) - vue-eslint-parser@9.4.3(eslint@9.20.1): + vue-eslint-parser@10.1.1(eslint@9.21.0): dependencies: - debug: 4.3.5 - eslint: 9.20.1 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 + debug: 4.4.0(supports-color@8.1.1) + eslint: 9.21.0 + eslint-scope: 8.2.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 esquery: 1.6.0 lodash: 4.17.21 - semver: 7.6.0 + semver: 7.6.3 transitivePeerDependencies: - supports-color @@ -22290,10 +22094,10 @@ snapshots: which-typed-array@1.1.11: dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 + gopd: 1.2.0 has-tostringtag: 1.0.2 which-typed-array@1.1.15: @@ -22301,7 +22105,7 @@ snapshots: available-typed-arrays: 1.0.7 call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 + gopd: 1.2.0 has-tostringtag: 1.0.2 which@1.3.1: @@ -22394,6 +22198,8 @@ snapshots: yallist@4.0.0: {} + yallist@5.0.0: {} + yargs-parser@18.1.3: dependencies: camelcase: 5.3.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d222614eda..68e4f0adc1 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,10 +1,29 @@ packages: - - 'packages/backend' - - 'packages/frontend-shared' - - 'packages/frontend' - - 'packages/frontend-embed' - - 'packages/sw' - - 'packages/misskey-js' - - 'packages/misskey-js/generator' - - 'packages/misskey-reversi' - - 'packages/misskey-bubble-game' + - packages/backend + - packages/frontend-shared + - packages/frontend + - packages/frontend-embed + - packages/sw + - packages/misskey-js + - packages/misskey-js/generator + - packages/misskey-reversi + - packages/misskey-bubble-game +onlyBuiltDependencies: + - '@nestjs/core' + - '@parcel/watcher' + - '@sentry/profiling-node' + - '@swc/core' + - '@tensorflow/tfjs-node' + - bufferutil + - canvas + - core-js + - cypress + - esbuild + - msgpackr-extract + - msw + - nice-napi + - re2 + - sharp + - utf-8-validate + - v-code-diff + - vue-demi diff --git a/scripts/changelog-checker/package-lock.json b/scripts/changelog-checker/package-lock.json index d017475fc8..2a1524f768 100644 --- a/scripts/changelog-checker/package-lock.json +++ b/scripts/changelog-checker/package-lock.json @@ -9,16 +9,16 @@ "version": "1.0.0", "devDependencies": { "@types/mdast": "4.0.4", - "@types/node": "22.13.7", - "@vitest/coverage-v8": "3.0.7", + "@types/node": "22.13.9", + "@vitest/coverage-v8": "3.0.8", "mdast-util-to-string": "4.0.0", "remark": "15.0.1", "remark-parse": "11.0.0", "typescript": "5.8.2", "unified": "11.0.5", - "vite": "6.2.0", - "vite-node": "3.0.7", - "vitest": "3.0.7" + "vite": "6.2.1", + "vite-node": "3.0.8", + "vitest": "3.0.8" } }, "node_modules/@ampproject/remapping": { @@ -909,9 +909,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "22.13.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.7.tgz", - "integrity": "sha512-oU2q+BsQldB9lYxHNp/5aZO+/Bs0Usa74Abo9mAKulz4ahQyXRHK6UVKYIN8KSC8HXwhWSi7b49JnX+txuac0w==", + "version": "22.13.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.9.tgz", + "integrity": "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==", "dev": true, "license": "MIT", "dependencies": { @@ -925,9 +925,9 @@ "dev": true }, "node_modules/@vitest/coverage-v8": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.7.tgz", - "integrity": "sha512-Av8WgBJLTrfLOer0uy3CxjlVuWK4CzcLBndW1Nm2vI+3hZ2ozHututkfc7Blu1u6waeQ7J8gzPK/AsBRnWA5mQ==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.8.tgz", + "integrity": "sha512-y7SAKsQirsEJ2F8bulBck4DoluhI2EEgTimHd6EEUgJBGKy9tC25cpywh1MH4FvDGoG2Unt7+asVd1kj4qOSAw==", "dev": true, "license": "MIT", "dependencies": { @@ -948,8 +948,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.0.7", - "vitest": "3.0.7" + "@vitest/browser": "3.0.8", + "vitest": "3.0.8" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -958,14 +958,14 @@ } }, "node_modules/@vitest/expect": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.7.tgz", - "integrity": "sha512-QP25f+YJhzPfHrHfYHtvRn+uvkCFCqFtW9CktfBxmB+25QqWsx7VB2As6f4GmwllHLDhXNHvqedwhvMmSnNmjw==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.8.tgz", + "integrity": "sha512-Xu6TTIavTvSSS6LZaA3EebWFr6tsoXPetOWNMOlc7LO88QVVBwq2oQWBoDiLCN6YTvNYsGSjqOO8CAdjom5DCQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.0.7", - "@vitest/utils": "3.0.7", + "@vitest/spy": "3.0.8", + "@vitest/utils": "3.0.8", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -974,13 +974,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.7.tgz", - "integrity": "sha512-qui+3BLz9Eonx4EAuR/i+QlCX6AUZ35taDQgwGkK/Tw6/WgwodSrjN1X2xf69IA/643ZX5zNKIn2svvtZDrs4w==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.8.tgz", + "integrity": "sha512-n3LjS7fcW1BCoF+zWZxG7/5XvuYH+lsFg+BDwwAz0arIwHQJFUEsKBQ0BLU49fCxuM/2HSeBPHQD8WjgrxMfow==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.0.7", + "@vitest/spy": "3.0.8", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -1001,9 +1001,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.7.tgz", - "integrity": "sha512-CiRY0BViD/V8uwuEzz9Yapyao+M9M008/9oMOSQydwbwb+CMokEq3XVaF3XK/VWaOK0Jm9z7ENhybg70Gtxsmg==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.8.tgz", + "integrity": "sha512-BNqwbEyitFhzYMYHUVbIvepOyeQOSFA/NeJMIP9enMntkkxLgOcgABH6fjyXG85ipTgvero6noreavGIqfJcIg==", "dev": true, "license": "MIT", "dependencies": { @@ -1014,13 +1014,13 @@ } }, "node_modules/@vitest/runner": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.7.tgz", - "integrity": "sha512-WeEl38Z0S2ZcuRTeyYqaZtm4e26tq6ZFqh5y8YD9YxfWuu0OFiGFUbnxNynwLjNRHPsXyee2M9tV7YxOTPZl2g==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.8.tgz", + "integrity": "sha512-c7UUw6gEcOzI8fih+uaAXS5DwjlBaCJUo7KJ4VvJcjL95+DSR1kova2hFuRt3w41KZEFcOEiq098KkyrjXeM5w==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.0.7", + "@vitest/utils": "3.0.8", "pathe": "^2.0.3" }, "funding": { @@ -1028,13 +1028,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.7.tgz", - "integrity": "sha512-eqTUryJWQN0Rtf5yqCGTQWsCFOQe4eNz5Twsu21xYEcnFJtMU5XvmG0vgebhdLlrHQTSq5p8vWHJIeJQV8ovsA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.8.tgz", + "integrity": "sha512-x8IlMGSEMugakInj44nUrLSILh/zy1f2/BgH0UeHpNyOocG18M9CWVIFBaXPt8TrqVZWmcPjwfG/ht5tnpba8A==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.0.7", + "@vitest/pretty-format": "3.0.8", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -1043,9 +1043,9 @@ } }, "node_modules/@vitest/spy": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.7.tgz", - "integrity": "sha512-4T4WcsibB0B6hrKdAZTM37ekuyFZt2cGbEGd2+L0P8ov15J1/HUsUaqkXEQPNAWr4BtPPe1gI+FYfMHhEKfR8w==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.8.tgz", + "integrity": "sha512-MR+PzJa+22vFKYb934CejhR4BeRpMSoxkvNoDit68GQxRLSf11aT6CTj3XaqUU9rxgWJFnqicN/wxw6yBRkI1Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1056,13 +1056,13 @@ } }, "node_modules/@vitest/utils": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.7.tgz", - "integrity": "sha512-xePVpCRfooFX3rANQjwoditoXgWb1MaFbzmGuPP59MK6i13mrnDw/yEIyJudLeW6/38mCNcwCiJIGmpDPibAIg==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.8.tgz", + "integrity": "sha512-nkBC3aEhfX2PdtQI/QwAWp8qZWwzASsU4Npbcd5RdMPBSSLCpkZp52P3xku3s3uA0HIEhGvEcF8rNkBsz9dQ4Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.0.7", + "@vitest/pretty-format": "3.0.8", "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" }, @@ -2724,9 +2724,9 @@ } }, "node_modules/vite": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.0.tgz", - "integrity": "sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.1.tgz", + "integrity": "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2796,9 +2796,9 @@ } }, "node_modules/vite-node": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.7.tgz", - "integrity": "sha512-2fX0QwX4GkkkpULXdT1Pf4q0tC1i1lFOyseKoonavXUNlQ77KpW2XqBGGNIm/J4Ows4KxgGJzDguYVPKwG/n5A==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.8.tgz", + "integrity": "sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg==", "dev": true, "license": "MIT", "dependencies": { @@ -2819,19 +2819,19 @@ } }, "node_modules/vitest": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.7.tgz", - "integrity": "sha512-IP7gPK3LS3Fvn44x30X1dM9vtawm0aesAa2yBIZ9vQf+qB69NXC5776+Qmcr7ohUXIQuLhk7xQR0aSUIDPqavg==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.8.tgz", + "integrity": "sha512-dfqAsNqRGUc8hB9OVR2P0w8PZPEckti2+5rdZip0WIz9WW0MnImJ8XiR61QhqLa92EQzKP2uPkzenKOAHyEIbA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "3.0.7", - "@vitest/mocker": "3.0.7", - "@vitest/pretty-format": "^3.0.7", - "@vitest/runner": "3.0.7", - "@vitest/snapshot": "3.0.7", - "@vitest/spy": "3.0.7", - "@vitest/utils": "3.0.7", + "@vitest/expect": "3.0.8", + "@vitest/mocker": "3.0.8", + "@vitest/pretty-format": "^3.0.8", + "@vitest/runner": "3.0.8", + "@vitest/snapshot": "3.0.8", + "@vitest/spy": "3.0.8", + "@vitest/utils": "3.0.8", "chai": "^5.2.0", "debug": "^4.4.0", "expect-type": "^1.1.0", @@ -2843,7 +2843,7 @@ "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.0.7", + "vite-node": "3.0.8", "why-is-node-running": "^2.3.0" }, "bin": { @@ -2859,8 +2859,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.0.7", - "@vitest/ui": "3.0.7", + "@vitest/browser": "3.0.8", + "@vitest/ui": "3.0.8", "happy-dom": "*", "jsdom": "*" }, diff --git a/scripts/changelog-checker/package.json b/scripts/changelog-checker/package.json index a98935ae69..87d48974fb 100644 --- a/scripts/changelog-checker/package.json +++ b/scripts/changelog-checker/package.json @@ -10,15 +10,15 @@ }, "devDependencies": { "@types/mdast": "4.0.4", - "@types/node": "22.13.7", - "@vitest/coverage-v8": "3.0.7", + "@types/node": "22.13.9", + "@vitest/coverage-v8": "3.0.8", "mdast-util-to-string": "4.0.0", "remark": "15.0.1", "remark-parse": "11.0.0", "typescript": "5.8.2", "unified": "11.0.5", - "vite": "6.2.0", - "vite-node": "3.0.7", - "vitest": "3.0.7" + "vite": "6.2.1", + "vite-node": "3.0.8", + "vitest": "3.0.8" } } diff --git a/scripts/dependency-patches/re2.patch b/scripts/dependency-patches/re2.patch new file mode 100644 index 0000000000..3e7ec9f56f --- /dev/null +++ b/scripts/dependency-patches/re2.patch @@ -0,0 +1,13 @@ +diff --git a/package.json b/package.json +index a56ab59ef647288ee6028abd2b1780eaa92ebc9d..ec2c43e63f3134b6d54d616b2ef715447f873bbe 100644 +--- a/package.json ++++ b/package.json +@@ -28,7 +28,7 @@ + "test": "node tests/tests.js", + "ts-test": "tsc", + "save-to-github": "save-to-github-cache --artifact build/Release/re2.node", +- "install": "install-from-cache --artifact build/Release/re2.node --host-var RE2_DOWNLOAD_MIRROR --skip-path-var RE2_DOWNLOAD_SKIP_PATH --skip-ver-var RE2_DOWNLOAD_SKIP_VER || node-gyp -j max rebuild", ++ "install": "npm_package_github=https://github.com/uhop/node-re2 npm_package_scripts_verify_build=true install-from-cache --artifact build/Release/re2.node --host-var RE2_DOWNLOAD_MIRROR --skip-path-var RE2_DOWNLOAD_SKIP_PATH --skip-ver-var RE2_DOWNLOAD_SKIP_VER || node-gyp -j max rebuild", + "verify-build": "node scripts/verify-build.js", + "build:dev": "node-gyp -j max build --debug", + "build": "node-gyp -j max build", diff --git a/scripts/tarball.mjs b/scripts/tarball.mjs index e9d8900aca..fddbe3b04a 100644 --- a/scripts/tarball.mjs +++ b/scripts/tarball.mjs @@ -9,7 +9,7 @@ import { resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import glob from 'fast-glob'; import walk from 'ignore-walk'; -import Pack from 'tar/lib/pack.js'; +import { Pack } from 'tar/pack'; import meta from '../package.json' with { type: "json" }; const cwd = fileURLToPath(new URL('..', import.meta.url)); |