Compare commits
25 Commits
renovate/n
...
2025.3.1-b
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8dfff79ca2 | ||
![]() |
83c3bb839f | ||
![]() |
a9fe7eff0a | ||
![]() |
d49ecab792 | ||
![]() |
56459bbe68 | ||
![]() |
6c150ef1fb | ||
![]() |
c78f45ea20 | ||
![]() |
82481c01e0 | ||
![]() |
741cbc34e6 | ||
![]() |
5e86550de3 | ||
![]() |
92aef300ee | ||
![]() |
9ce1b68fd7 | ||
![]() |
5be5c8bec4 | ||
![]() |
0214a0001f | ||
![]() |
46067f6e17 | ||
![]() |
2b71bdf114 | ||
![]() |
9d6b521351 | ||
![]() |
ad708d896b | ||
![]() |
22228b6756 | ||
![]() |
f7ea0c6991 | ||
![]() |
60a3513cfc | ||
![]() |
377f002d68 | ||
![]() |
896bde1005 | ||
![]() |
6d0242277d | ||
![]() |
60f90ca649 |
@@ -7,8 +7,8 @@
|
|||||||
"ghcr.io/devcontainers/features/node:1": {
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
"version": "22.11.0"
|
"version": "22.11.0"
|
||||||
},
|
},
|
||||||
"ghcr.io/devcontainers-extra/features/corepack:1": {
|
"ghcr.io/devcontainers-extra/features/pnpm:2": {
|
||||||
"version": "0.31.0"
|
"version": "10.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"forwardPorts": [3000],
|
"forwardPorts": [3000],
|
||||||
|
@@ -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
|
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 config --global --add safe.directory /workspace
|
||||||
git submodule update --init
|
git submodule update --init
|
||||||
corepack install
|
|
||||||
corepack enable
|
|
||||||
pnpm config set store-dir /home/node/.local/share/pnpm/store
|
pnpm config set store-dir /home/node/.local/share/pnpm/store
|
||||||
pnpm install --frozen-lockfile
|
pnpm install --frozen-lockfile
|
||||||
cp .devcontainer/devcontainer.yml .config/default.yml
|
cp .devcontainer/devcontainer.yml .config/default.yml
|
||||||
|
7
.github/workflows/api-misskey-js.yml
vendored
7
.github/workflows/api-misskey-js.yml
vendored
@@ -9,10 +9,6 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- packages/misskey-js/**
|
- packages/misskey-js/**
|
||||||
- .github/workflows/api-misskey-js.yml
|
- .github/workflows/api-misskey-js.yml
|
||||||
|
|
||||||
env:
|
|
||||||
COREPACK_DEFAULT_TO_LATEST: 0
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
report:
|
report:
|
||||||
|
|
||||||
@@ -22,7 +18,8 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
|
|
||||||
- run: corepack enable
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4.1.0
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.2.0
|
||||||
|
9
.github/workflows/get-api-diff.yml
vendored
9
.github/workflows/get-api-diff.yml
vendored
@@ -9,10 +9,6 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- packages/backend/**
|
- packages/backend/**
|
||||||
- .github/workflows/get-api-diff.yml
|
- .github/workflows/get-api-diff.yml
|
||||||
|
|
||||||
env:
|
|
||||||
COREPACK_DEFAULT_TO_LATEST: 0
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-from-misskey:
|
get-from-misskey:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -34,14 +30,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
ref: ${{ matrix.ref }}
|
ref: ${{ matrix.ref }}
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4.1.0
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.2.0
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- run: corepack enable
|
|
||||||
- run: pnpm i --frozen-lockfile
|
- run: pnpm i --frozen-lockfile
|
||||||
- name: Check pnpm-lock.yaml
|
- name: Check pnpm-lock.yaml
|
||||||
run: git diff --exit-code pnpm-lock.yaml
|
run: git diff --exit-code pnpm-lock.yaml
|
||||||
|
16
.github/workflows/lint.yml
vendored
16
.github/workflows/lint.yml
vendored
@@ -28,10 +28,6 @@ on:
|
|||||||
- packages/misskey-reversi/**
|
- packages/misskey-reversi/**
|
||||||
- packages/shared/eslint.config.js
|
- packages/shared/eslint.config.js
|
||||||
- .github/workflows/lint.yml
|
- .github/workflows/lint.yml
|
||||||
|
|
||||||
env:
|
|
||||||
COREPACK_DEFAULT_TO_LATEST: 0
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pnpm_install:
|
pnpm_install:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -40,12 +36,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: pnpm/action-setup@v4
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4.1.0
|
||||||
- uses: actions/setup-node@v4.2.0
|
- uses: actions/setup-node@v4.2.0
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- run: corepack enable
|
|
||||||
- run: pnpm i --frozen-lockfile
|
- run: pnpm i --frozen-lockfile
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
@@ -71,12 +67,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: pnpm/action-setup@v4
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4.1.0
|
||||||
- uses: actions/setup-node@v4.2.0
|
- uses: actions/setup-node@v4.2.0
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- run: corepack enable
|
|
||||||
- run: pnpm i --frozen-lockfile
|
- run: pnpm i --frozen-lockfile
|
||||||
- name: Restore eslint cache
|
- name: Restore eslint cache
|
||||||
uses: actions/cache@v4.2.2
|
uses: actions/cache@v4.2.2
|
||||||
@@ -101,12 +97,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: pnpm/action-setup@v4
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4.1.0
|
||||||
- uses: actions/setup-node@v4.2.0
|
- uses: actions/setup-node@v4.2.0
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- run: corepack enable
|
|
||||||
- run: pnpm i --frozen-lockfile
|
- run: pnpm i --frozen-lockfile
|
||||||
- run: pnpm --filter misskey-js run build
|
- run: pnpm --filter misskey-js run build
|
||||||
if: ${{ matrix.workspace == 'backend' || matrix.workspace == 'sw' }}
|
if: ${{ matrix.workspace == 'backend' || matrix.workspace == 'sw' }}
|
||||||
|
8
.github/workflows/locale.yml
vendored
8
.github/workflows/locale.yml
vendored
@@ -9,10 +9,6 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- locales/**
|
- locales/**
|
||||||
- .github/workflows/locale.yml
|
- .github/workflows/locale.yml
|
||||||
|
|
||||||
env:
|
|
||||||
COREPACK_DEFAULT_TO_LATEST: 0
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
locale_verify:
|
locale_verify:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -22,11 +18,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: pnpm/action-setup@v4
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4.1.0
|
||||||
- uses: actions/setup-node@v4.2.0
|
- uses: actions/setup-node@v4.2.0
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- run: corepack enable
|
|
||||||
- run: pnpm i --frozen-lockfile
|
- run: pnpm i --frozen-lockfile
|
||||||
- run: cd locales && node verify.js
|
- run: cd locales && node verify.js
|
||||||
|
8
.github/workflows/on-release-created.yml
vendored
8
.github/workflows/on-release-created.yml
vendored
@@ -6,9 +6,6 @@ on:
|
|||||||
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
|
||||||
COREPACK_DEFAULT_TO_LATEST: 0
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish-misskey-js:
|
publish-misskey-js:
|
||||||
name: Publish misskey-js
|
name: Publish misskey-js
|
||||||
@@ -26,8 +23,8 @@ jobs:
|
|||||||
- uses: actions/checkout@v4.2.2
|
- uses: actions/checkout@v4.2.2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4.1.0
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.2.0
|
||||||
with:
|
with:
|
||||||
@@ -36,7 +33,6 @@ jobs:
|
|||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
- name: Publish package
|
- name: Publish package
|
||||||
run: |
|
run: |
|
||||||
corepack enable
|
|
||||||
pnpm i --frozen-lockfile
|
pnpm i --frozen-lockfile
|
||||||
pnpm build
|
pnpm build
|
||||||
pnpm --filter misskey-js publish --access public --no-git-checks --provenance
|
pnpm --filter misskey-js publish --access public --no-git-checks --provenance
|
||||||
|
8
.github/workflows/storybook.yml
vendored
8
.github/workflows/storybook.yml
vendored
@@ -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.
|
# This is a waste of chromatic build quota, so we don't run storybook CI on pull requests targets master.
|
||||||
- master
|
- master
|
||||||
|
|
||||||
env:
|
|
||||||
COREPACK_DEFAULT_TO_LATEST: 0
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
# chromatic is not likely to be available for fork repositories, so we disable for fork repositories.
|
# chromatic is not likely to be available for fork repositories, so we disable for fork repositories.
|
||||||
@@ -43,14 +40,13 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "base=$(git rev-list --parents -n1 HEAD | cut -d" " -f2)" >> $GITHUB_OUTPUT
|
echo "base=$(git rev-list --parents -n1 HEAD | cut -d" " -f2)" >> $GITHUB_OUTPUT
|
||||||
git checkout $(git rev-list --parents -n1 HEAD | cut -d" " -f3)
|
git checkout $(git rev-list --parents -n1 HEAD | cut -d" " -f3)
|
||||||
- name: Install pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4.1.0
|
||||||
- name: Use Node.js 20.x
|
- name: Use Node.js 20.x
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.2.0
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- run: corepack enable
|
|
||||||
- run: pnpm i --frozen-lockfile
|
- run: pnpm i --frozen-lockfile
|
||||||
- name: Check pnpm-lock.yaml
|
- name: Check pnpm-lock.yaml
|
||||||
run: git diff --exit-code pnpm-lock.yaml
|
run: git diff --exit-code pnpm-lock.yaml
|
||||||
|
14
.github/workflows/test-backend.yml
vendored
14
.github/workflows/test-backend.yml
vendored
@@ -18,10 +18,6 @@ on:
|
|||||||
- packages/misskey-js/**
|
- packages/misskey-js/**
|
||||||
- .github/workflows/test-backend.yml
|
- .github/workflows/test-backend.yml
|
||||||
- .github/misskey/test.yml
|
- .github/misskey/test.yml
|
||||||
|
|
||||||
env:
|
|
||||||
COREPACK_DEFAULT_TO_LATEST: 0
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
unit:
|
unit:
|
||||||
name: Unit tests (backend)
|
name: Unit tests (backend)
|
||||||
@@ -48,8 +44,8 @@ jobs:
|
|||||||
- uses: actions/checkout@v4.2.2
|
- uses: actions/checkout@v4.2.2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4.1.0
|
||||||
- name: Install FFmpeg
|
- name: Install FFmpeg
|
||||||
run: |
|
run: |
|
||||||
for i in {1..3}; do
|
for i in {1..3}; do
|
||||||
@@ -70,7 +66,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- run: corepack enable
|
|
||||||
- run: pnpm i --frozen-lockfile
|
- run: pnpm i --frozen-lockfile
|
||||||
- name: Check pnpm-lock.yaml
|
- name: Check pnpm-lock.yaml
|
||||||
run: git diff --exit-code pnpm-lock.yaml
|
run: git diff --exit-code pnpm-lock.yaml
|
||||||
@@ -111,14 +106,13 @@ jobs:
|
|||||||
- uses: actions/checkout@v4.2.2
|
- uses: actions/checkout@v4.2.2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4.1.0
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.2.0
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- run: corepack enable
|
|
||||||
- run: pnpm i --frozen-lockfile
|
- run: pnpm i --frozen-lockfile
|
||||||
- name: Check pnpm-lock.yaml
|
- name: Check pnpm-lock.yaml
|
||||||
run: git diff --exit-code pnpm-lock.yaml
|
run: git diff --exit-code pnpm-lock.yaml
|
||||||
|
8
.github/workflows/test-federation.yml
vendored
8
.github/workflows/test-federation.yml
vendored
@@ -15,9 +15,6 @@ on:
|
|||||||
- packages/misskey-js/**
|
- packages/misskey-js/**
|
||||||
- .github/workflows/test-federation.yml
|
- .github/workflows/test-federation.yml
|
||||||
|
|
||||||
env:
|
|
||||||
COREPACK_DEFAULT_TO_LATEST: 0
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: Federation test
|
name: Federation test
|
||||||
@@ -29,8 +26,8 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4.1.0
|
||||||
- name: Install FFmpeg
|
- name: Install FFmpeg
|
||||||
run: |
|
run: |
|
||||||
for i in {1..3}; do
|
for i in {1..3}; do
|
||||||
@@ -53,7 +50,6 @@ jobs:
|
|||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- name: Build Misskey
|
- name: Build Misskey
|
||||||
run: |
|
run: |
|
||||||
corepack enable && corepack prepare
|
|
||||||
pnpm i --frozen-lockfile
|
pnpm i --frozen-lockfile
|
||||||
pnpm build
|
pnpm build
|
||||||
- name: Setup
|
- name: Setup
|
||||||
|
14
.github/workflows/test-frontend.yml
vendored
14
.github/workflows/test-frontend.yml
vendored
@@ -22,10 +22,6 @@ on:
|
|||||||
- packages/backend/**
|
- packages/backend/**
|
||||||
- .github/workflows/test-frontend.yml
|
- .github/workflows/test-frontend.yml
|
||||||
- .github/misskey/test.yml
|
- .github/misskey/test.yml
|
||||||
|
|
||||||
env:
|
|
||||||
COREPACK_DEFAULT_TO_LATEST: 0
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
vitest:
|
vitest:
|
||||||
name: Unit tests (frontend)
|
name: Unit tests (frontend)
|
||||||
@@ -39,14 +35,13 @@ jobs:
|
|||||||
- uses: actions/checkout@v4.2.2
|
- uses: actions/checkout@v4.2.2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4.1.0
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.2.0
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- run: corepack enable
|
|
||||||
- run: pnpm i --frozen-lockfile
|
- run: pnpm i --frozen-lockfile
|
||||||
- name: Check pnpm-lock.yaml
|
- name: Check pnpm-lock.yaml
|
||||||
run: git diff --exit-code pnpm-lock.yaml
|
run: git diff --exit-code pnpm-lock.yaml
|
||||||
@@ -95,14 +90,13 @@ jobs:
|
|||||||
# if: ${{ matrix.browser == 'firefox' }}
|
# if: ${{ matrix.browser == 'firefox' }}
|
||||||
#- uses: browser-actions/setup-firefox@latest
|
#- uses: browser-actions/setup-firefox@latest
|
||||||
# if: ${{ matrix.browser == 'firefox' }}
|
# if: ${{ matrix.browser == 'firefox' }}
|
||||||
- name: Install pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4.1.0
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.2.0
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- run: corepack enable
|
|
||||||
- run: pnpm i --frozen-lockfile
|
- run: pnpm i --frozen-lockfile
|
||||||
- name: Copy Configure
|
- name: Copy Configure
|
||||||
run: cp .github/misskey/test.yml .config
|
run: cp .github/misskey/test.yml .config
|
||||||
|
7
.github/workflows/test-misskey-js.yml
vendored
7
.github/workflows/test-misskey-js.yml
vendored
@@ -14,10 +14,6 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- packages/misskey-js/**
|
- packages/misskey-js/**
|
||||||
- .github/workflows/test-misskey-js.yml
|
- .github/workflows/test-misskey-js.yml
|
||||||
|
|
||||||
env:
|
|
||||||
COREPACK_DEFAULT_TO_LATEST: 0
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: Unit tests (misskey.js)
|
name: Unit tests (misskey.js)
|
||||||
@@ -33,7 +29,8 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4.2.2
|
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 }}
|
- name: Setup Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.2.0
|
||||||
|
6
.github/workflows/test-production.yml
vendored
6
.github/workflows/test-production.yml
vendored
@@ -9,7 +9,6 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
COREPACK_DEFAULT_TO_LATEST: 0
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
production:
|
production:
|
||||||
@@ -24,14 +23,13 @@ jobs:
|
|||||||
- uses: actions/checkout@v4.2.2
|
- uses: actions/checkout@v4.2.2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4.1.0
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.2.0
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- run: corepack enable
|
|
||||||
- run: pnpm i --frozen-lockfile
|
- run: pnpm i --frozen-lockfile
|
||||||
- name: Check pnpm-lock.yaml
|
- name: Check pnpm-lock.yaml
|
||||||
run: git diff --exit-code pnpm-lock.yaml
|
run: git diff --exit-code pnpm-lock.yaml
|
||||||
|
9
.github/workflows/validate-api-json.yml
vendored
9
.github/workflows/validate-api-json.yml
vendored
@@ -12,10 +12,6 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- packages/backend/**
|
- packages/backend/**
|
||||||
- .github/workflows/validate-api-json.yml
|
- .github/workflows/validate-api-json.yml
|
||||||
|
|
||||||
env:
|
|
||||||
COREPACK_DEFAULT_TO_LATEST: 0
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
validate-api-json:
|
validate-api-json:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -28,8 +24,8 @@ jobs:
|
|||||||
- uses: actions/checkout@v4.2.2
|
- uses: actions/checkout@v4.2.2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4.1.0
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.2.0
|
||||||
with:
|
with:
|
||||||
@@ -37,7 +33,6 @@ jobs:
|
|||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- name: Install Redocly CLI
|
- name: Install Redocly CLI
|
||||||
run: npm i -g @redocly/cli
|
run: npm i -g @redocly/cli
|
||||||
- run: corepack enable
|
|
||||||
- run: pnpm i --frozen-lockfile
|
- run: pnpm i --frozen-lockfile
|
||||||
- name: Check pnpm-lock.yaml
|
- name: Check pnpm-lock.yaml
|
||||||
run: git diff --exit-code pnpm-lock.yaml
|
run: git diff --exit-code pnpm-lock.yaml
|
||||||
|
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,18 +1,37 @@
|
|||||||
|
## 2025.3.1
|
||||||
|
|
||||||
|
### General
|
||||||
|
- pnpmをv10に更新
|
||||||
|
- Corepackを削除
|
||||||
|
|
||||||
|
### Client
|
||||||
|
- Feat: 設定の検索を追加(実験的)
|
||||||
|
- Enhance: 設定項目の再配置
|
||||||
|
|
||||||
|
### Server
|
||||||
|
- Fix: DBマイグレーション際にシステムアカウントのユーザーID判定が正しくない問題を修正
|
||||||
|
|
||||||
|
|
||||||
## 2025.3.0
|
## 2025.3.0
|
||||||
|
|
||||||
### General
|
### General
|
||||||
- Enhance: プロキシアカウントをシステムアカウントとして作成するように
|
- Enhance: プロキシアカウントをシステムアカウントとして作成するように
|
||||||
|
- Enhance: OAuthで外部アプリからロゴが提供されている場合、それを表示できるように
|
||||||
|
書式は https://indieauth.spec.indieweb.org/20220212/#example-2 に準じます。
|
||||||
- Fix: システムアカウントが削除できる問題を修正
|
- Fix: システムアカウントが削除できる問題を修正
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
- Enhance: モデレーターがセンシティブ設定を変更する際に確認ダイアログを出すように
|
- Enhance: モデレーターがセンシティブ設定を変更する際に確認ダイアログを出すように
|
||||||
- Enhance: 「UIのアニメーションを減らす」で画面上のエフェクトも減らせるように
|
- Enhance: 「UIのアニメーションを減らす」で画面上のエフェクトも減らせるように
|
||||||
- Fix: 削除して編集の削除タイミングを投稿後になるように `#14498`
|
- Enhance: 投稿フォームにおける、メディアの添付可能個数のカウントを反転しました
|
||||||
|
- これまでの表示は`添付可能残り個数/上限数`でしたが、`添付個数/上限数`としました
|
||||||
- Fix: フォローされたときのメッセージがちらつくことがある問題を修正
|
- Fix: フォローされたときのメッセージがちらつくことがある問題を修正
|
||||||
- Fix: 投稿ダイアログがサイズ限界を超えた際にスクロールできない問題を修正
|
- Fix: 投稿ダイアログがサイズ限界を超えた際にスクロールできない問題を修正
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
- Fix: 特定のケースでActivityPubの処理がデッドロックになることがあるのを修正
|
- Fix: 特定のケースでActivityPubの処理がデッドロックになることがあるのを修正
|
||||||
|
- Fix: S3互換オブジェクトストレージでファイルのアップロードに失敗することがある問題を修正
|
||||||
|
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/895)
|
||||||
|
|
||||||
|
|
||||||
## 2025.2.1
|
## 2025.2.1
|
||||||
|
15
Dockerfile
15
Dockerfile
@@ -6,8 +6,6 @@ ARG NODE_VERSION=22.11.0-bookworm
|
|||||||
|
|
||||||
FROM --platform=$BUILDPLATFORM node:${NODE_VERSION} AS native-builder
|
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 \
|
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||||
rm -f /etc/apt/apt.conf.d/docker-clean \
|
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 \
|
&& apt-get install -yqq --no-install-recommends \
|
||||||
build-essential
|
build-essential
|
||||||
|
|
||||||
RUN corepack enable
|
|
||||||
|
|
||||||
WORKDIR /misskey
|
WORKDIR /misskey
|
||||||
|
|
||||||
COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
|
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
|
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 \
|
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
|
||||||
pnpm i --frozen-lockfile --aggregate-output
|
pnpm i --frozen-lockfile --aggregate-output
|
||||||
|
|
||||||
@@ -46,14 +44,10 @@ RUN rm -rf .git/
|
|||||||
|
|
||||||
FROM --platform=$TARGETPLATFORM node:${NODE_VERSION} AS target-builder
|
FROM --platform=$TARGETPLATFORM node:${NODE_VERSION} AS target-builder
|
||||||
|
|
||||||
ENV COREPACK_DEFAULT_TO_LATEST=0
|
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -yqq --no-install-recommends \
|
&& apt-get install -yqq --no-install-recommends \
|
||||||
build-essential
|
build-essential
|
||||||
|
|
||||||
RUN corepack enable
|
|
||||||
|
|
||||||
WORKDIR /misskey
|
WORKDIR /misskey
|
||||||
|
|
||||||
COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
|
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
|
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 \
|
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
|
||||||
pnpm i --frozen-lockfile --aggregate-output
|
pnpm i --frozen-lockfile --aggregate-output
|
||||||
|
|
||||||
@@ -72,13 +68,11 @@ FROM --platform=$TARGETPLATFORM node:${NODE_VERSION}-slim AS runner
|
|||||||
|
|
||||||
ARG UID="991"
|
ARG UID="991"
|
||||||
ARG GID="991"
|
ARG GID="991"
|
||||||
ENV COREPACK_DEFAULT_TO_LATEST=0
|
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends \
|
&& apt-get install -y --no-install-recommends \
|
||||||
ffmpeg tini curl libjemalloc-dev libjemalloc2 \
|
ffmpeg tini curl libjemalloc-dev libjemalloc2 \
|
||||||
&& ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so \
|
&& ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so \
|
||||||
&& corepack enable \
|
|
||||||
&& groupadd -g "${GID}" misskey \
|
&& groupadd -g "${GID}" misskey \
|
||||||
&& useradd -l -u "${UID}" -g "${GID}" -m -d /misskey 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 {} \; \
|
&& 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 {} \; \
|
||||||
@@ -91,7 +85,6 @@ WORKDIR /misskey
|
|||||||
|
|
||||||
# add package.json to add pnpm
|
# add package.json to add pnpm
|
||||||
COPY --chown=misskey:misskey ./package.json ./package.json
|
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/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/backend/node_modules ./packages/backend/node_modules
|
||||||
|
@@ -111,7 +111,7 @@ followRequests: "Peticions de seguiment"
|
|||||||
unfollow: "Deixar de seguir"
|
unfollow: "Deixar de seguir"
|
||||||
followRequestPending: "Sol·licituds de seguiment pendents"
|
followRequestPending: "Sol·licituds de seguiment pendents"
|
||||||
enterEmoji: "Introduir un emoji"
|
enterEmoji: "Introduir un emoji"
|
||||||
renote: "Impulsos"
|
renote: "Impulsar"
|
||||||
unrenote: "Anul·la l'impuls"
|
unrenote: "Anul·la l'impuls"
|
||||||
renoted: "S'ha impulsat"
|
renoted: "S'ha impulsat"
|
||||||
renotedToX: "Impulsat per {name}."
|
renotedToX: "Impulsat per {name}."
|
||||||
@@ -260,7 +260,7 @@ noCustomEmojis: "No hi ha emojis personalitzats"
|
|||||||
noJobs: "No hi ha feines"
|
noJobs: "No hi ha feines"
|
||||||
federating: "Federant"
|
federating: "Federant"
|
||||||
blocked: "Bloquejat"
|
blocked: "Bloquejat"
|
||||||
suspended: "Suspés"
|
suspended: "Anul·lar subscripció "
|
||||||
all: "tot"
|
all: "tot"
|
||||||
subscribing: "Subscrit a"
|
subscribing: "Subscrit a"
|
||||||
publishing: "S'està publicant"
|
publishing: "S'està publicant"
|
||||||
@@ -1114,7 +1114,7 @@ forceShowAds: "Mostra els anuncis sempre "
|
|||||||
addMemo: "Afegir recordatori"
|
addMemo: "Afegir recordatori"
|
||||||
editMemo: "Editar recordatori"
|
editMemo: "Editar recordatori"
|
||||||
reactionsList: "Reaccions"
|
reactionsList: "Reaccions"
|
||||||
renotesList: "Impulsos"
|
renotesList: "Llistat d'impulsos "
|
||||||
notificationDisplay: "Notificacions"
|
notificationDisplay: "Notificacions"
|
||||||
leftTop: "Dalt a l'esquerra "
|
leftTop: "Dalt a l'esquerra "
|
||||||
rightTop: "Dalt a la dreta "
|
rightTop: "Dalt a la dreta "
|
||||||
@@ -1190,7 +1190,7 @@ pastAnnouncements: "Informes passats"
|
|||||||
youHaveUnreadAnnouncements: "Tens informes per llegir."
|
youHaveUnreadAnnouncements: "Tens informes per llegir."
|
||||||
useSecurityKey: "Segueix les instruccions del teu navegador O dispositiu per fer servir el teu passkey."
|
useSecurityKey: "Segueix les instruccions del teu navegador O dispositiu per fer servir el teu passkey."
|
||||||
replies: "Respostes"
|
replies: "Respostes"
|
||||||
renotes: "Impulsos"
|
renotes: "Impulsar"
|
||||||
loadReplies: "Mostrar les respostes"
|
loadReplies: "Mostrar les respostes"
|
||||||
loadConversation: "Mostrar la conversació "
|
loadConversation: "Mostrar la conversació "
|
||||||
pinnedList: "Llista fixada"
|
pinnedList: "Llista fixada"
|
||||||
@@ -1311,6 +1311,8 @@ federationSpecified: "Aquest servidor treballa amb una federació de llistes bla
|
|||||||
federationDisabled: "La unió es troba deshabilitada en aquest servidor. No es pot interactuar amb usuaris d'altres servidors."
|
federationDisabled: "La unió es troba deshabilitada en aquest servidor. No es pot interactuar amb usuaris d'altres servidors."
|
||||||
confirmOnReact: "Confirmar en reaccionar"
|
confirmOnReact: "Confirmar en reaccionar"
|
||||||
reactAreYouSure: "Vols reaccionar amb \"{emoji}\"?"
|
reactAreYouSure: "Vols reaccionar amb \"{emoji}\"?"
|
||||||
|
markAsSensitiveConfirm: "Vols marcar aquest contingut com a sensible?"
|
||||||
|
unmarkAsSensitiveConfirm: "Vols deixar de marcar com a sensible aquest contingut?"
|
||||||
_accountSettings:
|
_accountSettings:
|
||||||
requireSigninToViewContents: "És obligatori l'inici de sessió per poder veure el contingut"
|
requireSigninToViewContents: "És obligatori l'inici de sessió per poder veure el contingut"
|
||||||
requireSigninToViewContentsDescription1: "Es requereix l'inici de sessió per poder veure totes les notes i el contingut que has creat. Amb això esperem evitar que els rastrejadors recopilin informació."
|
requireSigninToViewContentsDescription1: "Es requereix l'inici de sessió per poder veure totes les notes i el contingut que has creat. Amb això esperem evitar que els rastrejadors recopilin informació."
|
||||||
@@ -1332,7 +1334,7 @@ _abuseUserReport:
|
|||||||
resolveTutorial: "Si l'informe és legítim selecciona \"Acceptar\" per resoldre'l positivament. Però si l'informe no és legítim selecciona \"Rebutjar\" per resoldre'l negativament."
|
resolveTutorial: "Si l'informe és legítim selecciona \"Acceptar\" per resoldre'l positivament. Però si l'informe no és legítim selecciona \"Rebutjar\" per resoldre'l negativament."
|
||||||
_delivery:
|
_delivery:
|
||||||
status: "Estat d'entrega "
|
status: "Estat d'entrega "
|
||||||
stop: "Suspés"
|
stop: "Anul·lar subscripció "
|
||||||
resume: "Torna a enviar"
|
resume: "Torna a enviar"
|
||||||
_type:
|
_type:
|
||||||
none: "S'està publicant"
|
none: "S'està publicant"
|
||||||
@@ -2450,7 +2452,7 @@ _notification:
|
|||||||
follow: "Segueix-me"
|
follow: "Segueix-me"
|
||||||
mention: "Menció"
|
mention: "Menció"
|
||||||
reply: "Respostes"
|
reply: "Respostes"
|
||||||
renote: "Renotar"
|
renote: "Impulsar"
|
||||||
quote: "Citar"
|
quote: "Citar"
|
||||||
reaction: "Reaccions"
|
reaction: "Reaccions"
|
||||||
pollEnded: "Enquesta terminada"
|
pollEnded: "Enquesta terminada"
|
||||||
@@ -2465,7 +2467,7 @@ _notification:
|
|||||||
_actions:
|
_actions:
|
||||||
followBack: "També et segueix"
|
followBack: "També et segueix"
|
||||||
reply: "Respondre"
|
reply: "Respondre"
|
||||||
renote: "Renotar"
|
renote: "Impulsos"
|
||||||
_deck:
|
_deck:
|
||||||
alwaysShowMainColumn: "Mostrar sempre la columna principal"
|
alwaysShowMainColumn: "Mostrar sempre la columna principal"
|
||||||
columnAlign: "Alinea les columnes"
|
columnAlign: "Alinea les columnes"
|
||||||
@@ -2594,6 +2596,7 @@ _moderationLogTypes:
|
|||||||
deletePage: "Esborrar la pàgina"
|
deletePage: "Esborrar la pàgina"
|
||||||
deleteFlash: "Esborrar el guió"
|
deleteFlash: "Esborrar el guió"
|
||||||
deleteGalleryPost: "Esborrar la publicació de la galeria"
|
deleteGalleryPost: "Esborrar la publicació de la galeria"
|
||||||
|
updateProxyAccountDescription: "Actualitzar descripció del compte proxy"
|
||||||
_fileViewer:
|
_fileViewer:
|
||||||
title: "Detall del fitxer"
|
title: "Detall del fitxer"
|
||||||
type: "Tipus de fitxer"
|
type: "Tipus de fitxer"
|
||||||
|
@@ -5,6 +5,7 @@ introMisskey: "Vítejte! Misskey je otevřený a decentralizovaný microblogový
|
|||||||
poweredByMisskeyDescription: "{name} je jeden ze serverů využívající open source platformu <b>Misskey<b> (nazývaná \"Misskey instance\")."
|
poweredByMisskeyDescription: "{name} je jeden ze serverů využívající open source platformu <b>Misskey<b> (nazývaná \"Misskey instance\")."
|
||||||
monthAndDay: "{day}. {month}."
|
monthAndDay: "{day}. {month}."
|
||||||
search: "Vyhledávání"
|
search: "Vyhledávání"
|
||||||
|
reset: "Obnovit"
|
||||||
notifications: "Oznámení"
|
notifications: "Oznámení"
|
||||||
username: "Uživatelské jméno"
|
username: "Uživatelské jméno"
|
||||||
password: "Heslo"
|
password: "Heslo"
|
||||||
@@ -365,8 +366,11 @@ hcaptcha: "hCaptcha"
|
|||||||
enableHcaptcha: "Aktivovat hCaptchu"
|
enableHcaptcha: "Aktivovat hCaptchu"
|
||||||
hcaptchaSiteKey: "Klíč stránky"
|
hcaptchaSiteKey: "Klíč stránky"
|
||||||
hcaptchaSecretKey: "Tajný Klíč (Secret Key)"
|
hcaptchaSecretKey: "Tajný Klíč (Secret Key)"
|
||||||
|
mcaptcha: "mCaptcha"
|
||||||
|
enableMcaptcha: "Aktivovat mCaptchu"
|
||||||
mcaptchaSiteKey: "Klíč stránky"
|
mcaptchaSiteKey: "Klíč stránky"
|
||||||
mcaptchaSecretKey: "Tajný Klíč (Secret Key)"
|
mcaptchaSecretKey: "Tajný Klíč (Secret Key)"
|
||||||
|
mcaptchaInstanceUrl: "URL mCaptcha serveru"
|
||||||
recaptcha: "reCAPTCHA"
|
recaptcha: "reCAPTCHA"
|
||||||
enableRecaptcha: "Zapnout ReCAPTCHu"
|
enableRecaptcha: "Zapnout ReCAPTCHu"
|
||||||
recaptchaSiteKey: "Klíč stránky"
|
recaptchaSiteKey: "Klíč stránky"
|
||||||
|
@@ -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."
|
federationDisabled: "Federation is disabled on this server. You cannot interact with users on other servers."
|
||||||
confirmOnReact: "Confirm when reacting"
|
confirmOnReact: "Confirm when reacting"
|
||||||
reactAreYouSure: "Would you like to add a \"{emoji}\" reaction?"
|
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:
|
_accountSettings:
|
||||||
requireSigninToViewContents: "Require sign-in to view contents"
|
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."
|
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"
|
deletePage: "Page deleted"
|
||||||
deleteFlash: "Play deleted"
|
deleteFlash: "Play deleted"
|
||||||
deleteGalleryPost: "Gallery post deleted"
|
deleteGalleryPost: "Gallery post deleted"
|
||||||
|
updateProxyAccountDescription: "Update the description of the proxy account"
|
||||||
_fileViewer:
|
_fileViewer:
|
||||||
title: "File details"
|
title: "File details"
|
||||||
type: "File type"
|
type: "File type"
|
||||||
@@ -2649,7 +2652,7 @@ _dataSaver:
|
|||||||
description: "Prevents images/videos from being loaded automatically. Hidden images/videos will be loaded when tapped."
|
description: "Prevents images/videos from being loaded automatically. Hidden images/videos will be loaded when tapped."
|
||||||
_avatar:
|
_avatar:
|
||||||
title: "Avatar image"
|
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:
|
_urlPreview:
|
||||||
title: "URL preview thumbnails"
|
title: "URL preview thumbnails"
|
||||||
description: "URL preview thumbnail images will no longer be loaded."
|
description: "URL preview thumbnail images will no longer be loaded."
|
||||||
@@ -2857,4 +2860,8 @@ _bootErrors:
|
|||||||
_search:
|
_search:
|
||||||
searchScopeAll: "All"
|
searchScopeAll: "All"
|
||||||
searchScopeLocal: "Local"
|
searchScopeLocal: "Local"
|
||||||
|
searchScopeServer: "Specific server"
|
||||||
searchScopeUser: "Specific user"
|
searchScopeUser: "Specific user"
|
||||||
|
pleaseEnterServerHost: "Enter the server host"
|
||||||
|
pleaseSelectUser: "Select user"
|
||||||
|
serverHostPlaceholder: "Example: misskey.example.com"
|
||||||
|
10
locales/index.d.ts
vendored
10
locales/index.d.ts
vendored
@@ -4971,7 +4971,7 @@ export interface Locale extends ILocale {
|
|||||||
*/
|
*/
|
||||||
"disableStreamingTimeline": string;
|
"disableStreamingTimeline": string;
|
||||||
/**
|
/**
|
||||||
* 通知をグルーピングして表示する
|
* 通知をグルーピング
|
||||||
*/
|
*/
|
||||||
"useGroupedNotifications": string;
|
"useGroupedNotifications": string;
|
||||||
/**
|
/**
|
||||||
@@ -5270,6 +5270,14 @@ export interface Locale extends ILocale {
|
|||||||
* このメディアのセンシティブ指定を解除しますか?
|
* このメディアのセンシティブ指定を解除しますか?
|
||||||
*/
|
*/
|
||||||
"unmarkAsSensitiveConfirm": string;
|
"unmarkAsSensitiveConfirm": string;
|
||||||
|
/**
|
||||||
|
* 環境設定
|
||||||
|
*/
|
||||||
|
"preferences": string;
|
||||||
|
/**
|
||||||
|
* アクセシビリティ
|
||||||
|
*/
|
||||||
|
"accessibility": string;
|
||||||
"_accountSettings": {
|
"_accountSettings": {
|
||||||
/**
|
/**
|
||||||
* コンテンツの表示にログインを必須にする
|
* コンテンツの表示にログインを必須にする
|
||||||
|
@@ -126,7 +126,7 @@ pinnedNote: "Nota in primo piano"
|
|||||||
pinned: "Fissa sul profilo"
|
pinned: "Fissa sul profilo"
|
||||||
you: "Tu"
|
you: "Tu"
|
||||||
clickToShow: "Contenuto occultato, cliccare solo se si intende vedere"
|
clickToShow: "Contenuto occultato, cliccare solo se si intende vedere"
|
||||||
sensitive: "Allegato esplicito"
|
sensitive: "Esplicito"
|
||||||
add: "Aggiungi"
|
add: "Aggiungi"
|
||||||
reaction: "Reazioni"
|
reaction: "Reazioni"
|
||||||
reactions: "Reazioni"
|
reactions: "Reazioni"
|
||||||
@@ -228,7 +228,7 @@ jobQueue: "Coda di lavoro"
|
|||||||
cpuAndMemory: "CPU e Memoria"
|
cpuAndMemory: "CPU e Memoria"
|
||||||
network: "Rete"
|
network: "Rete"
|
||||||
disk: "Disco"
|
disk: "Disco"
|
||||||
instanceInfo: "Informazioni sull'istanza"
|
instanceInfo: "Informazioni sul server"
|
||||||
statistics: "Statistiche"
|
statistics: "Statistiche"
|
||||||
clearQueue: "Svuota coda"
|
clearQueue: "Svuota coda"
|
||||||
clearQueueConfirmTitle: "Vuoi davvero svuotare la coda?"
|
clearQueueConfirmTitle: "Vuoi davvero svuotare la coda?"
|
||||||
@@ -445,7 +445,7 @@ exploreFediverse: "Esplora il Fediverso"
|
|||||||
popularTags: "Hashtag popolari"
|
popularTags: "Hashtag popolari"
|
||||||
userList: "Liste"
|
userList: "Liste"
|
||||||
about: "Informazioni"
|
about: "Informazioni"
|
||||||
aboutMisskey: "Informazioni di Misskey"
|
aboutMisskey: "A proposito di Misskey"
|
||||||
administrator: "Amministratore"
|
administrator: "Amministratore"
|
||||||
token: "Token"
|
token: "Token"
|
||||||
2fa: "Autenticazione a due fattori"
|
2fa: "Autenticazione a due fattori"
|
||||||
@@ -893,7 +893,7 @@ searchResult: "Risultati della Ricerca"
|
|||||||
hashtags: "Hashtag"
|
hashtags: "Hashtag"
|
||||||
troubleshooting: "Risoluzione problemi"
|
troubleshooting: "Risoluzione problemi"
|
||||||
useBlurEffect: "Utilizza effetto sfocatura"
|
useBlurEffect: "Utilizza effetto sfocatura"
|
||||||
learnMore: "Più dettagli"
|
learnMore: "Per saperne di più"
|
||||||
misskeyUpdated: "Misskey è stato aggiornato!"
|
misskeyUpdated: "Misskey è stato aggiornato!"
|
||||||
whatIsNew: "Informazioni sull'aggiornamento"
|
whatIsNew: "Informazioni sull'aggiornamento"
|
||||||
translate: "Traduci"
|
translate: "Traduci"
|
||||||
@@ -901,7 +901,7 @@ translatedFrom: "Traduzione da {x}"
|
|||||||
accountDeletionInProgress: "È in corso l'eliminazione del profilo"
|
accountDeletionInProgress: "È in corso l'eliminazione del profilo"
|
||||||
usernameInfo: "Un nome per identificare univocamente il tuo profilo sull'istanza. Puoi utilizzare caratteri alfanumerici maiuscoli, minuscoli e il trattino basso (_). Non potrai cambiare nome utente in seguito."
|
usernameInfo: "Un nome per identificare univocamente il tuo profilo sull'istanza. Puoi utilizzare caratteri alfanumerici maiuscoli, minuscoli e il trattino basso (_). Non potrai cambiare nome utente in seguito."
|
||||||
aiChanMode: "Modalità Ai"
|
aiChanMode: "Modalità Ai"
|
||||||
devMode: "Modalità sviluppatori"
|
devMode: "Modalità sviluppo"
|
||||||
keepCw: "Mostra i contenuti espliciti"
|
keepCw: "Mostra i contenuti espliciti"
|
||||||
pubSub: "Publish/Subscribe del profilo"
|
pubSub: "Publish/Subscribe del profilo"
|
||||||
lastCommunication: "La comunicazione più recente"
|
lastCommunication: "La comunicazione più recente"
|
||||||
@@ -1049,7 +1049,7 @@ permissionDeniedError: "Errore, attività non autorizzata"
|
|||||||
permissionDeniedErrorDescription: "Non si dispone dell'autorizzazione per eseguire questa operazione."
|
permissionDeniedErrorDescription: "Non si dispone dell'autorizzazione per eseguire questa operazione."
|
||||||
preset: "Preimpostato"
|
preset: "Preimpostato"
|
||||||
selectFromPresets: "Seleziona preimpostato"
|
selectFromPresets: "Seleziona preimpostato"
|
||||||
achievements: "Obiettivi raggiunti"
|
achievements: "Conquiste"
|
||||||
gotInvalidResponseError: "Risposta del server non valida"
|
gotInvalidResponseError: "Risposta del server non valida"
|
||||||
gotInvalidResponseErrorDescription: "Il server potrebbe essere irraggiungibile o in manutenzione. Riprova più tardi."
|
gotInvalidResponseErrorDescription: "Il server potrebbe essere irraggiungibile o in manutenzione. Riprova più tardi."
|
||||||
thisPostMayBeAnnoying: "Questa nota potrebbe essere offensiva"
|
thisPostMayBeAnnoying: "Questa nota potrebbe essere offensiva"
|
||||||
@@ -1311,6 +1311,10 @@ federationSpecified: "Questo server è federato solo con istanze specifiche del
|
|||||||
federationDisabled: "Questo server ha la federazione disabilitata. Non puoi interagire con profili provenienti da altri server."
|
federationDisabled: "Questo server ha la federazione disabilitata. Non puoi interagire con profili provenienti da altri server."
|
||||||
confirmOnReact: "Confermare le reazioni"
|
confirmOnReact: "Confermare le reazioni"
|
||||||
reactAreYouSure: "Vuoi davvero reagire con {emoji} ?"
|
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:
|
_accountSettings:
|
||||||
requireSigninToViewContents: "Per vedere il contenuto, è necessaria l'iscrizione"
|
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."
|
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."
|
||||||
@@ -1447,9 +1451,9 @@ _initialTutorial:
|
|||||||
description: "Queste sono solamente alcune delle funzionalità principali di Misskey. Per ulteriori informazioni, {link}."
|
description: "Queste sono solamente alcune delle funzionalità principali di Misskey. Per ulteriori informazioni, {link}."
|
||||||
_timelineDescription:
|
_timelineDescription:
|
||||||
home: "Nella Timeline Home, la tua cronologia principale, puoi vedere le Note provenienti dai profili che segui (Following)."
|
home: "Nella Timeline Home, la tua cronologia principale, puoi vedere le Note provenienti dai profili che segui (Following)."
|
||||||
local: "La Timeline Locale, è una cronologia di Note pubblicate da tutti i profili iscritti su questo server."
|
local: "La Timeline Locale è un flusso di Note pubblicate dai profili iscritti a questo server."
|
||||||
social: "La Timeline Sociale, unisce in ordine cronologico l'elenco di Note presenti nella Timeline Home e quella Locale."
|
social: "La Timeline Sociale elenca, in ordine cronologico, il flusso di Note nella Timeline Home e Locale."
|
||||||
global: "La Timeline Federata ti consente di vedere le Note pubblicate dai profili di tutti gli altri server federati a questo."
|
global: "Nella Timeline Federata trovi il flusso di Note provenienti da profili iscritti ad altri server, federati a questo."
|
||||||
_serverRules:
|
_serverRules:
|
||||||
description: "In Europa è necessario mostrare l'informativa sul trattamento dei dati personali, prima della registrazione al servizio."
|
description: "In Europa è necessario mostrare l'informativa sul trattamento dei dati personali, prima della registrazione al servizio."
|
||||||
_serverSettings:
|
_serverSettings:
|
||||||
@@ -1473,8 +1477,8 @@ _serverSettings:
|
|||||||
_accountMigration:
|
_accountMigration:
|
||||||
moveFrom: "Migra un altro profilo dentro a questo"
|
moveFrom: "Migra un altro profilo dentro a questo"
|
||||||
moveFromSub: "Crea un alias verso un altro profilo remoto"
|
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@istanza.it"
|
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"
|
moveTo: "Migrare questo profilo verso un un altro"
|
||||||
moveToLabel: "Profilo verso cui migrare"
|
moveToLabel: "Profilo verso cui migrare"
|
||||||
moveCannotBeUndone: "La migrazione è irreversibile, non può essere interrotta o annullata."
|
moveCannotBeUndone: "La migrazione è irreversibile, non può essere interrotta o annullata."
|
||||||
@@ -1550,13 +1554,13 @@ _achievements:
|
|||||||
title: "Principiante III"
|
title: "Principiante III"
|
||||||
description: "Hai totalizzato 15 accessi!"
|
description: "Hai totalizzato 15 accessi!"
|
||||||
_login30:
|
_login30:
|
||||||
title: "Misskist I"
|
title: "Missalcolista I"
|
||||||
description: "Hai totalizzato 30 accessi!"
|
description: "Hai totalizzato 30 accessi!"
|
||||||
_login60:
|
_login60:
|
||||||
title: "Misskeist II"
|
title: "Missalcolista II"
|
||||||
description: "Hai totalizzato 60 accessi!"
|
description: "Hai totalizzato 60 accessi!"
|
||||||
_login100:
|
_login100:
|
||||||
title: "Misskeist III"
|
title: "Missalcolista III"
|
||||||
description: "Hai totalizzato 100 accessi!"
|
description: "Hai totalizzato 100 accessi!"
|
||||||
flavor: "Violent Misskeist"
|
flavor: "Violent Misskeist"
|
||||||
_login200:
|
_login200:
|
||||||
@@ -1642,10 +1646,10 @@ _achievements:
|
|||||||
description: "Hai superato i 1.000 profili Follower"
|
description: "Hai superato i 1.000 profili Follower"
|
||||||
_collectAchievements30:
|
_collectAchievements30:
|
||||||
title: "Collezionista di successi"
|
title: "Collezionista di successi"
|
||||||
description: "Hai raggiunto 30 obiettivi"
|
description: "Hai raggiunto 30 conquiste"
|
||||||
_viewAchievements3min:
|
_viewAchievements3min:
|
||||||
title: "Mi piacciono i risultati"
|
title: "Mi piacciono i risultati"
|
||||||
description: "Guarda la tua collezione di obiettivi per almeno 3 minuti"
|
description: "Ammira la tua collezione di conquiste per almeno 3 minuti"
|
||||||
_iLoveMisskey:
|
_iLoveMisskey:
|
||||||
title: "I LOVE Misskey"
|
title: "I LOVE Misskey"
|
||||||
description: "Pubblica «I ♥ #Misskey»"
|
description: "Pubblica «I ♥ #Misskey»"
|
||||||
@@ -1910,7 +1914,7 @@ _registry:
|
|||||||
domain: "Dominio"
|
domain: "Dominio"
|
||||||
createKey: "Crea chiave"
|
createKey: "Crea chiave"
|
||||||
_aboutMisskey:
|
_aboutMisskey:
|
||||||
about: "Misskey è un software libero e open source, sviluppato da syuilo dal 2014."
|
about: "Misskey è software libero, open source, sviluppato da Syuilo fin dal lontano 2014."
|
||||||
contributors: "Principali sostenitori"
|
contributors: "Principali sostenitori"
|
||||||
allContributors: "Tutti i sostenitori"
|
allContributors: "Tutti i sostenitori"
|
||||||
source: "Codice sorgente"
|
source: "Codice sorgente"
|
||||||
@@ -2237,7 +2241,7 @@ _widgets:
|
|||||||
userList: "Elenco utenti"
|
userList: "Elenco utenti"
|
||||||
_userList:
|
_userList:
|
||||||
chooseList: "Seleziona una lista"
|
chooseList: "Seleziona una lista"
|
||||||
clicker: "Cliccaggio"
|
clicker: "Cliccheria"
|
||||||
birthdayFollowings: "Compleanni del giorno"
|
birthdayFollowings: "Compleanni del giorno"
|
||||||
_cw:
|
_cw:
|
||||||
hide: "Nascondere"
|
hide: "Nascondere"
|
||||||
@@ -2300,7 +2304,7 @@ _profile:
|
|||||||
metadataContent: "Contenuto"
|
metadataContent: "Contenuto"
|
||||||
changeAvatar: "Modifica immagine profilo"
|
changeAvatar: "Modifica immagine profilo"
|
||||||
changeBanner: "Cambia intestazione"
|
changeBanner: "Cambia intestazione"
|
||||||
verifiedLinkDescription: "Puoi verificare il tuo profilo mostrando una icona. Devi inserire la URL alla pagina che contiene un link al tuo profilo."
|
verifiedLinkDescription: "Puoi verificare il tuo profilo mostrando una icona. Devi inserire la URL alla pagina che contiene un link al tuo profilo.\nPer verificare il profilo tramite la spunta di conferma, devi inserire la url alla pagina che contiene un link al tuo profilo Misskey. Deve avere attributo rel='me'."
|
||||||
avatarDecorationMax: "Puoi aggiungere fino a {max} decorazioni."
|
avatarDecorationMax: "Puoi aggiungere fino a {max} decorazioni."
|
||||||
followedMessage: "Messaggio, quando qualcuno ti segue"
|
followedMessage: "Messaggio, quando qualcuno ti segue"
|
||||||
followedMessageDescription: "Puoi impostare un breve messaggio da mostrare agli altri profili quando ti seguono."
|
followedMessageDescription: "Puoi impostare un breve messaggio da mostrare agli altri profili quando ti seguono."
|
||||||
@@ -2594,6 +2598,7 @@ _moderationLogTypes:
|
|||||||
deletePage: "Pagina eliminata"
|
deletePage: "Pagina eliminata"
|
||||||
deleteFlash: "Play eliminato"
|
deleteFlash: "Play eliminato"
|
||||||
deleteGalleryPost: "Eliminazione pubblicazione nella Galleria"
|
deleteGalleryPost: "Eliminazione pubblicazione nella Galleria"
|
||||||
|
updateProxyAccountDescription: "Aggiornata la descrizione del profilo proxy"
|
||||||
_fileViewer:
|
_fileViewer:
|
||||||
title: "Dettagli del file"
|
title: "Dettagli del file"
|
||||||
type: "Tipo di file"
|
type: "Tipo di file"
|
||||||
@@ -2811,8 +2816,8 @@ _selfXssPrevention:
|
|||||||
description2: "Se non sai esattamente cosa stai facendo, %c smetti subito e chiudi questa finestra."
|
description2: "Se non sai esattamente cosa stai facendo, %c smetti subito e chiudi questa finestra."
|
||||||
description3: "Per favore, controlla questo collegamento per avere maggiori dettagli. {link}"
|
description3: "Per favore, controlla questo collegamento per avere maggiori dettagli. {link}"
|
||||||
_followRequest:
|
_followRequest:
|
||||||
recieved: "Ricezione richiesta di Follow"
|
recieved: "Richieste in ingresso"
|
||||||
sent: "Richiesta di Follow, inviata"
|
sent: "Richieste in uscita"
|
||||||
_remoteLookupErrors:
|
_remoteLookupErrors:
|
||||||
_federationNotAllowed:
|
_federationNotAllowed:
|
||||||
title: "Server irraggiungibile"
|
title: "Server irraggiungibile"
|
||||||
@@ -2857,4 +2862,8 @@ _bootErrors:
|
|||||||
_search:
|
_search:
|
||||||
searchScopeAll: "Tutte"
|
searchScopeAll: "Tutte"
|
||||||
searchScopeLocal: "Locale"
|
searchScopeLocal: "Locale"
|
||||||
|
searchScopeServer: "Specifiche del server"
|
||||||
searchScopeUser: "Profilo specifico"
|
searchScopeUser: "Profilo specifico"
|
||||||
|
pleaseEnterServerHost: "Inserire il nome host"
|
||||||
|
pleaseSelectUser: "Per favore, seleziona un profilo"
|
||||||
|
serverHostPlaceholder: "Es: misskey.example.com"
|
||||||
|
@@ -1238,7 +1238,7 @@ releaseToRefresh: "離してリロード"
|
|||||||
refreshing: "リロード中"
|
refreshing: "リロード中"
|
||||||
pullDownToRefresh: "引っ張ってリロード"
|
pullDownToRefresh: "引っ張ってリロード"
|
||||||
disableStreamingTimeline: "タイムラインのリアルタイム更新を無効にする"
|
disableStreamingTimeline: "タイムラインのリアルタイム更新を無効にする"
|
||||||
useGroupedNotifications: "通知をグルーピングして表示する"
|
useGroupedNotifications: "通知をグルーピング"
|
||||||
signupPendingError: "メールアドレスの確認中に問題が発生しました。リンクの有効期限が切れている可能性があります。"
|
signupPendingError: "メールアドレスの確認中に問題が発生しました。リンクの有効期限が切れている可能性があります。"
|
||||||
cwNotationRequired: "「内容を隠す」がオンの場合は注釈の記述が必要です。"
|
cwNotationRequired: "「内容を隠す」がオンの場合は注釈の記述が必要です。"
|
||||||
doReaction: "リアクションする"
|
doReaction: "リアクションする"
|
||||||
@@ -1313,6 +1313,8 @@ confirmOnReact: "リアクションする際に確認する"
|
|||||||
reactAreYouSure: "\" {emoji} \" をリアクションしますか?"
|
reactAreYouSure: "\" {emoji} \" をリアクションしますか?"
|
||||||
markAsSensitiveConfirm: "このメディアをセンシティブとして設定しますか?"
|
markAsSensitiveConfirm: "このメディアをセンシティブとして設定しますか?"
|
||||||
unmarkAsSensitiveConfirm: "このメディアのセンシティブ指定を解除しますか?"
|
unmarkAsSensitiveConfirm: "このメディアのセンシティブ指定を解除しますか?"
|
||||||
|
preferences: "環境設定"
|
||||||
|
accessibility: "アクセシビリティ"
|
||||||
|
|
||||||
_accountSettings:
|
_accountSettings:
|
||||||
requireSigninToViewContents: "コンテンツの表示にログインを必須にする"
|
requireSigninToViewContents: "コンテンツの表示にログインを必須にする"
|
||||||
|
@@ -1311,6 +1311,10 @@ federationSpecified: "此服务器已开启联合白名单。只能与管理员
|
|||||||
federationDisabled: "此服务器已禁用联合。无法与其它服务器上的用户通信。"
|
federationDisabled: "此服务器已禁用联合。无法与其它服务器上的用户通信。"
|
||||||
confirmOnReact: "发送回应前需要确认"
|
confirmOnReact: "发送回应前需要确认"
|
||||||
reactAreYouSure: "要用「{emoji}」进行回应吗?"
|
reactAreYouSure: "要用「{emoji}」进行回应吗?"
|
||||||
|
markAsSensitiveConfirm: "要将此媒体标记为敏感吗?"
|
||||||
|
unmarkAsSensitiveConfirm: "要将此媒体解除敏感标记吗?"
|
||||||
|
preferences: "设置"
|
||||||
|
accessibility: "辅助功能"
|
||||||
_accountSettings:
|
_accountSettings:
|
||||||
requireSigninToViewContents: "需要登录才能显示内容"
|
requireSigninToViewContents: "需要登录才能显示内容"
|
||||||
requireSigninToViewContentsDescription1: "您发布的所有帖子将变成需要登入后才会显示。有望防止爬虫收集各种信息。"
|
requireSigninToViewContentsDescription1: "您发布的所有帖子将变成需要登入后才会显示。有望防止爬虫收集各种信息。"
|
||||||
@@ -2594,6 +2598,7 @@ _moderationLogTypes:
|
|||||||
deletePage: "删除了页面"
|
deletePage: "删除了页面"
|
||||||
deleteFlash: "删除了 Play"
|
deleteFlash: "删除了 Play"
|
||||||
deleteGalleryPost: "删除了图库稿件"
|
deleteGalleryPost: "删除了图库稿件"
|
||||||
|
updateProxyAccountDescription: "更新代理账户的说明"
|
||||||
_fileViewer:
|
_fileViewer:
|
||||||
title: "文件信息"
|
title: "文件信息"
|
||||||
type: "文件类型"
|
type: "文件类型"
|
||||||
@@ -2857,4 +2862,8 @@ _bootErrors:
|
|||||||
_search:
|
_search:
|
||||||
searchScopeAll: "全部"
|
searchScopeAll: "全部"
|
||||||
searchScopeLocal: "本地"
|
searchScopeLocal: "本地"
|
||||||
searchScopeUser: "用户指定"
|
searchScopeServer: "指定服务器"
|
||||||
|
searchScopeUser: "指定用户"
|
||||||
|
pleaseEnterServerHost: "请填写服务器主机名"
|
||||||
|
pleaseSelectUser: "请选择用户"
|
||||||
|
serverHostPlaceholder: "如:misskey.example.com"
|
||||||
|
@@ -1311,6 +1311,10 @@ federationSpecified: "此伺服器以白名單聯邦的方式運作。除了管
|
|||||||
federationDisabled: "此伺服器未開啟站台聯邦。無法與其他伺服器上的使用者互動。"
|
federationDisabled: "此伺服器未開啟站台聯邦。無法與其他伺服器上的使用者互動。"
|
||||||
confirmOnReact: "反應時確認"
|
confirmOnReact: "反應時確認"
|
||||||
reactAreYouSure: "用「 {emoji} 」反應嗎?"
|
reactAreYouSure: "用「 {emoji} 」反應嗎?"
|
||||||
|
markAsSensitiveConfirm: "要將這個媒體設定為敏感嗎?"
|
||||||
|
unmarkAsSensitiveConfirm: "要解除這個媒體的敏感設定嗎?"
|
||||||
|
preferences: "環境設定"
|
||||||
|
accessibility: "輔助工具"
|
||||||
_accountSettings:
|
_accountSettings:
|
||||||
requireSigninToViewContents: "須登入以顯示內容"
|
requireSigninToViewContents: "須登入以顯示內容"
|
||||||
requireSigninToViewContentsDescription1: "必須登入才會顯示您建立的貼文等內容。可望有效防止資訊被爬蟲蒐集。"
|
requireSigninToViewContentsDescription1: "必須登入才會顯示您建立的貼文等內容。可望有效防止資訊被爬蟲蒐集。"
|
||||||
@@ -2594,6 +2598,7 @@ _moderationLogTypes:
|
|||||||
deletePage: "刪除頁面"
|
deletePage: "刪除頁面"
|
||||||
deleteFlash: "刪除 Play"
|
deleteFlash: "刪除 Play"
|
||||||
deleteGalleryPost: "刪除相簿的貼文"
|
deleteGalleryPost: "刪除相簿的貼文"
|
||||||
|
updateProxyAccountDescription: "更新代理帳戶的說明"
|
||||||
_fileViewer:
|
_fileViewer:
|
||||||
title: "檔案詳細資訊"
|
title: "檔案詳細資訊"
|
||||||
type: "檔案類型 "
|
type: "檔案類型 "
|
||||||
@@ -2857,4 +2862,8 @@ _bootErrors:
|
|||||||
_search:
|
_search:
|
||||||
searchScopeAll: "全部"
|
searchScopeAll: "全部"
|
||||||
searchScopeLocal: "本地"
|
searchScopeLocal: "本地"
|
||||||
|
searchScopeServer: "指定伺服器"
|
||||||
searchScopeUser: "指定使用者"
|
searchScopeUser: "指定使用者"
|
||||||
|
pleaseEnterServerHost: "請輸入伺服器的主機名稱"
|
||||||
|
pleaseSelectUser: "請選擇使用者"
|
||||||
|
serverHostPlaceholder: "例:misskey.example.com"
|
||||||
|
10
package.json
10
package.json
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "2025.3.0-beta.0",
|
"version": "2025.3.1-beta.0",
|
||||||
"codename": "nasubi",
|
"codename": "nasubi",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/misskey-dev/misskey.git"
|
"url": "https://github.com/misskey-dev/misskey.git"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.15.4",
|
"packageManager": "pnpm@10.6.1",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/frontend-shared",
|
"packages/frontend-shared",
|
||||||
"packages/frontend",
|
"packages/frontend",
|
||||||
@@ -73,9 +73,15 @@
|
|||||||
"eslint": "9.20.1",
|
"eslint": "9.20.1",
|
||||||
"globals": "15.15.0",
|
"globals": "15.15.0",
|
||||||
"ncp": "2.0.0",
|
"ncp": "2.0.0",
|
||||||
|
"pnpm": "10.6.1",
|
||||||
"start-server-and-test": "2.0.10"
|
"start-server-and-test": "2.0.10"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@tensorflow/tfjs-core": "4.22.0"
|
"@tensorflow/tfjs-core": "4.22.0"
|
||||||
|
},
|
||||||
|
"pnpm": {
|
||||||
|
"overrides": {
|
||||||
|
"@aiscript-dev/aiscript-languageserver": "-"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
}
|
||||||
|
}
|
@@ -46,6 +46,8 @@ export class S3Service {
|
|||||||
tls: meta.objectStorageUseSSL,
|
tls: meta.objectStorageUseSSL,
|
||||||
forcePathStyle: meta.objectStorageEndpoint ? meta.objectStorageS3ForcePathStyle : false, // AWS with endPoint omitted
|
forcePathStyle: meta.objectStorageEndpoint ? meta.objectStorageS3ForcePathStyle : false, // AWS with endPoint omitted
|
||||||
requestHandler: new NodeHttpHandler(handlerOption),
|
requestHandler: new NodeHttpHandler(handlerOption),
|
||||||
|
requestChecksumCalculation: 'WHEN_REQUIRED',
|
||||||
|
responseChecksumValidation: 'WHEN_REQUIRED',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -751,7 +751,7 @@ export class ActivityPubServerService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// follow
|
// 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
|
// 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.
|
// check if the following exists and only check if the follow request exists.
|
||||||
|
|
||||||
|
@@ -497,7 +497,7 @@ export class FileServerService {
|
|||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async downloadAndDetectTypeFromUrl(url: string): Promise<
|
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();
|
const [path, cleanup] = await createTemp();
|
||||||
try {
|
try {
|
||||||
|
@@ -95,6 +95,7 @@ interface ClientInformation {
|
|||||||
id: string;
|
id: string;
|
||||||
redirectUris: string[];
|
redirectUris: string[];
|
||||||
name: string;
|
name: string;
|
||||||
|
logo: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://indieauth.spec.indieweb.org/#client-information-discovery
|
// https://indieauth.spec.indieweb.org/#client-information-discovery
|
||||||
@@ -124,11 +125,19 @@ async function discoverClientInformation(logger: Logger, httpRequestService: Htt
|
|||||||
redirectUris.push(...[...fragment.querySelectorAll<HTMLLinkElement>('link[rel=redirect_uri][href]')].map(el => el.href));
|
redirectUris.push(...[...fragment.querySelectorAll<HTMLLinkElement>('link[rel=redirect_uri][href]')].map(el => el.href));
|
||||||
|
|
||||||
let name = id;
|
let name = id;
|
||||||
|
let logo: string | null = null;
|
||||||
if (text) {
|
if (text) {
|
||||||
const microformats = mf2(text, { baseUrl: res.url });
|
const microformats = mf2(text, { baseUrl: res.url });
|
||||||
const nameProperty = microformats.items.find(item => item.type?.includes('h-app') && item.properties.url.includes(id))?.properties.name[0];
|
const correspondingProperties = microformats.items.find(item => item.type?.includes('h-app') && item.properties.url.includes(id));
|
||||||
if (typeof nameProperty === 'string') {
|
if (correspondingProperties) {
|
||||||
name = nameProperty;
|
const nameProperty = correspondingProperties.properties.name?.[0];
|
||||||
|
if (typeof nameProperty === 'string') {
|
||||||
|
name = nameProperty;
|
||||||
|
}
|
||||||
|
const logoProperty = correspondingProperties.properties.logo?.[0];
|
||||||
|
if (typeof logoProperty === 'string') {
|
||||||
|
logo = logoProperty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,6 +145,7 @@ async function discoverClientInformation(logger: Logger, httpRequestService: Htt
|
|||||||
id,
|
id,
|
||||||
redirectUris: redirectUris.map(uri => new URL(uri, res.url).toString()),
|
redirectUris: redirectUris.map(uri => new URL(uri, res.url).toString()),
|
||||||
name: typeof name === 'string' ? name : id,
|
name: typeof name === 'string' ? name : id,
|
||||||
|
logo,
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -379,6 +389,7 @@ export class OAuth2ProviderService {
|
|||||||
return await reply.view('oauth', {
|
return await reply.view('oauth', {
|
||||||
transactionId: oauth2.transactionID,
|
transactionId: oauth2.transactionID,
|
||||||
clientName: oauth2.client.name,
|
clientName: oauth2.client.name,
|
||||||
|
clientLogo: oauth2.client.logo,
|
||||||
scope: oauth2.req.scope.join(' '),
|
scope: oauth2.req.scope.join(' '),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -6,4 +6,6 @@ block meta
|
|||||||
//- XXX: Remove navigation bar in auth page?
|
//- XXX: Remove navigation bar in auth page?
|
||||||
meta(name='misskey:oauth:transaction-id' content=transactionId)
|
meta(name='misskey:oauth:transaction-id' content=transactionId)
|
||||||
meta(name='misskey:oauth:client-name' content=clientName)
|
meta(name='misskey:oauth:client-name' content=clientName)
|
||||||
|
if clientLogo
|
||||||
|
meta(name='misskey:oauth:client-logo' content=clientLogo)
|
||||||
meta(name='misskey:oauth:scope' content=scope)
|
meta(name='misskey:oauth:scope' content=scope)
|
||||||
|
@@ -17,7 +17,6 @@ services:
|
|||||||
- ./.config/docker.env
|
- ./.config/docker.env
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
- COREPACK_DEFAULT_TO_LATEST=0
|
|
||||||
volumes:
|
volumes:
|
||||||
- type: bind
|
- type: bind
|
||||||
source: ../../../built
|
source: ../../../built
|
||||||
@@ -82,7 +81,7 @@ services:
|
|||||||
working_dir: /misskey
|
working_dir: /misskey
|
||||||
command: >
|
command: >
|
||||||
bash -c "
|
bash -c "
|
||||||
corepack enable && corepack prepare
|
npm install -g pnpm
|
||||||
pnpm -F backend migrate
|
pnpm -F backend migrate
|
||||||
pnpm -F backend start
|
pnpm -F backend start
|
||||||
"
|
"
|
||||||
|
@@ -9,7 +9,7 @@ services:
|
|||||||
service: misskey
|
service: misskey
|
||||||
command: >
|
command: >
|
||||||
bash -c "
|
bash -c "
|
||||||
corepack enable && corepack prepare
|
npm install -g pnpm
|
||||||
pnpm -F backend i
|
pnpm -F backend i
|
||||||
pnpm -F misskey-js i
|
pnpm -F misskey-js i
|
||||||
pnpm -F misskey-reversi i
|
pnpm -F misskey-reversi i
|
||||||
@@ -29,7 +29,6 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- NODE_ENV=development
|
- NODE_ENV=development
|
||||||
- NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/rootCA.crt
|
- NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/rootCA.crt
|
||||||
- COREPACK_DEFAULT_TO_LATEST=0
|
|
||||||
volumes:
|
volumes:
|
||||||
- type: bind
|
- type: bind
|
||||||
source: ../package.json
|
source: ../package.json
|
||||||
@@ -78,7 +77,7 @@ services:
|
|||||||
working_dir: /misskey
|
working_dir: /misskey
|
||||||
entrypoint: >
|
entrypoint: >
|
||||||
bash -c '
|
bash -c '
|
||||||
corepack enable && corepack prepare
|
npm install -g pnpm
|
||||||
pnpm -F misskey-js i --frozen-lockfile
|
pnpm -F misskey-js i --frozen-lockfile
|
||||||
pnpm -F backend i --frozen-lockfile
|
pnpm -F backend i --frozen-lockfile
|
||||||
exec "$0" "$@"
|
exec "$0" "$@"
|
||||||
@@ -90,8 +89,6 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
redis.test:
|
redis.test:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
environment:
|
|
||||||
- COREPACK_DEFAULT_TO_LATEST=0
|
|
||||||
volumes:
|
volumes:
|
||||||
- type: bind
|
- type: bind
|
||||||
source: ../package.json
|
source: ../package.json
|
||||||
@@ -120,7 +117,7 @@ services:
|
|||||||
working_dir: /misskey
|
working_dir: /misskey
|
||||||
command: >
|
command: >
|
||||||
bash -c "
|
bash -c "
|
||||||
corepack enable && corepack prepare
|
npm install -g pnpm
|
||||||
pnpm -F backend i --frozen-lockfile
|
pnpm -F backend i --frozen-lockfile
|
||||||
pnpm exec tsc -p ./packages/backend/test-federation
|
pnpm exec tsc -p ./packages/backend/test-federation
|
||||||
node ./packages/backend/test-federation/built/daemon.js
|
node ./packages/backend/test-federation/built/daemon.js
|
||||||
|
@@ -72,11 +72,12 @@ const clientConfig: ModuleOptions<'client_id'> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function getMeta(html: string): { transactionId: string | undefined, clientName: string | undefined } {
|
function getMeta(html: string): { transactionId: string | undefined, clientName: string | undefined, clientLogo: string | undefined } {
|
||||||
const fragment = JSDOM.fragment(html);
|
const fragment = JSDOM.fragment(html);
|
||||||
return {
|
return {
|
||||||
transactionId: fragment.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:transaction-id"]')?.content,
|
transactionId: fragment.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:transaction-id"]')?.content,
|
||||||
clientName: fragment.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:client-name"]')?.content,
|
clientName: fragment.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:client-name"]')?.content,
|
||||||
|
clientLogo: fragment.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:client-logo"]')?.content,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -915,6 +916,59 @@ describe('OAuth', () => {
|
|||||||
assert.strictEqual(getMeta(await response.text()).clientName, `http://127.0.0.1:${clientPort}/`);
|
assert.strictEqual(getMeta(await response.text()).clientName, `http://127.0.0.1:${clientPort}/`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('With Logo', async () => {
|
||||||
|
sender = (reply): void => {
|
||||||
|
reply.header('Link', '</redirect>; rel="redirect_uri"');
|
||||||
|
reply.send(`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<div class="h-app">
|
||||||
|
<a href="/" class="u-url p-name">Misklient</a>
|
||||||
|
<img src="/logo.png" class="u-logo" />
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
reply.send();
|
||||||
|
};
|
||||||
|
|
||||||
|
const client = new AuthorizationCode(clientConfig);
|
||||||
|
|
||||||
|
const response = await fetch(client.authorizeURL({
|
||||||
|
redirect_uri,
|
||||||
|
scope: 'write:notes',
|
||||||
|
state: 'state',
|
||||||
|
code_challenge: 'code',
|
||||||
|
code_challenge_method: 'S256',
|
||||||
|
} as AuthorizationParamsExtended));
|
||||||
|
assert.strictEqual(response.status, 200);
|
||||||
|
const meta = getMeta(await response.text());
|
||||||
|
assert.strictEqual(meta.clientName, 'Misklient');
|
||||||
|
assert.strictEqual(meta.clientLogo, `http://127.0.0.1:${clientPort}/logo.png`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Missing Logo', async () => {
|
||||||
|
sender = (reply): void => {
|
||||||
|
reply.header('Link', '</redirect>; rel="redirect_uri"');
|
||||||
|
reply.send(`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<div class="h-app"><a href="/" class="u-url p-name">Misklient
|
||||||
|
`);
|
||||||
|
reply.send();
|
||||||
|
};
|
||||||
|
|
||||||
|
const client = new AuthorizationCode(clientConfig);
|
||||||
|
|
||||||
|
const response = await fetch(client.authorizeURL({
|
||||||
|
redirect_uri,
|
||||||
|
scope: 'write:notes',
|
||||||
|
state: 'state',
|
||||||
|
code_challenge: 'code',
|
||||||
|
code_challenge_method: 'S256',
|
||||||
|
} as AuthorizationParamsExtended));
|
||||||
|
assert.strictEqual(response.status, 200);
|
||||||
|
const meta = getMeta(await response.text());
|
||||||
|
assert.strictEqual(meta.clientName, 'Misklient');
|
||||||
|
assert.strictEqual(meta.clientLogo, undefined);
|
||||||
|
});
|
||||||
|
|
||||||
test('Mismatching URL in h-app', async () => {
|
test('Mismatching URL in h-app', async () => {
|
||||||
sender = (reply): void => {
|
sender = (reply): void => {
|
||||||
reply.header('Link', '</redirect>; rel="redirect_uri"');
|
reply.header('Link', '</redirect>; rel="redirect_uri"');
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
"@rollup/plugin-json": "6.1.0",
|
"@rollup/plugin-json": "6.1.0",
|
||||||
"@rollup/plugin-replace": "6.0.2",
|
"@rollup/plugin-replace": "6.0.2",
|
||||||
"@rollup/pluginutils": "5.1.4",
|
"@rollup/pluginutils": "5.1.4",
|
||||||
"@tabler/icons-webfont": "https://github.com/misskey-dev/tabler-icons/archive/refs/tags/3.30.0-mi.1932+ab127beee.tar.gz",
|
"@tabler/icons-webfont": "3.31.0",
|
||||||
"@twemoji/parser": "15.1.1",
|
"@twemoji/parser": "15.1.1",
|
||||||
"@vitejs/plugin-vue": "5.2.1",
|
"@vitejs/plugin-vue": "5.2.1",
|
||||||
"@vue/compiler-sfc": "3.5.13",
|
"@vue/compiler-sfc": "3.5.13",
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
"typescript": "5.8.2",
|
"typescript": "5.8.2",
|
||||||
"uuid": "11.1.0",
|
"uuid": "11.1.0",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"vite": "6.2.0",
|
"vite": "6.2.1",
|
||||||
"vue": "3.5.13"
|
"vue": "3.5.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -48,14 +48,14 @@
|
|||||||
"@types/ws": "8.18.0",
|
"@types/ws": "8.18.0",
|
||||||
"@typescript-eslint/eslint-plugin": "8.26.0",
|
"@typescript-eslint/eslint-plugin": "8.26.0",
|
||||||
"@typescript-eslint/parser": "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",
|
"@vue/runtime-core": "3.5.13",
|
||||||
"acorn": "8.14.0",
|
"acorn": "8.14.1",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"eslint-plugin-import": "2.31.0",
|
"eslint-plugin-import": "2.31.0",
|
||||||
"eslint-plugin-vue": "9.33.0",
|
"eslint-plugin-vue": "9.33.0",
|
||||||
"fast-glob": "3.3.3",
|
"fast-glob": "3.3.3",
|
||||||
"happy-dom": "17.2.2",
|
"happy-dom": "17.3.0",
|
||||||
"intersection-observer": "0.12.2",
|
"intersection-observer": "0.12.2",
|
||||||
"micromatch": "4.0.8",
|
"micromatch": "4.0.8",
|
||||||
"msw": "2.7.3",
|
"msw": "2.7.3",
|
||||||
|
@@ -39,6 +39,10 @@ const config = {
|
|||||||
if (~replacePluginForIsChromatic) {
|
if (~replacePluginForIsChromatic) {
|
||||||
config.plugins?.splice(replacePluginForIsChromatic, 1);
|
config.plugins?.splice(replacePluginForIsChromatic, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//pluginsからcreateSearchIndexを削除、複数あるかもしれないので全て削除
|
||||||
|
config.plugins = config.plugins?.filter((plugin: Plugin) => plugin && plugin.name !== 'createSearchIndex') ?? [];
|
||||||
|
|
||||||
return mergeConfig(config, {
|
return mergeConfig(config, {
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
|
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
|
|
||||||
<link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true" as="image" type="image/png" crossorigin="anonymous">
|
<link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true" as="image" type="image/png" crossorigin="anonymous">
|
||||||
<link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true" as="image" type="image/jpeg" crossorigin="anonymous">
|
<link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true" as="image" type="image/jpeg" crossorigin="anonymous">
|
||||||
<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@3.3.0/dist/tabler-icons.min.css">
|
<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@3.31.0/dist/tabler-icons.min.css">
|
||||||
<link rel="stylesheet" href="https://unpkg.com/@fontsource/m-plus-rounded-1c/index.css">
|
<link rel="stylesheet" href="https://unpkg.com/@fontsource/m-plus-rounded-1c/index.css">
|
||||||
<style>
|
<style>
|
||||||
html {
|
html {
|
||||||
|
1496
packages/frontend/lib/vite-plugin-create-search-index.ts
Normal file
1496
packages/frontend/lib/vite-plugin-create-search-index.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,7 @@
|
|||||||
"@rollup/plugin-replace": "6.0.2",
|
"@rollup/plugin-replace": "6.0.2",
|
||||||
"@rollup/pluginutils": "5.1.4",
|
"@rollup/pluginutils": "5.1.4",
|
||||||
"@syuilo/aiscript": "0.19.0",
|
"@syuilo/aiscript": "0.19.0",
|
||||||
"@tabler/icons-webfont": "https://github.com/misskey-dev/tabler-icons/archive/refs/tags/3.30.0-mi.1932+ab127beee.tar.gz",
|
"@tabler/icons-webfont": "3.31.0",
|
||||||
"@twemoji/parser": "15.1.1",
|
"@twemoji/parser": "15.1.1",
|
||||||
"@vitejs/plugin-vue": "5.2.1",
|
"@vitejs/plugin-vue": "5.2.1",
|
||||||
"@vue/compiler-sfc": "3.5.13",
|
"@vue/compiler-sfc": "3.5.13",
|
||||||
@@ -51,6 +51,7 @@
|
|||||||
"insert-text-at-cursor": "0.3.0",
|
"insert-text-at-cursor": "0.3.0",
|
||||||
"is-file-animated": "1.0.2",
|
"is-file-animated": "1.0.2",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
|
"magic-string": "0.30.17",
|
||||||
"matter-js": "0.20.0",
|
"matter-js": "0.20.0",
|
||||||
"mfm-js": "0.24.0",
|
"mfm-js": "0.24.0",
|
||||||
"misskey-bubble-game": "workspace:*",
|
"misskey-bubble-game": "workspace:*",
|
||||||
@@ -72,30 +73,30 @@
|
|||||||
"typescript": "5.8.2",
|
"typescript": "5.8.2",
|
||||||
"uuid": "11.1.0",
|
"uuid": "11.1.0",
|
||||||
"v-code-diff": "1.13.1",
|
"v-code-diff": "1.13.1",
|
||||||
"vite": "6.2.0",
|
"vite": "6.2.1",
|
||||||
"vue": "3.5.13",
|
"vue": "3.5.13",
|
||||||
"vuedraggable": "next"
|
"vuedraggable": "next"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/summaly": "5.2.0",
|
"@misskey-dev/summaly": "5.2.0",
|
||||||
"@storybook/addon-actions": "8.6.3",
|
"@storybook/addon-actions": "8.6.4",
|
||||||
"@storybook/addon-essentials": "8.6.3",
|
"@storybook/addon-essentials": "8.6.4",
|
||||||
"@storybook/addon-interactions": "8.6.3",
|
"@storybook/addon-interactions": "8.6.4",
|
||||||
"@storybook/addon-links": "8.6.3",
|
"@storybook/addon-links": "8.6.4",
|
||||||
"@storybook/addon-mdx-gfm": "8.6.3",
|
"@storybook/addon-mdx-gfm": "8.6.4",
|
||||||
"@storybook/addon-storysource": "8.6.3",
|
"@storybook/addon-storysource": "8.6.4",
|
||||||
"@storybook/blocks": "8.6.3",
|
"@storybook/blocks": "8.6.4",
|
||||||
"@storybook/components": "8.6.3",
|
"@storybook/components": "8.6.4",
|
||||||
"@storybook/core-events": "8.6.3",
|
"@storybook/core-events": "8.6.4",
|
||||||
"@storybook/manager-api": "8.6.3",
|
"@storybook/manager-api": "8.6.4",
|
||||||
"@storybook/preview-api": "8.6.3",
|
"@storybook/preview-api": "8.6.4",
|
||||||
"@storybook/react": "8.6.3",
|
"@storybook/react": "8.6.4",
|
||||||
"@storybook/react-vite": "8.6.3",
|
"@storybook/react-vite": "8.6.4",
|
||||||
"@storybook/test": "8.6.3",
|
"@storybook/test": "8.6.4",
|
||||||
"@storybook/theming": "8.6.3",
|
"@storybook/theming": "8.6.4",
|
||||||
"@storybook/types": "8.6.3",
|
"@storybook/types": "8.6.4",
|
||||||
"@storybook/vue3": "8.6.3",
|
"@storybook/vue3": "8.6.4",
|
||||||
"@storybook/vue3-vite": "8.6.3",
|
"@storybook/vue3-vite": "8.6.4",
|
||||||
"@testing-library/vue": "8.1.0",
|
"@testing-library/vue": "8.1.0",
|
||||||
"@types/canvas-confetti": "1.9.0",
|
"@types/canvas-confetti": "1.9.0",
|
||||||
"@types/estree": "1.0.6",
|
"@types/estree": "1.0.6",
|
||||||
@@ -110,15 +111,15 @@
|
|||||||
"@types/ws": "8.18.0",
|
"@types/ws": "8.18.0",
|
||||||
"@typescript-eslint/eslint-plugin": "8.26.0",
|
"@typescript-eslint/eslint-plugin": "8.26.0",
|
||||||
"@typescript-eslint/parser": "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",
|
"@vue/runtime-core": "3.5.13",
|
||||||
"acorn": "8.14.0",
|
"acorn": "8.14.1",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "14.1.0",
|
"cypress": "14.1.0",
|
||||||
"eslint-plugin-import": "2.31.0",
|
"eslint-plugin-import": "2.31.0",
|
||||||
"eslint-plugin-vue": "9.33.0",
|
"eslint-plugin-vue": "9.33.0",
|
||||||
"fast-glob": "3.3.3",
|
"fast-glob": "3.3.3",
|
||||||
"happy-dom": "17.2.2",
|
"happy-dom": "17.3.0",
|
||||||
"intersection-observer": "0.12.2",
|
"intersection-observer": "0.12.2",
|
||||||
"micromatch": "4.0.8",
|
"micromatch": "4.0.8",
|
||||||
"msw": "2.7.3",
|
"msw": "2.7.3",
|
||||||
@@ -129,10 +130,10 @@
|
|||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"seedrandom": "3.0.5",
|
"seedrandom": "3.0.5",
|
||||||
"start-server-and-test": "2.0.10",
|
"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",
|
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
||||||
"vite-plugin-turbosnap": "1.0.3",
|
"vite-plugin-turbosnap": "1.0.3",
|
||||||
"vitest": "3.0.7",
|
"vitest": "3.0.8",
|
||||||
"vitest-fetch-mock": "0.4.5",
|
"vitest-fetch-mock": "0.4.5",
|
||||||
"vue-component-type-helpers": "2.2.8",
|
"vue-component-type-helpers": "2.2.8",
|
||||||
"vue-eslint-parser": "9.4.3",
|
"vue-eslint-parser": "9.4.3",
|
||||||
|
@@ -24,7 +24,8 @@ defineProps<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.disabled {
|
.disabled {
|
||||||
opacity: 0.7;
|
opacity: 0.3;
|
||||||
|
filter: saturate(0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cover {
|
.cover {
|
||||||
@@ -34,7 +35,7 @@ defineProps<{
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
cursor: not-allowed;
|
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-size: auto auto;
|
||||||
background-image: repeating-linear-gradient(135deg, transparent, transparent 10px, var(--color) 4px, var(--color) 14px);
|
background-image: repeating-linear-gradient(135deg, transparent, transparent 10px, var(--color) 4px, var(--color) 14px);
|
||||||
}
|
}
|
@@ -91,6 +91,14 @@ const buttonsRight = computed(() => {
|
|||||||
});
|
});
|
||||||
const reloadCount = ref(0);
|
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 => {
|
windowRouter.addListener('push', ctx => {
|
||||||
history.value.push({ path: ctx.path, key: ctx.key });
|
history.value.push({ path: ctx.path, key: ctx.key });
|
||||||
});
|
});
|
||||||
@@ -101,7 +109,8 @@ windowRouter.addListener('replace', ctx => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
windowRouter.addListener('change', 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({
|
analytics.page({
|
||||||
path: ctx.path,
|
path: ctx.path,
|
||||||
title: ctx.path,
|
title: ctx.path,
|
||||||
@@ -111,6 +120,7 @@ windowRouter.addListener('change', ctx => {
|
|||||||
windowRouter.init();
|
windowRouter.init();
|
||||||
|
|
||||||
provide('router', windowRouter);
|
provide('router', windowRouter);
|
||||||
|
provide('inAppSearchMarkerId', searchMarkerId);
|
||||||
provideMetadataReceiver((metadataGetter) => {
|
provideMetadataReceiver((metadataGetter) => {
|
||||||
const info = metadataGetter();
|
const info = metadataGetter();
|
||||||
pageMetadata.value = info;
|
pageMetadata.value = info;
|
||||||
|
@@ -104,18 +104,18 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed } from 'vue';
|
import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed } from 'vue';
|
||||||
|
import type { ShallowRef } from 'vue';
|
||||||
import * as mfm from 'mfm-js';
|
import * as mfm from 'mfm-js';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import insertTextAtCursor from 'insert-text-at-cursor';
|
import insertTextAtCursor from 'insert-text-at-cursor';
|
||||||
import { toASCII } from 'punycode.js';
|
import { toASCII } from 'punycode.js';
|
||||||
import { host, url } from '@@/js/config.js';
|
import { host, url } from '@@/js/config.js';
|
||||||
import type { ShallowRef } from 'vue';
|
|
||||||
import type { PostFormProps } from '@/types/post-form.js';
|
import type { PostFormProps } from '@/types/post-form.js';
|
||||||
import type { PollEditorModelValue } from '@/components/MkPollEditor.vue';
|
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
||||||
import MkNotePreview from '@/components/MkNotePreview.vue';
|
import MkNotePreview from '@/components/MkNotePreview.vue';
|
||||||
import XPostFormAttaches from '@/components/MkPostFormAttaches.vue';
|
import XPostFormAttaches from '@/components/MkPostFormAttaches.vue';
|
||||||
import MkPollEditor from '@/components/MkPollEditor.vue';
|
import MkPollEditor from '@/components/MkPollEditor.vue';
|
||||||
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
import type { PollEditorModelValue } from '@/components/MkPollEditor.vue';
|
||||||
import { erase, unique } from '@/scripts/array.js';
|
import { erase, unique } from '@/scripts/array.js';
|
||||||
import { extractMentions } from '@/scripts/extract-mentions.js';
|
import { extractMentions } from '@/scripts/extract-mentions.js';
|
||||||
import { formatTimeString } from '@/scripts/format-time-string.js';
|
import { formatTimeString } from '@/scripts/format-time-string.js';
|
||||||
@@ -150,7 +150,6 @@ const props = withDefaults(defineProps<PostFormProps & {
|
|||||||
autofocus: true,
|
autofocus: true,
|
||||||
mock: false,
|
mock: false,
|
||||||
initialLocalOnly: undefined,
|
initialLocalOnly: undefined,
|
||||||
deleteInitialNoteAfterPost: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
provide('mock', props.mock);
|
provide('mock', props.mock);
|
||||||
@@ -846,12 +845,6 @@ async function post(ev?: MouseEvent) {
|
|||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
// 削除して編集の対象ノートを削除
|
|
||||||
if (props.initialNote && props.deleteInitialNoteAfterPost) {
|
|
||||||
misskeyApi('notes/delete', {
|
|
||||||
noteId: props.initialNote.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
deleteDraft();
|
deleteDraft();
|
||||||
emit('posted');
|
emit('posted');
|
||||||
if (postData.text && postData.text !== '') {
|
if (postData.text && postData.text !== '') {
|
||||||
@@ -903,11 +896,6 @@ async function post(ev?: MouseEvent) {
|
|||||||
if (m === 0 && s === 0) {
|
if (m === 0 && s === 0) {
|
||||||
claimAchievement('postedAt0min0sec');
|
claimAchievement('postedAt0min0sec');
|
||||||
}
|
}
|
||||||
if (props.initialNote && props.deleteInitialNoteAfterPost) {
|
|
||||||
if (date.getTime() - new Date(props.initialNote.createdAt).getTime() < 1000 * 60 && props.initialNote.userId === $i.id) {
|
|
||||||
claimAchievement('noteDeletedWithin1min');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
posting.value = false;
|
posting.value = false;
|
||||||
|
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
[$style.exceeded]: props.modelValue.length > 16,
|
[$style.exceeded]: props.modelValue.length > 16,
|
||||||
}]"
|
}]"
|
||||||
>
|
>
|
||||||
{{ 16 - props.modelValue.length }}/16
|
{{ props.modelValue.length }}/16
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -4,27 +4,60 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="rrevdjwu" :class="{ grid }">
|
<div ref="rootEl" class="rrevdjwu" :class="{ grid }">
|
||||||
<div v-for="group in def" class="group">
|
<MkInput
|
||||||
<div v-if="group.title" class="title">{{ group.title }}</div>
|
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-if="search == ''">
|
||||||
<template v-for="(item, i) in group.items">
|
<div v-for="group in def" class="group">
|
||||||
<a v-if="item.type === 'a'" :href="item.href" :target="item.target" class="_button item" :class="{ danger: item.danger, active: item.active }">
|
<div v-if="group.title" class="title">{{ group.title }}</div>
|
||||||
<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span>
|
|
||||||
<span class="text">{{ item.text }}</span>
|
<div class="items">
|
||||||
</a>
|
<template v-for="(item, i) in group.items">
|
||||||
<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)">
|
<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 v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span>
|
||||||
<span class="text">{{ item.text }}</span>
|
<span class="text">{{ item.text }}</span>
|
||||||
</button>
|
</a>
|
||||||
<MkA v-else :to="item.to" class="_button item" :class="{ danger: item.danger, active: item.active }">
|
<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 v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span>
|
||||||
<span class="text">{{ item.text }}</span>
|
<span class="text">{{ item.text }}</span>
|
||||||
</MkA>
|
</button>
|
||||||
</template>
|
<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>
|
</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>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -58,10 +91,98 @@ export type SuperMenuDef = {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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[];
|
def: SuperMenuDef[];
|
||||||
grid?: boolean;
|
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>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<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>
|
</style>
|
||||||
|
14
packages/frontend/src/components/global/SearchKeyword.vue
Normal file
14
packages/frontend/src/components/global/SearchKeyword.vue
Normal file
@@ -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>
|
14
packages/frontend/src/components/global/SearchLabel.vue
Normal file
14
packages/frontend/src/components/global/SearchLabel.vue
Normal file
@@ -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>
|
116
packages/frontend/src/components/global/SearchMarker.vue
Normal file
116
packages/frontend/src/components/global/SearchMarker.vue
Normal file
@@ -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>
|
@@ -3,8 +3,6 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { App } from 'vue';
|
|
||||||
|
|
||||||
import Mfm from './global/MkMfm.js';
|
import Mfm from './global/MkMfm.js';
|
||||||
import MkA from './global/MkA.vue';
|
import MkA from './global/MkA.vue';
|
||||||
import MkAcct from './global/MkAcct.vue';
|
import MkAcct from './global/MkAcct.vue';
|
||||||
@@ -26,6 +24,11 @@ import MkSpacer from './global/MkSpacer.vue';
|
|||||||
import MkFooterSpacer from './global/MkFooterSpacer.vue';
|
import MkFooterSpacer from './global/MkFooterSpacer.vue';
|
||||||
import MkStickyContainer from './global/MkStickyContainer.vue';
|
import MkStickyContainer from './global/MkStickyContainer.vue';
|
||||||
import MkLazy from './global/MkLazy.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) {
|
export default function(app: App) {
|
||||||
for (const [key, value] of Object.entries(components)) {
|
for (const [key, value] of Object.entries(components)) {
|
||||||
@@ -55,6 +58,9 @@ export const components = {
|
|||||||
MkFooterSpacer: MkFooterSpacer,
|
MkFooterSpacer: MkFooterSpacer,
|
||||||
MkStickyContainer: MkStickyContainer,
|
MkStickyContainer: MkStickyContainer,
|
||||||
MkLazy: MkLazy,
|
MkLazy: MkLazy,
|
||||||
|
SearchMarker: SearchMarker,
|
||||||
|
SearchLabel: SearchLabel,
|
||||||
|
SearchKeyword: SearchKeyword,
|
||||||
};
|
};
|
||||||
|
|
||||||
declare module '@vue/runtime-core' {
|
declare module '@vue/runtime-core' {
|
||||||
@@ -80,5 +86,8 @@ declare module '@vue/runtime-core' {
|
|||||||
MkFooterSpacer: typeof MkFooterSpacer;
|
MkFooterSpacer: typeof MkFooterSpacer;
|
||||||
MkStickyContainer: typeof MkStickyContainer;
|
MkStickyContainer: typeof MkStickyContainer;
|
||||||
MkLazy: typeof MkLazy;
|
MkLazy: typeof MkLazy;
|
||||||
|
SearchMarker: typeof SearchMarker;
|
||||||
|
SearchLabel: typeof SearchLabel;
|
||||||
|
SearchKeyword: typeof SearchKeyword;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,6 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<MkAuthConfirm
|
<MkAuthConfirm
|
||||||
ref="authRoot"
|
ref="authRoot"
|
||||||
:name="name"
|
:name="name"
|
||||||
|
:icon="logo"
|
||||||
:permissions="permissions"
|
:permissions="permissions"
|
||||||
:waitOnDeny="true"
|
:waitOnDeny="true"
|
||||||
@accept="onAccept"
|
@accept="onAccept"
|
||||||
@@ -33,6 +34,7 @@ if (transactionIdMeta) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const name = document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:client-name"]')?.content;
|
const name = document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:client-name"]')?.content;
|
||||||
|
const logo = document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:client-logo"]')?.content;
|
||||||
const permissions = document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:scope"]')?.content.split(' ').filter((p): p is typeof Misskey.permissions[number] => (Misskey.permissions as readonly string[]).includes(p)) ?? [];
|
const permissions = document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:scope"]')?.content.split(' ').filter((p): p is typeof Misskey.permissions[number] => (Misskey.permissions as readonly string[]).includes(p)) ?? [];
|
||||||
|
|
||||||
function doPost(token: string, decision: 'accept' | 'deny') {
|
function doPost(token: string, decision: 'accept' | 'deny') {
|
||||||
|
@@ -4,74 +4,82 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FormSection :first="first">
|
<SearchMarker markerId="2fa" :keywords="['2fa']">
|
||||||
<template #label>{{ i18n.ts['2fa'] }}</template>
|
<FormSection :first="first">
|
||||||
|
<template #label><SearchLabel>{{ i18n.ts['2fa'] }}</SearchLabel></template>
|
||||||
|
|
||||||
<div v-if="$i" class="_gaps_s">
|
<div v-if="$i" class="_gaps_s">
|
||||||
<MkInfo v-if="$i.twoFactorEnabled && $i.twoFactorBackupCodesStock === 'partial'" warn>
|
<MkInfo v-if="$i.twoFactorEnabled && $i.twoFactorBackupCodesStock === 'partial'" warn>
|
||||||
{{ i18n.ts._2fa.backupCodeUsedWarning }}
|
{{ i18n.ts._2fa.backupCodeUsedWarning }}
|
||||||
</MkInfo>
|
</MkInfo>
|
||||||
<MkInfo v-if="$i.twoFactorEnabled && $i.twoFactorBackupCodesStock === 'none'" warn>
|
<MkInfo v-if="$i.twoFactorEnabled && $i.twoFactorBackupCodesStock === 'none'" warn>
|
||||||
{{ i18n.ts._2fa.backupCodesExhaustedWarning }}
|
{{ i18n.ts._2fa.backupCodesExhaustedWarning }}
|
||||||
</MkInfo>
|
</MkInfo>
|
||||||
|
|
||||||
<MkFolder :defaultOpen="true">
|
<SearchMarker :keywords="['totp', 'app']">
|
||||||
<template #icon><i class="ti ti-shield-lock"></i></template>
|
<MkFolder :defaultOpen="true">
|
||||||
<template #label>{{ i18n.ts.totp }}</template>
|
<template #icon><i class="ti ti-shield-lock"></i></template>
|
||||||
<template #caption>{{ i18n.ts.totpDescription }}</template>
|
<template #label><SearchLabel>{{ i18n.ts.totp }}</SearchLabel></template>
|
||||||
<template #suffix><i v-if="$i.twoFactorEnabled" class="ti ti-check" style="color: var(--MI_THEME-success)"></i></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-if="$i.twoFactorEnabled" class="_gaps_s">
|
||||||
<div v-text="i18n.ts._2fa.alreadyRegistered"/>
|
<div v-text="i18n.ts._2fa.alreadyRegistered"/>
|
||||||
<template v-if="$i.securityKeysList.length > 0">
|
<template v-if="$i.securityKeysList.length > 0">
|
||||||
<MkButton @click="renewTOTP">{{ i18n.ts._2fa.renewTOTP }}</MkButton>
|
<MkButton @click="renewTOTP">{{ i18n.ts._2fa.renewTOTP }}</MkButton>
|
||||||
<MkInfo>{{ i18n.ts._2fa.whyTOTPOnlyRenew }}</MkInfo>
|
<MkInfo>{{ i18n.ts._2fa.whyTOTPOnlyRenew }}</MkInfo>
|
||||||
</template>
|
</template>
|
||||||
<MkButton v-else danger @click="unregisterTOTP">{{ i18n.ts.unregister }}</MkButton>
|
<MkButton v-else danger @click="unregisterTOTP">{{ i18n.ts.unregister }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="!$i.twoFactorEnabled" class="_gaps_s">
|
<div v-else-if="!$i.twoFactorEnabled" class="_gaps_s">
|
||||||
<MkButton primary gradate @click="registerTOTP">{{ i18n.ts._2fa.registerTOTP }}</MkButton>
|
<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>
|
<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>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
<MkFolder>
|
<SearchMarker :keywords="['security', 'key', 'passkey']">
|
||||||
<template #icon><i class="ti ti-key"></i></template>
|
<MkFolder>
|
||||||
<template #label>{{ i18n.ts.securityKeyAndPasskey }}</template>
|
<template #icon><i class="ti ti-key"></i></template>
|
||||||
<div class="_gaps_s">
|
<template #label><SearchLabel>{{ i18n.ts.securityKeyAndPasskey }}</SearchLabel></template>
|
||||||
<MkInfo>
|
<div class="_gaps_s">
|
||||||
{{ i18n.ts._2fa.securityKeyInfo }}
|
<MkInfo>
|
||||||
</MkInfo>
|
{{ i18n.ts._2fa.securityKeyInfo }}
|
||||||
|
</MkInfo>
|
||||||
|
|
||||||
<MkInfo v-if="!webAuthnSupported()" warn>
|
<MkInfo v-if="!webAuthnSupported()" warn>
|
||||||
{{ i18n.ts._2fa.securityKeyNotSupported }}
|
{{ i18n.ts._2fa.securityKeyNotSupported }}
|
||||||
</MkInfo>
|
</MkInfo>
|
||||||
|
|
||||||
<MkInfo v-else-if="webAuthnSupported() && !$i.twoFactorEnabled" warn>
|
<MkInfo v-else-if="webAuthnSupported() && !$i.twoFactorEnabled" warn>
|
||||||
{{ i18n.ts._2fa.registerTOTPBeforeKey }}
|
{{ i18n.ts._2fa.registerTOTPBeforeKey }}
|
||||||
</MkInfo>
|
</MkInfo>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<MkButton primary @click="addSecurityKey">{{ i18n.ts._2fa.registerSecurityKey }}</MkButton>
|
<MkButton primary @click="addSecurityKey">{{ i18n.ts._2fa.registerSecurityKey }}</MkButton>
|
||||||
<MkFolder v-for="key in $i.securityKeysList" :key="key.id">
|
<MkFolder v-for="key in $i.securityKeysList" :key="key.id">
|
||||||
<template #label>{{ key.name }}</template>
|
<template #label>{{ key.name }}</template>
|
||||||
<template #suffix><I18n :src="i18n.ts.lastUsedAt"><template #t><MkTime :time="key.lastUsed"/></template></I18n></template>
|
<template #suffix><I18n :src="i18n.ts.lastUsedAt"><template #t><MkTime :time="key.lastUsed"/></template></I18n></template>
|
||||||
<div class="_buttons">
|
<div class="_buttons">
|
||||||
<MkButton @click="renameKey(key)"><i class="ti ti-forms"></i> {{ i18n.ts.rename }}</MkButton>
|
<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>
|
<MkButton danger @click="unregisterKey(key)"><i class="ti ti-trash"></i> {{ i18n.ts.unregister }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
<MkSwitch :disabled="!$i.twoFactorEnabled || $i.securityKeysList.length === 0" :modelValue="usePasswordLessLogin" @update:modelValue="v => updatePasswordLessLogin(v)">
|
<SearchMarker :keywords="['password', 'less', 'key', 'passkey', 'login', 'signin']">
|
||||||
<template #label>{{ i18n.ts.passwordLessLogin }}</template>
|
<MkSwitch :disabled="!$i.twoFactorEnabled || $i.securityKeysList.length === 0" :modelValue="usePasswordLessLogin" @update:modelValue="v => updatePasswordLessLogin(v)">
|
||||||
<template #caption>{{ i18n.ts.passwordLessLoginDescription }}</template>
|
<template #label><SearchLabel>{{ i18n.ts.passwordLessLogin }}</SearchLabel></template>
|
||||||
</MkSwitch>
|
<template #caption><SearchKeyword>{{ i18n.ts.passwordLessLoginDescription }}</SearchKeyword></template>
|
||||||
</div>
|
</MkSwitch>
|
||||||
</FormSection>
|
</SearchMarker>
|
||||||
|
</div>
|
||||||
|
</FormSection>
|
||||||
|
</SearchMarker>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
91
packages/frontend/src/pages/settings/accessibility.vue
Normal file
91
packages/frontend/src/pages/settings/accessibility.vue
Normal file
@@ -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>
|
287
packages/frontend/src/pages/settings/appearance.vue
Normal file
287
packages/frontend/src/pages/settings/appearance.vue
Normal file
@@ -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>
|
@@ -4,44 +4,46 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<SearchMarker path="/settings/avatar-decoration" :label="i18n.ts.avatarDecorations" :keywords="['avatar', 'icon', 'decoration']" icon="ti ti-sparkles">
|
||||||
<div v-if="!loading" class="_gaps">
|
<div>
|
||||||
<MkInfo>{{ i18n.tsx._profile.avatarDecorationMax({ max: $i.policies.avatarDecorationLimit }) }} ({{ i18n.tsx.remainingN({ n: $i.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo>
|
<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 v-if="$i.avatarDecorations.length > 0" v-panel :class="$style.current" class="_gaps_s">
|
||||||
<div>{{ i18n.ts.inUse }}</div>
|
<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">
|
<div :class="$style.decorations">
|
||||||
<XDecoration
|
<XDecoration
|
||||||
v-for="(avatarDecoration, i) in $i.avatarDecorations"
|
v-for="avatarDecoration in avatarDecorations"
|
||||||
:decoration="avatarDecorations.find(d => d.id === avatarDecoration.id)"
|
:key="avatarDecoration.id"
|
||||||
:angle="avatarDecoration.angle"
|
:decoration="avatarDecoration"
|
||||||
:flipH="avatarDecoration.flipH"
|
@click="openDecoration(avatarDecoration)"
|
||||||
:offsetX="avatarDecoration.offsetX"
|
|
||||||
:offsetY="avatarDecoration.offsetY"
|
|
||||||
:active="true"
|
|
||||||
@click="openDecoration(avatarDecoration, i)"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MkButton danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else>
|
||||||
<div :class="$style.decorations">
|
<MkLoading/>
|
||||||
<XDecoration
|
|
||||||
v-for="avatarDecoration in avatarDecorations"
|
|
||||||
:key="avatarDecoration.id"
|
|
||||||
:decoration="avatarDecoration"
|
|
||||||
@click="openDecoration(avatarDecoration)"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
</SearchMarker>
|
||||||
<MkLoading/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@@ -4,60 +4,81 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="_gaps_m">
|
<SearchMarker path="/settings/drive" :label="i18n.ts.drive" :keywords="['drive']" icon="ti ti-cloud">
|
||||||
<FormSection v-if="!fetching" first>
|
<div class="_gaps_m">
|
||||||
<template #label>{{ i18n.ts.usageAmount }}</template>
|
<SearchMarker :keywords="['capacity', 'usage']">
|
||||||
|
<FormSection first>
|
||||||
|
<template #label><SearchLabel>{{ i18n.ts.usageAmount }}</SearchLabel></template>
|
||||||
|
|
||||||
<div class="_gaps_m">
|
<div v-if="!fetching" class="_gaps_m">
|
||||||
<div>
|
<div>
|
||||||
<div :class="$style.meter"><div :class="$style.meterValue" :style="meterStyle"></div></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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<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>
|
</div>
|
||||||
<FormSplit>
|
</FormSection>
|
||||||
<MkKeyValue>
|
</div>
|
||||||
<template #key>{{ i18n.ts.capacity }}</template>
|
</SearchMarker>
|
||||||
<template #value>{{ bytes(capacity, 1) }}</template>
|
|
||||||
</MkKeyValue>
|
|
||||||
<MkKeyValue>
|
|
||||||
<template #key>{{ i18n.ts.inUse }}</template>
|
|
||||||
<template #value>{{ bytes(usage, 1) }}</template>
|
|
||||||
</MkKeyValue>
|
|
||||||
</FormSplit>
|
|
||||||
</div>
|
|
||||||
</FormSection>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@@ -4,47 +4,58 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="instance.enableEmail" class="_gaps_m">
|
<SearchMarker path="/settings/email" :label="i18n.ts.email" :keywords="['email']" icon="ti ti-mail">
|
||||||
<FormSection first>
|
<div class="_gaps_m">
|
||||||
<template #label>{{ i18n.ts.emailAddress }}</template>
|
<MkInfo v-if="!instance.enableEmail">{{ i18n.ts.emailNotSupported }}</MkInfo>
|
||||||
<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>
|
|
||||||
|
|
||||||
<FormSection>
|
<MkDisableSection :disabled="!instance.enableEmail">
|
||||||
<MkSwitch :modelValue="$i.receiveAnnouncementEmail" @update:modelValue="onChangeReceiveAnnouncementEmail">
|
<div class="_gaps_m">
|
||||||
{{ i18n.ts.receiveAnnouncementFromInstance }}
|
<SearchMarker :keywords="['email', 'address']">
|
||||||
</MkSwitch>
|
<FormSection first>
|
||||||
</FormSection>
|
<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>
|
<FormSection>
|
||||||
<template #label>{{ i18n.ts.emailNotification }}</template>
|
<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">
|
<SearchMarker :keywords="['notification', 'email']">
|
||||||
<MkSwitch v-model="emailNotification_mention">
|
<FormSection>
|
||||||
{{ i18n.ts._notification._types.mention }}
|
<template #label><SearchLabel>{{ i18n.ts.emailNotification }}</SearchLabel></template>
|
||||||
</MkSwitch>
|
|
||||||
<MkSwitch v-model="emailNotification_reply">
|
<div class="_gaps_s">
|
||||||
{{ i18n.ts._notification._types.reply }}
|
<MkSwitch v-model="emailNotification_mention">
|
||||||
</MkSwitch>
|
{{ i18n.ts._notification._types.mention }}
|
||||||
<MkSwitch v-model="emailNotification_quote">
|
</MkSwitch>
|
||||||
{{ i18n.ts._notification._types.quote }}
|
<MkSwitch v-model="emailNotification_reply">
|
||||||
</MkSwitch>
|
{{ i18n.ts._notification._types.reply }}
|
||||||
<MkSwitch v-model="emailNotification_follow">
|
</MkSwitch>
|
||||||
{{ i18n.ts._notification._types.follow }}
|
<MkSwitch v-model="emailNotification_quote">
|
||||||
</MkSwitch>
|
{{ i18n.ts._notification._types.quote }}
|
||||||
<MkSwitch v-model="emailNotification_receiveFollowRequest">
|
</MkSwitch>
|
||||||
{{ i18n.ts._notification._types.receiveFollowRequest }}
|
<MkSwitch v-model="emailNotification_follow">
|
||||||
</MkSwitch>
|
{{ i18n.ts._notification._types.follow }}
|
||||||
</div>
|
</MkSwitch>
|
||||||
</FormSection>
|
<MkSwitch v-model="emailNotification_receiveFollowRequest">
|
||||||
</div>
|
{{ i18n.ts._notification._types.receiveFollowRequest }}
|
||||||
<div v-if="!instance.enableEmail" class="_gaps_m">
|
</MkSwitch>
|
||||||
<MkInfo>{{ i18n.ts.emailNotSupported }}</MkInfo>
|
</div>
|
||||||
</div>
|
</FormSection>
|
||||||
|
</SearchMarker>
|
||||||
|
</div>
|
||||||
|
</MkDisableSection>
|
||||||
|
</div>
|
||||||
|
</SearchMarker>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@@ -53,6 +64,7 @@ import FormSection from '@/components/form/section.vue';
|
|||||||
import MkInfo from '@/components/MkInfo.vue';
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
|
import MkDisableSection from '@/components/MkDisableSection.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
import { signinRequired } from '@/account.js';
|
import { signinRequired } from '@/account.js';
|
||||||
|
@@ -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>
|
|
@@ -4,118 +4,143 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="_gaps_m">
|
<SearchMarker path="/settings/import-export" :label="i18n.ts.importAndExport" :keywords="['import', 'export', 'data']" icon="ti ti-package">
|
||||||
<FormSection first>
|
<div class="_gaps_m">
|
||||||
<template #label><i class="ti ti-pencil"></i> {{ i18n.ts._exportOrImport.allNotes }}</template>
|
<SearchMarker :keywords="['notes']">
|
||||||
<MkFolder>
|
<FormSection first>
|
||||||
<template #label>{{ i18n.ts.export }}</template>
|
<template #label><i class="ti ti-pencil"></i> <SearchLabel>{{ i18n.ts._exportOrImport.allNotes }}</SearchLabel></template>
|
||||||
<template #icon><i class="ti ti-download"></i></template>
|
<MkFolder>
|
||||||
<MkButton primary :class="$style.button" inline @click="exportNotes()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
|
<template #label>{{ i18n.ts.export }}</template>
|
||||||
</MkFolder>
|
<template #icon><i class="ti ti-download"></i></template>
|
||||||
</FormSection>
|
<MkButton primary :class="$style.button" inline @click="exportNotes()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
|
||||||
<FormSection>
|
</MkFolder>
|
||||||
<template #label><i class="ti ti-star"></i> {{ i18n.ts._exportOrImport.favoritedNotes }}</template>
|
</FormSection>
|
||||||
<MkFolder>
|
</SearchMarker>
|
||||||
<template #label>{{ i18n.ts.export }}</template>
|
|
||||||
<template #icon><i class="ti ti-download"></i></template>
|
<SearchMarker :keywords="['favorite', 'notes']">
|
||||||
<MkButton primary :class="$style.button" inline @click="exportFavorites()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
|
<FormSection>
|
||||||
</MkFolder>
|
<template #label><i class="ti ti-star"></i> <SearchLabel>{{ i18n.ts._exportOrImport.favoritedNotes }}</SearchLabel></template>
|
||||||
</FormSection>
|
<MkFolder>
|
||||||
<FormSection>
|
<template #label>{{ i18n.ts.export }}</template>
|
||||||
<template #label><i class="ti ti-star"></i> {{ i18n.ts._exportOrImport.clips }}</template>
|
<template #icon><i class="ti ti-download"></i></template>
|
||||||
<MkFolder>
|
<MkButton primary :class="$style.button" inline @click="exportFavorites()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
|
||||||
<template #label>{{ i18n.ts.export }}</template>
|
</MkFolder>
|
||||||
<template #icon><i class="ti ti-download"></i></template>
|
</FormSection>
|
||||||
<MkButton primary :class="$style.button" inline @click="exportClips()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
|
</SearchMarker>
|
||||||
</MkFolder>
|
|
||||||
</FormSection>
|
<SearchMarker :keywords="['clip', 'notes']">
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<template #label><i class="ti ti-users"></i> {{ i18n.ts._exportOrImport.followingList }}</template>
|
<template #label><i class="ti ti-star"></i> <SearchLabel>{{ i18n.ts._exportOrImport.clips }}</SearchLabel></template>
|
||||||
<div class="_gaps_s">
|
<MkFolder>
|
||||||
<MkFolder>
|
<template #label>{{ i18n.ts.export }}</template>
|
||||||
<template #label>{{ i18n.ts.export }}</template>
|
<template #icon><i class="ti ti-download"></i></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">
|
<div class="_gaps_s">
|
||||||
<MkSwitch v-model="excludeMutingUsers">
|
<MkFolder>
|
||||||
{{ i18n.ts._exportOrImport.excludeMutingUsers }}
|
<template #label>{{ i18n.ts.export }}</template>
|
||||||
</MkSwitch>
|
<template #icon><i class="ti ti-download"></i></template>
|
||||||
<MkSwitch v-model="excludeInactiveUsers">
|
<div class="_gaps_s">
|
||||||
{{ i18n.ts._exportOrImport.excludeInactiveUsers }}
|
<MkSwitch v-model="excludeMutingUsers">
|
||||||
</MkSwitch>
|
{{ i18n.ts._exportOrImport.excludeMutingUsers }}
|
||||||
<MkButton primary :class="$style.button" inline @click="exportFollowing()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
|
</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>
|
</div>
|
||||||
</MkFolder>
|
</FormSection>
|
||||||
<MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportFollowing">
|
</SearchMarker>
|
||||||
<template #label>{{ i18n.ts.import }}</template>
|
|
||||||
<template #icon><i class="ti ti-upload"></i></template>
|
<SearchMarker :keywords="['user', 'lists']">
|
||||||
<MkSwitch v-model="withReplies">
|
<FormSection>
|
||||||
{{ i18n.ts._exportOrImport.withReplies }}
|
<template #label><i class="ti ti-users"></i> <SearchLabel>{{ i18n.ts._exportOrImport.userLists }}</SearchLabel></template>
|
||||||
</MkSwitch>
|
<div class="_gaps_s">
|
||||||
<MkButton primary :class="$style.button" inline @click="importFollowing($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
|
<MkFolder>
|
||||||
</MkFolder>
|
<template #label>{{ i18n.ts.export }}</template>
|
||||||
</div>
|
<template #icon><i class="ti ti-download"></i></template>
|
||||||
</FormSection>
|
<MkButton primary :class="$style.button" inline @click="exportUserLists()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
|
||||||
<FormSection>
|
</MkFolder>
|
||||||
<template #label><i class="ti ti-users"></i> {{ i18n.ts._exportOrImport.userLists }}</template>
|
<MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportUserLists">
|
||||||
<div class="_gaps_s">
|
<template #label>{{ i18n.ts.import }}</template>
|
||||||
<MkFolder>
|
<template #icon><i class="ti ti-upload"></i></template>
|
||||||
<template #label>{{ i18n.ts.export }}</template>
|
<MkButton primary :class="$style.button" inline @click="importUserLists($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
|
||||||
<template #icon><i class="ti ti-download"></i></template>
|
</MkFolder>
|
||||||
<MkButton primary :class="$style.button" inline @click="exportUserLists()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
|
</div>
|
||||||
</MkFolder>
|
</FormSection>
|
||||||
<MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportUserLists">
|
</SearchMarker>
|
||||||
<template #label>{{ i18n.ts.import }}</template>
|
|
||||||
<template #icon><i class="ti ti-upload"></i></template>
|
<SearchMarker :keywords="['mute', 'users']">
|
||||||
<MkButton primary :class="$style.button" inline @click="importUserLists($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
|
<FormSection>
|
||||||
</MkFolder>
|
<template #label><i class="ti ti-user-off"></i> <SearchLabel>{{ i18n.ts._exportOrImport.muteList }}</SearchLabel></template>
|
||||||
</div>
|
<div class="_gaps_s">
|
||||||
</FormSection>
|
<MkFolder>
|
||||||
<FormSection>
|
<template #label>{{ i18n.ts.export }}</template>
|
||||||
<template #label><i class="ti ti-user-off"></i> {{ i18n.ts._exportOrImport.muteList }}</template>
|
<template #icon><i class="ti ti-download"></i></template>
|
||||||
<div class="_gaps_s">
|
<MkButton primary :class="$style.button" inline @click="exportMuting()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
|
||||||
<MkFolder>
|
</MkFolder>
|
||||||
<template #label>{{ i18n.ts.export }}</template>
|
<MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportMuting">
|
||||||
<template #icon><i class="ti ti-download"></i></template>
|
<template #label>{{ i18n.ts.import }}</template>
|
||||||
<MkButton primary :class="$style.button" inline @click="exportMuting()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
|
<template #icon><i class="ti ti-upload"></i></template>
|
||||||
</MkFolder>
|
<MkButton primary :class="$style.button" inline @click="importMuting($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
|
||||||
<MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportMuting">
|
</MkFolder>
|
||||||
<template #label>{{ i18n.ts.import }}</template>
|
</div>
|
||||||
<template #icon><i class="ti ti-upload"></i></template>
|
</FormSection>
|
||||||
<MkButton primary :class="$style.button" inline @click="importMuting($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
|
</SearchMarker>
|
||||||
</MkFolder>
|
|
||||||
</div>
|
<SearchMarker :keywords="['block', 'users']">
|
||||||
</FormSection>
|
<FormSection>
|
||||||
<FormSection>
|
<template #label><i class="ti ti-user-off"></i> <SearchLabel>{{ i18n.ts._exportOrImport.blockingList }}</SearchLabel></template>
|
||||||
<template #label><i class="ti ti-user-off"></i> {{ i18n.ts._exportOrImport.blockingList }}</template>
|
<div class="_gaps_s">
|
||||||
<div class="_gaps_s">
|
<MkFolder>
|
||||||
<MkFolder>
|
<template #label>{{ i18n.ts.export }}</template>
|
||||||
<template #label>{{ i18n.ts.export }}</template>
|
<template #icon><i class="ti ti-download"></i></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>
|
||||||
<MkButton primary :class="$style.button" inline @click="exportBlocking()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
|
</MkFolder>
|
||||||
</MkFolder>
|
<MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportBlocking">
|
||||||
<MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportBlocking">
|
<template #label>{{ i18n.ts.import }}</template>
|
||||||
<template #label>{{ i18n.ts.import }}</template>
|
<template #icon><i class="ti ti-upload"></i></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>
|
||||||
<MkButton primary :class="$style.button" inline @click="importBlocking($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
|
</MkFolder>
|
||||||
</MkFolder>
|
</div>
|
||||||
</div>
|
</FormSection>
|
||||||
</FormSection>
|
</SearchMarker>
|
||||||
<FormSection>
|
|
||||||
<template #label><i class="ti ti-antenna"></i> {{ i18n.ts.antennas }}</template>
|
<SearchMarker :keywords="['antennas']">
|
||||||
<div class="_gaps_s">
|
<FormSection>
|
||||||
<MkFolder>
|
<template #label><i class="ti ti-antenna"></i> <SearchLabel>{{ i18n.ts.antennas }}</SearchLabel></template>
|
||||||
<template #label>{{ i18n.ts.export }}</template>
|
<div class="_gaps_s">
|
||||||
<template #icon><i class="ti ti-download"></i></template>
|
<MkFolder>
|
||||||
<MkButton primary :class="$style.button" inline @click="exportAntennas()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
|
<template #label>{{ i18n.ts.export }}</template>
|
||||||
</MkFolder>
|
<template #icon><i class="ti ti-download"></i></template>
|
||||||
<MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportAntennas">
|
<MkButton primary :class="$style.button" inline @click="exportAntennas()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
|
||||||
<template #label>{{ i18n.ts.import }}</template>
|
</MkFolder>
|
||||||
<template #icon><i class="ti ti-upload"></i></template>
|
<MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportAntennas">
|
||||||
<MkButton primary :class="$style.button" inline @click="importAntennas($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
|
<template #label>{{ i18n.ts.import }}</template>
|
||||||
</MkFolder>
|
<template #icon><i class="ti ti-upload"></i></template>
|
||||||
</div>
|
<MkButton primary :class="$style.button" inline @click="importAntennas($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
|
||||||
</FormSection>
|
</MkFolder>
|
||||||
</div>
|
</div>
|
||||||
|
</FormSection>
|
||||||
|
</SearchMarker>
|
||||||
|
</div>
|
||||||
|
</SearchMarker>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<div v-if="!narrow || currentPage?.route.name == null" class="nav">
|
<div v-if="!narrow || currentPage?.route.name == null" class="nav">
|
||||||
<div class="baaadecd">
|
<div class="baaadecd">
|
||||||
<MkInfo v-if="emailNotConfigured" warn class="info">{{ i18n.ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
|
<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>
|
</div>
|
||||||
<div v-if="!(narrow && currentPage?.route.name == null)" class="main">
|
<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">
|
<script setup lang="ts">
|
||||||
import { computed, onActivated, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue';
|
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 { i18n } from '@/i18n.js';
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
import MkSuperMenu from '@/components/MkSuperMenu.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 { definePageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { useRouter } from '@/router/supplier.js';
|
import { useRouter } from '@/router/supplier.js';
|
||||||
import type { PageMetadata } from '@/scripts/page-metadata.js';
|
import { searchIndexes } from '@/scripts/autogen/settings-search-index.js';
|
||||||
import type { SuperMenuDef } from '@/components/MkSuperMenu.vue';
|
|
||||||
|
const SETTING_INDEX = searchIndexes; // TODO: lazy load
|
||||||
|
|
||||||
const indexInfo = {
|
const indexInfo = {
|
||||||
title: i18n.ts.settings,
|
title: i18n.ts.settings,
|
||||||
@@ -63,7 +66,6 @@ const ro = new ResizeObserver((entries, observer) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const menuDef = computed<SuperMenuDef[]>(() => [{
|
const menuDef = computed<SuperMenuDef[]>(() => [{
|
||||||
title: i18n.ts.basicSettings,
|
|
||||||
items: [{
|
items: [{
|
||||||
icon: 'ti ti-user',
|
icon: 'ti ti-user',
|
||||||
text: i18n.ts.profile,
|
text: i18n.ts.profile,
|
||||||
@@ -101,32 +103,31 @@ const menuDef = computed<SuperMenuDef[]>(() => [{
|
|||||||
active: currentPage.value?.route.name === 'security',
|
active: currentPage.value?.route.name === 'security',
|
||||||
}],
|
}],
|
||||||
}, {
|
}, {
|
||||||
title: i18n.ts.clientSettings,
|
|
||||||
items: [{
|
items: [{
|
||||||
icon: 'ti ti-adjustments',
|
icon: 'ti ti-adjustments',
|
||||||
text: i18n.ts.general,
|
text: i18n.ts.preferences,
|
||||||
to: '/settings/general',
|
to: '/settings/preferences',
|
||||||
active: currentPage.value?.route.name === 'general',
|
active: currentPage.value?.route.name === 'preferences',
|
||||||
}, {
|
}, {
|
||||||
icon: 'ti ti-palette',
|
icon: 'ti ti-palette',
|
||||||
text: i18n.ts.theme,
|
text: i18n.ts.theme,
|
||||||
to: '/settings/theme',
|
to: '/settings/theme',
|
||||||
active: currentPage.value?.route.name === 'theme',
|
active: currentPage.value?.route.name === 'theme',
|
||||||
}, {
|
}, {
|
||||||
icon: 'ti ti-menu-2',
|
icon: 'ti ti-device-desktop',
|
||||||
text: i18n.ts.navbar,
|
text: i18n.ts.appearance,
|
||||||
to: '/settings/navbar',
|
to: '/settings/appearance',
|
||||||
active: currentPage.value?.route.name === 'navbar',
|
active: currentPage.value?.route.name === 'appearance',
|
||||||
}, {
|
|
||||||
icon: 'ti ti-equal-double',
|
|
||||||
text: i18n.ts.statusbar,
|
|
||||||
to: '/settings/statusbar',
|
|
||||||
active: currentPage.value?.route.name === 'statusbar',
|
|
||||||
}, {
|
}, {
|
||||||
icon: 'ti ti-music',
|
icon: 'ti ti-music',
|
||||||
text: i18n.ts.sounds,
|
text: i18n.ts.sounds,
|
||||||
to: '/settings/sounds',
|
to: '/settings/sounds',
|
||||||
active: currentPage.value?.route.name === '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',
|
icon: 'ti ti-plug',
|
||||||
text: i18n.ts.plugins,
|
text: i18n.ts.plugins,
|
||||||
@@ -134,7 +135,6 @@ const menuDef = computed<SuperMenuDef[]>(() => [{
|
|||||||
active: currentPage.value?.route.name === 'plugin',
|
active: currentPage.value?.route.name === 'plugin',
|
||||||
}],
|
}],
|
||||||
}, {
|
}, {
|
||||||
title: i18n.ts.otherSettings,
|
|
||||||
items: [{
|
items: [{
|
||||||
icon: 'ti ti-badges',
|
icon: 'ti ti-badges',
|
||||||
text: i18n.ts.roles,
|
text: i18n.ts.roles,
|
||||||
@@ -160,11 +160,6 @@ const menuDef = computed<SuperMenuDef[]>(() => [{
|
|||||||
text: i18n.ts.importAndExport,
|
text: i18n.ts.importAndExport,
|
||||||
to: '/settings/import-export',
|
to: '/settings/import-export',
|
||||||
active: currentPage.value?.route.name === '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',
|
icon: 'ti ti-dots',
|
||||||
text: i18n.ts.other,
|
text: i18n.ts.other,
|
||||||
|
@@ -68,7 +68,6 @@ import MkUserInfo from '@/components/MkUserInfo.vue';
|
|||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
|
||||||
import { signinRequired } from '@/account.js';
|
import { signinRequired } from '@/account.js';
|
||||||
import { unisonReload } from '@/scripts/unison-reload.js';
|
import { unisonReload } from '@/scripts/unison-reload.js';
|
||||||
|
|
||||||
@@ -120,11 +119,6 @@ async function save(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
|
||||||
definePageMetadata(() => ({
|
|
||||||
title: i18n.ts.accountMigration,
|
|
||||||
icon: 'ti ti-plane',
|
|
||||||
}));
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@@ -4,132 +4,171 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="_gaps_m">
|
<SearchMarker path="/settings/mute-block" :label="i18n.ts.muteAndBlock" icon="ti ti-ban" :keywords="['mute', 'block']">
|
||||||
<MkFolder>
|
<div class="_gaps_m">
|
||||||
<template #icon><i class="ti ti-message-off"></i></template>
|
<SearchMarker
|
||||||
<template #label>{{ i18n.ts.wordMute }}</template>
|
: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">
|
<div class="_gaps_m">
|
||||||
<MkInfo>{{ i18n.ts.wordMuteDescription }}</MkInfo>
|
<MkInfo>{{ i18n.ts.wordMuteDescription }}</MkInfo>
|
||||||
<MkSwitch v-model="showSoftWordMutedWord">{{ i18n.ts.showMutedWord }}</MkSwitch>
|
|
||||||
<XWordMute :muted="$i.mutedWords" @save="saveMutedWords"/>
|
|
||||||
</div>
|
|
||||||
</MkFolder>
|
|
||||||
|
|
||||||
<MkFolder>
|
<SearchMarker
|
||||||
<template #icon><i class="ti ti-message-off"></i></template>
|
:label="i18n.ts.showMutedWord"
|
||||||
<template #label>{{ i18n.ts.hardWordMute }}</template>
|
:keywords="['show']"
|
||||||
|
>
|
||||||
|
<MkSwitch v-model="showSoftWordMutedWord">{{ i18n.ts.showMutedWord }}</MkSwitch>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
<div class="_gaps_m">
|
<XWordMute :muted="$i.mutedWords" @save="saveMutedWords"/>
|
||||||
<MkInfo>{{ i18n.ts.hardWordMuteDescription }}</MkInfo>
|
|
||||||
<XWordMute :muted="$i.hardMutedWords" @save="saveHardMutedWords"/>
|
|
||||||
</div>
|
|
||||||
</MkFolder>
|
|
||||||
|
|
||||||
<MkFolder v-if="instance.federation !== 'none'">
|
|
||||||
<template #icon><i class="ti ti-planet-off"></i></template>
|
|
||||||
<template #label>{{ i18n.ts.instanceMute }}</template>
|
|
||||||
|
|
||||||
<XInstanceMute/>
|
|
||||||
</MkFolder>
|
|
||||||
|
|
||||||
<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>
|
||||||
</template>
|
</MkFolder>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
<template #default="{ items }">
|
<SearchMarker
|
||||||
<div class="_gaps_s">
|
:label="i18n.ts.hardWordMute"
|
||||||
<div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedRenoteMuteItems.includes(item.id) }]">
|
:keywords="['note', 'word', 'hard', 'mute', 'hide']"
|
||||||
<div :class="$style.userItemMain">
|
>
|
||||||
<MkA :class="$style.userItemMainBody" :to="userPage(item.mutee)">
|
<MkFolder>
|
||||||
<MkUserCardMini :user="item.mutee"/>
|
<template #icon><i class="ti ti-message-off"></i></template>
|
||||||
</MkA>
|
<template #label>{{ i18n.ts.hardWordMute }}</template>
|
||||||
<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 class="_gaps_m">
|
||||||
</div>
|
<MkInfo>{{ i18n.ts.hardWordMuteDescription }}</MkInfo>
|
||||||
<div v-if="expandedRenoteMuteItems.includes(item.id)" :class="$style.userItemSub">
|
<XWordMute :muted="$i.hardMutedWords" @save="saveHardMutedWords"/>
|
||||||
<div>Muted at: <MkTime :time="item.createdAt" mode="detail"/></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</MkFolder>
|
||||||
</MkPagination>
|
</SearchMarker>
|
||||||
</MkFolder>
|
|
||||||
|
|
||||||
<MkFolder>
|
<SearchMarker
|
||||||
<template #icon><i class="ti ti-eye-off"></i></template>
|
:label="i18n.ts.instanceMute"
|
||||||
<template #label>{{ i18n.ts.mutedUsers }}</template>
|
: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="mutingPagination">
|
<XInstanceMute/>
|
||||||
<template #empty>
|
</MkFolder>
|
||||||
<div class="_fullinfo">
|
</SearchMarker>
|
||||||
<img :src="infoImageUrl" class="_ghost"/>
|
|
||||||
<div>{{ i18n.ts.noUsers }}</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #default="{ items }">
|
<SearchMarker
|
||||||
<div class="_gaps_s">
|
:label="`${i18n.ts.mutedUsers} (${ i18n.ts.renote })`"
|
||||||
<div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedMuteItems.includes(item.id) }]">
|
:keywords="['renote', 'mute', 'hide', 'user']"
|
||||||
<div :class="$style.userItemMain">
|
>
|
||||||
<MkA :class="$style.userItemMainBody" :to="userPage(item.mutee)">
|
<MkFolder>
|
||||||
<MkUserCardMini :user="item.mutee"/>
|
<template #icon><i class="ti ti-repeat-off"></i></template>
|
||||||
</MkA>
|
<template #label>{{ i18n.ts.mutedUsers }} ({{ i18n.ts.renote }})</template>
|
||||||
<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="renoteMutingPagination">
|
||||||
|
<template #empty>
|
||||||
|
<div class="_fullinfo">
|
||||||
|
<img :src="infoImageUrl" class="_ghost"/>
|
||||||
|
<div>{{ i18n.ts.noUsers }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="expandedMuteItems.includes(item.id)" :class="$style.userItemSub">
|
</template>
|
||||||
<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>
|
|
||||||
</template>
|
|
||||||
</MkPagination>
|
|
||||||
</MkFolder>
|
|
||||||
|
|
||||||
<MkFolder>
|
<template #default="{ items }">
|
||||||
<template #icon><i class="ti ti-ban"></i></template>
|
<div class="_gaps_s">
|
||||||
<template #label>{{ i18n.ts.blockedUsers }}</template>
|
<div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedRenoteMuteItems.includes(item.id) }]">
|
||||||
|
<div :class="$style.userItemMain">
|
||||||
<MkPagination :pagination="blockingPagination">
|
<MkA :class="$style.userItemMainBody" :to="userPage(item.mutee)">
|
||||||
<template #empty>
|
<MkUserCardMini :user="item.mutee"/>
|
||||||
<div class="_fullinfo">
|
</MkA>
|
||||||
<img :src="infoImageUrl" class="_ghost"/>
|
<button class="_button" :class="$style.userToggle" @click="toggleRenoteMuteItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button>
|
||||||
<div>{{ i18n.ts.noUsers }}</div>
|
<button class="_button" :class="$style.remove" @click="unrenoteMute(item.mutee, $event)"><i class="ti ti-x"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<div v-if="expandedRenoteMuteItems.includes(item.id)" :class="$style.userItemSub">
|
||||||
|
<div>Muted at: <MkTime :time="item.createdAt" mode="detail"/></div>
|
||||||
<template #default="{ items }">
|
</div>
|
||||||
<div class="_gaps_s">
|
</div>
|
||||||
<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>
|
||||||
<div v-if="expandedBlockItems.includes(item.id)" :class="$style.userItemSub">
|
</template>
|
||||||
<div>Blocked at: <MkTime :time="item.createdAt" mode="detail"/></div>
|
</MkPagination>
|
||||||
<div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div>
|
</MkFolder>
|
||||||
<div v-else>Period: {{ i18n.ts.indefinitely }}</div>
|
</SearchMarker>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<MkPagination :pagination="mutingPagination">
|
||||||
|
<template #empty>
|
||||||
|
<div class="_fullinfo">
|
||||||
|
<img :src="infoImageUrl" class="_ghost"/>
|
||||||
|
<div>{{ i18n.ts.noUsers }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
|
||||||
</template>
|
<template #default="{ items }">
|
||||||
</MkPagination>
|
<div class="_gaps_s">
|
||||||
</MkFolder>
|
<div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedMuteItems.includes(item.id) }]">
|
||||||
</div>
|
<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>
|
||||||
|
</template>
|
||||||
|
</MkPagination>
|
||||||
|
</MkFolder>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</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>
|
||||||
|
</template>
|
||||||
|
</MkPagination>
|
||||||
|
</MkFolder>
|
||||||
|
</SearchMarker>
|
||||||
|
</div>
|
||||||
|
</SearchMarker>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@@ -4,91 +4,111 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="_gaps_m">
|
<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 v-model="$i.injectFeaturedNote" @update:model-value="onChangeInjectFeaturedNote">
|
||||||
</MkSwitch>
|
<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>
|
<FormSection first>
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
<MkFolder>
|
<SearchMarker :keywords="['account', 'info']">
|
||||||
<template #icon><i class="ti ti-info-circle"></i></template>
|
<MkFolder>
|
||||||
<template #label>{{ i18n.ts.accountInfo }}</template>
|
<template #icon><i class="ti ti-info-circle"></i></template>
|
||||||
|
<template #label><SearchLabel>{{ i18n.ts.accountInfo }}</SearchLabel></template>
|
||||||
|
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<MkKeyValue>
|
<MkKeyValue>
|
||||||
<template #key>ID</template>
|
<template #key>ID</template>
|
||||||
<template #value><span class="_monospace">{{ $i.id }}</span></template>
|
<template #value><span class="_monospace">{{ $i.id }}</span></template>
|
||||||
</MkKeyValue>
|
</MkKeyValue>
|
||||||
|
|
||||||
<MkKeyValue>
|
<MkKeyValue>
|
||||||
<template #key>{{ i18n.ts.registeredDate }}</template>
|
<template #key>{{ i18n.ts.registeredDate }}</template>
|
||||||
<template #value><MkTime :time="$i.createdAt" mode="detail"/></template>
|
<template #value><MkTime :time="$i.createdAt" mode="detail"/></template>
|
||||||
</MkKeyValue>
|
</MkKeyValue>
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
<MkFolder>
|
<SearchMarker :keywords="['account', 'move', 'migration']">
|
||||||
<template #icon><i class="ti ti-alert-triangle"></i></template>
|
<MkFolder>
|
||||||
<template #label>{{ i18n.ts.closeAccount }}</template>
|
<template #icon><i class="ti ti-plane"></i></template>
|
||||||
|
<template #label><SearchLabel>{{ i18n.ts.accountMigration }}</SearchLabel></template>
|
||||||
|
|
||||||
<div class="_gaps_m">
|
<XMigration/>
|
||||||
<FormInfo warn>{{ i18n.ts._accountDelete.mayTakeTime }}</FormInfo>
|
</MkFolder>
|
||||||
<FormInfo>{{ i18n.ts._accountDelete.sendEmail }}</FormInfo>
|
</SearchMarker>
|
||||||
<MkButton v-if="!$i.isDeleted" danger @click="deleteAccount">{{ i18n.ts._accountDelete.requestAccountDelete }}</MkButton>
|
|
||||||
<MkButton v-else disabled>{{ i18n.ts._accountDelete.inProgress }}</MkButton>
|
|
||||||
</div>
|
|
||||||
</MkFolder>
|
|
||||||
|
|
||||||
<MkFolder>
|
<SearchMarker :keywords="['account', 'close', 'delete']">
|
||||||
<template #icon><i class="ti ti-flask"></i></template>
|
<MkFolder>
|
||||||
<template #label>{{ i18n.ts.experimentalFeatures }}</template>
|
<template #icon><i class="ti ti-alert-triangle"></i></template>
|
||||||
|
<template #label><SearchLabel>{{ i18n.ts.closeAccount }}</SearchLabel></template>
|
||||||
|
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<MkSwitch v-model="enableCondensedLine">
|
<FormInfo warn>{{ i18n.ts._accountDelete.mayTakeTime }}</FormInfo>
|
||||||
<template #label>Enable condensed line</template>
|
<FormInfo>{{ i18n.ts._accountDelete.sendEmail }}</FormInfo>
|
||||||
</MkSwitch>
|
<MkButton v-if="!$i.isDeleted" danger @click="deleteAccount"><SearchKeyword>{{ i18n.ts._accountDelete.requestAccountDelete }}</SearchKeyword></MkButton>
|
||||||
<MkSwitch v-model="skipNoteRender">
|
<MkButton v-else disabled>{{ i18n.ts._accountDelete.inProgress }}</MkButton>
|
||||||
<template #label>Enable note render skipping</template>
|
</div>
|
||||||
</MkSwitch>
|
</MkFolder>
|
||||||
</div>
|
</SearchMarker>
|
||||||
</MkFolder>
|
|
||||||
|
|
||||||
<MkFolder>
|
<SearchMarker :keywords="['experimental', 'feature', 'flags']">
|
||||||
<template #icon><i class="ti ti-code"></i></template>
|
<MkFolder>
|
||||||
<template #label>{{ i18n.ts.developer }}</template>
|
<template #icon><i class="ti ti-flask"></i></template>
|
||||||
|
<template #label><SearchLabel>{{ i18n.ts.experimentalFeatures }}</SearchLabel></template>
|
||||||
|
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<MkSwitch v-model="devMode">
|
<MkSwitch v-model="enableCondensedLine">
|
||||||
<template #label>{{ i18n.ts.devMode }}</template>
|
<template #label>Enable condensed line</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
</div>
|
<MkSwitch v-model="skipNoteRender">
|
||||||
</MkFolder>
|
<template #label>Enable note render skipping</template>
|
||||||
</div>
|
</MkSwitch>
|
||||||
</FormSection>
|
</div>
|
||||||
|
</MkFolder>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
<FormSection>
|
<SearchMarker :keywords="['developer', 'mode', 'debug']">
|
||||||
<FormLink to="/registry"><template #icon><i class="ti ti-adjustments"></i></template>{{ i18n.ts.registry }}</FormLink>
|
<MkFolder>
|
||||||
</FormSection>
|
<template #icon><i class="ti ti-code"></i></template>
|
||||||
|
<template #label><SearchLabel>{{ i18n.ts.developer }}</SearchLabel></template>
|
||||||
|
|
||||||
<FormSection>
|
<div class="_gaps_m">
|
||||||
<div class="_gaps_s">
|
<MkSwitch v-model="devMode">
|
||||||
<MkSwitch v-model="defaultWithReplies">{{ i18n.ts.withRepliesByDefaultForNewlyFollowed }}</MkSwitch>
|
<template #label>{{ i18n.ts.devMode }}</template>
|
||||||
<MkButton danger @click="updateRepliesAll(true)"><i class="ti ti-messages"></i> {{ i18n.ts.showRepliesToOthersInTimelineAll }}</MkButton>
|
</MkSwitch>
|
||||||
<MkButton danger @click="updateRepliesAll(false)"><i class="ti ti-messages-off"></i> {{ i18n.ts.hideRepliesToOthersInTimelineAll }}</MkButton>
|
</div>
|
||||||
</div>
|
</MkFolder>
|
||||||
</FormSection>
|
</SearchMarker>
|
||||||
</div>
|
</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>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, watch } from 'vue';
|
import { computed, watch } from 'vue';
|
||||||
|
import XMigration from './migration.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import FormLink from '@/components/form/link.vue';
|
import FormLink from '@/components/form/link.vue';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
|
434
packages/frontend/src/pages/settings/preferences.vue
Normal file
434
packages/frontend/src/pages/settings/preferences.vue
Normal file
@@ -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>
|
@@ -4,158 +4,208 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="_gaps_m">
|
<SearchMarker path="/settings/privacy" :label="i18n.ts.privacy" :keywords="['privacy']" icon="ti ti-lock-open">
|
||||||
<MkSwitch v-model="isLocked" @update:modelValue="save()">{{ i18n.ts.makeFollowManuallyApprove }}<template #caption>{{ i18n.ts.lockedAccountInfo }}</template></MkSwitch>
|
<div class="_gaps_m">
|
||||||
<MkSwitch v-if="isLocked" v-model="autoAcceptFollowed" @update:modelValue="save()">{{ i18n.ts.autoAcceptFollowed }}</MkSwitch>
|
<SearchMarker :keywords="['follow', 'lock']">
|
||||||
|
<MkSwitch v-model="isLocked" @update:modelValue="save()">
|
||||||
<MkSwitch v-model="publicReactions" @update:modelValue="save()">
|
<template #label><SearchLabel>{{ i18n.ts.makeFollowManuallyApprove }}</SearchLabel></template>
|
||||||
{{ i18n.ts.makeReactionsPublic }}
|
<template #caption><SearchKeyword>{{ i18n.ts.lockedAccountInfo }}</SearchKeyword></template>
|
||||||
<template #caption>{{ i18n.ts.makeReactionsPublicDescription }}</template>
|
|
||||||
</MkSwitch>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<FormSection>
|
|
||||||
<template #label>{{ i18n.ts.lockdown }}<span class="_beta">{{ i18n.ts.beta }}</span></template>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
<FormSlot>
|
<MkDisableSection :disabled="!isLocked">
|
||||||
<template #label>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBefore }}</template>
|
<SearchMarker :keywords="['follow', 'auto', 'accept']">
|
||||||
|
<MkSwitch v-model="autoAcceptFollowed" @update:modelValue="save()">
|
||||||
|
<template #label><SearchLabel>{{ i18n.ts.autoAcceptFollowed }}</SearchLabel></template>
|
||||||
|
</MkSwitch>
|
||||||
|
</SearchMarker>
|
||||||
|
</MkDisableSection>
|
||||||
|
|
||||||
<div class="_gaps_s">
|
<SearchMarker :keywords="['reaction', 'public']">
|
||||||
<MkSelect :modelValue="makeNotesFollowersOnlyBefore_type" @update:modelValue="makeNotesFollowersOnlyBefore = $event === 'relative' ? -604800 : $event === 'absolute' ? Math.floor(Date.now() / 1000) : null">
|
<MkSwitch v-model="publicReactions" @update:modelValue="save()">
|
||||||
<option :value="null">{{ i18n.ts.none }}</option>
|
<template #label><SearchLabel>{{ i18n.ts.makeReactionsPublic }}</SearchLabel></template>
|
||||||
<option value="relative">{{ i18n.ts._accountSettings.notesHavePassedSpecifiedPeriod }}</option>
|
<template #caption><SearchKeyword>{{ i18n.ts.makeReactionsPublicDescription }}</SearchKeyword></template>
|
||||||
<option value="absolute">{{ i18n.ts._accountSettings.notesOlderThanSpecifiedDateAndTime }}</option>
|
</MkSwitch>
|
||||||
</MkSelect>
|
</SearchMarker>
|
||||||
|
|
||||||
<MkSelect v-if="makeNotesFollowersOnlyBefore_type === 'relative'" v-model="makeNotesFollowersOnlyBefore">
|
<SearchMarker :keywords="['following', 'visibility']">
|
||||||
<option :value="-3600">{{ i18n.ts.oneHour }}</option>
|
<MkSelect v-model="followingVisibility" @update:modelValue="save()">
|
||||||
<option :value="-86400">{{ i18n.ts.oneDay }}</option>
|
<template #label><SearchLabel>{{ i18n.ts.followingVisibility }}</SearchLabel></template>
|
||||||
<option :value="-259200">{{ i18n.ts.threeDays }}</option>
|
<option value="public">{{ i18n.ts._ffVisibility.public }}</option>
|
||||||
<option :value="-604800">{{ i18n.ts.oneWeek }}</option>
|
<option value="followers">{{ i18n.ts._ffVisibility.followers }}</option>
|
||||||
<option :value="-2592000">{{ i18n.ts.oneMonth }}</option>
|
<option value="private">{{ i18n.ts._ffVisibility.private }}</option>
|
||||||
<option :value="-7776000">{{ i18n.ts.threeMonths }}</option>
|
</MkSelect>
|
||||||
<option :value="-31104000">{{ i18n.ts.oneYear }}</option>
|
</SearchMarker>
|
||||||
</MkSelect>
|
|
||||||
|
|
||||||
<MkInput
|
<SearchMarker :keywords="['follower', 'visibility']">
|
||||||
v-if="makeNotesFollowersOnlyBefore_type === 'absolute'"
|
<MkSelect v-model="followersVisibility" @update:modelValue="save()">
|
||||||
:modelValue="formatDateTimeString(new Date(makeNotesFollowersOnlyBefore * 1000), 'yyyy-MM-dd')"
|
<template #label><SearchLabel>{{ i18n.ts.followersVisibility }}</SearchLabel></template>
|
||||||
type="date"
|
<option value="public">{{ i18n.ts._ffVisibility.public }}</option>
|
||||||
:manualSave="true"
|
<option value="followers">{{ i18n.ts._ffVisibility.followers }}</option>
|
||||||
@update:modelValue="makeNotesFollowersOnlyBefore = Math.floor(new Date($event).getTime() / 1000)"
|
<option value="private">{{ i18n.ts._ffVisibility.private }}</option>
|
||||||
>
|
</MkSelect>
|
||||||
</MkInput>
|
</SearchMarker>
|
||||||
</div>
|
|
||||||
|
|
||||||
<template #caption>
|
<SearchMarker :keywords="['online', 'status']">
|
||||||
<div>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBeforeDescription }}</div>
|
<MkSwitch v-model="hideOnlineStatus" @update:modelValue="save()">
|
||||||
<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 #label><SearchLabel>{{ i18n.ts.hideOnlineStatus }}</SearchLabel></template>
|
||||||
</template>
|
<template #caption><SearchKeyword>{{ i18n.ts.hideOnlineStatusDescription }}</SearchKeyword></template>
|
||||||
</FormSlot>
|
</MkSwitch>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
<FormSlot>
|
<SearchMarker :keywords="['crawle', 'index', 'search']">
|
||||||
<template #label>{{ i18n.ts._accountSettings.makeNotesHiddenBefore }}</template>
|
<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>
|
||||||
|
|
||||||
<div class="_gaps_s">
|
<SearchMarker :keywords="['crawle', 'ai']">
|
||||||
<MkSelect :modelValue="makeNotesHiddenBefore_type" @update:modelValue="makeNotesHiddenBefore = $event === 'relative' ? -604800 : $event === 'absolute' ? Math.floor(Date.now() / 1000) : null">
|
<MkSwitch v-model="preventAiLearning" @update:modelValue="save()">
|
||||||
<option :value="null">{{ i18n.ts.none }}</option>
|
<template #label><SearchLabel>{{ i18n.ts.preventAiLearning }}</SearchLabel></template>
|
||||||
<option value="relative">{{ i18n.ts._accountSettings.notesHavePassedSpecifiedPeriod }}</option>
|
<template #caption><SearchKeyword>{{ i18n.ts.preventAiLearningDescription }}</SearchKeyword></template>
|
||||||
<option value="absolute">{{ i18n.ts._accountSettings.notesOlderThanSpecifiedDateAndTime }}</option>
|
</MkSwitch>
|
||||||
</MkSelect>
|
</SearchMarker>
|
||||||
|
|
||||||
<MkSelect v-if="makeNotesHiddenBefore_type === 'relative'" v-model="makeNotesHiddenBefore">
|
<SearchMarker :keywords="['explore']">
|
||||||
<option :value="-3600">{{ i18n.ts.oneHour }}</option>
|
<MkSwitch v-model="isExplorable" @update:modelValue="save()">
|
||||||
<option :value="-86400">{{ i18n.ts.oneDay }}</option>
|
<template #label><SearchLabel>{{ i18n.ts.makeExplorable }}</SearchLabel></template>
|
||||||
<option :value="-259200">{{ i18n.ts.threeDays }}</option>
|
<template #caption><SearchKeyword>{{ i18n.ts.makeExplorableDescription }}</SearchKeyword></template>
|
||||||
<option :value="-604800">{{ i18n.ts.oneWeek }}</option>
|
</MkSwitch>
|
||||||
<option :value="-2592000">{{ i18n.ts.oneMonth }}</option>
|
</SearchMarker>
|
||||||
<option :value="-7776000">{{ i18n.ts.threeMonths }}</option>
|
|
||||||
<option :value="-31104000">{{ i18n.ts.oneYear }}</option>
|
|
||||||
</MkSelect>
|
|
||||||
|
|
||||||
<MkInput
|
<SearchMarker :keywords="['lockdown']">
|
||||||
v-if="makeNotesHiddenBefore_type === 'absolute'"
|
<FormSection>
|
||||||
:modelValue="formatDateTimeString(new Date(makeNotesHiddenBefore * 1000), 'yyyy-MM-dd')"
|
<template #label><SearchLabel>{{ i18n.ts.lockdown }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||||
type="date"
|
|
||||||
:manualSave="true"
|
|
||||||
@update:modelValue="makeNotesHiddenBefore = Math.floor(new Date($event).getTime() / 1000)"
|
|
||||||
>
|
|
||||||
</MkInput>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<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_m">
|
<div class="_gaps_m">
|
||||||
<MkSelect v-model="defaultNoteVisibility">
|
<SearchMarker :keywords="['login', 'signin']">
|
||||||
<option value="public">{{ i18n.ts._visibility.public }}</option>
|
<MkSwitch :modelValue="requireSigninToViewContents" @update:modelValue="update_requireSigninToViewContents">
|
||||||
<option value="home">{{ i18n.ts._visibility.home }}</option>
|
<template #label><SearchLabel>{{ i18n.ts._accountSettings.requireSigninToViewContents }}</SearchLabel></template>
|
||||||
<option value="followers">{{ i18n.ts._visibility.followers }}</option>
|
<template #caption>
|
||||||
<option value="specified">{{ i18n.ts._visibility.specified }}</option>
|
<div>{{ i18n.ts._accountSettings.requireSigninToViewContentsDescription1 }}</div>
|
||||||
</MkSelect>
|
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.requireSigninToViewContentsDescription2 }}</div>
|
||||||
<MkSwitch v-model="defaultNoteLocalOnly">{{ i18n.ts._visibility.disableFederation }}</MkSwitch>
|
<div v-if="instance.federation !== 'none'"><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.requireSigninToViewContentsDescription3 }}</div>
|
||||||
</div>
|
</template>
|
||||||
</MkFolder>
|
</MkSwitch>
|
||||||
</div>
|
</SearchMarker>
|
||||||
</FormSection>
|
|
||||||
|
|
||||||
<MkSwitch v-model="keepCw" @update:modelValue="save()">{{ i18n.ts.keepCw }}</MkSwitch>
|
<SearchMarker :keywords="['follower']">
|
||||||
</div>
|
<FormSlot>
|
||||||
|
<template #label><SearchLabel>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBefore }}</SearchLabel></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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<SearchMarker :keywords="['hidden']">
|
||||||
|
<FormSlot>
|
||||||
|
<template #label><SearchLabel>{{ i18n.ts._accountSettings.makeNotesHiddenBefore }}</SearchLabel></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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<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>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@@ -174,6 +224,7 @@ import FormSlot from '@/components/form/slot.vue';
|
|||||||
import { formatDateTimeString } from '@/scripts/format-time-string.js';
|
import { formatDateTimeString } from '@/scripts/format-time-string.js';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
|
import MkDisableSection from '@/components/MkDisableSection.vue';
|
||||||
|
|
||||||
const $i = signinRequired();
|
const $i = signinRequired();
|
||||||
|
|
||||||
|
@@ -4,115 +4,152 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="_gaps_m">
|
<SearchMarker path="/settings/profile" :label="i18n.ts.profile" :keywords="['profile']" icon="ti ti-user">
|
||||||
<div class="_panel">
|
<div class="_gaps_m">
|
||||||
<div :class="$style.banner" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }">
|
<div class="_panel">
|
||||||
<MkButton primary rounded :class="$style.bannerEdit" @click="changeBanner">{{ i18n.ts._profile.changeBanner }}</MkButton>
|
<div :class="$style.banner" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }">
|
||||||
</div>
|
<div :class="$style.bannerEdit">
|
||||||
<div :class="$style.avatarContainer">
|
<SearchMarker :keywords="['banner', 'change']">
|
||||||
<MkAvatar :class="$style.avatar" :user="$i" forceShowDecoration @click="changeAvatar"/>
|
<MkButton primary rounded @click="changeBanner"><SearchLabel>{{ i18n.ts._profile.changeBanner }}</SearchLabel></MkButton>
|
||||||
<div class="_buttonsCenter">
|
</SearchMarker>
|
||||||
<MkButton primary rounded @click="changeAvatar">{{ i18n.ts._profile.changeAvatar }}</MkButton>
|
</div>
|
||||||
<MkButton primary rounded link to="/settings/avatar-decoration">{{ i18n.ts.decorate }} <i class="ti ti-sparkles"></i></MkButton>
|
</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>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<MkInput v-model="profile.name" :max="30" manualSave :mfmAutocomplete="['emoji']">
|
<SearchMarker :keywords="['name']">
|
||||||
<template #label>{{ i18n.ts._profile.name }}</template>
|
<MkInput v-model="profile.name" :max="30" manualSave :mfmAutocomplete="['emoji']">
|
||||||
</MkInput>
|
<template #label><SearchLabel>{{ i18n.ts._profile.name }}</SearchLabel></template>
|
||||||
|
</MkInput>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
<MkTextarea v-model="profile.description" :max="500" tall manualSave mfmAutocomplete :mfmPreview="true">
|
<SearchMarker :keywords="['description', 'bio']">
|
||||||
<template #label>{{ i18n.ts._profile.description }}</template>
|
<MkTextarea v-model="profile.description" :max="500" tall manualSave mfmAutocomplete :mfmPreview="true">
|
||||||
<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template>
|
<template #label><SearchLabel>{{ i18n.ts._profile.description }}</SearchLabel></template>
|
||||||
</MkTextarea>
|
<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template>
|
||||||
|
</MkTextarea>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
<MkInput v-model="profile.location" manualSave>
|
<SearchMarker :keywords="['location', 'locale']">
|
||||||
<template #label>{{ i18n.ts.location }}</template>
|
<MkInput v-model="profile.location" manualSave>
|
||||||
<template #prefix><i class="ti ti-map-pin"></i></template>
|
<template #label><SearchLabel>{{ i18n.ts.location }}</SearchLabel></template>
|
||||||
</MkInput>
|
<template #prefix><i class="ti ti-map-pin"></i></template>
|
||||||
|
</MkInput>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
<MkInput v-model="profile.birthday" type="date" manualSave>
|
<SearchMarker :keywords="['birthday', 'birthdate', 'age']">
|
||||||
<template #label>{{ i18n.ts.birthday }}</template>
|
<MkInput v-model="profile.birthday" type="date" manualSave>
|
||||||
<template #prefix><i class="ti ti-cake"></i></template>
|
<template #label><SearchLabel>{{ i18n.ts.birthday }}</SearchLabel></template>
|
||||||
</MkInput>
|
<template #prefix><i class="ti ti-cake"></i></template>
|
||||||
|
</MkInput>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
<MkSelect v-model="profile.lang">
|
<SearchMarker :keywords="['language', 'locale']">
|
||||||
<template #label>{{ i18n.ts.language }}</template>
|
<MkSelect v-model="profile.lang">
|
||||||
<option v-for="x in Object.keys(langmap)" :key="x" :value="x">{{ langmap[x].nativeName }}</option>
|
<template #label><SearchLabel>{{ i18n.ts.language }}</SearchLabel></template>
|
||||||
</MkSelect>
|
<option v-for="x in Object.keys(langmap)" :key="x" :value="x">{{ langmap[x].nativeName }}</option>
|
||||||
|
</MkSelect>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
<FormSlot>
|
<SearchMarker :keywords="['metadata']">
|
||||||
<MkFolder>
|
<FormSlot>
|
||||||
<template #icon><i class="ti ti-list"></i></template>
|
<MkFolder>
|
||||||
<template #label>{{ i18n.ts._profile.metadataEdit }}</template>
|
<template #icon><i class="ti ti-list"></i></template>
|
||||||
<template #footer>
|
<template #label><SearchLabel>{{ i18n.ts._profile.metadataEdit }}</SearchLabel></template>
|
||||||
<div class="_buttons">
|
<template #footer>
|
||||||
<MkButton primary @click="saveFields"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
|
<div class="_buttons">
|
||||||
<MkButton :disabled="fields.length >= 16" @click="addField"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
<MkButton primary @click="saveFields"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
|
||||||
<MkButton v-if="!fieldEditMode" :disabled="fields.length <= 1" danger @click="fieldEditMode = !fieldEditMode"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
|
<MkButton :disabled="fields.length >= 16" @click="addField"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
||||||
<MkButton v-else @click="fieldEditMode = !fieldEditMode"><i class="ti ti-arrows-sort"></i> {{ i18n.ts.rearrange }}</MkButton>
|
<MkButton v-if="!fieldEditMode" :disabled="fields.length <= 1" danger @click="fieldEditMode = !fieldEditMode"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
|
||||||
</div>
|
<MkButton v-else @click="fieldEditMode = !fieldEditMode"><i class="ti ti-arrows-sort"></i> {{ i18n.ts.rearrange }}</MkButton>
|
||||||
</template>
|
|
||||||
|
|
||||||
<div :class="$style.metadataRoot" class="_gaps_s">
|
|
||||||
<MkInfo>{{ i18n.ts._profile.verifiedLinkDescription }}</MkInfo>
|
|
||||||
|
|
||||||
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Sortable>
|
|
||||||
</div>
|
|
||||||
</MkFolder>
|
|
||||||
<template #caption>{{ i18n.ts._profile.metadataDescription }}</template>
|
|
||||||
</FormSlot>
|
|
||||||
|
|
||||||
<MkInput v-model="profile.followedMessage" :max="200" manualSave :mfmPreview="false">
|
<div :class="$style.metadataRoot" class="_gaps_s">
|
||||||
<template #label>{{ i18n.ts._profile.followedMessage }}<span class="_beta">{{ i18n.ts.beta }}</span></template>
|
<MkInfo>{{ i18n.ts._profile.verifiedLinkDescription }}</MkInfo>
|
||||||
<template #caption>
|
|
||||||
<div>{{ i18n.ts._profile.followedMessageDescription }}</div>
|
|
||||||
<div>{{ i18n.ts._profile.followedMessageDescriptionForLockedAccount }}</div>
|
|
||||||
</template>
|
|
||||||
</MkInput>
|
|
||||||
|
|
||||||
<MkSelect v-model="reactionAcceptance">
|
<Sortable
|
||||||
<template #label>{{ i18n.ts.reactionAcceptance }}</template>
|
v-model="fields"
|
||||||
<option :value="null">{{ i18n.ts.all }}</option>
|
class="_gaps_s"
|
||||||
<option value="likeOnlyForRemote">{{ i18n.ts.likeOnlyForRemote }}</option>
|
itemKey="id"
|
||||||
<option value="nonSensitiveOnly">{{ i18n.ts.nonSensitiveOnly }}</option>
|
:animation="150"
|
||||||
<option value="nonSensitiveOnlyForLocalLikeOnlyForRemote">{{ i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote }}</option>
|
:handle="'.' + $style.dragItemHandle"
|
||||||
<option value="likeOnly">{{ i18n.ts.likeOnly }}</option>
|
@start="e => e.item.classList.add('active')"
|
||||||
</MkSelect>
|
@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>
|
<SearchMarker :keywords="['follow', 'message']">
|
||||||
<template #label>{{ i18n.ts.advancedSettings }}</template>
|
<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">
|
<SearchMarker :keywords="['reaction']">
|
||||||
<MkSwitch v-model="profile.isCat">{{ i18n.ts.flagAsCat }}<template #caption>{{ i18n.ts.flagAsCatDescription }}</template></MkSwitch>
|
<MkSelect v-model="reactionAcceptance">
|
||||||
<MkSwitch v-model="profile.isBot">{{ i18n.ts.flagAsBot }}<template #caption>{{ i18n.ts.flagAsBotDescription }}</template></MkSwitch>
|
<template #label><SearchLabel>{{ i18n.ts.reactionAcceptance }}</SearchLabel></template>
|
||||||
</div>
|
<option :value="null">{{ i18n.ts.all }}</option>
|
||||||
</MkFolder>
|
<option value="likeOnlyForRemote">{{ i18n.ts.likeOnlyForRemote }}</option>
|
||||||
</div>
|
<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>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@@ -4,39 +4,48 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="_gaps_m">
|
<SearchMarker path="/settings/security" :label="i18n.ts.security" :keywords="['security']" icon="ti ti-lock" :inlining="['2fa']">
|
||||||
<FormSection first>
|
<div class="_gaps_m">
|
||||||
<template #label>{{ i18n.ts.password }}</template>
|
<SearchMarker :keywords="['password']">
|
||||||
<MkButton primary @click="change()">{{ i18n.ts.changePassword }}</MkButton>
|
<FormSection first>
|
||||||
</FormSection>
|
<template #label><SearchLabel>{{ i18n.ts.password }}</SearchLabel></template>
|
||||||
|
|
||||||
<X2fa/>
|
<SearchMarker>
|
||||||
|
<MkButton primary @click="change()">
|
||||||
|
<SearchLabel>{{ i18n.ts.changePassword }}</SearchLabel>
|
||||||
|
</MkButton>
|
||||||
|
</SearchMarker>
|
||||||
|
</FormSection>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
<FormSection>
|
<X2fa/>
|
||||||
<template #label>{{ i18n.ts.signinHistory }}</template>
|
|
||||||
<MkPagination :pagination="pagination" disableAutoLoad>
|
<FormSection>
|
||||||
<template #default="{items}">
|
<template #label>{{ i18n.ts.signinHistory }}</template>
|
||||||
<div>
|
<MkPagination :pagination="pagination" disableAutoLoad>
|
||||||
<div v-for="item in items" :key="item.id" v-panel class="timnmucd">
|
<template #default="{items}">
|
||||||
<header>
|
<div>
|
||||||
<i v-if="item.success" class="ti ti-check icon succ"></i>
|
<div v-for="item in items" :key="item.id" v-panel class="timnmucd">
|
||||||
<i v-else class="ti ti-circle-x icon fail"></i>
|
<header>
|
||||||
<code class="ip _monospace">{{ item.ip }}</code>
|
<i v-if="item.success" class="ti ti-check icon succ"></i>
|
||||||
<MkTime :time="item.createdAt" class="time"/>
|
<i v-else class="ti ti-circle-x icon fail"></i>
|
||||||
</header>
|
<code class="ip _monospace">{{ item.ip }}</code>
|
||||||
|
<MkTime :time="item.createdAt" class="time"/>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</template>
|
</MkPagination>
|
||||||
</MkPagination>
|
</FormSection>
|
||||||
</FormSection>
|
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<FormSlot>
|
<FormSlot>
|
||||||
<MkButton danger @click="regenerateToken"><i class="ti ti-refresh"></i> {{ i18n.ts.regenerateLoginToken }}</MkButton>
|
<MkButton danger @click="regenerateToken"><i class="ti ti-refresh"></i> {{ i18n.ts.regenerateLoginToken }}</MkButton>
|
||||||
<template #caption>{{ i18n.ts.regenerateLoginTokenDescription }}</template>
|
<template #caption>{{ i18n.ts.regenerateLoginTokenDescription }}</template>
|
||||||
</FormSlot>
|
</FormSlot>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
</div>
|
</div>
|
||||||
|
</SearchMarker>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@@ -4,43 +4,53 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="_gaps_m">
|
<SearchMarker path="/settings/sounds" :label="i18n.ts.sounds" :keywords="['sounds']" icon="ti ti-music">
|
||||||
<MkSwitch v-model="notUseSound">
|
<div class="_gaps_m">
|
||||||
<template #label>{{ i18n.ts.notUseSound }}</template>
|
<SearchMarker :keywords="['mute']">
|
||||||
</MkSwitch>
|
<MkSwitch v-model="notUseSound">
|
||||||
<MkSwitch v-model="useSoundOnlyWhenActive">
|
<template #label><SearchLabel>{{ i18n.ts.notUseSound }}</SearchLabel></template>
|
||||||
<template #label>{{ i18n.ts.useSoundOnlyWhenActive }}</template>
|
</MkSwitch>
|
||||||
</MkSwitch>
|
</SearchMarker>
|
||||||
<MkRange v-model="masterVolume" :min="0" :max="1" :step="0.05" :textConverter="(v) => `${Math.floor(v * 100)}%`">
|
|
||||||
<template #label>{{ i18n.ts.masterVolume }}</template>
|
|
||||||
</MkRange>
|
|
||||||
|
|
||||||
<FormSection>
|
<SearchMarker :keywords="['active', 'mute']">
|
||||||
<template #label>{{ i18n.ts.sounds }}</template>
|
<MkSwitch v-model="useSoundOnlyWhenActive">
|
||||||
<div class="_gaps_s">
|
<template #label><SearchLabel>{{ i18n.ts.useSoundOnlyWhenActive }}</SearchLabel></template>
|
||||||
<MkFolder v-for="type in operationTypes" :key="type">
|
</MkSwitch>
|
||||||
<template #label>{{ i18n.ts._sfx[type] }}</template>
|
</SearchMarker>
|
||||||
<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>
|
<SearchMarker :keywords="['volume', 'master']">
|
||||||
</div>
|
<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>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import type { Ref } from 'vue';
|
|
||||||
import XSound from './sounds.sound.vue';
|
import XSound from './sounds.sound.vue';
|
||||||
|
import type { Ref } from 'vue';
|
||||||
import type { SoundType, OperationType } from '@/scripts/sound.js';
|
import type { SoundType, OperationType } from '@/scripts/sound.js';
|
||||||
import type { SoundStore } from '@/store.js';
|
import type { SoundStore } from '@/store.js';
|
||||||
import MkRange from '@/components/MkRange.vue';
|
import MkRange from '@/components/MkRange.vue';
|
||||||
|
@@ -4,56 +4,72 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="_gaps_m rsljpzjq">
|
<SearchMarker path="/settings/theme" :label="i18n.ts.theme" :keywords="['theme']" icon="ti ti-palette">
|
||||||
<div v-adaptive-border class="rfqxtzch _panel">
|
<div class="_gaps_m rsljpzjq">
|
||||||
<div class="toggle">
|
<div v-adaptive-border class="rfqxtzch _panel">
|
||||||
<div class="toggleWrapper">
|
<div class="toggle">
|
||||||
<input id="dn" v-model="darkMode" type="checkbox" class="dn"/>
|
<div class="toggleWrapper">
|
||||||
<label for="dn" class="toggle">
|
<input id="dn" v-model="darkMode" type="checkbox" class="dn"/>
|
||||||
<span class="before">{{ i18n.ts.light }}</span>
|
<label for="dn" class="toggle">
|
||||||
<span class="after">{{ i18n.ts.dark }}</span>
|
<span class="before">{{ i18n.ts.light }}</span>
|
||||||
<span class="toggle__handler">
|
<span class="after">{{ i18n.ts.dark }}</span>
|
||||||
<span class="crater crater--1"></span>
|
<span class="toggle__handler">
|
||||||
<span class="crater crater--2"></span>
|
<span class="crater crater--1"></span>
|
||||||
<span class="crater crater--3"></span>
|
<span class="crater crater--2"></span>
|
||||||
</span>
|
<span class="crater crater--3"></span>
|
||||||
<span class="star star--1"></span>
|
</span>
|
||||||
<span class="star star--2"></span>
|
<span class="star star--1"></span>
|
||||||
<span class="star star--3"></span>
|
<span class="star star--2"></span>
|
||||||
<span class="star star--4"></span>
|
<span class="star star--3"></span>
|
||||||
<span class="star star--5"></span>
|
<span class="star star--4"></span>
|
||||||
<span class="star star--6"></span>
|
<span class="star star--5"></span>
|
||||||
</label>
|
<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>
|
</div>
|
||||||
<div class="sync">
|
|
||||||
<MkSwitch v-model="syncDeviceDarkMode">{{ i18n.ts.syncDeviceDarkMode }}</MkSwitch>
|
<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>
|
</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>
|
</div>
|
||||||
|
</SearchMarker>
|
||||||
<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>
|
|
||||||
</FormSection>
|
|
||||||
|
|
||||||
<MkButton v-if="wallpaper == null" @click="setWallpaper">{{ i18n.ts.setWallpaper }}</MkButton>
|
|
||||||
<MkButton v-else @click="wallpaper = null">{{ i18n.ts.removeWallpaper }}</MkButton>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@@ -90,9 +90,9 @@ const routes: RouteDef[] = [{
|
|||||||
name: 'security',
|
name: 'security',
|
||||||
component: page(() => import('@/pages/settings/security.vue')),
|
component: page(() => import('@/pages/settings/security.vue')),
|
||||||
}, {
|
}, {
|
||||||
path: '/general',
|
path: '/preferences',
|
||||||
name: 'general',
|
name: 'preferences',
|
||||||
component: page(() => import('@/pages/settings/general.vue')),
|
component: page(() => import('@/pages/settings/preferences.vue')),
|
||||||
}, {
|
}, {
|
||||||
path: '/theme/install',
|
path: '/theme/install',
|
||||||
name: 'theme',
|
name: 'theme',
|
||||||
@@ -105,6 +105,10 @@ const routes: RouteDef[] = [{
|
|||||||
path: '/theme',
|
path: '/theme',
|
||||||
name: 'theme',
|
name: 'theme',
|
||||||
component: page(() => import('@/pages/settings/theme.vue')),
|
component: page(() => import('@/pages/settings/theme.vue')),
|
||||||
|
}, {
|
||||||
|
path: '/appearance',
|
||||||
|
name: 'appearance',
|
||||||
|
component: page(() => import('@/pages/settings/appearance.vue')),
|
||||||
}, {
|
}, {
|
||||||
path: '/navbar',
|
path: '/navbar',
|
||||||
name: 'navbar',
|
name: 'navbar',
|
||||||
@@ -117,6 +121,10 @@ const routes: RouteDef[] = [{
|
|||||||
path: '/sounds',
|
path: '/sounds',
|
||||||
name: 'sounds',
|
name: 'sounds',
|
||||||
component: page(() => import('@/pages/settings/sounds.vue')),
|
component: page(() => import('@/pages/settings/sounds.vue')),
|
||||||
|
}, {
|
||||||
|
path: '/accessibility',
|
||||||
|
name: 'accessibility',
|
||||||
|
component: page(() => import('@/pages/settings/accessibility.vue')),
|
||||||
}, {
|
}, {
|
||||||
path: '/plugin/install',
|
path: '/plugin/install',
|
||||||
name: 'plugin',
|
name: 'plugin',
|
||||||
@@ -161,13 +169,9 @@ const routes: RouteDef[] = [{
|
|||||||
path: '/preferences-backups',
|
path: '/preferences-backups',
|
||||||
name: 'preferences-backups',
|
name: 'preferences-backups',
|
||||||
component: page(() => import('@/pages/settings/preferences-backups.vue')),
|
component: page(() => import('@/pages/settings/preferences-backups.vue')),
|
||||||
}, {
|
|
||||||
path: '/migration',
|
|
||||||
name: 'migration',
|
|
||||||
component: page(() => import('@/pages/settings/migration.vue')),
|
|
||||||
}, {
|
}, {
|
||||||
path: '/custom-css',
|
path: '/custom-css',
|
||||||
name: 'general',
|
name: 'preferences',
|
||||||
component: page(() => import('@/pages/settings/custom-css.vue')),
|
component: page(() => import('@/pages/settings/custom-css.vue')),
|
||||||
}, {
|
}, {
|
||||||
path: '/accounts',
|
path: '/accounts',
|
||||||
|
815
packages/frontend/src/scripts/autogen/settings-search-index.ts
Normal file
815
packages/frontend/src/scripts/autogen/settings-search-index.ts
Normal file
@@ -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;
|
@@ -4,10 +4,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { defineAsyncComponent } from 'vue';
|
import { defineAsyncComponent } from 'vue';
|
||||||
|
import type { Ref, ShallowRef } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { url } from '@@/js/config.js';
|
import { url } from '@@/js/config.js';
|
||||||
import { claimAchievement } from './achievements.js';
|
import { claimAchievement } from './achievements.js';
|
||||||
import type { Ref, ShallowRef } from 'vue';
|
|
||||||
import type { MenuItem } from '@/types/menu.js';
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
import { $i } from '@/account.js';
|
import { $i } from '@/account.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
@@ -208,7 +208,15 @@ export function getNoteMenu(props: {
|
|||||||
}).then(({ canceled }) => {
|
}).then(({ canceled }) => {
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
|
|
||||||
os.post({ initialNote: appearNote, renote: appearNote.renote, reply: appearNote.reply, channel: appearNote.channel, deleteInitialNoteAfterPost: true });
|
misskeyApi('notes/delete', {
|
||||||
|
noteId: appearNote.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
os.post({ initialNote: appearNote, renote: appearNote.renote, reply: appearNote.reply, channel: appearNote.channel });
|
||||||
|
|
||||||
|
if (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 60 && appearNote.userId === $i.id) {
|
||||||
|
claimAchievement('noteDeletedWithin1min');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,5 +19,4 @@ export interface PostFormProps {
|
|||||||
initialVisibleUsers?: Misskey.entities.UserDetailed[];
|
initialVisibleUsers?: Misskey.entities.UserDetailed[];
|
||||||
initialNote?: Misskey.entities.Note;
|
initialNote?: Misskey.entities.Note;
|
||||||
instant?: boolean;
|
instant?: boolean;
|
||||||
deleteInitialNoteAfterPost?: boolean;
|
|
||||||
};
|
};
|
||||||
|
@@ -56,21 +56,35 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button v-if="!forceIconOnly" class="_button" :class="$style.toggleButton" @click="toggleIconOnly">
|
|
||||||
<!--
|
<!--
|
||||||
<svg viewBox="0 0 16 48" :class="$style.toggleButtonShape">
|
<svg viewBox="0 0 16 48" :class="$style.subButtonShape">
|
||||||
<g transform="matrix(0.333333,0,0,0.222222,0.000895785,13.3333)">
|
<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);"/>
|
<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>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
-->
|
-->
|
||||||
<svg viewBox="0 0 16 64" :class="$style.toggleButtonShape">
|
|
||||||
<g transform="matrix(0.333333,0,0,0.222222,0.000895785,21.3333)">
|
<div :class="$style.subButtons">
|
||||||
<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);"/>
|
<div :class="[$style.subButton, $style.menuEditButton]">
|
||||||
</g>
|
<svg viewBox="0 0 16 64" :class="$style.subButtonShape">
|
||||||
</svg>
|
<g transform="matrix(0.333333,0,0,0.222222,0.000895785,21.3333)">
|
||||||
<i :class="'ti ' + `ti-chevron-${ iconOnly ? 'right' : 'left' }`" style="font-size: 12px; margin-left: -8px;"></i>
|
<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);"/>
|
||||||
</button>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -84,6 +98,9 @@ import { defaultStore } from '@/store.js';
|
|||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { instance } from '@/instance.js';
|
import { instance } from '@/instance.js';
|
||||||
import { getHTMLElementOrNull } from '@/scripts/get-dom-node-or-null.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 forceIconOnly = ref(window.innerWidth <= 1279);
|
||||||
const iconOnly = computed(() => {
|
const iconOnly = computed(() => {
|
||||||
@@ -128,6 +145,10 @@ function more(ev: MouseEvent) {
|
|||||||
closed: () => dispose(),
|
closed: () => dispose(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function menuEdit() {
|
||||||
|
router.push('/settings/navbar');
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
@@ -136,6 +157,8 @@ function more(ev: MouseEvent) {
|
|||||||
--nav-icon-only-width: 80px;
|
--nav-icon-only-width: 80px;
|
||||||
--nav-bg-transparent: color(from var(--MI_THEME-navBg) srgb r g b / 0.5);
|
--nav-bg-transparent: color(from var(--MI_THEME-navBg) srgb r g b / 0.5);
|
||||||
|
|
||||||
|
--subButtonWidth: 20px;
|
||||||
|
|
||||||
flex: 0 0 var(--nav-width);
|
flex: 0 0 var(--nav-width);
|
||||||
width: var(--nav-width);
|
width: var(--nav-width);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -171,23 +194,80 @@ function more(ev: MouseEvent) {
|
|||||||
direction: ltr;
|
direction: ltr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggleButton {
|
.subButtons {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 20px;
|
|
||||||
left: var(--nav-width);
|
left: var(--nav-width);
|
||||||
|
bottom: 80px;
|
||||||
z-index: 1001;
|
z-index: 1001;
|
||||||
width: 16px;
|
|
||||||
height: 64px;
|
|
||||||
box-sizing: border-box;
|
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;
|
position: absolute;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
left: 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;
|
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) {
|
.root:not(.iconOnly) {
|
||||||
@@ -419,7 +499,7 @@ function more(ev: MouseEvent) {
|
|||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggleButton {
|
.subButtons {
|
||||||
left: var(--nav-width);
|
left: var(--nav-width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -623,7 +703,7 @@ function more(ev: MouseEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggleButton {
|
.subButtons {
|
||||||
left: var(--nav-icon-only-width);
|
left: var(--nav-icon-only-width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -96,12 +96,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent, provide, onMounted, computed, ref, watch, shallowRef } from 'vue';
|
import { defineAsyncComponent, provide, onMounted, computed, ref, watch, shallowRef } from 'vue';
|
||||||
import type { Ref } from 'vue';
|
|
||||||
import { instanceName } from '@@/js/config.js';
|
import { instanceName } from '@@/js/config.js';
|
||||||
import { CURRENT_STICKY_BOTTOM } from '@@/js/const.js';
|
import { CURRENT_STICKY_BOTTOM } from '@@/js/const.js';
|
||||||
import { isLink } from '@@/js/is-link.js';
|
import { isLink } from '@@/js/is-link.js';
|
||||||
import XCommon from './_common_/common.vue';
|
import XCommon from './_common_/common.vue';
|
||||||
|
import type { Ref } from 'vue';
|
||||||
import type MkStickyContainer from '@/components/global/MkStickyContainer.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 XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
@@ -109,7 +110,6 @@ import { navbarItemDef } from '@/navbar.js';
|
|||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { $i } from '@/account.js';
|
import { $i } from '@/account.js';
|
||||||
import { provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.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 { deviceKind } from '@/scripts/device-kind.js';
|
||||||
import { miLocalStorage } from '@/local-storage.js';
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
import { useScrollPositionManager } from '@/nirax.js';
|
import { useScrollPositionManager } from '@/nirax.js';
|
||||||
@@ -331,6 +331,8 @@ $widgets-hide-threshold: 1090px;
|
|||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
overscroll-behavior: contain;
|
overscroll-behavior: contain;
|
||||||
background: var(--MI_THEME-bg);
|
background: var(--MI_THEME-bg);
|
||||||
|
scroll-padding-top: 60px; // TODO: ちゃんと計算する
|
||||||
|
scroll-padding-bottom: 60px; // TODO: ちゃんと計算する
|
||||||
}
|
}
|
||||||
|
|
||||||
.widgets {
|
.widgets {
|
||||||
|
@@ -10,6 +10,7 @@ import meta from '../../package.json';
|
|||||||
import packageInfo from './package.json' with { type: 'json' };
|
import packageInfo from './package.json' with { type: 'json' };
|
||||||
import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-module-class-name.js';
|
import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-module-class-name.js';
|
||||||
import pluginJson5 from './vite.json5.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 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;
|
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,
|
let h1 = 0xdeadbeef ^ seed,
|
||||||
h2 = 0x41c6ce57 ^ seed;
|
h2 = 0x41c6ce57 ^ seed;
|
||||||
for (let i = 0, ch; i < str.length; i++) {
|
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);
|
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) {
|
if (n === 0) {
|
||||||
return '0';
|
return '0';
|
||||||
}
|
}
|
||||||
@@ -83,6 +84,11 @@ export function getConfig(): UserConfig {
|
|||||||
},
|
},
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
|
pluginCreateSearchIndex({
|
||||||
|
targetFilePaths: ['src/pages/settings/*.vue'],
|
||||||
|
exportFilePath: './src/scripts/autogen/settings-search-index.ts',
|
||||||
|
verbose: process.env.FRONTEND_SEARCH_INDEX_VERBOSE === 'true',
|
||||||
|
}),
|
||||||
pluginVue(),
|
pluginVue(),
|
||||||
pluginUnwindCssModuleClassName(),
|
pluginUnwindCssModuleClassName(),
|
||||||
pluginJson5(),
|
pluginJson5(),
|
||||||
|
@@ -8,9 +8,9 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@readme/openapi-parser": "2.7.0",
|
"@readme/openapi-parser": "2.7.0",
|
||||||
"@types/node": "22.13.7",
|
"@types/node": "22.13.9",
|
||||||
"@typescript-eslint/eslint-plugin": "8.25.0",
|
"@typescript-eslint/eslint-plugin": "8.26.0",
|
||||||
"@typescript-eslint/parser": "8.25.0",
|
"@typescript-eslint/parser": "8.26.0",
|
||||||
"openapi-types": "12.1.3",
|
"openapi-types": "12.1.3",
|
||||||
"openapi-typescript": "6.7.6",
|
"openapi-typescript": "6.7.6",
|
||||||
"ts-case-convert": "2.1.0",
|
"ts-case-convert": "2.1.0",
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"name": "misskey-js",
|
"name": "misskey-js",
|
||||||
"version": "2025.3.0-beta.0",
|
"version": "2025.3.1-beta.0",
|
||||||
"description": "Misskey SDK for JavaScript",
|
"description": "Misskey SDK for JavaScript",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
@@ -35,12 +35,12 @@
|
|||||||
"directory": "packages/misskey-js"
|
"directory": "packages/misskey-js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@microsoft/api-extractor": "7.51.0",
|
"@microsoft/api-extractor": "7.51.1",
|
||||||
"@swc/jest": "0.2.37",
|
"@swc/jest": "0.2.37",
|
||||||
"@types/jest": "29.5.14",
|
"@types/jest": "29.5.14",
|
||||||
"@types/node": "22.13.7",
|
"@types/node": "22.13.9",
|
||||||
"@typescript-eslint/eslint-plugin": "8.25.0",
|
"@typescript-eslint/eslint-plugin": "8.26.0",
|
||||||
"@typescript-eslint/parser": "8.25.0",
|
"@typescript-eslint/parser": "8.26.0",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
"jest-fetch-mock": "3.0.3",
|
"jest-fetch-mock": "3.0.3",
|
||||||
"jest-websocket-mock": "2.5.0",
|
"jest-websocket-mock": "2.5.0",
|
||||||
|
1358
pnpm-lock.yaml
generated
1358
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,29 @@
|
|||||||
packages:
|
packages:
|
||||||
- 'packages/backend'
|
- packages/backend
|
||||||
- 'packages/frontend-shared'
|
- packages/frontend-shared
|
||||||
- 'packages/frontend'
|
- packages/frontend
|
||||||
- 'packages/frontend-embed'
|
- packages/frontend-embed
|
||||||
- 'packages/sw'
|
- packages/sw
|
||||||
- 'packages/misskey-js'
|
- packages/misskey-js
|
||||||
- 'packages/misskey-js/generator'
|
- packages/misskey-js/generator
|
||||||
- 'packages/misskey-reversi'
|
- packages/misskey-reversi
|
||||||
- 'packages/misskey-bubble-game'
|
- 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
|
||||||
|
118
scripts/changelog-checker/package-lock.json
generated
118
scripts/changelog-checker/package-lock.json
generated
@@ -9,16 +9,16 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/mdast": "4.0.4",
|
"@types/mdast": "4.0.4",
|
||||||
"@types/node": "22.13.7",
|
"@types/node": "22.13.9",
|
||||||
"@vitest/coverage-v8": "3.0.7",
|
"@vitest/coverage-v8": "3.0.8",
|
||||||
"mdast-util-to-string": "4.0.0",
|
"mdast-util-to-string": "4.0.0",
|
||||||
"remark": "15.0.1",
|
"remark": "15.0.1",
|
||||||
"remark-parse": "11.0.0",
|
"remark-parse": "11.0.0",
|
||||||
"typescript": "5.8.2",
|
"typescript": "5.8.2",
|
||||||
"unified": "11.0.5",
|
"unified": "11.0.5",
|
||||||
"vite": "6.2.0",
|
"vite": "6.2.1",
|
||||||
"vite-node": "3.0.7",
|
"vite-node": "3.0.8",
|
||||||
"vitest": "3.0.7"
|
"vitest": "3.0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ampproject/remapping": {
|
"node_modules/@ampproject/remapping": {
|
||||||
@@ -909,9 +909,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.13.7",
|
"version": "22.13.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.9.tgz",
|
||||||
"integrity": "sha512-oU2q+BsQldB9lYxHNp/5aZO+/Bs0Usa74Abo9mAKulz4ahQyXRHK6UVKYIN8KSC8HXwhWSi7b49JnX+txuac0w==",
|
"integrity": "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -925,9 +925,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/coverage-v8": {
|
"node_modules/@vitest/coverage-v8": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.8.tgz",
|
||||||
"integrity": "sha512-Av8WgBJLTrfLOer0uy3CxjlVuWK4CzcLBndW1Nm2vI+3hZ2ozHututkfc7Blu1u6waeQ7J8gzPK/AsBRnWA5mQ==",
|
"integrity": "sha512-y7SAKsQirsEJ2F8bulBck4DoluhI2EEgTimHd6EEUgJBGKy9tC25cpywh1MH4FvDGoG2Unt7+asVd1kj4qOSAw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -948,8 +948,8 @@
|
|||||||
"url": "https://opencollective.com/vitest"
|
"url": "https://opencollective.com/vitest"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@vitest/browser": "3.0.7",
|
"@vitest/browser": "3.0.8",
|
||||||
"vitest": "3.0.7"
|
"vitest": "3.0.8"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@vitest/browser": {
|
"@vitest/browser": {
|
||||||
@@ -958,14 +958,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/expect": {
|
"node_modules/@vitest/expect": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.8.tgz",
|
||||||
"integrity": "sha512-QP25f+YJhzPfHrHfYHtvRn+uvkCFCqFtW9CktfBxmB+25QqWsx7VB2As6f4GmwllHLDhXNHvqedwhvMmSnNmjw==",
|
"integrity": "sha512-Xu6TTIavTvSSS6LZaA3EebWFr6tsoXPetOWNMOlc7LO88QVVBwq2oQWBoDiLCN6YTvNYsGSjqOO8CAdjom5DCQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/spy": "3.0.7",
|
"@vitest/spy": "3.0.8",
|
||||||
"@vitest/utils": "3.0.7",
|
"@vitest/utils": "3.0.8",
|
||||||
"chai": "^5.2.0",
|
"chai": "^5.2.0",
|
||||||
"tinyrainbow": "^2.0.0"
|
"tinyrainbow": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -974,13 +974,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/mocker": {
|
"node_modules/@vitest/mocker": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.8.tgz",
|
||||||
"integrity": "sha512-qui+3BLz9Eonx4EAuR/i+QlCX6AUZ35taDQgwGkK/Tw6/WgwodSrjN1X2xf69IA/643ZX5zNKIn2svvtZDrs4w==",
|
"integrity": "sha512-n3LjS7fcW1BCoF+zWZxG7/5XvuYH+lsFg+BDwwAz0arIwHQJFUEsKBQ0BLU49fCxuM/2HSeBPHQD8WjgrxMfow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/spy": "3.0.7",
|
"@vitest/spy": "3.0.8",
|
||||||
"estree-walker": "^3.0.3",
|
"estree-walker": "^3.0.3",
|
||||||
"magic-string": "^0.30.17"
|
"magic-string": "^0.30.17"
|
||||||
},
|
},
|
||||||
@@ -1001,9 +1001,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/pretty-format": {
|
"node_modules/@vitest/pretty-format": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.8.tgz",
|
||||||
"integrity": "sha512-CiRY0BViD/V8uwuEzz9Yapyao+M9M008/9oMOSQydwbwb+CMokEq3XVaF3XK/VWaOK0Jm9z7ENhybg70Gtxsmg==",
|
"integrity": "sha512-BNqwbEyitFhzYMYHUVbIvepOyeQOSFA/NeJMIP9enMntkkxLgOcgABH6fjyXG85ipTgvero6noreavGIqfJcIg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1014,13 +1014,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/runner": {
|
"node_modules/@vitest/runner": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.8.tgz",
|
||||||
"integrity": "sha512-WeEl38Z0S2ZcuRTeyYqaZtm4e26tq6ZFqh5y8YD9YxfWuu0OFiGFUbnxNynwLjNRHPsXyee2M9tV7YxOTPZl2g==",
|
"integrity": "sha512-c7UUw6gEcOzI8fih+uaAXS5DwjlBaCJUo7KJ4VvJcjL95+DSR1kova2hFuRt3w41KZEFcOEiq098KkyrjXeM5w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/utils": "3.0.7",
|
"@vitest/utils": "3.0.8",
|
||||||
"pathe": "^2.0.3"
|
"pathe": "^2.0.3"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -1028,13 +1028,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/snapshot": {
|
"node_modules/@vitest/snapshot": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.8.tgz",
|
||||||
"integrity": "sha512-eqTUryJWQN0Rtf5yqCGTQWsCFOQe4eNz5Twsu21xYEcnFJtMU5XvmG0vgebhdLlrHQTSq5p8vWHJIeJQV8ovsA==",
|
"integrity": "sha512-x8IlMGSEMugakInj44nUrLSILh/zy1f2/BgH0UeHpNyOocG18M9CWVIFBaXPt8TrqVZWmcPjwfG/ht5tnpba8A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "3.0.7",
|
"@vitest/pretty-format": "3.0.8",
|
||||||
"magic-string": "^0.30.17",
|
"magic-string": "^0.30.17",
|
||||||
"pathe": "^2.0.3"
|
"pathe": "^2.0.3"
|
||||||
},
|
},
|
||||||
@@ -1043,9 +1043,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/spy": {
|
"node_modules/@vitest/spy": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.8.tgz",
|
||||||
"integrity": "sha512-4T4WcsibB0B6hrKdAZTM37ekuyFZt2cGbEGd2+L0P8ov15J1/HUsUaqkXEQPNAWr4BtPPe1gI+FYfMHhEKfR8w==",
|
"integrity": "sha512-MR+PzJa+22vFKYb934CejhR4BeRpMSoxkvNoDit68GQxRLSf11aT6CTj3XaqUU9rxgWJFnqicN/wxw6yBRkI1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1056,13 +1056,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/utils": {
|
"node_modules/@vitest/utils": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.8.tgz",
|
||||||
"integrity": "sha512-xePVpCRfooFX3rANQjwoditoXgWb1MaFbzmGuPP59MK6i13mrnDw/yEIyJudLeW6/38mCNcwCiJIGmpDPibAIg==",
|
"integrity": "sha512-nkBC3aEhfX2PdtQI/QwAWp8qZWwzASsU4Npbcd5RdMPBSSLCpkZp52P3xku3s3uA0HIEhGvEcF8rNkBsz9dQ4Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "3.0.7",
|
"@vitest/pretty-format": "3.0.8",
|
||||||
"loupe": "^3.1.3",
|
"loupe": "^3.1.3",
|
||||||
"tinyrainbow": "^2.0.0"
|
"tinyrainbow": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -2724,9 +2724,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.1.tgz",
|
||||||
"integrity": "sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==",
|
"integrity": "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -2796,9 +2796,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite-node": {
|
"node_modules/vite-node": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.8.tgz",
|
||||||
"integrity": "sha512-2fX0QwX4GkkkpULXdT1Pf4q0tC1i1lFOyseKoonavXUNlQ77KpW2XqBGGNIm/J4Ows4KxgGJzDguYVPKwG/n5A==",
|
"integrity": "sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -2819,19 +2819,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vitest": {
|
"node_modules/vitest": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.8.tgz",
|
||||||
"integrity": "sha512-IP7gPK3LS3Fvn44x30X1dM9vtawm0aesAa2yBIZ9vQf+qB69NXC5776+Qmcr7ohUXIQuLhk7xQR0aSUIDPqavg==",
|
"integrity": "sha512-dfqAsNqRGUc8hB9OVR2P0w8PZPEckti2+5rdZip0WIz9WW0MnImJ8XiR61QhqLa92EQzKP2uPkzenKOAHyEIbA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/expect": "3.0.7",
|
"@vitest/expect": "3.0.8",
|
||||||
"@vitest/mocker": "3.0.7",
|
"@vitest/mocker": "3.0.8",
|
||||||
"@vitest/pretty-format": "^3.0.7",
|
"@vitest/pretty-format": "^3.0.8",
|
||||||
"@vitest/runner": "3.0.7",
|
"@vitest/runner": "3.0.8",
|
||||||
"@vitest/snapshot": "3.0.7",
|
"@vitest/snapshot": "3.0.8",
|
||||||
"@vitest/spy": "3.0.7",
|
"@vitest/spy": "3.0.8",
|
||||||
"@vitest/utils": "3.0.7",
|
"@vitest/utils": "3.0.8",
|
||||||
"chai": "^5.2.0",
|
"chai": "^5.2.0",
|
||||||
"debug": "^4.4.0",
|
"debug": "^4.4.0",
|
||||||
"expect-type": "^1.1.0",
|
"expect-type": "^1.1.0",
|
||||||
@@ -2843,7 +2843,7 @@
|
|||||||
"tinypool": "^1.0.2",
|
"tinypool": "^1.0.2",
|
||||||
"tinyrainbow": "^2.0.0",
|
"tinyrainbow": "^2.0.0",
|
||||||
"vite": "^5.0.0 || ^6.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"
|
"why-is-node-running": "^2.3.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -2859,8 +2859,8 @@
|
|||||||
"@edge-runtime/vm": "*",
|
"@edge-runtime/vm": "*",
|
||||||
"@types/debug": "^4.1.12",
|
"@types/debug": "^4.1.12",
|
||||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
||||||
"@vitest/browser": "3.0.7",
|
"@vitest/browser": "3.0.8",
|
||||||
"@vitest/ui": "3.0.7",
|
"@vitest/ui": "3.0.8",
|
||||||
"happy-dom": "*",
|
"happy-dom": "*",
|
||||||
"jsdom": "*"
|
"jsdom": "*"
|
||||||
},
|
},
|
||||||
|
@@ -10,15 +10,15 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/mdast": "4.0.4",
|
"@types/mdast": "4.0.4",
|
||||||
"@types/node": "22.13.7",
|
"@types/node": "22.13.9",
|
||||||
"@vitest/coverage-v8": "3.0.7",
|
"@vitest/coverage-v8": "3.0.8",
|
||||||
"mdast-util-to-string": "4.0.0",
|
"mdast-util-to-string": "4.0.0",
|
||||||
"remark": "15.0.1",
|
"remark": "15.0.1",
|
||||||
"remark-parse": "11.0.0",
|
"remark-parse": "11.0.0",
|
||||||
"typescript": "5.8.2",
|
"typescript": "5.8.2",
|
||||||
"unified": "11.0.5",
|
"unified": "11.0.5",
|
||||||
"vite": "6.2.0",
|
"vite": "6.2.1",
|
||||||
"vite-node": "3.0.7",
|
"vite-node": "3.0.8",
|
||||||
"vitest": "3.0.7"
|
"vitest": "3.0.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user