diff --git a/.config/docker_example.env b/.config/docker_example.env index 7a0261524b2cd943ba32a02b8a83661ab3025e71..4fe8e76b784b05bfc1868cc8dced53f25bf51516 100644 --- a/.config/docker_example.env +++ b/.config/docker_example.env @@ -2,3 +2,4 @@ POSTGRES_PASSWORD=example-misskey-pass POSTGRES_USER=example-misskey-user POSTGRES_DB=misskey +DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}" diff --git a/.config/docker_example.yml b/.config/docker_example.yml index c6c83a98bf49b7edbb6639e9f32f857638ff9a84..296237c95d4e0aa606206780a096a5eb387f7ec0 100644 --- a/.config/docker_example.yml +++ b/.config/docker_example.yml @@ -2,6 +2,63 @@ # Misskey configuration #â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â” +# ┌──────────────────────────────┠+#───┘ a boring but important thing └──────────────────────────── + +# +# First of all, let me tell you a story that may possibly be +# boring to you and possibly important to you. +# +# Misskey is licensed under the AGPLv3 license. This license is +# known to be often misunderstood. Please read the following +# instructions carefully and select the appropriate option so +# that you do not negligently cause a license violation. +# + +# -------- +# Option 1: If you host Misskey AS-IS (without any changes to +# the source code. forks are not included). +# +# Step 1: Congratulations! You don't need to do anything. + +# -------- +# Option 2: If you have made changes to the source code (forks +# are included) and publish a Git repository of source +# code. There should be no access restrictions on +# this repository. Strictly speaking, it doesn't have +# to be a Git repository, but you'll probably use Git! +# +# Step 1: Build and run the Misskey server first. +# Step 2: Open <https://your.misskey.example/admin/settings> in +# your browser with the administrator account. +# Step 3: Enter the URL of your Git repository in the +# "Repository URL" field. + +# -------- +# Option 3: If neither of the above applies to you. +# (In this case, the source code should be published +# on the Misskey interface. IT IS NOT ENOUGH TO +# DISCLOSE THE SOURCE CODE WEHN A USER REQUESTS IT BY +# E-MAIL OR OTHER MEANS. If you are not satisfied +# with this, it is recommended that you read the +# license again carefully. Anyway, enabling this +# option will automatically generate and publish a +# tarball at build time, protecting you from +# inadvertent license violations. (There is no legal +# guarantee, of course.) The tarball will generated +# from the root directory of your codebase. So it is +# also recommended to check <built/tarball> directory +# once after building and before activating the server +# to avoid ACCIDENTAL LEAKING OF SENSITIVE INFORMATION. +# To prevent certain files from being included in the +# tarball, add a glob pattern after line 15 in +# <scripts/tarball.mjs>. DO NOT FORGET TO BUILD AFTER +# ENABLING THIS OPTION!) +# +# Step 1: Uncomment the following line. +# +# publishTarballInsteadOfProvideRepositoryUrl: true + # ┌─────┠#───┘ URL └───────────────────────────────────────────────────── @@ -148,14 +205,14 @@ id: 'aidx' # Job concurrency per worker # deliverJobConcurrency: 128 # inboxJobConcurrency: 16 -# relashionshipJobConcurrency: 16 -# What's relashionshipJob?: +# relationshipJobConcurrency: 16 +# What's relationshipJob?: # Follow, unfollow, block and unblock(ings) while following-imports, etc. or account migrations. # Job rate limiter # deliverJobPerSec: 128 # inboxJobPerSec: 32 -# relashionshipJobPerSec: 64 +# relationshipJobPerSec: 64 # Job attempts # deliverJobMaxAttempts: 12 diff --git a/.config/example.yml b/.config/example.yml index 4aa7757c611161ecdc8ee5686eceff418b88f4a3..c037a280b6ac2593873cef816b2144413f0fb452 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -2,6 +2,63 @@ # Misskey configuration #â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â” +# ┌──────────────────────────────┠+#───┘ a boring but important thing └──────────────────────────── + +# +# First of all, let me tell you a story that may possibly be +# boring to you and possibly important to you. +# +# Misskey is licensed under the AGPLv3 license. This license is +# known to be often misunderstood. Please read the following +# instructions carefully and select the appropriate option so +# that you do not negligently cause a license violation. +# + +# -------- +# Option 1: If you host Misskey AS-IS (without any changes to +# the source code. forks are not included). +# +# Step 1: Congratulations! You don't need to do anything. + +# -------- +# Option 2: If you have made changes to the source code (forks +# are included) and publish a Git repository of source +# code. There should be no access restrictions on +# this repository. Strictly speaking, it doesn't have +# to be a Git repository, but you'll probably use Git! +# +# Step 1: Build and run the Misskey server first. +# Step 2: Open <https://your.misskey.example/admin/settings> in +# your browser with the administrator account. +# Step 3: Enter the URL of your Git repository in the +# "Repository URL" field. + +# -------- +# Option 3: If neither of the above applies to you. +# (In this case, the source code should be published +# on the Misskey interface. IT IS NOT ENOUGH TO +# DISCLOSE THE SOURCE CODE WEHN A USER REQUESTS IT BY +# E-MAIL OR OTHER MEANS. If you are not satisfied +# with this, it is recommended that you read the +# license again carefully. Anyway, enabling this +# option will automatically generate and publish a +# tarball at build time, protecting you from +# inadvertent license violations. (There is no legal +# guarantee, of course.) The tarball will generated +# from the root directory of your codebase. So it is +# also recommended to check <built/tarball> directory +# once after building and before activating the server +# to avoid ACCIDENTAL LEAKING OF SENSITIVE INFORMATION. +# To prevent certain files from being included in the +# tarball, add a glob pattern after line 15 in +# <scripts/tarball.mjs>. DO NOT FORGET TO BUILD AFTER +# ENABLING THIS OPTION!) +# +# Step 1: Uncomment the following line. +# +# publishTarballInsteadOfProvideRepositoryUrl: true + # ┌─────┠#───┘ URL └───────────────────────────────────────────────────── @@ -118,7 +175,7 @@ redis: # ┌───────────────────────────┠#───┘ MeiliSearch configuration └───────────────────────────── -# You can set scope to local (default value) or global +# You can set scope to local (default value) or global # (include notes from remote). #meilisearch: @@ -160,14 +217,14 @@ id: 'aidx' # Job concurrency per worker #deliverJobConcurrency: 128 #inboxJobConcurrency: 16 -#relashionshipJobConcurrency: 16 -# What's relashionshipJob?: +#relationshipJobConcurrency: 16 +# What's relationshipJob?: # Follow, unfollow, block and unblock(ings) while following-imports, etc. or account migrations. # Job rate limiter #deliverJobPerSec: 128 #inboxJobPerSec: 32 -#relashionshipJobPerSec: 64 +#relationshipJobPerSec: 64 # Job attempts #deliverJobMaxAttempts: 12 @@ -219,7 +276,7 @@ signToActivityPubGet: true checkActivityPubGetSignature: false # For security reasons, uploading attachments from the intranet is prohibited, -# but exceptions can be made from the following settings. Default value is "undefined". +# but exceptions can be made from the following settings. Default value is "undefined". # Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)). #allowedPrivateNetworks: [ # '127.0.0.1/32' diff --git a/.forgejo/workflows/docker-develop.yml b/.forgejo/workflows/docker-develop.yml deleted file mode 100644 index 0c8338c4df0396a78d156da4cc7917e04eba10ee..0000000000000000000000000000000000000000 --- a/.forgejo/workflows/docker-develop.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Publish Docker image (develop) - -on: - push: - branches: - - develop - paths: - - packages/** - - locales/** - workflow_dispatch: - -env: - REGISTRY: git.joinsharkey.org - -jobs: - push_to_registry: - name: Push Docker image to GHCR - runs-on: docker - steps: - - name: install packages - run: apt-get update && apt-get install -y wget git curl - - uses: https://code.forgejo.org/actions/setup-node@v3 - with: - node-version: 20 - - name: Install docker - run: | - echo deb http://deb.debian.org/debian bullseye-backports main | tee /etc/apt/sources.list.d/backports.list && apt-get -qq update - DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -qq -y -t bullseye-backports docker.io - - name: Check out the repo - uses: actions/checkout@v4.1.1 - - name: Set up Docker Buildx - id: buildx - uses: https://github.com/docker/setup-buildx-action@v3.0.0 - with: - platforms: linux/amd64,linux/arm64 - - name: Docker meta - id: meta - uses: https://github.com/docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/sharkey/sharkey - - name: Log in to GHCR - uses: https://github.com/docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: Marie - password: ${{ secrets.TOKEN }} - - name: Build and Push to GHCR - id: build - uses: https://github.com/docker/build-push-action@v5 - with: - builder: ${{ steps.buildx.outputs.name }} - context: . - push: true - platforms: ${{ steps.buildx.outputs.platforms }} - provenance: false - tags: ${{ env.REGISTRY }}/sharkey/sharkey:develop - labels: develop - build-args: NODE_ENV=development \ No newline at end of file diff --git a/.forgejo/workflows/docker.yml b/.forgejo/workflows/docker.yml deleted file mode 100644 index 155c5f7debd322ec53864befca7206ff84a66211..0000000000000000000000000000000000000000 --- a/.forgejo/workflows/docker.yml +++ /dev/null @@ -1,62 +0,0 @@ -name: Publish Docker image - -on: - release: - types: [published] - workflow_dispatch: - -env: - REGISTRY: git.joinsharkey.org - -jobs: - push_to_registry: - name: Push Docker image to GHCR - runs-on: docker - - steps: - - name: install packages - run: apt-get update && apt-get install -y wget git curl - - uses: https://code.forgejo.org/actions/setup-node@v3 - with: - node-version: 20 - - name: Install docker - run: | - echo deb http://deb.debian.org/debian bullseye-backports main | tee /etc/apt/sources.list.d/backports.list && apt-get -qq update - DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -qq -y -t bullseye-backports docker.io - - name: Check out the repo - uses: actions/checkout@v4.1.1 - - name: Set up Docker Buildx - id: buildx - uses: https://github.com/docker/setup-buildx-action@v3.0.0 - with: - platforms: linux/amd64,linux/arm64 - - name: Docker meta - id: meta - uses: https://github.com/docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/sharkey/sharkey - tags: | - type=edge - type=ref,event=pr - type=ref,event=branch - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - type=raw,value=stable - - name: Log in to GHCR - uses: https://github.com/docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: Marie - password: ${{ secrets.TOKEN }} - - name: Build and Push to GHCR - id: build - uses: https://github.com/docker/build-push-action@v5 - with: - builder: ${{ steps.buildx.outputs.name }} - context: . - push: true - platforms: ${{ steps.buildx.outputs.platforms }} - provenance: false - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/.gitea/ISSUE_TEMPLATE/01_bug-report.yml b/.gitea/ISSUE_TEMPLATE/01_bug-report.yml deleted file mode 100644 index 6282cc43f9a78e440711f67e9f6f5ad5204663e9..0000000000000000000000000000000000000000 --- a/.gitea/ISSUE_TEMPLATE/01_bug-report.yml +++ /dev/null @@ -1,97 +0,0 @@ -name: 🛠Bug Report -description: Create a report to help us improve -title: 'bug: ' - -body: - - type: markdown - attributes: - value: | - Thanks for reporting! - First, in order to avoid duplicate Issues, please search to see if the problem you found has already been reported. - Also, If you are NOT owner/admin of server, PLEASE DONT REPORT SERVER SPECIFIC ISSUES TO HERE! (e.g. feature XXX is not working in misskey.example) Please try with another misskey servers, and if your issue is only reproducible with specific server, contact your server's owner/admin first. - - - type: textarea - attributes: - label: 💡 Summary - description: Tell us what the bug is - validations: - required: true - - - type: textarea - attributes: - label: 🥰 Expected Behavior - description: Tell us what should happen - validations: - required: true - - - type: textarea - attributes: - label: 🤬 Actual Behavior - description: | - Tell us what happens instead of the expected behavior. - Please include errors from the developer console and/or server log files if you have access to them. - validations: - required: true - - - type: textarea - attributes: - label: 📠Steps to Reproduce - placeholder: | - 1. - 2. - 3. - validations: - required: false - - - type: textarea - attributes: - label: 💻 Frontend Environment - description: | - Tell us where on the platform it happens - DO NOT WRITE "latest". Please provide the specific version. - - Examples: - * Model and OS of the device(s): MacBook Pro (14inch, 2021), macOS Ventura 13.4 - * Browser: Chrome 113.0.5672.126 - * Server URL: misskey.io - * Misskey: 13.x.x - value: | - * Model and OS of the device(s): - * Browser: - * Server URL: - * Misskey: - render: markdown - validations: - required: false - - - type: textarea - attributes: - label: 🛰 Backend Environment (for server admin) - description: | - Tell us where on the platform it happens - DO NOT WRITE "latest". Please provide the specific version. - If you are using a managed service, put that after the version. - - Examples: - * Installation Method or Hosting Service: docker compose, k8s/docker, systemd, "Misskey install shell script", development environment - * Misskey: 13.x.x - * Node: 20.x.x - * PostgreSQL: 15.x.x - * Redis: 7.x.x - * OS and Architecture: Ubuntu 22.04.2 LTS aarch64 - value: | - * Installation Method or Hosting Service: - * Misskey: - * Node: - * PostgreSQL: - * Redis: - * OS and Architecture: - render: markdown - validations: - required: false - - - type: checkboxes - attributes: - label: Do you want to address this bug yourself? - options: - - label: Yes, I will patch the bug myself and send a pull request diff --git a/.gitea/ISSUE_TEMPLATE/02_feature-request.yml b/.gitea/ISSUE_TEMPLATE/02_feature-request.yml deleted file mode 100644 index d3bf64d8695e4f5ea87a5375c56bbb3cc1d548e2..0000000000000000000000000000000000000000 --- a/.gitea/ISSUE_TEMPLATE/02_feature-request.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: ✨ Feature Request -description: Suggest an idea for this project -title: 'feat: ' - -body: - - type: textarea - attributes: - label: Summary - description: Tell us what the suggestion is - validations: - required: true - - type: textarea - attributes: - label: Purpose - description: Describe the specific problem or need you think this feature will solve, and who it will help. - validations: - required: true - - type: checkboxes - attributes: - label: Do you want to implement this feature yourself? - options: - - label: Yes, I will implement this by myself and send a pull request \ No newline at end of file diff --git a/.gitea/ISSUE_TEMPLATE/config.yml b/.gitea/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index b845c1c9ac8440209a0ebc5dfb8a204868a5d6d5..0000000000000000000000000000000000000000 --- a/.gitea/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,4 +0,0 @@ -contact_links: - - name: 💬 Transfem.org Discord - url: https://discord.gg/HJcAanTR6H - about: Chat freely about Sharkey diff --git a/.gitea/pull_request_template.md b/.gitea/pull_request_template.md deleted file mode 100644 index 63eb2ab623704daaa19e8224d2ad72cde4bb13fc..0000000000000000000000000000000000000000 --- a/.gitea/pull_request_template.md +++ /dev/null @@ -1,24 +0,0 @@ -<!-- ℹ ãŠèªã¿ãã ã•ã„ / README -PRã‚ã‚ŠãŒã¨ã†ã”ã–ã„ã¾ã™ï¼ PRを作æˆã™ã‚‹å‰ã«ã€ã‚³ãƒ³ãƒˆãƒªãƒ“ューションガイドをã”確èªãã ã•ã„: -Thank you for your PR! Before creating a PR, please check the contribution guide: -https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md ---> - -## What -<!-- ã“ã®PRã§ä½•ã‚’ã—ãŸã®ã‹ï¼Ÿ ã©ã†å¤‰ã‚ã‚‹ã®ã‹ï¼Ÿ --> -<!-- What did you do with this PR? How will it change things? --> - -## Why -<!-- ãªãœãã†ã™ã‚‹ã®ã‹ï¼Ÿ ã©ã†ã„ã†æ„図ãªã®ã‹ï¼Ÿ 何ãŒå›°ã£ã¦ã„ã‚‹ã®ã‹ï¼Ÿ --> -<!-- Why do you do it? What are your intentions? What is the problem? --> - -## Additional info (optional) -<!-- テスト観点ãªã© --> -<!-- Test perspective, etc --> - -## Checklist -- [ ] Read the [contribution guide](https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md) -- [ ] Test working in a local environment -- [ ] (If needed) Add story of storybook -- [ ] (If needed) Update CHANGELOG.md -- [ ] (If possible) Add tests \ No newline at end of file diff --git a/.gitignore b/.gitignore index 11e69b26211f93a7b7862f273d154d53b9fea318..2b6a5c1ebfb6b010b34e76ffb65834f86d1dae42 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ docker-compose.yml # misskey /build built +built-test /data /.cache-loader /db @@ -57,6 +58,7 @@ api-docs.json ormconfig.json temp /packages/frontend/src/**/*.stories.ts +tsdoc-metadata.json # Sharkey /packages/megalodon/lib diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index aea1307ca2ec77787997c11c4ba24962051334db..be00aa7097bfcec8af70796b47681c510d66860a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,7 +11,7 @@ testCommit: variables: POSTGRES_PASSWORD: ci script: - - apt-get update && apt-get install -y git wget curl build-essential python3 + - apt-get update && apt-get install -y git wget curl build-essential python3 - cp .config/ci.yml .config/default.yml - corepack enable - corepack prepare pnpm@latest --activate @@ -28,6 +28,7 @@ testCommit: - packages/*/node_modules/ only: - develop + - merge_requests - stable getImageTag: @@ -55,6 +56,7 @@ getImageTag: - stable - develop - tags + buildDocker: stage: deploy needs: @@ -79,6 +81,7 @@ buildDocker: - stable - develop - tags + mergeManifests: stage: deploy needs: diff --git a/.gitlab/issue_templates/bug.md b/.gitlab/issue_templates/bug.md new file mode 100644 index 0000000000000000000000000000000000000000..691464757039c38cd9462824dc83d42861d7f1bd --- /dev/null +++ b/.gitlab/issue_templates/bug.md @@ -0,0 +1,29 @@ +<!-- 💖 Thanks for taking the time to fill out this bug report! +💠Having trouble with deployment? [Ask the support chat.](https://discord.gg/4qUhaeeHmm) +🔒 Found a security vulnerability? [Please disclose it responsibly.](https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/SECURITY.md) +🤠By submitting this feature request, you agree to follow our [Contribution Guidelines.](https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/CONTRIBUTING.md) --> + +**What happened?** _(Please give us a brief description of what happened.)_ + +**What did you expect to happen?** _(Please give us a brief description of what you expected to happen.)_ + +**Version** _(What version of Sharkey is your instance running? You can find this by clicking your instance's logo at the top left and then clicking instance information.)_ + +**Instance** _(What instance of Sharkey are you using?)_ + +**What type of issue is this?** _(If this happens on your device and has to do with the user interface, it's client-side. If this happens on either with the API or the backend, or you got a server-side error in the client, it's server-side.)_ + +**What browser are you using? (Client-side issues only)** + +**What operating system are you using? (Client-side issues only)** + +**How do you deploy Sharkey on your server? (Server-side issues only)** + +**What operating system are you using? (Server-side issues only)** + +**Relevant log output** _(Please copy and paste any relevant log output. You can find your log by inspecting the page, and going to the "console" tab. This will be automatically formatted into code, so no need for backticks.)_ + +**Contribution Guidelines** +By submitting this issue, you agree to follow our [Contribution Guidelines](https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/CONTRIBUTING.md) +- [ ] I agree to follow this project's Contribution Guidelines +- [ ] I have searched the issue tracker for similar issues, and this is not a duplicate. diff --git a/.gitlab/issue_templates/feature.md b/.gitlab/issue_templates/feature.md new file mode 100644 index 0000000000000000000000000000000000000000..d4235eb5a3448712649de9b69827b96c6d430d91 --- /dev/null +++ b/.gitlab/issue_templates/feature.md @@ -0,0 +1,17 @@ +<!-- 💖 Thanks for taking the time to fill out this bug report! +💠Having trouble with deployment? [Ask the support chat.](https://discord.gg/4qUhaeeHmm) +🔒 Found a security vulnerability? [Please disclose it responsibly.](https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/SECURITY.md) +🤠By submitting this feature request, you agree to follow our [Contribution Guidelines.](https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/CONTRIBUTING.md) --> + +**What feature would you like implemented?** _(Please give us a brief description of what you'd like.)_ + +**Why should we add this feature?** _(Please give us a brief description of why your feature is important.)_ + +**Version** _(What version of Sharkey is your instance running? You can find this by clicking your instance's logo at the top left and then clicking instance information.)_ + +**Instance** _(What instance of Sharkey are you using?)_ + +**Contribution Guidelines** +By submitting this issue, you agree to follow our [Contribution Guidelines](https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/CONTRIBUTING.md) +- [ ] I agree to follow this project's Contribution Guidelines +- [ ] I have searched the issue tracker for similar requests, and this is not a duplicate. diff --git a/.gitlab/merge_request_templates/default.md b/.gitlab/merge_request_templates/default.md new file mode 100644 index 0000000000000000000000000000000000000000..18bffa54193ba9c528d16f1f50b3a281bbbe1601 --- /dev/null +++ b/.gitlab/merge_request_templates/default.md @@ -0,0 +1,11 @@ +<!-- Thanks for taking the time to make Sharkey better! --> + +**What does this PR do?** _(Please give us a brief description of what this PR does.)_ + +**Contribution Guidelines** +By submitting this merge request, you agree to follow our [Contribution Guidelines](https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/CONTRIBUTING.md) +- [ ] I agree to follow this project's Contribution Guidelines +- [ ] I have made sure to test this pull request + +<!-- Uncomment if your merge request has multiple authors --> +<!-- Co-authored-by: Name <email@email.com> --> diff --git a/.gitmodules b/.gitmodules index 225a69a65223632ffe66eb60efc97a52cfebd851..a3ca76cc960696348889c5c6cb7c1d651eeb133b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "fluent-emojis"] path = fluent-emojis url = https://github.com/misskey-dev/emojis.git +[submodule "tossface-emojis"] + path = tossface-emojis + url = https://activitypub.software/TransFem-org/tossface-emojis.git diff --git a/.npmrc b/.npmrc index da702c3af51635406465390b39d26e1f619d6f25..b949431c778724a186e2dfb017ef49a0fb8e66af 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1,2 @@ -@sharkey:registry=https://git.joinsharkey.org/api/packages/Sharkey/npm/ +@transfem-org:registry=https://activitypub.software/api/v4/packages/npm/ engine-strict = true diff --git a/.forgejo/workflows/lint.yml b/.old/workflows/lint.yml similarity index 84% rename from .forgejo/workflows/lint.yml rename to .old/workflows/lint.yml index 0a773d5fb0f8808ce02a9c0aa050ae82def18d43..8ad19b45c85b2dc836b706b029ac3a69c499cca4 100644 --- a/.forgejo/workflows/lint.yml +++ b/.old/workflows/lint.yml @@ -8,6 +8,12 @@ on: paths: - packages/** pull_request: + paths: + - packages/backend/** + - packages/frontend/** + - packages/sw/** + - packages/misskey-js/** + - packages/shared/.eslintrc.js jobs: pnpm_install: @@ -82,4 +88,8 @@ jobs: - run: pnpm i --frozen-lockfile - run: pnpm --filter misskey-js run build if: ${{ matrix.workspace == 'backend' }} + - run: pnpm --filter misskey-reversi run build:tsc + if: ${{ matrix.workspace == 'backend' }} + - run: pnpm --filter misskey-bubble-game run build + if: ${{ matrix.workspace == 'backend' }} - run: pnpm --filter ${{ matrix.workspace }} run typecheck diff --git a/CHANGELOG.md b/CHANGELOG.md index 30e2e57b7dc3a81a1d65152ec93bfc021823a84c..bafee277d259f1c5fbc303a6eb84f4684cfa6686 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ <!-- -## 2023.x.x (unreleased) +## 202x.x.x (unreleased) ### General - @@ -12,6 +12,146 @@ --> +## 2024.3.1 + +### General +- + +### Client +- Fix: 絵文å—関係ã®ä¸å…·åˆã‚’ä¿®æ£ (#13485) + - å±¥æ´ã«æ®‹ã£ã¦ã„ã‚‹ or ピン留ã‚ã•ã‚ŒãŸçµµæ–‡å—ãŒã‚³ãƒ³ãƒˆãƒãƒ¼ãƒ«ãƒ‘ãƒãƒ«ã‚ˆã‚Šå‰Šé™¤ã•ã‚Œã¦ã„ãŸéš›ã«ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ãƒ‡ãƒƒã‚ãŒè¡¨ç¤ºã§ããªããªã‚‹ + - Unicode絵文å—ãŒå±¥æ´ã«æ®‹ã£ã¦ã„ã‚‹ or ピン留ã‚ã•ã‚Œã¦ã„ã‚‹ã¨ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ãƒ‡ãƒƒã‚ãŒè¡¨ç¤ºã§ããªããªã‚‹ +- Fix: カスタム絵文å—ã®ç”»åƒèªã¿è¾¼ã¿ã«å¤±æ•—ã—ãŸéš›ã¯ãƒ†ã‚ストã§ã¯ãªãダミー画åƒã‚’表示 #13487 + +### Server +- + +## 2024.3.0 + +### General +- Enhance: 投稿者ã®ãƒãƒ¼ãƒ«ã«å¿œã˜ã¦ã€ä¸€ã¤ã®ãƒŽãƒ¼ãƒˆã«å«ã‚€ã“ã¨ã®ã§ãるメンションã¨ãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆæŠ•ç¨¿ã®å®›å…ˆã®äººæ•°ã«ä¸Šé™ã‚’è¨å®šã§ãるよã†ã« + * デフォルトã®ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ä¸Šé™ã¯20アカウントã«è¨å®šã•ã‚Œã¾ã™ã€‚(管ç†è€…ã¯ãƒ™ãƒ¼ã‚¹ãƒãƒ¼ãƒ«ã®è¨å®šã§å¤‰æ›´å¯èƒ½ã§ã™ã€‚) + * 連åˆã®å•ã„åˆã‚ã›ã«å¿œç”ã—ãªã„サーãƒãƒ¼ã®ãƒªãƒ¢ãƒ¼ãƒˆãƒ¦ãƒ¼ã‚¶ãƒ¼ã¸ã®ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ã¯ã€ä¸Šé™ã®äººæ•°ã«å«ã‚ãªã„実装ã«ãªã£ã¦ã„ã¾ã™ã€‚ +- Enhance: 通知ãŒãƒŸãƒ¥ãƒ¼ãƒˆã€å‡çµã‚’考慮ã™ã‚‹ã‚ˆã†ã«ãªã‚Šã¾ã—㟠+- Enhance: サーãƒãƒ¼ã”ã¨ã«ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãƒŽãƒ¼ãƒˆã‚’残ã›ã‚‹ã‚ˆã†ã« +- Enhance: コンディショナルãƒãƒ¼ãƒ«ã®æ¡ä»¶ã«ã€Œãƒžãƒ‹ãƒ¥ã‚¢ãƒ«ãƒãƒ¼ãƒ«ã¸ã®ã‚¢ã‚µã‚¤ãƒ³ã€ã‚’è¿½åŠ +- Enhance: 通知ã®å—ä¿¡è¨å®šã«ã€Œãƒ•ã‚©ãƒãƒ¼ä¸ã¾ãŸã¯ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã€ã‚’è¿½åŠ +- Enhance: 通知ã®å±¥æ´ã‚’リセットã§ãるよã†ã« +- Fix: ダイレクトãªãƒŽãƒ¼ãƒˆã«å¯¾ã—ã¦ã¯ãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆã§ã—ã‹è¿”ä¿¡ã§ããªã„よã†ã« + +### Client +- Enhance: ノート作æˆç”»é¢ã®ãƒ•ã‚¡ã‚¤ãƒ«æ·»ä»˜ãƒ¡ãƒ‹ãƒ¥ãƒ¼ã®åŒºåˆ‡ã‚Šç·šã®ä½ç½®ã‚’調整 +- Fix: syuilo/misskeyã®æ™‚代ã‹ã‚‰ã‚るインスタンスãŒæ”¹å¤‰ã•ã‚ŒãŸãƒãƒ¼ã‚¸ãƒ§ãƒ³ã§ã‚ã‚‹ã¨èª¤èªè˜ã•ã‚Œã‚‹å•é¡Œ +- Fix: MFMã®ã‚ªãƒ¼ãƒˆã‚³ãƒ³ãƒ—リートãŒå‡ºã‚‹ã¹ã状æ³ã§å‡ºãªã„ã“ã¨ãŒã‚ã‚‹å•é¡Œã‚’ä¿®æ£ +- Fix: ãƒãƒ£ãƒ¼ãƒˆã®ãƒ©ãƒ™ãƒ«ãŒæ¶ˆãˆã¦ã„ã‚‹å•é¡Œã‚’ä¿®æ£ +- Fix: ç”»é¢è¡¨ç¤ºå¾Œæœ€åˆã®éŸ³å£°å†ç”ŸãŒçˆ†éŸ³ã«ãªã‚‹ã“ã¨ãŒã‚ã‚‹å•é¡Œã‚’ä¿®æ£ +- Fix: è¨å®šã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—作æˆæ™‚ã«åå‰ã‚’入力ã—ãªã‹ã£ãŸå ´åˆã€ãƒãƒ¼ã‚«ãƒ©ã‚¤ã‚¼ãƒ¼ã‚·ãƒ§ãƒ³ãŒãŠã‹ã—ããªã‚‹å•é¡Œã‚’ä¿®æ£ +- Fix: ページ`/admin/emojis`ã®çµµæ–‡å—編集ダイアãƒã‚°ã§ã€Œãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¨ã—ã¦ä½¿ãˆã‚‹ãƒãƒ¼ãƒ«ã€ã‚’è¿½åŠ ã™ã‚‹éš›ã«ä½•ã‚‚é¸æŠžã›ãšOKを押下ã™ã‚‹ã¨ç”»é¢ãŒå›ºã¾ã‚‹å•é¡Œã‚’ä¿®æ£ +- Fix: 絵文å—サジェストã®é †ä½ã§ã€çµµæ–‡å—自体ã®åå‰ãŒåŒã˜ã‚‚ã®ã‚ˆã‚Šã‚‚ã‚¿ã‚°ã§ä¸€è‡´ã—ã¦ã„ã‚‹ã‚‚ã®ãŒå„ªå…ˆã•ã‚Œã¦ã—ã¾ã†å•é¡Œã‚’ä¿®æ£ +- Fix: ユーザã®æƒ…å ±ã®ãƒãƒƒãƒ—アップãŒæ¶ˆãˆãªããªã‚‹ã“ã¨ãŒã‚ã‚‹å•é¡Œã‚’ä¿®æ£ + +### Server +- Enhance: エンドãƒã‚¤ãƒ³ãƒˆ`flash/update`ã®`flashId`以外ã®ãƒ‘ラメータã¯å¿…é ˆã§ã¯ãªããªã‚Šã¾ã—㟠+- Fix: nodeinfoã«enableMcaptchaã¨enableTurnstileãŒç„¡ã„ã®ã‚’ä¿®æ£ +- Fix: ç ´æã—ãŸé€šçŸ¥ã‚’クライアントã«é€ä¿¡ã—ãªã„よã†ã« + * 通知欄ãŒç„¡é™ã«ãƒªãƒãƒ¼ãƒ‰ã•ã‚Œã‚‹å•é¡ŒãŒæ”¹å–„ã™ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ +- Fix: ç¦æ¢ã‚ーワードをå«ã‚€ãƒŽãƒ¼ãƒˆãŒDelayed Queueã«è¿½åŠ ã•ã‚Œã¦å†å‡¦ç†ã•ã‚Œã‚‹å•é¡Œã‚’ä¿®æ£ +- Fix: 自分ãŒãƒ•ã‚©ãƒãƒ¼ã—ã¦ã„ãªã„アカウントã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼é™å®šãƒŽãƒ¼ãƒˆãŒé–²è¦§ã§ãã‚‹ã“ã¨ãŒã‚ã‚‹å•é¡Œã‚’ä¿®æ£ +- Fix: タイムラインã®ã‚ªãƒ—ションã§ã€ŒãƒªãƒŽãƒ¼ãƒˆã‚’表示ã€ã‚’無効ã«ã—ã¦ã„ã‚‹éš›ã€æŠ•ç¥¨ã®ã¿ã®å¼•ç”¨ãƒªãƒŽãƒ¼ãƒˆãŒæµã‚Œã¦ã“ãªã„å•é¡Œã‚’ä¿®æ£ +- Fix: エンドãƒã‚¤ãƒ³ãƒˆ`admin/emoji/update`ã®å„ç¨®ä¿®æ£ + - å¿…é ˆãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ã‚’`id`ã¾ãŸã¯`name`ã®ã„ãšã‚Œã‹ã®ã¿ã« + - `id`ã®ä»£ã‚ã‚Šã«`name`ã§çµµæ–‡å—を指定å¯èƒ½ã«ï¼ˆ`id`・`name`両指定時ã¯å¾“æ¥é€šã‚Š`name`を変更ã™ã‚‹æŒ™å‹•ï¼‰ + - `category`ãŠã‚ˆã³`licence`ãŒæŒ‡å®šãªã—ã®æ™‚å‹æ‰‹ã«nullã«ä¸Šæ›¸ãã•ã‚Œã‚‹æŒ™å‹•ã‚’ä¿®æ£ +- Fix: 通知ã®å—ä¿¡è¨å®šã§ã€Œç›¸äº’フォãƒãƒ¼ã€ãŒæ£ã—ã動作ã—ãªã„å•é¡Œã‚’ä¿®æ£ + +## 2024.2.0 + +### Note +- 外部サイトã‹ã‚‰ãƒ—ラグインをインストールã™ã‚‹å ´åˆã®ãƒ‘スãŒ`/install-extentions`ã‹ã‚‰`/install-extensions`ã«å¤‰ã‚ã‚Šã¾ã™ã€‚以å‰ã®ãƒ‘スã‹ã‚‰ã¯è‡ªå‹•ã§ãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆã•ã‚Œã‚‹ã‚ˆã†ã«ãªã£ã¦ã„ã¾ã™ãŒã€æ–°ã—ã„パスã«å¤‰æ›´ã™ã‚‹ã“ã¨ã‚’ãŠå‹§ã‚ã—ã¾ã™ã€‚ + +### General +- Feat: [mCaptcha](https://github.com/mCaptcha/mCaptcha)ã®ã‚µãƒãƒ¼ãƒˆã‚’è¿½åŠ +- Feat: Add support for TrueMail +- Feat: AGPLv3ライセンスã«èª¤ã£ã¦é•åã™ã‚‹ã®ã‚’防æ¢ã™ã‚‹æ©Ÿèƒ½ã‚’è¿½åŠ + - 管ç†è€…ãŒrepositoryUrlを変更ã—ãŸã‚Šã€ã¾ãŸã¯ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã‚’ç›´æŽ¥é ’å¸ƒã™ã‚‹ã“ã¨ã‚’é¸æŠžã§ãるよã†ã«ãªã‚Šã¾ã™ + - 本体ã®ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã«æ”¹å¤‰ã‚’åŠ ãˆãŸéš›ã«ã€ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ã«åŸºã¥ãé©åˆ‡ãªæ¡ˆå†…を表示ã—ã¾ã™ +- Enhance: モデレーターã¯ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ä¸€è¦§ã‚’見られるよã†ã« +- Fix: リストライムラインã®ã€ŒãƒªãƒŽãƒ¼ãƒˆã‚’表示ã€ãŒæ£ã—ã機能ã—ãªã„å•é¡Œã‚’ä¿®æ£ +- Fix: リモートユーザーã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ä¸€è¦§ãŒã™ã¹ã¦è¦‹ãˆã¦ã—ã¾ã†ã®ã‚’ä¿®æ£ + * ã™ã¹ã¦ã®ãƒªãƒ¢ãƒ¼ãƒˆãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ä¸€è¦§ã‚’見ãˆãªã„よã†ã«ã—ã¾ã™ +- Fix: 特定ã®ã‚ーワードåŠã³æ£è¦è¡¨ç¾ã«ãƒžãƒƒãƒã™ã‚‹æ–‡å—列をå«ã‚€ãƒŽãƒ¼ãƒˆãŒæŠ•ç¨¿ã•ã‚ŒãŸéš›ã€ã‚¨ãƒ©ãƒ¼ã«å‡ºæ¥ã‚‹ã‚ˆã†ãªè¨å®šé …ç›®ã‚’è¿½åŠ #13207 + * デフォルトã¯ç©ºæ¬„ãªã®ã§é©ç”¨å‰ã¨åŒç‰ã®å‹•ä½œã«ãªã‚Šã¾ã™ + +### Client +- Feat: æ–°ã—ã„ã‚²ãƒ¼ãƒ ã‚’è¿½åŠ +- Feat: éŸ³å£°ãƒ»æ˜ åƒãƒ—ãƒ¬ã‚¤ãƒ¤ãƒ¼ã‚’è¿½åŠ +- Feat: 絵文å—ã®è©³ç´°ãƒ€ã‚¤ã‚¢ãƒã‚°ã‚’è¿½åŠ +- Feat: æž ç·šã‚’ã¤ã‘ã‚‹MFM`$[border.width=1,style=solid,color=fff,radius=0 ...]`ã‚’è¿½åŠ + - デフォルトã§æž ç·šã‹ã‚‰ã¯ã¿å‡ºã‚‹éƒ¨åˆ†ãŒéš ã•ã‚Œã‚‹ã‚ˆã†ã«ã—ã¾ã—ãŸã€‚åˆæœŸã¨åŒã˜æŒ™å‹•ã«ã™ã‚‹ã«ã¯`$[border.noclip`ãŒå¿…è¦ã§ã™ +- Feat: スワイプã§ã‚¿ãƒ–を切り替ãˆã‚‰ã‚Œã‚‹ã‚ˆã†ã« +- Enhance: MFMç‰ã®ã‚³ãƒ¼ãƒ‰ãƒ–ãƒãƒƒã‚¯ã«å…¨æ–‡ã‚³ãƒ”ー用ã®ãƒœã‚¿ãƒ³ã‚’è¿½åŠ +- Enhance: ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°å…¥åŠ›æ™‚ã«ã€æœ¬æ–‡ã®æœ«å°¾ã®è¡Œã«ä½•ã‚‚書ã‹ã‚Œã¦ã„ãªã„å ´åˆã¯æ–°ãŸã«ã‚¹ãƒšãƒ¼ã‚¹ã‚’è¿½åŠ ã—ãªã„よã†ã« +- Enhance: ãƒãƒ£ãƒ³ãƒãƒ«ãƒŽãƒ¼ãƒˆã®ãƒ”ン留ã‚をノートã®ãƒ¡ãƒ‹ãƒ¥ãƒ¼ã‹ã‚‰ã§ãるよã†ã« +- Enhance: 管ç†è€…ã®å ´åˆã¯API tokenã®ç™ºè¡Œç”»é¢ã§ç®¡ç†æ©Ÿèƒ½ã«é–¢ã™ã‚‹æ¨©é™ã‚’付与ã§ãるよã†ã« +- Enhance: AiScriptã‚’0.17.0ã«æ›´æ–°ã€€[CHANGELOG](https://github.com/aiscript-dev/aiscript/blob/bb89d132b633a622d3cb0eff0d0cc7e476c0cfdd/CHANGELOG.md) + - é…列ã®ç¯„囲外・éžæ•´æ•°ã®ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ã¸ã®ä»£å…¥ãŒå®Œå…¨ç¦æ¢ã«ãªã‚‹ã®ã§æ³¨æ„ +- Enhance: 絵文å—ピッカー・オートコンプリートã§ã€å®Œå…¨ä¸€è‡´ã—ãŸçµµæ–‡å—を優先的ã«è¡¨ç¤ºã™ã‚‹ã‚ˆã†ã« +- Enhance: Playã®èª¬æ˜Žæ¬„ã«MFMを使ãˆã‚‹ã‚ˆã†ã« +- Enhance: ãƒãƒ£ãƒ³ãƒãƒ«ãƒŽãƒ¼ãƒˆã®å ´åˆã¯è©³ç´°ãƒšãƒ¼ã‚¸ã‹ã‚‰ãã®å‰å¾Œã®ãƒŽãƒ¼ãƒˆã‚’見れるよã†ã« +- Enhance: å£ç¯€ã«å¿œã˜ãŸç”»é¢ã®æ¼”出をå—åŠçƒã§ã‚‚利用ã§ãるよã†ã« +- Enhance: タイムラインフィルターã®è¨å®šã‚’ã™ã¹ã¦ä¿æŒã§ãるよã†ã« + - 今ã¾ã§ã®ã€ŒTLã«ä»–ã®äººã¸ã®è¿”ä¿¡ã‚’å«ã‚ã‚‹ã€è¨å®šã¯ä¸€æ—¦ãƒªã‚»ãƒƒãƒˆã•ã‚Œã¾ã™ +- Enhance: タイムラインフィルターã«ã€Œã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ãªãƒ•ã‚¡ã‚¤ãƒ«ã‚’å«ã‚€ãƒŽãƒ¼ãƒˆã‚’表示ã€ã‚’è¿½åŠ +- Enhance: ノート作æˆç”»é¢ã®ãƒ•ã‚¡ã‚¤ãƒ«æ·»ä»˜ãƒ¡ãƒ‹ãƒ¥ãƒ¼ã‹ã‚‰ç›´æŽ¥ãƒ•ã‚¡ã‚¤ãƒ«ã‚’削除ã§ãるよã†ã« +- Enhance: MFMã®å±žæ€§ã§ã‚ªãƒ¼ãƒˆã‚³ãƒ³ãƒ—リートãŒä½¿ç”¨ã§ãるよã†ã« #12735 +- Enhance: 絵文å—編集ダイアãƒã‚°ã‚’モーダルã§ã¯ãªãウィンドウã§è¡¨ç¤ºã™ã‚‹ã‚ˆã†ã« +- Enhance: リモートã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ãƒ¡ãƒ‹ãƒ¥ãƒ¼ã‹ã‚‰ç›´æŽ¥ãƒªãƒ¢ãƒ¼ãƒˆã§è¡¨ç¤ºã§ãるよã†ã« +- Enhance: リモートã¸ã®å¼•ç”¨ãƒªãƒŽãƒ¼ãƒˆã¨åŒä¸€ã®ãƒªãƒ³ã‚¯ã«ã¯ãƒªãƒ³ã‚¯ãƒ—レビューを表示ã—ãªã„よã†ã« +- Enhance: コードã®ã‚·ãƒ³ã‚¿ãƒƒã‚¯ã‚¹ãƒã‚¤ãƒ©ã‚¤ãƒˆã«ãƒ†ãƒ¼ãƒžã‚’é©ç”¨ã§ãるよã†ã« +- Enhance: リアクション権é™ãŒãªã„å ´åˆã€ãƒãƒ¼ãƒˆã«ãƒ•ã‚©ãƒ¼ãƒ«ãƒãƒƒã‚¯ã™ã‚‹ã®ã§ã¯ãªãリアクションピッカーãªã©ã‹ã‚‰æ‰“ã¦ãªã„よã†ã« + - リモートã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ãƒãƒ¼ã‚«ãƒ«ã®ã¿ã®ã‚«ã‚¹ã‚¿ãƒ 絵文å—をリアクションã—よã†ã¨ã—ãŸå ´åˆ + - センシティブãªãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’èªã‚ã¦ã„ãªã„ユーザーã«ã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ãªã‚«ã‚¹ã‚¿ãƒ 絵文å—をリアクションã—よã†ã¨ã—ãŸå ´åˆ + - ãƒãƒ¼ãƒ«ãŒå¿…è¦ãªçµµæ–‡å—をリアクションã—よã†ã¨ã—ãŸå ´åˆ +- Enhance: ページé·ç§»æ™‚ã«Playerã‚’é–‰ã˜ã‚‹ã‚ˆã†ã« +- Enhance: é€šå ±ãƒšãƒ¼ã‚¸ã®ãƒ¦ãƒ¼ã‚¶ã‚’クリックã—ãŸéš›ã«ãƒ¦ãƒ¼ã‚¶ã‚’ウィンドウã§é–‹ãよã†ã« +- Enhance: ノートã®é€šå ±æ™‚ã«ãƒªãƒ¢ãƒ¼ãƒˆã®ãƒŽãƒ¼ãƒˆã§ã‚ã£ã¦ã‚‚自インスタンスã«ãŠã‘るノートã®ãƒªãƒ³ã‚¯ã‚’å«ã‚€ã‚ˆã†ã« +- Enhance: オフライン表示ã®ãƒ‡ã‚¶ã‚¤ãƒ³ã‚’改善・多言語対応 +- Fix: ãƒã‚¤ãƒ†ã‚£ãƒ–モードã®çµµæ–‡å—ãŒãƒ¢ãƒŽã‚¯ãƒã«ãªã‚‰ãªã„よã†ã« +- Fix: v2023.12.0ã§è¿½åŠ ã•ã‚ŒãŸã€Œãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚¿ãƒ¼ãŒãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã‚¢ã‚¤ã‚³ãƒ³ã‚‚ã—ãã¯ãƒãƒŠãƒ¼ç”»åƒã‚’未è¨å®šçŠ¶æ…‹ã«ã§ãる機能ã€ãŒç®¡ç†ç”»é¢ä¸Šã§æ£ã—ã表示ã•ã‚Œã¦ã„ãªã„å•é¡Œã‚’ä¿®æ£ +- Fix: AiScriptã®`readline`関数ãŒä¸æ£ãªå€¤ã‚’è¿”ã™ã“ã¨ãŒã‚ã‚‹å•é¡Œã®v2023.12.0時点ã§ã®ä¿®æ£ãŒPlay以外ã«é©ç”¨ã•ã‚Œã¦ã„ãªã„ã®ã‚’ä¿®æ£ +- Fix: v2023.12.1ã§è¿½åŠ ã•ã‚ŒãŸ`$[clickable ...]`ãŠã‚ˆã³`onClickEv`ãŒæ£ã—ã機能ã—ã¦ã„ãªã„ã®ã‚’ä¿®æ£ +- Fix: Renoteã®ã‚ーボードショートカットãŒæ©Ÿèƒ½ã—ã¦ã„ãªã‹ã£ãŸå•é¡Œã‚’ä¿®æ£ +- Fix: 投稿フォームã§ã‚¢ãƒ³ã‚±ãƒ¼ãƒˆã®æ—¥æ™‚指定をã—ãŸçŠ¶æ…‹ã§å†èªã¿è¾¼ã¿ã‚’ã™ã‚‹ã¨æœŸæ—¥ãŒå¾©å…ƒã•ã‚Œãªã„å•é¡Œã‚’ä¿®æ£ +- Fix: アンケートをè¨å®šã—ãŸãƒŽãƒ¼ãƒˆã‚’「削除ã—ã¦ç·¨é›†ã€ã‚’ã™ã‚‹ã¨ã‚¢ãƒ³ã‚±ãƒ¼ãƒˆã®æœŸæ—¥ãŒå¼•ã継ãŒã‚Œãšã€ãƒªã‚»ãƒƒãƒˆã•ã‚Œã¦ã—ã¾ã†å•é¡Œã‚’ä¿®æ£ +- Fix: デッã‚ã®ãƒ—ãƒãƒ•ã‚¡ã‚¤ãƒ«ä½œæˆæ™‚ã«åå‰ã‚’空ã«ã§ãã‚‹å•é¡Œã‚’ä¿®æ£ +- Fix: テーマ作æˆæ™‚ã«å称ãŒç©ºæ¬„ã§ã‚‚作æˆã§ãã¦ã—ã¾ã†å•é¡Œã‚’ä¿®æ£ +- Fix: プラグインã§`Plugin:register_note_post_interruptor`を使用ã™ã‚‹ã¨ã€ãƒŽãƒ¼ãƒˆãŒæŠ•ç¨¿ã§ããªããªã‚‹å•é¡Œã‚’ä¿®æ£ +- Fix: iOSã§å¤§ããªç”»åƒã‚’変æ›ã—ã¦ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã§ããªã„å•é¡Œã‚’ä¿®æ£ +- Fix: 「アニメーション画åƒã‚’å†ç”Ÿã—ãªã„ã€ã‚‚ã—ãã¯ã€Œãƒ‡ãƒ¼ã‚¿ã‚»ãƒ¼ãƒãƒ¼ï¼ˆã‚¢ã‚¤ã‚³ãƒ³ï¼‰ã€ã‚’有効ã«ã—ã¦ã„ã¦ã‚‚ã€ã‚¢ã‚¤ã‚³ãƒ³ãƒ‡ã‚³ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã®ã‚¢ãƒ‹ãƒ¡ãƒ¼ã‚·ãƒ§ãƒ³ãŒåœæ¢ã•ã‚Œãªã„å•é¡Œã‚’ä¿®æ£ +- Fix: ç”»åƒã‚’クãƒãƒƒãƒ—ã™ã‚‹ã¨ã‚¯ãƒãƒƒãƒ—後ã®è§£åƒåº¦ãŒç•°æ§˜ã«ä½Žããªã‚‹å•é¡Œã®ä¿®æ£ +- Fix: ç”»åƒã‚’クãƒãƒƒãƒ—時ã€æ£å¸¸ã«å®Œäº†ã§ããªã„å•é¡Œã®ä¿®æ£ +- Fix: ã‚ャプションãŒç©ºã®ç”»åƒã‚’クãƒãƒƒãƒ—ã™ã‚‹ã¨ã‚ャプションã«nullã¨ã„ã†æ–‡å—列ãŒå…¥ã£ã¦ã—ã¾ã†å•é¡Œã®ä¿®æ£ +- Fix: プãƒãƒ•ã‚£ãƒ¼ãƒ«ã‚’編集ã—ã¦ã‚‚リãƒãƒ¼ãƒ‰ã™ã‚‹ã¾ã§åæ˜ ã•ã‚Œãªã„å•é¡Œã‚’ä¿®æ£ +- Fix: エラー画åƒURLã‚’è¨å®šã—ãŸå¾Œè§£é™¤ã™ã‚‹ã¨ï¼Œãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã®ç”»åƒãŒè¡¨ç¤ºã•ã‚Œãªã„å•é¡Œã®ä¿®æ£ +- Fix: MkCodeEditorã§è¡ŒãŒãšã‚Œã¦ã„ã£ã¦ã—ã¾ã†å•é¡Œã®ä¿®æ£ +- Fix: Summaly proxy利用時ã«ãƒ—レイヤーãŒå‹•ä½œã—ãªã„ã“ã¨ãŒã‚ã‚‹ã®ã‚’ä¿®æ£ #13196 + +### Server +- Enhance: 連åˆå…ˆã®ãƒ¬ãƒ¼ãƒˆãƒªãƒŸãƒƒãƒˆã‚’超éŽã—ãŸéš›ã«ãƒªãƒˆãƒ©ã‚¤ã™ã‚‹ã‚ˆã†ã«ãªã‚Šã¾ã—㟠+- Enhance: ActivityPub Deliver queueã§Bodyを事å‰å‡¦ç†ã™ã‚‹ã‚ˆã†ã« (#12916) +- Enhance: クリップをエクスãƒãƒ¼ãƒˆã§ãるよã†ã« +- Enhance: `/files`ã®ãƒ•ã‚¡ã‚¤ãƒ«ã«å¯¾ã—ã¦HTTP Rangeリクエストを行ãˆã‚‹ã‚ˆã†ã« +- Enhance: `api.json`ã®OpenAPI Specificationã‚’3.1.0ã«æ›´æ–° +- Enhance: 連åˆå‘ã‘ã®ãƒŽãƒ¼ãƒˆé…信を軽é‡åŒ– #13192 +- Fix: `drive/files/update`ã§ãƒ•ã‚¡ã‚¤ãƒ«åã®ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ãŒæ©Ÿèƒ½ã—ã¦ã„ãªã„å•é¡Œã‚’ä¿®æ£ +- Fix: `notes/create`ã§ã€`text`ãŒç©ºç™½æ–‡å—ã®ã¿ã§æ§‹æˆã•ã‚Œã¦ã„ã‚‹ã‹`null`ã§ã‚ã£ã¦ã€ã‹ã¤`text`ã ã‘ã§ã‚るリクエストã«å¯¾ã™ã‚‹ãƒ¬ã‚¹ãƒãƒ³ã‚¹ãŒ400ã«ãªã‚‹ã‚ˆã†ã«å¤‰æ›´ +- Fix: `notes/create`ã§ã€`text`ãŒç©ºç™½æ–‡å—ã®ã¿ã§æ§‹æˆã•ã‚Œã¦ã„ã¦ã‹ã¤ãƒªãƒŽãƒ¼ãƒˆã€ãƒ•ã‚¡ã‚¤ãƒ«ã¾ãŸã¯æŠ•ç¥¨ã‚’å«ã‚“ã§ã„るリクエストã«å¯¾ã™ã‚‹ãƒ¬ã‚¹ãƒãƒ³ã‚¹ã®`text`ãŒ`""`ã‹ã‚‰`null`ã«ãªã‚‹ã‚ˆã†ã«å¤‰æ›´ +- Fix: ipv4ã¨ipv6ã®ä¸¡æ–¹ãŒåˆ©ç”¨å¯èƒ½ãªç’°å¢ƒã§allowedPrivateNetworksãŒè¨å®šã•ã‚Œã¦ã„ãŸå ´åˆãƒ—ライベートipã®æ¤œè¨¼ãŒã§ãã¦ã„ãªã‹ã£ãŸå•é¡Œã‚’ä¿®æ£ +- Fix: properly handle cc followers +- Fix: ジョブã«é–¢ã™ã‚‹è¨å®šã®åå‰ã‚’ä¿®æ£ relashionshipJobPerSec -> relationshipJobPerSec +- Fix: コントãƒãƒ¼ãƒ«ãƒ‘ãƒãƒ«->モデレーション->「誰ã§ã‚‚æ–°è¦ç™»éŒ²ã§ãるよã†ã«ã™ã‚‹ã€ã®åˆæœŸå€¤ã‚’ONã‹ã‚‰OFFã«å¤‰æ›´ #13122 +- Fix: リモートユーザーãŒå¾©æ´»ã—ã¦ã‚‚ã‚ャッシュã«ã‚ˆã‚Šè©²å½“ユーザーã®ActivityãŒå—ã‘入れられãªã„ã®ã‚’ä¿®æ£ #13273 + ## 2023.12.2 ### General diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7f6c1f4f82468f1f845c8dac5450a2a84a7bb463..a3263bf6aa139f7056d216ad686f5a3996795532 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -122,6 +122,19 @@ command. If you have not changed it from the default, it will be "http://localhost:3000". If "port" in .config/default.yml is set to something other than 3000, you need to change the proxy settings in packages/frontend/vite.config.local-dev.ts. +### `MK_DEV_PREFER=backend pnpm dev` +pnpm dev has another mode with `MK_DEV_PREFER=backend`. + +``` +MK_DEV_PREFER=backend pnpm dev +``` + +- This mode is closer to the production environment than the default mode. +- Vite runs behind the backend (the backend will proxy Vite at /vite). +- You can see Misskey by accessing `http://localhost:3000` (Replace `3000` with the port configured with `port` in .config/default.yml). +- To change the port of Vite, specify with `VITE_PORT` environment variable. +- HMR may not work in some environments such as Windows. + ### Dev Container Instead of running `pnpm` locally, you can use Dev Container to set up your development environment. To use Dev Container, open the project directory on VSCode with Dev Containers installed. @@ -286,18 +299,17 @@ export const argTypes = { min: 1, max: 4, }, + }, }; ``` Also, you can use msw to mock API requests in the storybook. Creating a `MyComponent.stories.msw.ts` file to define the mock handlers. ```ts -import { rest } from 'msw'; +import { HttpResponse, http } from 'msw'; export const handlers = [ - rest.post('/api/notes/timeline', (req, res, ctx) => { - return res( - ctx.json([]), - ); + http.post('/api/notes/timeline', ({ request }) => { + return HttpResponse.json([]); }), ]; ``` diff --git a/COPYING b/COPYING index c218443d42ac7f46ada0662d43f6a7bca3e1f79c..6a5f3ca1d598be6aba8db10b38f883a569c99e7e 100644 --- a/COPYING +++ b/COPYING @@ -1,5 +1,5 @@ Unless otherwise stated this repository is -Copyright © 2014-2023 syuilo and contributers +Copyright © 2014-2024 syuilo and contributors And is distributed under The GNU Affero General Public License Version 3, you should have received a copy of the license file as LICENSE. diff --git a/Dockerfile b/Dockerfile index 440c04e2df6afb5cf61e6a1f5be05fd589b97a68..8ad4bbbfb10b73b7c61ffe873add8765dfb52cb4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax = docker/dockerfile:1.4 -ARG NODE_VERSION=21.4.0-alpine3.18 +ARG NODE_VERSION=20.10.0-alpine3.18 FROM node:${NODE_VERSION} as build @@ -43,7 +43,12 @@ COPY --from=build /sharkey/packages/megalodon/lib ./packages/megalodon/lib COPY --from=build /sharkey/packages/megalodon/node_modules ./packages/megalodon/node_modules COPY --from=build /sharkey/packages/misskey-js/built ./packages/misskey-js/built COPY --from=build /sharkey/packages/misskey-js/node_modules ./packages/misskey-js/node_modules +COPY --from=build /sharkey/packages/misskey-reversi/built ./packages/misskey-reversi/built +COPY --from=build /sharkey/packages/misskey-reversi/node_modules ./packages/misskey-reversi/node_modules +COPY --from=build /sharkey/packages/misskey-bubble-game/built ./packages/misskey-bubble-game/built +COPY --from=build /sharkey/packages/misskey-bubble-game/node_modules ./packages/misskey-bubble-game/node_modules COPY --from=build /sharkey/fluent-emojis ./fluent-emojis +COPY --from=build /sharkey/tossface-emojis/dist ./tossface-emojis/dist COPY --from=build /sharkey/sharkey-assets ./packages/frontend/assets COPY package.json ./package.json @@ -55,6 +60,8 @@ COPY packages/backend/migration ./packages/backend/migration COPY packages/backend/assets ./packages/backend/assets COPY packages/megalodon/package.json ./packages/megalodon/package.json COPY packages/misskey-js/package.json ./packages/misskey-js/package.json +COPY packages/misskey-reversi/package.json ./packages/misskey-reversi/package.json +COPY packages/misskey-bubble-game/package.json ./packages/misskey-bubble-game/package.json ENV NODE_ENV=production RUN corepack enable diff --git a/README.md b/README.md index d3ba23c4e11fa37b3cb67779a9868bad693f6abb..c002acbff9da7b32b532dadb5d17953d7ac4b96f 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ <div align="center"> <a href="https://joinsharkey.org/"> - <img src="https://raw.githubusercontent.com/transfem-org/Sharkey/5180b4093f30e3bf3ff8d6b16751b69ebed9fb12/packages/frontend/assets/sharkey.svg" alt="Sharkey logo" style="border-radius:50%" width="400"/> + <img src="https://activitypub.software/TransFem-org/Sharkey/-/raw/develop/packages/frontend/assets/sharkey.svg" alt="Sharkey logo" style="border-radius:50%" width="300"/> </a> **🌎 **[Sharkey](https://joinsharkey.org/)** is an open source, decentralized social media platform that's free forever! 🚀** --- -<a href="https://joinsharkey.org"> +<a href="https://fedidb.org/software/sharkey"> <img src="https://custom-icon-badges.herokuapp.com/badge/find_an-instance-acea31?logoColor=acea31&style=for-the-badge&logo=sharkey&labelColor=363B40" alt="find an instance"/></a> <a href="https://docs.joinsharkey.org/docs/install/fresh/"> @@ -21,8 +21,6 @@ <a href="https://ko-fi.com/transfem"> <img src="https://custom-icon-badges.herokuapp.com/badge/donate-F96854?logoColor=F96854&style=for-the-badge&logo=kofi&labelColor=363B40" alt="donate"/></a> -<a href="https://hosted.weblate.org/projects/sharkey/"> - <img src="https://custom-icon-badges.herokuapp.com/badge/translate-sharkey-124437?logoColor=acea31&style=for-the-badge&logo=translate-sharkey&labelColor=363B40" alt="Translate Sharkey"/></a> --- @@ -30,7 +28,7 @@ <div> -<a href="https://joinsharkey.org/"><img src="https://cdn.transfem.social/files/b2721164-e015-463e-b851-3e953dd0d9f9.webp" align="right" height="520px"/></a> +<a href="https://joinsharkey.org/"><img src="https://cdn.shonk.social/files/b671c81c-58cf-4f13-bc96-af0b0c96c667.webp" align="right" height="520px"/></a> ## ✨ Features - **ActivityPub support**\ @@ -44,9 +42,9 @@ Sharkey makes some UI/UX improvements to make it easier to navigate - **Sign-Up Approval**\ With Sharkey, you can enable sign-ups, subject to manual moderator approval and mandatory user-provided reasons for joining. - **Rich Web UI**\ - Sharkey has a rich and easy to use Web UI! - It is highly customizable, from changing the layout and adding widgets to making custom themes. - Furthermore, plugins can be created using AiScript, an original programming language. + Sharkey has a rich and easy to use Web UI! + It is highly customizable, from changing the layout and adding widgets to making custom themes. + Furthermore, plugins can be created using AiScript, an original programming language. - And much more... </div> diff --git a/ROADMAP.md b/ROADMAP.md index 3077c41e73403118604ac1a2dd648f07d493080d..509ecb9fe787e1694ae504c7bfb0f0879b4a7e7a 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -6,6 +6,7 @@ Also, the later tasks are more indefinite and are subject to change as developme This is the phase we are at now. We need to make a high-maintenance environment that can withstand future development. - ~~Make the number of type errors zero (backend)~~ → Done âœ”ï¸ +- Make the number of type errors zero (frontend) - Improve CI - ~~Fix tests~~ → Done âœ”ï¸ - Fix random test failures - https://github.com/misskey-dev/misskey/issues/7985 and https://github.com/misskey-dev/misskey/issues/7986 diff --git a/SECURITY.md b/SECURITY.md index fa00b700e98433cda025e44fc1c0dd4cd08b3a9d..cfc0614dd6abf43f3c4f9a3458977475c47e2365 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,6 +4,6 @@ If you discover a security issue in Sharkey, please report it by sending an email to [admin@transfem.org](mailto:admin@transfem.org). This will allow us to assess the risk, and make a fix available before we add a -bug report to the GitHub repository. +bug report to the GitLab repository. Thanks for helping make Sharkey safe for everyone. diff --git a/cypress/e2e/basic.cy.js b/cypress/e2e/basic.cy.js index 5ab07c7480fb5b2999c1b55457bf4d3051fe895f..604241d13cc69a17861a2ded932338110cdf9c58 100644 --- a/cypress/e2e/basic.cy.js +++ b/cypress/e2e/basic.cy.js @@ -161,11 +161,13 @@ describe('After user signed in', () => { }); it('successfully loads', () => { - cy.get('[data-cy-user-setup-continue]').should('be.visible'); + // 表示ã«æ™‚é–“ãŒã‹ã‹ã‚‹ã®ã§ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆç§’æ•°ã ã¨ã‚¿ã‚¤ãƒ アウトã™ã‚‹ + cy.get('[data-cy-user-setup-continue]', { timeout: 30000 }).should('be.visible'); }); it('account setup wizard', () => { - cy.get('[data-cy-user-setup-continue]').click(); + // 表示ã«æ™‚é–“ãŒã‹ã‹ã‚‹ã®ã§ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆç§’æ•°ã ã¨ã‚¿ã‚¤ãƒ アウトã™ã‚‹ + cy.get('[data-cy-user-setup-continue]', { timeout: 30000 }).click(); cy.get('[data-cy-user-setup-user-name] input').type('ã‚ã‚Šã™'); cy.get('[data-cy-user-setup-user-description] textarea').type('ã»ã’'); @@ -202,7 +204,8 @@ describe('After user setup', () => { cy.login('alice', 'alice1234'); // アカウントåˆæœŸè¨å®šã‚¦ã‚£ã‚¶ãƒ¼ãƒ‰ - cy.get('[data-cy-user-setup] [data-cy-modal-window-close]').click(); + // 表示ã«æ™‚é–“ãŒã‹ã‹ã‚‹ã®ã§ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆç§’æ•°ã ã¨ã‚¿ã‚¤ãƒ アウトã™ã‚‹ + cy.get('[data-cy-user-setup] [data-cy-modal-window-close]', { timeout: 30000 }).click(); cy.get('[data-cy-modal-dialog-ok]').click(); }); diff --git a/cypress/e2e/router.cy.js b/cypress/e2e/router.cy.js new file mode 100644 index 0000000000000000000000000000000000000000..6de27be5f42970d2e08330e759ebb52dc84fa496 --- /dev/null +++ b/cypress/e2e/router.cy.js @@ -0,0 +1,30 @@ +describe('Router transition', () => { + describe('Redirect', () => { + // サーãƒã®åˆæœŸåŒ–。ルートã®ãƒ†ã‚¹ãƒˆã«é–¢ã—ã¦ã¯å„describeã”ã¨ã«1度ã ã‘実行ã§å分ã ã¨æ€ã†ï¼ˆä½¿ã„ã¾ã‚ã—ãŸæ–¹ãŒæ—©ã„) + before(() => { + cy.resetState(); + + // インスタンスåˆæœŸã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ— + cy.registerUser('admin', 'pass', true); + + // ãƒ¦ãƒ¼ã‚¶ãƒ¼ä½œæˆ + cy.registerUser('alice', 'alice1234'); + + cy.login('alice', 'alice1234'); + + // アカウントåˆæœŸè¨å®šã‚¦ã‚£ã‚¶ãƒ¼ãƒ‰ + // 表示ã«æ™‚é–“ãŒã‹ã‹ã‚‹ã®ã§ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆç§’æ•°ã ã¨ã‚¿ã‚¤ãƒ アウトã™ã‚‹ + cy.get('[data-cy-user-setup] [data-cy-modal-window-close]', { timeout: 30000 }).click(); + cy.wait(500); + cy.get('[data-cy-modal-dialog-ok]').click(); + }); + + it('redirect to user profile', () => { + // テストã®ãŸã‚ã ã‘ã«ç”¨æ„ã•ã‚ŒãŸãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆç”¨ãƒ«ãƒ¼ãƒˆã«é£›ã¶ + cy.visit('/redirect-test'); + + // プãƒãƒ•ã‚£ãƒ¼ãƒ«ãƒšãƒ¼ã‚¸ã®URLã§ã‚ã‚‹ã“ã¨ã‚’確èªã™ã‚‹ + cy.url().should('include', '/@alice') + }); + }); +}); diff --git a/docker-compose_example.yml b/docker-compose_example.yml index c967f331a4cf1220ccaf4a02ee3e1383d5b64b79..647f6f0c77afff19722e3eea8089b33c2c20ce57 100644 --- a/docker-compose_example.yml +++ b/docker-compose_example.yml @@ -8,6 +8,7 @@ services: links: - db - redis +# - mcaptcha # - meilisearch depends_on: db: @@ -48,12 +49,43 @@ services: interval: 5s retries: 20 +# mcaptcha: +# restart: always +# image: mcaptcha/mcaptcha:latest +# networks: +# internal_network: +# external_network: +# aliases: +# - localhost +# ports: +# - 7493:7493 +# env_file: +# - .config/docker.env +# environment: +# PORT: 7493 +# MCAPTCHA_redis_URL: "redis://mcaptcha_redis/" +# depends_on: +# db: +# condition: service_healthy +# mcaptcha_redis: +# condition: service_healthy +# +# mcaptcha_redis: +# image: mcaptcha/cache:latest +# networks: +# - internal_network +# healthcheck: +# test: "redis-cli ping" +# interval: 5s +# retries: 20 + # meilisearch: # restart: always # image: getmeili/meilisearch:v1.3.4 # environment: # - MEILI_NO_ANALYTICS=true # - MEILI_ENV=production +# - MEILI_MASTER_KEY=ChangeThis # networks: # - shonk # volumes: diff --git a/healthcheck.sh b/healthcheck.sh index 16119fc49a98b27ac7df7b4d137a0a0b9ab2966b..02f13576e95cea189e3f9f135a2de242777b0416 100644 --- a/healthcheck.sh +++ b/healthcheck.sh @@ -1,6 +1,6 @@ #!/bin/bash -# SPDX-FileCopyrightText: syuilo and other misskey contributors +# SPDX-FileCopyrightText: syuilo and misskey-project # SPDX-License-Identifier: AGPL-3.0-only PORT=$(grep '^port:' /sharkey/.config/default.yml | awk 'NR==1{print $2; exit}') diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index 0a7d86cc8932ac156f2aac83358fd41610d5f959..17c8f24fa548cb3525350a60cc13b4088d1d408a 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -360,6 +360,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Ùعّل hCaptcha" hcaptchaSiteKey: "Ù…ÙØªØ§Ø Ø§Ù„Ù…ÙˆÙ‚Ø¹" hcaptchaSecretKey: "المÙØªØ§Ø Ø§Ù„Ø³Ø±ÙŠ" +mcaptchaSiteKey: "Ù…ÙØªØ§Ø Ø§Ù„Ù…ÙˆÙ‚Ø¹" +mcaptchaSecretKey: "المÙØªØ§Ø Ø§Ù„Ø³Ø±ÙŠ" recaptcha: "reCAPTCHA" enableRecaptcha: "تمكين reCAPTCHA" recaptchaSiteKey: "Ù…ÙØªØ§Ø Ø§Ù„Ù…ÙˆÙ‚Ø¹" @@ -1009,7 +1011,10 @@ expired: "منتهية صلاØيته" icon: "الصورة الرمزية" replies: "رد" renotes: "أعد النشر" +sourceCode: "الشÙرة المصدرية" flip: "اقلب" +lastNDays: "آخر {n} أيام" +surrender: "ألغÙ" _initialAccountSetting: accountCreated: "Ù†Ø¬Ø Ø¥Ù†Ø´Ø§Ø¡ Øسابك!" letsStartAccountSetup: "إذا كنت جديدًا لنعدّ Øسابك الشخصي." @@ -1414,6 +1419,7 @@ _profile: _exportOrImport: allNotes: "كل الملاØظات" favoritedNotes: " الملاØظات المÙضلة" + clips: "Ù…Ùشبك" followingList: "المتابَعون" muteList: "المستخدمون المكتومون" blockingList: "المستخدمون المØجوبون" @@ -1561,3 +1567,6 @@ _moderationLogTypes: suspend: "علÙÙ‚" resetPassword: "أعد تعيين كلمتك السرية" createInvitation: "ولÙّد دعوة" +_reversi: + total: "المجموع" + diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index 77ba3f0306d4f90338bf95001766b01b8c1f1378..2a23cda06bda2d584697cbac5270136cd0520690 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -357,6 +357,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "hCaptcha চালৠকরà§à¦¨" hcaptchaSiteKey: "সাইট কী" hcaptchaSecretKey: "সিকà§à¦°à§‡à¦Ÿ কী" +mcaptchaSiteKey: "সাইট কী" +mcaptchaSecretKey: "সিকà§à¦°à§‡à¦Ÿ কী" recaptcha: "reCAPTCHA" enableRecaptcha: "reCAPTCHA চালৠকরà§à¦¨" recaptchaSiteKey: "সাইট কী" @@ -853,6 +855,7 @@ youFollowing: "অনà§à¦¸à¦°à¦£ করা হচà§à¦›à§‡" icon: "পà§à¦°à§‹à¦«à¦¾à¦‡à¦² ছবি" replies: "জবাব" renotes: "রিনোট" +sourceCode: "সোরà§à¦¸ কোড" flip: "উলà§à¦Ÿà¦¾à¦¨" _role: priority: "অগà§à¦°à¦¾à¦§à¦¿à¦•à¦¾à¦°" @@ -1190,6 +1193,7 @@ _profile: changeBanner: "বà§à¦¯à¦¾à¦¨à¦¾à¦° পরিবরà§à¦¤à¦¨ করà§à¦¨" _exportOrImport: allNotes: "সকল নোট" + clips: "কà§à¦²à¦¿à¦ª" followingList: "অনà§à¦¸à¦°à¦£ করা হচà§à¦›à§‡" muteList: "মিউট" blockingList: "বà§à¦²à¦•" @@ -1341,3 +1345,6 @@ _webhookSettings: _moderationLogTypes: suspend: "সà§à¦¥à¦—িত করা" resetPassword: "পাসওয়ারà§à¦¡ রিসেট করà§à¦¨" +_reversi: + total: "মোট" + diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index ba1e44256e80b328705d1044cb1abde2f4c4ac6a..ffdcc9787f9377d71c5eb6e7ab9c185564cf6519 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -130,6 +130,7 @@ overwriteFromPinnedEmojis: "Sobreescriu des dels emojis fixats" reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem \"+\" per afegir." rememberNoteVisibility: "Recorda la configuració de visibilitat de les notes" attachCancel: "Eliminar el fitxer adjunt" +deleteFile: "Esborrar l'arxiu " markAsSensitive: "Marcar com a NSFW" unmarkAsSensitive: "Deixar de marcar com a sensible" enterFileName: "Defineix nom del fitxer" @@ -336,8 +337,12 @@ whenServerDisconnected: "Quan es perdi la connexió al servidor" disconnectedFromServer: "Desconnectat pel servidor" reload: "Actualitza" doNothing: "Ignora" -accept: "Accepta" -normal: "Nomal" +reloadConfirm: "Vols recarregar?" +watch: "Veure" +unwatch: "Deixar de veure" +accept: "Acceptar" +reject: "Denegar" +normal: "Normal" instanceName: "Nom del servidor" instanceDescription: "Descripció del servidor" maintainerName: "Nom de l'administrador" @@ -355,25 +360,56 @@ connectService: "Connecta" disconnectService: "Desconnecta" enableLocalTimeline: "Activa la lÃnia de temps local" enableGlobalTimeline: "Activa la lÃnia de temps global" +disablingTimelinesInfo: "Fins i tot si aquestes lÃnies de temps són desactivades, els administradors i els moderadors poden continuar visualitzant per conveniència." registration: "Registre" +enableRegistration: "Permet els registres d'usuaris" invite: "Convida" +driveCapacityPerLocalAccount: "Capacitat del disc per usuaris locals" +driveCapacityPerRemoteAccount: "Capacitat del disc per usuaris remots" +inMb: "En megabytes" +bannerUrl: "Adreça URL del bà ner" +backgroundImageUrl: "Adreça URL de la imatge de fons" basicInfo: "Informació bà sica" pinnedUsers: "Usuaris fixats" +pinnedUsersDescription: "Llista d'usuaris, separats per salts de lÃnia, que seran fixats a la pestanya \"Explorar\"." +pinnedPages: "Pà gines fixades" +pinnedPagesDescription: "Escriu els camins de les pà gines que vols fixar a la pà gina d'inici d'aquesta instà ncia. Separades per salts de lÃnia." +pinnedClipId: "ID del retall fixat" pinnedNotes: "Nota fixada" +hcaptcha: "hCaptcha" +enableHcaptcha: "Activar hCaptcha" +hcaptchaSiteKey: "Clau del lloc" +hcaptchaSecretKey: "Clau secreta" +mcaptcha: "mCaptcha" +enableMcaptcha: "Activar mCaptcha" +mcaptchaSiteKey: "Clau del lloc" +mcaptchaSecretKey: "Clau secreta" +mcaptchaInstanceUrl: "Adreça URL del servidor mCaptcha" +recaptcha: "reCAPTCHA" +enableRecaptcha: "Activar reCAPTCHA" +recaptchaSiteKey: "Clau del lloc" +recaptchaSecretKey: "Clau secreta" turnstile: "Turnstile" enableTurnstile: "Activar Turnstile" turnstileSiteKey: "Clau del lloc" turnstileSecretKey: "Clau secreta" +avoidMultiCaptchaConfirm: "Fer servir diferents sistemes de Captcha a la vegada pot causar problemes entre ells. Vols desactivar els altres sistemes de Captcha activats? Si els vols mantenir actius fes clic a cancel·lar." antennas: "Antena" manageAntennas: "Gestiona les antenes" +name: "Nom" antennaSource: "Font de l'antena" antennaKeywords: "Paraules clau a seguir" antennaExcludeKeywords: "Paraules clau a excloure" antennaKeywordsDescription: "Separar amb espais per la condició AND o amb salts de lÃnia per la condició OR." notifyAntenna: "Notifica'm les publicacions noves" withFileAntenna: "Només les publicacions amb fitxers" +enableServiceworker: "Activar les notificacions al navegador" antennaUsersDescription: "Llistar un nom d'usuari per lÃnia" +caseSensitive: "Sensible a majúscules i minúscules " +withReplies: "Inclou respostes" +connectedTo: "Aquests comptes hi són connectats" notesAndReplies: "Amb respostes" +withFiles: "Incloure arxius" silence: "Silencia" silenceConfirm: "Segur que vols silenciar aquest usuari?" unsilence: "Deixa de silenciar" @@ -389,20 +425,40 @@ userList: "Llistes" about: "Informació" aboutMisskey: "Quant a Misskey" administrator: "Administrador/a" +token: "Codi de verificació" +2fa: "Autenticació de doble factor" +setupOf2fa: "Configurar l'autenticació de doble factor" +totp: "Aplicació d'autenticació" +totpDescription: "Escriu una contrasenya d'un sol us fent servir l'aplicació d'autenticació" moderator: "Moderador/a" moderation: "Moderació" +moderationNote: "Nota de moderació " +addModerationNote: "Afegir una nota de moderació " +moderationLogs: "Registre de moderació " nUsersMentioned: "{n} usuaris mencionats" +securityKeyAndPasskey: "Clau de seguretat / Clau de pas" securityKey: "Clau de seguretat" +lastUsed: "Fet servir per última vegada" +lastUsedAt: "Fet servir per última vegada: {t}" unregister: "Cancel·la el registre" passwordLessLogin: "Inici de sessió sense contrasenya" +passwordLessLoginDescription: "Permet l'inici de sessió sense contrasenya fent servir només una Clau de seguretat/Clau de pas" resetPassword: "Restableix la contrasenya" newPasswordIs: "La contrasenya nova és «{password}»" reduceUiAnimation: "Redueix les animacions de la interfÃcie" share: "Comparteix" notFound: "No s'ha trobat" +notFoundDescription: "No es troba cap pà gina que correspongui a aquesta adreça" +uploadFolder: "Carpeta per defecte per pujades" +markAsReadAllNotifications: "Marca totes les notificacions com a llegides" markAsReadAllUnreadNotes: "Marca-ho tot com a llegit" +markAsReadAllTalkMessages: "Marcar tots els missatges com llegits" help: "Ajuda" +inputMessageHere: "Escriu aquà el teu missatge " +close: "Tancar" invites: "Convida" +members: "Membres" +transfer: "Transferir" title: "TÃtol" text: "Text" enable: "Habilita" @@ -472,12 +528,62 @@ objectStorage: "Emmagatzematge d'objectes\n" useObjectStorage: "Utilitzar l'emmagatzematge d'objectes" objectStorageBaseUrl: "Base d'enllaç" objectStorageBaseUrlDesc: "Prefix d'enllaç utilitzat per a fer referencia als fitxers. Especifica l'enllaç del teu CDN o Proxy si n'està s utilitzant qualsevol, en cas contrari, especifica l'enllaç al que es pot accedir públicament segons la guia de servei que vosté utilitza.\nPer l'ús d'S3 utilitza 'https://<bucket>.s3.amazonaws.com' I per a GCS o serveis equivalents utilitza 'https://storage.googleapis.com/<bucket>'." +objectStorageBucket: "Dipòsit " +objectStorageBucketDesc: "Escriu el nom del dipòsit que fas servir al teu proveïdor d'emmagatzematge " +objectStoragePrefix: "Prefix" +objectStoragePrefixDesc: "Els fitxers es deixaren a directoris amb aquest prefix" +objectStorageEndpoint: "Endpoint" +objectStorageEndpointDesc: "Deixa'l buit si fas servir AWS S3, si no és aixà especÃfica un punt d'entrada com '<host>' o '<host>:<port>', depenent del servei que facis servir." +objectStorageRegion: "Regió " +objectStorageRegionDesc: "Especifica una regió com 'xx-east-1'. Si el teu servei no diferència regions has de posar 'us-east-1'. Deixa'l buit si fas servir variables d'entorn o un arxiu de configuració d'AWS." +objectStorageUseSSL: "Fes servir SSL" +objectStorageUseSSLDesc: "Desactiva'l si no tens pensat fer servir HTTPS per les connexions de l'API" +objectStorageUseProxy: "Connectar-se mitjançant un Proxy" +objectStorageUseProxyDesc: "Desactiva'l si no farà s servir un Proxy per les connexions de l'API" +objectStorageSetPublicRead: "Configurar les pujades com públiques " +s3ForcePathStyleDesc: "Si s3ForcePathStyle es troba activat el nom del dipòsit s'ha d'incloure a l'adreça URL en comtes del nom del host. Potser que necessitis activar-ho quan facis servir, per exemple, Minio a un servidor propi." +serverLogs: "Registres del servidor" +deleteAll: "Esborrar tot" +showFixedPostForm: "Mostrar el formulari per escriure a l'inici de la lÃnia de temps" +showFixedPostFormInChannel: "Mostrar el formulari d'escriptura al principi de la lÃnia de temps (Canals)" +withRepliesByDefaultForNewlyFollowed: "Inclou les respostes d'usuaris nous seguits a la lÃnia de temps per defecte." newNoteRecived: "Hi ha publicacions noves" +sounds: "Sons" +sound: "So" +listen: "Escoltar" +none: "Res" +showInPage: "Mostrar a la pà gina " +popout: "Finestra emergent" +volume: "Volum" +masterVolume: "Volum principal" +notUseSound: "Sense so" +useSoundOnlyWhenActive: "Reproduir sons només quan Misskey estigui actiu" +details: "Detalls" +chooseEmoji: "Tria un emoji" +unableToProcess: "L'operació no pot ser completada " +recentUsed: "Utilitzat recentment" +install: "Instal·lació " +uninstall: "Desinstal·lar " +installedApps: "Aplicacions autoritzades " +nothing: "No hi ha res per veure aquà " installedDate: "Data d'instal·lació" +lastUsedDate: "Utilitzat per última vegada" state: "Estat" sort: "Ordena" ascendingOrder: "Ascendent" descendingOrder: "Descendent" +scratchpad: "Bloc de proves" +scratchpadDescription: "El bloc de proves proporciona un entorn experimental per AiScript. Pot escriure i verificar els resultats que interactuen amb Misskey." +output: "Sortida" +script: "Script" +disablePagesScript: "Desactivar AiScript a les pà gines " +updateRemoteUser: "Actualitzar la informació de l'usuari remot" +unsetUserAvatar: "Desactivar l'avatar " +unsetUserAvatarConfirm: "Segur que vols desactivar l'avatar?" +unsetUserBanner: "Desactivar el bà ner " +unsetUserBannerConfirm: "Segur que vols desactivar el bà ner?" +deleteAllFiles: "Esborrar tots els arxius" +deleteAllFilesConfirm: "Segur que vols esborrar tots els arxius?" removeAllFollowing: "Deixar de seguir tots els usuaris seguits" removeAllFollowingDescription: "El fet d'executar això, et farà deixar de seguir a tots els usuaris de {host}. Si us plau, executa això si l'amfitrió, per exemple, ja no existeix." userSuspended: "Aquest usuari ha sigut suspès" @@ -526,48 +632,1387 @@ medium: "Mitjà " small: "Petit" generateAccessToken: "Genera codi d'accés" permission: "Permisos" +adminPermission: "Permisos d'administrador " enableAll: "Habilita tot" disableAll: "Deshabilita tot" tokenRequested: "Donar accés al compte" +pluginTokenRequestedDescription: "Aquest connector podrà fer servir tots els permisos configurats aquÃ." +notificationType: "Tipus de notificació " +edit: "Editar" +emailServer: "Servidor de correu electrònic " +enableEmail: "Activar l'enviament de correus electrònics " +emailConfigInfo: "Es fa servir per confirmar el teu correu quan et registres o oblides la contrasenya " +email: "Correu electrònic" +emailAddress: "Adreça de correu electrònic" +smtpConfig: "Configuració del servidor SMTP" smtpHost: "Amfitrió" +smtpPort: "Port" smtpUser: "Nom d'usuari" smtpPass: "Contrasenya" +emptyToDisableSmtpAuth: "No omplis el nom d'usuari i la contrasenya si vols deshabilitar l'autenticació SMTP" +smtpSecure: "Fes servir SSL/TLS per connexions SMTP" +smtpSecureInfo: "Desactiva això quan facis servir connexions STARTTLS" +testEmail: "Prova l'enviament de correu " +wordMute: "Silenciar paraules " +hardWordMute: "Silenciar paraules fortes" +regexpError: "Error de l'expressió regular " +regexpErrorDescription: "S'ha produït un error a l'expressió regular a la lÃnia {line} de les paraules silenciades {tab}:" +instanceMute: "Silenciar servidor" +userSaysSomething: "{name} n'ha dit alguna cosa" +makeActive: "Activar" +display: "Veure" +copy: "Copiar" +metrics: "Mètriques" +overview: "Visió General" +logs: "Registres" +delayed: "Endarrerits " +database: "Bases de dades" +channel: "Canals" +create: "Crear" +notificationSetting: "Parà metres de notificacions" +notificationSettingDesc: "Selecciona els tipus de notificacions que es mostraran" +useGlobalSetting: "Fer servir la configuració global" +useGlobalSettingDesc: "Si s'activa, es farà servir la configuració de notificacions del teu comte. Si no s'activa es poden fer configuracions individuals." +other: "Altre" +regenerateLoginToken: "Regenerar clau de seguretat d'inici de sessió" +regenerateLoginTokenDescription: "Regenera la clau de seguretat que es fa servir internament durant l'inici de sessió. Normalment aquesta acció no és necessà ria. Si es regenera es tancarà la sessió a tots els dispositius amb una sessió activa." +theKeywordWhenSearchingForCustomEmoji: "Cercar un emoji personalitzat " +setMultipleBySeparatingWithSpace: "Separa múltiples entrades amb un espai" +fileIdOrUrl: "ID de l'arxiu o URL" +behavior: "Comportament" +sample: "Mostrar" +abuseReports: "Denúncies " +reportAbuse: "Denuncia un abús " +reportAbuseRenote: "Denuncia una renota" +reportAbuseOf: "Denuncia a {name}" +fillAbuseReportDescription: "Omple els detalls sobre aquesta denúncia. Si la denúncia és sobre una nota en concret inclou l'adreça URL." +abuseReported: "La teva denúncia s'ha enviat. Moltes grà cies." +reporter: "Denunciant " +reporteeOrigin: "Origen de la denúncia " +reporterOrigin: "Origen del denunciant" +forwardReport: "Transferir la denúncia a una instà ncia remota" +forwardReportIsAnonymous: "En comptes del teu compte, es farà servir un compte anònim com a denunciat a la instà ncia remota." +send: "Enviar" +abuseMarkAsResolved: "Marcar la denúncia com a resolta" +openInNewTab: "Obre a una pestanya nova" +openInSideView: "Obre a una vista lateral" +defaultNavigationBehaviour: "Navegació per defecte" +editTheseSettingsMayBreakAccount: "Editar aquestes opcions pot deixar inoperatiu el teu compte" +instanceTicker: "Informació de notes de la instà ncia " +waitingFor: "Esperant {x}" +random: "Aleatori " +system: "Sistema" +switchUi: "Canviar interfÃcie d'usuari " +desktop: "Escriptori" +clip: "Retalls" +createNew: "Crear" +optional: "Opcional" +createNewClip: "Crear un nou Retall" +unclip: "Treure Retall" +confirmToUnclipAlreadyClippedNote: "Aquesta nota ja és inclosa al Retall \"{name}\". Vols treure-la d'aquest retall?" +public: "Públic " +private: "Privat" +i18nInfo: "Misskey està sent traduït a diferents idiomes per voluntaris. Pots ajudar aquà {link}." +manageAccessTokens: "Administrar claus de seguretat d'accés " +accountInfo: "Informació del compte" +notesCount: "Comptador de notes" +repliesCount: "Nombre de respostes" renotesCount: "Impulsos fets" +repliedCount: "Nombre de respostes rebudes" renotedCount: "Impulsos rebuts" +followingCount: "Nombre de comptes seguits" +followersCount: "Nombre de seguidors" +sentReactionsCount: "Nombre de reaccions enviades" +receivedReactionsCount: "Nombre de reaccions rebudes" +pollVotesCount: "Nombre de vots enviats a enquestes" +pollVotedCount: "Nombre de vots rebuts a les enquestes" +yes: "Sà " +no: "No" +driveFilesCount: "Nombre de fitxers al Disc" +driveUsage: "Utilització de l'espai del Disc" +noCrawle: "Rebutjar la indexació dels buscadors" +noCrawleDescription: "No permetis que els buscadors indexin el teu perfil, notes, pà gines, etc." +lockedAccountInfo: "Tret que establiu la visibilitat de la nota a \"Només seguidors\", les vostres notes seran visibles per qualsevol persona, fins i tot si heu d'aprovar els seguidors manualment" +alwaysMarkSensitive: "Marcar com a sensible per defecte" +loadRawImages: "Carregar les imatges originals en comptes de miniatures " +disableShowingAnimatedImages: "No reproduir imatges animades" +highlightSensitiveMedia: "Ressalta els medis marcats com a sensibles" +verificationEmailSent: "S'ha enviat un correu electrònic de verificació. Fes clic a l'enllaç per completar la verificació." +notSet: "Sense definir" +emailVerified: "El correu electrònic s'ha verificat" +noteFavoritesCount: "Nombre de notes favorites " +pageLikesCount: "Nombre de Pà gines que t'agraden " +pageLikedCount: "Nombre d'agraïments rebuts a les Pà gines " +contact: "Contacte" +useSystemFont: "Fes servir la font per defecte del sistema" +clips: "Retalls" +experimentalFeatures: "CaracterÃstiques experimentals" +experimental: "Experimental" +thisIsExperimentalFeature: "Aquesta és una caracterÃstica experimental. La seva funcionalitat pot canviar, i pot ser que no funcioni degudament." +developer: "Programador" +makeExplorable: "Fes que el compte sigui visible a la secció \"Explorar\"" +makeExplorableDescription: "Si desactives aquesta opció, el teu compte no sortirà a la secció \"Explorar\"" +showGapBetweenNotesInTimeline: "Mostra una separació entre els articles a la lÃnia de temps" +duplicate: "Duplicat" +left: "Esquerra" +center: "Centre" +wide: "Gran" +narrow: "Estret" +reloadToApplySetting: "Aquest ajust només s'aplicarà després de recarregar la pà gina. Vols fer-ho ara?" +needReloadToApply: "Es requereix recarregar per reflectir aquesta opció " +showTitlebar: "Mostra la barra del tÃtol " clearCache: "Esborra la memòria cau" +onlineUsersCount: "{n} Usuaris es troben en lÃnia " +nUsers: "{n} Usuaris" +nNotes: "{n} Notes" +sendErrorReports: "Enviar informes d'error " +sendErrorReportsDescription: "Quan s'activa, es compartirà amb Misskey informació detallada de l'error quan es trobi un problema això farà pujar la qualitat de Misskey.\nAixò inclourà informació com la versió del SO que fas servir, el navegador web que fas servir, la teva activitat a Misskey, etc." +myTheme: "El meu tema" +backgroundColor: "Color de fons" +accentColor: "Color principal" +textColor: "Color del text" +saveAs: "Desar com..." +advanced: "Avançat" +advancedSettings: "Configuració avançada" +value: "Valor" +createdAt: "Creat el" +updatedAt: "Actualitzat el" +saveConfirm: "Desar canvis?" +deleteConfirm: "Segur que vols esborrar?" +invalidValue: "Valor invà lid." +registry: "Registre " +closeAccount: "Tancar el compte" +currentVersion: "Versió actual" +latestVersion: "Versió nova" +youAreRunningUpToDateClient: "Ja està s fent servir la versió més recent del client." +newVersionOfClientAvailable: "Tens disponible una versió del client més recent." +usageAmount: "Ús " +capacity: "Capacitat" +inUse: "Fet servir" +editCode: "Editar el codi" +apply: "Aplicar" +receiveAnnouncementFromInstance: "Rep notificacions d'aquesta instà ncia " +emailNotification: "Notificacions per correu electrònic " +publish: "Publicar" +inChannelSearch: "Cerca al canal" +useReactionPickerForContextMenu: "Fes clic al botó dret del ratolà per obrir el menú de reaccions" +typingUsers: "{users} està /està n Escrivint " +jumpToSpecifiedDate: "Ves a una data concreta" showingPastTimeline: "Està s veient una lÃnia de temps antiga" +clear: "Tornar" +markAllAsRead: "Marcar tot com llegit" +goBack: "Tornar" +unlikeConfirm: "Vols esborrar el teu m'agrada?" +fullView: "Vista completa." +quitFullView: "Sortir de la vista completa" +addDescription: "Afegeix una descripció " +userPagePinTip: "Podeu seleccionar \"Fixar al perfil\" del menú de notes individuals per mostrar les notes aquÃ." +notSpecifiedMentionWarning: "Aquesta nota esmenta usuaris que no es troben com a destinataris" info: "Informació" +userInfo: "Informació de l'usuari" +unknown: "Desconegut" +onlineStatus: "Connectat" +hideOnlineStatus: "Ocultar l'estat de connexió" +hideOnlineStatusDescription: "Ocultant el teu estat de connexió redueix les funcionalitats d'algunes funcions com la cerca." +online: "Connectat" +active: "Actiu" +offline: "Desconnectat" +notRecommended: "No recomanat" +botProtection: "Protecció contra bots" +instanceBlocking: "Instà ncies blocades/silenciades" +selectAccount: "Seleccionar un compte" +switchAccount: "Canviar de compte" +enabled: "Activat" +disabled: "Desactivat" +quickAction: "Accions rà pides" user: "Usuaris" administration: "Administració" +accounts: "Comptes" +switch: "Canvia" +noMaintainerInformationWarning: "La informació de l'administrador no s'ha configurat" +noBotProtectionWarning: "La protecció contra bots no s'ha configurat." +configure: "Configurar" +postToGallery: "Crear una nova publicació a la galeria" +postToHashtag: "Pública a aquesta etiqueta" +gallery: "Galeria" +recentPosts: "Articles recents" +popularPosts: "Articles populars" +shareWithNote: "Comparteix amb una nota" +ads: "Anuncis" +expiration: "" +startingperiod: "Inici" +memo: "Recordatori" +priority: "Prioritat" +high: "Alta" middle: "Mitjà " +low: "Baixa" +emailNotConfiguredWarning: "Adreça de correu electrònic" +ratio: "Proporció" +previewNoteText: "Mostrar vista prèvia" +customCss: "CSS personalitzat" +customCssWarn: "Aquesta configuració només hauries de configurar-la si saps que fas. Si poses valors inadequats pots fer que el client deixi de funcionar correctament." global: "Global" +squareAvatars: "Mostrar avatars quadrats" +sent: "Enviar" +received: "Rebut" +searchResult: "Resultats de la cerca" +hashtags: "Etiquetes" +troubleshooting: "Solucionar problemes" +useBlurEffect: "Fes servir efectes de desenfocament a la interfÃcie" +learnMore: "Saber més " +misskeyUpdated: "Misskey s'ha actualitzat " +whatIsNew: "Mostra canvis" +translate: "Traduir " +translatedFrom: "Traduït del {x}" +accountDeletionInProgress: "S'està produint l'eliminació del compte" +usernameInfo: "Un nom que identifiqui el teu compte d'altres en aquest servidor. Pots fer servir lletres (a~z, A~Z), números (0~9) i guions baixos (_). Els noms d'usuari no es poden canviar després." +aiChanMode: "Mode IA" +devMode: "Mode desenvolupador" +keepCw: "Mantenir els avisos de contingut" +pubSub: "Comptes Pub/Sub" +lastCommunication: "Última comunicació " +resolved: "Resolt" +unresolved: "Sense resoldre" +breakFollow: "Deixar de seguir" +breakFollowConfirm: "Vols deixar de seguir?" +itsOn: "Activat" +itsOff: "Desactivat" +on: "Activar" +off: "Desactivar" +emailRequiredForSignup: "Demanar correu electrònic per registrar-se " +unread: "Sense llegir" +filter: "Filtrar" +controlPanel: "Panel de control" +manageAccounts: "Gestionar comptes" +makeReactionsPublic: "Reaccions públiques " +makeReactionsPublicDescription: "Això fa que totes les teves reaccions siguin visibles públicament " +classic: "Clà ssic " +muteThread: "Silenciar el fil" +unmuteThread: "Deixar de silenciar el fil" +followingVisibility: "Visibilitat dels seguiments" +followersVisibility: "Visibilitat dels seguidors" +continueThread: "Veure la continuació del fil" +deleteAccountConfirm: "Això eliminarà el teu compte irreversiblement. Procedir?" +incorrectPassword: "Contrasenya incorrecta." +voteConfirm: "Confirma el teu vot \"{choice}\"" +hide: "Amagar" +useDrawerReactionPickerForMobile: "Mostrar el selector de reaccions com un calaix al mòbil " +welcomeBackWithName: "Benvingut de nou, {name}" +clickToFinishEmailVerification: "Si us plau, fes clic a [{ok}] per completar la verificació per correu electrònic " +overridedDeviceKind: "Tipus de dispositiu" +smartphone: "Telèfon intel·ligent" +tablet: "Tauleta" +auto: "Automà tic " +themeColor: "Color del tema" +size: "Mida" +numberOfColumn: "Nombre de columnes" searchByGoogle: "Cercar" +instanceDefaultLightTheme: "Tema clar per defecte de tota la instà ncia " +instanceDefaultDarkTheme: "Tema fosc per defecte de tota la instà ncia " +instanceDefaultThemeDescription: "Introdueix el codi del tema en format d'objecte" +mutePeriod: "Duració del silenci" +period: "LÃmit de temps" +indefinitely: "Permanent" +tenMinutes: "10 minuts" +oneHour: "1 hora" +oneDay: "Un dia" +oneWeek: "Una setmana" +oneMonth: "Un mes" +reflectMayTakeTime: "Això pot trigar una estona a tenir efecte" +failedToFetchAccountInformation: "No es pot obtenir la informació del compte" +rateLimitExceeded: "S'ha arribat al mà xim de peticions" +cropImage: "Retalla la imatge" +cropImageAsk: "Vols retallar la imatge?" +cropYes: "Retallar" +cropNo: "Fer servir tal qual" file: "Fitxers" +recentNHours: "Últimes {n} hores" +recentNDays: "Últims {n} dies" +noEmailServerWarning: "Correu electrònic del servidor sense configurar" +thereIsUnresolvedAbuseReportWarning: "Hi ha informes sense solucionar." +recommended: "Recomanat" +check: "Verificar" +driveCapOverrideLabel: "Canvia la capacitat del Disc per aquest usuari" +driveCapOverrideCaption: "Restableix la mida original posant un valor de 0 o menys." +requireAdminForView: "Has de ser administrador per poder veure això." +isSystemAccount: "Un compte creat i operat automà ticament pel sistema." +typeToConfirm: "Si us plau, escriu {x} per confirmar" +deleteAccount: "Esborrar el compte" +document: "Documentació" +numberOfPageCache: "Nombre de pà gines a la memòria cau" +numberOfPageCacheDescription: "Incrementant aquest nombre farà que millori l'experiència de l'usuari, però es farà servir més memòria al dispositiu de l'usuari." +logoutConfirm: "Vols sortir?" +lastActiveDate: "Fet servir per última vegada" +statusbar: "Barra d'estat" +pleaseSelect: "Selecciona una opció" +reverse: "Invertir" +colored: "Colorit" +refreshInterval: "Interval d'actualització " +label: "Etiqueta" +type: "Tipus" +speed: "Velocitat" +slow: "Lent" +fast: "Rà pid " +sensitiveMediaDetection: "Detecció de contingut sensible" +localOnly: "Només local" +remoteOnly: "Només remot" +failedToUpload: "Ha fallat la pujada" +cannotUploadBecauseInappropriate: "Aquest fitxer no es pot pujar perquè s'ha trobat que algunes parts són inapropiades." +cannotUploadBecauseNoFreeSpace: "Ha fallat la pujada del fitxer perquè no hi ha capacitat al Disc." +cannotUploadBecauseExceedsFileSizeLimit: "Aquest fitxer no es pot pujar perquè supera la mida permesa." +beta: "Proves" +enableAutoSensitive: "Marcar com a sensible automà ticament " +enableAutoSensitiveDescription: "Permet la detecció i el marcat automà tic dels mitjans sensibles fent servir aprenentatge automà tic quan sigui possible. Si aquesta opció es troba desactivada potser que estigui activada per a tota la instà ncia. " +activeEmailValidationDescription: "Activa la validació estricta de comptes de correu electrònic, inclou la validació d'adreces d'un sol ús i si es possible comunicar-se amb aquestes. Quan es troba desactivada només es và lida el format del correu electrònic." +navbar: "Barra de navegació " +shuffle: "Aleatori" +account: "Compte" +move: "Mou" +pushNotification: "Enviament de notificacions" +subscribePushNotification: "Activar l'enviament de notificacions" +unsubscribePushNotification: "Desactivar l'enviament de notificacions" +pushNotificationAlreadySubscribed: "L'enviament de notificacions ja és activat" +pushNotificationNotSupported: "El teu navegador o la teva instà ncia no suporta l'enviament de notificacions " +sendPushNotificationReadMessage: "Esborrar les notificacions enviades quan s'hagin llegit" +sendPushNotificationReadMessageCaption: "Això pot fer que el teu dispositiu consumeixi més bateria" +windowMaximize: "Maximitzar " +windowMinimize: "Minimitzar" +windowRestore: "Restaurar" +caption: "Llegenda" +loggedInAsBot: "Identificat com a bot" +tools: "Eines" +cannotLoad: "No es pot carregar" +numberOfProfileView: "Visualitzacions del perfil" +like: "M'agrada " +unlike: "Treure m'agrada " +numberOfLikes: "M'agraden " +show: "Veure" +neverShow: "No mostrar més " +remindMeLater: "Recorda-m'ho més tard" +didYouLikeMisskey: "T'està agradant Misskey?" +pleaseDonate: "A {host} fem servir el software lliure Misskey. Considera fer un donatiu a Misskey perquè pugui continuar el seu desenvolupament!" +roles: "Rols" +role: "Rols" +noRole: "No s'han trobat rols" +normalUser: "Usuari normal" +undefined: "Sense definir" +assign: "Assignar " +unassign: "Treure" +color: "Color" +manageCustomEmojis: "Gestiona els emojis personalitzats" +manageAvatarDecorations: "Gestiona les decoracions dels avatars " +youCannotCreateAnymore: "Has arribat al mà xim de creacions" +cannotPerformTemporary: "Temporalment no disponible" +cannotPerformTemporaryDescription: "Aquesta acció no es pot dur a terme temporalment per arribar al seu lÃmit d'execució. Pots esperar una mica i tornar-ho a intentar." +invalidParamError: "Parà metres incorrectes " +invalidParamErrorDescription: "Els parà metres demanats no són correctes. Normalment això es deu a un error, però també pot ser a alguna entrada excedint els lÃmits o similar." +permissionDeniedError: "Operació no permesa " +permissionDeniedErrorDescription: "Aquest compte no té suficients permisos per dur a terme aquesta acció " +preset: "Predefinit" +selectFromPresets: "Escull des dels predefinits" +achievements: "Assoliments" +gotInvalidResponseError: "Resposta del servidor invà lida " +gotInvalidResponseErrorDescription: "No es pot contactar amb el servidor o potser es troba fora de lÃnia per manteniment. Provar-ho de nou més tard." +thisPostMayBeAnnoying: "Aquesta nota pot ser molesta per algú." +thisPostMayBeAnnoyingHome: "Publicar a la lÃnia de temps d'Inici" +thisPostMayBeAnnoyingCancel: "Cancel·lar " +thisPostMayBeAnnoyingIgnore: "Publicar de totes maneres" +collapseRenotes: "Col·lapsar les renotes que ja has vist" +internalServerError: "Error intern del servidor" +internalServerErrorDescription: "El servidor ha fallat de manera inexplicable." +copyErrorInfo: "Copiar la informació de l'error " +joinThisServer: "Registra't en aquesta instà ncia " +exploreOtherServers: "Cerca una altra instà ncia " +letsLookAtTimeline: "Dona una ullada a la lÃnia de temps" +disableFederationConfirm: "Vols treure la federació?" +disableFederationConfirmWarn: "Fins i tot traient la federació, les publicacions continuaren sent públiques, a no ser que es digui el contrari. Normalment no has de tocar això." +disableFederationOk: "Desactivar" +invitationRequiredToRegister: "Aquesta instà ncia només permet el registre per invitació. Per registrar-te has d'introduir el codi d'invitació." +emailNotSupported: "Aquesta instà ncia no suporta l'enviament de correus electrònics " +postToTheChannel: "Publicar a un Canal" +cannotBeChangedLater: "Això ja no es podrà canviar." +reactionAcceptance: "Acceptació de reaccions " +likeOnly: "Només m'agraden " +likeOnlyForRemote: "Tot (només m'agraden d'instà ncies remotes)" +nonSensitiveOnly: "Només sense contingut sensible" +nonSensitiveOnlyForLocalLikeOnlyForRemote: "Només contingut no sensible (Només m'agraden d'instà ncies remotes)" +rolesAssignedToMe: "Rols assignats " +resetPasswordConfirm: "Vols canviar la teva contrasenya?" +sensitiveWords: "Paraules sensibles" +sensitiveWordsDescription: "La visibilitat de totes les notes que continguin qualsevol de les paraules configurades seran, automà ticament, afegides a \"Inici\". Pots llistar diferents paraules separant les per lÃnies noves." +sensitiveWordsDescription2: "Fent servir espais crearà expressions AND si l'expressió s'envolta amb barres inclinades es converteix en una expressió regular." +prohibitedWords: "Paraules prohibides" +prohibitedWordsDescription: "Quan intenteu publicar una Nota que conté una paraula prohibida, feu que es converteixi en un error. Es poden dividir i establir múltiples lÃnies." +prohibitedWordsDescription2: "Fent servir espais crearà expressions AND si l'expressió s'envolta amb barres inclinades es converteix en una expressió regular." +hiddenTags: "Etiquetes ocultes" +hiddenTagsDescription: "La visibilitat de totes les notes que continguin qualsevol de les paraules configurades seran, automà ticament, afegides a \"Inici\". Pots llistar diferents paraules separant les per lÃnies noves." +notesSearchNotAvailable: "La cerca de notes no es troba disponible." +license: "Llicència" +unfavoriteConfirm: "Esborrar dels favorits?" +myClips: "Els meus retalls" +drivecleaner: "Netejador de Disc" +retryAllQueuesNow: "Prova de nou d'executar totes les cues" +retryAllQueuesConfirmTitle: "Tornar a intentar-ho tot?" +retryAllQueuesConfirmText: "Això farà que la cà rrega del servidor augmenti temporalment." +enableChartsForRemoteUser: "Generar grà fiques d'usuaris remots" +enableChartsForFederatedInstances: "Generar grà fiques d'instà ncies remotes" +showClipButtonInNoteFooter: "Afegir \"Retall\" al menú d'acció de la nota" +reactionsDisplaySize: "Mida de les reaccions" +limitWidthOfReaction: "Limitar l'amplada mà xima de la reacció i mostrar-les en una mida reduïda " +noteIdOrUrl: "ID o URL de la nota" +video: "VÃdeo" +videos: "VÃdeos " +audio: "So" +audioFiles: "So" +dataSaver: "Economitzador de dades" +accountMigration: "Migració del compte" +accountMoved: "Aquest usuari té un compte nou:" +accountMovedShort: "Aquest compte ha sigut migrat" +operationForbidden: "Operació no permesa " +forceShowAds: "Mostra els anuncis sempre " +addMemo: "Afegir recordatori" +editMemo: "Editar recordatori" +reactionsList: "Reaccions" +renotesList: "Impulsos" +notificationDisplay: "Notificacions" +leftTop: "Dalt a l'esquerra " +rightTop: "Dalt a la dreta " +leftBottom: "A baix a l'esquerra" +rightBottom: "A baix a la dreta" +stackAxis: "Apilar en direcció " +vertical: "Vertical" +horizontal: "Horitzontal " +position: "Posició " +serverRules: "Regles del servidor" +pleaseConfirmBelowBeforeSignup: "Per obrir un compte en aquest servidor, has de llegir i acceptar el següent." +pleaseAgreeAllToContinue: "Has d'acceptar tots els camps de dalt per poder continuar." +continue: "Continuar" +preservedUsernames: "Noms d'usuaris reservats" +preservedUsernamesDescription: "Llistat de noms d'usuaris que no es poden fer servir separats per salts de linia. Aquests noms d'usuaris no estaran disponibles quan es creï un compte d'usuari normal, però els administradors els poden fer servir per crear comptes manualment. Per altre banda els comptes ja creats amb aquests noms d'usuari no es veure'n afectats." +createNoteFromTheFile: "Compon una nota des d'aquest fitxer" +archive: "Arxiu" +channelArchiveConfirmTitle: "Vols arxivar {name}?" +channelArchiveConfirmDescription: "Un Canal arxivat no apareixerà a la llista de canals o als resultats de cerca. Tampoc es poden afegir noves entrades." +thisChannelArchived: "Aquest Canal ha sigut arxivat." +displayOfNote: "Mostrar notes" +initialAccountSetting: "Configuració del perfil" +youFollowing: "Seguit" +preventAiLearning: "Descartar l'ús d'aprenentatge automà tic (IA Generativa)" +preventAiLearningDescription: "Demanar els indexadors no fer servir els texts, imatges, etc. en cap conjunt de dades per alimentar l'aprenentatge automà tic (IA Predictiva/ Generativa). Això s'aconsegueix afegint la etiqueta \"noai\" com a resposta HTML al contingut corresponent. Prevenir aquest ús totalment pot ser que no sigui aconseguit, ja que molts indexadors poden obviar aquesta etiqueta." +options: "Opcions" +specifyUser: "Especificar usuari" +failedToPreviewUrl: "Vista prèvia no disponible" +update: "Actualitzar" +rolesThatCanBeUsedThisEmojiAsReaction: "Rols que poden fer servir aquest emoji com a reacció " +rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "Si cap rol es especificat tothom ho pot fer servir" +rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "Aquests rols han de ser públics " +cancelReactionConfirm: "Vols esborrar la teva reacció?" +changeReactionConfirm: "Vols canviar la teva reacció?" +later: "Més tard" +goToMisskey: "Ves a Misskey" +additionalEmojiDictionary: "Diccionari d'emojis adicionals" +installed: "Instal·lats " +branding: "Marca" +enableServerMachineStats: "Publicar estadÃstiques del maquinari del servidor" +enableIdenticonGeneration: "Activar la generació d'icones d'identificació " +turnOffToImprovePerformance: "Desactivant aquesta opció es pot millorar el rendiment." +createInviteCode: "Crear codi d'invitació " +createWithOptions: "Crear invitació amb opcions" +createCount: "Comptador d'invitacions " +inviteCodeCreated: "Invitació creada" +inviteLimitExceeded: "Has sobrepassat el lÃmit d'invitacions que pots crear." +createLimitRemaining: "Et queden {limit} invitacions restants" +inviteLimitResetCycle: "Cada {time} {limit} invitacions." +expirationDate: "Data de venciment" +noExpirationDate: "Sense data de venciment" +inviteCodeUsedAt: "Codi d'invitació fet servir el" +registeredUserUsingInviteCode: "Codi d'invitació fet servir per l'usuari " +waitingForMailAuth: "Esperant la verificació per correu electrònic " +inviteCodeCreator: "Invitació creada per" +usedAt: "Utilitzada el" +unused: "Sense utilitzar" +used: "Utilitzada" +expired: "Caducat" +doYouAgree: "Està s d'acord?" +beSureToReadThisAsItIsImportant: "Llegeix això perquè és molt important." +iHaveReadXCarefullyAndAgree: "He llegit {x} i estic d'acord." +dialog: "Dià leg " icon: "Icona" +forYou: "Per a tu" +currentAnnouncements: "Informes actuals" +pastAnnouncements: "Informes passats" +youHaveUnreadAnnouncements: "Tens informes per llegir." +useSecurityKey: "Segueix les instruccions del teu navegador O dispositiu per fer servir el teu passkey." replies: "Respostes" renotes: "Impulsa" +loadReplies: "Mostrar les respostes" +loadConversation: "Mostrar la conversació " +pinnedList: "Llista fixada" +keepScreenOn: "Mantenir la pantalla encesa" +verifiedLink: "La propietat de l'enllaç ha sigut verificada" +notifyNotes: "Notificar quan hi hagi notes noves" +unnotifyNotes: "Deixar de notificar quan hi hagi notes noves" +authentication: "Autenticació " +authenticationRequiredToContinue: "Si us plau autentificat per continuar" +dateAndTime: "Data i hora" +showRenotes: "Mostrar impulsos" +edited: "Editat" +notificationRecieveConfig: "Parà metres de notificacions" +mutualFollow: "Seguidor mutu" +fileAttachedOnly: "Només notes amb adjunts" +showRepliesToOthersInTimeline: "Mostrar les respostes a altres a la lÃnia de temps" +hideRepliesToOthersInTimeline: "Amagar les respostes a altres a la lÃnia de temps" +showRepliesToOthersInTimelineAll: "Mostrar les respostes a altres a usuaris que segueixes a la lÃnia de temps" +hideRepliesToOthersInTimelineAll: "Ocultar les teves respostes a tots els usuaris que segueixes a la lÃnia de temps" +confirmShowRepliesAll: "Aquesta opció no té marxa enrere. Vols mostrar les teves respostes a tots els que segueixes a la teva lÃnia de temps?" +confirmHideRepliesAll: "Aquesta opció no té marxa enrere. Vols ocultar les teves respostes a tots els usuaris que segueixes a la lÃnia de temps?" +externalServices: "Serveis externs" +sourceCode: "Codi font" +impressum: "Impressum" +impressumUrl: "Adreça URL impressum" +impressumDescription: "A països, com Alemanya, la inclusió de la informació de contacte de l'operador (un Impressum) és requereix de manera legal per llocs comercials." +privacyPolicy: "PolÃtica de privacitat" +privacyPolicyUrl: "Adreça URL de la polÃtica de privacitat" +tosAndPrivacyPolicy: "Termes d'ús i polÃtica de privacitat" +avatarDecorations: "Decoracions dels avatars" +attach: "Adjuntar" +detach: "Eliminar" +detachAll: "Treure tot" +angle: "Angle" +flip: "Girar" +showAvatarDecorations: "Mostrar les decoracions dels avatars" +releaseToRefresh: "Deixar anar per actualitzar" +refreshing: "Recarregant..." +pullDownToRefresh: "Llisca cap a baix per recarregar" +disableStreamingTimeline: "Desactivar l'actualització en temps real de les lÃnies de temps" +useGroupedNotifications: "Mostrar les notificacions agrupades " +signupPendingError: "Hi ha hagut un problema verificant l'adreça de correu electrònic. L'enllaç pot haver caducat." +cwNotationRequired: "Si està activat \"Amagar contingut\" s'ha d'escriure una descripció " +doReaction: "Afegeix una reacció " +code: "Codi" +reloadRequiredToApplySettings: "És necessari recarregar la pà gina per aplicar els canvis." +remainingN: "Queden: {n}" +overwriteContentConfirm: "Vols substituir el contingut actual?" +seasonalScreenEffect: "Efectes de pantalla segons les estacions" +decorate: "Decorar" +addMfmFunction: "Afegeix funcions MFM" +enableQuickAddMfmFunction: "Activar accés rà pid per afegir funcions MFM" +bubbleGame: "Bubble Game" +sfx: "Efectes de so" +soundWillBePlayed: "Es reproduiran efectes de so" +showReplay: "Veure reproducció" +replay: "Reproduir" +replaying: "Reproduint" +ranking: "Classificació" +lastNDays: "Últims {n} dies" +backToTitle: "Torna al tÃtol" +hemisphere: "Geolocalització" +withSensitive: "Incloure notes amb fitxers sensibles" +userSaysSomethingSensitive: "La publicació de {name} conte material sensible" +enableHorizontalSwipe: "Lliscar per canviar de pestanya" +surrender: "Cancel·lar " +_bubbleGame: + howToPlay: "Com es juga" + _howToPlay: + section1: "Ajusta la posició i deixa caure l'objecte dintre la caixa." + section2: "Quan dos objectes del mateix tipus es toquen, canviaran en un objecte diferent i guanyares punts." + section3: "El joc s'acabarà quan els objectes sobresurtin de la caixa. Intenta aconseguir la puntuació més gran possible fusionant objectes mentre impedeixes que sobresurtin de la caixa!" +_announcement: + forExistingUsers: "Anunci per usuaris registrats" + forExistingUsersDescription: "Aquest avÃs només es mostrarà als usuaris existents fins al moment de la publicació. Si no també es mostrarà als usuaris que es registrin després de la publicació." + needConfirmationToRead: "Es necessita confirmació de lectura de la notificació " + needConfirmationToReadDescription: "Si s'activa es mostrarà un dià leg per confirmar la lectura d'aquesta notificació. A més aquesta notificació serà exclosa de qualsevol funcionalitat com \"Marcar tot com a llegit\"." + end: "Final de la notificació " + tooManyActiveAnnouncementDescription: "Tenir massa notificacions actives pot empitjorar l'experiència de l'usuari. Considera finalitzar els anuncis que siguin antics." + readConfirmTitle: "Marcar com llegida?" + readConfirmText: "Això marcarà el contingut de \"{title}\" com llegit." + shouldNotBeUsedToPresentPermanentInfo: "Ja que l'ús de notificacions pot impactar l'experiència dels nous usuaris, és recomanable fer servir les notificacions amb el flux d'informació en comptes de fer-les servir en un únic bloc." + dialogAnnouncementUxWarn: "Tenir dues o més notificacions amb l'estil de finestres pot impactar l'experiència de l'usuari, és per això que és recomana fer-lo servir amb cura." + silence: "Sense notificacions" + silenceDescription: "Activant aquesta opció la notificació no es mostrarà ni l'usuari l'haurà de llegir." +_initialAccountSetting: + accountCreated: "S'ha completat la creació del compte!" + letsStartAccountSetup: "Posem rà pidament la configuració inicial del compte." + letsFillYourProfile: "Comencem establint el teu perfil." + profileSetting: "Configuració del perfil" + privacySetting: "Configuració de seguretat" + theseSettingsCanEditLater: "Aquests ajustos es poden canviar més tard." + youCanEditMoreSettingsInSettingsPageLater: "A més d'això, es poden fer diferents configuracions a través de la pà gina de configuració. Assegureu-vos de comprovar-ho més tard." + followUsers: "Prova de seguir usuaris que t'interessin per construir la teva lÃnia de temps." + pushNotificationDescription: "Activant les notificacions emergents et permetrà rebre notificacions de {name} directament al teu dispositiu." + initialAccountSettingCompleted: "Configuració del perfil completada!" + haveFun: "Disfruta {name}!" + youCanContinueTutorial: "Pots continuar amb un tutorial per aprendre a Fer servir {name} (MissKey) o tu pots estalviar i començar a fer-lo servir ja." + startTutorial: "Començar el tutorial" + skipAreYouSure: "Et vols saltar la configuració del perfil?" + laterAreYouSure: "Vols continuar la configuració del perfil més tard?" +_initialTutorial: + launchTutorial: "Començar tutorial" + title: "Tutorial" + wellDone: "Ben fet!" + skipAreYouSure: "Sortir del tutorial?" + _landing: + title: "Benvingut al tutorial" + description: "Aquà aprendrà s el bà sic per poder fer servir Misskey i les seves caracterÃstiques." + _note: + title: "Què és una Nota?" + description: "Les publicacions a Misskey es diuen 'Notes'. Les Notes s'ordenen cronològicament a la lÃnia de temps i s'actualitzen de forma automà tica." + reply: "Fes clic en aquest botó per contestar a un missatge. També és possible contestar a una contestació, continuant la conversació en forma de fil." + renote: "Pots compartir una Nota a la teva pròpia lÃnia de temps. Inclús pots citar-les amb els teus comentaris." + reaction: "Pots afegir reaccions a les Notes. Entrarem més en detall a la pròxima pà gina." + menu: "Pots veure els detalls de les Notes, copiar enllaços i fer diferents accions." + _reaction: + title: "Què són les Reaccions?" + description: "Es poden reaccionar a les Notes amb diferents emoticones. Les reaccions et permeten expressar matisos que hi són més enllà d'un simple m'agrada." + letsTryReacting: "Es poden afegir reaccions fent clic al botó '+'. Prova reaccionant a aquesta nota!" + reactToContinue: "Afegeix una reacció per continuar." + reactNotification: "Rebrà s notificacions en temps real quan un usuari reaccioni a les teves notes." + reactDone: "Pots desfer una reacció fent clic al botó '-'." + _timeline: + title: "El concepte de les lÃnies de temps" + description1: "Misskey mostra diferents lÃnies de temps basades en l'ús (algunes poden no estar disponibles depenent de la polÃtica del servidor)" + home: "Pots veure notes dels comptes que segueixes" + local: "Pots veure les notes dels usuaris del servidor." + social: "Es mostren les notes de les lÃnies de temps d'Inici i Local." + global: "Pots veure les notes de tots els servidors connectats." + description2: "Pots canviar la lÃnia de temps en qualsevol moment fent servir la barra de la pantalla superior." + description3: "A més hi ha lÃnies de temps per llistes i per canals. Si vols saber més {link}." + _postNote: + title: "Configuració de la publicació de les notes" + description1: "Quan públiques una nota a Misskey hi ha diferents opcions disponibles. El formulari de publicació es veu aixÃ" + _visibility: + description: "Pots limitar qui pot veure les teves notes." + public: "La teva nota serà visible per a tots els usuaris." + home: "Publicar només a lÃnia de temps d'Inici. La gent que visiti el teu perfil o mitjançant les remotes també la podran veure." + followers: "Només visible per a seguidors. Només els teus seguidors la podran veure i ningú més. Ningú més podrà fer renotes." + direct: "Només visible per a alguns seguidors, el destinatari rebre una notificació. Es pot fer servir com una alternativa als missatges directes." + doNotSendConfidencialOnDirect1: "Tingues cura quan enviïs informació sensible." + doNotSendConfidencialOnDirect2: "Els administradors del servidor poden veure tot el que escrius. Ves compte quan enviïs informació sensible en enviar notes directes a altres usuaris en servidors de poca confiança." + localOnly: "Publicar amb aquesta opció activada farà que la nota no federi amb altres servidors. Els usuaris d'altres servidors no podran veure la nota directament, sense importar les opcions de visualització." + _cw: + title: "AvÃs de Contingut (CW)" + description: "En comptes del cos de la nota es mostrarà el que s'escrigui al camp de 'comentaris'. Fent clic a 'Llegir més' es mostrarà el cos." + _exampleNote: + cw: "Això et farà venir gana!" + note: "Acabo de menjar-me un donut de xocolata ðŸ©ðŸ˜‹" + useCases: "Això es fa servir per seguir normes del servidor sobre certes notes o per ocultar contingut sensible O revelador." + _howToMakeAttachmentsSensitive: + title: "Com marcar adjunts com a contingut sensible?" + description: "Per adjunts que sigui requerit per les normes del servidor o que puguin contenir material sensible, s'ha d'afegir l'opció 'sensible'." + tryThisFile: "Prova de marcar la imatge adjunta en aquest formulari com a sensible!" + _exampleNote: + note: "Oops! L'he fet bona en obrir la tapa de Nocilla..." + method: "Per marcar un adjunt com a sensible, fes clic a la miniatura de l'adjunt, obre el menú i fes clic a 'Marcar com a sensible'." + sensitiveSucceeded: "Quan adjuntis fitxers si us plau marca la sensibilitat seguint les normes del servidor." + doItToContinue: "Marca el fitxer adjunt com a sensible per poder continuar." + _done: + title: "Has completat el tutorial 🎉" + description: "Les funcions explicades aquà és una petita mostra. Per una explicació més detallada de com fer servir MissKey consulta {link}." +_timelineDescription: + home: "A la lÃnia de temps d'Inici pots veure les notes dels usuaris que segueixes." + local: "A la lÃnia de temps Local pots veure les notes de tots els usuaris d'aquest servidor." + social: "La lÃnia de temps Social mostren les notes de les lÃnies de temps d'Inici i Local." + global: "A la lÃnia de temps Global pots veure les notes de tots els servidors connectats." +_serverRules: + description: "Un conjunt de regles que seran mostrades abans de registrar-se. Es recomanable configurar un resum dels termes d'ús." +_serverSettings: + iconUrl: "URL de la icona" + appIconDescription: "Especifica la icona que es mostrarà quan el {host} es mostri en una aplicació." + appIconUsageExample: "Per exemple com a PWA, o quan es mostri com un favorit a la pà gina d'inici del telèfon mòbil" + appIconStyleRecommendation: "Com la icona pot ser retallada com un cercle o un quadrat, es recomana fer servir una icona amb un marge acolorit que l'envolti." + appIconResolutionMustBe: "La resolució mÃnima és {resolution}." + manifestJsonOverride: "Sobreescriure manifest.json" + shortName: "Nom curt" + shortNameDescription: "Una abreviatura del nom de la instà ncia que es poguà mostrar en cas que el nom oficial sigui massa llarg" + fanoutTimelineDescription: "Quan es troba activat millora bastant el rendiment quan es recuperen les lÃnies de temps i redueix la carrega de la base de dades. Com a contrapunt, l'ús de memòria de Redis es veurà incrementada. Considera d'estabilitat aquesta opció en cas de tenir un servidor amb poca memòria o si tens problemes de inestabilitat." + fanoutTimelineDbFallback: "Carregar de la base de dades" + fanoutTimelineDbFallbackDescription: "Quan s'activa, la lÃnia de temps fa servir la base de dades per consultes adicionals si la lÃnia de temps no es troba a la memòria cau. Si és desactiva la cà rrega del servidor és veure reduïda, però també és reduirà el nombre de lÃnies de temps que és poden obtenir." +_accountMigration: + moveFrom: "Migrar un altre compte a aquest" + moveFromSub: "Crear un à lies per un altre compte" + moveFromLabel: "Compte original #{n}" + moveFromDescription: "Has de crear un à lies del compte que vols migrar en aquest compte.\nFes servir aquest format per posar el compte que vols migrar: @nomusuari@servidor.exemple.com\nPer esborrar l'à lies deixa el camp en blanc (no és recomanable de fer)" + moveTo: "Migrar aquest compte a un altre" + moveToLabel: "Compte al qual es vol migrar:" + moveCannotBeUndone: "Les migracions dels comptes no es poden desfer." + moveAccountDescription: "Això migrarà la teva compte a un altre diferent.\n ・Els seguidors d'aquest compte és passaran al compte nou de forma automà tica\n ・Es deixaran de seguir a tots els usuaris que es segueixen actualment en aquest compte\n ・No es poden crear notes noves, etc. en aquest compte\n\nSi bé la migració de seguidors es automà tica, has de preparar alguns pasos manualment per migrar la llista d'usuaris que segueixes. Per fer això has d'exportar els seguidors que després importaraes al compte nou mitjançant el menú de configuració. El mateix procediment s'ha de seguir per less teves llistes i els teus usuaris silenciats i bloquejats.\n\n(Aquesta explicació s'aplica a Misskey v13.12.0 i posteriors. Altres aplicacions, com Mastodon, poden funcionar diferent.)" + moveAccountHowTo: "Per fer la migració, primer has de crear un à lies per aquest compte al compte al qual vols migrar.\nDesprés de crear l'à lies, introdueix el compte al qual vols migrar amb el format següent: @nomusuari@servidor.exemple.com" + startMigration: "Migrar" + migrationConfirm: "Vols migrar aquest compte a {account}? Una vegada comenci la migració no es podrà parar O fer marxa enrere i no podrà s tornar a fer servir aquest compte mai més." + movedAndCannotBeUndone: "Aquest compte ha migrat.\nLes migracions no es poden desfer." + postMigrationNote: "Aquest compte deixarà de seguir tots els comptes que segueix 24 hores després de germinar la migració.\nEl nombre de seguidors i seguits passarà a ser de zero. Per evitar que els teus seguidors no puguin veure les publicacions marcades com a només seguidors continuaren seguint aquest compte." + movedTo: "Nou compte:" +_achievements: + earnedAt: "Desbloquejat el" + _types: + _notes1: + title: "AquÃ, configurant el meu msky" + description: "Publica la teva primera Nota" + flavor: "Passa-t'ho bé fent servir Miskey!" + _notes10: + title: "Algunes notes" + description: "Publica 10 notes" + _notes100: + title: "Un piló de notes" + description: "Publica 100 notes" + _notes500: + title: "Cobert de notes" + description: "Publica 500 notes" + _notes1000: + title: "Un piló de notes" + description: "1 000 notes publicades" + _notes5000: + title: "Desbordament de notes" + description: "5 000 notes publicades" + _notes10000: + title: "Supernota" + description: "10 000 notes publicades" + _notes20000: + title: "Necessito... Més... Notes!" + description: "20 000 notes publicades" + _notes30000: + title: "Notes notes notes!" + description: "30 000 notes publicades" + _notes40000: + title: "Fà brica de notes" + description: "40 000 notes publicades" + _notes50000: + title: "Planeta de notes" + description: "50 000 notes publicades" + _notes60000: + title: "Quà sar de notes" + description: "60 000 notes publicades" + _notes70000: + title: "Forat negre de notes" + description: "70 000 notes publicades" + _notes80000: + title: "Galà xia de notes" + description: "80 000 notes publicades" + _notes90000: + title: "Univers de notes" + description: "90 000 notes publicades" + _notes100000: + title: "ALL YOUR NOTE ARE BELONG TO US" + description: "100 000 notes publicades" + flavor: "Segur que tens moltes coses a dir?" + _login3: + title: "Principiant I" + description: "Vas iniciar sessió fa tres dies" + flavor: "Des d'avui diguem Misskist" + _login7: + title: "Principiant II" + description: "Vas iniciar sessió fa set dies" + flavor: "Ja saps com va funcionant tot?" + _login15: + title: "Principiant III" + description: "Vas iniciar sessió fa quinze dies" + _login30: + title: "Misskist I" + description: "Vas iniciar sessió fa trenta dies" + _login60: + title: "Misskist II" + description: "Vas iniciar sessió fa seixanta dies" + _login100: + title: "Misskist III" + description: "Vas iniciar sessió fa cent dies" + flavor: "Misskist violent" + _login200: + title: "Regular I" + description: "Vas iniciar sessió fa dos-cents dies" + _login300: + title: "Regular II" + description: "Vas iniciar sessió fa tres-cents dies" + _login400: + title: "Regular III" + description: "Vas iniciar sessió fa quatre-cents dies" + _login500: + title: "Expert I" + description: "Vas iniciar sessió fa cinc-cents dies" + flavor: "Amics, he dit massa vegades que soc un amant de les notes" + _login600: + title: "Expert II" + description: "Vas iniciar sessió fa sis-cents dies" + _login700: + title: "Expert III" + description: "Vas iniciar sessió fa set-cents dies" + _login800: + title: "Mestre de les Notes I" + description: "Vas iniciar sessió fa vuit-cents dies " + _login900: + title: "Mestre de les Notes II" + description: "Vas iniciar sessió fa nou-cents dies" + _login1000: + title: "Mestre de les Notes III" + description: "Vas iniciar sessió fa mil dies" + flavor: "Grà cies per fer servir MissKey!" + _noteClipped1: + title: "He de retallar-te!" + description: "Retalla la teva primera nota" + _noteFavorited1: + title: "Quan miro les estrelles" + description: "La primera vegada que vaig registrar el meu favorit" + _myNoteFavorited1: + title: "Vull una estrella" + description: "La meva nota va ser registrada com favorita per una de les altres persones" + _profileFilled: + title: "Estic a punt" + description: "Vaig fer la configuració de perfil" + _markedAsCat: + title: "Soc un gat" + description: "He establert el meu compte com si fos un Gat" + flavor: "Encara no tinc nom" + _following1: + title: "És el meu primer seguiment" + description: "És la primera vegada que et segueixo" + _following10: + title: "Segueix-me... Segueix-me..." + description: "Seguir 10 usuaris" + _following50: + title: "Molts amics" + description: "Seguir 50 comptes" + _following100: + title: "100 amics" + description: "Segueixes 100 comptes" + _following300: + title: "Sobrecà rrega d'amics" + description: "Segueixes 300 comptes" + _followers1: + title: "Primer seguidor" + description: "1 seguidor guanyat" + _followers10: + title: "Segueix-me!" + description: "10 seguidors guanyats" + _followers50: + title: "Venen en manada" + description: "50 seguidors guanyats" + _followers100: + title: "Popular" + description: "100 seguidors guanyats" + _followers300: + title: "Si us plau, d'un en un!" + description: "300 seguidors guanyats" + _followers500: + title: "Torre de rà dio" + description: "500 seguidors guanyats" + _followers1000: + title: "Influenciador" + description: "1 000 seguidors guanyats" + _collectAchievements30: + title: "Col·leccionista d'èxits " + description: "Desbloqueja 30 assoliments" + _viewAchievements3min: + title: "M'agraden els èxits " + description: "Mira la teva llista d'assoliments durant més de 3 minuts" + _iLoveMisskey: + title: "Estimo Misskey" + description: "Publica \"I ⤠#Misskey\"" + flavor: "L'equip de desenvolupament de Misskey agraeix el vostre suport!" + _foundTreasure: + title: "A la Recerca del Tresor" + description: "Has trobat el tresor amagat" + _client30min: + title: "Parem una estona" + description: "Mantingues obert Misskey per 30 minuts" + _client60min: + title: "A totes amb Misskey" + description: "Mantingues Misskey obert per 60 minuts" + _noteDeletedWithin1min: + title: "No et preocupis" + description: "Esborra una nota al minut de publicar-la" + _postedAtLateNight: + title: "Nocturn" + description: "Publica una nota a altes hores de la nit " + flavor: "És hora d'anar a dormir." + _postedAt0min0sec: + title: "Rellotge xerraire" + description: "Publica una nota a les 0:00" + flavor: "Tic tac, tic tac, tic tac, DING!" + _selfQuote: + title: "Autoreferència " + description: "Cita una nota teva" + _htl20npm: + title: "LÃnia de temps fluida" + description: "La teva lÃnia de temps va a més de 20npm (notes per minut)" + _viewInstanceChart: + title: "Analista " + description: "Mira els grà fics de la teva instà ncia " + _outputHelloWorldOnScratchpad: + title: "Hola, món!" + description: "Escriu \"hola, món\" al bloc de notes" + _open3windows: + title: "Multi finestres" + description: "I va obrir més de tres finestres" + _driveFolderCircularReference: + title: "Consulteu la secció de bucle" + description: "Intenta crear carpetes recursives al Disc" + _reactWithoutRead: + title: "De veritat has llegit això?" + description: "Reaccions a una nota de més de 100 carà cters publicada fa menys de 3 segons " + _clickedClickHere: + title: "Fer clic" + description: "Has fet clic aquà " + _justPlainLucky: + title: "Ha sigut sort" + description: "Oportunitat de guanyar-lo amb una probabilitat d'un 0.005% cada 10 segons" + _setNameToSyuilo: + title: "soc millor" + description: "Posat \"siuylo\" com a nom" + _passedSinceAccountCreated1: + title: "Primer aniversari" + description: "Ja ha passat un any d'ençà que vas crear el teu compte" + _passedSinceAccountCreated2: + title: "Segon aniversari" + description: "Ja han passat dos anys d'ençà que vas crear el teu compte" + _passedSinceAccountCreated3: + title: "Tres anys" + description: "Ja han passat tres anys d'ençà que vas crear el teu compte" + _loggedInOnBirthday: + title: "Felicitats!" + description: "T'has identificat el dia del teu aniversari" + _loggedInOnNewYearsDay: + title: "Bon any nou!" + description: "T'has identificat el primer dia de l'any " + flavor: "A per un altre any memorable a la teva instà ncia " + _cookieClicked: + title: "Un joc en què fas clic a les galetes" + description: "Pica galetes" + flavor: "Espera, ets al lloc web correcte?" + _brainDiver: + title: "Busseja Ments" + description: "Publica un enllaç al Busseja Ments" + flavor: "Misskey-Misskey La-Tu-Ma" + _smashTestNotificationButton: + title: "Sobrecà rrega de proves" + description: "Envia moltes notificacions de prova en un perÃode de temps molt curt" + _tutorialCompleted: + title: "Diploma del Curs Elemental de Misskey" + description: "Has completat el tutorial" + _bubbleGameExplodingHead: + title: "🤯" + description: "L'objecte més gran del joc de la bombolla " + _bubbleGameDoubleExplodingHead: + title: "Doble 🤯" + description: "Dos dels objectes més grans del joc de la bombolla al mateix temps" + flavor: "Pots emplenar una carmanyola com aquesta 🤯🤯 una mica" _role: + new: "Nou rol" + edit: "Editar el rol" + name: "Nom del rol" + description: "Descripció del rol" + permission: "Permisos de rol" + descriptionOfPermission: "Els <b>Moderadors</b> poden fer operacions bà siques de moderació.\nEls <b>Administradors</b> poden canviar tots els ajustos del servidor." + assignTarget: "Assignar " + descriptionOfAssignTarget: "<b>Manual</b> per canviar manualment qui és part d'aquest rol i qui no.\n<b>Condicional</b> per afegir o eliminar de manera automà tica els usuaris d'aquest rol basat en una determinada condició." + manual: "Manual" + manualRoles: "Rols manuals" + conditional: "Condicional" + conditionalRoles: "Rols condicionals" + condition: "Condició" + isConditionalRole: "Aquest és un rol condicional" + isPublic: "Rol públic" + descriptionOfIsPublic: "Aquest rol es mostrarà al perfil dels usuaris al que se'ls assigni." + options: "Opcions" + policies: "PolÃtiques" + baseRole: "Plantilla de rols" + useBaseValue: "Fer servir els valors de la plantilla de rols" + chooseRoleToAssign: "Selecciona els rols a assignar" + iconUrl: "URL de la icona " + asBadge: "Mostrar com a insÃgnia " + descriptionOfAsBadge: "La icona d'aquest rol es mostrarà al costat dels noms d'usuaris que tinguin assignats aquest rol." + isExplorable: "Fer el rol explorable" + descriptionOfIsExplorable: "La lÃnia de temps d'aquest rol i la llista d'usuaris seran públics si s'activa." + displayOrder: "Posició " + descriptionOfDisplayOrder: "Com més gran és el número, més dalt la seva posició a la interfÃcie." + canEditMembersByModerator: "Permetre que els moderadors editin la llista d'usuaris en aquest rol" + descriptionOfCanEditMembersByModerator: "Quan s'activa, els moderadors, aixà com els administradors, podran afegir i treure usuaris d'aquest rol. Si es troba desactivat, només els administradors poden assignar usuaris." + priority: "Prioritat" _priority: + low: "Baixa" middle: "Mitjà " + high: "Alta" _options: + gtlAvailable: "Pot veure la lÃnia de temps global" + ltlAvailable: "Pot veure la lÃnia de temps local" + canPublicNote: "Pot enviar notes públiques" + canInvite: "Pot crear invitacions a la instà ncia " + inviteLimit: "LÃmit d'invitacions " + inviteLimitCycle: "Temps de refresc de les invitacions" + inviteExpirationTime: "Interval de caducitat de les invitacions" + canManageCustomEmojis: "Gestiona els emojis personalitzats" + canManageAvatarDecorations: "Gestiona les decoracions dels avatars " + driveCapacity: "Capacitat del disc" + alwaysMarkNsfw: "Marca sempre els fitxers com a sensibles" + pinMax: "Nombre mà xim de notes fixades" antennaMax: "Nombre mà xim d'antenes" + wordMuteMax: "Nombre mà xim de carà cters permesos a les paraules silenciades" + webhookMax: "Nombre mà xim de Webhooks" + clipMax: "Nombre mà xim de clips" + noteEachClipsMax: "Nombre mà xim de notes dintre d'un clip" + userListMax: "Nombre mà xim de llistes d'usuaris " + userEachUserListsMax: "Nombre mà xim d'usuaris dintre d'una llista d'usuaris " + rateLimitFactor: "Limitador" + descriptionOfRateLimitFactor: "LÃmits baixos són menys restrictius, lÃmits alts són més restrictius." + canHideAds: "Pot amagar els anuncis" + canSearchNotes: "Pot cercar notes" + canUseTranslator: "Pot fer servir el traductor" + avatarDecorationLimit: "Nombre mà xim de decoracions que es poden aplicar els avatars" + _condition: + isLocal: "Usuari local" + isRemote: "Usuari remot" + createdLessThan: "Han passat menys de X a passat des de la creació del compte" + createdMoreThan: "Han passat més de X des de la creació del compte" + followersLessThanOrEq: "Té menys de X seguidors" + followersMoreThanOrEq: "Té X o més seguidors" + followingLessThanOrEq: "Segueix X o menys comptes" + followingMoreThanOrEq: "Segueix a X o més comptes" + notesLessThanOrEq: "Les publicacions són menys o igual a " + notesMoreThanOrEq: "Les publicacions són més o igual a " + and: "AND condicional " + or: "OR condicional" + not: "NOT condicional" +_sensitiveMediaDetection: + description: "Redueix els esforços de moderació grà cies al reconeixement automà tic dels fitxers amb contingut sensible mitjançant Machine Learing. Això augmentarà la cà rrega del servidor." + sensitivity: "Sensibilitat de la detecció " + sensitivityDescription: "Reduint la sensibilitat provocarà menys falsos positius. D'altra banda incrementant-ho generarà més falsos negatius." + setSensitiveFlagAutomatically: "Marcar com a sensible" + setSensitiveFlagAutomaticallyDescription: "Els resultats de la detecció interna seran desats, inclòs si aquesta opció es troba desactivada." + analyzeVideos: "Activar anà lisis de vÃdeos " + analyzeVideosDescription: "Analitzar els vÃdeos a més de les imatges. Això incrementarà lleugerament la cà rrega del servidor." +_emailUnavailable: + used: "Aquest correu electrònic ja s'està fent servir" + format: "El format del correu electrònic és invà lid " + disposable: "No es poden fer servir adreces de correu electrònic d'un sol ús " + mx: "Aquest servidor de correu electrònic no és và lid " + smtp: "Aquest servidor de correu electrònic no respon" + banned: "No pots registrar-te amb aquesta adreça de correu electrònic " +_ffVisibility: + public: "Publicar" + followers: "Visible només per a seguidors " + private: "Privat" +_signup: + almostThere: "Ja quasi estem" + emailAddressInfo: "Si us plau, escriu la teva adreça de correu electrònic. No es farà pública." + emailSent: "S'ha enviat un correu de confirmació a ({email}). Si us plau, fes clic a l'enllaç per completar el registre." +_accountDelete: + accountDelete: "Eliminar el compte" + mayTakeTime: "Com l'eliminació d'un compte consumeix bastants recursos, pot trigar un temps perquè es completi l'esborrat, depenent si tens molt contingut i la quantitat de fitxer que hagis pujat." + sendEmail: "Una vegada hagi finalitzat l'esborrat del compte rebrà s un correu electrònic a l'adreça que tinguis registrada en aquest compte." + requestAccountDelete: "Demanar l'eliminació del compte" + started: "Ha començat l'esborrat del compte." + inProgress: "L'esborrat es troba en procés " +_ad: + back: "Tornar" + reduceFrequencyOfThisAd: "Mostrar menys aquest anunci" + hide: "No mostrar mai" + timezoneinfo: "El dia de la setmana ve determinat del fus horari del servidor." + adsSettings: "Configuració d'anuncis " + notesPerOneAd: "Interval d'emplaçament d'anuncis en temps real (Notes per anuncis)" + setZeroToDisable: "Ajusta aquest valor a 0 per deshabilitar l'actualització d'anuncis en temps real" + adsTooClose: "L'interval actual pot fer que l'experiència de l'usuari sigui dolenta perquè l'interval és molt baix." +_forgotPassword: + enterEmail: "Escriu l'adreça de correu electrònic amb la que et vas registrar. S'enviarà un correu electrònic amb un enllaç perquè puguis canviar-la." + ifNoEmail: "Si no vas fer servir una adreça de correu electrònic per registrar-te, si us plau posa't en contacte amb l'administrador." + contactAdmin: "Aquesta instà ncia no suporta registrar-se amb correu electrònic. Si us plau, contacta amb l'administrador del servidor." +_gallery: + my: "La meva Galeria " + liked: "Publicacions que t'han agradat" + like: "M'agrada " + unlike: "Ja no m'agrada" _email: _follow: title: "t'ha seguit" + _receiveFollowRequest: + title: "Has rebut una sol·licitud de seguiment" +_plugin: + install: "Instal·lar un afegit " + installWarn: "Si us plau, no instal·lis afegits que no siguin de confiança." + manage: "Gestionar els afegits" + viewSource: "Veure l'origen " +_preferencesBackups: + list: "Llista de còpies de seguretat" + saveNew: "Fer una còpia de seguretat nova" + loadFile: "Carregar des d'un fitxer" + apply: "Aplicar en aquest dispositiu" + save: "Desar els canvis" + inputName: "Escriu un nom per aquesta còpia de seguretat" + cannotSave: "No s'ha pogut desar" + nameAlreadyExists: "Ja existeix una còpia de seguretat anomenada \"{name}\". Escriu un nom diferent." + applyConfirm: "Vols aplicar la còpia de seguretat \"{name}\" a aquest dispositiu? La configuració actual del dispositiu serà esborrada." + saveConfirm: "Desar còpia de seguretat com {name}?" + deleteConfirm: "Esborrar la còpia de seguretat {name}?" + renameConfirm: "Vols canvia el nom de la còpia de seguretat de \"{old}\" a \"{new}\"?" + noBackups: "No hi ha còpies de seguretat. Pots fer una còpia de seguretat de la configuració d'aquest dispositiu al servidor fent servir \"Crear nova còpia de seguretat\"" + createdAt: "Creat el: {date} {time}" + updatedAt: "Actualitzat el: {date} {time}" + cannotLoad: "Hi ha hagut un error al carregar" + invalidFile: "Format del fitxer no và lid " +_registry: + scope: "Àmbit " + key: "Clau" + keys: "Claus" + domain: "Domini" + createKey: "Crear una clau" +_aboutMisskey: + about: "Misskey és un programa de codi obert desenvolupar per syuilo des de 2014" + contributors: "Col·laboradors principals" + allContributors: "Tots els col·laboradors " + source: "Codi font" + translation: "Tradueix Misskey" + donate: "Fes un donatiu a Misskey" + morePatrons: "També agraïm el suport d'altres col·laboradors que no surten en aquesta llista. Grà cies! 🥰" + patrons: "Patrocinadors" + projectMembers: "Membres del projecte" +_displayOfSensitiveMedia: + respect: "Ocultar imatges o vÃdeos marcats com a sensibles" + ignore: "Mostrar imatges o vÃdeos marcats com a sensibles" + force: "Ocultar totes les imatges o vÃdeos " +_instanceTicker: + none: "No mostrar mai" + remote: "Mostrar per usuaris remots" + always: "Mostrar sempre" +_serverDisconnectedBehavior: + reload: "Recarregar automà ticament " + dialog: "Mostrar finestres de confirmació " + quiet: "Mostrar un avÃs que no molesti" +_channel: + create: "Crear un canal" + edit: "Editar canal" + setBanner: "Estableix el bà ner " + removeBanner: "Eliminar el.bà ner" + featured: "Popular" + owned: "Propietat" + following: "Seguin" + usersCount: "{n} Participants" + notesCount: "{n} Notes" + nameAndDescription: "Nom i descripció " + nameOnly: "Nom només " + allowRenoteToExternal: "Permet la citació i l'impuls fora del canal" +_menuDisplay: + sideFull: "Horitzontal " + sideIcon: "Horitzontal (icones)" + top: "A dalt" + hide: "Amagar" +_wordMute: + muteWords: "Paraules silenciades" + muteWordsDescription: "Separar amb espais per la condició AND o amb salts de lÃnia per la condició OR." + muteWordsDescription2: "Envolta les paraules amb barres per fer servir expressions regulars." _instanceMute: instanceMuteDescription: "Silencia tots els impulsos dels servidors seleccionats, també els usuaris que responen a altres d'un servidor silenciat." + instanceMuteDescription2: "Separar amb salts de lÃnia" + title: "Ocultar notes de les instà ncies en la llista." + heading: "Llista d'instà ncies a silenciar" _theme: + explore: "Explorar els temes " + install: "Instal·lar un tema" + manage: "Gestionar els temes " + code: "Codi del tema" description: "Descripció" + installed: "{name} Instal·lat " + installedThemes: "Temes instal·lats " + builtinThemes: "Temes integrats" + alreadyInstalled: "Aquest tema ja es troba instal·lat " + invalid: "El format d'aquest tema no és correcte" + make: "Crear un tema" + base: "Base" + addConstant: "Afegir constant " + constant: "Constant" + defaultValue: "Valor per defecte" + color: "Color" + refProp: "Referència a una propietat" + refConst: "Referència a una constant " + key: "Clau" + func: "Funcions" + funcKind: "Tipus de funció " + argument: "Argument" + basedProp: "Propietat referenciada" + alpha: "Opacitat" + darken: "Enfosquir " + lighten: "Brillantor" + inputConstantName: "Escriu un nom per aquesta constant" + importInfo: "Si escrius el codi del tema aquÃ, el podrà s importar a l'editor del tema" + deleteConstantConfirm: "Vols esborrar la constant {const}?" keys: + accent: "Accent" + bg: "Fons" + fg: "Text" + focus: "Enfocament" + indicator: "Indicador" + panel: "Taulell " + shadow: "Ombra" + header: "Capçalera" + navBg: "Fons de la barra lateral" + navFg: "Text de la barra lateral" + navHoverFg: "Text barra lateral (en passar per sobre)" + navActive: "Text barra lateral (actiu)" + navIndicator: "Indicador barra lateral" + link: "Enllaç" + hashtag: "Etiqueta" mention: "Menció" + mentionMe: "Mencions (jo)" renote: "Renotar" + modalBg: "Fons del modal" divider: "Divisor" + scrollbarHandle: "Maneta de la barra de desplaçament" + scrollbarHandleHover: "Maneta de la barra de desplaçament (en passar-hi per sobre)" + dateLabelFg: "Text de l'etiqueta de la data" + infoBg: "Fons d'informació " + infoFg: "Text d'informació " + infoWarnBg: "Fons avÃs " + infoWarnFg: "Text avÃs " + toastBg: "Fons notificació " + toastFg: "Text notificació " + buttonBg: "Fons botó " + buttonHoverBg: "Fons botó (en passar-hi per sobre)" + inputBorder: "Contorn del cap d'introducció " + listItemHoverBg: "Fons dels elements d'una llista" + driveFolderBg: "Fons de la carpeta Disc" + wallpaperOverlay: "Superposició del fons de pantalla " + badge: "InsÃgnia " + messageBg: "Fons del xat" + accentDarken: "Accent (fosc)" + accentLighten: "Accent (clar)" + fgHighlighted: "Text ressaltat" _sfx: note: "Notes" + noteMy: "Nota (per mi)" notification: "Notificacions" antenna: "Antenes" + channel: "Notificacions dels canals" + reaction: "Quan se selecciona una reacció " +_soundSettings: + driveFile: "Fer servir un fitxer d'à udio del disc" + driveFileWarn: "Seleccionar un fitxer d'à udio del disc" + driveFileTypeWarn: "Fitxer no suportat " + driveFileTypeWarnDescription: "Seleccionar un fitxer d'à udio " + driveFileDurationWarn: "L'à udio és massa llarg" + driveFileDurationWarnDescription: "Els à udios molt llargs pot interrompre l'ús de Misskey. Vols continuar?" +_ago: + future: "Futur " + justNow: "Ara mateix" + secondsAgo: "Fa {n} segons" + minutesAgo: "Fa {n} minuts" + hoursAgo: "Fa {n} hores" + daysAgo: "Fa {n} dies" + weeksAgo: "Fa {n} setmanes" + monthsAgo: "Fa {n} mesos" + yearsAgo: "Fa {n} anys" + invalid: "Res" +_timeIn: + seconds: "En {n} segons" + minutes: "En {n} minuts" + hours: "En {n} hores" + days: "En {n} dies" + weeks: "En {n} setmanes" + months: "En {n} mesos" + years: "En {n} anys" +_time: + second: "Segon(s)" + minute: "Minut(s)" + hour: "Hor(a)(es)" + day: "Di(a)(es)" _2fa: + alreadyRegistered: "J has registrat un dispositiu d'autenticació de doble factor." + registerTOTP: "Registrar una aplicació autenticadora" + step1: "Primer instal·la una aplicació autenticadora (com {a} o {b}) al teu dispositiu." + step2: "Després escaneja el codi QR que es mostra en aquesta pantalla." + step2Click: "Fent clic en aquest codi QR et permetrà registrar l'autenticació de doble factor a la teva clau de seguretat o en l'aplicació d'autenticació del teu dispositiu." + step2Uri: "Escriu la següent URI si està s fent servir una aplicació d'escriptori " + step3Title: "Escriu un codi d'autenticació" + step3: "Escriu el codi d'autenticació (token) que es mostra a la teva aplicació per finalitzar la configuració." + setupCompleted: "Configuració terminada" + step4: "D'ara endavant quan accedeixis se't demanarà el token que has introduït." + securityKeyNotSupported: "El teu navegador no suporta claus de seguretat" + registerTOTPBeforeKey: "Configura una aplicació d'autenticació per registrar una clau de seguretat o una clau de pas." + securityKeyInfo: "A més de l'empremta digital o PIN per autenticar-te, pots configurar autenticació mitjançant maquinari que suporti claus de seguretat FIDO2, per protegir encara més el teu compte." + registerSecurityKey: "Registrar una clau de seguretat o clau de pas" + securityKeyName: "Escriu un nom per la clau" + tapSecurityKey: "Seguiu les instruccions del navegador i registrar les claus de seguretat o la clau de pas" + removeKey: "Esborrar la clau de seguretat" + removeKeyConfirm: "Esborrar la còpia de seguretat {name}?" + whyTOTPOnlyRenew: "L'aplicació d'autenticació no es pot eliminar mentre hi hagi una clau de seguretat registrada." + renewTOTP: "Reconfigurar l'aplicació d'autenticació " + renewTOTPConfirm: "Això farà que els codis de validació de l'antiga aplicació deixin de funcionar" + renewTOTPOk: "Reconfigurar" renewTOTPCancel: "No, grà cies" + checkBackupCodesBeforeCloseThisWizard: "Abans de tancar aquesta finestra, comprova el següent codi de seguretat." + backupCodes: "Codi de seguretat." + backupCodesDescription: "Si l'aplicació d'autenticació no es pot utilitzar, es pot accedir al compte utilitzant els següents codis de còpia de seguretat. Assegura't de mantenir aquests codis en un lloc segur. Cada codi es pot utilitzar només una vegada." + backupCodeUsedWarning: "Es va utilitzar un codi de còpia de seguretat. Si l'aplicació de certificació està disponible, reconfigura l'aplicació d'autenticació tan aviat com sigui possible." + backupCodesExhaustedWarning: "Es van utilitzar tots els codis de còpia de seguretat. Si no es pot utilitzar l'aplicació d'autenticació, ja no es pot accedir al compte. Torna a registrar l'aplicació d'autenticació." +_permissions: + "read:account": "Veure la informació del compte." + "write:account": "Editar la informació del compte." + "read:blocks": "Veure la llista d'usuaris bloquejats" + "write:blocks": "Editar la llista d'usuaris blocats" + "read:drive": "Accedeix als teus fitxers i carpetes del Disc" + "write:drive": "Editar o eliminar els teus fitxers i carpetes al Disc" + "read:favorites": "Veure la teva llista de favorits" + "write:favorites": "Editar la teva llista de favorits" + "read:following": "Veure informació de qui segueixes" + "write:following": "Segueix o deixa de seguir altres comptes" + "read:messaging": "Veure els teus xats" + "write:messaging": "Crear o esborrar missatges de xat" + "read:mutes": "Veure la teva llista d'usuaris silenciats" + "write:mutes": "Editar la teva llista d'usuaris silenciats" + "write:notes": "Crear o esborrar notes" + "read:notifications": "Veure les teves notificacions" + "write:notifications": "Gestionar les teves notificacions" + "read:reactions": "Veure les teves reaccions" + "write:reactions": "Editar les teves reaccions" + "write:votes": "Votar en una enquesta" + "read:pages": "Veure les teves pà gines " + "write:pages": "Editar o esborrar les teves pà gines " + "read:page-likes": "Veure la llista de les pà gines que t'han agradat" + "write:page-likes": "Editar la llista de les pà gines que t'han agradat" + "read:user-groups": "Veure els teus grups d'usuaris " + "write:user-groups": "Editar o esborrar els teus grups d'usuaris " + "read:channels": "Veure els teus canals" + "write:channels": "Editar els teus canals" + "read:gallery": "Veure la teva galeria " + "write:gallery": "Editar la teva galeria" + "read:gallery-likes": "Veure la llista de publicacions de galeries que t'han agradat" + "write:gallery-likes": "Editar la llista de publicacions de galeries que t'han agradat" + "read:flash": "Veure reproduccions" + "write:flash": "Editar reproduccions" + "read:flash-likes": "Veure la llista de reproduccions que t'han agradat" + "write:flash-likes": "Editar la llista de reproduccions que t'han agradat" + "read:admin:abuse-user-reports": "Veure informes d'usuaris " + "write:admin:delete-account": "Esborrar compte d'usuari " + "write:admin:delete-all-files-of-a-user": "Esborrar tots els fitxers d'un usuari" + "read:admin:index-stats": "Veure l'Ãndex de la base de dades" + "read:admin:table-stats": "Veure la informació de les taules a la base de dades" + "read:admin:user-ips": "Veure adreça IP de l'usuari " + "read:admin:meta": "Veure meta-informació del servidor" + "write:admin:reset-password": "Reiniciar contrasenya d'usuari " + "write:admin:resolve-abuse-user-report": "Resoldre informes d'usuaris " + "write:admin:send-email": "Enviar correu electrònic " + "read:admin:server-info": "Veure informació del servidor" + "read:admin:show-moderation-log": "Veure registre de moderació " + "read:admin:show-user": "Veure informació privada de l'usuari " + "read:admin:show-users": "Veure informació privada de l'usuari " + "write:admin:suspend-user": "Suspendre usuari" + "write:admin:unset-user-avatar": "Esborrar avatar d'usuari " + "write:admin:unset-user-banner": "Esborrar bà ner de l'usuari " + "write:admin:unsuspend-user": "Treure la suspensió d'un usuari" + "write:admin:meta": "Gestionar les metadades de la instà ncia" + "write:admin:user-note": "Gestionar les notes de moderació " + "write:admin:roles": "Gestionar rols" + "read:admin:roles": "Veure rols" + "write:admin:relays": "Gestionar relé" + "read:admin:relays": "Veure relés" + "write:admin:invite-codes": "Gestionar codis d'invitació " + "read:admin:invite-codes": "Veure codis d'invitació " + "write:admin:announcements": "Gestionar anuncis" + "read:admin:announcements": "Veure anuncis" + "write:admin:avatar-decorations": "Gestionar la decoració dels avatars" + "read:admin:avatar-decorations": "Veure les decoracions dels avatars" + "write:admin:federation": "Gestionar la federació d'instà ncies " + "write:admin:account": "Gestionar els comptes d'usuaris " + "read:admin:account": "Veure els comptes d'usuaris " + "write:admin:emoji": "Edició d'emojis" + "read:admin:emoji": "Veure emojis" + "write:admin:queue": "Gestionar la cua de feines" + "read:admin:queue": "Veure la cua de feines" _antennaSources: all: "Totes les publicacions" homeTimeline: "Publicacions dels usuaris seguits" @@ -580,18 +2025,76 @@ _widgets: timeline: "LÃnia de temps" activity: "Activitat" federation: "Federació" + button: "Botó " jobQueue: "Cua de tasques" _userList: chooseList: "Tria una llista" _cw: + hide: "Amagar" show: "Carregar més" + chars: "{count} carà cters " + files: "{count} fitxer(s)" +_poll: + noOnlyOneChoice: "Es necessita escollir dues opcions com a mÃnim " + choiceN: "Opció {n}" + noMore: "No pots afegir més opcions" + canMultipleVote: "Permetre escollir diferents opcions" + expiration: "Finalitza el" + infinite: "Mai" + at: "Finalitza en..." + after: "Finalitza després..." + deadlineDate: "Data de finalització " + deadlineTime: "Hor(a)(es)" + duration: "Duració " + votesCount: "{n} vots" + totalVotes: "{n} vots en total" + vote: "Votar en una enquesta" + showResult: "Veure resultats" + voted: "Has votat" + closed: "Finalitzada" + remainingDays: "Queden {d} dies i {h} hores per finalitzar" + remainingHours: "Queden {h} hores i {m} minuts" + remainingMinutes: "Queden {m} minuts i {s} segons" + remainingSeconds: "Queden {s} segons" _visibility: + public: "Públic " + publicDescription: "La teva nota la podrà veure tothom " home: "Inici" + homeDescription: "Publicar només a la lÃnia de temps d'Inici " followers: "Seguidors" + followersDescription: "Fes només visible per als teus seguidors" + specified: "Directe" + specifiedDescription: "Fer visible només per alguns usuaris" + disableFederation: "Sense federar" + disableFederationDescription: "No enviar a altres servidors" +_postForm: + replyPlaceholder: "Contestar..." + quotePlaceholder: "Citar..." + channelPlaceholder: "Publicar a un canal..." + _placeholders: + a: "Que vols dir?..." + b: "Alguna cosa interessant al teu voltant?..." + c: "Què et passa pel cap?..." + d: "Què vols dir?..." + e: "Escriu alguna cosa..." + f: "Esperant que escriguis qualsevol cosa..." _profile: + name: "Nom" username: "Nom d'usuari" + description: "Biografia " + youCanIncludeHashtags: "Pots posar etiquetes a la teva biografia " + metadata: "Informació adicional " + metadataEdit: "Editar la informació adicional " + metadataDescription: "Amb això podrà s mostrar camps d'informació adicional al teu perfil." + metadataLabel: "Etiqueta " + metadataContent: "Contingut" + changeAvatar: "Canviar l'avatar " + changeBanner: "Canviar el bà ner " + verifiedLinkDescription: "Escrivint una adreça URL que enllaci a aquest perfil, una icona de propietat verificada es mostrarà al costat del camp." + avatarDecorationMax: "Pot afegir un mà xim de {max} decoracions." _exportOrImport: allNotes: "Totes les publicacions" + clips: "Retalls" followingList: "Seguint" muteList: "Silencia" blockingList: "Bloqueja" @@ -604,18 +2107,74 @@ _timelines: social: "Social" global: "Global" _play: + viewSource: "Veure l'origen " + featured: "Popular" + title: "TÃtol " + script: "Script" summary: "Descripció" _pages: + viewSource: "Veure l'origen " + viewPage: "Veure les teves pà gines " + like: "M'agrada " + unlike: "Treure m'agrada " + my: "Les meves pà gines " + liked: "Pà gines que m'agraden " + featured: "Popular" + inspector: "Inspeccionar" contents: "Contingut" + content: "Bloquejar la pà gina " + variables: "Variables" + title: "TÃtol " + url: "URL de la pà gina " + summary: "Resum de la pà gina " + alignCenter: "Centrar elements" + hideTitleWhenPinned: "Amagar el tÃtol de la pà gina quan estigui fixada al perfil" + font: "Lletra tipogrà fica" + fontSerif: "Serif" + fontSansSerif: "Sans Serif" + eyeCatchingImageSet: "Escull una miniatura" + eyeCatchingImageRemove: "Esborrar la miniatura" + chooseBlock: "Afegeix un bloc" + selectType: "Seleccionar tipus" + contentBlocks: "Contingut" + inputBlocks: "Entrada " + specialBlocks: "Especial" blocks: + text: "Text" + textarea: "Àrea de text" + section: "Secció " image: "Imatges" + button: "Botó " + note: "Incorporar una Nota" _note: id: "ID de la publicació" + idDescription: "Alternativament pots enganxar l'adreça URL de la nota aquÃ." detailed: "Mostra els detalls" +_relayStatus: + requesting: "Pendent" + accepted: "Acceptat" + rejected: "Rebutjat" _notification: + fileUploaded: "Fitxer pujat sense cap problema" + youGotMention: "{name} t'ha mencionat" + youGotReply: "{name} t'ha contestat" + youGotQuote: "{name} t'ha citat" youRenoted: "Impulsat per {name}" youWereFollowed: "t'ha seguit" + youReceivedFollowRequest: "Has rebut una petició de seguiment" + yourFollowRequestAccepted: "La teva petició de seguiment ha sigut acceptada" + pollEnded: "Ja pots veure els resultats de l'enquesta " + newNote: "Nota nova" unreadAntennaNote: "Antena {name}" + roleAssigned: "Rol assignat " + emptyPushNotificationMessage: "Les notificacions han sigut actualitzades" + achievementEarned: "Aconseguiment desblocat" + testNotification: "Notificació de prova" + checkNotificationBehavior: "Comprova el comportament de la notificació " + sendTestNotification: "Enviar notificació de prova" + notificationWillBeDisplayedLikeThis: "Les notificacions és veure'n aixà " + reactedBySomeUsers: "Han reaccionat {n} usuaris" + renotedBySomeUsers: "L'han impulsat {n} usuaris" _types: all: "Tots" follow: "Seguint" @@ -645,8 +2204,55 @@ _deck: tl: "LÃnia de temps" antenna: "Antena" list: "Llistes" + channel: "Canals" mentions: "Mencions" direct: "Publicacions directes" +_webhookSettings: + name: "Nom" + active: "Activat" _moderationLogTypes: suspend: "Suspèn" resetPassword: "Restableix la contrasenya" + suspendRemoteInstance: "Servidor remot suspès " + unsuspendRemoteInstance: "S'ha tret la suspensió del servidor remot" + markSensitiveDriveFile: "Fitxer marcat com a sensible" + unmarkSensitiveDriveFile: "S'ha tret la marca de sensible del fitxer" + resolveAbuseReport: "Informe resolt" + createInvitation: "Crear codi d'invitació " + createAd: "Anunci creat" + deleteAd: "Anunci esborrat" + updateAd: "Anunci actualitzat" + createAvatarDecoration: "Decoració de l'avatar creada" + updateAvatarDecoration: "S'ha actualitzat la decoració de l'avatar " + deleteAvatarDecoration: "S'ha esborrat la decoració de l'avatar " + unsetUserAvatar: "Esborrar l'avatar d'aquest usuari" + unsetUserBanner: "Esborrar el bà ner d'aquest usuari" +_fileViewer: + title: "Detall del fitxer" + type: "Tipus de fitxer" + size: "Mida" + url: "URL" + uploadedAt: "Pujat el" + attachedNotes: "Notes amb aquest fitxer" + thisPageCanBeSeenFromTheAuthor: "Aquesta pà gina només la pot veure l'usuari que ha pujat aquest fitxer." +_externalResourceInstaller: + title: "Instal·lar des d'un lloc extern" + checkVendorBeforeInstall: "Assegura't que qui distribueix aquest recurs és fiable abans d'instal·lar-ho." + _plugin: + title: "Vols instal·lar aquest afegit?" + metaTitle: "Informació de l'afegit " + _theme: + title: "Vols instal·lar aquest tema?" + metaTitle: "Informació del tema" + _meta: + base: "Paleta de colors base" + _vendorInfo: + title: "Informació del distribuïdor " + endpoint: "Punt final referenciat" + hashVerify: "Verificació d'integritat " + _errors: + _invalidParams: + title: "Parà metres no và lids " +_reversi: + total: "Total" + diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index afe613677f0018e57b7d603e3962e2d006163c41..6dad336b7f419174b1d9ed6158919513ff5ab3d0 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -366,6 +366,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Aktivovat hCaptchu" hcaptchaSiteKey: "KlÃÄ stránky" hcaptchaSecretKey: "Tajný KlÃÄ (Secret Key)" +mcaptchaSiteKey: "KlÃÄ stránky" +mcaptchaSecretKey: "Tajný KlÃÄ (Secret Key)" recaptcha: "reCAPTCHA" enableRecaptcha: "Zapnout ReCAPTCHu" recaptchaSiteKey: "KlÃÄ stránky" @@ -1003,6 +1005,7 @@ resetPasswordConfirm: "Opravdu chcete resetovat heslo?" sensitiveWords: "Citlivá slova" sensitiveWordsDescription: "Viditelnost vÅ¡ech poznámek obsahujÃcÃch nÄ›které z nakonfigurovaných slov bude automaticky nastavena na \"Domů\". Můžete jich uvést vÃce tak, že je oddÄ›lÃte pomocà řádků." sensitiveWordsDescription2: "Použità mezer vytvořà výrazy AND a obklopenà klÃÄových slov lomÃtky je zmÄ›nà na regulárnà výraz." +prohibitedWordsDescription2: "Použità mezer vytvořà výrazy AND a obklopenà klÃÄových slov lomÃtky je zmÄ›nà na regulárnà výraz." notesSearchNotAvailable: "Vyhledávánà poznámek je nedostupné." license: "Licence" unfavoriteConfirm: "Opravdu chcete odstranit z oblÃbených?" @@ -1092,7 +1095,10 @@ iHaveReadXCarefullyAndAgree: "PÅ™eÄetl jsem si text \"{x}\" a souhlasÃm s nÃm icon: "Avatar" replies: "OdpovÄ›di" renotes: "PÅ™eposlat" +sourceCode: "Zdrojový kód" flip: "OtoÄit" +lastNDays: "PoslednÃch {n} dnů" +surrender: "ZruÅ¡it" _initialAccountSetting: accountCreated: "Váš úÄet byl úspěšnÄ› vytvoÅ™en!" letsStartAccountSetup: "Pro zaÄátek si nastavte svůj profil." @@ -1825,6 +1831,7 @@ _profile: _exportOrImport: allNotes: "VÅ¡echny poznámky" favoritedNotes: "OblÃbené poznámky" + clips: "OÅ™Ãznout" followingList: "SledovanÃ" muteList: "Ztlumit" blockingList: "Zablokovat" @@ -2016,3 +2023,6 @@ _moderationLogTypes: suspend: "Zmrazit" resetPassword: "Resetovat heslo" createInvitation: "Vygenerovat pozvánku" +_reversi: + total: "Celkem" + diff --git a/locales/da-DK.yml b/locales/da-DK.yml index 08c15ed092fc217aec263728d823d1f62a0fb88f..d1fbec9f6791cea281989c9cb2a38ef4c6c44588 100644 --- a/locales/da-DK.yml +++ b/locales/da-DK.yml @@ -1,2 +1,3 @@ --- _lang_: "Dansk" + diff --git a/locales/de-DE.yml b/locales/de-DE.yml index 83b254b2d58211f66316e98c7006ec2103734d96..7cb451e233f78a800439c70317766cb832ceec0b 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -121,9 +121,15 @@ sensitive: "Sensibel" add: "Hinzufügen" reaction: "Reaktionen" reactions: "Reaktionen" +emojiPicker: "Emoji auswählen" +pinnedEmojisForReactionSettingDescription: "Lege Emojis fest, die angepinnt werden sollen, um sie beim Reagieren als Erstes anzuzeigen." +pinnedEmojisSettingDescription: "Lege Emojis fest, die angepinnt werden sollen, um sie in der Emoji-Auswahl als Erstes anzuzeigen" +overwriteFromPinnedEmojisForReaction: "Ãœberschreiben mit den Reaktions-Einstellungen" +overwriteFromPinnedEmojis: "Ãœberschreiben mit den allgemeinen Einstellungen" reactionSettingDescription2: "Ziehe um Anzuordnen, klicke um zu löschen, drücke „+“ um hinzuzufügen" rememberNoteVisibility: "Notizsichtbarkeit merken" attachCancel: "Anhang entfernen" +deleteFile: "Datei gelöscht" markAsSensitive: "Als sensibel markieren" unmarkAsSensitive: "Als nicht sensibel markieren" enterFileName: "Dateinamen eingeben" @@ -178,7 +184,7 @@ searchWith: "Suchen: {q}" youHaveNoLists: "Du hast keine Listen" followConfirm: "Möchtest du {name} wirklich folgen?" proxyAccount: "Proxy-Benutzerkonto" -proxyAccountDescription: "Ein Proxy-Benutzerkonto ist ein Benutzerkonto, das sich für Nutzer unter bestimmten Konditionen wie ein Follower aus einer fremden Instanz verhält. Zum Beispiel wird die Aktivität eines Nutzers aus einer fremden Instanz nicht an diese Instanz übermittelt, falls es keinen Benutzer dieser Instanz gibt, der diesem Nutzer aus fremder Instanz folgt. In diesem Fall folgt stattdessen das Proxy-Benutzerkonto." +proxyAccountDescription: "Ein Proxy-Konto ist ein Benutzerkonto, das unter bestimmten Bedingungen als Follower für Benutzer fremder Instanzen fungiert. Wenn zum Beispiel ein Benutzer einen Benutzer einer fremden Instanz zu einer Liste hinzufügt, werden die Aktivitäten des entfernten Benutzers nicht an die Instanz übermittelt, wenn kein lokaler Benutzer diesem Benutzer folgt; stattdessen folgt das Proxy-Konto." host: "Hostname" selectUser: "Benutzer auswählen" recipient: "Empfänger" @@ -260,6 +266,7 @@ removed: "Erfolgreich gelöscht" removeAreYouSure: "Möchtest du „{x}“ wirklich entfernen?" deleteAreYouSure: "Möchtest du „{x}“ wirklich löschen?" resetAreYouSure: "Wirklich zurücksetzen?" +areYouSure: "Bist du sicher?" saved: "Erfolgreich gespeichert" messaging: "Chat" upload: "Hochladen" @@ -354,7 +361,7 @@ enableLocalTimeline: "Lokale Chronik aktivieren" enableGlobalTimeline: "Globale Chronik aktivieren" disablingTimelinesInfo: "Administratoren und Moderatoren haben immer Zugriff auf alle Chroniken, auch wenn diese deaktiviert sind." registration: "Registrieren" -enableRegistration: "Registration neuer Benutzer erlauben" +enableRegistration: "Registrierung neuer Benutzer erlauben" invite: "Einladen" driveCapacityPerLocalAccount: "Drive-Kapazität pro lokalem Benutzerkonto" driveCapacityPerRemoteAccount: "Drive-Kapazität pro Benutzer fremder Instanzen" @@ -372,6 +379,11 @@ hcaptcha: "hCaptcha" enableHcaptcha: "hCaptcha aktivieren" hcaptchaSiteKey: "Site key" hcaptchaSecretKey: "Secret key" +mcaptcha: "mCaptcha" +enableMcaptcha: "mCaptcha aktivieren" +mcaptchaSiteKey: "Site key" +mcaptchaSecretKey: "Secret key" +mcaptchaInstanceUrl: "mCaptcha Instanz-URL" recaptcha: "reCAPTCHA" enableRecaptcha: "reCAPTCHA aktivieren" recaptchaSiteKey: "Site key" @@ -429,7 +441,7 @@ lastUsed: "Zuletzt benutzt" lastUsedAt: "Zuletzt verwendet: {t}" unregister: "Deaktivieren" passwordLessLogin: "Passwortloses Anmelden" -passwordLessLoginDescription: "Ermöglicht passwortfreies Einloggen, nur via Security-Token oder Passkey" +passwordLessLoginDescription: "Ermöglicht passwortloses Einloggen mit einem Security-Token oder Passkey" resetPassword: "Passwort zurücksetzen" newPasswordIs: "Das neue Passwort ist „{password}“" reduceUiAnimation: "Animationen der Benutzeroberfläche reduzieren" @@ -619,6 +631,7 @@ medium: "Mittel" small: "Klein" generateAccessToken: "Zugriffstoken generieren" permission: "Berechtigungen" +adminPermission: "Administratorberechtigung" enableAll: "Alle aktivieren" disableAll: "Alle deaktivieren" tokenRequested: "Zugriff zum Benutzerkonto gewähren" @@ -973,6 +986,7 @@ neverShow: "Nicht wieder anzeigen" remindMeLater: "Vielleicht später" didYouLikeMisskey: "Gefällt dir Sharkey?" pleaseDonate: "Sharkey ist die kostenlose Software, die von {host} verwendet wird. Wir würden uns über Spenden freuen, damit dessen Entwicklung weitergeführt werden kann!" +pleaseDonateInstance: "Du kannst {host} auch direkt unterstützen, indem du an deine Instanz Administration spendest." roles: "Rollen" role: "Rolle" noRole: "Rolle nicht gefunden" @@ -1023,6 +1037,7 @@ resetPasswordConfirm: "Wirklich Passwort zurücksetzen?" sensitiveWords: "Sensible Wörter" sensitiveWordsDescription: "Die Notizsichtbarkeit aller Notizen, die diese Wörter enthalten, wird automatisch auf \"Startseite\" gesetzt. Durch Zeilenumbrüche können mehrere konfiguriert werden." sensitiveWordsDescription2: "Durch die Verwendung von Leerzeichen können AND-Verknüpfungen angegeben werden und durch das Umgeben von Schrägstrichen können reguläre Ausdrücke verwendet werden." +prohibitedWordsDescription2: "Durch die Verwendung von Leerzeichen können AND-Verknüpfungen angegeben werden und durch das Umgeben von Schrägstrichen können reguläre Ausdrücke verwendet werden." hiddenTags: "Ausgeblendete Hashtags" hiddenTagsDescription: "Die hier eingestellten Tags werden nicht mehr in den Trends angezeigt. Mit der Umschalttaste können mehrere ausgewählt werden." notesSearchNotAvailable: "Die Notizsuche ist nicht verfügbar." @@ -1144,12 +1159,15 @@ hideRepliesToOthersInTimelineAll: "Antworten von allen momentan gefolgten Benutz confirmShowRepliesAll: "Dies ist eine unwiderrufliche Aktion. Wirklich Antworten von allen momentan gefolgten Benutzern in der Chronik anzeigen?" confirmHideRepliesAll: "Dies ist eine unwiderrufliche Aktion. Wirklich Antworten von allen momentan gefolgten Benutzern nicht in der Chronik anzeigen?" externalServices: "Externe Dienste" +sourceCode: "Quellcode" impressum: "Impressum" impressumUrl: "Impressums-URL" impressumDescription: "In manchen Ländern, wie Deutschland und dessen Umgebung, ist die Angabe von Betreiberinformationen (ein Impressum) bei kommerziellem Betrieb zwingend." privacyPolicy: "Datenschutzerklärung" privacyPolicyUrl: "Datenschutzerklärungs-URL" tosAndPrivacyPolicy: "Nutzungsbedingungen und Datenschutzerklärung" +donation: "Spenden" +donationUrl: "Spenden-URL" avatarDecorations: "Profilbilddekoration" attach: "Anbringen" detach: "Entfernen" @@ -1165,6 +1183,11 @@ signupPendingError: "Beim Ãœberprüfen der Mailadresse ist etwas schiefgelaufen. cwNotationRequired: "Ist \"Inhaltswarnung verwenden\" aktiviert, muss eine Beschreibung gegeben werden." doReaction: "Reagieren" code: "Code" +decorate: "Dekorieren" +addMfmFunction: "MFM hinzufügen" +sfx: "Soundeffekte" +lastNDays: "Letzten {n} Tage" +surrender: "Abbrechen" _announcement: forExistingUsers: "Nur für existierende Nutzer" forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt." @@ -1174,6 +1197,7 @@ _announcement: tooManyActiveAnnouncementDescription: "Zu viele aktive Ankündigungen können die Benutzerfreundlichkeit verschlechtern. Es wird empfohlen, veraltete Ankündigungen zu archivieren." readConfirmTitle: "Als gelesen markieren?" readConfirmText: "Dies markiert den Inhalt von \"{title}\" als gelesen." + shouldNotBeUsedToPresentPermanentInfo: "Es wird empfohlen, Ankündigungen für aktuelle und zeitlich begrenzte Neuigkeiten zu nutzen, statt für Informationen, die langfristig relevant sind." dialogAnnouncementUxWarn: "Bei der Verwendung von mehr als zwei Meldungen im Dialog-Format wird um Vorsicht geboten, da dies negative Auswirkungen auf die UX haben kann." silence: "Keine Benachrichtigung" silenceDescription: "Wenn aktiviert, gibt diese Meldung keine Nachricht aus und muss nicht als \"gelesen\" markiert werden." @@ -1203,6 +1227,24 @@ _initialTutorial: description: "Hier kannst du sehen, wie Misskey funktioniert" _note: title: "Was sind Notizen?" + description: "Beiträge auf Misskey heißen \"Notizen\". Notizen werden chronologisch in der Chronik angeordnet und in Echtzeit aktualisiert." + reply: "Klicke auf diesen Button, um auf eine Nachricht zu antworten. Es ist auch möglich, auf Antworten zu antworten und die Unterhaltung wie einen Thread fortzusetzen." + _reaction: + title: "Was sind Reaktionen?" + reactToContinue: "Füge eine Reaktion hinzu, um fortzufahren." + reactNotification: "Du erhältst Echtzeit-Benachrichtigungen, wenn jemand auf deine Notiz reagiert." + _postNote: + _visibility: + description: "Du kannst einschränken, wer deine Notiz sehen kann." + public: "Deine Notiz wird für alle Nutzer sichtbar sein." + doNotSendConfidencialOnDirect1: "Sei vorsichtig, wenn du sensible Informationen verschickst!" + _cw: + title: "Inhaltswarnung" + _done: + title: "Du hast das Tutorial abgeschlossen! 🎉" +_timelineDescription: + local: "In der lokalen Chronik siehst du Notizen von allen Benutzern auf diesem Server." + global: "In der globalen Chronik siehst du Notizen von allen föderierten Servern." _serverRules: description: "Eine Reihe von Regeln, die vor der Registrierung angezeigt werden. Eine Zusammenfassung der Nutzungsbedingungen anzuzeigen ist empfohlen." _serverSettings: @@ -1215,6 +1257,8 @@ _serverSettings: shortName: "Abkürzung" shortNameDescription: "Ein Kürzel für den Namen der Instanz, der angezeigt werden kann, falls der volle Instanzname lang ist." fanoutTimelineDescription: "Ist diese Option aktiviert, kann eine erhebliche Verbesserung im Abrufen von Chroniken und eine Reduzierung der Datenbankbelastung erzielt werden, im Gegenzug zu einer Steigerung in der Speichernutzung von Redis. Bei geringem Serverspeicher oder Serverinstabilität kann diese Option deaktiviert werden." + fanoutTimelineDbFallback: "Auf die Datenbank zurückfallen" + fanoutTimelineDbFallbackDescription: "Ist diese Option aktiviert, wird die Chronik auf zusätzliche Abfragen in der Datenbank zurückgreifen, wenn sich die Chronik nicht im Cache befindet. Eine Deaktivierung führt zu geringerer Serverlast, aber schränkt den Zeitraum der abrufbaren Chronik ein. " _accountMigration: moveFrom: "Von einem anderen Konto zu diesem migrieren" moveFromSub: "Alias für ein anderes Konto erstellen" @@ -1472,6 +1516,8 @@ _achievements: _smashTestNotificationButton: title: "Testüberfluss" description: "Betätige den Benachrichtigungstest mehrfach innerhalb einer extrem kurzen Zeitspanne" + _tutorialCompleted: + description: "Tutorial abgeschlossen" _role: new: "Rolle erstellen" edit: "Rolle bearbeiten" @@ -1482,7 +1528,9 @@ _role: assignTarget: "Zuweisungsart" descriptionOfAssignTarget: "<b>Manuell</b> bedeutet, dass die Liste der Benutzer einer Rolle manuell verwaltet wird.\n<b>Konditional</b> bedeutet, dass die Liste der Benutzer einer Rolle durch eine Bedingung automatisch verwaltet wird." manual: "Manuell" + manualRoles: "Manuelle Rollen" conditional: "Konditional" + conditionalRoles: "Bedingte Rolle" condition: "Bedingung" isConditionalRole: "Dies ist eine konditionale Rolle." isPublic: "Öffentliche Rolle" @@ -1524,13 +1572,14 @@ _role: webhookMax: "Maximale Anzahl an Webhooks" clipMax: "Maximale Anzahl an Clips" noteEachClipsMax: "Maximale Anzahl an Notizen innerhalb eines Clips" - userListMax: "Maximale Anzahl an Benutzern in einer Benutzerliste" - userEachUserListsMax: "Maximale Anzahl an Benutzerlisten" + userListMax: "Maximale Anzahl an Benutzerlisten" + userEachUserListsMax: "Maximale Anzahl an Benutzern in einer Benutzerliste" rateLimitFactor: "Versuchsanzahl" descriptionOfRateLimitFactor: "Je niedriger desto weniger restriktiv, je höher destro restriktiver." canHideAds: "Kann Werbung ausblenden" canSearchNotes: "Nutzung der Notizsuchfunktion" canUseTranslator: "Verwendung des Ãœbersetzers" + avatarDecorationLimit: "Maximale Anzahl an Profilbilddekorationen, die angebracht werden können" _condition: isLocal: "Lokaler Benutzer" isRemote: "Benutzer fremder Instanz" @@ -1559,6 +1608,7 @@ _emailUnavailable: disposable: "Wegwerf-Email-Adressen können nicht verwendet werden" mx: "Dieser Email-Server ist ungültig" smtp: "Dieser Email-Server antwortet nicht" + banned: "Du kannst dich mit dieser E-Mail-Adresse nicht registrieren" _ffVisibility: public: "Öffentlich" followers: "Nur für Follower sichtbar" @@ -1887,6 +1937,7 @@ _widgets: _userList: chooseList: "Liste auswählen" clicker: "Klickzähler" + birthdayFollowings: "Nutzer, die heute Geburtstag haben" _cw: hide: "Inhalt verbergen" show: "Inhalt anzeigen" @@ -1952,6 +2003,7 @@ _profile: _exportOrImport: allNotes: "Alle Notizen" favoritedNotes: "Als Favorit markierte Notizen" + clips: "Clip erstellen" followingList: "Gefolgte Benutzer" muteList: "Stummschaltungen" blockingList: "Blockierungen" @@ -2234,3 +2286,10 @@ _externalResourceInstaller: _themeInstallFailed: title: "Das Farbschema konnte nicht installiert werden" description: "Während der Installation des Farbschemas ist ein Problem aufgetreten. Bitte versuche es erneut. Detaillierte Fehlerinformationen können über die Javascript-Konsole abgerufen werden." +_reversi: + blackOrWhite: "Schwarz/Weiß" + rules: "Regeln" + black: "Schwarz" + white: "Weiß" + total: "Gesamt" + diff --git a/locales/el-GR.yml b/locales/el-GR.yml index 30a52b726e87cfe817904c7954a6b5e537cf2551..bb5639a74127b3b30fc12938a596473a64248be5 100644 --- a/locales/el-GR.yml +++ b/locales/el-GR.yml @@ -356,6 +356,7 @@ _profile: username: "Όνομα μÎλους" _exportOrImport: allNotes: "Όλα τα σημειώματα" + clips: "Κλιπ" followingList: "Ακολουθεί" muteList: "ÎœÎλη σε σίγαση" blockingList: "ΜπλοκαÏισμÎνα μÎλη" @@ -395,3 +396,6 @@ _webhookSettings: name: "Όνομα" _moderationLogTypes: suspend: "Αποβολή" +_reversi: + total: "ΣÏνολο" + diff --git a/locales/en-US.yml b/locales/en-US.yml index 64f5d568eb4d37934ce8ae2bd0088dad2c518672..a1abba47e605ceb28468f0412f373f0700cb408c 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -127,13 +127,14 @@ reaction: "Reactions" reactions: "Reactions" emojiPicker: "Emoji picker" pinnedEmojisForReactionSettingDescription: "Set the emojis which should be pinned and displayed immediately when reacting." -pinnedEmojisSettingDescription: "Set the emojis to be pinned and displayed when entering emojis" +pinnedEmojisSettingDescription: "Set the emojis to be pinned and displayed when viewing emoji picker" emojiPickerDisplay: "Emoji picker display" overwriteFromPinnedEmojisForReaction: "Override from reaction settings" overwriteFromPinnedEmojis: "Override from general settings" reactionSettingDescription2: "Drag to reorder, click to delete, press \"+\" to add." rememberNoteVisibility: "Remember note visibility settings" attachCancel: "Remove attachment" +deleteFile: "File deleted" markAsSensitive: "Mark as sensitive" unmarkAsSensitive: "Unmark as sensitive" enterFileName: "Enter filename" @@ -390,6 +391,11 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Enable hCaptcha" hcaptchaSiteKey: "Site key" hcaptchaSecretKey: "Secret key" +mcaptcha: "mCaptcha" +enableMcaptcha: "Enable mCaptcha" +mcaptchaSiteKey: "Site key" +mcaptchaSecretKey: "Secret key" +mcaptchaInstanceUrl: "mCaptcha instance URL" recaptcha: "reCAPTCHA" enableRecaptcha: "Enable reCAPTCHA" recaptchaSiteKey: "Site key" @@ -518,6 +524,7 @@ mediaListWithOneImageAppearance: "Height of media lists with one image only" limitTo: "Limit to {x}" noFollowRequests: "You don't have any pending follow requests" openImageInNewTab: "Open images in new tab" +warnForMissingAltText: "Warn you when you forget to put alt text" dashboard: "Dashboard" local: "Local" remote: "Remote" @@ -550,6 +557,8 @@ objectStorageUseProxy: "Connect over Proxy" objectStorageUseProxyDesc: "Turn this off if you are not going to use a Proxy for API connections" objectStorageSetPublicRead: "Set \"public-read\" on upload" s3ForcePathStyleDesc: "If s3ForcePathStyle is enabled, the bucket name has to included in the path of the URL as opposed to the hostname of the URL. You may need to enable this setting when using services such as a self-hosted Minio instance." +deeplFreeMode: "Use DeepLX-JS (No Auth Key)" +deeplFreeModeDescription: "Need Help? Check our documentation to know how to setup DeepLX-JS." serverLogs: "Server logs" deleteAll: "Delete all" showFixedPostForm: "Display the posting form at the top of the timeline" @@ -640,6 +649,7 @@ medium: "Medium" small: "Small" generateAccessToken: "Generate access token" permission: "Permissions" +adminPermission: "Admin Permissions" enableAll: "Enable all" disableAll: "Disable all" tokenRequested: "Grant access to account" @@ -656,7 +666,7 @@ smtpHost: "Host" smtpPort: "Port" smtpUser: "Username" smtpPass: "Password" -emptyToDisableSmtpAuth: "Leave username and password empty to disable SMTP verification" +emptyToDisableSmtpAuth: "Leave username and password empty to disable SMTP authentication" smtpSecure: "Use implicit SSL/TLS for SMTP connections" smtpSecureInfo: "Turn this off when using STARTTLS" testEmail: "Test email delivery" @@ -683,6 +693,7 @@ useGlobalSettingDesc: "If turned on, your account's notification settings will b other: "Other" regenerateLoginToken: "Regenerate login token" regenerateLoginTokenDescription: "Regenerates the token used internally during login. Normally this action is not necessary. If regenerated, all devices will be logged out." +theKeywordWhenSearchingForCustomEmoji: "This is the keyword when searching for custom emojis." setMultipleBySeparatingWithSpace: "Separate multiple entries with spaces." fileIdOrUrl: "File ID or URL" behavior: "Behavior" @@ -959,6 +970,10 @@ numberOfPageCache: "Number of cached pages" numberOfPageCacheDescription: "Increasing this number will improve convenience for but cause more load as more memory usage on the user's device." numberOfReplies: "Number of replies in a thread" numberOfRepliesDescription: "Increasing this number will display more replies. Setting this too high can cause replies to be cramped and unreadable." +boostSettings: "Boost Settings" +showVisibilitySelectorOnBoost: "Show Visibility Selector" +showVisibilitySelectorOnBoostDescription: "Shows the visiblity selector if enabled when clicking boost, if disabled it will use the default visiblity defined below and the selector will not show up." +visibilityOnBoost: "Default boost visibility" logoutConfirm: "Really log out?" lastActiveDate: "Last used at" statusbar: "Status bar" @@ -1010,6 +1025,8 @@ neverShow: "Don't show again" remindMeLater: "Maybe later" didYouLikeMisskey: "Have you taken a liking to Sharkey?" pleaseDonate: "{host} uses the free software, Sharkey. We would highly appreciate your donations so development of Sharkey can continue!" +pleaseDonateInstance: "You can also support {host} directly by donating to your instance administration." +correspondingSourceIsAvailable: "The corresponding source code is available at {anchor}" roles: "Roles" role: "Role" noRole: "Role not found" @@ -1036,6 +1053,9 @@ thisPostMayBeAnnoying: "This note may annoy others." thisPostMayBeAnnoyingHome: "Post to home timeline" thisPostMayBeAnnoyingCancel: "Cancel" thisPostMayBeAnnoyingIgnore: "Post anyway" +thisPostIsMissingAltTextCancel: "Cancel" +thisPostIsMissingAltTextIgnore: "Post anyway" +thisPostIsMissingAltText: "One of the files attached to this post is missing alt text. Please ensure all the attachments have alt text." collapseRenotes: "Collapse boosts you've already seen" collapseFiles: "Collapse files" autoloadConversation: "Load conversation on replies" @@ -1063,6 +1083,9 @@ resetPasswordConfirm: "Really reset your password?" sensitiveWords: "Sensitive words" sensitiveWordsDescription: "The visibility of all notes containing any of the configured words will be set to \"Home\" automatically. You can list multiple by separating them via line breaks." sensitiveWordsDescription2: "Using spaces will create AND expressions and surrounding keywords with slashes will turn them into a regular expression." +prohibitedWords: "Prohibited words" +prohibitedWordsDescription: "Enables an error when attempting to post a note containing the set word(s). Multiple words can be set, separated by a new line." +prohibitedWordsDescription2: "Using spaces will create AND expressions and surrounding keywords with slashes will turn them into a regular expression." hiddenTags: "Hidden hashtags" hiddenTagsDescription: "Select tags which will not shown on trend list.\nMultiple tags could be registered by lines." notesSearchNotAvailable: "Note search is unavailable." @@ -1081,12 +1104,15 @@ limitWidthOfReaction: "Limits the maximum width of reactions and display them in noteIdOrUrl: "Note ID or URL" video: "Video" videos: "Videos" +audio: "Audio" +audioFiles: "Audio" dataSaver: "Data Saver" accountMigration: "Account Migration" accountMoved: "This user has moved to a new account:" accountMovedShort: "This account has been migrated." operationForbidden: "Operation forbidden" forceShowAds: "Always show ads" +oneko: "Cat friend :3" addMemo: "Add memo" editMemo: "Edit memo" reactionsList: "Reactions" @@ -1182,6 +1208,7 @@ showRenotes: "Show boosts" edited: "Edited" notificationRecieveConfig: "Notification Settings" mutualFollow: "Mutual follow" +followingOrFollower: "Following or follower" fileAttachedOnly: "Only notes with files" showRepliesToOthersInTimeline: "Show replies to others in timeline" hideRepliesToOthersInTimeline: "Hide replies to others from timeline" @@ -1190,16 +1217,25 @@ hideRepliesToOthersInTimelineAll: "Hide replies to others from everyone you foll confirmShowRepliesAll: "This operation is irreversible. Would you really like to show replies to others from everyone you follow in your timeline?" confirmHideRepliesAll: "This operation is irreversible. Would you really like to hide replies to others from everyone you follow in your timeline?" externalServices: "External Services" +sourceCode: "Source code" +sourceCodeIsNotYetProvided: "The source code is not yet available. Please contact your administrator to fix this problem." +repositoryUrl: "Repository URL" +repositoryUrlDescription: "If there is a repository where the source code is publicly available, enter its URL. If you are using Sharkey as-is (without any changes to the source code), enter https://activitypub.software/TransFem-org/Sharkey/." +repositoryUrlOrTarballRequired: "If you have not published a repository, you must provide a tarball instead. See .config/example.yml for more information." +feedback: "Feedback" +feedbackUrl: "Feedback URL" impressum: "Impressum" impressumUrl: "Impressum URL" impressumDescription: "In some countries, like germany, the inclusion of operator contact information (an Impressum) is legally required for commercial websites." privacyPolicy: "Privacy Policy" privacyPolicyUrl: "Privacy Policy URL" tosAndPrivacyPolicy: "Terms of Service and Privacy Policy" +donation: "Donate" +donationUrl: "Donation URL" avatarDecorations: "Avatar decorations" attach: "Attach" detach: "Remove" -detachAll: "Remove all" +detachAll: "Remove All" angle: "Angle" flip: "Flip" showAvatarDecorations: "Show avatar decorations" @@ -1219,6 +1255,39 @@ seasonalScreenEffect: "Seasonal screen effects" decorate: "Decorate" addMfmFunction: "Add MFM" enableQuickAddMfmFunction: "Show advanced MFM picker" +bubbleGame: "Bubble Game" +sfx: "Sound Effects" +soundWillBePlayed: "Sound will be played" +showReplay: "View Replay" +replay: "Replay" +replaying: "Showing replay" +endReplay: "Exit Replay" +copyReplayData: "Copy replay data" +ranking: "Ranking" +lastNDays: "Last {n} days" +backToTitle: "Go back to title" +hemisphere: "Where are you located" +withSensitive: "Include notes with sensitive files" +userSaysSomethingSensitive: "Post by {name} contains sensitive content" +enableHorizontalSwipe: "Swipe to switch tabs" +loading: "Loading" +surrender: "Cancel" +gameRetry: "Retry" +_bubbleGame: + howToPlay: "How to play" + hold: "Hold" + _score: + score: "Score" + scoreYen: "Amount of money earned" + highScore: "High score" + maxChain: "Maximum number of chains" + yen: "{yen} Yen" + estimatedQty: "{qty} Pieces" + scoreSweets: "{onigiriQtyWithUnit} Onigiri" + _howToPlay: + section1: "Adjust the position and drop the object into the box." + section2: "When two objects of the same type touch each other, they will change into a different object and you score points." + section3: "The game is over when objects overflow from the box. Aim for a high score by fusing objects together while you avoid overflowing the box!" _announcement: forExistingUsers: "Existing users only" forExistingUsersDescription: "This announcement will only be shown to users existing at the point of publishment if enabled. If disabled, those newly signing up after it has been posted will also see it." @@ -1228,7 +1297,7 @@ _announcement: tooManyActiveAnnouncementDescription: "Having too many active announcements may worsen the user experience. Please consider archiving announcements that have become obsolete." readConfirmTitle: "Mark as read?" readConfirmText: "This will mark the contents of \"{title}\" as read." - shouldNotBeUsedToPresentPermanentInfo: "As it may significantly impact the user experience for new users, it is recommended to use notifications in the flow information rather than stock information." + shouldNotBeUsedToPresentPermanentInfo: "It's best to use announcements to publish fresh and time-bound information, not for information that will be relevant in the long term." dialogAnnouncementUxWarn: "Having two or more dialog-style notifications simultaneously can significantly impact the user experience, so please use them carefully." silence: "No notification" silenceDescription: "Turning this on will skip the notification of this announcement and the user won't need to read it." @@ -1589,6 +1658,13 @@ _achievements: _tutorialCompleted: title: "Sharkey Elementary Course Diploma" description: "Tutorial completed" + _bubbleGameExplodingHead: + title: "🤯" + description: "The biggest object in the bubble game" + _bubbleGameDoubleExplodingHead: + title: "Double🤯" + description: "Two of the biggest objects in the bubble game at the same time" + flavor: "You can fill a lunch box like this 🤯 🤯 a bit." _role: new: "New role" edit: "Edit role" @@ -1631,6 +1707,7 @@ _role: ltlAvailable: "Can view the local timeline" canPublicNote: "Can send public notes" canImportNotes: "Can import notes" + mentionMax: "Maximum number of mentions in a note" canInvite: "Can create instance invite codes" inviteLimit: "Invite limit" inviteLimitCycle: "Invite limit cooldown" @@ -1654,6 +1731,7 @@ _role: canUseTranslator: "Translator usage" avatarDecorationLimit: "Maximum number of avatar decorations that can be applied" _condition: + roleAssignedTo: "Assigned to manual roles" isLocal: "Local user" isRemote: "Remote user" createdLessThan: "Less than X has passed since account creation" @@ -1756,8 +1834,12 @@ _aboutMisskey: contributors: "Main contributors" allContributors: "All contributors" source: "Source code" + original: "Misskey original" + original_sharkey: "Sharkey original" + thisIsModifiedVersion: "{name} uses a modified version of the original Sharkey." translation: "Translate Sharkey" - donate: "Donate to Sharkey" + donate: "Donate to Misskey" + donate_sharkey: "Donate to Sharkey" morePatrons: "We also appreciate the support of many other helpers not listed here. Thank you! 🥰" patrons: "Patrons" projectMembers: "Project members" @@ -1977,54 +2059,54 @@ _permissions: "read:flash-likes": "View list of liked Plays" "write:flash-likes": "Edit list of liked Plays" "read:admin:abuse-user-reports": "View user reports" - "write:admin:delete-account": "Delete account" + "write:admin:delete-account": "Delete user account" "write:admin:delete-all-files-of-a-user": "Delete all files of a user" - "read:admin:index-stats": "View information about database indexes" - "read:admin:table-stats": "View information about database tables" - "read:admin:user-ips": "View user IP address" + "read:admin:index-stats": "View database index stats" + "read:admin:table-stats": "View database table stats" + "read:admin:user-ips": "View user IP addresses" "read:admin:meta": "View instance metadata" - "write:admin:reset-password": "Reset user passwords" - "write:admin:resolve-abuse-user-report": "Resolve user reports" - "write:admin:send-email": "Send Email" + "write:admin:reset-password": "Reset user password" + "write:admin:resolve-abuse-user-report": "Resolve user report" + "write:admin:send-email": "Send email" "read:admin:server-info": "View server info" "read:admin:show-moderation-log": "View moderation log" - "read:admin:show-user": "View user information" - "read:admin:show-users": "View users" + "read:admin:show-user": "View private user info" + "read:admin:show-users": "View private user info" "write:admin:suspend-user": "Suspend user" - "write:admin:unset-user-avatar": "Remove avatar from user" - "write:admin:unset-user-banner": "Remove banner from user" + "write:admin:unset-user-avatar": "Remove user avatar" + "write:admin:unset-user-banner": "Remove user banner" "write:admin:unsuspend-user": "Unsuspend user" - "write:admin:meta": "Edit instance metadata" - "write:admin:user-note": "Edit user note" - "write:admin:roles": "Edit roles" + "write:admin:meta": "Manage instance metadata" + "write:admin:user-note": "Manage moderation note" + "write:admin:roles": "Manage roles" "read:admin:roles": "View roles" - "write:admin:relays": "Edit relays" + "write:admin:relays": "Manage relays" "read:admin:relays": "View relays" - "write:admin:invite-codes": "Edit invite codes" + "write:admin:invite-codes": "Manage invite codes" "read:admin:invite-codes": "View invite codes" - "write:admin:announcements": "Edit announcements" + "write:admin:announcements": "Manage announcements" "read:admin:announcements": "View announcements" - "write:admin:avatar-decorations": "Edit avatar decorations" + "write:admin:avatar-decorations": "Manage avatar decorations" "read:admin:avatar-decorations": "View avatar decorations" - "write:admin:federation": "Edit remote instance information" - "write:admin:account": "Edit users" - "read:admin:account": "View information about user" - "write:admin:emoji": "Edit emojis" - "read:admin:emoji": "View emojis" - "write:admin:queue": "Edit queue" - "read:admin:queue": "View queue" - "write:admin:promo": "Edit promo" - "write:admin:drive": "Edit user drive" - "read:admin:drive": "View user drive" - "read:admin:stream": "Using the Websocket API for Admin" - "write:admin:ad": "Edit ads" + "write:admin:federation": "Manage federation data" + "write:admin:account": "Manage user account" + "read:admin:account": "View user account" + "write:admin:emoji": "Manage emoji" + "read:admin:emoji": "View emoji" + "write:admin:queue": "Manage job queue" + "read:admin:queue": "View job queue info" + "write:admin:promo": "Manage promotion notes" + "write:admin:drive": "Manage user drive" + "read:admin:drive": "View user drive info" + "read:admin:stream": "Use WebSocket API for Admin" + "write:admin:ad": "Manage ads" "read:admin:ad": "View ads" - "write:invite-codes": "Create Invitation Code" - "read:invite-codes": "View Invitation Code" - "write:clip-favorite": "Edit clips and likes" - "read:clip-favorite": "View clips and likes" - "read:federation": "View information about remote instance" - "write:report-abuse": "Report abuse" + "write:invite-codes": "Create invite codes" + "read:invite-codes": "Get invite codes" + "write:clip-favorite": "Manage favorited clips" + "read:clip-favorite": "View favorited clips" + "read:federation": "Get federation data" + "write:report-abuse": "Report violation" _auth: shareAccessTitle: "Granting application permissions" shareAccess: "Would you like to authorize \"{name}\" to access this account?" @@ -2143,12 +2225,17 @@ _profile: metadataContent: "Content" changeAvatar: "Change avatar" changeBanner: "Change banner" + updateBanner: "Update banner" + removeBanner: "Remove banner" changeBackground: "Change background" + updateBackground: "Update background" + removeBackground: "Remove background" verifiedLinkDescription: "By entering an URL that contains a link to your profile here, an ownership verification icon can be displayed next to the field." avatarDecorationMax: "You can add up to {max} decorations." _exportOrImport: allNotes: "All notes" favoritedNotes: "Favorite notes" + clips: "Clip" followingList: "Followed users" muteList: "Muted users" blockingList: "Blocked users" @@ -2277,6 +2364,8 @@ _notification: reactedBySomeUsers: "{n} users reacted" renotedBySomeUsers: "Boosted by {n} users" followedBySomeUsers: "Followed by {n} users" + flushNotification: "Clear notifications" + edited: "Note got edited" _types: all: "All" note: "New notes" @@ -2375,6 +2464,7 @@ _moderationLogTypes: resetPassword: "Password reset" suspendRemoteInstance: "Remote instance suspended" unsuspendRemoteInstance: "Remote instance unsuspended" + updateRemoteInstanceNote: "Moderation note updated for remote instance." markSensitiveDriveFile: "File marked as sensitive" unmarkSensitiveDriveFile: "File unmarked as sensitive" resolveAbuseReport: "Report resolved" @@ -2536,3 +2626,54 @@ _dataSaver: _code: title: "Code highlighting" description: "If code highlighting notations are used in MFM, etc., they will not load until tapped. Syntax highlighting requires downloading the highlight definition files for each programming language. Therefore, disabling the automatic loading of these files is expected to reduce the amount of communication data." +_hemisphere: + N: "Northern Hemisphere" + S: "Southern Hemisphere" + caption: "Used in some client settings to determine season." +_reversi: + reversi: "Reversi" + gameSettings: "Game settings" + chooseBoard: "Choose a board" + blackOrWhite: "Black/White" + blackIs: "{name} is playing Black" + rules: "Rules" + thisGameIsStartedSoon: "The game will begin shortly" + waitingForOther: "Waiting for opponent's turn" + waitingForMe: "Waiting for your turn" + waitingBoth: "Get ready" + ready: "Ready" + cancelReady: "Not ready" + opponentTurn: "Opponent's turn" + myTurn: "Your turn" + turnOf: "It's {name}'s turn" + pastTurnOf: "{name}'s turn" + surrender: "Surrender" + surrendered: "Surrendered" + timeout: "Out of time" + drawn: "Draw" + won: "{name} wins" + black: "Black" + white: "White" + total: "Total" + turnCount: "Turn {count}" + myGames: "My rounds" + allGames: "All rounds" + ended: "Ended" + playing: "Currently playing" + isLlotheo: "The one with fewer stones wins (Llotheo)" + loopedMap: "Looping map" + canPutEverywhere: "Tiles are placeable everywhere" + timeLimitForEachTurn: "Time limit for turn" + freeMatch: "Free Match" + lookingForPlayer: "Finding opponent..." + gameCanceled: "The game has been cancelled." + shareToTlTheGameWhenStart: "Share Game to timeline when started" + iStartedAGame: "The game has begun! #MisskeyReversi" + opponentHasSettingsChanged: "The opponent has changed their settings." + allowIrregularRules: "Irregular rules (completely free)" + disallowIrregularRules: "No irregular rules" + showBoardLabels: "Display row and column numbering on the board" + useAvatarAsStone: "Turn stones into user avatars" +_offlineScreen: + title: "Offline - cannot connect to the server" + header: "Unable to connect to the server" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index c269cc4d75e7740c332c23275ec20645b5fb6ea8..2288038e647df2cf716806ebd605993f715a0a1e 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -11,7 +11,7 @@ password: "Contraseña" forgotPassword: "Olvidé mi contraseña" fetchingAsApObject: "Buscando en el fediverso" ok: "OK" -gotIt: "Entendido" +gotIt: "¡Lo tengo!" cancel: "Cancelar" noThankYou: "No gracias" enterUsername: "Introduce el nombre de usuario" @@ -130,6 +130,7 @@ overwriteFromPinnedEmojis: "Sobreescribir los emojis fijados" reactionSettingDescription2: "Arrastre para reordenar, click para borrar, apriete la tecla + para añadir." rememberNoteVisibility: "Recordar visibilidad" attachCancel: "Quitar adjunto" +deleteFile: "Archivo eliminado" markAsSensitive: "Marcar como sensible" unmarkAsSensitive: "Desmarcar como sensible" enterFileName: "Ingrese el nombre del archivo" @@ -379,6 +380,11 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Habilitar hCaptcha" hcaptchaSiteKey: "Clave del sitio" hcaptchaSecretKey: "Clave secreta" +mcaptcha: "mCaptcha" +enableMcaptcha: "Activar mCaptcha" +mcaptchaSiteKey: "Clave del sitio" +mcaptchaSecretKey: "Clave secreta" +mcaptchaInstanceUrl: "URL del servidor mCaptcha" recaptcha: "reCAPTCHA" enableRecaptcha: "activar reCAPTCHA" recaptchaSiteKey: "Clave del sitio" @@ -626,6 +632,7 @@ medium: "Mediano" small: "Pequeño" generateAccessToken: "Generar token de acceso" permission: "Permisos" +adminPermission: "Permiso de administrador" enableAll: "Activar todo" disableAll: "Desactivar todo" tokenRequested: "Permiso de acceso a la cuenta" @@ -669,6 +676,7 @@ useGlobalSettingDesc: "Al activarse, se usará la configuración de notificacion other: "Otro" regenerateLoginToken: "Regenerar token de login" regenerateLoginTokenDescription: "Regenerar el token usado internamente durante el login. No siempre es necesario hacerlo. Al hacerlo de nuevo, se deslogueará en todos los dispositivos." +theKeywordWhenSearchingForCustomEmoji: "Palabra clave para buscar el emoji personalizado." setMultipleBySeparatingWithSpace: "Puedes añadir mas de uno, separado por espacios." fileIdOrUrl: "Id del archivo o URL" behavior: "Comportamiento" @@ -1033,6 +1041,8 @@ resetPasswordConfirm: "¿Realmente quieres cambiar la contraseña?" sensitiveWords: "Palabras sensibles" sensitiveWordsDescription: "La visibilidad de todas las notas que contienen cualquiera de las palabras configuradas serán puestas en \"Inicio\" automáticamente. Puedes enumerás varias separándolas con saltos de lÃnea" sensitiveWordsDescription2: "Si se usan espacios se crearán expresiones AND y las palabras subsecuentes con barras inclinadas se convertirán en expresiones regulares." +prohibitedWords: "Palabras explÃcitas" +prohibitedWordsDescription2: "Si se usan espacios se crearán expresiones AND y las palabras subsecuentes con barras inclinadas se convertirán en expresiones regulares." hiddenTags: "Hashtags ocultos" hiddenTagsDescription: "Selecciona las etiquetas que no se mostrarán en tendencias. Una etiqueta por lÃnea." notesSearchNotAvailable: "No se puede buscar una nota" @@ -1051,6 +1061,8 @@ limitWidthOfReaction: "Limitar ancho de las reacciones" noteIdOrUrl: "ID o URL de la nota" video: "Video" videos: "Video" +audio: "Sonido" +audioFiles: "Sonido" dataSaver: "Ahorro de datos" accountMigration: "Migración de cuenta" accountMoved: "Este usuario se movió a una nueva cuenta:" @@ -1154,6 +1166,7 @@ hideRepliesToOthersInTimelineAll: "Ocultar tus respuestas a otros usuarios que s confirmShowRepliesAll: "Esta operación es irreversible. ¿Confirmas que quieres mostrar tus respuestas a otros usuarios que sigues en tu lÃnea de tiempo?" confirmHideRepliesAll: "Esta operación es irreversible. ¿Confirmas que quieres ocultar tus respuestas a otros usuarios que sigues en tu lÃnea de tiempo?" externalServices: "Servicios Externos" +sourceCode: "Código fuente" impressum: "Impressum" impressumUrl: "Impressum URL" impressumDescription: "En algunos paÃses, como Alemania, la inclusión del operador de datos (el Impressum) es requerido legalmente para sitios web comerciales." @@ -1181,6 +1194,28 @@ remainingN: "Faltan: {n}" overwriteContentConfirm: "¿Quieres sustituir todo el contenido actual?" seasonalScreenEffect: "Efectos de pantalla asociados a estaciones" decorate: "Decorar" +addMfmFunction: "Añadir función MFM" +enableQuickAddMfmFunction: "Activar acceso rápido para añadir funciones MFM" +bubbleGame: "Bubble Game" +sfx: "Efectos de sonido" +soundWillBePlayed: "Se reproducirán efectos sonoros" +showReplay: "Ver reproducción" +replay: "Reproducir" +replaying: "Reproduciendo" +ranking: "Clasificación" +lastNDays: "Últimos {n} dÃas" +backToTitle: "Regresar al inicio" +hemisphere: "Región" +withSensitive: "Mostrar notas que contengan material sensible" +userSaysSomethingSensitive: "La publicación de {name} contiene material sensible" +enableHorizontalSwipe: "Deslice para cambiar de pestaña" +surrender: "detener" +_bubbleGame: + howToPlay: "Cómo jugar" + _howToPlay: + section1: "Ajuste la posición y deje caer el objeto en la caja" + section2: "Cuando dos objetos del mismo tipo se tocan, cambian a otro tipo y consigues puntos" + section3: "El juego termina cuando la caja se desborda de objetos. ¡Intenta conseguir una puntuación alta al juntar objetos mientras evitas desbordar la caja!" _announcement: forExistingUsers: "Solo para usuarios registrados" forExistingUsersDescription: "Este anuncio solo se mostrará a aquellos usuarios registrados en el momento de su publicación. Si se deshabilita esta opción, aquellos usuarios que se registren tras su publicación también lo verán." @@ -1551,6 +1586,10 @@ _achievements: _tutorialCompleted: title: "Diploma del Curso Básico de Misskey" description: "Tutorial completado" + _bubbleGameExplodingHead: + title: "🤯" + _bubbleGameDoubleExplodingHead: + title: "Doble 🤯" _role: new: "Crear rol" edit: "Editar rol" @@ -1933,6 +1972,54 @@ _permissions: "write:flash": "Editar Plays" "read:flash-likes": "Ver los Play que me gustan" "write:flash-likes": "Editar lista de Play que me gustan" + "read:admin:abuse-user-reports": "Ver reportes de usuarios" + "write:admin:delete-account": "Eliminar cuentas de usuario" + "write:admin:delete-all-files-of-a-user": "Eliminar todos los archivos de un usuario" + "read:admin:index-stats": "Ver datos indexados" + "read:admin:user-ips": "Ver dirección IP de usuario" + "read:admin:meta": "Ver metadatos de la instancia" + "write:admin:reset-password": "Restablecer contraseñas de usuario" + "write:admin:resolve-abuse-user-report": "Resolución de reportes de usuario" + "write:admin:send-email": "Enviar email" + "read:admin:server-info": "Ver información del servidor" + "read:admin:show-moderation-log": "Ver log de moderación" + "read:admin:show-user": "Ver información privada de usuario" + "read:admin:show-users": "Ver información privada de usuario" + "write:admin:suspend-user": "Suspender cuentas de usuario" + "write:admin:unset-user-avatar": "Quitar avatares de usuario" + "write:admin:unset-user-banner": "Quitar banner de usuarios" + "write:admin:unsuspend-user": "Quitar suspensión de cuentas de usuario" + "write:admin:meta": "Edición de metadatos de la instancia" + "write:admin:user-note": "Moderación de notas" + "write:admin:roles": "Edición de roles de usuario" + "read:admin:roles": "Ver roles de usuario" + "write:admin:relays": "Edición de relays" + "read:admin:relays": "Ver relays" + "write:admin:invite-codes": "Edición de códigos de invitación" + "read:admin:invite-codes": "Ver códigos de invitación" + "write:admin:announcements": "Edición de anuncios" + "read:admin:announcements": "Ver anuncios" + "write:admin:avatar-decorations": "Edición de decoración de avatares" + "read:admin:avatar-decorations": "Ver decoraciones de avatar" + "write:admin:federation": "Edición de federación de instancias" + "write:admin:account": "Edición de cuentas de usuario" + "read:admin:account": "Ver cuentas de usuario" + "write:admin:emoji": "Edición de emojis" + "read:admin:emoji": "Ver emojis" + "write:admin:queue": "Edición de cola de tareas" + "read:admin:queue": "Ver cola de tareas" + "write:admin:promo": "Edición de promociones" + "write:admin:drive": "Edición de Drive de usuarios" + "read:admin:drive": "Ver Drive de usuarios" + "read:admin:stream": "Usar la API de Websocket para administradores" + "write:admin:ad": "Edición de anuncios" + "read:admin:ad": "Ver anuncios" + "write:invite-codes": "Crear códigos de invitación" + "read:invite-codes": "Ver códigos de invitación" + "write:clip-favorite": "Marcar me gusta en clips" + "read:clip-favorite": "Ver los clips que me gustan" + "read:federation": "Ver instancias federadas" + "write:report-abuse": "Crear reportes de usuario" _auth: shareAccessTitle: "Permisos de la aplicación" shareAccess: "¿Desea permitir el acceso a la cuenta \"{name}\"?" @@ -2055,6 +2142,7 @@ _profile: _exportOrImport: allNotes: "Todas las notas" favoritedNotes: "Notas favoritas" + clips: "Clip" followingList: "Siguiendo" muteList: "Silenciados" blockingList: "Bloqueados" @@ -2354,3 +2442,11 @@ _dataSaver: _code: title: "Resaltar código" description: "Si se usa resaltado de código en MFM, etc., no se cargará hasta pulsar en ello. El resaltado de sintaxis requiere la descarga de archivos de definición para cada lenguaje de programación. Debido a esto, al deshabilitar la carga automática de estos archivos reducirás el consumo de datos." +_hemisphere: + N: "Hemisferio norte" + S: "Hemisferio sur" +_reversi: + reversi: "Reversi" + won: "{name} ha ganado" + total: "Total" + diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index ac9e94a01a30dc1a0aabff5da2e9eb036fb5b604..24243c1f188cce058bfb30861c7b0c0c7b6fceb9 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -2,7 +2,7 @@ _lang_: "Français" headlineMisskey: "Réseau relié par des notes" introMisskey: "Bienvenue ! Misskey est un service de microblogage décentralisé, libre et ouvert.\nÉcrivez des « notes » et partagez ce qui se passe à l’instant présent, autour de vous avec les autres 📡\nLa fonction « réactions », vous permet également d’ajouter une réaction rapide aux notes des autres utilisateur·rice·s ðŸ‘\nExplorons un nouveau monde 🚀" -poweredByMisskeyDescription: "{nom} est l'un des services propulsés par la plateforme ouverte <b>Misskey</b> (appelée \"instance Misskey\")." +poweredByMisskeyDescription: "{name} est l'un des services propulsés par la plateforme ouverte <b>Misskey</b> (appelée \"instance Misskey\")." monthAndDay: "{day}/{month}" search: "Rechercher" notifications: "Notifications" @@ -130,6 +130,7 @@ overwriteFromPinnedEmojis: "Remplacer par les émojis épinglés globalement" reactionSettingDescription2: "Déplacer pour réorganiser, cliquer pour effacer, utiliser « + » pour ajouter." rememberNoteVisibility: "Se souvenir de la visibilité des notes" attachCancel: "Supprimer le fichier attaché" +deleteFile: "Fichier supprimé" markAsSensitive: "Marquer comme sensible" unmarkAsSensitive: "Supprimer le marquage comme sensible" enterFileName: "Entrer le nom du fichier" @@ -168,7 +169,7 @@ cacheRemoteSensitiveFilesDescription: "Si vous désactivez ce paramètre, les fi flagAsBot: "Ce compte est un robot" flagAsBotDescription: "Si ce compte est géré de manière automatisée, choisissez cette option. Si elle est activée, elle agira comme un marqueur pour les autres développeurs afin d'éviter des chaînes d'interaction sans fin avec d'autres robots et d'ajuster les systèmes internes de Misskey pour traiter ce compte comme un robot." flagAsCat: "Ce compte est un chat" -flagAsCatDescription: "Activer l'option \" Je suis un chat \" pour ce compte." +flagAsCatDescription: "Miaou miaou miaou ?" flagShowTimelineReplies: "Afficher les réponses dans le fil" flagShowTimelineRepliesDescription: "Affiche les réponses des utilisateurs aux notes des autres utilisateurs dans la timeline si cette option est activée." autoAcceptFollowed: "Accepter automatiquement les demandes d’abonnement venant d’utilisateur·rice·s que vous suivez" @@ -379,6 +380,11 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Activer hCaptcha" hcaptchaSiteKey: "Clé du site" hcaptchaSecretKey: "Clé secrète" +mcaptcha: "mCaptcha" +enableMcaptcha: "Activer mCaptcha" +mcaptchaSiteKey: "Clé du site" +mcaptchaSecretKey: "Clé secrète" +mcaptchaInstanceUrl: "URL de l'instance de mCaptcha" recaptcha: "reCAPTCHA" enableRecaptcha: "Activer reCAPTCHA" recaptchaSiteKey: "Clé du site" @@ -396,7 +402,7 @@ antennaKeywords: "Mots clés à recevoir" antennaExcludeKeywords: "Mots clés à exclure" antennaKeywordsDescription: "Séparer avec des espaces pour la condition AND. Séparer avec un saut de ligne pour une condition OR." notifyAntenna: "Me notifier pour les nouvelles notes" -withFileAntenna: "Notes ayant des attachements uniquement" +withFileAntenna: "Notes ayant des fichiers joints uniquement" enableServiceworker: "Activer ServiceWorker" antennaUsersDescription: "Saisissez un seul nom d’utilisateur·rice par ligne" caseSensitive: "Sensible à la casse" @@ -520,7 +526,7 @@ hideThisNote: "Masquer cette note" showFeaturedNotesInTimeline: "Afficher les notes des Tendances dans le fil d'actualité" objectStorage: "Stockage d'objets" useObjectStorage: "Utiliser le stockage d'objets" -objectStorageBaseUrl: "Base URL" +objectStorageBaseUrl: "URL de base" objectStorageBaseUrlDesc: "Préfixe d’URL utilisé pour construire l’URL vers le référencement d’objet (média). Spécifiez son URL si vous utilisez un CDN ou un proxy, sinon spécifiez l’adresse accessible au public selon le guide de service que vous allez utiliser. P.ex. 'https://<bucket>.s3.amazonaws.com' pour AWS S3 et 'https://storage.googleapis.com/<bucket>' pour GCS." objectStorageBucket: "Bucket" objectStorageBucketDesc: "Veuillez spécifier le nom du compartiment utilisé sur le service configuré." @@ -625,6 +631,7 @@ medium: "Moyen" small: "Petit" generateAccessToken: "Générer un jeton d'accès" permission: "Autorisations " +adminPermission: "Droits de l'administrateur" enableAll: "Tout activer" disableAll: "Tout désactiver" tokenRequested: "Autoriser l'accès au compte" @@ -696,7 +703,7 @@ system: "Système" switchUi: "Modifier l'interface utilisateur" desktop: "Bureau" clip: "Clip" -createNew: "Créer nouveau" +createNew: "Créer" optional: "Facultatif" createNewClip: "Créer un nouveau clip" unclip: "Supprimer le clip" @@ -1028,12 +1035,18 @@ nonSensitiveOnlyForLocalLikeOnlyForRemote: "Non sensibles seulement (mentions j' rolesAssignedToMe: "Rôles attribués à moi" resetPasswordConfirm: "Souhaitez-vous réinitialiser votre mot de passe ?" sensitiveWords: "Mots sensibles" +sensitiveWordsDescription2: "Séparer par une espace pour créer une expression AND ; entourer de barres obliques pour créer une expression régulière." +prohibitedWords: "Mots interdits" +prohibitedWordsDescription2: "Séparer par une espace pour créer une expression AND ; entourer de barres obliques pour créer une expression régulière." hiddenTags: "Hashtags cachés" hiddenTagsDescription: "Les hashtags définis ne s'afficheront pas dans les tendances. Vous pouvez définir plusieurs hashtags en faisant un saut de ligne." notesSearchNotAvailable: "La recherche de notes n'est pas disponible." license: "Licence" +unfavoriteConfirm: "Vraiment supprimer des favoris ?" myClips: "Mes clips" drivecleaner: "Nettoyeur du Disque" +retryAllQueuesNow: "Réessayer tous les fils d'attente immédiatement" +retryAllQueuesConfirmTitle: "Vraiment réessayer ?" retryAllQueuesConfirmText: "Cela peut augmenter temporairement la charge du serveur." enableChartsForRemoteUser: "Générer les graphiques pour les utilisateurs distants" enableChartsForFederatedInstances: "Générer les graphiques pour les instances distantes" @@ -1043,6 +1056,8 @@ limitWidthOfReaction: "Limiter la largeur maximale des réactions et les affiche noteIdOrUrl: "Identifiant de la note ou URL" video: "Vidéo" videos: "Vidéos" +audio: "Audio" +audioFiles: "Fichiers audio" dataSaver: "Économiseur de données" accountMigration: "Migration de compte" accountMoved: "Cet·te utilisateur·rice a migré son compte vers :" @@ -1081,12 +1096,27 @@ specifyUser: "Spécifier l'utilisateur·rice" failedToPreviewUrl: "Aperçu d'URL échoué" update: "Mettre à jour" rolesThatCanBeUsedThisEmojiAsReaction: "Rôles qui peuvent utiliser cet émoji comme réaction" +rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "Si aucun rôle n'est spécifié, tout le monde peut utiliser cet émoji comme réaction." +rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "Il faut un rôle public." +cancelReactionConfirm: "Supprimez la réaction ?" +changeReactionConfirm: "Changer la réaction ?" later: "Plus tard" goToMisskey: "Retour vers Misskey" additionalEmojiDictionary: "Dictionnaires d'émojis additionnels" installed: "Installé" branding: "Image de marque" +enableServerMachineStats: "Publier les statistiques du matériel du serveur" +enableIdenticonGeneration: "Générer les identicons des utilisateurs" +turnOffToImprovePerformance: "Désactiver peut améliorer la performance." +createInviteCode: "Créer un code d'invitation" +createWithOptions: "Options" +createCount: "Quantité à créer" +inviteCodeCreated: "Code d'invitation créé" +inviteLimitExceeded: "Vous avez atteint la limite de codes d'invitation que vous pouvez générer." expirationDate: "Date d’expiration" +noExpirationDate: "Ne pas expirer" +inviteCodeUsedAt: "Code d'invitation utilisé à " +registeredUserUsingInviteCode: "Code d'invitation utilisé par" waitingForMailAuth: "En attente de la vérification de l'adresse courriel" inviteCodeCreator: "Créateur·rice de ce code d'invitation" usedAt: "Utilisé le" @@ -1095,17 +1125,20 @@ used: "Utilisé" expired: "Expiré" doYouAgree: "Êtes-vous d’accord ?" beSureToReadThisAsItIsImportant: "Assurez-vous de le lire ; c'est important." +iHaveReadXCarefullyAndAgree: "J'ai lu le contenu de « {x} » et donne mon accord." dialog: "Dialogue" icon: "Avatar" forYou: "Pour vous" currentAnnouncements: "Annonces actuelles" pastAnnouncements: "Annonces passées" +youHaveUnreadAnnouncements: "Il y a des annonces non lues." replies: "Réponses" renotes: "Renotes" loadReplies: "Inclure les réponses" loadConversation: "Afficher la conversation" pinnedList: "Liste épinglée" notifyNotes: "Notifier à propos des nouvelles notes" +unnotifyNotes: "Ne pas notifier pour la publication des notes" authentication: "Authentification" authenticationRequiredToContinue: "Veuillez vous authentifier pour continuer" dateAndTime: "Date et heure" @@ -1113,6 +1146,7 @@ showRenotes: "Afficher les renotes" edited: "Modifié" notificationRecieveConfig: "Paramètres des notifications" mutualFollow: "Abonnement mutuel" +fileAttachedOnly: "Avec fichiers joints seulement" showRepliesToOthersInTimeline: "Afficher les réponses aux autres dans le fil" hideRepliesToOthersInTimeline: "Masquer les réponses aux autres dans le fil" showRepliesToOthersInTimelineAll: "Afficher les réponses de toutes les personnes que vous suivez dans le fil" @@ -1120,6 +1154,12 @@ hideRepliesToOthersInTimelineAll: "Masquer les réponses de toutes les personnes confirmShowRepliesAll: "Cette opération est irréversible. Voulez-vous vraiment afficher les réponses de toutes les personnes que vous suivez dans le fil ?" confirmHideRepliesAll: "Cette opération est irréversible. Voulez-vous vraiment masquer les réponses de toutes les personnes que vous suivez dans le fil ?" externalServices: "Services externes" +sourceCode: "Code source" +sourceCodeIsNotYetProvided: "Le code source n'est pas encore disponible. Veuillez signaler ce problème aux administrateurs." +repositoryUrl: "URL du dépôt" +repositoryUrlDescription: "Entrez l'URL du dépôt où se trouve le code source ici. Si vous utilisez Misskey tel quel (sans changer le code source), entrez https://github.com/misskey-dev/misskey" +feedback: "Commentaires" +feedbackUrl: "URL pour les commentaires" impressum: "Impressum" impressumUrl: "URL de l'impressum" impressumDescription: "Dans certains pays comme l'Allemagne, il est obligatoire d'afficher les informations sur l'opérateur d'un site (un impressum)." @@ -1147,7 +1187,34 @@ remainingN: "Restants : {n}" overwriteContentConfirm: "Voulez-vous remplacer le contenu actuel ?" seasonalScreenEffect: "Effet d'écran saisonnier" decorate: "Décorer" +addMfmFunction: "Insérer MFM" +enableQuickAddMfmFunction: "Afficher le sélecteur de MFM avancé" +bubbleGame: "Jeu de bulles" +sfx: "Effets sonores" +soundWillBePlayed: "Le son sera joué" +showReplay: "Voir le replay" +replay: "Rediffusion" +replaying: "En cours de rediffusion" +endReplay: "Arrêter la rediffusion" +copyReplayData: "Copier les données de la rediffusion" +ranking: "Classement" +lastNDays: "Derniers {n} jours" +backToTitle: "Retourner au titre" +hemisphere: "Votre région" +enableHorizontalSwipe: "Glisser pour changer d'onglet" +loading: "Chargement en cours" +surrender: "Annuler" +gameRetry: "Réessayer" +_bubbleGame: + howToPlay: "Comment jouer" + hold: "Réserver" + _score: + score: "Score" + scoreYen: "Montant gagné" + highScore: "Meilleur score" + yen: "{yen} yens" _announcement: + forExistingUsers: "Pour les utilisateurs existants seulement" readConfirmTitle: "Marquer comme lu ?" shouldNotBeUsedToPresentPermanentInfo: "Puisque cela pourrait nuire considérablement à l'expérience utilisateur pour les nouveaux utilisateurs, il est recommandé d'utiliser les annonces pour afficher des informations temporaires plutôt que des informations persistantes." dialogAnnouncementUxWarn: "Avoir deux ou plus annonces de style dialogue en même temps pourrait nuire considérablement à l'expérience utilisateur. Veuillez les utiliser avec caution." @@ -1157,7 +1224,7 @@ _initialAccountSetting: profileSetting: "Paramètres du profil" privacySetting: "Paramètres de confidentialité" initialAccountSettingCompleted: "Configuration du profil terminée avec succès !" - youCanContinueTutorial: "Vous pouvez procéder au tutoriel sur l'utilisation de {nom}(Misskey) ou vous arrêter ici et commencer à l'utiliser immédiatement." + youCanContinueTutorial: "Vous pouvez procéder au tutoriel sur l'utilisation de {name}(Misskey) ou vous arrêter ici et commencer à l'utiliser immédiatement." startTutorial: "Démarrer le tutoriel" skipAreYouSure: "Désirez-vous ignorer la configuration du profil ?" _initialTutorial: @@ -1242,6 +1309,7 @@ _accountMigration: startMigration: "Migrer" movedTo: "Compte vers lequel vous migrez :" _achievements: + earnedAt: "Date d'obtention" _types: _notes1: title: "Je viens tout juste de configurer mon shonk" @@ -1282,10 +1350,13 @@ _achievements: title: "Régulier III" description: "Se connecter pour un total de 400 jours" _login500: + title: "Expert I" description: "Se connecter pour un total de 500 jours" _login600: + title: "Expert II" description: "Se connecter pour un total de 600 jours" _login700: + title: "Expert III" description: "Se connecter pour un total de 700 jours" _login800: description: "Se connecter pour un total de 800 jours" @@ -1380,9 +1451,12 @@ _role: description: "Description du rôle" permission: "Rôle et autorisations" assignTarget: "Attribuer" + manual: "Manuel" manualRoles: "Rôles manuels" + conditional: "Conditionnel" conditionalRoles: "Rôles conditionnels" condition: "Condition" + isConditionalRole: "Ceci est un rôle conditionnel." isPublic: "Rôle public" options: "Options" policies: "Stratégies" @@ -1799,6 +1873,7 @@ _profile: avatarDecorationMax: "Vous pouvez mettre au plus {max} décorations d'avatar." _exportOrImport: allNotes: "Toutes les notes" + clips: "Clip" followingList: "Abonnements" muteList: "Comptes masqués" blockingList: "Comptes bloqués" @@ -2063,3 +2138,6 @@ _dataSaver: _code: title: "Mise en évidence du code" description: "Si la notation de mise en évidence du code est utilisée, par exemple dans la MFM, elle ne sera pas chargée tant qu'elle n'aura pas été tapée. La mise en évidence du code nécessite le chargement du fichier de définition de chaque langue à mettre en évidence, mais comme ces fichiers ne sont plus chargés automatiquement, on peut s'attendre à une réduction du trafic de données." +_reversi: + total: "Total" + diff --git a/locales/generateDTS.js b/locales/generateDTS.js index d3afdd6e15028d09f3289cd1ca07d242aaf820f2..49807144ec64aac5d4ec0858db3b218676c52c6e 100644 --- a/locales/generateDTS.js +++ b/locales/generateDTS.js @@ -6,54 +6,176 @@ import ts from 'typescript'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); +const parameterRegExp = /\{(\w+)\}/g; + +function createMemberType(item) { + if (typeof item !== 'string') { + return ts.factory.createTypeLiteralNode(createMembers(item)); + } + const parameters = Array.from( + item.matchAll(parameterRegExp), + ([, parameter]) => parameter, + ); + return parameters.length + ? ts.factory.createTypeReferenceNode( + ts.factory.createIdentifier('ParameterizedString'), + [ + ts.factory.createUnionTypeNode( + parameters.map((parameter) => + ts.factory.createStringLiteral(parameter), + ), + ), + ], + ) + : ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword); +} function createMembers(record) { - return Object.entries(record) - .map(([k, v]) => ts.factory.createPropertySignature( + return Object.entries(record).map(([k, v]) => { + const node = ts.factory.createPropertySignature( undefined, ts.factory.createStringLiteral(k), undefined, - typeof v === 'string' - ? ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) - : ts.factory.createTypeLiteralNode(createMembers(v)), - )); + createMemberType(v), + ); + if (typeof v === 'string') { + ts.addSyntheticLeadingComment( + node, + ts.SyntaxKind.MultiLineCommentTrivia, + `* + * ${v.replace(/\n/g, '\n * ')} + `, + true, + ); + } + return node; + }); } export default function generateDTS() { const locale = yaml.load(fs.readFileSync(`${__dirname}/ja-JP.yml`, 'utf-8')); const members = createMembers(locale); const elements = [ - ts.factory.createInterfaceDeclaration( - [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], - ts.factory.createIdentifier('Locale'), - undefined, - undefined, - members, - ), ts.factory.createVariableStatement( [ts.factory.createToken(ts.SyntaxKind.DeclareKeyword)], ts.factory.createVariableDeclarationList( - [ts.factory.createVariableDeclaration( - ts.factory.createIdentifier('locales'), + [ + ts.factory.createVariableDeclaration( + ts.factory.createIdentifier('kParameters'), + undefined, + ts.factory.createTypeOperatorNode( + ts.SyntaxKind.UniqueKeyword, + ts.factory.createKeywordTypeNode(ts.SyntaxKind.SymbolKeyword), + ), + undefined, + ), + ], + ts.NodeFlags.Const, + ), + ), + ts.factory.createInterfaceDeclaration( + [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], + ts.factory.createIdentifier('ParameterizedString'), + [ + ts.factory.createTypeParameterDeclaration( undefined, - ts.factory.createTypeLiteralNode([ts.factory.createIndexSignature( + ts.factory.createIdentifier('T'), + ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + ), + ], + undefined, + [ + ts.factory.createPropertySignature( + undefined, + ts.factory.createComputedPropertyName( + ts.factory.createIdentifier('kParameters'), + ), + undefined, + ts.factory.createTypeReferenceNode( + ts.factory.createIdentifier('T'), undefined, - [ts.factory.createParameterDeclaration( + ), + ), + ], + ), + ts.factory.createInterfaceDeclaration( + [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], + ts.factory.createIdentifier('ILocale'), + undefined, + undefined, + [ + ts.factory.createIndexSignature( + undefined, + [ + ts.factory.createParameterDeclaration( undefined, undefined, - ts.factory.createIdentifier('lang'), + ts.factory.createIdentifier('_'), undefined, ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), undefined, - )], + ), + ], + ts.factory.createUnionTypeNode([ + ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + ts.factory.createTypeReferenceNode( + ts.factory.createIdentifier('ParameterizedString'), + ), ts.factory.createTypeReferenceNode( - ts.factory.createIdentifier('Locale'), + ts.factory.createIdentifier('ILocale'), undefined, ), - )]), - undefined, - )], - ts.NodeFlags.Const | ts.NodeFlags.Ambient | ts.NodeFlags.ContextFlags, + ]), + ), + ], + ), + ts.factory.createInterfaceDeclaration( + [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], + ts.factory.createIdentifier('Locale'), + undefined, + [ + ts.factory.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [ + ts.factory.createExpressionWithTypeArguments( + ts.factory.createIdentifier('ILocale'), + undefined, + ), + ]), + ], + members, + ), + ts.factory.createVariableStatement( + [ts.factory.createToken(ts.SyntaxKind.DeclareKeyword)], + ts.factory.createVariableDeclarationList( + [ + ts.factory.createVariableDeclaration( + ts.factory.createIdentifier('locales'), + undefined, + ts.factory.createTypeLiteralNode([ + ts.factory.createIndexSignature( + undefined, + [ + ts.factory.createParameterDeclaration( + undefined, + undefined, + ts.factory.createIdentifier('lang'), + undefined, + ts.factory.createKeywordTypeNode( + ts.SyntaxKind.StringKeyword, + ), + undefined, + ), + ], + ts.factory.createTypeReferenceNode( + ts.factory.createIdentifier('Locale'), + undefined, + ), + ), + ]), + undefined, + ), + ], + ts.NodeFlags.Const, ), ), ts.factory.createFunctionDeclaration( @@ -70,16 +192,39 @@ export default function generateDTS() { ), ts.factory.createExportDefault(ts.factory.createIdentifier('locales')), ]; - const printed = ts.createPrinter({ - newLine: ts.NewLineKind.LineFeed, - }).printList( - ts.ListFormat.MultiLine, - ts.factory.createNodeArray(elements), - ts.createSourceFile('index.d.ts', '', ts.ScriptTarget.ESNext, true, ts.ScriptKind.TS), + ts.addSyntheticLeadingComment( + elements[0], + ts.SyntaxKind.MultiLineCommentTrivia, + ' eslint-disable ', + true, + ); + ts.addSyntheticLeadingComment( + elements[0], + ts.SyntaxKind.SingleLineCommentTrivia, + ' This file is generated by locales/generateDTS.js', + true, ); + ts.addSyntheticLeadingComment( + elements[0], + ts.SyntaxKind.SingleLineCommentTrivia, + ' Do not edit this file directly.', + true, + ); + const printed = ts + .createPrinter({ + newLine: ts.NewLineKind.LineFeed, + }) + .printList( + ts.ListFormat.MultiLine, + ts.factory.createNodeArray(elements), + ts.createSourceFile( + 'index.d.ts', + '', + ts.ScriptTarget.ESNext, + true, + ts.ScriptKind.TS, + ), + ); - fs.writeFileSync(`${__dirname}/index.d.ts`, `/* eslint-disable */ -// This file is generated by locales/generateDTS.js -// Do not edit this file directly. -${printed}`, 'utf-8'); + fs.writeFileSync(`${__dirname}/index.d.ts`, printed, 'utf-8'); } diff --git a/locales/hr-HR.yml b/locales/hr-HR.yml index 9cfebdd01a612d21681d6c9ee70e1a7bdeb37273..881aa8464e06b4033d7f30c822e48a951c9be406 100644 --- a/locales/hr-HR.yml +++ b/locales/hr-HR.yml @@ -3,3 +3,4 @@ _lang_: "japanski" ok: "OK" gotIt: "Razumijem" cancel: "otkazati" + diff --git a/locales/ht-HT.yml b/locales/ht-HT.yml index e3595c79b6560afb6dfe2e947f844c01f73eede1..1698c9f280418a32a9da2357a1af4a80ed017323 100644 --- a/locales/ht-HT.yml +++ b/locales/ht-HT.yml @@ -16,3 +16,4 @@ _2fa: renewTOTPCancel: "Sispann" _widgets: profile: "pwofil" + diff --git a/locales/hu-HU.yml b/locales/hu-HU.yml index 023a91494d027743c7242b61ce5b8a874a5f279d..2f7006484afe72ef702a44bc8317868b35c30834 100644 --- a/locales/hu-HU.yml +++ b/locales/hu-HU.yml @@ -102,3 +102,4 @@ _deck: _columns: notifications: "ÉrtesÃtések" tl: "IdÅ‘vonal" + diff --git a/locales/id-ID.yml b/locales/id-ID.yml index 00844550fdcefd12d5cc3c156c54b44dd3d55be8..b638d7991f4f75f59d9b7f2f7055b76c04b5d9e4 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -81,7 +81,7 @@ exportRequested: "Kamu telah meminta ekspor. Ini akan memakan waktu sesaat. Sete importRequested: "Kamu telah meminta impor. Ini akan memakan waktu sesaat." lists: "Daftar" noLists: "Kamu tidak memiliki daftar apapun" -note: "Catat" +note: "Catatan" notes: "Catatan" following: "Ikuti" followers: "Pengikut" @@ -125,9 +125,12 @@ emojiPicker: "Emoji Picker" pinnedEmojisForReactionSettingDescription: "Atur sematan emoji pada reaksi" pinnedEmojisSettingDescription: "Atur sematan emoji pada masukan emoji" emojiPickerDisplay: "Tampilan Emoji Picker" +overwriteFromPinnedEmojisForReaction: "Timpa dari pengaturan reaksi" +overwriteFromPinnedEmojis: "Timpa dari pengaturan umum" reactionSettingDescription2: "Geser untuk memindah urutan emoji, klik untuk menghapus, tekan \"+\" untuk menambahkan" rememberNoteVisibility: "Ingat pengaturan visibilitas catatan" attachCancel: "Hapus lampiran" +deleteFile: "Berkas dihapus" markAsSensitive: "Tandai sebagai konten sensitif" unmarkAsSensitive: "Hapus tanda konten sensitif" enterFileName: "Masukkan nama berkas" @@ -377,6 +380,11 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Nyalakan hCaptcha" hcaptchaSiteKey: "Site Key" hcaptchaSecretKey: "Secret Key" +mcaptcha: "mCaptcha" +enableMcaptcha: "Nyalakan mCaptcha" +mcaptchaSiteKey: "Site key" +mcaptchaSecretKey: "Secret Key" +mcaptchaInstanceUrl: "URL instansi mCaptcha" recaptcha: "reCAPTCHA" enableRecaptcha: "Nyalakan reCAPTCHA" recaptchaSiteKey: "Site key" @@ -624,6 +632,7 @@ medium: "Sedang" small: "Kecil" generateAccessToken: "Buat token akses" permission: "Izin" +adminPermission: "Wewenang Izin Admin" enableAll: "Aktifkan semua" disableAll: "Nonaktifkan semua" tokenRequested: "Berikan ijin akses ke akun" @@ -667,6 +676,7 @@ useGlobalSettingDesc: "Jika dinyalakan, setelan notifikasi akun kamu akan diguna other: "Lainnya" regenerateLoginToken: "Perbarui token login" regenerateLoginTokenDescription: "Perbarui token yang digunakan secara internal saat login. Normalnya aksi ini tidak diperlukan. Jika diperbarui, semua perangkat akan dilogout." +theKeywordWhenSearchingForCustomEmoji: "Kata kunci ini digunakan untuk mencari emoji kustom yang dicari." setMultipleBySeparatingWithSpace: "Kamu dapat menyetel banyak dengan memisahkannya menggunakan spasi." fileIdOrUrl: "File-ID atau URL" behavior: "Perilaku" @@ -879,6 +889,8 @@ makeReactionsPublicDescription: "Pengaturan ini akan membuat daftar dari semua r classic: "Klasik" muteThread: "Bisukan thread" unmuteThread: "Suarakan thread" +followingVisibility: "Visibilitas mengikuti" +followersVisibility: "Visibilitas pengikut" continueThread: "Lihat lanjutan thread" deleteAccountConfirm: "Akun akan dihapus. Apakah kamu yakin?" incorrectPassword: "Kata sandi salah." @@ -1029,6 +1041,8 @@ resetPasswordConfirm: "Yakin untuk mereset kata sandimu?" sensitiveWords: "Kata sensitif" sensitiveWordsDescription: "Visibilitas dari semua catatan mengandung kata yang telah diatur akan dijadikan \"Beranda\" secara otomatis. Kamu dapat mendaftarkan kata tersebut lebih dari satu dengan menuliskannya di baris baru." sensitiveWordsDescription2: "Menggunakan spasi akan membuat ekspresi AND dan kata kunci disekitarnya dengan garis miring akan mengubahnya menjadi ekspresi reguler." +prohibitedWords: "Kata yang dilarang" +prohibitedWordsDescription2: "Menggunakan spasi akan membuat ekspresi AND dan kata kunci disekitarnya dengan garis miring akan mengubahnya menjadi ekspresi reguler." hiddenTags: "Tagar tersembunyi" hiddenTagsDescription: "Pilih tanda yang mana akan tidak diperlihatkan dalam daftar tren.\nTanda lebih dari satu dapat didaftarkan dengan tiap baris." notesSearchNotAvailable: "Pencarian catatan tidak tersedia." @@ -1047,6 +1061,8 @@ limitWidthOfReaction: "Batasi lebar maksimum reaksi dan tampilkan dalam ukuran t noteIdOrUrl: "ID catatan atau URL" video: "Video" videos: "Video" +audio: "Suara" +audioFiles: "Berkas Suara" dataSaver: "Penghemat data" accountMigration: "Pemindahan akun" accountMoved: "Pengguna ini telah berpindah ke akun baru:" @@ -1150,6 +1166,7 @@ hideRepliesToOthersInTimelineAll: "Sembuyikan balasan ke lainnya dari semua oran confirmShowRepliesAll: "Operasi ini tidak dapat diubah. Apakah kamu yakin untuk menampilkan balasan ke lainnya dari semua orang yang kamu ikuti di lini masa?" confirmHideRepliesAll: "Operasi ini tidak dapat diubah. Apakah kamu yakin untuk menyembunyikan balasan ke lainnya dari semua orang yang kamu ikuti di lini masa?" externalServices: "Layanan eksternal" +sourceCode: "Sumber kode" impressum: "Impressum" impressumUrl: "Tautan Impressum" impressumDescription: "Pada beberapa negara seperti Jerman, inklusi dari informasi kontak operator (sebuah Impressum) diperlukan secara legal untuk situs web komersil." @@ -1174,7 +1191,29 @@ doReaction: "Tambahkan reaksi" code: "Kode" reloadRequiredToApplySettings: "Muat ulang diperlukan untuk menerapkan pengaturan." remainingN: "Sisa : {n}" +overwriteContentConfirm: "Apakah kamu yakin untuk menimpa konten saat ini?" +seasonalScreenEffect: "Efek layar musiman" decorate: "Dekor" +addMfmFunction: "Tambahkan dekorasi" +enableQuickAddMfmFunction: "Tampilkan pemilih MFM tingkat lanjut" +bubbleGame: "Bubble Game" +sfx: "Efek Suara" +soundWillBePlayed: "Suara yang akan dimainkan" +showReplay: "Lihat tayangan ulang" +replay: "Tayangan ulang" +replaying: "Menayangkan Ulang" +ranking: "Peringkat" +lastNDays: "{n} hari terakhir" +backToTitle: "Ke Judul" +hemisphere: "Letak kamu tinggal" +withSensitive: "Lampirkan catatan dengan berkas sensitif" +userSaysSomethingSensitive: "Postingan oleh {name} mengandung konten sensitif" +enableHorizontalSwipe: "Geser untuk mengganti tab" +surrender: "Batalkan" +_bubbleGame: + howToPlay: "Cara bermain" + _howToPlay: + section1: "Atur posisi dan jatuhkan obyek ke dalam kotak." _announcement: forExistingUsers: "Hanya pengguna yang telah ada" forExistingUsersDescription: "Pengumuman ini akan dimunculkan ke pengguna yang sudah ada dari titik waktu publikasi jika dinyalakan. Apabila dimatikan, mereka yang baru mendaftar setelah publikasi ini akan juga melihatnya." @@ -1184,7 +1223,10 @@ _announcement: tooManyActiveAnnouncementDescription: "Terlalu banyak pengumuman dapat memperburuk pengalaman pengguna. Mohon pertimbangkan untuk mengarsipkan pengumuman yang sudah usang/tidak relevan." readConfirmTitle: "Tandai telah dibaca?" readConfirmText: "Aksi ini akan menandai konten dari \"{title}\" telah dibaca." + shouldNotBeUsedToPresentPermanentInfo: "Karena dapat berdampak pada pengalaman pengguna untuk pengguna baru, sangat direkomendasikan untuk menggunakan notifikasi secara mengalir daripada tetap." + dialogAnnouncementUxWarn: "Memiliki dua atau lebih gaya dialog notifikasi secara bersamaan dapat berdampak signifikan pada pengalaman pengguna, mohon untuk menggunakannya dengan hati-hati." silence: "Tiada notifikasi" + silenceDescription: "Apabila diaktifkan, notifikasi dari pengumuman ini akan dilewatkan dan pengguna tidak perlu membacanya." _initialAccountSetting: accountCreated: "Akun kamu telah sukses dibuat!" letsStartAccountSetup: "Untuk pemula, ayo atur profilmu dulu." @@ -1197,6 +1239,7 @@ _initialAccountSetting: pushNotificationDescription: "Menyalakan notifikasi dorong akan membuatmu menerima notifikasi dari {name} secara langsung ke perangkatmu." initialAccountSettingCompleted: "Pengaturan profil selesai!" haveFun: "Selamat menikmati, {name}!" + youCanContinueTutorial: "Kamu dapat menjutkan ke tutorial dalam bagaimana menggunakan {name} (Misskey) atau kamu dapat keluar dari pemasangan ini dan langsung menggunakannya segera." startTutorial: "Mulai Tutorial" skipAreYouSure: "Yakin melewati atur profil?" laterAreYouSure: "Yakin banget untuk atur profil nanti?" @@ -1210,6 +1253,10 @@ _initialTutorial: description: "Di sini kamu dapat mempelajari dasar-dasar dari penggunaan Misskey dan fitur-fiturnya." _note: title: "Apa itu Catatan?" + description: "Postingan di Misskey disebut sebagai 'Catatan'. Catatan ditampilkan secara kronologis pada lini masa dan dimutakhirkan secara real-time." + reply: "Klik pada tombol ini untuk membalas ke sebuah pesan. Bisa juga untuk membalas ke sebuah balasan dan melanjutkannya seperti percakapan selayaknya utas." + renote: "Kamu dapat membagikan catatan ke lini masa milikmu. Kamu juga dapat mengutipnya dengan komentarmu." + reaction: "Kamu dapat menambahkan reaksi ke Catatan. Detil lebih lanjut akan dijelaskan di halaman berikutnya." _reaction: title: "Apa itu Reaksi?" _timeline: @@ -1228,6 +1275,8 @@ _initialTutorial: note: "Baru aja makan donat berlapis coklat ðŸ©ðŸ˜‹" _howToMakeAttachmentsSensitive: title: "Bagaimana menandai lampiran sebagai sensitif?" + _done: + title: "Kamu telah menyelesaikan tutorial! 🎉" _serverRules: description: "Daftar peraturan akan ditampilkan sebelum pendaftaran. Mengatur ringkasan dari Syarat dan Ketentuan sangat direkomendasikan." _serverSettings: @@ -1774,6 +1823,14 @@ _sfx: notification: "Notifikasi" antenna: "Penerimaan Antenna" channel: "Notifikasi Kanal" + reaction: "Ketika memilih reaksi" +_soundSettings: + driveFile: "Menggunakan berkas audio dalam Drive" + driveFileWarn: "Pilih berkas audio dari Drive" + driveFileTypeWarn: "Berkas ini tidak didukung" + driveFileTypeWarnDescription: "Pilih berkas audio" + driveFileDurationWarn: "Audio ini terlalu panjang" + driveFileDurationWarnDescription: "Audio panjang dapat mengganggu penggunaan Misskey. Masih ingin melanjutkan?" _ago: future: "Masa depan" justNow: "Baru saja" @@ -1785,6 +1842,14 @@ _ago: monthsAgo: "{n} bulan lalu" yearsAgo: "{n} tahun lalu" invalid: "Tidak ada sama sekali disini" +_timeIn: + seconds: "dalam {n} detik" + minutes: "dalam {n} menit" + hours: "dalam {n} jam" + days: "dalam {n} hari" + weeks: "dalam {n} minggu" + months: "dalam {n} bulan" + years: "dalam {n} tahun" _time: second: "detik" minute: "menit" @@ -1856,6 +1921,55 @@ _permissions: "write:flash": "Sunting Play" "read:flash-likes": "Lihat daftar Play yang disukai" "write:flash-likes": "Sunting daftar Play yang disukai" + "read:admin:abuse-user-reports": "Lihat laporan pengguna" + "write:admin:delete-account": "Hapus akun pengguna" + "write:admin:delete-all-files-of-a-user": "Hapus semua berkas dari seorang pengguna" + "read:admin:index-stats": "Lihat statistik indeks basis data" + "read:admin:table-stats": "Lihat statistik tabel basis data" + "read:admin:user-ips": "Lihat alamat IP pengguna" + "read:admin:meta": "Lihat metadata instansi" + "write:admin:reset-password": "Atur ulang kata sandi pengguna" + "write:admin:resolve-abuse-user-report": "Selesaikan laporan pengguna" + "write:admin:send-email": "Mengirim surel" + "read:admin:server-info": "Lihat informasi peladen" + "read:admin:show-moderation-log": "Lihat log moderasi" + "read:admin:show-user": "Lihat informasi pengguna privat" + "read:admin:show-users": "Lihat informasi pengguna privat" + "write:admin:suspend-user": "Tangguhkan pengguna" + "write:admin:unset-user-avatar": "Hapus avatar pengguna" + "write:admin:unset-user-banner": "Hapus banner pengguna" + "write:admin:unsuspend-user": "Batalkan penangguhan pengguna" + "write:admin:meta": "Kelola metadata instansi" + "write:admin:user-note": "Kelola moderasi catatan" + "write:admin:roles": "Kelola peran" + "read:admin:roles": "Lihat peran" + "write:admin:relays": "Kelola relay" + "read:admin:relays": "Lihat relay" + "write:admin:invite-codes": "Kelola kode undangan" + "read:admin:invite-codes": "Lihat kode undangan" + "write:admin:announcements": "Kelola pengumuman" + "read:admin:announcements": "Lihat Pengumuman" + "write:admin:avatar-decorations": "Kelola dekorasi avatar" + "read:admin:avatar-decorations": "Lihat dekorasi avatar" + "write:admin:federation": "Kelola data federasi" + "write:admin:account": "Kelola akun pengguna" + "read:admin:account": "Lihat akun pengguna" + "write:admin:emoji": "Kelola emoji" + "read:admin:emoji": "Lihat emoji" + "write:admin:queue": "Kelola antrian kerja" + "read:admin:queue": "Lihat informasi antrian kerja" + "write:admin:promo": "Kelola catatan promosi" + "write:admin:drive": "Kelola drive pengguna" + "read:admin:drive": "Kelola informasi drive pengguna" + "read:admin:stream": "Gunakan API WebSocket untuk Admin" + "write:admin:ad": "Kelola iklan" + "read:admin:ad": "Lihat iklan" + "write:invite-codes": "Membuat kode undangan" + "read:invite-codes": "Mendapatkan kode undangan" + "write:clip-favorite": "Kelola klip yang difavoritkan" + "read:clip-favorite": "Lihat klip yang difavoritkan" + "read:federation": "Mendapatkan data federasi" + "write:report-abuse": "Melaporkan pelanggaran" _auth: shareAccessTitle: "Mendapatkan ijin akses aplikasi" shareAccess: "Apakah kamu ingin mengijinkan \"{name}\" untuk mengakses akun ini?" @@ -1910,6 +2024,7 @@ _widgets: _userList: chooseList: "Pilih daftar" clicker: "Pengeklik" + birthdayFollowings: "Pengguna yang merayakan hari ulang tahunnya hari ini" _cw: hide: "Sembunyikan" show: "Lihat konten" @@ -1972,9 +2087,11 @@ _profile: changeAvatar: "Ubah avatar" changeBanner: "Ubah header" verifiedLinkDescription: "Dengan memasukkan URL yang mengandung tautan ke profil kamu di sini, ikon verifikasi kepemilikan dapat ditampilkan di sebelah kolom ini." + avatarDecorationMax: "Dapat ditambahkan hingga {max} dekorasi." _exportOrImport: allNotes: "Semua catatan" favoritedNotes: "Catatan favorit" + clips: "Klip" followingList: "Ikuti" muteList: "Bisukan" blockingList: "Blokir" @@ -2093,12 +2210,16 @@ _notification: pollEnded: "Hasil Kuesioner telah keluar" newNote: "Catatan baru" unreadAntennaNote: "Antena {name}" + roleAssigned: "Peran Diberikan" emptyPushNotificationMessage: "Pembaruan notifikasi dorong" achievementEarned: "Pencapaian didapatkan" testNotification: "Tes notifikasi" checkNotificationBehavior: "Cek tampilan notifikasi" sendTestNotification: "Kirim tes notifikasi" notificationWillBeDisplayedLikeThis: "Notifikasi akan terlihat seperti ini" + reactedBySomeUsers: "{n} orang memberikan reaksi" + renotedBySomeUsers: "{n} orang telah merenote" + followedBySomeUsers: "{n} orang telah mengikuti" _types: all: "Semua" note: "Catatan baru" @@ -2111,6 +2232,7 @@ _notification: pollEnded: "Jajak pendapat berakhir" receiveFollowRequest: "Permintaan mengikuti diterima" followRequestAccepted: "Permintaan mengikuti disetujui" + roleAssigned: "Peran Diberikan" achievementEarned: "Pencapaian didapatkan" app: "Notifikasi dari aplikasi tertaut" _actions: @@ -2202,6 +2324,11 @@ _moderationLogTypes: createAd: "Iklan telah dibuat" deleteAd: "Iklan telah dihapus" updateAd: "Iklan telah diperbaharui" + createAvatarDecoration: "Buat dekorasi avatar" + updateAvatarDecoration: "Perbarui dekorasi avatar" + deleteAvatarDecoration: "Hapus dekorasi avatar" + unsetUserAvatar: "Hapus avatar pengguna" + unsetUserBanner: "Hapus banner pengguna" _fileViewer: title: "Rincian berkas" type: "Jenis berkas" @@ -2210,3 +2337,95 @@ _fileViewer: uploadedAt: "Diunggah pada" attachedNotes: "Catatan yang dilampirkan" thisPageCanBeSeenFromTheAuthor: "Halaman ini hanya dapat dilihat oleh pengguna yang mengunggah bekas ini." +_externalResourceInstaller: + title: "Pasang dari situs eksternal" + checkVendorBeforeInstall: "Pastikan sumber dari sumber daya ini terpercaya sebelum melakukan pemasangan." + _plugin: + title: "Apakah kamu ingin memasang plugin ini?" + metaTitle: "Informasi plugin" + _theme: + title: "Apakah kamu ingin memasang tema ini?" + metaTitle: "Informasi tema" + _meta: + base: "Skema warna dasar" + _vendorInfo: + title: "Informasi sumber" + endpoint: "Referensi Endpoint" + hashVerify: "Verifikasi hash" + _errors: + _invalidParams: + title: "Parameter tidak valid" + description: "Tidak cukup informasi untuk memuat data dari situs eksternal. Mohon konfirmasi kembali URL yang dimasukkan." + _resourceTypeNotSupported: + title: "Sumber daya eksternal ini tidak didukung" + description: "Tipe sumber daya eksternal ini tidak didukung. Mohon kontak administrator dari situs tersebut." + _failedToFetch: + title: "Gagal memuat data" + fetchErrorDescription: "Kesalahan terjadi ketika menghubungkan dengan situs eksternal. Jika percobaan kembali tidak dapat memperbaiki masalah ini, mohon hubungi administrator dari situs tersebut." + parseErrorDescription: "Kesalahan terjadi dalam memproses data yang dimuat dari situs eksternal. Mohon hubungi administrator dari situs tersebut." + _hashUnmatched: + title: "Verifikasi data gagal" + description: "Kesalahan terjadi dalam memverifikasi integritas data yang diambil. Sebagai pencegahan keamanan, pemasangan tidak dapat dilanjutkan. Mohon hubungi administrator dari situs tersebut." + _pluginParseFailed: + title: "Kesalahan AiScript" + description: "Data yang diminta telah diambil dengan sukses, namun kesalahan terjadi ketika AiScript melakukan parsing. Mohon hubungi pembuat plugin. Detil kesalahan dapat dilihat pada konsol Javascript." + _pluginInstallFailed: + title: "Pemasangan plugin gagal" + description: "Kesalahan terjadi ketika pemasangan plugin. Mohon coba lagi. Detil kesalahan dapat dilihat pada konsol Javascript." + _themeParseFailed: + title: "Parsing tema gagal" + description: "Data yang diminta telah diambil dengan sukses, namun kesalahan terjadi ketika tema melakukan parsing. Mohon hubungi pembuat tema. Detil kesalahan dapat dilihat pada konsol Javascript." + _themeInstallFailed: + title: "Pemasangan tema gagal" + description: "Kesalahan terjadi ketika pemasangan tema. Mohon coba lagi. Detil kesalahan dapat dilihat pada konsol Javascript." +_dataSaver: + _media: + title: "Memuat media" + description: "Mencegah gambar/video dimuat secara otomatis. Menyembunyikan gambar/video dan akan dimuat ketika diketuk." + _avatar: + title: "Gambar avatar" + description: "Hentikan animasi gambar avatar. Gambar animasi dapat berukuran lebih besar dari gambar biasa, berpotensi pada pengurangan lalu lintas data lebih jauh." + _urlPreview: + title: "Gambar kecil URL pratinjau" + description: "Gambar kecil URL pratinjau tidak akan dimuat lagi." + _code: + title: "Penyorotan kode" + description: "Jika notasi penyorotan kode digunakan di MFM, dll. Fungsi tersebut tidak akan dimuat apabila tidak diketuk. Penyorotan sintaks membutuhkan pengunduhan berkas definisi penyorotan untuk setiap bahasa pemrograman. Oleh sebab itu, menonaktifkan pemuatan otomatis dari berkas ini dilakukan untuk mengurangi jumlah komunikasi data." +_hemisphere: + N: "Bumi belahan utara" + S: "Bumi belahan selatan" + caption: "Digunakan dalam beberapa pengaturan klien untuk menentukan musim." +_reversi: + reversi: "Reversi" + gameSettings: "Pengaturan permainan" + chooseBoard: "Pilih papan" + blackOrWhite: "Hitam/Putih" + blackIs: "{name} bermain sebagai Hitam" + rules: "Aturan" + thisGameIsStartedSoon: "Permainan akan segera dimulai" + waitingForOther: "Menunggu langkah giliran dari lawan" + waitingForMe: "Menungguh langkah giliran dari kamu" + waitingBoth: "Bersiap" + ready: "Siap" + cancelReady: "Belum siap" + opponentTurn: "Giliran lawan" + myTurn: "Giliran kamu" + turnOf: "Giliran {name}" + pastTurnOf: "Giliran {name}" + surrender: "Menyerah" + surrendered: "Telah menyerah" + timeout: "Waktu habis" + drawn: "Seri" + won: "{name} menang" + black: "Hitam" + white: "Putih" + total: "Jumlah" + turnCount: "Langkah ke {count}" + myGames: "Rondeku" + allGames: "Semua ronde" + ended: "Selesai" + playing: "Sedang bermain" + isLlotheo: "Pemain dengan batu yang sedikit menang (Llotheo)" + loopedMap: "Peta melingkar" + canPutEverywhere: "Keping dapat ditaruh dimana saja" + diff --git a/locales/index.d.ts b/locales/index.d.ts index dd2f34a69a3fc5bccd2570d8e0a96d22216c7a25..e407d2119bb9e1de5a532a3c6ac586c98e99921c 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1,2653 +1,10052 @@ /* eslint-disable */ // This file is generated by locales/generateDTS.js // Do not edit this file directly. -export interface Locale { +declare const kParameters: unique symbol; +export interface ParameterizedString<T extends string = string> { + [kParameters]: T; +} +export interface ILocale { + [_: string]: string | ParameterizedString | ILocale; +} +export interface Locale extends ILocale { + /** + * 日本語 + */ "_lang_": string; + /** + * ノートã§ã¤ãªãŒã‚‹ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ + */ "headlineMisskey": string; + /** + * よã†ã“ãï¼Sharkeyã¯ã€ã‚ªãƒ¼ãƒ—ンソースã®åˆ†æ•£åž‹ãƒžã‚¤ã‚¯ãƒãƒ–ãƒã‚°ã‚µãƒ¼ãƒ“スã§ã™ã€‚ + * 「ノートã€ã‚’作æˆã—ã¦ã€ã„ã¾èµ·ã“ã£ã¦ã„ã‚‹ã“ã¨ã‚’共有ã—ãŸã‚Šã€ã‚ãªãŸã«ã¤ã„ã¦çš†ã«ç™ºä¿¡ã—よã†ðŸ“¡ + * 「リアクションã€æ©Ÿèƒ½ã§ã€çš†ã®ãƒŽãƒ¼ãƒˆã«ç´ æ—©ãåå¿œã‚’è¿½åŠ ã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ðŸ‘ + * æ–°ã—ã„世界を探検ã—よã†ðŸš€ + */ "introMisskey": string; - "poweredByMisskeyDescription": string; - "monthAndDay": string; + /** + * {name}ã¯ã€ã‚ªãƒ¼ãƒ—ンソースã®ãƒ—ラットフォーム<b>Sharkey</b>ã®ã‚µãƒ¼ãƒãƒ¼ã®ã²ã¨ã¤ã§ã™ã€‚ + */ + "poweredByMisskeyDescription": ParameterizedString<"name">; + /** + * {month}月 {day}æ—¥ + */ + "monthAndDay": ParameterizedString<"month" | "day">; + /** + * 検索 + */ "search": string; + /** + * 通知 + */ "notifications": string; + /** + * ユーザーå + */ "username": string; + /** + * パスワード + */ "password": string; + /** + * パスワードを忘れ㟠+ */ "forgotPassword": string; + /** + * 連åˆã«ç…§ä¼šä¸ + */ "fetchingAsApObject": string; + /** + * OK + */ "ok": string; + /** + * ã‚ã‹ã£ãŸ + */ "gotIt": string; + /** + * ã‚ャンセル + */ "cancel": string; + /** + * ã‚„ã‚ã¦ãŠã + */ "noThankYou": string; + /** + * ユーザーåを入力 + */ "enterUsername": string; - "renotedBy": string; + /** + * {user}ãŒãƒ–ースト + */ + "renotedBy": ParameterizedString<"user">; + /** + * ノートã¯ã‚ã‚Šã¾ã›ã‚“ + */ "noNotes": string; + /** + * 通知ã¯ã‚ã‚Šã¾ã›ã‚“ + */ "noNotifications": string; + /** + * サーãƒãƒ¼ + */ "instance": string; + /** + * è¨å®š + */ "settings": string; + /** + * 通知ã®è¨å®š + */ "notificationSettings": string; + /** + * 基本è¨å®š + */ "basicSettings": string; + /** + * ãã®ä»–ã®è¨å®š + */ "otherSettings": string; + /** + * ウィンドウã§é–‹ã + */ "openInWindow": string; + /** + * プãƒãƒ•ã‚£ãƒ¼ãƒ« + */ "profile": string; + /** + * タイムライン + */ "timeline": string; + /** + * 自己紹介ã¯ã‚ã‚Šã¾ã›ã‚“ + */ "noAccountDescription": string; + /** + * ãƒã‚°ã‚¤ãƒ³ + */ "login": string; + /** + * ãƒã‚°ã‚¤ãƒ³ä¸ + */ "loggingIn": string; + /** + * ãƒã‚°ã‚¢ã‚¦ãƒˆ + */ "logout": string; + /** + * æ–°è¦ç™»éŒ² + */ "signup": string; + /** + * アップãƒãƒ¼ãƒ‰ä¸ + */ "uploading": string; + /** + * ä¿å˜ + */ "save": string; + /** + * ユーザー + */ "users": string; + /** + * æ‰¿èª + */ "approvals": string; + /** + * ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’è¿½åŠ + */ "addUser": string; + /** + * ãŠæ°—ã«å…¥ã‚Š + */ "favorite": string; + /** + * ãŠæ°—ã«å…¥ã‚Š + */ "favorites": string; + /** + * ãŠæ°—ã«å…¥ã‚Šè§£é™¤ + */ "unfavorite": string; + /** + * ãŠæ°—ã«å…¥ã‚Šã«ç™»éŒ²ã—ã¾ã—ãŸã€‚ + */ "favorited": string; + /** + * æ—¢ã«ãŠæ°—ã«å…¥ã‚Šã«ç™»éŒ²ã•ã‚Œã¦ã„ã¾ã™ã€‚ + */ "alreadyFavorited": string; + /** + * ãŠæ°—ã«å…¥ã‚Šã«ç™»éŒ²ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ + */ "cantFavorite": string; + /** + * ピン留゠+ */ "pin": string; + /** + * ピン留ã‚解除 + */ "unpin": string; + /** + * 内容をコピー + */ "copyContent": string; + /** + * リンクをコピー + */ "copyLink": string; + /** + * ブーストã®ãƒªãƒ³ã‚¯ã‚’コピー + */ "copyLinkRenote": string; + /** + * 削除 + */ "delete": string; + /** + * 削除ã—ã¦ç·¨é›† + */ "deleteAndEdit": string; + /** + * ã“ã®ãƒŽãƒ¼ãƒˆã‚’削除ã—ã¦ã‚‚ã†ä¸€åº¦ç·¨é›†ã—ã¾ã™ã‹ï¼Ÿã“ã®ãƒŽãƒ¼ãƒˆã¸ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã€ãƒ–ーストã€è¿”ä¿¡ã‚‚å…¨ã¦å‰Šé™¤ã•ã‚Œã¾ã™ã€‚ + */ "deleteAndEditConfirm": string; + /** + * リストã«è¿½åŠ + */ "addToList": string; + /** + * アンテナã«è¿½åŠ + */ "addToAntenna": string; + /** + * メッセージをé€ä¿¡ + */ "sendMessage": string; + /** + * RSSをコピー + */ "copyRSS": string; + /** + * ユーザーåをコピー + */ "copyUsername": string; + /** + * リモートプãƒãƒ•ã‚£ãƒ¼ãƒ«ã‚’é–‹ã + */ "openRemoteProfile": string; + /** + * ユーザーIDをコピー + */ "copyUserId": string; + /** + * ノートIDをコピー + */ "copyNoteId": string; + /** + * ファイルIDをコピー + */ "copyFileId": string; + /** + * フォルダーIDをコピー + */ "copyFolderId": string; + /** + * プãƒãƒ•ã‚£ãƒ¼ãƒ«URLをコピー + */ "copyProfileUrl": string; + /** + * ユーザーを検索 + */ "searchUser": string; + /** + * 返信 + */ "reply": string; + /** + * ã‚‚ã£ã¨è¦‹ã‚‹ + */ "loadMore": string; + /** + * ã‚‚ã£ã¨è¦‹ã‚‹ + */ "showMore": string; + /** + * é–‰ã˜ã‚‹ + */ "showLess": string; + /** + * フォãƒãƒ¼ã•ã‚Œã¾ã—㟠+ */ "youGotNewFollower": string; + /** + * フォãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã•ã‚Œã¾ã—㟠+ */ "receiveFollowRequest": string; + /** + * フォãƒãƒ¼ãŒæ‰¿èªã•ã‚Œã¾ã—㟠+ */ "followRequestAccepted": string; + /** + * メンション + */ "mention": string; + /** + * ã‚ãªãŸå®›ã¦ + */ "mentions": string; + /** + * ダイレクト投稿 + */ "directNotes": string; + /** + * インãƒãƒ¼ãƒˆã¨ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ + */ "importAndExport": string; + /** + * インãƒãƒ¼ãƒˆ + */ "import": string; + /** + * エクスãƒãƒ¼ãƒˆ + */ "export": string; + /** + * ファイル + */ "files": string; + /** + * ダウンãƒãƒ¼ãƒ‰ + */ "download": string; - "driveFileDeleteConfirm": string; - "unfollowConfirm": string; + /** + * ファイル「{name}ã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿã“ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’使用ã—ãŸä¸€éƒ¨ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„も削除ã•ã‚Œã¾ã™ã€‚ + */ + "driveFileDeleteConfirm": ParameterizedString<"name">; + /** + * {name}ã®ãƒ•ã‚©ãƒãƒ¼ã‚’解除ã—ã¾ã™ã‹ï¼Ÿ + */ + "unfollowConfirm": ParameterizedString<"name">; + /** + * エクスãƒãƒ¼ãƒˆã‚’リクエストã—ã¾ã—ãŸã€‚ã“ã‚Œã«ã¯æ™‚é–“ãŒã‹ã‹ã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ã€‚エクスãƒãƒ¼ãƒˆãŒçµ‚ã‚ã‚‹ã¨ã€ã€Œãƒ‰ãƒ©ã‚¤ãƒ–ã€ã«è¿½åŠ ã•ã‚Œã¾ã™ã€‚ + */ "exportRequested": string; + /** + * インãƒãƒ¼ãƒˆã‚’リクエストã—ã¾ã—ãŸã€‚ã“ã‚Œã«ã¯æ™‚é–“ãŒã‹ã‹ã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ã€‚ + */ "importRequested": string; + /** + * リスト + */ "lists": string; + /** + * リストã¯ã‚ã‚Šã¾ã›ã‚“ + */ "noLists": string; + /** + * ノート + */ "note": string; + /** + * ノート + */ "notes": string; + /** + * フォãƒãƒ¼ + */ "following": string; + /** + * フォãƒãƒ¯ãƒ¼ + */ "followers": string; + /** + * フォãƒãƒ¼ã•ã‚Œã¦ã„ã¾ã™ + */ "followsYou": string; + /** + * ãƒªã‚¹ãƒˆä½œæˆ + */ "createList": string; + /** + * リストã®ç®¡ç† + */ "manageLists": string; + /** + * エラー + */ "error": string; + /** + * å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—㟠+ */ "somethingHappened": string; + /** + * å†è©¦è¡Œ + */ "retry": string; + /** + * ページã®èªã¿è¾¼ã¿ã«å¤±æ•—ã—ã¾ã—ãŸã€‚ + */ "pageLoadError": string; + /** + * ã“ã‚Œã¯é€šå¸¸ã€ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã¾ãŸã¯ãƒ–ラウザã‚ャッシュãŒåŽŸå› ã§ã™ã€‚ã‚ャッシュをクリアã™ã‚‹ã‹ã€ã—ã°ã‚‰ãå¾…ã£ã¦ã‹ã‚‰å†åº¦è©¦ã—ã¦ãã ã•ã„。 + */ "pageLoadErrorDescription": string; + /** + * サーãƒãƒ¼ã®å¿œç”ãŒã‚ã‚Šã¾ã›ã‚“。ã—ã°ã‚‰ãå¾…ã£ã¦ã‹ã‚‰å†åº¦è©¦ã—ã¦ãã ã•ã„。 + */ "serverIsDead": string; + /** + * ã“ã®ãƒšãƒ¼ã‚¸ã‚’表示ã™ã‚‹ãŸã‚ã«ã¯ã€ãƒªãƒãƒ¼ãƒ‰ã—ã¦æ–°ã—ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆã‚’ã”利用ãã ã•ã„。 + */ "youShouldUpgradeClient": string; + /** + * リストåを入力 + */ "enterListName": string; + /** + * プライãƒã‚·ãƒ¼ + */ "privacy": string; + /** + * フォãƒãƒ¼ã‚’承èªåˆ¶ã«ã™ã‚‹ + */ "makeFollowManuallyApprove": string; + /** + * デフォルトã®å…¬é–‹ç¯„囲 + */ "defaultNoteVisibility": string; + /** + * フォãƒãƒ¼ + */ "follow": string; + /** + * フォãƒãƒ¼ç”³è«‹ + */ "followRequest": string; + /** + * フォãƒãƒ¼ç”³è«‹ + */ "followRequests": string; + /** + * フォãƒãƒ¼è§£é™¤ + */ "unfollow": string; + /** + * フォãƒãƒ¼è¨±å¯å¾…ã¡ + */ "followRequestPending": string; + /** + * 絵文å—を入力 + */ "enterEmoji": string; + /** + * ブースト + */ "renote": string; + /** + * ブースト解除 + */ "unrenote": string; + /** + * ブーストã—ã¾ã—ãŸã€‚ + */ "renoted": string; + /** + * 引用。 + */ "quoted": string; + /** + * ブースト解除ã—ã¾ã—ãŸã€‚ + */ "rmboost": string; + /** + * ã“ã®æŠ•ç¨¿ã¯ãƒ–ーストã§ãã¾ã›ã‚“。 + */ "cantRenote": string; + /** + * ブーストをブーストã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。 + */ "cantReRenote": string; + /** + * 引用 + */ "quote": string; + /** + * ãƒãƒ£ãƒ³ãƒãƒ«å†…ブースト + */ "inChannelRenote": string; + /** + * ãƒãƒ£ãƒ³ãƒãƒ«å†…引用 + */ "inChannelQuote": string; + /** + * ピン留ã‚ã•ã‚ŒãŸãƒŽãƒ¼ãƒˆ + */ "pinnedNote": string; + /** + * ピン留゠+ */ "pinned": string; + /** + * ã‚ãªãŸ + */ "you": string; + /** + * クリックã—ã¦è¡¨ç¤º + */ "clickToShow": string; + /** + * センシティブ + */ "sensitive": string; + /** + * è¿½åŠ + */ "add": string; + /** + * リアクション + */ "reaction": string; + /** + * リアクション + */ "reactions": string; + /** + * 絵文å—ピッカー + */ "emojiPicker": string; + /** + * リアクション時ã«ãƒ”ン留ã‚表示ã™ã‚‹çµµæ–‡å—ã‚’è¨å®šã§ãã¾ã™ + */ "pinnedEmojisForReactionSettingDescription": string; + /** + * 絵文å—入力時ã«ãƒ”ン留ã‚表示ã™ã‚‹çµµæ–‡å—ã‚’è¨å®šã§ãã¾ã™ + */ "pinnedEmojisSettingDescription": string; + /** + * ピッカーã®è¡¨ç¤º + */ "emojiPickerDisplay": string; + /** + * リアクションè¨å®šã‹ã‚‰ä¸Šæ›¸ãã™ã‚‹ + */ "overwriteFromPinnedEmojisForReaction": string; + /** + * 全般è¨å®šã‹ã‚‰ä¸Šæ›¸ãã™ã‚‹ + */ "overwriteFromPinnedEmojis": string; + /** + * ドラッグã—ã¦ä¸¦ã³æ›¿ãˆã€ã‚¯ãƒªãƒƒã‚¯ã—ã¦å‰Šé™¤ã€ï¼‹ã‚’押ã—ã¦è¿½åŠ ã—ã¾ã™ã€‚ + */ "reactionSettingDescription2": string; + /** + * 公開範囲を記憶ã™ã‚‹ + */ "rememberNoteVisibility": string; + /** + * 添付å–り消㗠+ */ "attachCancel": string; + /** + * ファイルを削除 + */ + "deleteFile": string; + /** + * センシティブã¨ã—ã¦è¨å®š + */ "markAsSensitive": string; + /** + * センシティブを解除ã™ã‚‹ + */ "unmarkAsSensitive": string; + /** + * ファイルåを入力 + */ "enterFileName": string; + /** + * ミュート + */ "mute": string; + /** + * ミュート解除 + */ "unmute": string; + /** + * ブーストをミュート + */ "renoteMute": string; + /** + * ブーストã®ãƒŸãƒ¥ãƒ¼ãƒˆã‚’解除 + */ "renoteUnmute": string; + /** + * ブãƒãƒƒã‚¯ + */ "block": string; + /** + * ブãƒãƒƒã‚¯è§£é™¤ + */ "unblock": string; + /** + * ユーザーã®ã™ã¹ã¦ã®ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’NSFWã¨ã—ã¦ãƒžãƒ¼ã‚¯ã™ã‚‹ + */ "markAsNSFW": string; + /** + * å‡çµ + */ "suspend": string; + /** + * è§£å‡ + */ "unsuspend": string; + /** + * ブãƒãƒƒã‚¯ã—ã¾ã™ã‹ï¼Ÿ + */ "blockConfirm": string; + /** + * ブãƒãƒƒã‚¯è§£é™¤ã—ã¾ã™ã‹ï¼Ÿ + */ "unblockConfirm": string; + /** + * ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã®ã™ã¹ã¦ã®ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’NSFWã¨ã—ã¦ãƒžãƒ¼ã‚¯ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ + */ "nsfwConfirm": string; + /** + * ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ã™ã¹ã¦ã®ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’NSFWã¨ã—ã¦ãƒžãƒ¼ã‚¯è§£é™¤ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ + */ "unNsfwConfirm": string; + /** + * å‡çµã—ã¾ã™ã‹ï¼Ÿ + */ "suspendConfirm": string; + /** + * ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’承èªã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ + */ "approveConfirm": string; + /** + * 解å‡ã—ã¾ã™ã‹ï¼Ÿ + */ "unsuspendConfirm": string; + /** + * リストをé¸æŠž + */ "selectList": string; + /** + * リストを編集 + */ "editList": string; + /** + * ãƒãƒ£ãƒ³ãƒãƒ«ã‚’é¸æŠž + */ "selectChannel": string; + /** + * アンテナをé¸æŠž + */ "selectAntenna": string; + /** + * アンテナを編集 + */ "editAntenna": string; + /** + * ウィジェットをé¸æŠž + */ "selectWidget": string; + /** + * ウィジェットを編集 + */ "editWidgets": string; + /** + * 編集を終了 + */ "editWidgetsExit": string; + /** + * ã‚«ã‚¹ã‚¿ãƒ çµµæ–‡å— + */ "customEmojis": string; + /** + * çµµæ–‡å— + */ "emoji": string; + /** + * çµµæ–‡å— + */ "emojis": string; + /** + * 絵文å—å + */ "emojiName": string; + /** + * 絵文å—ç”»åƒURL + */ "emojiUrl": string; + /** + * 絵文å—ã‚’è¿½åŠ + */ "addEmoji": string; + /** + * ãŠã™ã™ã‚è¨å®š + */ "settingGuide": string; + /** + * リモートã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’ã‚ャッシュã™ã‚‹ + */ "cacheRemoteFiles": string; + /** + * ã“ã®è¨å®šã‚’有効ã«ã™ã‚‹ã¨ã€ãƒªãƒ¢ãƒ¼ãƒˆãƒ•ã‚¡ã‚¤ãƒ«ã‚’ã“ã®ã‚µãƒ¼ãƒãƒ¼ã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã«ã‚ャッシュã™ã‚‹ã‚ˆã†ã«ãªã‚Šã¾ã™ã€‚ç”»åƒã®è¡¨ç¤ºãŒé«˜é€Ÿã«ãªã‚Šã¾ã™ãŒã€ã‚µãƒ¼ãƒãƒ¼ã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã‚’多ã消費ã—ã¾ã™ã€‚リモートユーザーãŒã©ã‚Œã»ã©ã‚ャッシュをä¿æŒã™ã‚‹ã‹ã¯ã€ãƒãƒ¼ãƒ«ã«ã‚ˆã‚‹ãƒ‰ãƒ©ã‚¤ãƒ–容é‡åˆ¶é™ã«ã‚ˆã£ã¦æ±ºå®šã•ã‚Œã¾ã™ã€‚ã“ã®åˆ¶é™ã‚’超ãˆãŸå ´åˆã€å¤ã„ファイルã‹ã‚‰ã‚ャッシュãŒå‰Šé™¤ã•ã‚Œãƒªãƒ³ã‚¯ã«ãªã‚Šã¾ã™ã€‚ã“ã®è¨å®šãŒç„¡åŠ¹ã®å ´åˆã€ãƒªãƒ¢ãƒ¼ãƒˆã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’最åˆã‹ã‚‰ãƒªãƒ³ã‚¯ã¨ã—ã¦ä¿æŒã—ã¾ã™ãŒã€ç”»åƒã®ã‚µãƒ ãƒã‚¤ãƒ«ç”Ÿæˆã‚„ユーザーã®ãƒ—ライãƒã‚·ãƒ¼ä¿è·ã®ãŸã‚ã«ã€default.ymlã§proxyRemoteFilesã‚’trueã«ã™ã‚‹ã“ã¨ã‚’ãŠå‹§ã‚ã—ã¾ã™ã€‚ + */ "cacheRemoteFilesDescription": string; + /** + * ファイル管ç†ã®ðŸ—‘ï¸ãƒœã‚¿ãƒ³ã§å…¨ã¦ã®ã‚ャッシュを削除ã§ãã¾ã™ã€‚ + */ "youCanCleanRemoteFilesCache": string; + /** + * リモートã®ã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ãªãƒ•ã‚¡ã‚¤ãƒ«ã‚’ã‚ャッシュã™ã‚‹ + */ "cacheRemoteSensitiveFiles": string; + /** + * ã“ã®è¨å®šã‚’無効ã«ã™ã‚‹ã¨ã€ãƒªãƒ¢ãƒ¼ãƒˆã®ã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ãªãƒ•ã‚¡ã‚¤ãƒ«ã¯ã‚ャッシュã›ãšç›´ãƒªãƒ³ã‚¯ã™ã‚‹ã‚ˆã†ã«ãªã‚Šã¾ã™ã€‚ + */ "cacheRemoteSensitiveFilesDescription": string; + /** + * Botã¨ã—ã¦è¨å®š + */ "flagAsBot": string; + /** + * ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒãƒ—ãƒã‚°ãƒ©ãƒ ã«ã‚ˆã£ã¦é‹ç”¨ã•ã‚Œã‚‹å ´åˆã¯ã€ã“ã®ãƒ•ãƒ©ã‚°ã‚’オンã«ã—ã¾ã™ã€‚オンã«ã™ã‚‹ã¨ã€åå¿œã®é€£éŽ–を防ããŸã‚ã®ãƒ•ãƒ©ã‚°ã¨ã—ã¦ä»–ã®é–‹ç™ºè€…ã«å½¹ç«‹ã£ãŸã‚Šã€Sharkeyã®ã‚·ã‚¹ãƒ†ãƒ 上ã§ã®æ‰±ã„ãŒBotã«åˆã£ãŸã‚‚ã®ã«ãªã‚Šã¾ã™ã€‚ + */ "flagAsBotDescription": string; + /** + * ã«ã‚ƒã‚ã‚ã‚ã‚ã‚ã‚ã‚ã‚ã‚ã‚ã‚ã‚ã‚ã‚ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ + */ "flagAsCat": string; + /** + * ã«ã‚ƒã«ã‚ƒã«ã‚ƒï¼Ÿï¼Ÿ + */ "flagAsCatDescription": string; + /** + * 猫語ã§è©±ã™ + */ "flagSpeakAsCat": string; + /** + * 有効ã«ã™ã‚‹ã¨ã€ã‚ãªãŸã®æŠ•ç¨¿ã® 「ãªã€ã‚’「ã«ã‚ƒã€ã«ã—ã¾ã™ã€‚ + */ "flagSpeakAsCatDescription": string; + /** + * タイムラインã«ãƒŽãƒ¼ãƒˆã¸ã®è¿”信を表示ã™ã‚‹ + */ "flagShowTimelineReplies": string; + /** + * オンã«ã™ã‚‹ã¨ã€ã‚¿ã‚¤ãƒ ラインã«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆä»¥å¤–ã«ã‚‚ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ä»–ã®ãƒŽãƒ¼ãƒˆã¸ã®è¿”信を表示ã—ã¾ã™ã€‚ + */ "flagShowTimelineRepliesDescription": string; + /** + * フォãƒãƒ¼ä¸ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒªã‚¯ã‚’è‡ªå‹•æ‰¿èª + */ "autoAcceptFollowed": string; + /** + * ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’è¿½åŠ + */ "addAccount": string; + /** + * アカウントリストã®æƒ…å ±ã‚’æ›´æ–° + */ "reloadAccountsList": string; + /** + * ãƒã‚°ã‚¤ãƒ³ã«å¤±æ•—ã—ã¾ã—㟠+ */ "loginFailed": string; + /** + * リモートã§è¡¨ç¤º + */ "showOnRemote": string; + /** + * 全般 + */ "general": string; + /** + * å£ç´™ + */ "wallpaper": string; + /** + * å£ç´™ã‚’è¨å®š + */ "setWallpaper": string; + /** + * å£ç´™ã‚’削除 + */ "removeWallpaper": string; - "searchWith": string; + /** + * 検索: {q} + */ + "searchWith": ParameterizedString<"q">; + /** + * リストãŒã‚ã‚Šã¾ã›ã‚“ + */ "youHaveNoLists": string; - "followConfirm": string; + /** + * {name}をフォãƒãƒ¼ã—ã¾ã™ã‹ï¼Ÿ + */ + "followConfirm": ParameterizedString<"name">; + /** + * プãƒã‚シアカウント + */ "proxyAccount": string; + /** + * プãƒã‚シアカウントã¯ã€ç‰¹å®šã®æ¡ä»¶ä¸‹ã§ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒªãƒ¢ãƒ¼ãƒˆãƒ•ã‚©ãƒãƒ¼ã‚’代行ã™ã‚‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã™ã€‚例ãˆã°ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒãƒªãƒ¢ãƒ¼ãƒˆãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’リストã«å…¥ã‚ŒãŸã¨ãã€ãƒªã‚¹ãƒˆã«å…¥ã‚Œã‚‰ã‚ŒãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’誰もフォãƒãƒ¼ã—ã¦ã„ãªã„ã¨ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティãŒã‚µãƒ¼ãƒãƒ¼ã«é…é”ã•ã‚Œãªã„ãŸã‚ã€ä»£ã‚ã‚Šã«ãƒ—ãƒã‚シアカウントãŒãƒ•ã‚©ãƒãƒ¼ã™ã‚‹ã‚ˆã†ã«ã—ã¾ã™ã€‚ + */ "proxyAccountDescription": string; + /** + * ホスト + */ "host": string; + /** + * ユーザーをé¸æŠž + */ "selectUser": string; + /** + * 宛先 + */ "recipient": string; + /** + * 注釈 + */ "annotation": string; + /** + * é€£åˆ + */ "federation": string; + /** + * サーãƒãƒ¼ + */ "instances": string; + /** + * åˆè¦³æ¸¬ + */ "registeredAt": string; + /** + * ç›´è¿‘ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆå—ä¿¡ + */ "latestRequestReceivedAt": string; + /** + * ç›´è¿‘ã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ + */ "latestStatus": string; + /** + * ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ä½¿ç”¨é‡ + */ "storageUsage": string; + /** + * ãƒãƒ£ãƒ¼ãƒˆ + */ "charts": string; + /** + * 1時間ã”㨠+ */ "perHour": string; + /** + * 1æ—¥ã”㨠+ */ "perDay": string; + /** + * アクティビティã®é…é€ã‚’åœæ¢ + */ "stopActivityDelivery": string; + /** + * ã“ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã‚’ブãƒãƒƒã‚¯ + */ "blockThisInstance": string; + /** + * インスタンスをサイレンス + */ "silenceThisInstance": string; + /** + * æ“作 + */ "operations": string; + /** + * ソフトウェア + */ "software": string; + /** + * ãƒãƒ¼ã‚¸ãƒ§ãƒ³ + */ "version": string; + /** + * メタデータ + */ "metadata": string; - "withNFiles": string; + /** + * {n}ã¤ã®ãƒ•ã‚¡ã‚¤ãƒ« + */ + "withNFiles": ParameterizedString<"n">; + /** + * モニター + */ "monitor": string; + /** + * ジョブã‚ュー + */ "jobQueue": string; + /** + * CPUã¨ãƒ¡ãƒ¢ãƒª + */ "cpuAndMemory": string; + /** + * ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ + */ "network": string; + /** + * ディスク + */ "disk": string; + /** + * サーãƒãƒ¼æƒ…å ± + */ "instanceInfo": string; + /** + * 統計 + */ "statistics": string; + /** + * ã‚ューをクリア + */ "clearQueue": string; + /** + * ã‚ューをクリアã—ã¾ã™ã‹ï¼Ÿ + */ "clearQueueConfirmTitle": string; + /** + * 未é…é”ã®æŠ•ç¨¿ã¯é…é€ã•ã‚Œãªããªã‚Šã¾ã™ã€‚通常ã“ã®æ“作を行ã†å¿…è¦ã¯ã‚ã‚Šã¾ã›ã‚“。 + */ "clearQueueConfirmText": string; + /** + * ã‚ャッシュをクリア + */ "clearCachedFiles": string; + /** + * ã‚ャッシュã•ã‚ŒãŸãƒªãƒ¢ãƒ¼ãƒˆãƒ•ã‚¡ã‚¤ãƒ«ã‚’ã™ã¹ã¦å‰Šé™¤ã—ã¾ã™ã‹ï¼Ÿ + */ "clearCachedFilesConfirm": string; + /** + * ブãƒãƒƒã‚¯ã—ãŸã‚µãƒ¼ãƒãƒ¼ + */ "blockedInstances": string; + /** + * ブãƒãƒƒã‚¯ã—ãŸã„サーãƒãƒ¼ã®ãƒ›ã‚¹ãƒˆã‚’改行ã§åŒºåˆ‡ã£ã¦è¨å®šã—ã¾ã™ã€‚ブãƒãƒƒã‚¯ã•ã‚ŒãŸã‚µãƒ¼ãƒãƒ¼ã¯ã€ã“ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã¨ã‚„ã‚Šå–ã‚Šã§ããªããªã‚Šã¾ã™ã€‚ + */ "blockedInstancesDescription": string; + /** + * サイレンスã—ãŸã‚µãƒ¼ãƒãƒ¼ + */ "silencedInstances": string; + /** + * サイレンスã—ãŸã„インスタンスã®ãƒ›ã‚¹ãƒˆã‚’改行ã§åŒºåˆ‡ã£ã¦è¨å®šã—ã¾ã™ã€‚サイレンスã•ã‚ŒãŸã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã«æ‰€å±žã™ã‚‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ã™ã¹ã¦ã€Œã‚µã‚¤ãƒ¬ãƒ³ã‚¹ã€ã¨ã—ã¦æ‰±ã‚ã‚Œã€ãƒ•ã‚©ãƒãƒ¼ãŒã™ã¹ã¦ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«ãªã‚Šã€ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã§ãªã„ãƒãƒ¼ã‚«ãƒ«ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ã¯ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ã§ããªããªã‚Šã¾ã™ã€‚ブãƒãƒƒã‚¯ã—ãŸã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã«ã¯å½±éŸ¿ã—ã¾ã›ã‚“。 + */ "silencedInstancesDescription": string; + /** + * ミュートã¨ãƒ–ãƒãƒƒã‚¯ + */ "muteAndBlock": string; + /** + * ミュートã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ + */ "mutedUsers": string; + /** + * ブãƒãƒƒã‚¯ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ + */ "blockedUsers": string; + /** + * ユーザーã¯ã„ã¾ã›ã‚“ + */ "noUsers": string; + /** + * プãƒãƒ•ã‚£ãƒ¼ãƒ«ã‚’編集 + */ "editProfile": string; + /** + * ã“ã®ãƒŽãƒ¼ãƒˆã‚’削除ã—ã¾ã™ã‹ï¼Ÿ + */ "noteDeleteConfirm": string; + /** + * ã“れ以上ピン留ã‚ã§ãã¾ã›ã‚“ + */ "pinLimitExceeded": string; + /** + * Sharkeyã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ãŒå®Œäº†ã—ã¾ã—ãŸï¼ç®¡ç†è€…アカウントを作æˆã—ã¾ã—ょã†ã€‚ + */ "intro": string; + /** + * 完了 + */ "done": string; + /** + * 処ç†ä¸ + */ "processing": string; + /** + * プレビュー + */ "preview": string; + /** + * デフォルト + */ "default": string; - "defaultValueIs": string; + /** + * デフォルト: {value} + */ + "defaultValueIs": ParameterizedString<"value">; + /** + * 絵文å—ã¯ã‚ã‚Šã¾ã›ã‚“ + */ "noCustomEmojis": string; + /** + * ジョブã¯ã‚ã‚Šã¾ã›ã‚“ + */ "noJobs": string; + /** + * 連åˆä¸ + */ "federating": string; + /** + * ブãƒãƒƒã‚¯ä¸ + */ "blocked": string; + /** + * é…ä¿¡åœæ¢ + */ "suspended": string; + /** + * 全㦠+ */ "all": string; + /** + * è³¼èªä¸ + */ "subscribing": string; + /** + * é…ä¿¡ä¸ + */ "publishing": string; + /** + * å¿œç”ãªã— + */ "notResponding": string; + /** + * サーãƒãƒ¼ã®ãƒ•ã‚©ãƒãƒ¼ + */ "instanceFollowing": string; + /** + * サーãƒãƒ¼ã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼ + */ "instanceFollowers": string; + /** + * サーãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ + */ "instanceUsers": string; + /** + * パスワードを変更 + */ "changePassword": string; + /** + * ã‚»ã‚ュリティ + */ "security": string; + /** + * 入力ãŒä¸€è‡´ã—ã¾ã›ã‚“。 + */ "retypedNotMatch": string; + /** + * ç¾åœ¨ã®ãƒ‘スワード + */ "currentPassword": string; + /** + * æ–°ã—ã„パスワード + */ "newPassword": string; + /** + * æ–°ã—ã„パスワード(å†å…¥åŠ›) + */ "newPasswordRetype": string; + /** + * ファイルを添付 + */ "attachFile": string; + /** + * ã‚‚ã£ã¨ï¼ + */ "more": string; + /** + * ãƒã‚¤ãƒ©ã‚¤ãƒˆ + */ "featured": string; + /** + * ユーザーåã‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ID + */ "usernameOrUserId": string; + /** + * ユーザーãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ + */ "noSuchUser": string; + /** + * 照会 + */ "lookup": string; + /** + * ãŠçŸ¥ã‚‰ã› + */ "announcements": string; + /** + * ç”»åƒURL + */ "imageUrl": string; + /** + * 削除 + */ "remove": string; + /** + * 削除ã—ã¾ã—㟠+ */ "removed": string; - "removeAreYouSure": string; - "deleteAreYouSure": string; + /** + * 「{x}ã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ + */ + "removeAreYouSure": ParameterizedString<"x">; + /** + * 「{x}ã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ + */ + "deleteAreYouSure": ParameterizedString<"x">; + /** + * リセットã—ã¾ã™ã‹ï¼Ÿ + */ "resetAreYouSure": string; + /** + * よã‚ã—ã„ã§ã™ã‹ï¼Ÿ + */ "areYouSure": string; + /** + * ä¿å˜ã—ã¾ã—㟠+ */ "saved": string; + /** + * ãƒãƒ£ãƒƒãƒˆ + */ "messaging": string; + /** + * アップãƒãƒ¼ãƒ‰ + */ "upload": string; + /** + * オリジナル画åƒã‚’ä¿æŒ + */ "keepOriginalUploading": string; + /** + * ç”»åƒã‚’アップãƒãƒ¼ãƒ‰ã™ã‚‹æ™‚ã«ã‚ªãƒªã‚¸ãƒŠãƒ«ç‰ˆã‚’ä¿æŒã—ã¾ã™ã€‚オフã«ã™ã‚‹ã¨ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰æ™‚ã«ãƒ–ラウザã§Web公開用画åƒã‚’生æˆã—ã¾ã™ã€‚ + */ "keepOriginalUploadingDescription": string; + /** + * ドライブã‹ã‚‰ + */ "fromDrive": string; + /** + * URLã‹ã‚‰ + */ "fromUrl": string; + /** + * URLアップãƒãƒ¼ãƒ‰ + */ "uploadFromUrl": string; + /** + * アップãƒãƒ¼ãƒ‰ã—ãŸã„ファイルã®URL + */ "uploadFromUrlDescription": string; + /** + * アップãƒãƒ¼ãƒ‰ã‚’リクエストã—ã¾ã—㟠+ */ "uploadFromUrlRequested": string; + /** + * アップãƒãƒ¼ãƒ‰ãŒå®Œäº†ã™ã‚‹ã¾ã§æ™‚é–“ãŒã‹ã‹ã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ã€‚ + */ "uploadFromUrlMayTakeTime": string; + /** + * ã¿ã¤ã‘ã‚‹ + */ "explore": string; + /** + * æ—¢èª + */ "messageRead": string; + /** + * ã“れよりéŽåŽ»ã®å±¥æ´ã¯ã‚ã‚Šã¾ã›ã‚“ + */ "noMoreHistory": string; + /** + * ãƒãƒ£ãƒƒãƒˆã‚’開始 + */ "startMessaging": string; - "nUsersRead": string; - "agreeTo": string; + /** + * {n}人ãŒèªã¿ã¾ã—㟠+ */ + "nUsersRead": ParameterizedString<"n">; + /** + * {0}ã«åŒæ„ + */ + "agreeTo": ParameterizedString<"0">; + /** + * åŒæ„ã™ã‚‹ + */ "agree": string; + /** + * 下記ã«åŒæ„ã™ã‚‹ + */ "agreeBelow": string; + /** + * 基本的ãªæ³¨æ„äº‹é … + */ "basicNotesBeforeCreateAccount": string; + /** + * 利用è¦ç´„ + */ "termsOfService": string; + /** + * 始ã‚ã‚‹ + */ "start": string; + /** + * ホーム+ */ "home": string; + /** + * リモートユーザーã®ãŸã‚ã€æƒ…å ±ãŒä¸å®Œå…¨ã§ã™ã€‚ + */ "remoteUserCaution": string; + /** + * アクティビティ + */ "activity": string; + /** + * ç”»åƒ + */ "images": string; + /** + * ç”»åƒ + */ "image": string; + /** + * 誕生日 + */ "birthday": string; - "yearsOld": string; + /** + * {age}æ³ + */ + "yearsOld": ParameterizedString<"age">; + /** + * 登録日 + */ "registeredDate": string; + /** + * å ´æ‰€ + */ "location": string; + /** + * テーマ + */ "theme": string; + /** + * ライトモードã§ä½¿ã†ãƒ†ãƒ¼ãƒž + */ "themeForLightMode": string; + /** + * ダークモードã§ä½¿ã†ãƒ†ãƒ¼ãƒž + */ "themeForDarkMode": string; + /** + * ライト + */ "light": string; + /** + * ダーク + */ "dark": string; + /** + * 明るã„テーマ + */ "lightThemes": string; + /** + * æš—ã„テーマ + */ "darkThemes": string; + /** + * デãƒã‚¤ã‚¹ã®ãƒ€ãƒ¼ã‚¯ãƒ¢ãƒ¼ãƒ‰ã¨åŒæœŸã™ã‚‹ + */ "syncDeviceDarkMode": string; + /** + * ドライブ + */ "drive": string; + /** + * ファイルå + */ "fileName": string; + /** + * ファイルをé¸æŠž + */ "selectFile": string; + /** + * ファイルをé¸æŠž + */ "selectFiles": string; + /** + * フォルダーをé¸æŠž + */ "selectFolder": string; + /** + * フォルダーをé¸æŠž + */ "selectFolders": string; + /** + * ファイルåを変更 + */ "renameFile": string; + /** + * フォルダーå + */ "folderName": string; + /** + * ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã‚’ä½œæˆ + */ "createFolder": string; + /** + * フォルダーåを変更 + */ "renameFolder": string; + /** + * フォルダーを削除 + */ "deleteFolder": string; + /** + * フォルダー + */ "folder": string; + /** + * ãƒ•ã‚¡ã‚¤ãƒ«ã‚’è¿½åŠ + */ "addFile": string; + /** + * ドライブã¯ç©ºã§ã™ + */ "emptyDrive": string; + /** + * フォルダーã¯ç©ºã§ã™ + */ "emptyFolder": string; + /** + * 削除ã§ãã¾ã›ã‚“ + */ "unableToDelete": string; + /** + * æ–°ã—ã„ファイルåを入力ã—ã¦ãã ã•ã„ + */ "inputNewFileName": string; + /** + * æ–°ã—ã„ã‚ャプションを入力ã—ã¦ãã ã•ã„ + */ "inputNewDescription": string; + /** + * æ–°ã—ã„フォルダåを入力ã—ã¦ãã ã•ã„ + */ "inputNewFolderName": string; + /** + * 移動先ã®ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã¯ã€ç§»å‹•ã™ã‚‹ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã®ã‚µãƒ–フォルダーã§ã™ã€‚ + */ "circularReferenceFolder": string; + /** + * ã“ã®ãƒ•ã‚©ãƒ«ãƒ€ã¯ç©ºã§ãªã„ãŸã‚ã€å‰Šé™¤ã§ãã¾ã›ã‚“。 + */ "hasChildFilesOrFolders": string; + /** + * URLをコピー + */ "copyUrl": string; + /** + * åå‰ã‚’変更 + */ "rename": string; + /** + * アイコン + */ "avatar": string; + /** + * ãƒãƒŠãƒ¼ + */ "banner": string; + /** + * 背景 + */ "background": string; + /** + * センシティブãªãƒ¡ãƒ‡ã‚£ã‚¢ã®è¡¨ç¤º + */ "displayOfSensitiveMedia": string; + /** + * サーãƒãƒ¼ã¨ã®æŽ¥ç¶šãŒå¤±ã‚ã‚ŒãŸã¨ã + */ "whenServerDisconnected": string; + /** + * サーãƒãƒ¼ã‹ã‚‰åˆ‡æ–ã•ã‚Œã¾ã—㟠+ */ "disconnectedFromServer": string; + /** + * リãƒãƒ¼ãƒ‰ + */ "reload": string; + /** + * ãªã«ã‚‚ã—ãªã„ + */ "doNothing": string; + /** + * リãƒãƒ¼ãƒ‰ã—ã¾ã™ã‹ï¼Ÿ + */ "reloadConfirm": string; + /** + * ウォッム+ */ "watch": string; + /** + * ウォッãƒè§£é™¤ + */ "unwatch": string; + /** + * è¨±å¯ + */ "accept": string; + /** + * æ‹’å¦ + */ "reject": string; + /** + * 通常 + */ "normal": string; + /** + * サーãƒãƒ¼å + */ "instanceName": string; + /** + * サーãƒãƒ¼ã®ç´¹ä»‹ + */ "instanceDescription": string; + /** + * 管ç†è€…ã®åå‰ + */ "maintainerName": string; + /** + * 管ç†è€…ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ + */ "maintainerEmail": string; + /** + * 利用è¦ç´„URL + */ "tosUrl": string; + /** + * 今年 + */ "thisYear": string; + /** + * 今月 + */ "thisMonth": string; + /** + * 今日 + */ "today": string; - "dayX": string; - "monthX": string; - "yearX": string; + /** + * {day}æ—¥ + */ + "dayX": ParameterizedString<"day">; + /** + * {month}月 + */ + "monthX": ParameterizedString<"month">; + /** + * {year}å¹´ + */ + "yearX": ParameterizedString<"year">; + /** + * ページ + */ "pages": string; + /** + * é€£æº + */ "integration": string; + /** + * 接続ã™ã‚‹ + */ "connectService": string; + /** + * 切æ–ã™ã‚‹ + */ "disconnectService": string; + /** + * ãƒãƒ¼ã‚«ãƒ«ã‚¿ã‚¤ãƒ ラインを有効ã«ã™ã‚‹ + */ "enableLocalTimeline": string; + /** + * ã‚°ãƒãƒ¼ãƒãƒ«ã‚¿ã‚¤ãƒ ラインを有効ã«ã™ã‚‹ + */ "enableGlobalTimeline": string; + /** + * ã“れらã®ã‚¿ã‚¤ãƒ ラインを無効化ã—ã¦ã‚‚ã€åˆ©ä¾¿æ€§ã®ãŸã‚管ç†è€…ãŠã‚ˆã³ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚¿ãƒ¼ã¯å¼•ã続ã利用ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + */ "disablingTimelinesInfo": string; + /** + * 登録 + */ "registration": string; + /** + * 誰ã§ã‚‚æ–°è¦ç™»éŒ²ã§ãるよã†ã«ã™ã‚‹ + */ "enableRegistration": string; + /** + * 招待 + */ "invite": string; + /** + * ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã²ã¨ã‚Šã‚ãŸã‚Šã®ãƒ‰ãƒ©ã‚¤ãƒ–å®¹é‡ + */ "driveCapacityPerLocalAccount": string; + /** + * リモートユーザーã²ã¨ã‚Šã‚ãŸã‚Šã®ãƒ‰ãƒ©ã‚¤ãƒ–å®¹é‡ + */ "driveCapacityPerRemoteAccount": string; + /** + * メガãƒã‚¤ãƒˆå˜ä½ + */ "inMb": string; + /** + * ãƒãƒŠãƒ¼ç”»åƒã®URL + */ "bannerUrl": string; + /** + * 背景画åƒã®URL + */ "backgroundImageUrl": string; + /** + * åŸºæœ¬æƒ…å ± + */ "basicInfo": string; + /** + * ピン留ã‚ユーザー + */ "pinnedUsers": string; + /** + * 「ã¿ã¤ã‘ã‚‹ã€ãƒšãƒ¼ã‚¸ãªã©ã«ãƒ”ン留ã‚ã—ãŸã„ユーザーを改行ã§åŒºåˆ‡ã£ã¦è¨˜è¿°ã—ã¾ã™ã€‚ + */ "pinnedUsersDescription": string; + /** + * ピン留ã‚ページ + */ "pinnedPages": string; + /** + * サーãƒãƒ¼ã®ãƒˆãƒƒãƒ—ページã«ãƒ”ン留ã‚ã—ãŸã„ページã®ãƒ‘スを改行ã§åŒºåˆ‡ã£ã¦è¨˜è¿°ã—ã¾ã™ã€‚ + */ "pinnedPagesDescription": string; + /** + * ピン留ã‚ã™ã‚‹ã‚¯ãƒªãƒƒãƒ—ã®ID + */ "pinnedClipId": string; + /** + * ピン留ã‚ã•ã‚ŒãŸãƒŽãƒ¼ãƒˆ + */ "pinnedNotes": string; + /** + * hCaptcha + */ "hcaptcha": string; + /** + * hCaptchaを有効ã«ã™ã‚‹ + */ "enableHcaptcha": string; + /** + * サイトã‚ー + */ "hcaptchaSiteKey": string; + /** + * シークレットã‚ー + */ "hcaptchaSecretKey": string; + /** + * mCaptcha + */ + "mcaptcha": string; + /** + * mCaptchaを有効ã«ã™ã‚‹ + */ + "enableMcaptcha": string; + /** + * サイトã‚ー + */ + "mcaptchaSiteKey": string; + /** + * シークレットã‚ー + */ + "mcaptchaSecretKey": string; + /** + * mCaptchaã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã®URL + */ + "mcaptchaInstanceUrl": string; + /** + * reCAPTCHA + */ "recaptcha": string; + /** + * reCAPTCHAを有効ã«ã™ã‚‹ + */ "enableRecaptcha": string; + /** + * サイトã‚ー + */ "recaptchaSiteKey": string; + /** + * シークレットã‚ー + */ "recaptchaSecretKey": string; + /** + * Turnstile + */ "turnstile": string; + /** + * Turnstileを有効ã«ã™ã‚‹ + */ "enableTurnstile": string; + /** + * サイトã‚ー + */ "turnstileSiteKey": string; + /** + * シークレットã‚ー + */ "turnstileSecretKey": string; + /** + * 複数ã®Captchaを使用ã™ã‚‹ã¨å¹²æ¸‰ã‚’èµ·ã“ã™å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚ä»–ã®Captchaを無効ã«ã—ã¾ã™ã‹ï¼Ÿã‚ャンセルã—ã¦è¤‡æ•°ã®Captchaを有効化ã—ãŸã¾ã¾ã«ã™ã‚‹ã“ã¨ã‚‚å¯èƒ½ã§ã™ã€‚ + */ "avoidMultiCaptchaConfirm": string; + /** + * アンテナ + */ "antennas": string; + /** + * アンテナã®ç®¡ç† + */ "manageAntennas": string; + /** + * åå‰ + */ "name": string; + /** + * å—信ソース + */ "antennaSource": string; + /** + * å—ä¿¡ã‚ーワード + */ "antennaKeywords": string; + /** + * 除外ã‚ーワード + */ "antennaExcludeKeywords": string; + /** + * スペースã§åŒºåˆ‡ã‚‹ã¨AND指定ã«ãªã‚Šã€æ”¹è¡Œã§åŒºåˆ‡ã‚‹ã¨OR指定ã«ãªã‚Šã¾ã™ + */ "antennaKeywordsDescription": string; + /** + * æ–°ã—ã„ノートを通知ã™ã‚‹ + */ "notifyAntenna": string; + /** + * ファイルãŒæ·»ä»˜ã•ã‚ŒãŸãƒŽãƒ¼ãƒˆã®ã¿ + */ "withFileAntenna": string; + /** + * ブラウザã¸ã®ãƒ—ッシュ通知を有効ã«ã™ã‚‹ + */ "enableServiceworker": string; + /** + * ユーザーåを改行ã§åŒºåˆ‡ã£ã¦æŒ‡å®šã—ã¾ã™ + */ "antennaUsersDescription": string; + /** + * 大文å—å°æ–‡å—を区別ã™ã‚‹ + */ "caseSensitive": string; + /** + * 返信をå«ã‚€ + */ "withReplies": string; + /** + * 次ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«æŽ¥ç¶šã•ã‚Œã¦ã„ã¾ã™ + */ "connectedTo": string; + /** + * 投稿ã¨è¿”ä¿¡ + */ "notesAndReplies": string; + /** + * ファイル付ã + */ "withFiles": string; + /** + * サイレンス + */ "silence": string; + /** + * サイレンスã—ã¾ã™ã‹ï¼Ÿ + */ "silenceConfirm": string; + /** + * サイレンス解除 + */ "unsilence": string; + /** + * サイレンス解除ã—ã¾ã™ã‹ï¼Ÿ + */ "unsilenceConfirm": string; + /** + * 人気ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ + */ "popularUsers": string; + /** + * 最近投稿ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ + */ "recentlyUpdatedUsers": string; + /** + * 最近登録ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ + */ "recentlyRegisteredUsers": string; + /** + * 最近発見ã•ã‚ŒãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ + */ "recentlyDiscoveredUsers": string; - "exploreUsersCount": string; + /** + * {count}ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒã„ã¾ã™ + */ + "exploreUsersCount": ParameterizedString<"count">; + /** + * Fediverseを探索 + */ "exploreFediverse": string; + /** + * 人気ã®ã‚¿ã‚° + */ "popularTags": string; + /** + * リスト + */ "userList": string; + /** + * æƒ…å ± + */ "about": string; + /** + * Sharkeyã«ã¤ã„㦠+ */ "aboutMisskey": string; + /** + * 管ç†è€… + */ "administrator": string; + /** + * 確èªã‚³ãƒ¼ãƒ‰ + */ "token": string; + /** + * 二è¦ç´ èªè¨¼ + */ "2fa": string; + /** + * 二è¦ç´ èªè¨¼ã®ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ— + */ "setupOf2fa": string; + /** + * èªè¨¼ã‚¢ãƒ—リ + */ "totp": string; + /** + * èªè¨¼ã‚¢ãƒ—リを使ã£ã¦ãƒ¯ãƒ³ã‚¿ã‚¤ãƒ パスワードを入力 + */ "totpDescription": string; + /** + * モデレーター + */ "moderator": string; + /** + * モデレーション + */ "moderation": string; + /** + * モデレーションノート + */ "moderationNote": string; + /** + * ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãƒŽãƒ¼ãƒˆã‚’è¿½åŠ ã™ã‚‹ + */ "addModerationNote": string; + /** + * モデãƒã‚° + */ "moderationLogs": string; - "nUsersMentioned": string; + /** + * {n}人ãŒæŠ•ç¨¿ + */ + "nUsersMentioned": ParameterizedString<"n">; + /** + * ã‚»ã‚ュリティã‚ー・パスã‚ー + */ "securityKeyAndPasskey": string; + /** + * ã‚»ã‚ュリティã‚ー + */ "securityKey": string; + /** + * 最後ã®ä½¿ç”¨ + */ "lastUsed": string; - "lastUsedAt": string; + /** + * 最後ã®ä½¿ç”¨: {t} + */ + "lastUsedAt": ParameterizedString<"t">; + /** + * 登録を解除 + */ "unregister": string; + /** + * パスワードレスãƒã‚°ã‚¤ãƒ³ + */ "passwordLessLogin": string; + /** + * パスワードを使用ã›ãšã€ã‚»ã‚ュリティã‚ーやパスã‚ーãªã©ã®ã¿ã§ãƒã‚°ã‚¤ãƒ³ã—ã¾ã™ + */ "passwordLessLoginDescription": string; + /** + * パスワードをリセット + */ "resetPassword": string; - "newPasswordIs": string; + /** + * æ–°ã—ã„パスワードã¯ã€Œ{password}ã€ã§ã™ + */ + "newPasswordIs": ParameterizedString<"password">; + /** + * UIã®ã‚¢ãƒ‹ãƒ¡ãƒ¼ã‚·ãƒ§ãƒ³ã‚’減ら㙠+ */ "reduceUiAnimation": string; + /** + * 共有 + */ "share": string; + /** + * 見ã¤ã‹ã‚Šã¾ã›ã‚“ + */ "notFound": string; + /** + * 指定ã•ã‚ŒãŸURLã«è©²å½“ã™ã‚‹ãƒšãƒ¼ã‚¸ã¯ã‚ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ + */ "notFoundDescription": string; + /** + * 既定アップãƒãƒ¼ãƒ‰å…ˆ + */ "uploadFolder": string; + /** + * ã™ã¹ã¦ã®é€šçŸ¥ã‚’æ—¢èªã«ã™ã‚‹ + */ "markAsReadAllNotifications": string; + /** + * ã™ã¹ã¦ã®æŠ•ç¨¿ã‚’æ—¢èªã«ã™ã‚‹ + */ "markAsReadAllUnreadNotes": string; + /** + * ã™ã¹ã¦ã®ãƒãƒ£ãƒƒãƒˆã‚’æ—¢èªã«ã™ã‚‹ + */ "markAsReadAllTalkMessages": string; + /** + * ヘルプ + */ "help": string; + /** + * ã“ã“ã«ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’入力 + */ "inputMessageHere": string; + /** + * é–‰ã˜ã‚‹ + */ "close": string; + /** + * 招待 + */ "invites": string; + /** + * メンãƒãƒ¼ + */ "members": string; + /** + * è²æ¸¡ + */ "transfer": string; + /** + * タイトル + */ "title": string; + /** + * テã‚スト + */ "text": string; + /** + * 有効ã«ã™ã‚‹ + */ "enable": string; + /** + * 次 + */ "next": string; + /** + * å†å…¥åŠ› + */ "retype": string; - "noteOf": string; + /** + * {user}ã®ãƒŽãƒ¼ãƒˆ + */ + "noteOf": ParameterizedString<"user">; + /** + * ã™ã¹ã¦ã®è¿”ä¿¡ã®å†…容を表示ã™ã‚‹ + */ "expandAllCws": string; + /** + * ã™ã¹ã¦ã®è¿”ä¿¡ã®å†…å®¹ã‚’éš ã™ + */ "collapseAllCws": string; + /** + * 引用付ã + */ "quoteAttached": string; + /** + * 引用ã¨ã—ã¦æ·»ä»˜ã—ã¾ã™ã‹ï¼Ÿ + */ "quoteQuestion": string; + /** + * ã¾ã ãƒãƒ£ãƒƒãƒˆã¯ã‚ã‚Šã¾ã›ã‚“ + */ "noMessagesYet": string; + /** + * æ–°ã—ã„メッセージãŒã‚ã‚Šã¾ã™ + */ "newMessageExists": string; + /** + * メッセージã«æ·»ä»˜ã§ãるファイルã¯ã²ã¨ã¤ã§ã™ + */ "onlyOneFileCanBeAttached": string; + /** + * 続行ã™ã‚‹å‰ã«ã€ã‚µã‚¤ãƒ³ã‚¢ãƒƒãƒ—ã¾ãŸã¯ã‚µã‚¤ãƒ³ã‚¤ãƒ³ãŒå¿…è¦ã§ã™ + */ "signinRequired": string; + /** + * 招待 + */ "invitations": string; + /** + * 招待コード + */ "invitationCode": string; + /** + * 確èªã—ã¦ã„ã¾ã™ + */ "checking": string; + /** + * 利用ã§ãã¾ã™ + */ "available": string; + /** + * 利用ã§ãã¾ã›ã‚“ + */ "unavailable": string; + /** + * a~zã€A~Zã€0~9ã€_ãŒä½¿ãˆã¾ã™ + */ "usernameInvalidFormat": string; + /** + * çŸã™ãŽã¾ã™ + */ "tooShort": string; + /** + * é•·ã™ãŽã¾ã™ + */ "tooLong": string; + /** + * å¼±ã„パスワード + */ "weakPassword": string; + /** + * 普通ã®ãƒ‘スワード + */ "normalPassword": string; + /** + * å¼·ã„パスワード + */ "strongPassword": string; + /** + * 一致ã—ã¾ã—㟠+ */ "passwordMatched": string; + /** + * 一致ã—ã¦ã„ã¾ã›ã‚“ + */ "passwordNotMatched": string; - "signinWith": string; + /** + * {x}ã§ãƒã‚°ã‚¤ãƒ³ + */ + "signinWith": ParameterizedString<"x">; + /** + * ãƒã‚°ã‚¤ãƒ³ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ユーザーåã¨ãƒ‘スワードを確èªã—ã¦ãã ã•ã„。 + */ "signinFailed": string; + /** + * ã‚‚ã—ã㯠+ */ "or": string; + /** + * 言語 + */ "language": string; + /** + * UIã®è¡¨ç¤ºè¨€èªž + */ "uiLanguage": string; - "aboutX": string; + /** + * {x}ã«ã¤ã„㦠+ */ + "aboutX": ParameterizedString<"x">; + /** + * 絵文å—ã®ã‚¹ã‚¿ã‚¤ãƒ« + */ "emojiStyle": string; + /** + * ãƒã‚¤ãƒ†ã‚£ãƒ– + */ "native": string; + /** + * メニューをドãƒãƒ¯ãƒ¼ã§è¡¨ç¤ºã—ãªã„ + */ "disableDrawer": string; + /** + * ノートã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’ホãƒãƒ¼æ™‚ã®ã¿è¡¨ç¤ºã™ã‚‹ + */ "showNoteActionsOnlyHover": string; + /** + * å±¥æ´ã¯ã‚ã‚Šã¾ã›ã‚“ + */ "noHistory": string; + /** + * ãƒã‚°ã‚¤ãƒ³å±¥æ´ + */ "signinHistory": string; + /** + * 高度ãªMFMを有効ã«ã™ã‚‹ + */ "enableAdvancedMfm": string; + /** + * å‹•ãã®ã‚ã‚‹MFMを有効ã«ã™ã‚‹ + */ "enableAnimatedMfm": string; + /** + * ã‚„ã£ã¦ã„ã¾ã™ + */ "doing": string; + /** + * カテゴリ + */ "category": string; + /** + * ã‚¿ã‚° + */ "tags": string; + /** + * ã“ã®ãƒ‰ã‚ュメントã®ã‚½ãƒ¼ã‚¹ + */ "docSource": string; + /** + * ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ä½œæˆ + */ "createAccount": string; + /** + * æ—¢å˜ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ + */ "existingAccount": string; + /** + * å†ç”Ÿæˆ + */ "regenerate": string; + /** + * フォントサイズ + */ "fontSize": string; + /** + * コーナーã®ä¸¸ã¿ + */ "cornerRadius": string; + /** + * ç”»åƒãŒ1æžšã®ã¿ã®ãƒ¡ãƒ‡ã‚£ã‚¢ãƒªã‚¹ãƒˆã®é«˜ã• + */ "mediaListWithOneImageAppearance": string; - "limitTo": string; + /** + * {x}を上é™ã« + */ + "limitTo": ParameterizedString<"x">; + /** + * フォãƒãƒ¼ç”³è«‹ã¯ã‚ã‚Šã¾ã›ã‚“ + */ "noFollowRequests": string; + /** + * ç”»åƒã‚’æ–°ã—ã„タブã§é–‹ã + */ "openImageInNewTab": string; + /** + * ダッシュボード + */ "dashboard": string; + /** + * ãƒãƒ¼ã‚«ãƒ« + */ "local": string; + /** + * リモート + */ "remote": string; + /** + * åˆè¨ˆ + */ "total": string; + /** + * å‰é€±æ¯” + */ "weekOverWeekChanges": string; + /** + * å‰æ—¥æ¯” + */ "dayOverDayChanges": string; + /** + * アピアランス + */ "appearance": string; + /** + * クライアントè¨å®š + */ "clientSettings": string; + /** + * アカウントè¨å®š + */ "accountSettings": string; + /** + * プãƒãƒ¢ãƒ¼ã‚·ãƒ§ãƒ³ + */ "promotion": string; + /** + * プãƒãƒ¢ãƒ¼ãƒˆ + */ "promote": string; + /** + * 日数 + */ "numberOfDays": string; + /** + * ã“ã®ãƒŽãƒ¼ãƒˆã‚’éžè¡¨ç¤º + */ "hideThisNote": string; + /** + * タイムラインã«ãŠã™ã™ã‚ã®ãƒŽãƒ¼ãƒˆã‚’表示ã™ã‚‹ + */ "showFeaturedNotesInTimeline": string; + /** + * オブジェクトストレージ + */ "objectStorage": string; + /** + * オブジェクトストレージを使用 + */ "useObjectStorage": string; + /** + * Base URL + */ "objectStorageBaseUrl": string; + /** + * å‚ç…§ã«ä½¿ç”¨ã™ã‚‹URL。CDNã‚„Proxyを使用ã—ã¦ã„ã‚‹å ´åˆã¯ãã®URLã€S3: 'https://<bucket>.s3.amazonaws.com'ã€GCSç‰: 'https://storage.googleapis.com/<bucket>'。 + */ "objectStorageBaseUrlDesc": string; + /** + * Bucket + */ "objectStorageBucket": string; + /** + * 使用サービスã®bucketåを指定ã—ã¦ãã ã•ã„。 + */ "objectStorageBucketDesc": string; + /** + * Prefix + */ "objectStoragePrefix": string; + /** + * ã“ã®prefixã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªä¸‹ã«æ ¼ç´ã•ã‚Œã¾ã™ã€‚ + */ "objectStoragePrefixDesc": string; + /** + * Endpoint + */ "objectStorageEndpoint": string; + /** + * S3ã®å ´åˆã¯ç©ºã€ãれ以外ã®å ´åˆã¯å„サービスã®endpointを指定ã—ã¦ãã ã•ã„。'<host>'ã¾ãŸã¯'<host>:<port>'ã®ã‚ˆã†ã«æŒ‡å®šã—ã¾ã™ã€‚ + */ "objectStorageEndpointDesc": string; + /** + * Region + */ "objectStorageRegion": string; + /** + * 'xx-east-1'ã®ã‚ˆã†ãªregionを指定ã—ã¦ãã ã•ã„。使用サービスã«regionã®æ¦‚念ãŒãªã„å ´åˆã¯'us-east-1'ã«ã—ã¦ãã ã•ã„。AWSè¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã¾ãŸã¯ç’°å¢ƒå¤‰æ•°ã‚’å‚ç…§ã™ã‚‹å ´åˆã¯ç©ºã«ã—ã¦ãã ã•ã„。 + */ "objectStorageRegionDesc": string; + /** + * SSLを使用ã™ã‚‹ + */ "objectStorageUseSSL": string; + /** + * API接続ã«httpsを使用ã—ãªã„å ´åˆã¯ã‚ªãƒ•ã«ã—ã¦ãã ã•ã„ + */ "objectStorageUseSSLDesc": string; + /** + * Proxyを利用ã™ã‚‹ + */ "objectStorageUseProxy": string; + /** + * API接続ã«proxyを利用ã—ãªã„å ´åˆã¯ã‚ªãƒ•ã«ã—ã¦ãã ã•ã„ + */ "objectStorageUseProxyDesc": string; + /** + * アップãƒãƒ¼ãƒ‰æ™‚ã«'public-read'ã‚’è¨å®šã™ã‚‹ + */ "objectStorageSetPublicRead": string; + /** + * s3ForcePathStyleを有効ã«ã™ã‚‹ã¨ã€ãƒã‚±ãƒƒãƒˆåã‚’URLã®ãƒ›ã‚¹ãƒˆåã§ã¯ãªãパスã®ä¸€éƒ¨ã¨ã—ã¦æŒ‡å®šã™ã‚‹ã“ã¨ã‚’強制ã—ã¾ã™ã€‚セルフホストã•ã‚ŒãŸMinioãªã©ã®ä½¿ç”¨æ™‚ã«æœ‰åŠ¹ã«ã™ã‚‹å¿…è¦ãŒã‚ã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ã€‚ + */ "s3ForcePathStyleDesc": string; + /** + * DeepLX-JS を使用ã™ã‚‹ (èªè¨¼ã‚ーãªã—) + */ + "deeplFreeMode": string; + /** + * ヘルプãŒå¿…è¦ã§ã™ã‹? DeepLX-JSã®ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—方法ã«ã¤ã„ã¦ã¯ã€ãƒ‰ã‚ュメントをå‚ç…§ã—ã¦ãã ã•ã„。 + */ + "deeplFreeModeDescription": string; + /** + * サーãƒãƒ¼ãƒã‚° + */ "serverLogs": string; + /** + * å…¨ã¦å‰Šé™¤ + */ "deleteAll": string; + /** + * タイムライン上部ã«æŠ•ç¨¿ãƒ•ã‚©ãƒ¼ãƒ を表示ã™ã‚‹ + */ "showFixedPostForm": string; + /** + * タイムライン上部ã«æŠ•ç¨¿ãƒ•ã‚©ãƒ¼ãƒ を表示ã™ã‚‹(ãƒãƒ£ãƒ³ãƒãƒ«) + */ "showFixedPostFormInChannel": string; + /** + * フォãƒãƒ¼ã™ã‚‹éš›ã€ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã§è¿”ä¿¡ã‚’TLã«å«ã‚€ã‚ˆã†ã«ã™ã‚‹ + */ "withRepliesByDefaultForNewlyFollowed": string; + /** + * æ–°ã—ã„ノートãŒã‚ã‚Šã¾ã™ + */ "newNoteRecived": string; + /** + * サウンド + */ "sounds": string; + /** + * サウンド + */ "sound": string; + /** + * è´ã + */ "listen": string; + /** + * ãªã— + */ "none": string; + /** + * ページã§è¡¨ç¤º + */ "showInPage": string; + /** + * ãƒãƒƒãƒ—アウト + */ "popout": string; + /** + * éŸ³é‡ + */ "volume": string; + /** + * ãƒžã‚¹ã‚¿ãƒ¼éŸ³é‡ + */ "masterVolume": string; + /** + * サウンドを出力ã—ãªã„ + */ "notUseSound": string; + /** + * MisskeyãŒã‚¢ã‚¯ãƒ†ã‚£ãƒ–ãªæ™‚ã®ã¿ã‚µã‚¦ãƒ³ãƒ‰ã‚’出力ã™ã‚‹ + */ "useSoundOnlyWhenActive": string; + /** + * 詳細 + */ "details": string; + /** + * 絵文å—ã‚’é¸æŠž + */ "chooseEmoji": string; + /** + * æ“作を完了ã§ãã¾ã›ã‚“ + */ "unableToProcess": string; + /** + * 最近使用 + */ "recentUsed": string; + /** + * インストール + */ "install": string; + /** + * アンインストール + */ "uninstall": string; + /** + * インストールã•ã‚ŒãŸã‚¢ãƒ—リ + */ "installedApps": string; + /** + * ã‚ã‚Šã¾ã›ã‚“ + */ "nothing": string; + /** + * インストール日時 + */ "installedDate": string; + /** + * 最終使用日時 + */ "lastUsedDate": string; + /** + * 状態 + */ "state": string; + /** + * ソート + */ "sort": string; + /** + * æ˜‡é † + */ "ascendingOrder": string; + /** + * é™é † + */ "descendingOrder": string; + /** + * スクラッãƒãƒ‘ッド + */ "scratchpad": string; + /** + * スクラッãƒãƒ‘ッドã¯ã€AiScriptã®å®Ÿé¨“環境をæä¾›ã—ã¾ã™ã€‚Sharkeyã¨å¯¾è©±ã™ã‚‹ã‚³ãƒ¼ãƒ‰ã®è¨˜è¿°ã€å®Ÿè¡Œã€çµæžœã®ç¢ºèªãŒã§ãã¾ã™ã€‚ + */ "scratchpadDescription": string; + /** + * 出力 + */ "output": string; + /** + * スクリプト + */ "script": string; + /** + * Pagesã®ã‚¹ã‚¯ãƒªãƒ—トを無効ã«ã™ã‚‹ + */ "disablePagesScript": string; + /** + * ãƒªãƒ¢ãƒ¼ãƒˆãƒ¦ãƒ¼ã‚¶ãƒ¼æƒ…å ±ã®æ›´æ–° + */ "updateRemoteUser": string; + /** + * アイコンを解除 + */ "unsetUserAvatar": string; + /** + * アイコンを解除ã—ã¾ã™ã‹ï¼Ÿ + */ "unsetUserAvatarConfirm": string; + /** + * ãƒãƒŠãƒ¼ã‚’解除 + */ "unsetUserBanner": string; + /** + * ãƒãƒŠãƒ¼ã‚’解除ã—ã¾ã™ã‹ï¼Ÿ + */ "unsetUserBannerConfirm": string; + /** + * ã™ã¹ã¦ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’削除 + */ "deleteAllFiles": string; + /** + * ã™ã¹ã¦ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ + */ "deleteAllFilesConfirm": string; + /** + * フォãƒãƒ¼ã‚’全解除 + */ "removeAllFollowing": string; - "removeAllFollowingDescription": string; + /** + * {host}ã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒ¼ã‚’ã™ã¹ã¦è§£é™¤ã—ã¾ã™ã€‚ãã®ã‚µãƒ¼ãƒãƒ¼ãŒã‚‚ã†å˜åœ¨ã—ãªããªã£ãŸå ´åˆãªã©ã«å®Ÿè¡Œã—ã¦ãã ã•ã„。 + */ + "removeAllFollowingDescription": ParameterizedString<"host">; + /** + * ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯å‡çµã•ã‚Œã¦ã„ã¾ã™ã€‚ + */ "userSuspended": string; + /** + * ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã‚µã‚¤ãƒ¬ãƒ³ã‚¹ã•ã‚Œã¦ã„ã¾ã™ã€‚ + */ "userSilenced": string; + /** + * アカウントãŒå‡çµã•ã‚Œã¦ã„ã¾ã™ + */ "yourAccountSuspendedTitle": string; + /** + * ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ã€ã‚µãƒ¼ãƒãƒ¼ã®åˆ©ç”¨è¦ç´„ã«é•åã—ãŸãªã©ã®ç†ç”±ã«ã‚ˆã‚Šã€å‡çµã•ã‚Œã¦ã„ã¾ã™ã€‚詳細ã«ã¤ã„ã¦ã¯ç®¡ç†è€…ã¾ã§ãŠå•ã„åˆã‚ã›ãã ã•ã„。新ã—ã„アカウントを作らãªã„ã§ãã ã•ã„。 + */ "yourAccountSuspendedDescription": string; + /** + * トークンãŒç„¡åŠ¹ã§ã™ + */ "tokenRevoked": string; + /** + * ãƒã‚°ã‚¤ãƒ³ãƒˆãƒ¼ã‚¯ãƒ³ãŒå¤±åŠ¹ã—ã¦ã„ã¾ã™ã€‚ãƒã‚°ã‚¤ãƒ³ã—ç›´ã—ã¦ãã ã•ã„。 + */ "tokenRevokedDescription": string; + /** + * アカウントã¯å‰Šé™¤ã•ã‚Œã¦ã„ã¾ã™ + */ "accountDeleted": string; + /** + * ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯å‰Šé™¤ã•ã‚Œã¦ã„ã¾ã™ã€‚ + */ "accountDeletedDescription": string; + /** + * メニュー + */ "menu": string; + /** + * 分割線 + */ "divider": string; + /** + * é …ç›®ã‚’è¿½åŠ + */ "addItem": string; + /** + * 並ã³æ›¿ãˆ + */ "rearrange": string; + /** + * リレー + */ "relays": string; + /** + * リレーã®è¿½åŠ + */ "addRelay": string; + /** + * inboxã®URL + */ "inboxUrl": string; + /** + * è¿½åŠ æ¸ˆã¿ã®ãƒªãƒ¬ãƒ¼ + */ "addedRelays": string; + /** + * プッシュ通知を行ã†ã«ã¯æœ‰åŠ¹ã«ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ + */ "serviceworkerInfo": string; + /** + * 削除ã•ã‚ŒãŸæŠ•ç¨¿ + */ "deletedNote": string; + /** + * éžå…¬é–‹ã®æŠ•ç¨¿ + */ "invisibleNote": string; + /** + * 自動ã§ã‚‚ã£ã¨è¦‹ã‚‹ + */ "enableInfiniteScroll": string; + /** + * 公開範囲 + */ "visibility": string; + /** + * アンケート + */ "poll": string; + /** + * å†…å®¹ã‚’éš ã™ + */ "useCw": string; + /** + * プレイヤーを開ã + */ "enablePlayer": string; + /** + * プレイヤーを閉ã˜ã‚‹ + */ "disablePlayer": string; + /** + * ãƒã‚¹ãƒˆã‚’展開ã™ã‚‹ + */ "expandTweet": string; + /** + * テーマエディター + */ "themeEditor": string; + /** + * 説明 + */ "description": string; + /** + * ã‚ャプションを付ã‘ã‚‹ + */ "describeFile": string; + /** + * ã‚ャプションを入力 + */ "enterFileDescription": string; + /** + * 作者 + */ "author": string; + /** + * 未ä¿å˜ã®å¤‰æ›´ãŒã‚ã‚Šã¾ã™ã€‚ç ´æ£„ã—ã¾ã™ã‹ï¼Ÿ + */ "leaveConfirm": string; + /** + * ç®¡ç† + */ "manage": string; + /** + * プラグイン + */ "plugins": string; + /** + * è¨å®šã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ— + */ "preferencesBackups": string; + /** + * デッ゠+ */ "deck": string; + /** + * デッã‚解除 + */ "undeck": string; + /** + * モーダルã«ã¼ã‹ã—効果を使用 + */ "useBlurEffectForModal": string; + /** + * フル機能リアクションピッカーを使用 + */ "useFullReactionPicker": string; + /** + * å¹… + */ "width": string; + /** + * 高㕠+ */ "height": string; + /** + * 大 + */ "large": string; + /** + * ä¸ + */ "medium": string; + /** + * å° + */ "small": string; + /** + * アクセストークンã®ç™ºè¡Œ + */ "generateAccessToken": string; + /** + * æ¨©é™ + */ "permission": string; + /** + * 管ç†è€…æ¨©é™ + */ + "adminPermission": string; + /** + * å…¨ã¦æœ‰åŠ¹ã«ã™ã‚‹ + */ "enableAll": string; + /** + * å…¨ã¦ç„¡åŠ¹ã«ã™ã‚‹ + */ "disableAll": string; + /** + * アカウントã¸ã®ã‚¢ã‚¯ã‚»ã‚¹è¨±å¯ + */ "tokenRequested": string; + /** + * ã“ã®ãƒ—ラグインã¯ã“ã“ã§è¨å®šã—ãŸæ¨©é™ã‚’行使ã§ãるよã†ã«ãªã‚Šã¾ã™ã€‚ + */ "pluginTokenRequestedDescription": string; + /** + * 通知ã®ç¨®é¡ž + */ "notificationType": string; + /** + * 編集 + */ "edit": string; + /** + * メールサーãƒãƒ¼ + */ "emailServer": string; + /** + * メールé…信機能を有効化ã™ã‚‹ + */ "enableEmail": string; + /** + * メールアドレスã®ç¢ºèªã‚„パスワードリセットã®éš›ã«ä½¿ã„ã¾ã™ + */ "emailConfigInfo": string; + /** + * メール + */ "email": string; + /** + * メールアドレス + */ "emailAddress": string; + /** + * SMTP サーãƒãƒ¼ã®è¨å®š + */ "smtpConfig": string; + /** + * ホスト + */ "smtpHost": string; + /** + * ãƒãƒ¼ãƒˆ + */ "smtpPort": string; + /** + * ユーザーå + */ "smtpUser": string; + /** + * パスワード + */ "smtpPass": string; + /** + * ユーザーåã¨ãƒ‘スワードを空欄ã«ã™ã‚‹ã“ã¨ã§ã€SMTPèªè¨¼ã‚’無効化出æ¥ã¾ã™ + */ "emptyToDisableSmtpAuth": string; + /** + * SMTP 接続ã«æš—黙的ãªSSL/TLSを使用ã™ã‚‹ + */ "smtpSecure": string; + /** + * STARTTLS使用時ã¯ã‚ªãƒ•ã«ã—ã¾ã™ã€‚ + */ "smtpSecureInfo": string; + /** + * é…信テスト + */ "testEmail": string; + /** + * ワードミュート + */ "wordMute": string; + /** + * ãƒãƒ¼ãƒ‰ãƒ¯ãƒ¼ãƒ‰ãƒŸãƒ¥ãƒ¼ãƒˆ + */ "hardWordMute": string; + /** + * æ£è¦è¡¨ç¾ã‚¨ãƒ©ãƒ¼ + */ "regexpError": string; - "regexpErrorDescription": string; + /** + * {tab}ワードミュートã®{line}行目ã®æ£è¦è¡¨ç¾ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ: + */ + "regexpErrorDescription": ParameterizedString<"tab" | "line">; + /** + * サーãƒãƒ¼ãƒŸãƒ¥ãƒ¼ãƒˆ + */ "instanceMute": string; - "userSaysSomething": string; + /** + * {name}ãŒä½•ã‹ã‚’言ã„ã¾ã—㟠+ */ + "userSaysSomething": ParameterizedString<"name">; + /** + * アクティブã«ã™ã‚‹ + */ "makeActive": string; + /** + * 表示 + */ "display": string; + /** + * コピー + */ "copy": string; + /** + * メトリクス + */ "metrics": string; + /** + * æ¦‚è¦ + */ "overview": string; + /** + * ãƒã‚° + */ "logs": string; + /** + * é…延 + */ "delayed": string; + /** + * データベース + */ "database": string; + /** + * ãƒãƒ£ãƒ³ãƒãƒ« + */ "channel": string; + /** + * ä½œæˆ + */ "create": string; + /** + * 通知è¨å®š + */ "notificationSetting": string; + /** + * 表示ã™ã‚‹é€šçŸ¥ã®ç¨®åˆ¥ã‚’é¸æŠžã—ã¦ãã ã•ã„。 + */ "notificationSettingDesc": string; + /** + * ã‚°ãƒãƒ¼ãƒãƒ«è¨å®šã‚’使ㆠ+ */ "useGlobalSetting": string; + /** + * オンã«ã™ã‚‹ã¨ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®é€šçŸ¥è¨å®šãŒä½¿ç”¨ã•ã‚Œã¾ã™ã€‚オフã«ã™ã‚‹ã¨ã€å€‹åˆ¥ã«è¨å®šã§ãるよã†ã«ãªã‚Šã¾ã™ã€‚ + */ "useGlobalSettingDesc": string; + /** + * ãã®ä»– + */ "other": string; + /** + * ãƒã‚°ã‚¤ãƒ³ãƒˆãƒ¼ã‚¯ãƒ³ã‚’å†ç”Ÿæˆ + */ "regenerateLoginToken": string; + /** + * ãƒã‚°ã‚¤ãƒ³ã«ä½¿ç”¨ã•ã‚Œã‚‹å†…部トークンをå†ç”Ÿæˆã—ã¾ã™ã€‚通常ã“ã®æ“作を行ã†å¿…è¦ã¯ã‚ã‚Šã¾ã›ã‚“。å†ç”Ÿæˆã™ã‚‹ã¨ã€å…¨ã¦ã®ãƒ‡ãƒã‚¤ã‚¹ã§ãƒã‚°ã‚¢ã‚¦ãƒˆã•ã‚Œã¾ã™ã€‚ + */ "regenerateLoginTokenDescription": string; + /** + * カスタム絵文å—を検索ã™ã‚‹æ™‚ã®ã‚ーワードã«ãªã‚Šã¾ã™ã€‚ + */ + "theKeywordWhenSearchingForCustomEmoji": string; + /** + * スペースã§åŒºåˆ‡ã£ã¦è¤‡æ•°è¨å®šã§ãã¾ã™ã€‚ + */ "setMultipleBySeparatingWithSpace": string; + /** + * ファイルIDã¾ãŸã¯URL + */ "fileIdOrUrl": string; + /** + * 動作 + */ "behavior": string; + /** + * サンプル + */ "sample": string; + /** + * é€šå ± + */ "abuseReports": string; + /** + * é€šå ± + */ "reportAbuse": string; + /** + * ãƒ–ãƒ¼ã‚¹ãƒˆã‚’é€šå ± + */ "reportAbuseRenote": string; - "reportAbuseOf": string; + /** + * {name}ã‚’é€šå ±ã™ã‚‹ + */ + "reportAbuseOf": ParameterizedString<"name">; + /** + * é€šå ±ç†ç”±ã®è©³ç´°ã‚’記入ã—ã¦ãã ã•ã„。対象ã®ãƒŽãƒ¼ãƒˆãŒã‚ã‚‹å ´åˆã¯ãã®URLも記入ã—ã¦ãã ã•ã„。 + */ "fillAbuseReportDescription": string; + /** + * 内容ãŒé€ä¿¡ã•ã‚Œã¾ã—ãŸã€‚ã”å ±å‘Šã‚ã‚ŠãŒã¨ã†ã”ã–ã„ã¾ã—ãŸã€‚ + */ "abuseReported": string; + /** + * é€šå ±è€… + */ "reporter": string; + /** + * é€šå ±å…ˆ + */ "reporteeOrigin": string; + /** + * é€šå ±å…ƒ + */ "reporterOrigin": string; + /** + * リモートサーãƒãƒ¼ã«é€šå ±ã‚’転é€ã™ã‚‹ + */ "forwardReport": string; + /** + * リモートサーãƒãƒ¼ã‹ã‚‰ã¯ã‚ãªãŸã®æƒ…å ±ã¯è¦‹ã‚Œãšã€åŒ¿åã®ã‚·ã‚¹ãƒ†ãƒ アカウントã¨ã—ã¦è¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ + */ "forwardReportIsAnonymous": string; + /** + * é€ä¿¡ + */ "send": string; + /** + * 対応済ã¿ã«ã™ã‚‹ + */ "abuseMarkAsResolved": string; + /** + * æ–°ã—ã„タブã§é–‹ã + */ "openInNewTab": string; + /** + * サイドビューã§é–‹ã + */ "openInSideView": string; + /** + * デフォルトã®ãƒŠãƒ“ゲーション + */ "defaultNavigationBehaviour": string; + /** + * ã“れらã®è¨å®šã‚’編集ã™ã‚‹ã¨ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒç ´æã™ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚ + */ "editTheseSettingsMayBreakAccount": string; + /** + * ノートã®ã‚µãƒ¼ãƒãƒ¼æƒ…å ± + */ "instanceTicker": string; - "waitingFor": string; + /** + * {x}ã‚’å¾…ã£ã¦ã„ã¾ã™ + */ + "waitingFor": ParameterizedString<"x">; + /** + * ランダム+ */ "random": string; + /** + * システム+ */ "system": string; + /** + * UI切り替㈠+ */ "switchUi": string; + /** + * デスクトップ + */ "desktop": string; + /** + * クリップ + */ "clip": string; + /** + * æ–°è¦ä½œæˆ + */ "createNew": string; + /** + * ä»»æ„ + */ "optional": string; + /** + * æ–°ã—ã„ã‚¯ãƒªãƒƒãƒ—ã‚’ä½œæˆ + */ "createNewClip": string; + /** + * クリップ解除 + */ "unclip": string; - "confirmToUnclipAlreadyClippedNote": string; + /** + * ã“ã®ãƒŽãƒ¼ãƒˆã¯ã™ã§ã«ã‚¯ãƒªãƒƒãƒ—「{name}ã€ã«å«ã¾ã‚Œã¦ã„ã¾ã™ã€‚ノートをã“ã®ã‚¯ãƒªãƒƒãƒ—ã‹ã‚‰é™¤å¤–ã—ã¾ã™ã‹ï¼Ÿ + */ + "confirmToUnclipAlreadyClippedNote": ParameterizedString<"name">; + /** + * パブリック + */ "public": string; + /** + * éžå…¬é–‹ + */ "private": string; - "i18nInfo": string; + /** + * Sharkeyã¯æœ‰å¿—ã«ã‚ˆã£ã¦æ§˜ã€…ãªè¨€èªžã«ç¿»è¨³ã•ã‚Œã¦ã„ã¾ã™ã€‚{link}ã§ç¿»è¨³ã«å”力ã§ãã¾ã™ã€‚ + */ + "i18nInfo": ParameterizedString<"link">; + /** + * アクセストークンã®ç®¡ç† + */ "manageAccessTokens": string; + /** + * ã‚¢ã‚«ã‚¦ãƒ³ãƒˆæƒ…å ± + */ "accountInfo": string; + /** + * ノートã®æ•° + */ "notesCount": string; + /** + * 返信ã—ãŸæ•° + */ "repliesCount": string; + /** + * ブーストã—ãŸæ•° + */ "renotesCount": string; + /** + * 返信ã•ã‚ŒãŸæ•° + */ "repliedCount": string; + /** + * ブーストã•ã‚ŒãŸæ•° + */ "renotedCount": string; + /** + * フォãƒãƒ¼æ•° + */ "followingCount": string; + /** + * フォãƒãƒ¯ãƒ¼æ•° + */ "followersCount": string; + /** + * リアクションã—ãŸæ•° + */ "sentReactionsCount": string; + /** + * リアクションã•ã‚ŒãŸæ•° + */ "receivedReactionsCount": string; + /** + * アンケートã«æŠ•ç¥¨ã—ãŸæ•° + */ "pollVotesCount": string; + /** + * アンケートã«æŠ•ç¥¨ã•ã‚ŒãŸæ•° + */ "pollVotedCount": string; + /** + * ã¯ã„ + */ "yes": string; + /** + * ã„ã„㈠+ */ "no": string; + /** + * ドライブã®ãƒ•ã‚¡ã‚¤ãƒ«æ•° + */ "driveFilesCount": string; + /** + * ãƒ‰ãƒ©ã‚¤ãƒ–ä½¿ç”¨é‡ + */ "driveUsage": string; + /** + * クãƒãƒ¼ãƒ©ãƒ¼ã«ã‚ˆã‚‹ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ã‚’æ‹’å¦ + */ "noCrawle": string; + /** + * 外部ã®æ¤œç´¢ã‚¨ãƒ³ã‚¸ãƒ³ã«ã‚ãªãŸã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãƒšãƒ¼ã‚¸ã€ãƒŽãƒ¼ãƒˆã€Pagesãªã©ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„を登録(インデックス)ã—ãªã„よã†è¦æ±‚ã—ã¾ã™ã€‚ + */ "noCrawleDescription": string; + /** + * フォãƒãƒ¼ã‚’承èªåˆ¶ã«ã—ã¦ã‚‚ã€ãƒŽãƒ¼ãƒˆã®å…¬é–‹ç¯„囲を「フォãƒãƒ¯ãƒ¼ã€ã«ã—ãªã„é™ã‚Šã€èª°ã§ã‚‚ã‚ãªãŸã®ãƒŽãƒ¼ãƒˆã‚’見るã“ã¨ãŒã§ãã¾ã™ã€‚ + */ "lockedAccountInfo": string; + /** + * デフォルトã§ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’センシティブè¨å®šã«ã™ã‚‹ + */ "alwaysMarkSensitive": string; + /** + * 添付画åƒã®ã‚µãƒ ãƒã‚¤ãƒ«ã‚’オリジナル画質ã«ã™ã‚‹ + */ "loadRawImages": string; + /** + * アニメーション画åƒã‚’å†ç”Ÿã—ãªã„ + */ "disableShowingAnimatedImages": string; + /** + * メディアãŒã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ã§ã‚ã‚‹ã“ã¨ã‚’分ã‹ã‚Šã‚„ã™ã表示 + */ "highlightSensitiveMedia": string; + /** + * 確èªã®ãƒ¡ãƒ¼ãƒ«ã‚’é€ä¿¡ã—ã¾ã—ãŸã€‚メールã«è¨˜è¼‰ã•ã‚ŒãŸãƒªãƒ³ã‚¯ã«ã‚¢ã‚¯ã‚»ã‚¹ã—ã¦ã€è¨å®šã‚’完了ã—ã¦ãã ã•ã„。 + */ "verificationEmailSent": string; + /** + * 未è¨å®š + */ "notSet": string; + /** + * メールアドレスãŒç¢ºèªã•ã‚Œã¾ã—㟠+ */ "emailVerified": string; + /** + * ãŠæ°—ã«å…¥ã‚ŠãƒŽãƒ¼ãƒˆã®æ•° + */ "noteFavoritesCount": string; + /** + * Pageã«ã„ã„ãã—ãŸæ•° + */ "pageLikesCount": string; + /** + * Pageã«ã„ã„ãã•ã‚ŒãŸæ•° + */ "pageLikedCount": string; + /** + * 連絡先 + */ "contact": string; + /** + * システムã®ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã®ãƒ•ã‚©ãƒ³ãƒˆã‚’使ㆠ+ */ "useSystemFont": string; + /** + * クリップ + */ "clips": string; + /** + * 実験的機能 + */ "experimentalFeatures": string; + /** + * 実験的 + */ "experimental": string; + /** + * ã“ã‚Œã¯å®Ÿé¨“çš„ãªæ©Ÿèƒ½ã§ã™ã€‚仕様ãŒå¤‰æ›´ã•ã‚ŒãŸã‚Šã€æ£å¸¸ã«å‹•ä½œã—ãªã‹ã£ãŸã‚Šã™ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚ + */ "thisIsExperimentalFeature": string; + /** + * 開発者 + */ "developer": string; + /** + * アカウントを見ã¤ã‘ã‚„ã™ãã™ã‚‹ + */ "makeExplorable": string; + /** + * オフã«ã™ã‚‹ã¨ã€ã€Œã¿ã¤ã‘ã‚‹ã€ã«ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒè¼‰ã‚‰ãªããªã‚Šã¾ã™ã€‚ + */ "makeExplorableDescription": string; + /** + * 公開ノートをインデックスä¸å¯ã«ã™ã‚‹ + */ "makeIndexable": string; + /** + * ノート検索ãŒã‚ãªãŸã®å…¬é–‹ãƒŽãƒ¼ãƒˆã‚’インデックス化ã—ãªã„よã†ã«ã—ã¾ã™ã€‚ + */ "makeIndexableDescription": string; + /** + * タイムラインã®ãƒŽãƒ¼ãƒˆã‚’離ã—ã¦è¡¨ç¤º + */ "showGapBetweenNotesInTimeline": string; + /** + * 複製 + */ "duplicate": string; + /** + * å·¦ + */ "left": string; + /** + * ä¸å¤® + */ "center": string; + /** + * 広ㄠ+ */ "wide": string; + /** + * ç‹ã„ + */ "narrow": string; + /** + * è¨å®šã¯ãƒšãƒ¼ã‚¸ãƒªãƒãƒ¼ãƒ‰å¾Œã«åæ˜ ã•ã‚Œã¾ã™ã€‚今ã™ãリãƒãƒ¼ãƒ‰ã—ã¾ã™ã‹ï¼Ÿ + */ "reloadToApplySetting": string; + /** + * åæ˜ ã«ã¯å†èµ·å‹•ãŒå¿…è¦ã§ã™ã€‚ + */ "needReloadToApply": string; + /** + * タイトルãƒãƒ¼ã‚’表示ã™ã‚‹ + */ "showTitlebar": string; + /** + * ã‚ャッシュをクリア + */ "clearCache": string; - "onlineUsersCount": string; - "nUsers": string; - "nNotes": string; + /** + * {n}人ãŒã‚ªãƒ³ãƒ©ã‚¤ãƒ³ + */ + "onlineUsersCount": ParameterizedString<"n">; + /** + * {n}ユーザー + */ + "nUsers": ParameterizedString<"n">; + /** + * {n}ノート + */ + "nNotes": ParameterizedString<"n">; + /** + * エラーリãƒãƒ¼ãƒˆã‚’é€ä¿¡ + */ "sendErrorReports": string; + /** + * オンã«ã™ã‚‹ã¨ã€å•é¡ŒãŒç™ºç”Ÿã—ãŸã¨ãã«ã‚¨ãƒ©ãƒ¼ã®è©³ç´°æƒ…å ±ãŒSharkeyã«å…±æœ‰ã•ã‚Œã€ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã®å“質å‘上ã«å½¹ç«‹ã¦ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ã‚¨ãƒ©ãƒ¼æƒ…å ±ã«ã¯ã€OSã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã€ãƒ–ラウザã®ç¨®é¡žã€è¡Œå‹•å±¥æ´ãªã©ãŒå«ã¾ã‚Œã¾ã™ã€‚ + */ "sendErrorReportsDescription": string; + /** + * マイテーマ + */ "myTheme": string; + /** + * 背景 + */ "backgroundColor": string; + /** + * アクセント + */ "accentColor": string; + /** + * æ–‡å— + */ "textColor": string; + /** + * åå‰ã‚’付ã‘ã¦ä¿å˜ + */ "saveAs": string; + /** + * 高度 + */ "advanced": string; + /** + * 高度ãªè¨å®š + */ "advancedSettings": string; + /** + * 値 + */ "value": string; + /** + * 作æˆæ—¥æ™‚ + */ "createdAt": string; + /** + * 更新日時 + */ "updatedAt": string; + /** + * ä¿å˜ã—ã¾ã™ã‹ï¼Ÿ + */ "saveConfirm": string; + /** + * 削除ã—ã¾ã™ã‹ï¼Ÿ + */ "deleteConfirm": string; + /** + * 有効ãªå€¤ã§ã¯ã‚ã‚Šã¾ã›ã‚“。 + */ "invalidValue": string; + /** + * レジストリ + */ "registry": string; + /** + * アカウントを閉鎖ã™ã‚‹ + */ "closeAccount": string; + /** + * ç¾åœ¨ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ + */ "currentVersion": string; + /** + * 最新ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ + */ "latestVersion": string; + /** + * ãŠä½¿ã„ã®ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆã¯æœ€æ–°ã§ã™ã€‚ + */ "youAreRunningUpToDateClient": string; + /** + * æ–°ã—ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆãŒåˆ©ç”¨å¯èƒ½ã§ã™ã€‚ + */ "newVersionOfClientAvailable": string; + /** + * ä½¿ç”¨é‡ + */ "usageAmount": string; + /** + * å®¹é‡ + */ "capacity": string; + /** + * ä½¿ç”¨ä¸ + */ "inUse": string; + /** + * コードを編集 + */ "editCode": string; + /** + * é©ç”¨ + */ "apply": string; + /** + * サーãƒãƒ¼ã‹ã‚‰ã®ãŠçŸ¥ã‚‰ã›ã‚’å—ã‘å–ã‚‹ + */ "receiveAnnouncementFromInstance": string; + /** + * メール通知 + */ "emailNotification": string; + /** + * 公開 + */ "publish": string; + /** + * ãƒãƒ£ãƒ³ãƒãƒ«å†…検索 + */ "inChannelSearch": string; + /** + * å³ã‚¯ãƒªãƒƒã‚¯ã§ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ãƒ”ッカーを開ã + */ "useReactionPickerForContextMenu": string; - "typingUsers": string; + /** + * {users}ãŒå…¥åŠ›ä¸ + */ + "typingUsers": ParameterizedString<"users">; + /** + * 特定ã®æ—¥ä»˜ã«ã‚¸ãƒ£ãƒ³ãƒ— + */ "jumpToSpecifiedDate": string; + /** + * éŽåŽ»ã®ã‚¿ã‚¤ãƒ ラインを表示ã—ã¦ã„ã¾ã™ + */ "showingPastTimeline": string; + /** + * クリア + */ "clear": string; + /** + * å…¨ã¦æ—¢èªã«ã™ã‚‹ + */ "markAllAsRead": string; + /** + * 戻る + */ "goBack": string; + /** + * ã„ã„ã解除ã—ã¾ã™ã‹ï¼Ÿ + */ "unlikeConfirm": string; + /** + * フルビュー + */ "fullView": string; + /** + * フルビュー解除 + */ "quitFullView": string; + /** + * èª¬æ˜Žã‚’è¿½åŠ + */ "addDescription": string; + /** + * 個々ã®ãƒŽãƒ¼ãƒˆã®ãƒ¡ãƒ‹ãƒ¥ãƒ¼ã‹ã‚‰ã€Œãƒ”ン留ã‚ã€ã‚’é¸æŠžã™ã‚‹ã“ã¨ã§ã€ã“ã“ã«ãƒŽãƒ¼ãƒˆã‚’表示ã—ã¦ãŠãã“ã¨ãŒã§ãã¾ã™ã€‚ + */ "userPagePinTip": string; + /** + * 宛先ã«å«ã¾ã‚Œã¦ã„ãªã„メンションãŒã‚ã‚Šã¾ã™ + */ "notSpecifiedMentionWarning": string; + /** + * æƒ…å ± + */ "info": string; + /** + * ãƒ¦ãƒ¼ã‚¶ãƒ¼æƒ…å ± + */ "userInfo": string; + /** + * ä¸æ˜Ž + */ "unknown": string; + /** + * オンライン状態 + */ "onlineStatus": string; + /** + * ã‚ªãƒ³ãƒ©ã‚¤ãƒ³çŠ¶æ…‹ã‚’éš ã™ + */ "hideOnlineStatus": string; + /** + * ã‚ªãƒ³ãƒ©ã‚¤ãƒ³çŠ¶æ…‹ã‚’éš ã™ã¨ã€æ¤œç´¢ãªã©ã®ä¸€éƒ¨æ©Ÿèƒ½ã«ãŠã„ã¦åˆ©ä¾¿æ€§ãŒä½Žä¸‹ã™ã‚‹ã“ã¨ãŒã‚ã‚Šã¾ã™ã€‚ + */ "hideOnlineStatusDescription": string; + /** + * オンライン + */ "online": string; + /** + * アクティブ + */ "active": string; + /** + * オフライン + */ "offline": string; + /** + * éžæŽ¨å¥¨ + */ "notRecommended": string; + /** + * Botプãƒãƒ†ã‚¯ã‚·ãƒ§ãƒ³ + */ "botProtection": string; + /** + * サーãƒãƒ¼ãƒ–ãƒãƒƒã‚¯ãƒ»ã‚µã‚¤ãƒ¬ãƒ³ã‚¹ + */ "instanceBlocking": string; + /** + * アカウントをé¸æŠž + */ "selectAccount": string; + /** + * アカウントを切り替㈠+ */ "switchAccount": string; + /** + * 有効 + */ "enabled": string; + /** + * 無効 + */ "disabled": string; + /** + * クイックアクション + */ "quickAction": string; + /** + * ユーザー + */ "user": string; + /** + * ç®¡ç† + */ "administration": string; + /** + * アカウント + */ "accounts": string; + /** + * 切り替㈠+ */ "switch": string; + /** + * 管ç†è€…æƒ…å ±ãŒè¨å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。 + */ "noMaintainerInformationWarning": string; + /** + * Botプãƒãƒ†ã‚¯ã‚·ãƒ§ãƒ³ãŒè¨å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。 + */ "noBotProtectionWarning": string; + /** + * è¨å®šã™ã‚‹ + */ "configure": string; + /** + * ギャラリーã¸æŠ•ç¨¿ + */ "postToGallery": string; + /** + * ã“ã®ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ã§æŠ•ç¨¿ + */ "postToHashtag": string; + /** + * ギャラリー + */ "gallery": string; + /** + * 最近ã®æŠ•ç¨¿ + */ "recentPosts": string; + /** + * 人気ã®æŠ•ç¨¿ + */ "popularPosts": string; + /** + * ノートã§å…±æœ‰ + */ "shareWithNote": string; + /** + * 広告 + */ "ads": string; + /** + * æœŸé™ + */ "expiration": string; + /** + * 開始期間 + */ "startingperiod": string; + /** + * メモ + */ "memo": string; + /** + * 優先度 + */ "priority": string; + /** + * 高 + */ "high": string; + /** + * ä¸ + */ "middle": string; + /** + * 低 + */ "low": string; + /** + * メールアドレスã®è¨å®šãŒã•ã‚Œã¦ã„ã¾ã›ã‚“。 + */ "emailNotConfiguredWarning": string; + /** + * 比率 + */ "ratio": string; + /** + * 本文をプレビュー + */ "previewNoteText": string; + /** + * カスタムCSS + */ "customCss": string; + /** + * ã“ã®è¨å®šã¯å¿…ãšçŸ¥è˜ã®ã‚ã‚‹æ–¹ãŒè¡Œã£ã¦ãã ã•ã„。ä¸é©åˆ‡ãªè¨å®šã‚’è¡Œã†ã¨ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆãŒæ£å¸¸ã«ä½¿ç”¨ã§ããªããªã‚‹æã‚ŒãŒã‚ã‚Šã¾ã™ã€‚ + */ "customCssWarn": string; + /** + * ã‚°ãƒãƒ¼ãƒãƒ« + */ "global": string; + /** + * アイコンを四角形ã§è¡¨ç¤º + */ "squareAvatars": string; + /** + * é€ä¿¡ + */ "sent": string; + /** + * å—ä¿¡ + */ "received": string; + /** + * 検索çµæžœ + */ "searchResult": string; + /** + * ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚° + */ "hashtags": string; + /** + * トラブルシューティング + */ "troubleshooting": string; + /** + * UIã«ã¼ã‹ã—効果を使用 + */ "useBlurEffect": string; + /** + * 詳ã—ã + */ "learnMore": string; + /** + * SharkeyãŒæ›´æ–°ã•ã‚Œã¾ã—ãŸï¼ + */ "misskeyUpdated": string; + /** + * æ›´æ–°æƒ…å ±ã‚’è¦‹ã‚‹ + */ "whatIsNew": string; + /** + * 翻訳 + */ "translate": string; - "translatedFrom": string; + /** + * {x}ã‹ã‚‰ç¿»è¨³ + */ + "translatedFrom": ParameterizedString<"x">; + /** + * アカウントã®å‰Šé™¤ãŒé€²è¡Œä¸ã§ã™ + */ "accountDeletionInProgress": string; + /** + * サーãƒãƒ¼ä¸Šã§ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’一æ„ã«è˜åˆ¥ã™ã‚‹ãŸã‚ã®åå‰ã€‚アルファベット(a~z, A~Z)ã€æ•°å—(0~9)ã€ãŠã‚ˆã³ã‚¢ãƒ³ãƒ€ãƒ¼ãƒãƒ¼(_)ãŒä½¿ç”¨ã§ãã¾ã™ã€‚ユーザーåã¯å¾Œã‹ã‚‰å¤‰æ›´ã™ã‚‹ã“ã¨ã¯å‡ºæ¥ã¾ã›ã‚“。 + */ "usernameInfo": string; + /** + * è—モード + */ "aiChanMode": string; + /** + * 開発者モード + */ "devMode": string; + /** + * CWã‚’ç¶æŒã™ã‚‹ + */ "keepCw": string; + /** + * Pub/Subã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ + */ "pubSub": string; + /** + * ç›´è¿‘ã®é€šä¿¡ + */ "lastCommunication": string; + /** + * 解決済㿠+ */ "resolved": string; + /** + * 未解決 + */ "unresolved": string; + /** + * フォãƒãƒ¯ãƒ¼ã‚’解除 + */ "breakFollow": string; + /** + * フォãƒãƒ¯ãƒ¼è§£é™¤ã—ã¾ã™ã‹ï¼Ÿ + */ "breakFollowConfirm": string; + /** + * オンã«ãªã£ã¦ã„ã¾ã™ + */ "itsOn": string; + /** + * オフã«ãªã£ã¦ã„ã¾ã™ + */ "itsOff": string; + /** + * オン + */ "on": string; + /** + * オフ + */ "off": string; + /** + * アカウント登録ã«ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’å¿…é ˆã«ã™ã‚‹ + */ "emailRequiredForSignup": string; + /** + * æ–°è¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æ‰¿èªãŒå¿…è¦ + */ "approvalRequiredForSignup": string; + /** + * æœªèª + */ "unread": string; + /** + * フィルタ + */ "filter": string; + /** + * コントãƒãƒ¼ãƒ«ãƒ‘ãƒãƒ« + */ "controlPanel": string; + /** + * ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ç®¡ç† + */ "manageAccounts": string; + /** + * リアクション一覧を公開ã™ã‚‹ + */ "makeReactionsPublic": string; + /** + * ã‚ãªãŸãŒã—ãŸãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ä¸€è¦§ã‚’誰ã§ã‚‚見れるよã†ã«ã—ã¾ã™ã€‚ + */ "makeReactionsPublicDescription": string; + /** + * クラシック + */ "classic": string; + /** + * スレッドをミュート + */ "muteThread": string; + /** + * スレッドã®ãƒŸãƒ¥ãƒ¼ãƒˆã‚’解除 + */ "unmuteThread": string; + /** + * フォãƒãƒ¼ã®å…¬é–‹ç¯„囲 + */ "followingVisibility": string; + /** + * フォãƒãƒ¯ãƒ¼ã®å…¬é–‹ç¯„囲 + */ "followersVisibility": string; + /** + * ã•ã‚‰ã«ã‚¹ãƒ¬ãƒƒãƒ‰ã‚’見る + */ "continueThread": string; + /** + * アカウントãŒå‰Šé™¤ã•ã‚Œã¾ã™ã€‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ + */ "deleteAccountConfirm": string; + /** + * パスワードãŒé–“é•ã£ã¦ã„ã¾ã™ã€‚ + */ "incorrectPassword": string; - "voteConfirm": string; - "voteConfirmMulti": string; + /** + * 「{choice}ã€ã«æŠ•ç¥¨ã—ã¾ã™ã‹ï¼Ÿ + */ + "voteConfirm": ParameterizedString<"choice">; + /** + * 「{choice}ã€ã«æŠ•ç¥¨ã—ã¾ã™ã‹ï¼Ÿ + *  確èªå¾Œã€é¸æŠžè‚¢ã‚’増やã™ã“ã¨ãŒã§ãã¾ã™ã€‚ + */ + "voteConfirmMulti": ParameterizedString<"choice">; + /** + * éš ã™ + */ "hide": string; + /** + * モãƒã‚¤ãƒ«ãƒ‡ãƒã‚¤ã‚¹ã®ã¨ãドãƒãƒ¯ãƒ¼ã§è¡¨ç¤º + */ "useDrawerReactionPickerForMobile": string; - "welcomeBackWithName": string; - "clickToFinishEmailVerification": string; + /** + * ãŠã‹ãˆã‚Šãªã•ã„ã€{name}ã•ã‚“ + */ + "welcomeBackWithName": ParameterizedString<"name">; + /** + * [{ok}]を押ã—ã¦ã€ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã®ç¢ºèªã‚’完了ã—ã¦ãã ã•ã„。 + */ + "clickToFinishEmailVerification": ParameterizedString<"ok">; + /** + * デãƒã‚¤ã‚¹ã‚¿ã‚¤ãƒ— + */ "overridedDeviceKind": string; + /** + * スマートフォン + */ "smartphone": string; + /** + * タブレット + */ "tablet": string; + /** + * 自動 + */ "auto": string; + /** + * テーマカラー + */ "themeColor": string; + /** + * サイズ + */ "size": string; + /** + * 列ã®æ•° + */ "numberOfColumn": string; + /** + * 検索 + */ "searchByGoogle": string; + /** + * サーãƒãƒ¼ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã®ãƒ©ã‚¤ãƒˆãƒ†ãƒ¼ãƒž + */ "instanceDefaultLightTheme": string; + /** + * サーãƒãƒ¼ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã®ãƒ€ãƒ¼ã‚¯ãƒ†ãƒ¼ãƒž + */ "instanceDefaultDarkTheme": string; + /** + * オブジェクト形å¼ã®ãƒ†ãƒ¼ãƒžã‚³ãƒ¼ãƒ‰ã‚’記入ã—ã¾ã™ã€‚ + */ "instanceDefaultThemeDescription": string; + /** + * ミュートã™ã‚‹æœŸé™ + */ "mutePeriod": string; + /** + * æœŸé™ + */ "period": string; + /** + * ç„¡æœŸé™ + */ "indefinitely": string; + /** + * 10分 + */ "tenMinutes": string; + /** + * 1時間 + */ "oneHour": string; + /** + * 1æ—¥ + */ "oneDay": string; + /** + * 1週間 + */ "oneWeek": string; + /** + * 1ヶ月 + */ "oneMonth": string; + /** + * åæ˜ ã•ã‚Œã‚‹ã¾ã§æ™‚é–“ãŒã‹ã‹ã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ã€‚ + */ "reflectMayTakeTime": string; + /** + * ã‚¢ã‚«ã‚¦ãƒ³ãƒˆæƒ…å ±ã®å–å¾—ã«å¤±æ•—ã—ã¾ã—㟠+ */ "failedToFetchAccountInformation": string; + /** + * レート制é™ã‚’超ãˆã¾ã—㟠+ */ "rateLimitExceeded": string; + /** + * ç”»åƒã®ã‚¯ãƒãƒƒãƒ— + */ "cropImage": string; + /** + * ç”»åƒã‚’クãƒãƒƒãƒ—ã—ã¾ã™ã‹ï¼Ÿ + */ "cropImageAsk": string; + /** + * クãƒãƒƒãƒ—ã™ã‚‹ + */ "cropYes": string; + /** + * ãã®ã¾ã¾ä½¿ã† + */ "cropNo": string; + /** + * ファイル + */ "file": string; - "recentNHours": string; - "recentNDays": string; + /** + * ç›´è¿‘{n}時間 + */ + "recentNHours": ParameterizedString<"n">; + /** + * ç›´è¿‘{n}æ—¥ + */ + "recentNDays": ParameterizedString<"n">; + /** + * メールサーãƒãƒ¼ã®è¨å®šãŒã•ã‚Œã¦ã„ã¾ã›ã‚“。 + */ "noEmailServerWarning": string; + /** + * 未対応ã®é€šå ±ãŒã‚ã‚Šã¾ã™ã€‚ + */ "thereIsUnresolvedAbuseReportWarning": string; + /** + * 承èªå¾…ã¡ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒã„る。 + */ "pendingUserApprovals": string; + /** + * 推奨 + */ "recommended": string; + /** + * ãƒã‚§ãƒƒã‚¯ + */ "check": string; + /** + * ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ‰ãƒ©ã‚¤ãƒ–容é‡ä¸Šé™ã‚’変更 + */ "driveCapOverrideLabel": string; + /** + * 0以下を指定ã™ã‚‹ã¨è§£é™¤ã•ã‚Œã¾ã™ã€‚ + */ "driveCapOverrideCaption": string; + /** + * 閲覧ã™ã‚‹ã«ã¯ç®¡ç†è€…アカウントã§ãƒã‚°ã‚¤ãƒ³ã—ã¦ã„ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ + */ "requireAdminForView": string; + /** + * システムã«ã‚ˆã‚Šè‡ªå‹•ã§ä½œæˆãƒ»ç®¡ç†ã•ã‚Œã¦ã„るアカウントã§ã™ã€‚ + */ "isSystemAccount": string; - "typeToConfirm": string; + /** + * ã“ã®æ“作を行ã†ã«ã¯ {x} ã¨å…¥åŠ›ã—ã¦ãã ã•ã„ + */ + "typeToConfirm": ParameterizedString<"x">; + /** + * アカウント削除 + */ "deleteAccount": string; + /** + * 承èªã™ã‚‹ + */ "approveAccount": string; + /** + * æ‹’å¦ã¨å‰Šé™¤ + */ "denyAccount": string; + /** + * 承èªæ¸ˆã¿ + */ "approved": string; + /** + * 承èªã•ã‚Œã¦ã„ãªã„ + */ "notApproved": string; + /** + * 承èªçŠ¶æ³ + */ "approvalStatus": string; + /** + * ドã‚ュメント + */ "document": string; + /** + * ページã‚ャッシュ数 + */ "numberOfPageCache": string; + /** + * 多ãã™ã‚‹ã¨åˆ©ä¾¿æ€§ãŒå‘上ã—ã¾ã™ãŒã€è² è·ã¨ãƒ¡ãƒ¢ãƒªä½¿ç”¨é‡ãŒå¢—ãˆã¾ã™ã€‚ + */ "numberOfPageCacheDescription": string; + /** + * スレッド内ã®è¿”ä¿¡æ•° + */ "numberOfReplies": string; + /** + * ã“ã®æ•°å€¤ã‚’大ããã™ã‚‹ã¨ã€ã‚ˆã‚Šå¤šãã®è¿”ä¿¡ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ã“ã®å€¤ã‚’大ããã—ã™ãŽã‚‹ã¨ã€è¿”ä¿¡ãŒçª®å±ˆã«ãªã‚Šã€èªã‚ãªããªã‚‹ã“ã¨ãŒã‚ã‚Šã¾ã™ã€‚ + */ "numberOfRepliesDescription": string; + /** + * ブーストè¨å®š + */ + "boostSettings": string; + /** + * å¯è¦–性セレクタを表示 + */ + "showVisibilitySelectorOnBoost": string; + /** + * 無効ã®å ´åˆã€ä»¥ä¸‹ã§å®šç¾©ã•ã‚Œã‚‹ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã®å¯è¦–性ãŒä½¿ç”¨ã•ã‚Œã€ã‚»ãƒ¬ã‚¯ã‚¿ã¯è¡¨ç¤ºã•ã‚Œã¾ã›ã‚“。 + */ + "showVisibilitySelectorOnBoostDescription": string; + /** + * デフォルトã®ãƒ–ーストå¯è¦–性ã®è¨å®š + */ + "visibilityOnBoost": string; + /** + * ãƒã‚°ã‚¢ã‚¦ãƒˆã—ã¾ã™ã‹ï¼Ÿ + */ "logoutConfirm": string; + /** + * 最終利用日時 + */ "lastActiveDate": string; + /** + * ステータスãƒãƒ¼ + */ "statusbar": string; + /** + * é¸æŠžã—ã¦ãã ã•ã„ + */ "pleaseSelect": string; + /** + * å転 + */ "reverse": string; + /** + * 色付ã + */ "colored": string; + /** + * æ›´æ–°é–“éš” + */ "refreshInterval": string; + /** + * ラベル + */ "label": string; + /** + * タイプ + */ "type": string; + /** + * 速度 + */ "speed": string; + /** + * é…ã„ + */ "slow": string; + /** + * 速ㄠ+ */ "fast": string; + /** + * センシティブãªãƒ¡ãƒ‡ã‚£ã‚¢ã®æ¤œå‡º + */ "sensitiveMediaDetection": string; + /** + * ãƒãƒ¼ã‚«ãƒ«ã®ã¿ + */ "localOnly": string; + /** + * リモートã®ã¿ + */ "remoteOnly": string; + /** + * アップãƒãƒ¼ãƒ‰å¤±æ•— + */ "failedToUpload": string; + /** + * ä¸é©åˆ‡ãªå†…容をå«ã‚€å¯èƒ½æ€§ãŒã‚ã‚‹ã¨åˆ¤å®šã•ã‚ŒãŸãŸã‚アップãƒãƒ¼ãƒ‰ã§ãã¾ã›ã‚“。 + */ "cannotUploadBecauseInappropriate": string; + /** + * ドライブã®ç©ºã容é‡ãŒç„¡ã„ãŸã‚アップãƒãƒ¼ãƒ‰ã§ãã¾ã›ã‚“。 + */ "cannotUploadBecauseNoFreeSpace": string; + /** + * ファイルサイズã®åˆ¶é™ã‚’超ãˆã¦ã„ã‚‹ãŸã‚アップãƒãƒ¼ãƒ‰ã§ãã¾ã›ã‚“。 + */ "cannotUploadBecauseExceedsFileSizeLimit": string; + /** + * ベータ + */ "beta": string; + /** + * 自動センシティブ判定 + */ "enableAutoSensitive": string; + /** + * 利用å¯èƒ½ãªå ´åˆã¯ã€æ©Ÿæ¢°å¦ç¿’を利用ã—ã¦è‡ªå‹•ã§ãƒ¡ãƒ‡ã‚£ã‚¢ã«ã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–フラグをè¨å®šã—ã¾ã™ã€‚ã“ã®æ©Ÿèƒ½ã‚’オフã«ã—ã¦ã‚‚ã€ã‚µãƒ¼ãƒãƒ¼ã«ã‚ˆã£ã¦ã¯è‡ªå‹•ã§è¨å®šã•ã‚Œã‚‹ã“ã¨ãŒã‚ã‚Šã¾ã™ã€‚ + */ "enableAutoSensitiveDescription": string; + /** + * ユーザーã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã®ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ã‚’ã€æ¨ã¦ã‚¢ãƒ‰ã‹ã©ã†ã‹ã‚„実際ã«é€šä¿¡å¯èƒ½ã‹ã©ã†ã‹ãªã©ã‚’判定ã—よりç©æ¥µçš„ã«è¡Œã„ã¾ã™ã€‚オフã«ã™ã‚‹ã¨å˜ã«æ–‡å—列ã¨ã—ã¦æ£ã—ã„ã‹ã©ã†ã‹ã®ã¿ãƒã‚§ãƒƒã‚¯ã•ã‚Œã¾ã™ã€‚ + */ "activeEmailValidationDescription": string; + /** + * ナビゲーションãƒãƒ¼ + */ "navbar": string; + /** + * シャッフル + */ "shuffle": string; + /** + * アカウント + */ "account": string; + /** + * 移動 + */ "move": string; + /** + * プッシュ通知 + */ "pushNotification": string; + /** + * プッシュ通知を有効化 + */ "subscribePushNotification": string; + /** + * プッシュ通知をåœæ¢ã™ã‚‹ + */ "unsubscribePushNotification": string; + /** + * プッシュ通知ã¯æœ‰åŠ¹ã§ã™ + */ "pushNotificationAlreadySubscribed": string; + /** + * ブラウザã‹ã‚µãƒ¼ãƒãƒ¼ãŒãƒ—ッシュ通知ã«éžå¯¾å¿œ + */ "pushNotificationNotSupported": string; + /** + * 通知ãŒæ—¢èªã«ãªã£ãŸã‚‰ãƒ—ッシュ通知を削除ã™ã‚‹ + */ "sendPushNotificationReadMessage": string; + /** + * 端末ã®é›»æ± 消費é‡ãŒå¢—åŠ ã™ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚ + */ "sendPushNotificationReadMessageCaption": string; + /** + * 最大化 + */ "windowMaximize": string; + /** + * 最å°åŒ– + */ "windowMinimize": string; + /** + * å…ƒã«æˆ»ã™ + */ "windowRestore": string; + /** + * ã‚ャプション + */ "caption": string; + /** + * Botアカウントã§ãƒã‚°ã‚¤ãƒ³ä¸ + */ "loggedInAsBot": string; + /** + * ツール + */ "tools": string; + /** + * èªã¿è¾¼ã‚ã¾ã›ã‚“ + */ "cannotLoad": string; + /** + * プãƒãƒ•ã‚£ãƒ¼ãƒ«è¡¨ç¤ºå›žæ•° + */ "numberOfProfileView": string; + /** + * ã„ã„ãï¼ + */ "like": string; + /** + * ã„ã„ãを解除 + */ "unlike": string; + /** + * 絵文å—ã®ã‚ˆã†ãªãƒ‡ãƒ•ã‚©ãƒ«ãƒˆ + */ "defaultLike": string; + /** + * ã„ã„ãæ•° + */ "numberOfLikes": string; + /** + * 表示 + */ "show": string; + /** + * 今後表示ã—ãªã„ + */ "neverShow": string; + /** + * ã¾ãŸå¾Œã§ + */ "remindMeLater": string; + /** + * Sharkeyã‚’æ°—ã«å…¥ã£ã¦ã„ãŸã ã‘ã¾ã—ãŸã‹ï¼Ÿ + */ "didYouLikeMisskey": string; - "pleaseDonate": string; + /** + * Sharkeyã¯{host}ãŒä½¿ç”¨ã—ã¦ã„ã‚‹ç„¡æ–™ã®ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã§ã™ã€‚ã“ã‚Œã‹ã‚‰ã‚‚開発を続ã‘られるよã†ã«ã€ãœã²å¯„付をãŠé¡˜ã„ã—ã¾ã™ï¼ + */ + "pleaseDonate": ParameterizedString<"host">; + /** + * インスタンス管ç†è€…ã¸ã®å¯„付ã«ã‚ˆã£ã¦{host}を直接サãƒãƒ¼ãƒˆã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚ + */ + "pleaseDonateInstance": ParameterizedString<"host">; + /** + * 対応ã™ã‚‹ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã¯{anchor}ã‹ã‚‰åˆ©ç”¨å¯èƒ½ã§ã™ã€‚ + */ + "correspondingSourceIsAvailable": ParameterizedString<"anchor">; + /** + * ãƒãƒ¼ãƒ« + */ "roles": string; + /** + * ãƒãƒ¼ãƒ« + */ "role": string; + /** + * ãƒãƒ¼ãƒ«ã¯ã‚ã‚Šã¾ã›ã‚“ + */ "noRole": string; + /** + * 一般ユーザー + */ "normalUser": string; + /** + * 未定義 + */ "undefined": string; + /** + * アサイン + */ "assign": string; + /** + * アサインを解除 + */ "unassign": string; + /** + * 色 + */ "color": string; + /** + * カスタム絵文å—ã®ç®¡ç† + */ "manageCustomEmojis": string; + /** + * ã‚¢ãƒã‚¿ãƒ¼ãƒ‡ã‚³ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã®ç®¡ç† + */ "manageAvatarDecorations": string; + /** + * ã“れ以上作æˆã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。 + */ "youCannotCreateAnymore": string; + /** + * 一時的ã«åˆ©ç”¨ã§ãã¾ã›ã‚“ + */ "cannotPerformTemporary": string; + /** + * æ“作回数ãŒåˆ¶é™ã‚’超éŽã™ã‚‹ãŸã‚一時的ã«åˆ©ç”¨ã§ãã¾ã›ã‚“。ã—ã°ã‚‰ã時間を置ã„ã¦ã‹ã‚‰å†åº¦ãŠè©¦ã—ãã ã•ã„。 + */ "cannotPerformTemporaryDescription": string; + /** + * パラメータエラー + */ "invalidParamError": string; + /** + * リクエストパラメータã«å•é¡ŒãŒã‚ã‚Šã¾ã™ã€‚通常ã“ã‚Œã¯ãƒã‚°ã§ã™ãŒã€å…¥åŠ›ã—ãŸæ–‡å—æ•°ãŒå¤šã™ãŽã‚‹ç‰ã®å¯èƒ½æ€§ã‚‚ã‚ã‚Šã¾ã™ã€‚ + */ "invalidParamErrorDescription": string; + /** + * æ“作ãŒæ‹’å¦ã•ã‚Œã¾ã—㟠+ */ "permissionDeniedError": string; + /** + * ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ã¯ã“ã®æ“作を行ã†ãŸã‚ã®æ¨©é™ãŒã‚ã‚Šã¾ã›ã‚“。 + */ "permissionDeniedErrorDescription": string; + /** + * プリセット + */ "preset": string; + /** + * プリセットã‹ã‚‰é¸æŠž + */ "selectFromPresets": string; + /** + * 実績 + */ "achievements": string; + /** + * サーãƒãƒ¼ã®å¿œç”ãŒç„¡åŠ¹ã§ã™ + */ "gotInvalidResponseError": string; + /** + * サーãƒãƒ¼ãŒãƒ€ã‚¦ãƒ³ã¾ãŸã¯ãƒ¡ãƒ³ãƒ†ãƒŠãƒ³ã‚¹ã—ã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚ã—ã°ã‚‰ãã—ã¦ã‹ã‚‰å†åº¦ãŠè©¦ã—ãã ã•ã„。 + */ "gotInvalidResponseErrorDescription": string; + /** + * ã“ã®æŠ•ç¨¿ã¯è¿·æƒ‘ã«ãªã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚ + */ "thisPostMayBeAnnoying": string; + /** + * ホームã«æŠ•ç¨¿ + */ "thisPostMayBeAnnoyingHome": string; + /** + * ã‚„ã‚ã‚‹ + */ "thisPostMayBeAnnoyingCancel": string; + /** + * ã“ã®ã¾ã¾æŠ•ç¨¿ + */ "thisPostMayBeAnnoyingIgnore": string; + /** + * ã‚„ã‚ã‚‹ + */ + "thisPostIsMissingAltTextCancel": string; + /** + * ã“ã®ã¾ã¾æŠ•ç¨¿ + */ + "thisPostIsMissingAltTextIgnore": string; + /** + * ã“ã®æŠ•ç¨¿ã«æ·»ä»˜ã•ã‚ŒãŸãƒ•ã‚¡ã‚¤ãƒ«ã® 1 ã¤ã«ä»£æ›¿ãƒ†ã‚ストãŒã‚ã‚Šã¾ã›ã‚“。ã™ã¹ã¦ã®æ·»ä»˜ãƒ•ã‚¡ã‚¤ãƒ«ã«ä»£æ›¿ãƒ†ã‚ストãŒå«ã¾ã‚Œã¦ã„ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。 + */ + "thisPostIsMissingAltText": string; + /** + * 見ãŸã“ã¨ã®ã‚るブーストをçœç•¥ã—ã¦è¡¨ç¤º + */ "collapseRenotes": string; + /** + * ファイルを折りãŸãŸã‚€ + */ "collapseFiles": string; + /** + * 返信ã«ä¼šè©±ã‚’èªã¿è¾¼ã‚€ + */ "autoloadConversation": string; + /** + * サーãƒãƒ¼å†…部エラー + */ "internalServerError": string; + /** + * サーãƒãƒ¼å†…部ã§äºˆæœŸã—ãªã„エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ + */ "internalServerErrorDescription": string; + /** + * ã‚¨ãƒ©ãƒ¼æƒ…å ±ã‚’ã‚³ãƒ”ãƒ¼ + */ "copyErrorInfo": string; + /** + * ã“ã®ã‚µãƒ¼ãƒãƒ¼ã«ç™»éŒ²ã™ã‚‹ + */ "joinThisServer": string; + /** + * ä»–ã®ã‚µãƒ¼ãƒãƒ¼ã‚’探㙠+ */ "exploreOtherServers": string; + /** + * タイムラインを見ã¦ã¿ã‚‹ + */ "letsLookAtTimeline": string; + /** + * 連åˆãªã—ã«ã—ã¾ã™ã‹ï¼Ÿ + */ "disableFederationConfirm": string; + /** + * 連åˆãªã—ã«ã—ã¦ã‚‚投稿ã¯éžå…¬é–‹ã«ãªã‚Šã¾ã›ã‚“。ã»ã¨ã‚“ã©ã®å ´åˆã€é€£åˆãªã—ã«ã™ã‚‹å¿…è¦ã¯ã‚ã‚Šã¾ã›ã‚“。 + */ "disableFederationConfirmWarn": string; + /** + * 連åˆãªã—ã«ã™ã‚‹ + */ "disableFederationOk": string; + /** + * ç¾åœ¨ã“ã®ã‚µãƒ¼ãƒãƒ¼ã¯æ‹›å¾…制ã§ã™ã€‚招待コードをãŠæŒã¡ã®æ–¹ã®ã¿ç™»éŒ²ã§ãã¾ã™ã€‚ + */ "invitationRequiredToRegister": string; + /** + * ã“ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã¯ã€ç™»éŒ²ç†ç”±ã‚’指定ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã¿ã‚’å—ã‘入れã¦ã„ã¾ã™ã€‚ + */ "approvalRequiredToRegister": string; + /** + * ã“ã®ã‚µãƒ¼ãƒãƒ¼ã§ã¯ãƒ¡ãƒ¼ãƒ«é…ä¿¡ã¯ã‚µãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ã¾ã›ã‚“ + */ "emailNotSupported": string; + /** + * ãƒãƒ£ãƒ³ãƒãƒ«ã«æŠ•ç¨¿ + */ "postToTheChannel": string; + /** + * 後ã‹ã‚‰å¤‰æ›´ã§ãã¾ã›ã‚“。 + */ "cannotBeChangedLater": string; + /** + * リアクションã®å—ã‘入れ + */ "reactionAcceptance": string; + /** + * ã„ã„ãã®ã¿ + */ "likeOnly": string; + /** + * 全㦠(リモートã¯ã„ã„ãã®ã¿) + */ "likeOnlyForRemote": string; + /** + * éžã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ã®ã¿ + */ "nonSensitiveOnly": string; + /** + * éžã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ã®ã¿ (リモートã¯ã„ã„ãã®ã¿) + */ "nonSensitiveOnlyForLocalLikeOnlyForRemote": string; + /** + * 自分ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚ŒãŸãƒãƒ¼ãƒ« + */ "rolesAssignedToMe": string; + /** + * パスワードリセットã—ã¾ã™ã‹ï¼Ÿ + */ "resetPasswordConfirm": string; + /** + * センシティブワード + */ "sensitiveWords": string; + /** + * è¨å®šã—ãŸãƒ¯ãƒ¼ãƒ‰ãŒå«ã¾ã‚Œã‚‹ãƒŽãƒ¼ãƒˆã®å…¬é–‹ç¯„囲をホームã«ã—ã¾ã™ã€‚改行ã§åŒºåˆ‡ã£ã¦è¤‡æ•°è¨å®šã§ãã¾ã™ã€‚ + */ "sensitiveWordsDescription": string; + /** + * スペースã§åŒºåˆ‡ã‚‹ã¨AND指定ã«ãªã‚Šã€ã‚ーワードをスラッシュã§å›²ã‚€ã¨æ£è¦è¡¨ç¾ã«ãªã‚Šã¾ã™ã€‚ + */ "sensitiveWordsDescription2": string; + /** + * ç¦æ¢ãƒ¯ãƒ¼ãƒ‰ + */ + "prohibitedWords": string; + /** + * è¨å®šã—ãŸãƒ¯ãƒ¼ãƒ‰ãŒå«ã¾ã‚Œã‚‹ãƒŽãƒ¼ãƒˆã‚’投稿ã—よã†ã¨ã—ãŸéš›ã€ã‚¨ãƒ©ãƒ¼ã¨ãªã‚‹ã‚ˆã†ã«ã—ã¾ã™ã€‚改行ã§åŒºåˆ‡ã£ã¦è¤‡æ•°è¨å®šã§ãã¾ã™ã€‚ + */ + "prohibitedWordsDescription": string; + /** + * スペースã§åŒºåˆ‡ã‚‹ã¨AND指定ã«ãªã‚Šã€ã‚ーワードをスラッシュã§å›²ã‚€ã¨æ£è¦è¡¨ç¾ã«ãªã‚Šã¾ã™ã€‚ + */ + "prohibitedWordsDescription2": string; + /** + * éžè¡¨ç¤ºãƒãƒƒã‚·ãƒ¥ã‚¿ã‚° + */ "hiddenTags": string; + /** + * è¨å®šã—ãŸã‚¿ã‚°ã‚’トレンドã«è¡¨ç¤ºã•ã›ãªã„よã†ã«ã—ã¾ã™ã€‚改行ã§åŒºåˆ‡ã£ã¦è¤‡æ•°è¨å®šã§ãã¾ã™ã€‚ + */ "hiddenTagsDescription": string; + /** + * ノート検索ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。 + */ "notesSearchNotAvailable": string; + /** + * ライセンス + */ "license": string; + /** + * ãŠæ°—ã«å…¥ã‚Šè§£é™¤ã—ã¾ã™ã‹ï¼Ÿ + */ "unfavoriteConfirm": string; + /** + * 自分ã®ã‚¯ãƒªãƒƒãƒ— + */ "myClips": string; + /** + * ドライブクリーナー + */ "drivecleaner": string; + /** + * ã™ã¹ã¦ã®ã‚ューを今ã™ãå†è©¦è¡Œ + */ "retryAllQueuesNow": string; + /** + * 今ã™ãå†è©¦è¡Œã—ã¾ã™ã‹ï¼Ÿ + */ "retryAllQueuesConfirmTitle": string; + /** + * 一時的ã«ã‚µãƒ¼ãƒãƒ¼ã®è² è·ãŒå¢—大ã™ã‚‹ã“ã¨ãŒã‚ã‚Šã¾ã™ã€‚ + */ "retryAllQueuesConfirmText": string; + /** + * リモートユーザーã®ãƒãƒ£ãƒ¼ãƒˆã‚’ç”Ÿæˆ + */ "enableChartsForRemoteUser": string; + /** + * リモートサーãƒãƒ¼ã®ãƒãƒ£ãƒ¼ãƒˆã‚’ç”Ÿæˆ + */ "enableChartsForFederatedInstances": string; + /** + * ノートã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã«ã‚¯ãƒªãƒƒãƒ—ã‚’è¿½åŠ + */ "showClipButtonInNoteFooter": string; + /** + * リアクションã®è¡¨ç¤ºã‚µã‚¤ã‚º + */ "reactionsDisplaySize": string; + /** + * リアクションã®æœ€å¤§æ¨ªå¹…を制é™ã—ã€ç¸®å°ã—ã¦è¡¨ç¤ºã™ã‚‹ + */ "limitWidthOfReaction": string; + /** + * ノートIDã¾ãŸã¯URL + */ "noteIdOrUrl": string; + /** + * å‹•ç”» + */ "video": string; + /** + * å‹•ç”» + */ "videos": string; + /** + * 音声 + */ + "audio": string; + /** + * 音声 + */ + "audioFiles": string; + /** + * データセーãƒãƒ¼ + */ "dataSaver": string; + /** + * アカウントã®ç§»è¡Œ + */ "accountMigration": string; + /** + * ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯æ–°ã—ã„アカウントã«ç§»è¡Œã—ã¾ã—ãŸï¼š + */ "accountMoved": string; + /** + * ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ç§»è¡Œã•ã‚Œã¦ã„ã¾ã™ + */ "accountMovedShort": string; + /** + * ã“ã®æ“作ã¯ã§ãã¾ã›ã‚“ + */ "operationForbidden": string; + /** + * 常ã«åºƒå‘Šã‚’表示ã™ã‚‹ + */ "forceShowAds": string; + /** + * 猫å‹é” :3 + */ + "oneko": string; + /** + * ãƒ¡ãƒ¢ã‚’è¿½åŠ + */ "addMemo": string; + /** + * メモを編集 + */ "editMemo": string; + /** + * リアクション一覧 + */ "reactionsList": string; + /** + * ブースト一覧 + */ "renotesList": string; + /** + * 通知ã®è¡¨ç¤º + */ "notificationDisplay": string; + /** + * 左上 + */ "leftTop": string; + /** + * å³ä¸Š + */ "rightTop": string; + /** + * 左下 + */ "leftBottom": string; + /** + * å³ä¸‹ + */ "rightBottom": string; + /** + * ã‚¹ã‚¿ãƒƒã‚¯æ–¹å‘ + */ "stackAxis": string; + /** + * 縦 + */ "vertical": string; + /** + * 横 + */ "horizontal": string; + /** + * ä½ç½® + */ "position": string; + /** + * サーãƒãƒ¼ãƒ«ãƒ¼ãƒ« + */ "serverRules": string; + /** + * ã“ã®ã‚µãƒ¼ãƒãƒ¼ã«ç™»éŒ²ã™ã‚‹ã«ã¯ã€ä»¥ä¸‹ã®å†…容を確èªã—åŒæ„ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ + */ "pleaseConfirmBelowBeforeSignup": string; + /** + * 続ã‘ã‚‹ã«ã¯ã€å…¨ã¦ã®ã€ŒåŒæ„ã™ã‚‹ã€ã«ãƒã‚§ãƒƒã‚¯ãŒå…¥ã£ã¦ã„ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ + */ "pleaseAgreeAllToContinue": string; + /** + * 続ã‘ã‚‹ + */ "continue": string; + /** + * 予約ユーザーå + */ "preservedUsernames": string; + /** + * 予約ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼åを改行ã§åˆ—挙ã—ã¾ã™ã€‚ã“ã“ã§æŒ‡å®šã•ã‚ŒãŸãƒ¦ãƒ¼ã‚¶ãƒ¼åã¯ã‚¢ã‚«ã‚¦ãƒ³ãƒˆä½œæˆæ™‚ã«ä½¿ãˆãªããªã‚Šã¾ã™ãŒã€ç®¡ç†è€…ã«ã‚ˆã‚‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆä½œæˆæ™‚ã¯ã“ã®åˆ¶é™ã‚’å—ã‘ã¾ã›ã‚“。ã¾ãŸã€æ—¢ã«å˜åœ¨ã™ã‚‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚‚影響をå—ã‘ã¾ã›ã‚“。 + */ "preservedUsernamesDescription": string; + /** + * ã“ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‹ã‚‰ãƒŽãƒ¼ãƒˆã‚’ä½œæˆ + */ "createNoteFromTheFile": string; + /** + * アーカイブ + */ "archive": string; - "channelArchiveConfirmTitle": string; + /** + * {name}をアーカイブã—ã¾ã™ã‹ï¼Ÿ + */ + "channelArchiveConfirmTitle": ParameterizedString<"name">; + /** + * アーカイブã™ã‚‹ã¨ã€ãƒãƒ£ãƒ³ãƒãƒ«ä¸€è¦§ã‚„検索çµæžœã«è¡¨ç¤ºã•ã‚Œãªããªã‚Šã€æ–°ãŸãªæ›¸ãè¾¼ã¿ã‚‚ã§ããªããªã‚Šã¾ã™ã€‚ + */ "channelArchiveConfirmDescription": string; + /** + * ã“ã®ãƒãƒ£ãƒ³ãƒãƒ«ã¯ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–ã•ã‚Œã¦ã„ã¾ã™ã€‚ + */ "thisChannelArchived": string; + /** + * ノートã®è¡¨ç¤º + */ "displayOfNote": string; + /** + * åˆæœŸè¨å®š + */ "initialAccountSetting": string; + /** + * フォãƒãƒ¼ä¸ + */ "youFollowing": string; + /** + * 生æˆAIã«ã‚ˆã‚‹å¦ç¿’ã‚’æ‹’å¦ + */ "preventAiLearning": string; + /** + * 外部ã®æ–‡ç« 生æˆAIã‚„ç”»åƒç”ŸæˆAIã«å¯¾ã—ã¦ã€æŠ•ç¨¿ã—ãŸãƒŽãƒ¼ãƒˆã‚„ç”»åƒãªã©ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã‚’å¦ç¿’ã®å¯¾è±¡ã«ã—ãªã„よã†ã«è¦æ±‚ã—ã¾ã™ã€‚ã“ã‚Œã¯noaiフラグをHTMLレスãƒãƒ³ã‚¹ã«å«ã‚ã‚‹ã“ã¨ã«ã‚ˆã£ã¦å®Ÿç¾ã•ã‚Œã¾ã™ãŒã€ã“ã®è¦æ±‚ã«å¾“ã†ã‹ã¯ãã®AI次第ã§ã‚ã‚‹ãŸã‚ã€å¦ç¿’を完全ã«é˜²æ¢ã™ã‚‹ã‚‚ã®ã§ã¯ã‚ã‚Šã¾ã›ã‚“。 + */ "preventAiLearningDescription": string; + /** + * オプション + */ "options": string; + /** + * ユーザー指定 + */ "specifyUser": string; + /** + * プレビューã§ãã¾ã›ã‚“ + */ "failedToPreviewUrl": string; + /** + * æ›´æ–° + */ "update": string; + /** + * リアクションã¨ã—ã¦ä½¿ãˆã‚‹ãƒãƒ¼ãƒ« + */ "rolesThatCanBeUsedThisEmojiAsReaction": string; + /** + * ãƒãƒ¼ãƒ«ã®æŒ‡å®šãŒä¸€ã¤ã‚‚ãªã„å ´åˆã€èª°ã§ã‚‚リアクションã¨ã—ã¦ä½¿ãˆã¾ã™ã€‚ + */ "rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription": string; + /** + * ãƒãƒ¼ãƒ«ã¯å…¬é–‹ãƒãƒ¼ãƒ«ã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ + */ "rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn": string; + /** + * リアクションをå–り消ã—ã¾ã™ã‹ï¼Ÿ + */ "cancelReactionConfirm": string; + /** + * リアクションを変更ã—ã¾ã™ã‹ï¼Ÿ + */ "changeReactionConfirm": string; + /** + * ã‚ã¨ã§ + */ "later": string; + /** + * Sharkey㸠+ */ "goToMisskey": string; + /** + * 絵文å—ã®è¿½åŠ 辞書 + */ "additionalEmojiDictionary": string; + /** + * インストール済㿠+ */ "installed": string; + /** + * ブランディング + */ "branding": string; + /** + * サーãƒãƒ¼ã®ãƒžã‚·ãƒ³æƒ…å ±ã‚’å…¬é–‹ã™ã‚‹ + */ "enableServerMachineStats": string; + /** + * 実績を有効ã«ã™ã‚‹ + */ "enableAchievements": string; + /** + * オフã«ã™ã‚‹ã¨å®Ÿç¸¾ã‚·ã‚¹ãƒ†ãƒ ã¯ç„¡åŠ¹ã«ãªã‚Šã¾ã™ã€‚ + */ "turnOffAchievements": string; + /** + * botã®ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°è¿½åŠ を許å¯ã™ã‚‹ + */ "enableBotTrending": string; + /** + * オフã«ã™ã‚‹ã¨ãƒœãƒƒãƒˆãŒãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ã‚’入力ã—ãªããªã‚Šã¾ã™ã€‚ + */ "turnOffBotTrending": string; + /** + * ユーザーã”ã¨ã®Identicon生æˆã‚’有効ã«ã™ã‚‹ + */ "enableIdenticonGeneration": string; + /** + * オフã«ã™ã‚‹ã¨ãƒ‘フォーマンスãŒå‘上ã—ã¾ã™ã€‚ + */ "turnOffToImprovePerformance": string; + /** + * æ‹›å¾…ã‚³ãƒ¼ãƒ‰ã‚’ä½œæˆ + */ "createInviteCode": string; + /** + * オプションを指定ã—ã¦ä½œæˆ + */ "createWithOptions": string; + /** + * 作æˆæ•° + */ "createCount": string; + /** + * 招待コードを作æˆã—ã¾ã—㟠+ */ "inviteCodeCreated": string; + /** + * 作æˆã§ãる招待コードã®æ•°ãŒä¸Šé™ã«é”ã—ã¦ã„ã¾ã™ã€‚ + */ "inviteLimitExceeded": string; - "createLimitRemaining": string; - "inviteLimitResetCycle": string; + /** + * 作æˆã§ãる招待コード: 残り {limit} 個 + */ + "createLimitRemaining": ParameterizedString<"limit">; + /** + * {time}ã§æœ€å¤§ {limit} 個ã®æ‹›å¾…コードを作æˆã§ãã¾ã™ã€‚ + */ + "inviteLimitResetCycle": ParameterizedString<"time" | "limit">; + /** + * æœ‰åŠ¹æœŸé™ + */ "expirationDate": string; + /** + * 有効期é™ã‚’è¨ã‘ãªã„ + */ "noExpirationDate": string; + /** + * 招待コードãŒä½¿ç”¨ã•ã‚ŒãŸæ—¥æ™‚ + */ "inviteCodeUsedAt": string; + /** + * 招待コードを使用ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ + */ "registeredUserUsingInviteCode": string; + /** + * メールèªè¨¼å¾…ã¡ + */ "waitingForMailAuth": string; + /** + * 招待コードを作æˆã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ + */ "inviteCodeCreator": string; + /** + * 使用日時 + */ "usedAt": string; + /** + * 未使用 + */ "unused": string; + /** + * 使用済㿠+ */ "used": string; + /** + * 期é™åˆ‡ã‚Œ + */ "expired": string; + /** + * åŒæ„ã—ã¾ã™ã‹ï¼Ÿ + */ "doYouAgree": string; + /** + * é‡è¦ã§ã™ã®ã§å¿…ãšãŠèªã¿ãã ã•ã„。 + */ "beSureToReadThisAsItIsImportant": string; - "iHaveReadXCarefullyAndAgree": string; + /** + * 「{x}ã€ã®å†…容をよãèªã¿ã€åŒæ„ã—ã¾ã™ã€‚ + */ + "iHaveReadXCarefullyAndAgree": ParameterizedString<"x">; + /** + * ダイアãƒã‚° + */ "dialog": string; + /** + * アイコン + */ "icon": string; + /** + * ã‚ãªãŸã¸ + */ "forYou": string; + /** + * ç¾åœ¨ã®ãŠçŸ¥ã‚‰ã› + */ "currentAnnouncements": string; + /** + * éŽåŽ»ã®ãŠçŸ¥ã‚‰ã› + */ "pastAnnouncements": string; + /** + * 未èªã®ãŠçŸ¥ã‚‰ã›ãŒã‚ã‚Šã¾ã™ã€‚ + */ "youHaveUnreadAnnouncements": string; + /** + * ブラウザã¾ãŸã¯ãƒ‡ãƒã‚¤ã‚¹ã®æŒ‡ç¤ºã«å¾“ã£ã¦ã€ã‚»ã‚ュリティã‚ーã¾ãŸã¯ãƒ‘スã‚ーを使用ã—ã¦ãã ã•ã„。 + */ "useSecurityKey": string; + /** + * 返信 + */ "replies": string; + /** + * ブースト + */ "renotes": string; + /** + * 返信を見る + */ "loadReplies": string; + /** + * 会話を見る + */ "loadConversation": string; + /** + * ピン留ã‚ã•ã‚ŒãŸãƒªã‚¹ãƒˆ + */ "pinnedList": string; + /** + * デãƒã‚¤ã‚¹ã®ç”»é¢ã‚’常ã«ã‚ªãƒ³ã«ã™ã‚‹ + */ "keepScreenOn": string; + /** + * クリックã—ã¦ãƒŽãƒ¼ãƒˆã‚’é–‹ã + */ "clickToOpen": string; + /** + * ボットをタイムラインã«è¡¨ç¤º + */ "showBots": string; + /** + * ã“ã®ãƒªãƒ³ã‚¯å…ˆã®æ‰€æœ‰è€…ã§ã‚ã‚‹ã“ã¨ãŒç¢ºèªã•ã‚Œã¾ã—㟠+ */ "verifiedLink": string; + /** + * 投稿を通知 + */ "notifyNotes": string; + /** + * 投稿ã®é€šçŸ¥ã‚’解除 + */ "unnotifyNotes": string; + /** + * èªè¨¼ + */ "authentication": string; + /** + * 続ã‘ã‚‹ã«ã¯èªè¨¼ã‚’è¡Œã£ã¦ãã ã•ã„ + */ "authenticationRequiredToContinue": string; + /** + * 日時 + */ "dateAndTime": string; + /** + * ブーストを表示 + */ "showRenotes": string; + /** + * 編集済㿠+ */ "edited": string; + /** + * 通知ã®å—ä¿¡è¨å®š + */ "notificationRecieveConfig": string; + /** + * 相互フォãƒãƒ¼ + */ "mutualFollow": string; + /** + * フォãƒãƒ¼ä¸ã¾ãŸã¯ãƒ•ã‚©ãƒãƒ¯ãƒ¼ + */ + "followingOrFollower": string; + /** + * ファイル付ãã®ã¿ + */ "fileAttachedOnly": string; + /** + * TLã«ä»–ã®äººã¸ã®è¿”ä¿¡ã‚’å«ã‚ã‚‹ + */ "showRepliesToOthersInTimeline": string; + /** + * TLã«ä»–ã®äººã¸ã®è¿”ä¿¡ã‚’å«ã‚ãªã„ + */ "hideRepliesToOthersInTimeline": string; + /** + * TLã«ç¾åœ¨ãƒ•ã‚©ãƒãƒ¼ä¸ã®äººå…¨å“¡ã®è¿”ä¿¡ã‚’å«ã‚るよã†ã«ã™ã‚‹ + */ "showRepliesToOthersInTimelineAll": string; + /** + * TLã«ç¾åœ¨ãƒ•ã‚©ãƒãƒ¼ä¸ã®äººå…¨å“¡ã®è¿”ä¿¡ã‚’å«ã‚ãªã„よã†ã«ã™ã‚‹ + */ "hideRepliesToOthersInTimelineAll": string; + /** + * ã“ã®æ“作ã¯å…ƒã«æˆ»ã›ã¾ã›ã‚“。本当ã«TLã«ç¾åœ¨ãƒ•ã‚©ãƒãƒ¼ä¸ã®äººå…¨å“¡ã®è¿”ä¿¡ã‚’å«ã‚るよã†ã«ã—ã¾ã™ã‹ï¼Ÿ + */ "confirmShowRepliesAll": string; + /** + * ã“ã®æ“作ã¯å…ƒã«æˆ»ã›ã¾ã›ã‚“。本当ã«TLã«ç¾åœ¨ãƒ•ã‚©ãƒãƒ¼ä¸ã®äººå…¨å“¡ã®è¿”ä¿¡ã‚’å«ã‚ãªã„よã†ã«ã—ã¾ã™ã‹ï¼Ÿ + */ "confirmHideRepliesAll": string; + /** + * 外部サービス + */ "externalServices": string; + /** + * ソースコード + */ + "sourceCode": string; + /** + * ソースコードã¯ã¾ã æä¾›ã•ã‚Œã¦ã„ã¾ã›ã‚“。ã“ã®å•é¡Œã®ä¿®æ£ã«ã¤ã„ã¦ç®¡ç†è€…ã«å•ã„åˆã‚ã›ã¦ãã ã•ã„。 + */ + "sourceCodeIsNotYetProvided": string; + /** + * リãƒã‚¸ãƒˆãƒªURL + */ + "repositoryUrl": string; + /** + * ソースコードãŒå…¬é–‹ã•ã‚Œã¦ã„るリãƒã‚¸ãƒˆãƒªãŒã‚ã‚‹å ´åˆã€ãã®URLを記入ã—ã¾ã™ã€‚Misskeyã‚’ç¾çŠ¶ã®ã¾ã¾ï¼ˆã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã«ã„ã‹ãªã‚‹å¤‰æ›´ã‚‚åŠ ãˆãšã«ï¼‰ä½¿ç”¨ã—ã¦ã„ã‚‹å ´åˆã¯ https://github.com/misskey-dev/misskey ã¨è¨˜å…¥ã—ã¾ã™ã€‚ + */ + "repositoryUrlDescription": string; + /** + * リãƒã‚¸ãƒˆãƒªã‚’公開ã—ã¦ã„ãªã„å ´åˆã€ä»£ã‚ã‚Šã«tarballã‚’æä¾›ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚詳細ã¯.config/example.ymlã‚’å‚ç…§ã—ã¦ãã ã•ã„。 + */ + "repositoryUrlOrTarballRequired": string; + /** + * フィードãƒãƒƒã‚¯ + */ + "feedback": string; + /** + * フィードãƒãƒƒã‚¯URL + */ + "feedbackUrl": string; + /** + * é‹å–¶è€…æƒ…å ± + */ "impressum": string; + /** + * é‹å–¶è€…æƒ…å ±URL + */ "impressumUrl": string; + /** + * ドイツãªã©ã®ä¸€éƒ¨ã®å›½ã¨åœ°åŸŸã§ã¯è¡¨ç¤ºãŒç¾©å‹™ä»˜ã‘られã¦ã„ã¾ã™(Impressum)。 + */ "impressumDescription": string; + /** + * プライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼ + */ "privacyPolicy": string; + /** + * プライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼URL + */ "privacyPolicyUrl": string; + /** + * 利用è¦ç´„・プライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼ + */ "tosAndPrivacyPolicy": string; + /** + * 寄付ã™ã‚‹ + */ + "donation": string; + /** + * 寄付URL + */ + "donationUrl": string; + /** + * アイコンデコレーション + */ "avatarDecorations": string; + /** + * 付ã‘ã‚‹ + */ "attach": string; + /** + * 外㙠+ */ "detach": string; + /** + * å…¨ã¦å¤–ã™ + */ "detachAll": string; + /** + * 角度 + */ "angle": string; + /** + * å転 + */ "flip": string; + /** + * アイコンã®ãƒ‡ã‚³ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã‚’表示 + */ "showAvatarDecorations": string; + /** + * 離ã—ã¦ãƒªãƒãƒ¼ãƒ‰ + */ "releaseToRefresh": string; + /** + * リãƒãƒ¼ãƒ‰ä¸ + */ "refreshing": string; + /** + * 引ã£å¼µã£ã¦ãƒªãƒãƒ¼ãƒ‰ + */ "pullDownToRefresh": string; + /** + * タイムラインã®ãƒªã‚¢ãƒ«ã‚¿ã‚¤ãƒ 更新を無効ã«ã™ã‚‹ + */ "disableStreamingTimeline": string; + /** + * 通知をグルーピングã—ã¦è¡¨ç¤ºã™ã‚‹ + */ "useGroupedNotifications": string; + /** + * メールアドレスã®ç¢ºèªä¸ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚リンクã®æœ‰åŠ¹æœŸé™ãŒåˆ‡ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚ + */ "signupPendingError": string; + /** + * ã€Œå†…å®¹ã‚’éš ã™ã€ãŒã‚ªãƒ³ã®å ´åˆã¯æ³¨é‡ˆã®è¨˜è¿°ãŒå¿…è¦ã§ã™ã€‚ + */ "cwNotationRequired": string; + /** + * リアクションã™ã‚‹ + */ "doReaction": string; + /** + * コード + */ "code": string; + /** + * è¨å®šã®åæ˜ ã«ã¯ãƒªãƒãƒ¼ãƒ‰ãŒå¿…è¦ã§ã™ã€‚ + */ "reloadRequiredToApplySettings": string; - "remainingN": string; + /** + * 残り: {n} + */ + "remainingN": ParameterizedString<"n">; + /** + * ç¾åœ¨ã®å†…容ã«ä¸Šæ›¸ãã•ã‚Œã¾ã™ãŒã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ + */ "overwriteContentConfirm": string; + /** + * å£ç¯€ã«å¿œã˜ãŸç”»é¢ã®æ¼”出 + */ "seasonalScreenEffect": string; + /** + * デコる + */ "decorate": string; + /** + * è£…é£¾ã‚’è¿½åŠ + */ "addMfmFunction": string; + /** + * 高度ãªMFMã®ãƒ”ッカーを表示ã™ã‚‹ + */ "enableQuickAddMfmFunction": string; + /** + * ãƒãƒ–ルゲーム+ */ + "bubbleGame": string; + /** + * 効果音 + */ + "sfx": string; + /** + * サウンドãŒå†ç”Ÿã•ã‚Œã¾ã™ + */ + "soundWillBePlayed": string; + /** + * リプレイを見る + */ + "showReplay": string; + /** + * リプレイ + */ + "replay": string; + /** + * ãƒªãƒ—ãƒ¬ã‚¤ä¸ + */ + "replaying": string; + /** + * リプレイを終了 + */ + "endReplay": string; + /** + * リプレイデータをコピー + */ + "copyReplayData": string; + /** + * ランã‚ング + */ + "ranking": string; + /** + * ç›´è¿‘{n}æ—¥ + */ + "lastNDays": ParameterizedString<"n">; + /** + * タイトル㸠+ */ + "backToTitle": string; + /** + * ãŠä½ã¾ã„ã®åœ°åŸŸ + */ + "hemisphere": string; + /** + * センシティブãªãƒ•ã‚¡ã‚¤ãƒ«ã‚’å«ã‚€ãƒŽãƒ¼ãƒˆã‚’表示 + */ + "withSensitive": string; + /** + * {name}ã®ã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ãªãƒ•ã‚¡ã‚¤ãƒ«ã‚’å«ã‚€æŠ•ç¨¿ + */ + "userSaysSomethingSensitive": ParameterizedString<"name">; + /** + * スワイプã—ã¦ã‚¿ãƒ–を切り替ãˆã‚‹ + */ + "enableHorizontalSwipe": string; + /** + * èªã¿è¾¼ã¿ä¸ + */ + "loading": string; + /** + * ã‚„ã‚ã‚‹ + */ + "surrender": string; + /** + * リトライ + */ + "gameRetry": string; + "_bubbleGame": { + /** + * éŠã³æ–¹ + */ + "howToPlay": string; + /** + * ホールド + */ + "hold": string; + "_score": { + /** + * スコア + */ + "score": string; + /** + * 稼ã„ã é‡‘é¡ + */ + "scoreYen": string; + /** + * ãƒã‚¤ã‚¹ã‚³ã‚¢ + */ + "highScore": string; + /** + * 最大ãƒã‚§ãƒ¼ãƒ³æ•° + */ + "maxChain": string; + /** + * {yen}円 + */ + "yen": ParameterizedString<"yen">; + /** + * {qty}個分 + */ + "estimatedQty": ParameterizedString<"qty">; + /** + * ãŠã«ãŽã‚Š {onigiriQtyWithUnit} + */ + "scoreSweets": ParameterizedString<"onigiriQtyWithUnit">; + }; + "_howToPlay": { + /** + * ä½ç½®ã‚’調整ã—ã¦ãƒã‚³ã«ãƒ¢ãƒŽã‚’è½ã¨ã—ã¾ã™ã€‚ + */ + "section1": string; + /** + * åŒã˜ç¨®é¡žã®ãƒ¢ãƒŽãŒãã£ã¤ãã¨åˆ¥ã®ãƒ¢ãƒŽã«å¤‰åŒ–ã—ã¦ã€ã‚¹ã‚³ã‚¢ãŒå¾—られã¾ã™ã€‚ + */ + "section2": string; + /** + * モノãŒãƒã‚³ã‹ã‚‰ã‚ãµã‚Œã‚‹ã¨ã‚²ãƒ¼ãƒ オーãƒãƒ¼ã§ã™ã€‚ãƒã‚³ã‹ã‚‰ã‚ãµã‚Œãªã„よã†ã«ã—ã¤ã¤ãƒ¢ãƒŽã‚’èžåˆã•ã›ã¦ãƒã‚¤ã‚¹ã‚³ã‚¢ã‚’目指ãã†ï¼ + */ + "section3": string; + }; + }; "_announcement": { + /** + * æ—¢å˜ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã¿ + */ "forExistingUsers": string; + /** + * 有効ã«ã™ã‚‹ã¨ã€ã“ã®ãŠçŸ¥ã‚‰ã›ä½œæˆæ™‚点ã§å˜åœ¨ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã®ã¿ãŠçŸ¥ã‚‰ã›ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚無効ã«ã™ã‚‹ã¨ã€ã“ã®ãŠçŸ¥ã‚‰ã›ä½œæˆå¾Œã«ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’作æˆã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã‚‚ãŠçŸ¥ã‚‰ã›ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ + */ "forExistingUsersDescription": string; + /** + * æ—¢èªã«ã™ã‚‹ã®ã«ç¢ºèªãŒå¿…è¦ + */ "needConfirmationToRead": string; + /** + * 有効ã«ã™ã‚‹ã¨ã€ã“ã®ãŠçŸ¥ã‚‰ã›ã‚’æ—¢èªã«ã™ã‚‹éš›ã«ç¢ºèªãƒ€ã‚¤ã‚¢ãƒã‚°ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ã¾ãŸã€ä¸€æ‹¬æ—¢èªæ“作ã®å¯¾è±¡ã«ãªã‚Šã¾ã›ã‚“。 + */ "needConfirmationToReadDescription": string; + /** + * ãŠçŸ¥ã‚‰ã›ã‚’終了 + */ "end": string; + /** + * アクティブãªãŠçŸ¥ã‚‰ã›ãŒå¤šã„ãŸã‚ã€UXãŒä½Žä¸‹ã™ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚終了ã—ãŸãŠçŸ¥ã‚‰ã›ã¯ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–ã™ã‚‹ã“ã¨ã‚’検討ã—ã¦ãã ã•ã„。 + */ "tooManyActiveAnnouncementDescription": string; + /** + * æ—¢èªã«ã—ã¾ã™ã‹ï¼Ÿ + */ "readConfirmTitle": string; - "readConfirmText": string; + /** + * 「{title}ã€ã®å†…容をèªã¿ã€æ—¢èªã«ã—ã¾ã™ã€‚ + */ + "readConfirmText": ParameterizedString<"title">; + /** + * 特ã«æ–°è¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®UXã‚’æãã‚‹å¯èƒ½æ€§ãŒé«˜ã„ãŸã‚ã€å¸¸æ™‚掲示ã™ã‚‹ãŸã‚ã®æƒ…å ±ã§ã¯ãªãã€å³æ™‚性ãŒæ±‚ã‚ã‚‰ã‚Œã‚‹æƒ…å ±ã®æŽ²ç¤ºã®ãŸã‚ã«ãŠçŸ¥ã‚‰ã›ã‚’使用ã™ã‚‹ã“ã¨ã‚’推奨ã—ã¾ã™ã€‚ + */ "shouldNotBeUsedToPresentPermanentInfo": string; + /** + * ダイアãƒã‚°å½¢å¼ã®ãŠçŸ¥ã‚‰ã›ãŒåŒæ™‚ã«2ã¤ä»¥ä¸Šã‚ã‚‹å ´åˆã€UXã«æ‚ªå½±éŸ¿ã‚’åŠã¼ã™å¯èƒ½æ€§ãŒéžå¸¸ã«é«˜ã„ãŸã‚ã€ä½¿ç”¨ã¯æ…Žé‡ã«è¡Œã†ã“ã¨ã‚’推奨ã—ã¾ã™ã€‚ + */ "dialogAnnouncementUxWarn": string; + /** + * éžé€šçŸ¥ + */ "silence": string; + /** + * オンã«ã™ã‚‹ã¨ã€ã“ã®ãŠçŸ¥ã‚‰ã›ã¯é€šçŸ¥ã•ã‚Œãšã€æ—¢èªã«ã™ã‚‹å¿…è¦ã‚‚ãªããªã‚Šã¾ã™ã€‚ + */ "silenceDescription": string; }; "_initialAccountSetting": { + /** + * アカウントã®ä½œæˆãŒå®Œäº†ã—ã¾ã—ãŸï¼ + */ "accountCreated": string; + /** + * ã•ã£ããアカウントã®åˆæœŸè¨å®šã‚’è¡Œã„ã¾ã—ょã†ã€‚ + */ "letsStartAccountSetup": string; + /** + * ã¾ãšã¯ã‚ãªãŸã®ãƒ—ãƒãƒ•ã‚£ãƒ¼ãƒ«ã‚’è¨å®šã—ã¾ã—ょã†ã€‚ + */ "letsFillYourProfile": string; + /** + * プãƒãƒ•ã‚£ãƒ¼ãƒ«è¨å®š + */ "profileSetting": string; + /** + * プライãƒã‚·ãƒ¼è¨å®š + */ "privacySetting": string; + /** + * ã“れらã®è¨å®šã¯å¾Œã‹ã‚‰å¤‰æ›´ã§ãã¾ã™ã€‚ + */ "theseSettingsCanEditLater": string; + /** + * ã“ã®ä»–ã«ã‚‚様々ãªè¨å®šã‚’「è¨å®šã€ãƒšãƒ¼ã‚¸ã‹ã‚‰è¡Œãˆã¾ã™ã€‚ãœã²å¾Œã§ç¢ºèªã—ã¦ã¿ã¦ãã ã•ã„。 + */ "youCanEditMoreSettingsInSettingsPageLater": string; + /** + * タイムラインを構築ã™ã‚‹ãŸã‚ã€æ°—ã«ãªã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’フォãƒãƒ¼ã—ã¦ã¿ã¾ã—ょã†ã€‚ + */ "followUsers": string; - "pushNotificationDescription": string; + /** + * プッシュ通知を有効ã«ã™ã‚‹ã¨{name}ã®é€šçŸ¥ã‚’ãŠä½¿ã„ã®ãƒ‡ãƒã‚¤ã‚¹ã§å—ã‘å–ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + */ + "pushNotificationDescription": ParameterizedString<"name">; + /** + * åˆæœŸè¨å®šãŒå®Œäº†ã—ã¾ã—ãŸï¼ + */ "initialAccountSettingCompleted": string; - "haveFun": string; - "youCanContinueTutorial": string; + /** + * {name}ã‚’ãŠæ¥½ã—ã¿ãã ã•ã„ï¼ + */ + "haveFun": ParameterizedString<"name">; + /** + * ã“ã®ã¾ã¾{name}(Sharkey)ã®ä½¿ã„æ–¹ã«ã¤ã„ã¦ã®ãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ«ã«é€²ã‚€ã“ã¨ã‚‚ã§ãã¾ã™ãŒã€ã“ã“ã§ä¸æ–ã—ã¦ã™ãã«ä½¿ã„始ã‚ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚ + */ + "youCanContinueTutorial": ParameterizedString<"name">; + /** + * ãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ«ã‚’開始 + */ "startTutorial": string; + /** + * åˆæœŸè¨å®šã‚’スã‚ップã—ã¾ã™ã‹ï¼Ÿ + */ "skipAreYouSure": string; + /** + * åˆæœŸè¨å®šã‚’ã‚ã¨ã§ã‚„ã‚Šç›´ã—ã¾ã™ã‹ï¼Ÿ + */ "laterAreYouSure": string; }; "_initialTutorial": { + /** + * ãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ«ã‚’見る + */ "launchTutorial": string; + /** + * ãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ« + */ "title": string; + /** + * よãã§ãã¾ã—㟠+ */ "wellDone": string; + /** + * ãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ«ã‚’終了ã—ã¾ã™ã‹ï¼Ÿ + */ "skipAreYouSure": string; "_landing": { + /** + * ãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ«ã¸ã‚ˆã†ã“ã + */ "title": string; + /** + * ã“ã“ã§ã¯ã€Sharkeyã®åŸºæœ¬çš„ãªä½¿ã„方や機能を確èªã§ãã¾ã™ã€‚ + */ "description": string; }; "_note": { + /** + * ノートã£ã¦ä½•ï¼Ÿ + */ "title": string; + /** + * Sharkeyã§ã®æŠ•ç¨¿ã¯ã€ŒãƒŽãƒ¼ãƒˆã€ã¨å‘¼ã³ã¾ã™ã€‚ノートã¯ã‚¿ã‚¤ãƒ ラインã«æ™‚系列ã§ä¸¦ã‚“ã§ã„ã¦ã€ãƒªã‚¢ãƒ«ã‚¿ã‚¤ãƒ ã§æ›´æ–°ã•ã‚Œã¦ã„ãã¾ã™ã€‚ + */ "description": string; + /** + * 返信ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚返信ã«å¯¾ã—ã¦ã®è¿”ä¿¡ã‚‚å¯èƒ½ã§ã€ã‚¹ãƒ¬ãƒƒãƒ‰ã®ã‚ˆã†ã«ä¼šè©±ã‚’続ã‘ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚ + */ "reply": string; + /** + * ãã®ãƒŽãƒ¼ãƒˆã‚’自分ã®ã‚¿ã‚¤ãƒ ラインã«æµã—ã¦å…±æœ‰ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚テã‚ã‚¹ãƒˆã‚’è¿½åŠ ã—ã¦å¼•ç”¨ã™ã‚‹ã“ã¨ã‚‚å¯èƒ½ã§ã™ã€‚ + */ "renote": string; + /** + * リアクションをã¤ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚詳ã—ãã¯æ¬¡ã®ãƒšãƒ¼ã‚¸ã§è§£èª¬ã—ã¾ã™ã€‚ + */ "reaction": string; + /** + * ノートã®è©³ç´°ã‚’表示ã—ãŸã‚Šã€ãƒªãƒ³ã‚¯ã‚’コピーã—ãŸã‚Šãªã©ã®æ§˜ã€…ãªæ“作ãŒè¡Œãˆã¾ã™ã€‚ + */ "menu": string; }; "_reaction": { + /** + * リアクションã£ã¦ä½•ï¼Ÿ + */ "title": string; + /** + * ノートã«ã¯ã€Œãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã€ã‚’ã¤ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚「ã„ã„ãã€ã§ã¯ä¼ã‚らãªã„ニュアンスもã€ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã§ç°¡å˜ãƒ»æ°—軽ã«è¡¨ç¾ã§ãã¾ã™ã€‚ + */ "description": string; + /** + * リアクションã¯ã€ãƒŽãƒ¼ãƒˆã®ã€Œï¼‹ã€ãƒœã‚¿ãƒ³ã‚’クリックã™ã‚‹ã¨ã¤ã‘られã¾ã™ã€‚試ã—ã«ã“ã®ã‚µãƒ³ãƒ—ルã®ãƒŽãƒ¼ãƒˆã«ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’ã¤ã‘ã¦ã¿ã¦ãã ã•ã„ï¼ + */ "letsTryReacting": string; + /** + * リアクションをã¤ã‘ã‚‹ã¨å…ˆã«é€²ã‚るよã†ã«ãªã‚Šã¾ã™ã€‚ + */ "reactToContinue": string; + /** + * ã‚ãªãŸã®ãƒŽãƒ¼ãƒˆãŒèª°ã‹ã«ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã•ã‚Œã‚‹ã¨ã€ãƒªã‚¢ãƒ«ã‚¿ã‚¤ãƒ ã§é€šçŸ¥ã‚’å—ã‘å–ã‚Šã¾ã™ã€‚ + */ "reactNotification": string; + /** + * 「ーã€ãƒœã‚¿ãƒ³ã‚’押ã™ã¨ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’å–り消ã™ã“ã¨ãŒã§ãã¾ã™ã€‚ + */ "reactDone": string; }; "_timeline": { + /** + * タイムラインã®ã—ãã¿ + */ "title": string; + /** + * Sharkeyã«ã¯ã€ä½¿ã„æ–¹ã«å¿œã˜ã¦è¤‡æ•°ã®ã‚¿ã‚¤ãƒ ラインãŒç”¨æ„ã•ã‚Œã¦ã„ã¾ã™ï¼ˆã‚µãƒ¼ãƒãƒ¼ã«ã‚ˆã£ã¦ã¯ã„ãšã‚Œã‹ãŒç„¡åŠ¹ã«ãªã£ã¦ã„ã‚‹ã“ã¨ãŒã‚ã‚Šã¾ã™ï¼‰ã€‚ + */ "description1": string; + /** + * ã‚ãªãŸãŒãƒ•ã‚©ãƒãƒ¼ã—ã¦ã„るアカウントã®æŠ•ç¨¿ã‚’見られã¾ã™ã€‚ + */ "home": string; + /** + * ã“ã®ã‚µãƒ¼ãƒãƒ¼ã«ã„るユーザー全員ã®æŠ•ç¨¿ã‚’見られã¾ã™ã€‚ + */ "local": string; + /** + * ホームタイムラインã¨ãƒãƒ¼ã‚«ãƒ«ã‚¿ã‚¤ãƒ ラインã®æŠ•ç¨¿ãŒä¸¡æ–¹è¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ + */ "social": string; + /** + * 接続ã—ã¦ã„ã‚‹ä»–ã®ã™ã¹ã¦ã®ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰ã®æŠ•ç¨¿ã‚’見られã¾ã™ã€‚ + */ "global": string; + /** + * ãã‚Œãžã‚Œã®ã‚¿ã‚¤ãƒ ラインã¯ã€ç”»é¢ä¸Šéƒ¨ã§ã„ã¤ã§ã‚‚切り替ãˆã‚‰ã‚Œã¾ã™ã€‚ + */ "description2": string; - "description3": string; + /** + * ãã®ä»–ã«ã‚‚ã€ãƒªã‚¹ãƒˆã‚¿ã‚¤ãƒ ラインやãƒãƒ£ãƒ³ãƒãƒ«ã‚¿ã‚¤ãƒ ラインãªã©ãŒã‚ã‚Šã¾ã™ã€‚詳ã—ãã¯{link}ã‚’ã”覧ãã ã•ã„。 + */ + "description3": ParameterizedString<"link">; }; "_postNote": { + /** + * ノートã®æŠ•ç¨¿è¨å®š + */ "title": string; + /** + * Sharkeyã«ãƒŽãƒ¼ãƒˆã‚’投稿ã™ã‚‹éš›ã«ã¯ã€æ§˜ã€…ãªã‚ªãƒ—ションã®è¨å®šãŒå¯èƒ½ã§ã™ã€‚投稿フォームã¯ã“ã®ã‚ˆã†ã«ãªã£ã¦ã„ã¾ã™ã€‚ + */ "description1": string; "_visibility": { + /** + * ノートを表示ã§ãる相手を制é™ã§ãã¾ã™ã€‚ + */ "description": string; + /** + * ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«å…¬é–‹ã€‚ + */ "public": string; + /** + * ホームタイムラインã®ã¿ã«å…¬é–‹ã€‚フォãƒãƒ¯ãƒ¼ãƒ»ãƒ—ãƒãƒ•ã‚£ãƒ¼ãƒ«ã‚’見ã«æ¥ãŸäººãƒ»ãƒ–ーストã‹ã‚‰ã€ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚‚見るã“ã¨ãŒã§ãã¾ã™ã€‚ + */ "home": string; + /** + * フォãƒãƒ¯ãƒ¼ã«ã®ã¿å…¬é–‹ã€‚本人以外ãŒãƒ–ーストã™ã‚‹ã“ã¨ã¯ã§ããšã€ã¾ãŸãƒ•ã‚©ãƒãƒ¯ãƒ¼ä»¥å¤–ã¯é–²è¦§ã§ãã¾ã›ã‚“。 + */ "followers": string; + /** + * 指定ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã®ã¿å…¬é–‹ã•ã‚Œã€ã¾ãŸç›¸æ‰‹ã«é€šçŸ¥ãŒå…¥ã‚Šã¾ã™ã€‚ダイレクトメッセージã®ã‹ã‚ã‚Šã«ãŠä½¿ã„ã„ãŸã ã‘ã¾ã™ã€‚ + */ "direct": string; + /** + * æ©Ÿå¯†æƒ…å ±ã¯é€ä¿¡ã™ã‚‹éš›ã¯æ³¨æ„ã—ã¦ãã ã•ã„。 + */ "doNotSendConfidencialOnDirect1": string; + /** + * é€ä¿¡å…ˆã®ã‚µãƒ¼ãƒãƒ¼ã®ç®¡ç†è€…ã¯æŠ•ç¨¿å†…容を見るã“ã¨ãŒå¯èƒ½ãªã®ã§ã€ä¿¡é ¼ã§ããªã„サーãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆæŠ•ç¨¿ã‚’é€ä¿¡ã™ã‚‹å ´åˆã¯ã€æ©Ÿå¯†æƒ…å ±ã®æ‰±ã„ã«æ³¨æ„ãŒå¿…è¦ã§ã™ã€‚ + */ "doNotSendConfidencialOnDirect2": string; + /** + * ä»–ã®ã‚µãƒ¼ãƒãƒ¼ã«æŠ•ç¨¿ã‚’連åˆã—ã¾ã›ã‚“。上記ã®å…¬é–‹ç¯„囲ã«é–¢ã‚らãšã€ä»–ã®ã‚µãƒ¼ãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã€ã“ã®è¨å®šãŒã¤ã„ãŸãƒŽãƒ¼ãƒˆã‚’直接閲覧ã™ã‚‹ã“ã¨ãŒã§ããªããªã‚Šã¾ã™ã€‚ + */ "localOnly": string; }; "_cw": { + /** + * å†…å®¹ã‚’éš ã™ï¼ˆCW) + */ "title": string; + /** + * 本文ã®ã‹ã‚ã‚Šã«ã€Œæ³¨é‡ˆã€ã«æ›¸ã„ãŸå†…容ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚「もã£ã¨è¦‹ã‚‹ã€ã‚’押ã™ã¨æœ¬æ–‡ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ + */ "description": string; "_exampleNote": { + /** + * 飯テãƒæ³¨æ„ + */ "cw": string; + /** + * ãƒãƒ§ã‚³ã®ã‹ã‹ã£ãŸãƒ‰ãƒ¼ãƒŠãƒ„を食ã¹ã¾ã—ãŸðŸ©ðŸ˜‹ + */ "note": string; }; + /** + * サーãƒãƒ¼ã®ã‚¬ã‚¤ãƒ‰ãƒ©ã‚¤ãƒ³ã«ã‚ˆã‚Šå¿…è¦ã¨ã•ã‚Œã‚‹ãƒŽãƒ¼ãƒˆã«æŒ‡å®šã—ãŸã‚Šã€ãƒã‚¿ãƒãƒ¬æŠ•ç¨¿ã‚„センシティブãªæ–‡ç« を自主è¦åˆ¶ã—ãŸã‚Šã™ã‚‹ã¨ãã«ä½¿ã„ã¾ã™ã€‚ + */ "useCases": string; }; }; "_howToMakeAttachmentsSensitive": { + /** + * 添付ファイルをセンシティブã«ã™ã‚‹ã«ã¯ï¼Ÿ + */ "title": string; + /** + * サーãƒãƒ¼ã®ã‚¬ã‚¤ãƒ‰ãƒ©ã‚¤ãƒ³ã«ã‚ˆã‚Šå¿…è¦ã¨ã•ã‚Œã‚‹éš›ã‚„ã€ãã®ã¾ã¾è¦‹ã‚Œã‚‹çŠ¶æ…‹ã«ã—ã¦ãŠãã¹ãã§ã¯ãªã„添付ファイルã«ã¯ã€ã€Œã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ã€è¨å®šã‚’付ã‘ã¾ã™ã€‚ + */ "description": string; + /** + * 試ã—ã«ã€ã“ã®ãƒ•ã‚©ãƒ¼ãƒ ã«æ·»ä»˜ã•ã‚ŒãŸç”»åƒã‚’センシティブã«ã—ã¦ã¿ã¦ãã ã•ã„ï¼ + */ "tryThisFile": string; "_exampleNote": { + /** + * ç´è±†ã®ãƒ•ã‚¿é–‹ã‘ã‚‹ã®ãƒŸã‚¹ã£ãŸã‚ã… + */ "note": string; }; + /** + * 添付ファイルをセンシティブã«ã™ã‚‹éš›ã¯ã€ãã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’クリックã—ã¦ãƒ¡ãƒ‹ãƒ¥ãƒ¼ã‚’é–‹ãã€ã€Œã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ã¨ã—ã¦è¨å®šã€ã‚’クリックã—ã¾ã™ã€‚ + */ "method": string; + /** + * ファイルを添付ã™ã‚‹éš›ã¯ã€ã‚µãƒ¼ãƒãƒ¼ã®ã‚¬ã‚¤ãƒ‰ãƒ©ã‚¤ãƒ³ã«å¾“ã£ã¦ã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ã‚’é©åˆ‡ã«è¨å®šã—ã¦ãã ã•ã„。 + */ "sensitiveSucceeded": string; + /** + * ç”»åƒã‚’センシティブã«è¨å®šã™ã‚‹ã¨å…ˆã«é€²ã‚るよã†ã«ãªã‚Šã¾ã™ã€‚ + */ "doItToContinue": string; }; "_done": { + /** + * ãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ«ã¯çµ‚了ã§ã™ðŸŽ‰ + */ "title": string; - "description": string; + /** + * ã“ã“ã§ç´¹ä»‹ã—ãŸæ©Ÿèƒ½ã¯ã»ã‚“ã®ä¸€éƒ¨ã«ã™ãŽã¾ã›ã‚“。Sharkeyã®ä½¿ã„方をより詳ã—ã知るã«ã¯ã€{link}ã‚’ã”覧ãã ã•ã„。 + */ + "description": ParameterizedString<"link">; }; }; "_timelineDescription": { + /** + * ホームタイムラインã§ã¯ã€ã‚ãªãŸãŒãƒ•ã‚©ãƒãƒ¼ã—ã¦ã„るアカウントã®æŠ•ç¨¿ã‚’見られã¾ã™ã€‚ + */ "home": string; + /** + * ãƒãƒ¼ã‚«ãƒ«ã‚¿ã‚¤ãƒ ラインã§ã¯ã€ã“ã®ã‚µãƒ¼ãƒãƒ¼ã«ã„るユーザー全員ã®æŠ•ç¨¿ã‚’見られã¾ã™ã€‚ + */ "local": string; + /** + * ソーシャルタイムラインã«ã¯ã€ãƒ›ãƒ¼ãƒ タイムラインã¨ãƒãƒ¼ã‚«ãƒ«ã‚¿ã‚¤ãƒ ラインã®æŠ•ç¨¿ãŒä¸¡æ–¹è¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ + */ "social": string; + /** + * ã‚°ãƒãƒ¼ãƒãƒ«ã‚¿ã‚¤ãƒ ラインã§ã¯ã€æŽ¥ç¶šã—ã¦ã„ã‚‹ä»–ã®ã™ã¹ã¦ã®ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰ã®æŠ•ç¨¿ã‚’見られã¾ã™ã€‚ + */ "global": string; }; "_serverRules": { + /** + * æ–°è¦ç™»éŒ²å‰ã«è¡¨ç¤ºã™ã‚‹ã€ã‚µãƒ¼ãƒãƒ¼ã®ç°¡æ½”ãªãƒ«ãƒ¼ãƒ«ã‚’è¨å®šã—ã¾ã™ã€‚内容ã¯åˆ©ç”¨è¦ç´„ã®è¦ç´„ã¨ã™ã‚‹ã“ã¨ã‚’推奨ã—ã¾ã™ã€‚ + */ "description": string; }; "_serverSettings": { + /** + * アイコン画åƒã®URL + */ "iconUrl": string; - "appIconDescription": string; + /** + * {host}ãŒã‚¢ãƒ—リã¨ã—ã¦è¡¨ç¤ºã•ã‚Œã‚‹éš›ã®ã‚¢ã‚¤ã‚³ãƒ³ã‚’指定ã—ã¾ã™ã€‚ + */ + "appIconDescription": ParameterizedString<"host">; + /** + * 例: PWAã‚„ã€ã‚¹ãƒžãƒ¼ãƒˆãƒ•ã‚©ãƒ³ã®ãƒ›ãƒ¼ãƒ ç”»é¢ã«ãƒ–ックマークã¨ã—ã¦è¿½åŠ ã•ã‚ŒãŸæ™‚ãªã© + */ "appIconUsageExample": string; + /** + * 円形もã—ãã¯è§’丸ã«ã‚¯ãƒãƒƒãƒ—ã•ã‚Œã‚‹å ´åˆãŒã‚ã‚‹ãŸã‚ã€å¡—ã‚Šæ½°ã•ã‚ŒãŸä½™ç™½ã®ã‚る背景をæŒã¤ã“ã¨ãŒæŽ¨å¥¨ã•ã‚Œã¾ã™ã€‚ + */ "appIconStyleRecommendation": string; - "appIconResolutionMustBe": string; + /** + * 解åƒåº¦ã¯å¿…ãš{resolution}ã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ + */ + "appIconResolutionMustBe": ParameterizedString<"resolution">; + /** + * manifest.jsonã®ã‚ªãƒ¼ãƒãƒ¼ãƒ©ã‚¤ãƒ‰ + */ "manifestJsonOverride": string; + /** + * 略称 + */ "shortName": string; + /** + * サーãƒãƒ¼ã®æ£å¼å称ãŒé•·ã„å ´åˆã«ã€ä»£ã‚ã‚Šã«è¡¨ç¤ºã™ã‚‹ã“ã¨ã®ã§ãる略称や通称。 + */ "shortNameDescription": string; + /** + * 有効ã«ã™ã‚‹ã¨ã€å„種タイムラインをå–å¾—ã™ã‚‹éš›ã®ãƒ‘フォーマンスãŒå¤§å¹…ã«å‘上ã—ã€ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã¸ã®è² è·ã‚’軽減ã™ã‚‹ã“ã¨ãŒå¯èƒ½ã§ã™ã€‚ãŸã ã—ã€Redisã®ãƒ¡ãƒ¢ãƒªä½¿ç”¨é‡ã¯å¢—åŠ ã—ã¾ã™ã€‚サーãƒãƒ¼ã®ãƒ¡ãƒ¢ãƒªå®¹é‡ãŒå°‘ãªã„å ´åˆã€ã¾ãŸã¯å‹•ä½œãŒä¸å®‰å®šãªå ´åˆã¯ç„¡åŠ¹ã«ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + */ "fanoutTimelineDescription": string; + /** + * データベースã¸ã®ãƒ•ã‚©ãƒ¼ãƒ«ãƒãƒƒã‚¯ + */ "fanoutTimelineDbFallback": string; + /** + * 有効ã«ã™ã‚‹ã¨ã€ã‚¿ã‚¤ãƒ ラインãŒã‚ャッシュã•ã‚Œã¦ã„ãªã„å ´åˆã«DBã¸è¿½åŠ ã§å•ã„åˆã‚ã›ã‚’è¡Œã†ãƒ•ã‚©ãƒ¼ãƒ«ãƒãƒƒã‚¯å‡¦ç†ã‚’è¡Œã„ã¾ã™ã€‚無効ã«ã™ã‚‹ã¨ã€ãƒ•ã‚©ãƒ¼ãƒ«ãƒãƒƒã‚¯å‡¦ç†ã‚’è¡Œã‚ãªã„ã“ã¨ã§ã•ã‚‰ã«ã‚µãƒ¼ãƒãƒ¼ã®è² è·ã‚’軽減ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ãŒã€ã‚¿ã‚¤ãƒ ラインãŒå–å¾—ã§ãる範囲ã«åˆ¶é™ãŒç”Ÿã˜ã¾ã™ã€‚ + */ "fanoutTimelineDbFallbackDescription": string; }; "_accountMigration": { + /** + * 別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ç§»è¡Œ + */ "moveFrom": string; + /** + * 別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¸ã‚¨ã‚¤ãƒªã‚¢ã‚¹ã‚’ä½œæˆ + */ "moveFromSub": string; - "moveFromLabel": string; + /** + * 移行元ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ #{n} + */ + "moveFromLabel": ParameterizedString<"n">; + /** + * 別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ç§»è¡Œã—ãŸã„å ´åˆã€ã“ã“ã§ã‚¨ã‚¤ãƒªã‚¢ã‚¹ã‚’作æˆã—ã¦ãŠãå¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ + * 移行元ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ã“ã®ã‚ˆã†ã«å…¥åŠ›ã—ã¦ãã ã•ã„: @username@server.example.com + * 削除ã™ã‚‹ã«ã¯ã€å…¥åŠ›æ¬„を空ã«ã—ã¦ä¿å˜ã—ã¾ã™ï¼ˆéžæŽ¨å¥¨ï¼‰ã€‚ + */ "moveFromDescription": string; + /** + * ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’æ–°ã—ã„アカウントã¸ç§»è¡Œ + */ "moveTo": string; + /** + * 移行先ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ: + */ "moveToLabel": string; + /** + * アカウントを移行ã™ã‚‹ã¨ã€å–り消ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“。 + */ "moveCannotBeUndone": string; + /** + * æ–°ã—ã„アカウントã¸ç§»è¡Œã—ã¾ã™ã€‚ + *  ・フォãƒãƒ¯ãƒ¼ãŒæ–°ã—ã„アカウントを自動ã§ãƒ•ã‚©ãƒãƒ¼ã—ã¾ã™ + *  ・ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒ¼ã¯å…¨ã¦è§£é™¤ã•ã‚Œã¾ã™ + *  ・ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã¯ãƒŽãƒ¼ãƒˆã®ä½œæˆãªã©ãŒã§ããªããªã‚Šã¾ã™ + * + * フォãƒãƒ¯ãƒ¼ã®ç§»è¡Œã¯è‡ªå‹•ã§ã™ãŒã€ãƒ•ã‚©ãƒãƒ¼ã®ç§»è¡Œã¯æ‰‹å‹•ã§è¡Œã†å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚移行å‰ã«ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ãƒ•ã‚©ãƒãƒ¼ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã—ã€ç§»è¡Œå¾Œã™ãã«ç§»è¡Œå…ˆã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã‚¤ãƒ³ãƒãƒ¼ãƒˆã‚’è¡Œãªã£ã¦ãã ã•ã„。 + * リスト・ミュート・ブãƒãƒƒã‚¯ã«ã¤ã„ã¦ã‚‚åŒæ§˜ã§ã™ã®ã§ã€æ‰‹å‹•ã§ç§»è¡Œã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ + * + * (ã“ã®èª¬æ˜Žã¯ã“ã®ã‚µãƒ¼ãƒãƒ¼ï¼ˆSharkey v13.12.0以é™ï¼‰ã®ä»•æ§˜ã§ã™ã€‚Mastodonãªã©ã®ä»–ã®ActivityPubソフトウェアã§ã¯æŒ™å‹•ãŒç•°ãªã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ã€‚) + */ "moveAccountDescription": string; + /** + * アカウントã®ç§»è¡Œã«ã¯ã€ã¾ãšã¯ç§»è¡Œå…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«å¯¾ã—エイリアスを作æˆã—ã¾ã™ã€‚ + * エイリアス作æˆå¾Œã€ç§»è¡Œå…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’次ã®ã‚ˆã†ã«å…¥åŠ›ã—ã¦ãã ã•ã„: @username@server.example.com + */ "moveAccountHowTo": string; + /** + * 移行ã™ã‚‹ + */ "startMigration": string; - "migrationConfirm": string; + /** + * 本当ã«ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ {account} ã«ç§»è¡Œã—ã¾ã™ã‹ï¼Ÿä¸€åº¦ç§»è¡Œã™ã‚‹ã¨å–り消ã›ãšã€äºŒåº¦ã¨ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’å…ƒã®çŠ¶æ…‹ã§ä½¿ç”¨ã§ããªããªã‚Šã¾ã™ã€‚ + */ + "migrationConfirm": ParameterizedString<"account">; + /** + * + * アカウントã¯ç§»è¡Œã•ã‚Œã¦ã„ã¾ã™ã€‚ + * 移行をå–り消ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“。 + */ "movedAndCannotBeUndone": string; + /** + * ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒ¼è§£é™¤ã¯ç§»è¡Œæ“作ã‹ã‚‰24時間後ã«å®Ÿè¡Œã•ã‚Œã¾ã™ã€‚ + * ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ãƒ•ã‚©ãƒãƒ¼ãƒ»ãƒ•ã‚©ãƒãƒ¯ãƒ¼æ•°ã¯0ã«ãªã£ã¦ã„ã¾ã™ã€‚フォãƒãƒ¯ãƒ¼ã®è§£é™¤ã¯ã•ã‚Œãªã„ãŸã‚ã€ã‚ãªãŸã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã¯ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼å‘ã‘投稿を引ã続ã閲覧ã§ãã¾ã™ã€‚ + */ "postMigrationNote": string; + /** + * 移行先ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ: + */ "movedTo": string; }; "_achievements": { + /** + * ç²å¾—日時 + */ "earnedAt": string; "_types": { "_notes1": { + /** + * just setting up my shonk + */ "title": string; + /** + * åˆã‚ã¦ãƒŽãƒ¼ãƒˆã‚’投稿ã—㟠+ */ "description": string; + /** + * 良ã„Sharkeyãƒ©ã‚¤ãƒ•ã‚’ï¼ + */ "flavor": string; }; "_notes10": { + /** + * ã„ãã¤ã‹ã®ãƒŽãƒ¼ãƒˆ + */ "title": string; + /** + * ノートを10回投稿ã—㟠+ */ "description": string; }; "_notes100": { + /** + * ãŸãã•ã‚“ã®ãƒŽãƒ¼ãƒˆ + */ "title": string; + /** + * ノートを100回投稿ã—㟠+ */ "description": string; }; "_notes500": { + /** + * ノートã¾ã¿ã‚Œ + */ "title": string; + /** + * ノートを500回投稿ã—㟠+ */ "description": string; }; "_notes1000": { + /** + * ノートã®å±± + */ "title": string; + /** + * ノートを1,000回投稿ã—㟠+ */ "description": string; }; "_notes5000": { + /** + * 湧ã出るノート + */ "title": string; + /** + * ノートを5,000回投稿ã—㟠+ */ "description": string; }; "_notes10000": { + /** + * スーパーノート + */ "title": string; + /** + * ノートを10,000回投稿ã—㟠+ */ "description": string; }; "_notes20000": { + /** + * ニードモアノート + */ "title": string; + /** + * ノートを20,000回投稿ã—㟠+ */ "description": string; }; "_notes30000": { + /** + * ノートノートノート + */ "title": string; + /** + * ノートを30,000回投稿ã—㟠+ */ "description": string; }; "_notes40000": { + /** + * ãƒŽãƒ¼ãƒˆå·¥å ´ + */ "title": string; + /** + * ノートを40,000回投稿ã—㟠+ */ "description": string; }; "_notes50000": { + /** + * ノートã®æƒ‘星 + */ "title": string; + /** + * ノートを50,000回投稿ã—㟠+ */ "description": string; }; "_notes60000": { + /** + * ノートクエーサー + */ "title": string; + /** + * ノートを60,000回投稿ã—㟠+ */ "description": string; }; "_notes70000": { + /** + * ブラックノートホール + */ "title": string; + /** + * ノートを70,000回投稿ã—㟠+ */ "description": string; }; "_notes80000": { + /** + * ノートギャラクシー + */ "title": string; + /** + * ノートを80,000回投稿ã—㟠+ */ "description": string; }; "_notes90000": { + /** + * ノートãƒãƒ¼ã‚¹ + */ "title": string; + /** + * ノートを90,000回投稿ã—㟠+ */ "description": string; }; "_notes100000": { + /** + * ALL YOUR NOTE ARE BELONG TO US + */ "title": string; + /** + * ノートを100,000回投稿ã—㟠+ */ "description": string; + /** + * ãã‚“ãªã«æ›¸ãã“ã¨ã‚る? + */ "flavor": string; }; "_login3": { + /** + * ãƒ“ã‚®ãƒŠãƒ¼â… + */ "title": string; + /** + * 通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ3æ—¥ + */ "description": string; + /** + * 今日ã‹ã‚‰ã僕㯠ミスã‚ストã£ã¦ã“ã¨ã§ + */ "flavor": string; }; "_login7": { + /** + * ビギナーⅡ + */ "title": string; + /** + * 通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ7æ—¥ + */ "description": string; + /** + * 慣れã¦ãã¾ã—ãŸã‹ï¼Ÿ + */ "flavor": string; }; "_login15": { + /** + * ビギナーⅢ + */ "title": string; + /** + * 通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ15æ—¥ + */ "description": string; }; "_login30": { + /** + * ミスã‚ã‚¹ãƒˆâ… + */ "title": string; + /** + * 通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ30æ—¥ + */ "description": string; }; "_login60": { + /** + * ミスã‚ストⅡ + */ "title": string; + /** + * 通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ60æ—¥ + */ "description": string; }; "_login100": { + /** + * ミスã‚ストⅢ + */ "title": string; + /** + * 通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ100æ—¥ + */ "description": string; + /** + * ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã€ãƒŸã‚¹ã‚ストã«ã¤ã + */ "flavor": string; }; "_login200": { + /** + * å¸¸é€£â… + */ "title": string; + /** + * 通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ200æ—¥ + */ "description": string; }; "_login300": { + /** + * 常連Ⅱ + */ "title": string; + /** + * 通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ300æ—¥ + */ "description": string; }; "_login400": { + /** + * 常連Ⅲ + */ "title": string; + /** + * 通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ400æ—¥ + */ "description": string; }; "_login500": { + /** + * ãƒ™ãƒ†ãƒ©ãƒ³â… + */ "title": string; + /** + * 通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ500æ—¥ + */ "description": string; + /** + * 諸å›ã€ç§ã¯ãƒŽãƒ¼ãƒˆãŒå¥½ãã + */ "flavor": string; }; "_login600": { + /** + * ベテランⅡ + */ "title": string; + /** + * 通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ600æ—¥ + */ "description": string; }; "_login700": { + /** + * ベテランⅢ + */ "title": string; + /** + * 通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ700æ—¥ + */ "description": string; }; "_login800": { + /** + * ãƒŽãƒ¼ãƒˆãƒžã‚¹ã‚¿ãƒ¼â… + */ "title": string; + /** + * 通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ800æ—¥ + */ "description": string; }; "_login900": { + /** + * ノートマスターⅡ + */ "title": string; + /** + * 通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ900æ—¥ + */ "description": string; }; "_login1000": { + /** + * ノートマスターⅢ + */ "title": string; + /** + * 通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ1,000æ—¥ + */ "description": string; + /** + * Sharkeyを使ã£ã¦ãã‚Œã¦ã‚ã‚ŠãŒã¨ã†ï¼ + */ "flavor": string; }; "_noteClipped1": { + /** + * クリップã›ãšã«ã¯ã„られãªã„㪠+ */ "title": string; + /** + * åˆã‚ã¦ãƒŽãƒ¼ãƒˆã‚’クリップã—㟠+ */ "description": string; }; "_noteFavorited1": { + /** + * 星をã¿ã‚‹ã²ã¨ + */ "title": string; + /** + * åˆã‚ã¦ãƒŽãƒ¼ãƒˆã‚’ãŠæ°—ã«å…¥ã‚Šã«ç™»éŒ²ã—㟠+ */ "description": string; }; "_myNoteFavorited1": { + /** + * 星ãŒæ¬²ã—ã„ + */ "title": string; + /** + * 自分ã®ãƒŽãƒ¼ãƒˆãŒä»–ã®äººã‹ã‚‰ãŠæ°—ã«å…¥ã‚Šã«ç™»éŒ²ã•ã‚ŒãŸ + */ "description": string; }; "_profileFilled": { + /** + * 準備万端 + */ "title": string; + /** + * プãƒãƒ•ã‚£ãƒ¼ãƒ«è¨å®šã‚’è¡Œã£ãŸ + */ "description": string; }; "_markedAsCat": { + /** + * å¾è¼©ã¯çŒ«ã§ã‚ã‚‹ + */ "title": string; + /** + * アカウントをCatã¨ã—ã¦è¨å®šã—㟠+ */ "description": string; + /** + * åå‰ã¯ã¾ã ãªã„。 + */ "flavor": string; }; "_following1": { + /** + * ã¯ã˜ã‚ã¦ã®ãƒ•ã‚©ãƒãƒ¼ + */ "title": string; + /** + * åˆã‚ã¦ãƒ•ã‚©ãƒãƒ¼ã—㟠+ */ "description": string; }; "_following10": { + /** + * ã¤ã„ã¦ãã€ã¤ã„ã¦ã + */ "title": string; + /** + * フォãƒãƒ¼ãŒ10人を超ã—㟠+ */ "description": string; }; "_following50": { + /** + * å‹é”ãŸãã•ã‚“ + */ "title": string; + /** + * フォãƒãƒ¼ãŒ50人を超ã—㟠+ */ "description": string; }; "_following100": { + /** + * å‹é”100人 + */ "title": string; + /** + * フォãƒãƒ¼ãŒ100人を超ã—㟠+ */ "description": string; }; "_following300": { + /** + * å‹é”éŽå¤š + */ "title": string; + /** + * フォãƒãƒ¼ãŒ300人を超ã—㟠+ */ "description": string; }; "_followers1": { + /** + * ã¯ã˜ã‚ã¦ã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼ + */ "title": string; + /** + * åˆã‚ã¦ãƒ•ã‚©ãƒãƒ¼ã•ã‚ŒãŸ + */ "description": string; }; "_followers10": { + /** + * フォãƒãƒ¼ãƒŸãƒ¼ï¼ + */ "title": string; + /** + * フォãƒãƒ¯ãƒ¼ãŒ10人を超ã—㟠+ */ "description": string; }; "_followers50": { + /** + * ãžã‚ãžã‚ + */ "title": string; + /** + * フォãƒãƒ¯ãƒ¼ãŒ50人を超ã—㟠+ */ "description": string; }; "_followers100": { + /** + * 人気者 + */ "title": string; + /** + * フォãƒãƒ¯ãƒ¼ãŒ100人を超ã—㟠+ */ "description": string; }; "_followers300": { + /** + * 一列ã§ãŠä¸¦ã³ãã ã•ã„ + */ "title": string; + /** + * フォãƒãƒ¯ãƒ¼ãŒ300人を超ã—㟠+ */ "description": string; }; "_followers500": { + /** + * 基地局 + */ "title": string; + /** + * フォãƒãƒ¯ãƒ¼ãŒ500人を超ã—㟠+ */ "description": string; }; "_followers1000": { + /** + * インフルエンサー + */ "title": string; + /** + * フォãƒãƒ¯ãƒ¼ãŒ1,000人を超ã—㟠+ */ "description": string; }; "_collectAchievements30": { + /** + * 実績コレクター + */ "title": string; + /** + * 実績を30個以上ç²å¾—ã—㟠+ */ "description": string; }; "_viewAchievements3min": { + /** + * 実績好ã + */ "title": string; + /** + * 実績一覧を3分以上眺ã‚続ã‘㟠+ */ "description": string; }; "_iLoveMisskey": { + /** + * I Love Sharkey + */ "title": string; + /** + * "I ⤠#Sharkey"を投稿ã—㟠+ */ "description": string; + /** + * Sharkeyを使ã£ã¦ãã ã•ã‚Šã‚ã‚ŠãŒã¨ã†ã”ã–ã„ã¾ã™ï¼ by 開発ãƒãƒ¼ãƒ + */ "flavor": string; }; "_foundTreasure": { + /** + * å®æŽ¢ã— + */ "title": string; + /** + * éš ã•ã‚ŒãŸãŠå®ã‚’発見ã—㟠+ */ "description": string; }; "_client30min": { + /** + * ã²ã¨ã‚„ã™ã¿ + */ "title": string; + /** + * クライアントを起動ã—ã¦ã‹ã‚‰30分以上経éŽã—㟠+ */ "description": string; }; "_client60min": { + /** + * Sharkeyã®è¦‹ã™ãŽ + */ "title": string; + /** + * クライアントを起動ã—ã¦ã‹ã‚‰60分以上経éŽã—㟠+ */ "description": string; }; "_noteDeletedWithin1min": { + /** + * ã„ã¾ã®ãªã— + */ "title": string; + /** + * 投稿ã—ã¦ã‹ã‚‰1分以内ã«ãã®æŠ•ç¨¿ã‚’削除ã—㟠+ */ "description": string; }; "_postedAtLateNight": { + /** + * 夜行性 + */ "title": string; + /** + * 深夜ã«ãƒŽãƒ¼ãƒˆã‚’投稿ã—㟠+ */ "description": string; + /** + * ãã‚ãã‚å¯ã‚ˆã†ã€‚ + */ "flavor": string; }; "_postedAt0min0sec": { + /** + * æ™‚å ± + */ "title": string; + /** + * 0分0秒ã«ãƒŽãƒ¼ãƒˆã‚’投稿ã—㟠+ */ "description": string; + /** + * ãƒãƒƒ ãƒãƒƒ ãƒãƒƒ ピーン + */ "flavor": string; }; "_selfQuote": { + /** + * è‡ªå·±è¨€åŠ + */ "title": string; + /** + * 自分ã®ãƒŽãƒ¼ãƒˆã‚’引用ã—㟠+ */ "description": string; }; "_htl20npm": { + /** + * æµã‚Œã‚‹TL + */ "title": string; + /** + * ホームタイムラインã®æµé€ŸãŒ20npmを越㙠+ */ "description": string; }; "_viewInstanceChart": { + /** + * アナリスト + */ "title": string; + /** + * サーãƒãƒ¼ã®ãƒãƒ£ãƒ¼ãƒˆã‚’表示ã—㟠+ */ "description": string; }; "_outputHelloWorldOnScratchpad": { + /** + * Hello, world! + */ "title": string; + /** + * スクラッãƒãƒ‘ッド㧠hello world を出力ã—㟠+ */ "description": string; }; "_open3windows": { + /** + * マルãƒã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ + */ "title": string; + /** + * ウィンドウを3ã¤ä»¥ä¸Šé–‹ã„ãŸçŠ¶æ…‹ã«ã—㟠+ */ "description": string; }; "_driveFolderCircularReference": { + /** + * 循環å‚ç…§ + */ "title": string; + /** + * ドライブã®ãƒ•ã‚©ãƒ«ãƒ€ã‚’å†å¸°çš„ãªå…¥ã‚Œåã«ã—よã†ã¨ã—㟠+ */ "description": string; }; "_reactWithoutRead": { + /** + * ã¡ã‚ƒã‚“ã¨èªã‚“ã ? + */ "title": string; + /** + * 100æ–‡å—以上ã®ãƒ†ã‚ストをå«ã‚€ãƒŽãƒ¼ãƒˆã«æŠ•ç¨¿ã•ã‚Œã¦ã‹ã‚‰3秒以内ã«ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã—㟠+ */ "description": string; }; "_clickedClickHere": { + /** + * ã“ã“をクリック + */ "title": string; + /** + * ã“ã“をクリックã—㟠+ */ "description": string; }; "_justPlainLucky": { + /** + * å˜ãªã‚‹ãƒ©ãƒƒã‚ー + */ "title": string; + /** + * 10秒ã”ã¨ã«0.005%ã®ç¢ºçŽ‡ã§ç²å¾— + */ "description": string; }; "_setNameToSyuilo": { + /** + * 神様コンプレックス + */ "title": string; + /** + * åå‰ã‚’ syuilo ã«è¨å®šã—㟠+ */ "description": string; }; "_passedSinceAccountCreated1": { + /** + * 一周年 + */ "title": string; + /** + * アカウント作æˆã‹ã‚‰1年経éŽã—㟠+ */ "description": string; }; "_passedSinceAccountCreated2": { + /** + * 二周年 + */ "title": string; + /** + * アカウント作æˆã‹ã‚‰2年経éŽã—㟠+ */ "description": string; }; "_passedSinceAccountCreated3": { + /** + * 三周年 + */ "title": string; + /** + * アカウント作æˆã‹ã‚‰3年経éŽã—㟠+ */ "description": string; }; "_loggedInOnBirthday": { + /** + * ãƒãƒƒãƒ”ーãƒãƒ¼ã‚¹ãƒ‡ãƒ¼ + */ "title": string; + /** + * 誕生日ã«ãƒã‚°ã‚¤ãƒ³ã—㟠+ */ "description": string; }; "_loggedInOnNewYearsDay": { + /** + * ã‚ã‘ã¾ã—ã¦ãŠã‚ã§ã¨ã†ã”ã–ã„ã¾ã™ + */ "title": string; + /** + * 元日ã«ãƒã‚°ã‚¤ãƒ³ã—㟠+ */ "description": string; + /** + * 今年も弊サーãƒãƒ¼ã‚’よã‚ã—ããŠé¡˜ã„ã—ã¾ã™ + */ "flavor": string; }; "_cookieClicked": { + /** + * クッã‚ーをクリックã™ã‚‹ã‚²ãƒ¼ãƒ + */ "title": string; + /** + * クッã‚ーをクリックã—㟠+ */ "description": string; + /** + * ソフト間é•ã£ã¦ãªã„? + */ "flavor": string; }; "_brainDiver": { + /** + * Brain Diver + */ "title": string; + /** + * Brain Diverã¸ã®ãƒªãƒ³ã‚¯ã‚’投稿ã—㟠+ */ "description": string; + /** + * Misskey-Misskey La-Tu-Ma + */ "flavor": string; }; "_smashTestNotificationButton": { + /** + * テストéŽå‰° + */ "title": string; + /** + * 通知ã®ãƒ†ã‚¹ãƒˆã‚’ã”ãçŸæ™‚é–“ã®ã†ã¡ã«é€£ç¶šã—ã¦è¡Œã£ãŸ + */ "description": string; }; "_tutorialCompleted": { + /** + * Sharkeyåˆå¿ƒè€…講座 修了証 + */ "title": string; + /** + * ãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ«ã‚’完了ã—㟠+ */ "description": string; }; + "_bubbleGameExplodingHead": { + /** + * 🤯 + */ + "title": string; + /** + * ãƒãƒ–ルゲームã§æœ€ã‚‚大ãã„モノを出ã—㟠+ */ + "description": string; + }; + "_bubbleGameDoubleExplodingHead": { + /** + * ダブル🤯 + */ + "title": string; + /** + * ãƒãƒ–ルゲームã§æœ€ã‚‚大ãã„モノを2ã¤åŒæ™‚ã«å‡ºã—㟠+ */ + "description": string; + /** + * ã“ã‚Œãらã„ã®ã€€ãŠã¹ã‚“ã¨ã°ã“ã«ã€€ðŸ¤¯ã€€ðŸ¤¯ã€€ã¡ã‚‡ã£ã¨ã¤ã‚㦠+ */ + "flavor": string; + }; }; }; "_role": { + /** + * ãƒãƒ¼ãƒ«ã®ä½œæˆ + */ "new": string; + /** + * ãƒãƒ¼ãƒ«ã®ç·¨é›† + */ "edit": string; + /** + * ãƒãƒ¼ãƒ«å + */ "name": string; + /** + * ãƒãƒ¼ãƒ«ã®èª¬æ˜Ž + */ "description": string; + /** + * ãƒãƒ¼ãƒ«ã®æ¨©é™ + */ "permission": string; + /** + * <b>モデレーター</b>ã¯åŸºæœ¬çš„ãªãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã«é–¢ã™ã‚‹æ“作を行ãˆã¾ã™ã€‚ + * <b>管ç†è€…</b>ã¯ã‚µãƒ¼ãƒãƒ¼ã®å…¨ã¦ã®è¨å®šã‚’変更ã§ãã¾ã™ã€‚ + */ "descriptionOfPermission": string; + /** + * アサイン + */ "assignTarget": string; + /** + * <b>マニュアル</b>ã¯èª°ãŒã“ã®ãƒãƒ¼ãƒ«ã«å«ã¾ã‚Œã‚‹ã‹ã‚’手動ã§ç®¡ç†ã—ã¾ã™ã€‚ + * <b>コンディショナル</b>ã¯æ¡ä»¶ã‚’è¨å®šã—ã€ãã‚Œã«åˆè‡´ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒè‡ªå‹•ã§å«ã¾ã‚Œã‚‹ã‚ˆã†ã«ãªã‚Šã¾ã™ã€‚ + */ "descriptionOfAssignTarget": string; + /** + * マニュアル + */ "manual": string; + /** + * マニュアルãƒãƒ¼ãƒ« + */ "manualRoles": string; + /** + * コンディショナル + */ "conditional": string; + /** + * コンディショナルãƒãƒ¼ãƒ« + */ "conditionalRoles": string; + /** + * æ¡ä»¶ + */ "condition": string; + /** + * ã“ã‚Œã¯ã‚³ãƒ³ãƒ‡ã‚£ã‚·ãƒ§ãƒŠãƒ«ãƒãƒ¼ãƒ«ã§ã™ã€‚ + */ "isConditionalRole": string; + /** + * 公開ãƒãƒ¼ãƒ« + */ "isPublic": string; + /** + * ユーザーã®ãƒ—ãƒãƒ•ã‚£ãƒ¼ãƒ«ã§ã“ã®ãƒãƒ¼ãƒ«ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ + */ "descriptionOfIsPublic": string; + /** + * オプション + */ "options": string; + /** + * ãƒãƒªã‚·ãƒ¼ + */ "policies": string; + /** + * ベースãƒãƒ¼ãƒ« + */ "baseRole": string; + /** + * ベースãƒãƒ¼ãƒ«ã®å€¤ã‚’使用 + */ "useBaseValue": string; + /** + * アサインã™ã‚‹ãƒãƒ¼ãƒ«ã‚’é¸æŠž + */ "chooseRoleToAssign": string; + /** + * アイコン画åƒã®URL + */ "iconUrl": string; + /** + * ãƒãƒƒã‚¸ã¨ã—ã¦è¡¨ç¤º + */ "asBadge": string; + /** + * オンã«ã™ã‚‹ã¨ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼åã®æ¨ªã«ãƒãƒ¼ãƒ«ã®ã‚¢ã‚¤ã‚³ãƒ³ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ + */ "descriptionOfAsBadge": string; + /** + * ユーザーを見ã¤ã‘ã‚„ã™ãã™ã‚‹ + */ "isExplorable": string; + /** + * オンã«ã™ã‚‹ã¨ã€ã€Œã¿ã¤ã‘ã‚‹ã€ã§ãƒ¡ãƒ³ãƒãƒ¼ä¸€è¦§ãŒå…¬é–‹ã•ã‚Œã‚‹ã»ã‹ã€ãƒãƒ¼ãƒ«ã®ã‚¿ã‚¤ãƒ ラインãŒåˆ©ç”¨å¯èƒ½ã«ãªã‚Šã¾ã™ã€‚ + */ "descriptionOfIsExplorable": string; + /** + * è¡¨ç¤ºé † + */ "displayOrder": string; + /** + * 数値ãŒå¤§ãã„ã»ã©UI上ã§å…ˆé ã«è¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ + */ "descriptionOfDisplayOrder": string; + /** + * モデレーターã®ãƒ¡ãƒ³ãƒãƒ¼ç·¨é›†ã‚’è¨±å¯ + */ "canEditMembersByModerator": string; + /** + * オンã«ã™ã‚‹ã¨ã€ç®¡ç†è€…ã«åŠ ãˆã¦ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚¿ãƒ¼ã‚‚ã“ã®ãƒãƒ¼ãƒ«ã¸ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’アサイン/アサイン解除ã§ãるよã†ã«ãªã‚Šã¾ã™ã€‚オフã«ã™ã‚‹ã¨ç®¡ç†è€…ã®ã¿ãŒè¡Œãˆã¾ã™ã€‚ + */ "descriptionOfCanEditMembersByModerator": string; + /** + * 優先度 + */ "priority": string; "_priority": { + /** + * 低 + */ "low": string; + /** + * ä¸ + */ "middle": string; + /** + * 高 + */ "high": string; }; "_options": { + /** + * ã‚°ãƒãƒ¼ãƒãƒ«ã‚¿ã‚¤ãƒ ラインã®é–²è¦§ + */ "gtlAvailable": string; + /** + * ãƒãƒ–ルタイムラインã®é–²è¦§ + */ "btlAvailable": string; + /** + * ãƒãƒ¼ã‚«ãƒ«ã‚¿ã‚¤ãƒ ラインã®é–²è¦§ + */ "ltlAvailable": string; + /** + * パブリック投稿ã®è¨±å¯ + */ "canPublicNote": string; + /** + * ノートã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆãŒå¯èƒ½ + */ "canImportNotes": string; + /** + * ノート内ã®æœ€å¤§ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³æ•° + */ + "mentionMax": string; + /** + * サーãƒãƒ¼æ‹›å¾…コードã®ç™ºè¡Œ + */ "canInvite": string; + /** + * 招待コードã®ä½œæˆå¯èƒ½æ•° + */ "inviteLimit": string; + /** + * 招待コードã®ç™ºè¡Œé–“éš” + */ "inviteLimitCycle": string; + /** + * 招待コードã®æœ‰åŠ¹æœŸé™ + */ "inviteExpirationTime": string; + /** + * カスタム絵文å—ã®ç®¡ç† + */ "canManageCustomEmojis": string; + /** + * ã‚¢ãƒã‚¿ãƒ¼ãƒ‡ã‚³ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã®ç®¡ç† + */ "canManageAvatarDecorations": string; + /** + * ãƒ‰ãƒ©ã‚¤ãƒ–å®¹é‡ + */ "driveCapacity": string; + /** + * ファイルã«NSFWを常ã«ä»˜ä¸Ž + */ "alwaysMarkNsfw": string; + /** + * ノートã®ãƒ”ン留ã‚ã®æœ€å¤§æ•° + */ "pinMax": string; + /** + * アンテナã®ä½œæˆå¯èƒ½æ•° + */ "antennaMax": string; + /** + * ワードミュートã®æœ€å¤§æ–‡å—æ•° + */ "wordMuteMax": string; + /** + * Webhookã®ä½œæˆå¯èƒ½æ•° + */ "webhookMax": string; + /** + * クリップã®ä½œæˆå¯èƒ½æ•° + */ "clipMax": string; + /** + * クリップ内ã®ãƒŽãƒ¼ãƒˆã®æœ€å¤§æ•° + */ "noteEachClipsMax": string; + /** + * ユーザーリストã®ä½œæˆå¯èƒ½æ•° + */ "userListMax": string; + /** + * ユーザーリスト内ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æœ€å¤§æ•° + */ "userEachUserListsMax": string; + /** + * レートリミット + */ "rateLimitFactor": string; + /** + * å°ã•ã„ã»ã©åˆ¶é™ãŒç·©å’Œã•ã‚Œã€å¤§ãã„ã»ã©åˆ¶é™ãŒå¼·åŒ–ã•ã‚Œã¾ã™ã€‚ + */ "descriptionOfRateLimitFactor": string; + /** + * 広告ã®éžè¡¨ç¤º + */ "canHideAds": string; + /** + * ノート検索ã®åˆ©ç”¨ + */ "canSearchNotes": string; + /** + * 翻訳機能ã®åˆ©ç”¨ + */ "canUseTranslator": string; + /** + * アイコンデコレーションã®æœ€å¤§å–付個数 + */ "avatarDecorationLimit": string; }; "_condition": { + /** + * マニュアルãƒãƒ¼ãƒ«ã«ã‚¢ã‚µã‚¤ãƒ³æ¸ˆã¿ + */ + "roleAssignedTo": string; + /** + * ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ + */ "isLocal": string; + /** + * リモートユーザー + */ "isRemote": string; + /** + * アカウント作æˆã‹ã‚‰ï½žä»¥å†… + */ "createdLessThan": string; + /** + * アカウント作æˆã‹ã‚‰ï½žçµŒéŽ + */ "createdMoreThan": string; + /** + * フォãƒãƒ¯ãƒ¼æ•°ãŒï½žä»¥ä¸‹ + */ "followersLessThanOrEq": string; + /** + * フォãƒãƒ¯ãƒ¼æ•°ãŒï½žä»¥ä¸Š + */ "followersMoreThanOrEq": string; + /** + * フォãƒãƒ¼æ•°ãŒï½žä»¥ä¸‹ + */ "followingLessThanOrEq": string; + /** + * フォãƒãƒ¼æ•°ãŒï½žä»¥ä¸Š + */ "followingMoreThanOrEq": string; + /** + * 投稿数ãŒï½žä»¥ä¸‹ + */ "notesLessThanOrEq": string; + /** + * 投稿数ãŒï½žä»¥ä¸Š + */ "notesMoreThanOrEq": string; + /** + * ~ã‹ã¤ï½ž + */ "and": string; + /** + * ~ã¾ãŸã¯ï½ž + */ "or": string; + /** + * ~ã§ã¯ãªã„ + */ "not": string; }; }; "_sensitiveMediaDetection": { + /** + * 機械å¦ç¿’を使ã£ã¦è‡ªå‹•ã§ã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ãªãƒ¡ãƒ‡ã‚£ã‚¢ã‚’検出ã—ã€ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã«å½¹ç«‹ã¦ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚サーãƒãƒ¼ã®è² è·ãŒå°‘ã—増ãˆã¾ã™ã€‚ + */ "description": string; + /** + * 検出感度 + */ "sensitivity": string; + /** + * 感度を低ãã™ã‚‹ã¨ã€èª¤æ¤œçŸ¥(å½é™½æ€§)ãŒæ¸›ã‚Šã¾ã™ã€‚感度を高ãã™ã‚‹ã¨ã€æ¤œçŸ¥æ¼ã‚Œ(å½é™°æ€§)ãŒæ¸›ã‚Šã¾ã™ã€‚ + */ "sensitivityDescription": string; + /** + * センシティブフラグをè¨å®šã™ã‚‹ + */ "setSensitiveFlagAutomatically": string; + /** + * ã“ã®è¨å®šã‚’オフã«ã—ã¦ã‚‚内部的ã«åˆ¤å®šçµæžœã¯ä¿æŒã•ã‚Œã¾ã™ã€‚ + */ "setSensitiveFlagAutomaticallyDescription": string; + /** + * å‹•ç”»ã®è§£æžã‚’有効化 + */ "analyzeVideos": string; + /** + * é™æ¢ç”»ã«åŠ ãˆã¦å‹•ç”»ã‚‚解æžã™ã‚‹ã‚ˆã†ã«ã—ã¾ã™ã€‚サーãƒãƒ¼ã®è² è·ãŒå°‘ã—増ãˆã¾ã™ã€‚ + */ "analyzeVideosDescription": string; }; "_emailUnavailable": { + /** + * æ—¢ã«ä½¿ç”¨ã•ã‚Œã¦ã„ã¾ã™ + */ "used": string; + /** + * å½¢å¼ãŒæ£ã—ãã‚ã‚Šã¾ã›ã‚“ + */ "format": string; + /** + * æ’ä¹…çš„ã«ä½¿ç”¨å¯èƒ½ãªã‚¢ãƒ‰ãƒ¬ã‚¹ã§ã¯ã‚ã‚Šã¾ã›ã‚“ + */ "disposable": string; + /** + * æ£ã—ã„メールサーãƒãƒ¼ã§ã¯ã‚ã‚Šã¾ã›ã‚“ + */ "mx": string; + /** + * メールサーãƒãƒ¼ãŒå¿œç”ã—ã¾ã›ã‚“ + */ "smtp": string; + /** + * ã“ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã§ã¯ç™»éŒ²ã§ãã¾ã›ã‚“ + */ "banned": string; }; "_ffVisibility": { + /** + * 公開 + */ "public": string; + /** + * フォãƒãƒ¯ãƒ¼ã ã‘ã«å…¬é–‹ + */ "followers": string; + /** + * éžå…¬é–‹ + */ "private": string; }; "_signup": { + /** + * ã»ã¨ã‚“ã©å®Œäº†ã§ã™ + */ "almostThere": string; + /** + * ã‚ãªãŸãŒä½¿ã£ã¦ã„るメールアドレスを入力ã—ã¦ãã ã•ã„。メールアドレスãŒå…¬é–‹ã•ã‚Œã‚‹ã“ã¨ã¯ã‚ã‚Šã¾ã›ã‚“。 + */ "emailAddressInfo": string; - "emailSent": string; + /** + * 入力ã•ã‚ŒãŸãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹({email})å®›ã«ç¢ºèªã®ãƒ¡ãƒ¼ãƒ«ãŒé€ä¿¡ã•ã‚Œã¾ã—ãŸã€‚メールã«è¨˜è¼‰ã•ã‚ŒãŸãƒªãƒ³ã‚¯ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã¨ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ä½œæˆãŒå®Œäº†ã—ã¾ã™ã€‚メールã«è¨˜è¼‰ã•ã‚Œã¦ã„るリンクã®æœ‰åŠ¹æœŸé™ã¯30分ã§ã™ã€‚ + */ + "emailSent": ParameterizedString<"email">; + /** + * アカウントãŒä½œæˆã•ã‚Œã€æ‰¿èªå¾…ã¡ã®çŠ¶æ…‹ã§ã™ã€‚ + */ "approvalPending": string; + /** + * インスタンスã«å‚åŠ ã—ãŸã„ç†ç”±ã‚’入力ã—ã¦ãã ã•ã„。 + */ "reasonInfo": string; }; "_accountDelete": { + /** + * アカウントã®å‰Šé™¤ + */ "accountDelete": string; + /** + * アカウントã®å‰Šé™¤ã¯è² è·ã®ã‹ã‹ã‚‹å‡¦ç†ã§ã‚ã‚‹ãŸã‚ã€ä½œæˆã—ãŸã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã®æ•°ã‚„アップãƒãƒ¼ãƒ‰ã—ãŸãƒ•ã‚¡ã‚¤ãƒ«ã®æ•°ãŒå¤šã„ã¨å®Œäº†ã¾ã§ã«æ™‚é–“ãŒã‹ã‹ã‚‹ã“ã¨ãŒã‚ã‚Šã¾ã™ã€‚ + */ "mayTakeTime": string; + /** + * アカウントã®å‰Šé™¤ãŒå®Œäº†ã™ã‚‹éš›ã¯ã€ç™»éŒ²ã—ã¦ã‚ã£ãŸãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹å®›ã«é€šçŸ¥ã‚’é€ä¿¡ã—ã¾ã™ã€‚ + */ "sendEmail": string; + /** + * アカウント削除をリクエスト + */ "requestAccountDelete": string; + /** + * 削除処ç†ãŒé–‹å§‹ã•ã‚Œã¾ã—ãŸã€‚ + */ "started": string; + /** + * 削除ãŒé€²è¡Œä¸ + */ "inProgress": string; }; "_ad": { + /** + * 戻る + */ "back": string; + /** + * ã“ã®åºƒå‘Šã®è¡¨ç¤ºé »åº¦ã‚’下ã’ã‚‹ + */ "reduceFrequencyOfThisAd": string; + /** + * 表示ã—ãªã„ + */ "hide": string; + /** + * 曜日ã¯ã‚µãƒ¼ãƒãƒ¼ã®ã‚¿ã‚¤ãƒ ゾーンを元ã«æŒ‡å®šã•ã‚Œã¾ã™ã€‚ + */ "timezoneinfo": string; + /** + * 広告é…ä¿¡è¨å®š + */ "adsSettings": string; + /** + * リアルタイム更新ä¸ã«åºƒå‘Šã‚’é…ä¿¡ã™ã‚‹é–“隔(ノートã®å€‹æ•°ï¼‰ + */ "notesPerOneAd": string; + /** + * 0ã§ãƒªã‚¢ãƒ«ã‚¿ã‚¤ãƒ 更新時ã®åºƒå‘Šé…信を無効 + */ "setZeroToDisable": string; + /** + * 広告ã®é…ä¿¡é–“éš”ãŒæ¥µã‚ã¦çŸã„ãŸã‚ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ä½“験ãŒè‘—ã—ãæã‚れるå¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚ + */ "adsTooClose": string; }; "_forgotPassword": { + /** + * アカウントã«ç™»éŒ²ã—ãŸãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’入力ã—ã¦ãã ã•ã„。ãã®ã‚¢ãƒ‰ãƒ¬ã‚¹å®›ã¦ã«ã€ãƒ‘スワードリセット用ã®ãƒªãƒ³ã‚¯ãŒé€ä¿¡ã•ã‚Œã¾ã™ã€‚ + */ "enterEmail": string; + /** + * メールアドレスを登録ã—ã¦ã„ãªã„å ´åˆã¯ã€ç®¡ç†è€…ã¾ã§ãŠå•ã„åˆã‚ã›ãã ã•ã„。 + */ "ifNoEmail": string; + /** + * ã“ã®ã‚µãƒ¼ãƒãƒ¼ã§ã¯ãƒ¡ãƒ¼ãƒ«ãŒã‚µãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ãªã„ãŸã‚ã€ãƒ‘スワードリセットを行ã†å ´åˆã¯ç®¡ç†è€…ã¾ã§ãŠå•ã„åˆã‚ã›ãã ã•ã„。 + */ "contactAdmin": string; }; "_gallery": { + /** + * 自分ã®æŠ•ç¨¿ + */ "my": string; + /** + * ã„ã„ãã—ãŸæŠ•ç¨¿ + */ "liked": string; + /** + * ã„ã„ãï¼ + */ "like": string; + /** + * ã„ã„ã解除 + */ "unlike": string; }; "_email": { "_follow": { + /** + * フォãƒãƒ¼ã•ã‚Œã¾ã—㟠+ */ "title": string; }; "_receiveFollowRequest": { + /** + * フォãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’å—ã‘å–ã‚Šã¾ã—㟠+ */ "title": string; }; }; "_plugin": { + /** + * プラグインã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ« + */ "install": string; + /** + * ä¿¡é ¼ã§ããªã„プラグインã¯ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ãªã„ã§ãã ã•ã„。 + */ "installWarn": string; + /** + * プラグインã®ç®¡ç† + */ "manage": string; + /** + * ソースを表示 + */ "viewSource": string; }; "_preferencesBackups": { + /** + * 作æˆã—ãŸãƒãƒƒã‚¯ã‚¢ãƒƒãƒ— + */ "list": string; + /** + * æ–°è¦ä¿å˜ + */ "saveNew": string; + /** + * ファイルをèªã¿è¾¼ã¿ + */ "loadFile": string; + /** + * ã“ã®ãƒ‡ãƒã‚¤ã‚¹ã«é©ç”¨ + */ "apply": string; + /** + * 上書ãä¿å˜ + */ "save": string; + /** + * ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—åを入力 + */ "inputName": string; + /** + * ä¿å˜ã§ãã¾ã›ã‚“ + */ "cannotSave": string; - "nameAlreadyExists": string; - "applyConfirm": string; - "saveConfirm": string; - "deleteConfirm": string; - "renameConfirm": string; + /** + * ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—å「{name}ã€ã¯æ—¢ã«å˜åœ¨ã—ã¾ã™ã€‚é•ã†åå‰ã‚’指定ã—ã¦ãã ã•ã„。 + */ + "nameAlreadyExists": ParameterizedString<"name">; + /** + * ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—「{name}ã€ã‚’ç¾åœ¨ã®ãƒ‡ãƒã‚¤ã‚¹ã«é©ç”¨ã—ã¾ã™ã‹ï¼Ÿç¾åœ¨ã®ãƒ‡ãƒã‚¤ã‚¹è¨å®šã¯å¤±ã‚ã‚Œã¾ã™ã€‚ + */ + "applyConfirm": ParameterizedString<"name">; + /** + * {name}ã«ä¸Šæ›¸ãä¿å˜ã—ã¾ã™ã‹ï¼Ÿ + */ + "saveConfirm": ParameterizedString<"name">; + /** + * {name}を削除ã—ã¾ã™ã‹ï¼Ÿ + */ + "deleteConfirm": ParameterizedString<"name">; + /** + * 「{old}ã€ã‚’「{new}ã€ã«å¤‰æ›´ã—ã¾ã™ã‹ï¼Ÿ + */ + "renameConfirm": ParameterizedString<"old" | "new">; + /** + * ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã¯ã‚ã‚Šã¾ã›ã‚“。「新è¦ä¿å˜ã€ã§ç¾åœ¨ã®ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆè¨å®šã‚’サーãƒãƒ¼ã«ä¿å˜ã§ãã¾ã™ã€‚ + */ "noBackups": string; - "createdAt": string; - "updatedAt": string; + /** + * 作æˆæ—¥æ™‚: {date} {time} + */ + "createdAt": ParameterizedString<"date" | "time">; + /** + * 更新日時: {date} {time} + */ + "updatedAt": ParameterizedString<"date" | "time">; + /** + * èªã¿è¾¼ã¿ã§ãã¾ã›ã‚“ + */ "cannotLoad": string; + /** + * ファイル形å¼ãŒé•ã„ã¾ã™ã€‚ + */ "invalidFile": string; }; "_registry": { + /** + * スコープ + */ "scope": string; + /** + * ã‚ー + */ "key": string; + /** + * ã‚ー + */ "keys": string; + /** + * ドメイン + */ "domain": string; + /** + * ã‚ãƒ¼ã‚’ä½œæˆ + */ "createKey": string; }; "_aboutMisskey": { + /** + * Sharkeyã¯ã€Misskeyをベースã«ã—ãŸã‚ªãƒ¼ãƒ—ンソースã®ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã§ã™ã€‚ + */ "about": string; + /** + * 主ãªã‚³ãƒ³ãƒˆãƒªãƒ“ューター + */ "contributors": string; + /** + * å…¨ã¦ã®ã‚³ãƒ³ãƒˆãƒªãƒ“ューター + */ "allContributors": string; + /** + * ソースコード + */ "source": string; + /** + * Misskey オリジナル + */ + "original": string; + /** + * Sharkey オリジナル + */ + "original_sharkey": string; + /** + * {name}ã¯ã‚ªãƒªã‚¸ãƒŠãƒ«ã®Sharkeyを改変ã—ãŸãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’使用ã—ã¦ã„ã¾ã™ã€‚ + */ + "thisIsModifiedVersion": ParameterizedString<"name">; + /** + * Sharkeyを翻訳 + */ "translation": string; + /** + * Misskeyã«å¯„付 + */ "donate": string; + /** + * Sharkeyã«å¯„付 + */ + "donate_sharkey": string; + /** + * ä»–ã«ã‚‚多ãã®æ–¹ãŒæ”¯æ´ã—ã¦ãã‚Œã¦ã„ã¾ã™ã€‚ã‚ã‚ŠãŒã¨ã†ã”ã–ã„ã¾ã™ðŸ¥° + */ "morePatrons": string; + /** + * 支æ´è€… + */ "patrons": string; + /** + * プãƒã‚¸ã‚§ã‚¯ãƒˆãƒ¡ãƒ³ãƒãƒ¼ + */ "projectMembers": string; }; "_displayOfSensitiveMedia": { + /** + * センシティブè¨å®šã•ã‚ŒãŸãƒ¡ãƒ‡ã‚£ã‚¢ã‚’éš ã™ + */ "respect": string; + /** + * センシティブè¨å®šã•ã‚ŒãŸãƒ¡ãƒ‡ã‚£ã‚¢ã‚’éš ã•ãªã„ + */ "ignore": string; + /** + * 常ã«ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’éš ã™ + */ "force": string; }; "_instanceTicker": { + /** + * 表示ã—ãªã„ + */ "none": string; + /** + * リモートユーザーã«è¡¨ç¤º + */ "remote": string; + /** + * 常ã«è¡¨ç¤º + */ "always": string; }; "_serverDisconnectedBehavior": { + /** + * 自動ã§ãƒªãƒãƒ¼ãƒ‰ + */ "reload": string; + /** + * ダイアãƒã‚°ã§è¦å‘Š + */ "dialog": string; + /** + * 控ãˆã‚ã«è¦å‘Š + */ "quiet": string; + /** + * è¦å‘Šã‚’無効ã«ã™ã‚‹ + */ "disabled": string; }; "_channel": { + /** + * ãƒãƒ£ãƒ³ãƒãƒ«ã‚’ä½œæˆ + */ "create": string; + /** + * ãƒãƒ£ãƒ³ãƒãƒ«ã‚’編集 + */ "edit": string; + /** + * ãƒãƒŠãƒ¼ã‚’è¨å®š + */ "setBanner": string; + /** + * ãƒãƒŠãƒ¼ã‚’削除 + */ "removeBanner": string; + /** + * トレンド + */ "featured": string; + /** + * 管ç†ä¸ + */ "owned": string; + /** + * フォãƒãƒ¼ä¸ + */ "following": string; - "usersCount": string; - "notesCount": string; + /** + * {n}人ãŒå‚åŠ ä¸ + */ + "usersCount": ParameterizedString<"n">; + /** + * {n}投稿ãŒã‚ã‚Šã¾ã™ + */ + "notesCount": ParameterizedString<"n">; + /** + * åå‰ã¨èª¬æ˜Ž + */ "nameAndDescription": string; + /** + * åå‰ã®ã¿ + */ "nameOnly": string; + /** + * ãƒãƒ£ãƒ³ãƒãƒ«å¤–ã¸ã®ãƒ–ーストã¨å¼•ç”¨ãƒ–ーストを許å¯ã™ã‚‹ + */ "allowRenoteToExternal": string; }; "_menuDisplay": { + /** + * 横 + */ "sideFull": string; + /** + * 横(アイコン) + */ "sideIcon": string; + /** + * 上部 + */ "top": string; + /** + * éš ã™ + */ "hide": string; }; "_wordMute": { + /** + * ミュートã™ã‚‹ãƒ¯ãƒ¼ãƒ‰ + */ "muteWords": string; + /** + * スペースã§åŒºåˆ‡ã‚‹ã¨AND指定ã«ãªã‚Šã€æ”¹è¡Œã§åŒºåˆ‡ã‚‹ã¨OR指定ã«ãªã‚Šã¾ã™ã€‚ + */ "muteWordsDescription": string; + /** + * ã‚ーワードをスラッシュã§å›²ã‚€ã¨æ£è¦è¡¨ç¾ã«ãªã‚Šã¾ã™ã€‚ + */ "muteWordsDescription2": string; }; "_instanceMute": { + /** + * ミュートã—ãŸã‚µãƒ¼ãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¸ã®è¿”ä¿¡ã‚’å«ã‚ã¦ã€è¨å®šã—ãŸã‚µãƒ¼ãƒãƒ¼ã®å…¨ã¦ã®ãƒŽãƒ¼ãƒˆã¨ãƒ–ーストをミュートã—ã¾ã™ã€‚ + */ "instanceMuteDescription": string; + /** + * 改行ã§åŒºåˆ‡ã£ã¦è¨å®šã—ã¾ã™ + */ "instanceMuteDescription2": string; + /** + * è¨å®šã—ãŸã‚µãƒ¼ãƒãƒ¼ã®ãƒŽãƒ¼ãƒˆã‚’éš ã—ã¾ã™ã€‚ + */ "title": string; + /** + * ミュートã™ã‚‹ã‚µãƒ¼ãƒãƒ¼ + */ "heading": string; }; "_theme": { + /** + * テーマを探㙠+ */ "explore": string; + /** + * テーマã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ« + */ "install": string; + /** + * テーマã®ç®¡ç† + */ "manage": string; + /** + * テーマコード + */ "code": string; + /** + * 説明 + */ "description": string; - "installed": string; + /** + * {name}をインストールã—ã¾ã—㟠+ */ + "installed": ParameterizedString<"name">; + /** + * インストールã•ã‚ŒãŸãƒ†ãƒ¼ãƒž + */ "installedThemes": string; + /** + * 標準ã®ãƒ†ãƒ¼ãƒž + */ "builtinThemes": string; + /** + * ãã®ãƒ†ãƒ¼ãƒžã¯æ—¢ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã¦ã„ã¾ã™ + */ "alreadyInstalled": string; + /** + * テーマã®å½¢å¼ãŒé–“é•ã£ã¦ã„ã¾ã™ + */ "invalid": string; + /** + * テーマを作る + */ "make": string; + /** + * ベース + */ "base": string; + /** + * å®šæ•°ã‚’è¿½åŠ + */ "addConstant": string; + /** + * 定数 + */ "constant": string; + /** + * デフォルト値 + */ "defaultValue": string; + /** + * 色 + */ "color": string; + /** + * プãƒãƒ‘ティをå‚ç…§ + */ "refProp": string; + /** + * 定数をå‚ç…§ + */ "refConst": string; + /** + * ã‚ー + */ "key": string; + /** + * 関数 + */ "func": string; + /** + * 関数ã®ç¨®é¡ž + */ "funcKind": string; + /** + * 引数 + */ "argument": string; + /** + * å…ƒã«ã™ã‚‹ãƒ—ãƒãƒ‘ティã®åå‰ + */ "basedProp": string; + /** + * ä¸é€æ˜Žåº¦ + */ "alpha": string; + /** + * æš—ã• + */ "darken": string; + /** + * 明る㕠+ */ "lighten": string; + /** + * 定数åを入力ã—ã¦ãã ã•ã„ + */ "inputConstantName": string; + /** + * ã“ã“ã«ãƒ†ãƒ¼ãƒžã‚³ãƒ¼ãƒ‰ã‚’貼り付ã‘ã¦ã€ã‚¨ãƒ‡ã‚£ã‚¿ãƒ¼ã«ã‚¤ãƒ³ãƒãƒ¼ãƒˆã§ãã¾ã™ + */ "importInfo": string; - "deleteConstantConfirm": string; + /** + * 定数 {const} を削除ã—ã¦ã‚‚良ã„ã§ã™ã‹ï¼Ÿ + */ + "deleteConstantConfirm": ParameterizedString<"const">; "keys": { + /** + * アクセント + */ "accent": string; + /** + * 背景 + */ "bg": string; + /** + * æ–‡å— + */ "fg": string; + /** + * フォーカス + */ "focus": string; + /** + * インジケーター + */ "indicator": string; + /** + * パãƒãƒ« + */ "panel": string; + /** + * å½± + */ "shadow": string; + /** + * ヘッダー + */ "header": string; + /** + * サイドãƒãƒ¼ã®èƒŒæ™¯ + */ "navBg": string; + /** + * サイドãƒãƒ¼ã®æ–‡å— + */ "navFg": string; + /** + * サイドãƒãƒ¼æ–‡å—(ホãƒãƒ¼) + */ "navHoverFg": string; + /** + * サイドãƒãƒ¼æ–‡å—(アクティブ) + */ "navActive": string; + /** + * サイドãƒãƒ¼ã®ã‚¤ãƒ³ã‚¸ã‚±ãƒ¼ã‚¿ãƒ¼ + */ "navIndicator": string; + /** + * リンク + */ "link": string; + /** + * ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚° + */ "hashtag": string; + /** + * メンション + */ "mention": string; + /** + * ã‚ãªãŸå®›ã¦ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ + */ "mentionMe": string; + /** + * Boost + */ "renote": string; + /** + * モーダルã®èƒŒæ™¯ + */ "modalBg": string; + /** + * 分割線 + */ "divider": string; + /** + * スクãƒãƒ¼ãƒ«ãƒãƒ¼ã®å–ã£æ‰‹ + */ "scrollbarHandle": string; + /** + * スクãƒãƒ¼ãƒ«ãƒãƒ¼ã®å–ã£æ‰‹ï¼ˆãƒ›ãƒãƒ¼) + */ "scrollbarHandleHover": string; + /** + * 日付ラベルã®æ–‡å— + */ "dateLabelFg": string; + /** + * æƒ…å ±ã®èƒŒæ™¯ + */ "infoBg": string; + /** + * æƒ…å ±ã®æ–‡å— + */ "infoFg": string; + /** + * è¦å‘Šã®èƒŒæ™¯ + */ "infoWarnBg": string; + /** + * è¦å‘Šã®æ–‡å— + */ "infoWarnFg": string; + /** + * 通知トーストã®èƒŒæ™¯ + */ "toastBg": string; + /** + * 通知トーストã®æ–‡å— + */ "toastFg": string; + /** + * ボタンã®èƒŒæ™¯ + */ "buttonBg": string; + /** + * ボタンã®èƒŒæ™¯ (ホãƒãƒ¼) + */ "buttonHoverBg": string; + /** + * 入力ボックスã®ç¸å–ã‚Š + */ "inputBorder": string; + /** + * ãƒªã‚¹ãƒˆé …ç›®ã®èƒŒæ™¯ (ホãƒãƒ¼) + */ "listItemHoverBg": string; + /** + * ドライブフォルダーã®èƒŒæ™¯ + */ "driveFolderBg": string; + /** + * å£ç´™ã®ã‚ªãƒ¼ãƒãƒ¼ãƒ¬ã‚¤ + */ "wallpaperOverlay": string; + /** + * ãƒãƒƒã‚¸ + */ "badge": string; + /** + * ãƒãƒ£ãƒƒãƒˆã®èƒŒæ™¯ + */ "messageBg": string; + /** + * アクセント (æš—ã‚) + */ "accentDarken": string; + /** + * アクセント (明るã‚) + */ "accentLighten": string; + /** + * 強調ã•ã‚ŒãŸæ–‡å— + */ "fgHighlighted": string; }; }; "_sfx": { + /** + * ノート + */ "note": string; + /** + * ノート(自分) + */ "noteMy": string; + /** + * 通知 + */ "notification": string; + /** + * アンテナå—ä¿¡ + */ "antenna": string; + /** + * ãƒãƒ£ãƒ³ãƒãƒ«é€šçŸ¥ + */ "channel": string; + /** + * リアクションé¸æŠžæ™‚ + */ "reaction": string; }; "_soundSettings": { + /** + * ドライブã®éŸ³å£°ã‚’使用 + */ "driveFile": string; + /** + * ドライブã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã—ã¦ãã ã•ã„ + */ "driveFileWarn": string; + /** + * ã“ã®ãƒ•ã‚¡ã‚¤ãƒ«ã¯å¯¾å¿œã—ã¦ã„ã¾ã›ã‚“ + */ "driveFileTypeWarn": string; + /** + * 音声ファイルをé¸æŠžã—ã¦ãã ã•ã„ + */ "driveFileTypeWarnDescription": string; + /** + * 音声ãŒé•·ã™ãŽã¾ã™ + */ "driveFileDurationWarn": string; + /** + * é•·ã„音声を使用ã™ã‚‹ã¨Misskeyã®ä½¿ç”¨ã«æ”¯éšœã‚’ããŸã™å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚ãã‚Œã§ã‚‚続行ã—ã¾ã™ã‹ï¼Ÿ + */ "driveFileDurationWarnDescription": string; }; "_ago": { + /** + * æœªæ¥ + */ "future": string; + /** + * ãŸã£ãŸä»Š + */ "justNow": string; - "secondsAgo": string; - "minutesAgo": string; - "hoursAgo": string; - "daysAgo": string; - "weeksAgo": string; - "monthsAgo": string; - "yearsAgo": string; + /** + * {n}ç§’å‰ + */ + "secondsAgo": ParameterizedString<"n">; + /** + * {n}åˆ†å‰ + */ + "minutesAgo": ParameterizedString<"n">; + /** + * {n}æ™‚é–“å‰ + */ + "hoursAgo": ParameterizedString<"n">; + /** + * {n}æ—¥å‰ + */ + "daysAgo": ParameterizedString<"n">; + /** + * {n}é€±é–“å‰ + */ + "weeksAgo": ParameterizedString<"n">; + /** + * {n}ãƒ¶æœˆå‰ + */ + "monthsAgo": ParameterizedString<"n">; + /** + * {n}å¹´å‰ + */ + "yearsAgo": ParameterizedString<"n">; + /** + * 日時ã®è§£æžã«å¤±æ•— + */ "invalid": string; }; "_timeIn": { - "seconds": string; - "minutes": string; - "hours": string; - "days": string; - "weeks": string; - "months": string; - "years": string; + /** + * {n}秒後 + */ + "seconds": ParameterizedString<"n">; + /** + * {n}分後 + */ + "minutes": ParameterizedString<"n">; + /** + * {n}時間後 + */ + "hours": ParameterizedString<"n">; + /** + * {n}日後 + */ + "days": ParameterizedString<"n">; + /** + * {n}週間後 + */ + "weeks": ParameterizedString<"n">; + /** + * {n}ヶ月後 + */ + "months": ParameterizedString<"n">; + /** + * {n}年後 + */ + "years": ParameterizedString<"n">; }; "_time": { + /** + * 秒 + */ "second": string; + /** + * 分 + */ "minute": string; + /** + * 時間 + */ "hour": string; + /** + * æ—¥ + */ "day": string; }; "_2fa": { + /** + * æ—¢ã«è¨å®šã¯å®Œäº†ã—ã¦ã„ã¾ã™ã€‚ + */ "alreadyRegistered": string; + /** + * èªè¨¼ã‚¢ãƒ—リã®è¨å®šã‚’開始 + */ "registerTOTP": string; - "step1": string; + /** + * ã¾ãšã€{a}ã‚„{b}ãªã©ã®èªè¨¼ã‚¢ãƒ—リをãŠä½¿ã„ã®ãƒ‡ãƒã‚¤ã‚¹ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ã¾ã™ã€‚ + */ + "step1": ParameterizedString<"a" | "b">; + /** + * 次ã«ã€è¡¨ç¤ºã•ã‚Œã¦ã„ã‚‹QRコードをアプリã§ã‚¹ã‚ャンã—ã¾ã™ã€‚ + */ "step2": string; + /** + * QRコードをクリックã™ã‚‹ã¨ã€ãŠä½¿ã„ã®ç«¯æœ«ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã¦ã„ã‚‹èªè¨¼ã‚¢ãƒ—リやã‚ーリングã«ç™»éŒ²ã§ãã¾ã™ã€‚ + */ "step2Click": string; + /** + * デスクトップアプリを使用ã™ã‚‹å ´åˆã¯æ¬¡ã®URIを入力ã—ã¾ã™ + */ "step2Uri": string; + /** + * 確èªã‚³ãƒ¼ãƒ‰ã‚’入力 + */ "step3Title": string; + /** + * アプリã«è¡¨ç¤ºã•ã‚Œã¦ã„る確èªã‚³ãƒ¼ãƒ‰ï¼ˆãƒˆãƒ¼ã‚¯ãƒ³ï¼‰ã‚’入力ã—ã¾ã™ã€‚ + */ "step3": string; + /** + * è¨å®šãŒå®Œäº†ã—ã¾ã—㟠+ */ "setupCompleted": string; + /** + * ã“ã‚Œã‹ã‚‰ãƒã‚°ã‚¤ãƒ³ã™ã‚‹ã¨ãã‚‚ã€åŒã˜ã‚ˆã†ã«ã‚³ãƒ¼ãƒ‰ã‚’入力ã—ã¾ã™ã€‚ + */ "step4": string; + /** + * ãŠä½¿ã„ã®ãƒ–ラウザã¯ã‚»ã‚ュリティã‚ーã«å¯¾å¿œã—ã¦ã„ã¾ã›ã‚“。 + */ "securityKeyNotSupported": string; + /** + * ã‚»ã‚ュリティã‚ー・パスã‚ーを登録ã™ã‚‹ã«ã¯ã€ã¾ãšèªè¨¼ã‚¢ãƒ—リã®è¨å®šã‚’è¡Œãªã£ã¦ãã ã•ã„。 + */ "registerTOTPBeforeKey": string; + /** + * FIDO2をサãƒãƒ¼ãƒˆã™ã‚‹ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã‚»ã‚ュリティã‚ーã€ç«¯æœ«ã®ç”Ÿä½“èªè¨¼ã‚„PINãƒãƒƒã‚¯ã€ãƒ‘スã‚ーã¨ã„ã£ãŸã€WebAuthnç”±æ¥ã®éµã‚’登録ã—ã¾ã™ã€‚ + */ "securityKeyInfo": string; + /** + * ã‚»ã‚ュリティã‚ー・パスã‚ーを登録ã™ã‚‹ + */ "registerSecurityKey": string; + /** + * ã‚ーã®åå‰ã‚’入力 + */ "securityKeyName": string; + /** + * ブラウザã®æŒ‡ç¤ºã«å¾“ã„ã€ã‚»ã‚ュリティã‚ーやパスã‚ーを登録ã—ã¦ãã ã•ã„ + */ "tapSecurityKey": string; + /** + * ã‚»ã‚ュリティã‚ーを削除 + */ "removeKey": string; - "removeKeyConfirm": string; + /** + * {name}を削除ã—ã¾ã™ã‹ï¼Ÿ + */ + "removeKeyConfirm": ParameterizedString<"name">; + /** + * ã‚»ã‚ュリティã‚ーãŒç™»éŒ²ã•ã‚Œã¦ã„ã‚‹å ´åˆã€èªè¨¼ã‚¢ãƒ—リã®è¨å®šã¯è§£é™¤ã§ãã¾ã›ã‚“。 + */ "whyTOTPOnlyRenew": string; + /** + * èªè¨¼ã‚¢ãƒ—リをå†è¨å®š + */ "renewTOTP": string; + /** + * 今ã¾ã§ã®èªè¨¼ã‚¢ãƒ—リã®ç¢ºèªã‚³ãƒ¼ãƒ‰ãŠã‚ˆã³ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—コードã¯ä½¿ç”¨ã§ããªããªã‚Šã¾ã™ + */ "renewTOTPConfirm": string; + /** + * å†è¨å®šã™ã‚‹ + */ "renewTOTPOk": string; + /** + * ã‚„ã‚ã¦ãŠã + */ "renewTOTPCancel": string; + /** + * ã“ã®ã‚¦ã‚£ã‚¶ãƒ¼ãƒ‰ã‚’é–‰ã˜ã‚‹å‰ã«ã€ä»¥ä¸‹ã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—コードを確èªã—ã¦ãã ã•ã„。 + */ "checkBackupCodesBeforeCloseThisWizard": string; + /** + * ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—コード + */ "backupCodes": string; + /** + * èªè¨¼ã‚¢ãƒ—リãŒä½¿ç”¨ã§ããªããªã£ãŸå ´åˆã€ä»¥ä¸‹ã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—コードを使ã£ã¦ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã™ã€‚ã“れらã®ã‚³ãƒ¼ãƒ‰ã¯å¿…ãšå®‰å…¨ãªå ´æ‰€ã«ä¿ç®¡ã—ã¦ãã ã•ã„。å„コードã¯ä¸€å›žã ã‘使用ã§ãã¾ã™ã€‚ + */ "backupCodesDescription": string; + /** + * ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—コードãŒä½¿ç”¨ã•ã‚Œã¾ã—ãŸã€‚èªè¨¼ã‚¢ãƒ—リãŒä½¿ãˆãªããªã£ã¦ã„ã‚‹å ´åˆã€ãªã‚‹ã¹ãæ—©ãèªè¨¼ã‚¢ãƒ—リをå†è¨å®šã—ã¦ãã ã•ã„。 + */ "backupCodeUsedWarning": string; + /** + * ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—コードãŒå…¨ã¦ä½¿ç”¨ã•ã‚Œã¾ã—ãŸã€‚èªè¨¼ã‚¢ãƒ—リを利用ã§ããªã„å ´åˆã€ã“れ以上アカウントã«ã‚¢ã‚¯ã‚»ã‚¹ã§ããªããªã‚Šã¾ã™ã€‚èªè¨¼ã‚¢ãƒ—リをå†ç™»éŒ²ã—ã¦ãã ã•ã„。 + */ "backupCodesExhaustedWarning": string; }; "_permissions": { + /** + * アカウントã®æƒ…å ±ã‚’è¦‹ã‚‹ + */ "read:account": string; + /** + * アカウントã®æƒ…å ±ã‚’å¤‰æ›´ã™ã‚‹ + */ "write:account": string; + /** + * ブãƒãƒƒã‚¯ã‚’見る + */ "read:blocks": string; + /** + * ブãƒãƒƒã‚¯ã‚’æ“作ã™ã‚‹ + */ "write:blocks": string; + /** + * ドライブを見る + */ "read:drive": string; + /** + * ドライブをæ“作ã™ã‚‹ + */ "write:drive": string; + /** + * ãŠæ°—ã«å…¥ã‚Šã‚’見る + */ "read:favorites": string; + /** + * ãŠæ°—ã«å…¥ã‚Šã‚’æ“作ã™ã‚‹ + */ "write:favorites": string; + /** + * フォãƒãƒ¼ã®æƒ…å ±ã‚’è¦‹ã‚‹ + */ "read:following": string; + /** + * フォãƒãƒ¼ãƒ»ãƒ•ã‚©ãƒãƒ¼è§£é™¤ã™ã‚‹ + */ "write:following": string; + /** + * ãƒãƒ£ãƒƒãƒˆã‚’見る + */ "read:messaging": string; + /** + * ãƒãƒ£ãƒƒãƒˆã‚’æ“作ã™ã‚‹ + */ "write:messaging": string; + /** + * ミュートを見る + */ "read:mutes": string; + /** + * ミュートをæ“作ã™ã‚‹ + */ "write:mutes": string; + /** + * ノートを作æˆãƒ»å‰Šé™¤ã™ã‚‹ + */ "write:notes": string; + /** + * 通知を見る + */ "read:notifications": string; + /** + * 通知をæ“作ã™ã‚‹ + */ "write:notifications": string; + /** + * リアクションを見る + */ "read:reactions": string; + /** + * リアクションをæ“作ã™ã‚‹ + */ "write:reactions": string; + /** + * 投票ã™ã‚‹ + */ "write:votes": string; + /** + * ページを見る + */ "read:pages": string; + /** + * ページをæ“作ã™ã‚‹ + */ "write:pages": string; + /** + * ページã®ã„ã„ãを見る + */ "read:page-likes": string; + /** + * ページã®ã„ã„ãã‚’æ“作ã™ã‚‹ + */ "write:page-likes": string; + /** + * ユーザーグループを見る + */ "read:user-groups": string; + /** + * ユーザーグループをæ“作ã™ã‚‹ + */ "write:user-groups": string; + /** + * ãƒãƒ£ãƒ³ãƒãƒ«ã‚’見る + */ "read:channels": string; + /** + * ãƒãƒ£ãƒ³ãƒãƒ«ã‚’æ“作ã™ã‚‹ + */ "write:channels": string; + /** + * ギャラリーを見る + */ "read:gallery": string; + /** + * ギャラリーをæ“作ã™ã‚‹ + */ "write:gallery": string; + /** + * ギャラリーã®ã„ã„ãを見る + */ "read:gallery-likes": string; + /** + * ギャラリーã®ã„ã„ãã‚’æ“作ã™ã‚‹ + */ "write:gallery-likes": string; + /** + * Playを見る + */ "read:flash": string; + /** + * Playã‚’æ“作ã™ã‚‹ + */ "write:flash": string; + /** + * Playã®ã„ã„ãを見る + */ "read:flash-likes": string; + /** + * Playã®ã„ã„ãã‚’æ“作ã™ã‚‹ + */ "write:flash-likes": string; + /** + * ユーザーã‹ã‚‰ã®é€šå ±ã‚’見る + */ "read:admin:abuse-user-reports": string; + /** + * ユーザーアカウントを削除ã™ã‚‹ + */ "write:admin:delete-account": string; + /** + * ユーザーã®ã™ã¹ã¦ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’削除ã™ã‚‹ + */ "write:admin:delete-all-files-of-a-user": string; + /** + * データベースインデックスã«é–¢ã™ã‚‹æƒ…å ±ã‚’è¦‹ã‚‹ + */ "read:admin:index-stats": string; + /** + * データベーステーブルã«é–¢ã™ã‚‹æƒ…å ±ã‚’è¦‹ã‚‹ + */ "read:admin:table-stats": string; + /** + * ユーザーã®IPアドレスを見る + */ "read:admin:user-ips": string; + /** + * インスタンスã®ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã‚’見る + */ "read:admin:meta": string; + /** + * ユーザーã®ãƒ‘スワードをリセットã™ã‚‹ + */ "write:admin:reset-password": string; + /** + * ユーザーã‹ã‚‰ã®é€šå ±ã‚’解決ã™ã‚‹ + */ "write:admin:resolve-abuse-user-report": string; + /** + * メールをé€ã‚‹ + */ "write:admin:send-email": string; + /** + * サーãƒãƒ¼ã®æƒ…å ±ã‚’è¦‹ã‚‹ + */ "read:admin:server-info": string; + /** + * モデレーションãƒã‚°ã‚’見る + */ "read:admin:show-moderation-log": string; + /** + * ユーザーã®ãƒ—ライベートãªæƒ…å ±ã‚’è¦‹ã‚‹ + */ "read:admin:show-user": string; + /** + * ユーザーã®ãƒ—ライベートãªæƒ…å ±ã‚’è¦‹ã‚‹ + */ "read:admin:show-users": string; + /** + * ユーザーをå‡çµã™ã‚‹ + */ "write:admin:suspend-user": string; + /** + * ユーザーã®ã‚¢ãƒã‚¿ãƒ¼ã‚’削除ã™ã‚‹ + */ "write:admin:unset-user-avatar": string; + /** + * ユーザーã®ãƒãƒ¼ãƒŠãƒ¼ã‚’削除ã™ã‚‹ + */ "write:admin:unset-user-banner": string; + /** + * ユーザーã®å‡çµã‚’解除ã™ã‚‹ + */ "write:admin:unsuspend-user": string; + /** + * インスタンスã®ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã‚’æ“作ã™ã‚‹ + */ "write:admin:meta": string; + /** + * モデレーションノートをæ“作ã™ã‚‹ + */ "write:admin:user-note": string; + /** + * ãƒãƒ¼ãƒ«ã‚’æ“作ã™ã‚‹ + */ "write:admin:roles": string; + /** + * ãƒãƒ¼ãƒ«ã‚’見る + */ "read:admin:roles": string; + /** + * リレーをæ“作ã™ã‚‹ + */ "write:admin:relays": string; + /** + * リレーを見る + */ "read:admin:relays": string; + /** + * 招待コードをæ“作ã™ã‚‹ + */ "write:admin:invite-codes": string; + /** + * 招待コードを見る + */ "read:admin:invite-codes": string; + /** + * ãŠçŸ¥ã‚‰ã›ã‚’æ“作ã™ã‚‹ + */ "write:admin:announcements": string; + /** + * ãŠçŸ¥ã‚‰ã›ã‚’見る + */ "read:admin:announcements": string; + /** + * ã‚¢ãƒã‚¿ãƒ¼ãƒ‡ã‚³ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã‚’æ“作ã™ã‚‹ + */ "write:admin:avatar-decorations": string; + /** + * ã‚¢ãƒã‚¿ãƒ¼ãƒ‡ã‚³ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã‚’見る + */ "read:admin:avatar-decorations": string; + /** + * 連åˆã«é–¢ã™ã‚‹æƒ…å ±ã‚’æ“作ã™ã‚‹ + */ "write:admin:federation": string; + /** + * ユーザーアカウントをæ“作ã™ã‚‹ + */ "write:admin:account": string; + /** + * ユーザーã«é–¢ã™ã‚‹æƒ…å ±ã‚’è¦‹ã‚‹ + */ "read:admin:account": string; + /** + * 絵文å—ã‚’æ“作ã™ã‚‹ + */ "write:admin:emoji": string; + /** + * 絵文å—を見る + */ "read:admin:emoji": string; + /** + * ジョブã‚ューをæ“作ã™ã‚‹ + */ "write:admin:queue": string; + /** + * ジョブã‚ューã«é–¢ã™ã‚‹æƒ…å ±ã‚’è¦‹ã‚‹ + */ "read:admin:queue": string; + /** + * プãƒãƒ¢ãƒ¼ã‚·ãƒ§ãƒ³ãƒŽãƒ¼ãƒˆã‚’æ“作ã™ã‚‹ + */ "write:admin:promo": string; + /** + * ユーザーã®ãƒ‰ãƒ©ã‚¤ãƒ–ã‚’æ“作ã™ã‚‹ + */ "write:admin:drive": string; + /** + * ユーザーã®ãƒ‰ãƒ©ã‚¤ãƒ–ã®é–¢ã™ã‚‹æƒ…å ±ã‚’è¦‹ã‚‹ + */ "read:admin:drive": string; + /** + * 管ç†è€…用ã®Websocket APIを使ㆠ+ */ "read:admin:stream": string; + /** + * 広告をæ“作ã™ã‚‹ + */ "write:admin:ad": string; + /** + * 広告を見る + */ "read:admin:ad": string; + /** + * 招待コードを作æˆã™ã‚‹ + */ "write:invite-codes": string; + /** + * 招待コードをå–å¾—ã™ã‚‹ + */ "read:invite-codes": string; + /** + * クリップã®ã„ã„ãã‚’æ“作ã™ã‚‹ + */ "write:clip-favorite": string; + /** + * クリップã®ã„ã„ãを見る + */ "read:clip-favorite": string; + /** + * 連åˆã«é–¢ã™ã‚‹æƒ…å ±ã‚’å–å¾—ã™ã‚‹ + */ "read:federation": string; + /** + * é•åã‚’å ±å‘Šã™ã‚‹ + */ "write:report-abuse": string; }; "_auth": { + /** + * アプリã¸ã®ã‚¢ã‚¯ã‚»ã‚¹è¨±å¯ + */ "shareAccessTitle": string; - "shareAccess": string; + /** + * 「{name}ã€ãŒã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã“ã¨ã‚’許å¯ã—ã¾ã™ã‹ï¼Ÿ + */ + "shareAccess": ParameterizedString<"name">; + /** + * アカウントã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’許å¯ã—ã¾ã™ã‹ï¼Ÿ + */ "shareAccessAsk": string; - "permission": string; + /** + * {name}ã¯æ¬¡ã®æ¨©é™ã‚’è¦æ±‚ã—ã¦ã„ã¾ã™ + */ + "permission": ParameterizedString<"name">; + /** + * ã“ã®ã‚¢ãƒ—リã¯æ¬¡ã®æ¨©é™ã‚’è¦æ±‚ã—ã¦ã„ã¾ã™ + */ "permissionAsk": string; + /** + * アプリケーションã«æˆ»ã£ã¦ã‚„ã£ã¦ã„ã£ã¦ãã ã•ã„ + */ "pleaseGoBack": string; + /** + * アプリケーションã«æˆ»ã£ã¦ã„ã¾ã™ + */ "callback": string; + /** + * アクセスを拒å¦ã—ã¾ã—㟠+ */ "denied": string; + /** + * アプリケーションã«ã‚¢ã‚¯ã‚»ã‚¹è¨±å¯ã‚’与ãˆã‚‹ã«ã¯ã€ãƒã‚°ã‚¤ãƒ³ãŒå¿…è¦ã§ã™ã€‚ + */ "pleaseLogin": string; }; "_antennaSources": { + /** + * å…¨ã¦ã®ãƒŽãƒ¼ãƒˆ + */ "all": string; + /** + * フォãƒãƒ¼ã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆ + */ "homeTimeline": string; + /** + * 指定ã—ãŸä¸€äººã¾ãŸã¯è¤‡æ•°ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆ + */ "users": string; + /** + * 指定ã—ãŸãƒªã‚¹ãƒˆã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆ + */ "userList": string; + /** + * 指定ã—ãŸä¸€äººã¾ãŸã¯è¤‡æ•°ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’除ã„ãŸå…¨ã¦ã®ãƒŽãƒ¼ãƒˆ + */ "userBlacklist": string; }; "_weekday": { + /** + * 日曜日 + */ "sunday": string; + /** + * 月曜日 + */ "monday": string; + /** + * ç«æ›œæ—¥ + */ "tuesday": string; + /** + * 水曜日 + */ "wednesday": string; + /** + * 木曜日 + */ "thursday": string; + /** + * 金曜日 + */ "friday": string; + /** + * 土曜日 + */ "saturday": string; }; "_widgets": { + /** + * プãƒãƒ•ã‚£ãƒ¼ãƒ« + */ "profile": string; + /** + * サーãƒãƒ¼æƒ…å ± + */ "instanceInfo": string; + /** + * 付箋 + */ "memo": string; + /** + * 通知 + */ "notifications": string; + /** + * タイムライン + */ "timeline": string; + /** + * カレンダー + */ "calendar": string; + /** + * トレンド + */ "trends": string; + /** + * 時計 + */ "clock": string; + /** + * RSSリーダー + */ "rss": string; + /** + * RSSティッカー + */ "rssTicker": string; + /** + * アクティビティ + */ "activity": string; + /** + * フォト + */ "photos": string; + /** + * デジタル時計 + */ "digitalClock": string; + /** + * UNIX時計 + */ "unixClock": string; + /** + * é€£åˆ + */ "federation": string; + /** + * サーãƒãƒ¼ã‚¯ãƒ©ã‚¦ãƒ‰ + */ "instanceCloud": string; + /** + * 投稿フォーム+ */ "postForm": string; + /** + * スライドショー + */ "slideshow": string; + /** + * ボタン + */ "button": string; + /** + * オンラインユーザー + */ "onlineUsers": string; + /** + * ジョブã‚ュー + */ "jobQueue": string; + /** + * サーãƒãƒ¼ãƒ¡ãƒˆãƒªã‚¯ã‚¹ + */ "serverMetric": string; + /** + * AiScriptコンソール + */ "aiscript": string; + /** + * AiScript App + */ "aiscriptApp": string; + /** + * è— + */ "aichan": string; + /** + * ユーザーリスト + */ "userList": string; "_userList": { + /** + * リストをé¸æŠž + */ "chooseList": string; }; + /** + * クリッカー + */ "clicker": string; + /** + * 検索 + */ "search": string; + /** + * 今日誕生日ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ + */ "birthdayFollowings": string; }; "_cw": { + /** + * éš ã™ + */ "hide": string; + /** + * ã‚‚ã£ã¨è¦‹ã‚‹ + */ "show": string; - "chars": string; - "files": string; + /** + * {count}æ–‡å— + */ + "chars": ParameterizedString<"count">; + /** + * {count}ファイル + */ + "files": ParameterizedString<"count">; }; "_poll": { + /** + * é¸æŠžè‚¢ã¯æœ€ä½Ž2ã¤å¿…è¦ã§ã™ + */ "noOnlyOneChoice": string; - "choiceN": string; + /** + * é¸æŠžè‚¢{n} + */ + "choiceN": ParameterizedString<"n">; + /** + * ã“ã‚Œä»¥ä¸Šè¿½åŠ ã§ãã¾ã›ã‚“ + */ "noMore": string; + /** + * 複数回ç”å¯ + */ "canMultipleVote": string; + /** + * æœŸé™ + */ "expiration": string; + /** + * ç„¡æœŸé™ + */ "infinite": string; + /** + * 日時指定 + */ "at": string; + /** + * 経éŽæŒ‡å®š + */ "after": string; + /** + * 期日 + */ "deadlineDate": string; + /** + * 時間 + */ "deadlineTime": string; + /** + * 期間 + */ "duration": string; - "votesCount": string; - "totalVotes": string; + /** + * {n}票 + */ + "votesCount": ParameterizedString<"n">; + /** + * 計{n}票 + */ + "totalVotes": ParameterizedString<"n">; + /** + * 投票ã™ã‚‹ + */ "vote": string; + /** + * çµæžœã‚’見る + */ "showResult": string; + /** + * 投票済㿠+ */ "voted": string; + /** + * 終了済㿠+ */ "closed": string; - "remainingDays": string; - "remainingHours": string; - "remainingMinutes": string; - "remainingSeconds": string; + /** + * 終了ã¾ã§ã‚ã¨{d}æ—¥{h}時間 + */ + "remainingDays": ParameterizedString<"d" | "h">; + /** + * 終了ã¾ã§ã‚ã¨{h}時間{m}分 + */ + "remainingHours": ParameterizedString<"h" | "m">; + /** + * 終了ã¾ã§ã‚ã¨{m}分{s}秒 + */ + "remainingMinutes": ParameterizedString<"m" | "s">; + /** + * 終了ã¾ã§ã‚ã¨{s}秒 + */ + "remainingSeconds": ParameterizedString<"s">; + /** + * 複数ã®é¸æŠžè‚¢ + */ "multiple": string; }; "_visibility": { + /** + * パブリック + */ "public": string; + /** + * å…¨ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«å…¬é–‹ + */ "publicDescription": string; + /** + * ホーム+ */ "home": string; + /** + * ホームタイムラインã®ã¿ã«å…¬é–‹ + */ "homeDescription": string; + /** + * フォãƒãƒ¯ãƒ¼ + */ "followers": string; + /** + * 自分ã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã®ã¿ã«å…¬é–‹ + */ "followersDescription": string; + /** + * ダイレクト + */ "specified": string; + /** + * 指定ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã¿ã«å…¬é–‹ + */ "specifiedDescription": string; + /** + * 連åˆãªã— + */ "disableFederation": string; + /** + * 他サーãƒãƒ¼ã¸ã®é…ä¿¡ã‚’è¡Œã„ã¾ã›ã‚“ + */ "disableFederationDescription": string; }; "_postForm": { + /** + * ã“ã®ãƒŽãƒ¼ãƒˆã«è¿”ä¿¡... + */ "replyPlaceholder": string; + /** + * ã“ã®ãƒŽãƒ¼ãƒˆã‚’引用... + */ "quotePlaceholder": string; + /** + * ãƒãƒ£ãƒ³ãƒãƒ«ã«æŠ•ç¨¿... + */ "channelPlaceholder": string; "_placeholders": { + /** + * ã„ã¾ã©ã†ã—ã¦ã‚‹ï¼Ÿ + */ "a": string; + /** + * 何ã‹ã‚ã‚Šã¾ã—ãŸã‹ï¼Ÿ + */ "b": string; + /** + * 何をãŠè€ƒãˆã§ã™ã‹ï¼Ÿ + */ "c": string; + /** + * 言ã„ãŸã„ã“ã¨ã¯ï¼Ÿ + */ "d": string; + /** + * ã“ã“ã«æ›¸ã„ã¦ãã ã•ã„ + */ "e": string; + /** + * ã‚ãªãŸãŒæ›¸ãã®ã‚’å¾…ã£ã¦ã„ã¾ã™... + */ "f": string; }; }; "_profile": { + /** + * åå‰ + */ "name": string; + /** + * ユーザーå + */ "username": string; + /** + * 自己紹介 + */ "description": string; + /** + * ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ã‚’å«ã‚ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + */ "youCanIncludeHashtags": string; + /** + * è¿½åŠ æƒ…å ± + */ "metadata": string; + /** + * è¿½åŠ æƒ…å ±ã‚’ç·¨é›† + */ "metadataEdit": string; + /** + * プãƒãƒ•ã‚£ãƒ¼ãƒ«ã«è¡¨ã¨ã—ã¦è¿½åŠ æƒ…å ±ã‚’è¡¨ç¤ºã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + */ "metadataDescription": string; + /** + * ラベル + */ "metadataLabel": string; + /** + * 内容 + */ "metadataContent": string; + /** + * アイコン画åƒã‚’変更 + */ "changeAvatar": string; + /** + * ãƒãƒŠãƒ¼ç”»åƒã‚’変更 + */ "changeBanner": string; + /** + * æ›´æ–°ãƒãƒŠãƒ¼ + */ + "updateBanner": string; + /** + * ãƒãƒŠãƒ¼ã‚’削除 + */ + "removeBanner": string; + /** + * 背景を変更ã™ã‚‹ + */ "changeBackground": string; + /** + * 背景を更新ã™ã‚‹ + */ + "updateBackground": string; + /** + * 背景を削除ã™ã‚‹ + */ + "removeBackground": string; + /** + * 内容ã«URLã‚’è¨å®šã™ã‚‹ã¨ã€ãƒªãƒ³ã‚¯å…ˆã®Webサイトã«è‡ªåˆ†ã®ãƒ—ãƒãƒ•ã‚£ãƒ¼ãƒ«ã¸ã®ãƒªãƒ³ã‚¯ãŒå«ã¾ã‚Œã¦ã„ã‚‹å ´åˆã«æ‰€æœ‰è€…確èªæ¸ˆã¿ã‚¢ã‚¤ã‚³ãƒ³ã‚’表示ã•ã›ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + */ "verifiedLinkDescription": string; - "avatarDecorationMax": string; + /** + * 最大{max}ã¤ã¾ã§ãƒ‡ã‚³ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã‚’付ã‘られã¾ã™ã€‚ + */ + "avatarDecorationMax": ParameterizedString<"max">; }; "_exportOrImport": { + /** + * å…¨ã¦ã®ãƒŽãƒ¼ãƒˆ + */ "allNotes": string; + /** + * ãŠæ°—ã«å…¥ã‚Šã«ã—ãŸãƒŽãƒ¼ãƒˆ + */ "favoritedNotes": string; + /** + * クリップ + */ + "clips": string; + /** + * フォãƒãƒ¼ + */ "followingList": string; + /** + * ミュート + */ "muteList": string; + /** + * ブãƒãƒƒã‚¯ + */ "blockingList": string; + /** + * リスト + */ "userLists": string; + /** + * ミュートã—ã¦ã„るユーザーを除外 + */ "excludeMutingUsers": string; + /** + * 使ã‚ã‚Œã¦ã„ãªã„アカウントを除外 + */ "excludeInactiveUsers": string; + /** + * インãƒãƒ¼ãƒˆã—ãŸäººã«ã‚ˆã‚‹è¿”ä¿¡ã‚’TLã«å«ã‚€ã‚ˆã†ã«ã™ã‚‹ + */ "withReplies": string; }; "_charts": { + /** + * é€£åˆ + */ "federation": string; + /** + * リクエスト + */ "apRequest": string; + /** + * ユーザーã®å¢—減 + */ "usersIncDec": string; + /** + * ユーザーã®åˆè¨ˆ + */ "usersTotal": string; + /** + * アクティブユーザー数 + */ "activeUsers": string; + /** + * ノートã®å¢—減 + */ "notesIncDec": string; + /** + * ãƒãƒ¼ã‚«ãƒ«ã®ãƒŽãƒ¼ãƒˆã®å¢—減 + */ "localNotesIncDec": string; + /** + * リモートã®ãƒŽãƒ¼ãƒˆã®å¢—減 + */ "remoteNotesIncDec": string; + /** + * ノートã®åˆè¨ˆ + */ "notesTotal": string; + /** + * ファイルã®å¢—減 + */ "filesIncDec": string; + /** + * ファイルã®åˆè¨ˆ + */ "filesTotal": string; + /** + * ストレージ使用é‡ã®å¢—減 + */ "storageUsageIncDec": string; + /** + * ストレージ使用é‡ã®åˆè¨ˆ + */ "storageUsageTotal": string; }; "_instanceCharts": { + /** + * リクエスト + */ "requests": string; + /** + * ユーザーã®å¢—減 + */ "users": string; + /** + * ユーザーã®ç´¯ç© + */ "usersTotal": string; + /** + * ノートã®å¢—減 + */ "notes": string; + /** + * ノートã®ç´¯ç© + */ "notesTotal": string; + /** + * フォãƒãƒ¼/フォãƒãƒ¯ãƒ¼ã®å¢—減 + */ "ff": string; + /** + * フォãƒãƒ¼/フォãƒãƒ¯ãƒ¼ã®ç´¯ç© + */ "ffTotal": string; + /** + * ã‚ャッシュサイズã®å¢—減 + */ "cacheSize": string; + /** + * ã‚ャッシュサイズã®ç´¯ç© + */ "cacheSizeTotal": string; + /** + * ファイル数ã®å¢—減 + */ "files": string; + /** + * ファイル数ã®ç´¯ç© + */ "filesTotal": string; }; "_timelines": { + /** + * ホーム+ */ "home": string; + /** + * ãƒãƒ¼ã‚«ãƒ« + */ "local": string; + /** + * ソーシャル + */ "social": string; + /** + * ã‚°ãƒãƒ¼ãƒãƒ« + */ "global": string; }; "_play": { + /** + * Playã®ä½œæˆ + */ "new": string; + /** + * Playã®ç·¨é›† + */ "edit": string; + /** + * Playを作æˆã—ã¾ã—㟠+ */ "created": string; + /** + * Playã‚’æ›´æ–°ã—ã¾ã—㟠+ */ "updated": string; + /** + * Playを削除ã—ã¾ã—㟠+ */ "deleted": string; + /** + * Playè¨å®š + */ "pageSetting": string; + /** + * ã“ã®Playを編集 + */ "editThisPage": string; + /** + * ソースを表示 + */ "viewSource": string; + /** + * 自分ã®Play + */ "my": string; + /** + * ã„ã„ãã—ãŸPlay + */ "liked": string; + /** + * 人気 + */ "featured": string; + /** + * タイトル + */ "title": string; + /** + * スクリプト + */ "script": string; + /** + * 説明 + */ "summary": string; }; "_pages": { + /** + * ページã®ä½œæˆ + */ "newPage": string; + /** + * ページã®ç·¨é›† + */ "editPage": string; + /** + * ã‚½ãƒ¼ã‚¹ã‚’è¡¨ç¤ºä¸ + */ "readPage": string; + /** + * ページを作æˆã—ã¾ã—㟠+ */ "created": string; + /** + * ページを更新ã—ã¾ã—㟠+ */ "updated": string; + /** + * ページを削除ã—ã¾ã—㟠+ */ "deleted": string; + /** + * ページè¨å®š + */ "pageSetting": string; + /** + * 指定ã•ã‚ŒãŸãƒšãƒ¼ã‚¸URLã¯æ—¢ã«å˜åœ¨ã—ã¦ã„ã¾ã™ + */ "nameAlreadyExists": string; + /** + * ä¸æ£ãªãƒšãƒ¼ã‚¸URLã§ã™ + */ "invalidNameTitle": string; + /** + * 空白ã§ãªã„ã‹ç¢ºèªã—ã¦ãã ã•ã„ + */ "invalidNameText": string; + /** + * ã“ã®ãƒšãƒ¼ã‚¸ã‚’編集 + */ "editThisPage": string; + /** + * ソースを表示 + */ "viewSource": string; + /** + * ページを見る + */ "viewPage": string; + /** + * ã„ã„ã + */ "like": string; + /** + * ã„ã„ã解除 + */ "unlike": string; + /** + * 自分ã®ãƒšãƒ¼ã‚¸ + */ "my": string; + /** + * ã„ã„ãã—ãŸãƒšãƒ¼ã‚¸ + */ "liked": string; + /** + * 人気 + */ "featured": string; + /** + * インスペクター + */ "inspector": string; + /** + * コンテンツ + */ "contents": string; + /** + * ページブãƒãƒƒã‚¯ + */ "content": string; + /** + * 変数 + */ "variables": string; + /** + * タイトル + */ "title": string; + /** + * ページURL + */ "url": string; + /** + * ページã®è¦ç´„ + */ "summary": string; + /** + * ä¸å¤®å¯„ã› + */ "alignCenter": string; + /** + * ピン留ã‚ã•ã‚Œã¦ã„ã‚‹ã¨ãã«ã‚¿ã‚¤ãƒˆãƒ«ã‚’éžè¡¨ç¤º + */ "hideTitleWhenPinned": string; + /** + * フォント + */ "font": string; + /** + * セリフ + */ "fontSerif": string; + /** + * サンセリフ + */ "fontSansSerif": string; + /** + * アイã‚ャッãƒç”»åƒã‚’è¨å®š + */ "eyeCatchingImageSet": string; + /** + * アイã‚ャッãƒç”»åƒã‚’削除 + */ "eyeCatchingImageRemove": string; + /** + * ブãƒãƒƒã‚¯ã‚’è¿½åŠ + */ "chooseBlock": string; + /** + * 種類をé¸æŠž + */ "selectType": string; + /** + * コンテンツ + */ "contentBlocks": string; + /** + * 入力 + */ "inputBlocks": string; + /** + * 特殊 + */ "specialBlocks": string; "blocks": { + /** + * テã‚スト + */ "text": string; + /** + * テã‚ストエリア + */ "textarea": string; + /** + * セクション + */ "section": string; + /** + * ç”»åƒ + */ "image": string; + /** + * ボタン + */ "button": string; + /** + * ノート埋ã‚込㿠+ */ "note": string; "_note": { + /** + * ノートID + */ "id": string; + /** + * ノートURLをペーストã—ã¦è¨å®šã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚ + */ "idDescription": string; + /** + * 詳細ãªè¡¨ç¤º + */ "detailed": string; }; }; }; "_relayStatus": { + /** + * 承èªå¾…ã¡ + */ "requesting": string; + /** + * 承èªæ¸ˆã¿ + */ "accepted": string; + /** + * æ‹’å¦æ¸ˆã¿ + */ "rejected": string; }; "_notification": { + /** + * ファイルãŒã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã•ã‚Œã¾ã—㟠+ */ "fileUploaded": string; - "youGotMention": string; - "youGotReply": string; - "youGotQuote": string; - "youRenoted": string; + /** + * {name}ã‹ã‚‰ã®ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ + */ + "youGotMention": ParameterizedString<"name">; + /** + * {name}ã‹ã‚‰ã®ãƒªãƒ—ライ + */ + "youGotReply": ParameterizedString<"name">; + /** + * {name}ã«ã‚ˆã‚‹å¼•ç”¨ + */ + "youGotQuote": ParameterizedString<"name">; + /** + * {name}ãŒBoostã—ã¾ã—㟠+ */ + "youRenoted": ParameterizedString<"name">; + /** + * フォãƒãƒ¼ã•ã‚Œã¾ã—㟠+ */ "youWereFollowed": string; + /** + * フォãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒæ¥ã¾ã—㟠+ */ "youReceivedFollowRequest": string; + /** + * フォãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒæ‰¿èªã•ã‚Œã¾ã—㟠+ */ "yourFollowRequestAccepted": string; + /** + * アンケートã®çµæžœãŒå‡ºã¾ã—㟠+ */ "pollEnded": string; + /** + * 投稿ãŒç·¨é›†ã•ã‚Œã¾ã—㟠+ */ + "edited": string; + /** + * æ–°ã—ã„投稿 + */ "newNote": string; - "unreadAntennaNote": string; + /** + * アンテナ {name} + */ + "unreadAntennaNote": ParameterizedString<"name">; + /** + * ãƒãƒ¼ãƒ«ãŒä»˜ä¸Žã•ã‚Œã¾ã—㟠+ */ "roleAssigned": string; + /** + * プッシュ通知ã®æ›´æ–°ã‚’ã—ã¾ã—㟠+ */ "emptyPushNotificationMessage": string; + /** + * 実績をç²å¾— + */ "achievementEarned": string; + /** + * 通知テスト + */ "testNotification": string; + /** + * 通知ã®è¡¨ç¤ºã‚’確ã‹ã‚ã‚‹ + */ "checkNotificationBehavior": string; + /** + * テスト通知をé€ä¿¡ã™ã‚‹ + */ "sendTestNotification": string; + /** + * 通知ã¯ã“ã®ã‚ˆã†ã«è¡¨ç¤ºã•ã‚Œã¾ã™ + */ "notificationWillBeDisplayedLikeThis": string; - "reactedBySomeUsers": string; - "renotedBySomeUsers": string; - "followedBySomeUsers": string; + /** + * {n}人ãŒãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã—ã¾ã—㟠+ */ + "reactedBySomeUsers": ParameterizedString<"n">; + /** + * {n}人ãŒãƒ–ーストã—ã¾ã—㟠+ */ + "renotedBySomeUsers": ParameterizedString<"n">; + /** + * {n}人ã«ãƒ•ã‚©ãƒãƒ¼ã•ã‚Œã¾ã—㟠+ */ + "followedBySomeUsers": ParameterizedString<"n">; + /** + * 通知ã®å±¥æ´ã‚’リセットã™ã‚‹ + */ + "flushNotification": string; "_types": { + /** + * ã™ã¹ã¦ + */ "all": string; + /** + * ユーザーã®æ–°è¦æŠ•ç¨¿ + */ "note": string; + /** + * フォãƒãƒ¼ + */ "follow": string; + /** + * メンション + */ "mention": string; + /** + * リプライ + */ "reply": string; + /** + * Boost + */ "renote": string; + /** + * 引用 + */ "quote": string; + /** + * リアクション + */ "reaction": string; + /** + * アンケートãŒçµ‚了 + */ "pollEnded": string; + /** + * フォãƒãƒ¼ç”³è«‹ã‚’å—ã‘å–ã£ãŸ + */ "receiveFollowRequest": string; + /** + * フォãƒãƒ¼ãŒå—ç†ã•ã‚ŒãŸ + */ "followRequestAccepted": string; + /** + * ãƒãƒ¼ãƒ«ãŒä»˜ä¸Žã•ã‚ŒãŸ + */ "roleAssigned": string; + /** + * 実績ã®ç²å¾— + */ "achievementEarned": string; + /** + * 連æºã‚¢ãƒ—リã‹ã‚‰ã®é€šçŸ¥ + */ "app": string; }; "_actions": { + /** + * フォãƒãƒ¼ãƒãƒƒã‚¯ + */ "followBack": string; + /** + * 返信 + */ "reply": string; + /** + * Boost + */ "renote": string; }; }; "_deck": { + /** + * 常ã«ãƒ¡ã‚¤ãƒ³ã‚«ãƒ©ãƒ を表示 + */ "alwaysShowMainColumn": string; + /** + * カラムã®å¯„ã› + */ "columnAlign": string; + /** + * ã‚«ãƒ©ãƒ ã‚’è¿½åŠ + */ "addColumn": string; + /** + * カラムã®è¨å®š + */ "configureColumn": string; + /** + * å·¦ã«ç§»å‹• + */ "swapLeft": string; + /** + * å³ã«ç§»å‹• + */ "swapRight": string; + /** + * 上ã«ç§»å‹• + */ "swapUp": string; + /** + * 下ã«ç§»å‹• + */ "swapDown": string; + /** + * å·¦ã«ã‚¹ã‚¿ãƒƒã‚¯ + */ "stackLeft": string; + /** + * å³ã«å‡ºã™ + */ "popRight": string; + /** + * プãƒãƒ•ã‚¡ã‚¤ãƒ« + */ "profile": string; + /** + * æ–°è¦ãƒ—ãƒãƒ•ã‚¡ã‚¤ãƒ« + */ "newProfile": string; + /** + * プãƒãƒ•ã‚¡ã‚¤ãƒ«ã‚’削除 + */ "deleteProfile": string; + /** + * カラムを組ã¿åˆã‚ã›ã¦è‡ªåˆ†ã ã‘ã®ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ã‚§ã‚¤ã‚¹ã‚’作りã¾ã—ょã†ï¼ + */ "introduction": string; + /** + * ç”»é¢ã®å³ã«ã‚ã‚‹ + を押ã—ã¦ã€ã„ã¤ã§ã‚‚ã‚«ãƒ©ãƒ ã‚’è¿½åŠ ã§ãã¾ã™ã€‚ + */ "introduction2": string; + /** + * カラムã®ãƒ¡ãƒ‹ãƒ¥ãƒ¼ã‹ã‚‰ã€ã€Œã‚¦ã‚£ã‚¸ã‚§ãƒƒãƒˆã®ç·¨é›†ã€ã‚’é¸æŠžã—ã¦ã‚¦ã‚£ã‚¸ã‚§ãƒƒãƒˆã‚’è¿½åŠ ã—ã¦ãã ã•ã„ + */ "widgetsIntroduction": string; + /** + * éžãƒ«ãƒ¼ãƒˆãƒšãƒ¼ã‚¸ã¯ç°¡æ˜“UIã§è¡¨ç¤º + */ "useSimpleUiForNonRootPages": string; + /** + * 「幅を自動調整ã€ãŒæœ‰åŠ¹ã®å ´åˆã€ã“ã‚ŒãŒå¹…ã®æœ€å°å€¤ã¨ãªã‚Šã¾ã™ + */ "usedAsMinWidthWhenFlexible": string; + /** + * 幅を自動調整 + */ "flexible": string; "_columns": { + /** + * メイン + */ "main": string; + /** + * ウィジェット + */ "widgets": string; + /** + * 通知 + */ "notifications": string; + /** + * タイムライン + */ "tl": string; + /** + * アンテナ + */ "antenna": string; + /** + * リスト + */ "list": string; + /** + * ãƒãƒ£ãƒ³ãƒãƒ« + */ "channel": string; + /** + * ã‚ãªãŸå®›ã¦ + */ "mentions": string; + /** + * ダイレクト + */ "direct": string; + /** + * ãƒãƒ¼ãƒ«ã‚¿ã‚¤ãƒ ライン + */ "roleTimeline": string; }; }; "_dialog": { - "charactersExceeded": string; - "charactersBelow": string; + /** + * 最大文å—数を超ãˆã¦ã„ã¾ã™ï¼ ç¾åœ¨ {current} / åˆ¶é™ {max} + */ + "charactersExceeded": ParameterizedString<"current" | "max">; + /** + * 最å°æ–‡å—数を下回ã£ã¦ã„ã¾ã™ï¼ ç¾åœ¨ {current} / åˆ¶é™ {min} + */ + "charactersBelow": ParameterizedString<"current" | "min">; }; "_disabledTimeline": { + /** + * 無効化ã•ã‚ŒãŸã‚¿ã‚¤ãƒ ライン + */ "title": string; + /** + * ç¾åœ¨ã®ãƒãƒ¼ãƒ«ã§ã¯ã€ã“ã®ã‚¿ã‚¤ãƒ ラインを使用ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。 + */ "description": string; }; "_drivecleaner": { + /** + * サイズãŒå¤§ãã„é † + */ "orderBySizeDesc": string; + /** + * è¿½åŠ æ—¥ãŒå¤ã„é † + */ "orderByCreatedAtAsc": string; }; "_webhookSettings": { + /** + * Webhookã‚’ä½œæˆ + */ "createWebhook": string; + /** + * åå‰ + */ "name": string; + /** + * シークレット + */ "secret": string; + /** + * Webhookを実行ã™ã‚‹ã‚¿ã‚¤ãƒŸãƒ³ã‚° + */ "events": string; + /** + * 有効 + */ "active": string; "_events": { + /** + * フォãƒãƒ¼ã—ãŸã¨ã + */ "follow": string; + /** + * フォãƒãƒ¼ã•ã‚ŒãŸã¨ã + */ "followed": string; + /** + * ノートを投稿ã—ãŸã¨ã + */ "note": string; + /** + * 返信ã•ã‚ŒãŸã¨ã + */ "reply": string; + /** + * Boostã•ã‚ŒãŸã¨ã + */ "renote": string; + /** + * リアクションãŒã‚ã£ãŸã¨ã + */ "reaction": string; + /** + * メンションã•ã‚ŒãŸã¨ã + */ "mention": string; }; }; "_moderationLogTypes": { + /** + * ãƒãƒ¼ãƒ«ã‚’ä½œæˆ + */ "createRole": string; + /** + * ãƒãƒ¼ãƒ«ã‚’削除 + */ "deleteRole": string; + /** + * ãƒãƒ¼ãƒ«ã‚’æ›´æ–° + */ "updateRole": string; + /** + * ãƒãƒ¼ãƒ«ã¸ã‚¢ã‚µã‚¤ãƒ³ + */ "assignRole": string; + /** + * ãƒãƒ¼ãƒ«ã®ã‚¢ã‚µã‚¤ãƒ³è§£é™¤ + */ "unassignRole": string; + /** + * 承èªæ¸ˆã¿ + */ "approve": string; + /** + * å‡çµ + */ "suspend": string; + /** + * å‡çµè§£é™¤ + */ "unsuspend": string; + /** + * カスタム絵文å—è¿½åŠ + */ "addCustomEmoji": string; + /** + * カスタム絵文å—æ›´æ–° + */ "updateCustomEmoji": string; + /** + * カスタム絵文å—削除 + */ "deleteCustomEmoji": string; + /** + * サーãƒãƒ¼è¨å®šæ›´æ–° + */ "updateServerSettings": string; + /** + * ユーザーã®ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãƒŽãƒ¼ãƒˆæ›´æ–° + */ "updateUserNote": string; + /** + * ファイルを削除 + */ "deleteDriveFile": string; + /** + * ノートを削除 + */ "deleteNote": string; + /** + * 全体ã®ãŠçŸ¥ã‚‰ã›ã‚’ä½œæˆ + */ "createGlobalAnnouncement": string; + /** + * ユーザーã¸ãŠçŸ¥ã‚‰ã›ã‚’ä½œæˆ + */ "createUserAnnouncement": string; + /** + * 全体ã®ãŠçŸ¥ã‚‰ã›ã‚’æ›´æ–° + */ "updateGlobalAnnouncement": string; + /** + * ユーザーã®ãŠçŸ¥ã‚‰ã›ã‚’æ›´æ–° + */ "updateUserAnnouncement": string; + /** + * 全体ã®ãŠçŸ¥ã‚‰ã›ã‚’削除 + */ "deleteGlobalAnnouncement": string; + /** + * ユーザーã®ãŠçŸ¥ã‚‰ã›ã‚’削除 + */ "deleteUserAnnouncement": string; + /** + * パスワードをリセット + */ "resetPassword": string; + /** + * リモートサーãƒãƒ¼ã‚’åœæ¢ + */ "suspendRemoteInstance": string; + /** + * リモートサーãƒãƒ¼ã‚’å†é–‹ + */ "unsuspendRemoteInstance": string; + /** + * リモートサーãƒãƒ¼ã®ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãƒŽãƒ¼ãƒˆæ›´æ–° + */ + "updateRemoteInstanceNote": string; + /** + * ファイルをセンシティブ付与 + */ "markSensitiveDriveFile": string; + /** + * ファイルをセンシティブ解除 + */ "unmarkSensitiveDriveFile": string; + /** + * é€šå ±ã‚’è§£æ±º + */ "resolveAbuseReport": string; + /** + * æ‹›å¾…ã‚³ãƒ¼ãƒ‰ã‚’ä½œæˆ + */ "createInvitation": string; + /** + * åºƒå‘Šã‚’ä½œæˆ + */ "createAd": string; + /** + * 広告を削除 + */ "deleteAd": string; + /** + * 広告を更新 + */ "updateAd": string; + /** + * ã‚¢ã‚¤ã‚³ãƒ³ãƒ‡ã‚³ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã‚’ä½œæˆ + */ "createAvatarDecoration": string; + /** + * アイコンデコレーションを更新 + */ "updateAvatarDecoration": string; + /** + * アイコンデコレーションを削除 + */ "deleteAvatarDecoration": string; + /** + * ユーザーã®ã‚¢ã‚¤ã‚³ãƒ³ã‚’解除 + */ "unsetUserAvatar": string; + /** + * ユーザーã®ãƒãƒŠãƒ¼ã‚’解除 + */ "unsetUserBanner": string; }; "_fileViewer": { + /** + * ファイルã®è©³ç´° + */ "title": string; + /** + * ファイルタイプ + */ "type": string; + /** + * ファイルサイズ + */ "size": string; + /** + * URL + */ "url": string; + /** + * è¿½åŠ æ—¥ + */ "uploadedAt": string; + /** + * 添付ã•ã‚Œã¦ã„るノート + */ "attachedNotes": string; + /** + * ã“ã®ãƒšãƒ¼ã‚¸ã¯ã€ã“ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’アップãƒãƒ¼ãƒ‰ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã—ã‹é–²è¦§ã§ãã¾ã›ã‚“。 + */ "thisPageCanBeSeenFromTheAuthor": string; }; "_externalResourceInstaller": { + /** + * 外部サイトã‹ã‚‰ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ« + */ "title": string; + /** + * é…布元ãŒä¿¡é ¼ã§ãã‚‹ã‹ã‚’確èªã—ãŸä¸Šã§ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ã¦ãã ã•ã„。 + */ "checkVendorBeforeInstall": string; "_plugin": { + /** + * ã“ã®ãƒ—ラグインをインストールã—ã¾ã™ã‹ï¼Ÿ + */ "title": string; + /** + * ãƒ—ãƒ©ã‚°ã‚¤ãƒ³æƒ…å ± + */ "metaTitle": string; }; "_theme": { + /** + * ã“ã®ãƒ†ãƒ¼ãƒžã‚’インストールã—ã¾ã™ã‹ï¼Ÿ + */ "title": string; + /** + * ãƒ†ãƒ¼ãƒžæƒ…å ± + */ "metaTitle": string; }; "_meta": { + /** + * 基本ã®ã‚«ãƒ©ãƒ¼ã‚¹ã‚ーム+ */ "base": string; }; "_vendorInfo": { + /** + * é…å¸ƒå…ƒæƒ…å ± + */ "title": string; + /** + * å‚ç…§ã—ãŸã‚¨ãƒ³ãƒ‰ãƒã‚¤ãƒ³ãƒˆ + */ "endpoint": string; + /** + * ファイル整åˆæ€§ã®ç¢ºèª + */ "hashVerify": string; }; "_errors": { "_invalidParams": { + /** + * パラメータãŒä¸è¶³ã—ã¦ã„ã¾ã™ + */ "title": string; + /** + * 外部サイトã‹ã‚‰ãƒ‡ãƒ¼ã‚¿ã‚’å–å¾—ã™ã‚‹ãŸã‚ã«å¿…è¦ãªæƒ…å ±ãŒä¸è¶³ã—ã¦ã„ã¾ã™ã€‚URLã‚’ãŠç¢ºã‹ã‚ãã ã•ã„。 + */ "description": string; }; "_resourceTypeNotSupported": { + /** + * ã“ã®å¤–部リソースã«ã¯å¯¾å¿œã—ã¦ã„ã¾ã›ã‚“ + */ "title": string; + /** + * ã“ã®å¤–部サイトã‹ã‚‰å–å¾—ã—ãŸãƒªã‚½ãƒ¼ã‚¹ã®ç¨®åˆ¥ã«ã¯å¯¾å¿œã—ã¦ã„ã¾ã›ã‚“。サイト管ç†è€…ã«ãŠå•ã„åˆã‚ã›ãã ã•ã„。 + */ "description": string; }; "_failedToFetch": { + /** + * データã®å–å¾—ã«å¤±æ•—ã—ã¾ã—㟠+ */ "title": string; + /** + * 外部サイトã¨ã®é€šä¿¡ã«å¤±æ•—ã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦è©¦ã—ã¦ã‚‚改善ã—ãªã„å ´åˆã€ã‚µã‚¤ãƒˆç®¡ç†è€…ã«ãŠå•ã„åˆã‚ã›ãã ã•ã„。 + */ "fetchErrorDescription": string; + /** + * 外部サイトã‹ã‚‰å–å¾—ã—ãŸãƒ‡ãƒ¼ã‚¿ãŒèªã¿å–ã‚Œã¾ã›ã‚“ã§ã—ãŸã€‚サイト管ç†è€…ã«ãŠå•ã„åˆã‚ã›ãã ã•ã„。 + */ "parseErrorDescription": string; }; "_hashUnmatched": { + /** + * æ£ã—ã„データãŒå–å¾—ã§ãã¾ã›ã‚“ã§ã—㟠+ */ "title": string; + /** + * æä¾›ã•ã‚ŒãŸãƒ‡ãƒ¼ã‚¿ã®æ•´åˆæ€§ã®ç¢ºèªã«å¤±æ•—ã—ã¾ã—ãŸã€‚ã‚»ã‚ュリティ上ã€ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã¯ç¶šè¡Œã§ãã¾ã›ã‚“。サイト管ç†è€…ã«ãŠå•ã„åˆã‚ã›ãã ã•ã„。 + */ "description": string; }; "_pluginParseFailed": { + /** + * AiScript エラー + */ "title": string; + /** + * データã¯å–å¾—ã§ããŸã‚‚ã®ã®ã€AiScriptã®è§£æžæ™‚ã«ã‚¨ãƒ©ãƒ¼ãŒã‚ã£ãŸãŸã‚èªã¿è¾¼ã‚ã¾ã›ã‚“ã§ã—ãŸã€‚プラグインã®ä½œè€…ã«ãŠå•ã„åˆã‚ã›ãã ã•ã„。エラーã®è©³ç´°ã¯Javascriptコンソールをã”確èªãã ã•ã„。 + */ "description": string; }; "_pluginInstallFailed": { + /** + * プラグインã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã«å¤±æ•—ã—ã¾ã—㟠+ */ "title": string; + /** + * プラグインã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ä¸ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„。エラーã®è©³ç´°ã¯Javascriptコンソールをã”覧ãã ã•ã„。 + */ "description": string; }; "_themeParseFailed": { + /** + * テーマ解æžã‚¨ãƒ©ãƒ¼ + */ "title": string; + /** + * データã¯å–å¾—ã§ããŸã‚‚ã®ã®ã€ãƒ†ãƒ¼ãƒžãƒ•ã‚¡ã‚¤ãƒ«ã®è§£æžæ™‚ã«ã‚¨ãƒ©ãƒ¼ãŒã‚ã£ãŸãŸã‚èªã¿è¾¼ã‚ã¾ã›ã‚“ã§ã—ãŸã€‚テーマã®ä½œè€…ã«ãŠå•ã„åˆã‚ã›ãã ã•ã„。エラーã®è©³ç´°ã¯Javascriptコンソールをã”確èªãã ã•ã„。 + */ "description": string; }; "_themeInstallFailed": { + /** + * テーマã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã«å¤±æ•—ã—ã¾ã—㟠+ */ "title": string; + /** + * テーマã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ä¸ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„。エラーã®è©³ç´°ã¯Javascriptコンソールをã”覧ãã ã•ã„。 + */ "description": string; }; }; }; "_animatedMFM": { + /** + * MFMアニメーションをå†ç”Ÿ + */ "play": string; + /** + * MFMアニメーションåœæ¢ + */ "stop": string; "_alert": { + /** + * MFMアニメーションã«ã¯ã€ç‚¹æ»…ã™ã‚‹ãƒ©ã‚¤ãƒˆã‚„高速ã§å‹•ãテã‚スト/絵文å—ã‚’å«ã¾ã‚Œã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ã€‚ + */ "text": string; + /** + * å†ç”Ÿã™ã‚‹ + */ "confirm": string; }; }; "_dataRequest": { + /** + * データリクエスト + */ "title": string; + /** + * データリクエストã¯3æ—¥ã”ã¨ã«å¯èƒ½ã§ã™ã€‚ + */ "warn": string; + /** + * データã®ä¿å˜ãŒå®Œäº†ã™ã‚‹ã¨ã€ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ç™»éŒ²ã•ã‚Œã¦ã„ã‚‹Eメールアドレスã«ãƒ¡ãƒ¼ãƒ«ãŒé€ä¿¡ã•ã‚Œã¾ã™ã€‚ + */ "text": string; + /** + * リクエスト + */ "button": string; }; "_dataSaver": { "_media": { + /** + * メディアã®èªã¿è¾¼ã¿ + */ "title": string; + /** + * ç”»åƒãƒ»å‹•ç”»ãŒè‡ªå‹•ã§èªã¿è¾¼ã¾ã‚Œã‚‹ã®ã‚’防æ¢ã—ã¾ã™ã€‚éš ã‚Œã¦ã„ã‚‹ç”»åƒãƒ»å‹•ç”»ã¯ã‚¿ãƒƒãƒ—ã™ã‚‹ã¨èªã¿è¾¼ã¾ã‚Œã¾ã™ã€‚ + */ "description": string; }; "_avatar": { + /** + * ã‚¢ã‚¤ã‚³ãƒ³ç”»åƒ + */ "title": string; + /** + * アイコン画åƒã®ã‚¢ãƒ‹ãƒ¡ãƒ¼ã‚·ãƒ§ãƒ³ãŒåœæ¢ã—ã¾ã™ã€‚アニメーション画åƒã¯é€šå¸¸ã®ç”»åƒã‚ˆã‚Šãƒ•ã‚¡ã‚¤ãƒ«ã‚µã‚¤ã‚ºãŒå¤§ãã„ã“ã¨ãŒã‚ã‚‹ã®ã§ã€ãƒ‡ãƒ¼ã‚¿é€šä¿¡é‡ã‚’ã•ã‚‰ã«å‰Šæ¸›ã§ãã¾ã™ã€‚ + */ "description": string; }; "_urlPreview": { + /** + * URLプレビューã®ã‚µãƒ ãƒã‚¤ãƒ« + */ "title": string; + /** + * URLプレビューã®ã‚µãƒ ãƒã‚¤ãƒ«ç”»åƒãŒèªã¿è¾¼ã¾ã‚Œãªããªã‚Šã¾ã™ã€‚ + */ "description": string; }; "_code": { + /** + * コードãƒã‚¤ãƒ©ã‚¤ãƒˆ + */ "title": string; + /** + * MFMãªã©ã§ã‚³ãƒ¼ãƒ‰ãƒã‚¤ãƒ©ã‚¤ãƒˆè¨˜æ³•ãŒä½¿ã‚ã‚Œã¦ã„ã‚‹å ´åˆã€ã‚¿ãƒƒãƒ—ã™ã‚‹ã¾ã§èªã¿è¾¼ã¾ã‚Œãªããªã‚Šã¾ã™ã€‚コードãƒã‚¤ãƒ©ã‚¤ãƒˆã§ã¯ãƒã‚¤ãƒ©ã‚¤ãƒˆã™ã‚‹è¨€èªžã”ã¨ã«ãã®å®šç¾©ãƒ•ã‚¡ã‚¤ãƒ«ã‚’èªã¿è¾¼ã‚€å¿…è¦ãŒã‚ã‚Šã¾ã™ãŒã€ãれらãŒè‡ªå‹•ã§èªã¿è¾¼ã¾ã‚Œãªããªã‚‹ãŸã‚ã€é€šä¿¡é‡ã®å‰Šæ¸›ãŒè¦‹è¾¼ã‚ã¾ã™ã€‚ + */ "description": string; }; }; + "_hemisphere": { + /** + * 北åŠçƒ + */ + "N": string; + /** + * å—åŠçƒ + */ + "S": string; + /** + * 一部ã®ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆè¨å®šã§ã€å£ç¯€ã‚’判定ã™ã‚‹ãŸã‚ã«ä½¿ç”¨ã—ã¾ã™ã€‚ + */ + "caption": string; + }; + "_reversi": { + /** + * リãƒãƒ¼ã‚· + */ + "reversi": string; + /** + * 対局ã®è¨å®š + */ + "gameSettings": string; + /** + * ボードをé¸æŠž + */ + "chooseBoard": string; + /** + * 先行/後攻 + */ + "blackOrWhite": string; + /** + * {name}ãŒé»’(先行) + */ + "blackIs": ParameterizedString<"name">; + /** + * ルール + */ + "rules": string; + /** + * 対局ã¯ã¾ã‚‚ãªã開始ã•ã‚Œã¾ã™ + */ + "thisGameIsStartedSoon": string; + /** + * 相手ã®æº–å‚™ãŒå®Œäº†ã™ã‚‹ã®ã‚’å¾…ã£ã¦ã„ã¾ã™ + */ + "waitingForOther": string; + /** + * ã‚ãªãŸã®æº–å‚™ãŒå®Œäº†ã™ã‚‹ã®ã‚’å¾…ã£ã¦ã„ã¾ã™ + */ + "waitingForMe": string; + /** + * 準備ã—ã¦ãã ã•ã„ + */ + "waitingBoth": string; + /** + * 準備完了 + */ + "ready": string; + /** + * 準備をå†é–‹ + */ + "cancelReady": string; + /** + * 相手ã®ã‚¿ãƒ¼ãƒ³ã§ã™ + */ + "opponentTurn": string; + /** + * ã‚ãªãŸã®ã‚¿ãƒ¼ãƒ³ã§ã™ + */ + "myTurn": string; + /** + * {name}ã®ã‚¿ãƒ¼ãƒ³ã§ã™ + */ + "turnOf": ParameterizedString<"name">; + /** + * {name}ã®ã‚¿ãƒ¼ãƒ³ + */ + "pastTurnOf": ParameterizedString<"name">; + /** + * 投了 + */ + "surrender": string; + /** + * 投了ã«ã‚ˆã‚Š + */ + "surrendered": string; + /** + * 時間切れ + */ + "timeout": string; + /** + * 引ã分㑠+ */ + "drawn": string; + /** + * {name}ã®å‹ã¡ + */ + "won": ParameterizedString<"name">; + /** + * é»’ + */ + "black": string; + /** + * 白 + */ + "white": string; + /** + * åˆè¨ˆ + */ + "total": string; + /** + * {count}ターン目 + */ + "turnCount": ParameterizedString<"count">; + /** + * 自分ã®å¯¾å±€ + */ + "myGames": string; + /** + * ã¿ã‚“ãªã®å¯¾å±€ + */ + "allGames": string; + /** + * 終了 + */ + "ended": string; + /** + * å¯¾å±€ä¸ + */ + "playing": string; + /** + * 石ã®å°‘ãªã„æ–¹ãŒå‹ã¡(ãƒã‚»ã‚ª) + */ + "isLlotheo": string; + /** + * ループマップ + */ + "loopedMap": string; + /** + * ã©ã“ã§ã‚‚ç½®ã‘るモード + */ + "canPutEverywhere": string; + /** + * 1ターンã®æ™‚é–“åˆ¶é™ + */ + "timeLimitForEachTurn": string; + /** + * フリーマッム+ */ + "freeMatch": string; + /** + * 対戦相手を探ã—ã¦ã„ã¾ã™ + */ + "lookingForPlayer": string; + /** + * 対局ãŒã‚ャンセルã•ã‚Œã¾ã—㟠+ */ + "gameCanceled": string; + /** + * 開始時ã«å¯¾å±€ã‚’タイムラインã«æŠ•ç¨¿ + */ + "shareToTlTheGameWhenStart": string; + /** + * 対局を開始ã—ã¾ã—ãŸï¼ #MisskeyReversi + */ + "iStartedAGame": string; + /** + * 相手ãŒè¨å®šã‚’変更ã—ã¾ã—㟠+ */ + "opponentHasSettingsChanged": string; + /** + * å¤‰å‰‡è¨±å¯ (完全フリー) + */ + "allowIrregularRules": string; + /** + * 変則ãªã— + */ + "disallowIrregularRules": string; + /** + * 盤é¢ã«è¡Œãƒ»åˆ—番å·ã‚’表示 + */ + "showBoardLabels": string; + /** + * 石をアイコンã«ã™ã‚‹ + */ + "useAvatarAsStone": string; + }; + "_offlineScreen": { + /** + * オフライン - サーãƒãƒ¼ã«æŽ¥ç¶šã§ãã¾ã›ã‚“ + */ + "title": string; + /** + * サーãƒãƒ¼ã«æŽ¥ç¶šã§ãã¾ã›ã‚“ + */ + "header": string; + }; } declare const locales: { [lang: string]: Locale; diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 33686ddc3df2137be6fc137341ef8a04b9b71f05..7414e083d06129933901530ad682b39f1dfc8d1b 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -103,7 +103,7 @@ defaultNoteVisibility: "Privacy predefinita delle note" follow: "Segui" followRequest: "Richiesta di follow" followRequests: "Richieste di follow" -unfollow: "Interrompi following" +unfollow: "Smetti di seguire" followRequestPending: "Richiesta in approvazione" enterEmoji: "Inserisci emoji" renote: "Rinota" @@ -131,6 +131,7 @@ overwriteFromPinnedEmojis: "Sovrascrivi con le impostazioni globali" reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere." rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note" attachCancel: "Rimuovi allegato" +deleteFile: "File da Drive eliminato" markAsSensitive: "Segna come esplicito" unmarkAsSensitive: "Non segnare come esplicito " enterFileName: "Nome del file" @@ -380,6 +381,11 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Abilita hCaptcha" hcaptchaSiteKey: "Chiave del sito" hcaptchaSecretKey: "Chiave segreta" +mcaptcha: "mCaptcha" +enableMcaptcha: "Abilita hCaptcha" +mcaptchaSiteKey: "Chiave del sito" +mcaptchaSecretKey: "Chiave segreta" +mcaptchaInstanceUrl: "URL della istanza mCaptcha" recaptcha: "reCAPTCHA" enableRecaptcha: "Abilita reCAPTCHA" recaptchaSiteKey: "Chiave del sito" @@ -430,7 +436,7 @@ moderation: "moderazione" moderationNote: "Promemoria di moderazione" addModerationNote: "Aggiungi promemoria di moderazione" moderationLogs: "Cronologia di moderazione" -nUsersMentioned: "{n} profili menzionati" +nUsersMentioned: "{n} profili ne parlano" securityKeyAndPasskey: "Chiave di sicurezza e accesso" securityKey: "Chiave di sicurezza" lastUsed: "Ultima attività " @@ -627,6 +633,7 @@ medium: "Medio" small: "Piccolo" generateAccessToken: "Genera token di accesso" permission: "Autorizzazioni " +adminPermission: "Privilegi amministrativi" enableAll: "Abilita tutto" disableAll: "Disabilita tutto" tokenRequested: "Autorizza accesso al profilo" @@ -652,7 +659,7 @@ hardWordMute: "Filtro parole forte" regexpError: "errore regex" regexpErrorDescription: "Si è verificato un errore nell'espressione regolare alla riga {line} della parola muta {tab}:" instanceMute: "Silenzia l'istanza" -userSaysSomething: "{name} ha detto qualcosa" +userSaysSomething: "{name} ha parlato" makeActive: "Attiva" display: "Visualizza" copy: "Copia" @@ -670,6 +677,7 @@ useGlobalSettingDesc: "Quando attiva, verranno utilizzate le impostazioni notifi other: "Ulteriori" regenerateLoginToken: "Genera di nuovo un token di connessione" regenerateLoginTokenDescription: "Genera un nuovo token di autenticazione. Solitamente questa operazione non è necessaria: quando si genera un nuovo token, tutti i dispositivi vanno disconnessi." +theKeywordWhenSearchingForCustomEmoji: "Questa sarà la parola chiave durante la ricerca di emoji personalizzate" setMultipleBySeparatingWithSpace: "È possibile creare multiple voci separate da spazi." fileIdOrUrl: "ID o URL del file" behavior: "Comportamento" @@ -757,7 +765,7 @@ reloadToApplySetting: "Le tue preferenze verranno impostate dopo il ricaricament needReloadToApply: "È necessario riavviare per rendere effettive le modifiche." showTitlebar: "Visualizza la barra del titolo" clearCache: "Svuota la cache" -onlineUsersCount: "{n} persone online" +onlineUsersCount: "{n} persone attive adesso" nUsers: "{n} profili" nNotes: "{n}Note" sendErrorReports: "Invia segnalazioni di errori" @@ -868,7 +876,7 @@ pubSub: "Publish/Subscribe del profilo" lastCommunication: "La comunicazione più recente" resolved: "Risolto" unresolved: "Non risolto" -breakFollow: "Interrompi follow" +breakFollow: "Impedire di seguirmi" breakFollowConfirm: "Vuoi davvero che questo profilo smetta di seguirti?" itsOn: "Abilitato" itsOff: "Disabilitato" @@ -884,6 +892,8 @@ makeReactionsPublicDescription: "La lista delle reazioni che avete fatto è a di classic: "Classico" muteThread: "Silenzia conversazione" unmuteThread: "Riattiva la conversazione" +followingVisibility: "Visibilità dei profili seguiti" +followersVisibility: "Visibilità dei profili che ti seguono" continueThread: "Altre conversazioni" deleteAccountConfirm: "Così verrà eliminato il profilo. Vuoi procedere?" incorrectPassword: "La password è errata." @@ -985,6 +995,7 @@ neverShow: "Non mostrare più" remindMeLater: "Rimanda" didYouLikeMisskey: "Ti piace Misskey?" pleaseDonate: "Misskey è il software libero utilizzato su {host}. Offrendo una donazione è più facile continuare a svilupparlo!" +correspondingSourceIsAvailable: "" roles: "Ruoli" role: "Ruolo" noRole: "Ruolo non trovato" @@ -1035,6 +1046,9 @@ resetPasswordConfirm: "Vuoi davvero ripristinare la password?" sensitiveWords: "Parole esplicite" sensitiveWordsDescription: "Imposta automaticamente \"Home\" alla visibilità delle Note che contengono una qualsiasi parola tra queste configurate. Puoi separarle per riga." sensitiveWordsDescription2: "Gli spazi creano la relazione \"E\" tra parole (questo E quello). Racchiudere una parola nelle slash \"/\" la trasforma in Espressione Regolare." +prohibitedWords: "Parole proibite" +prohibitedWordsDescription: "Verrà impedito di pubblicare Note che abbiano le parole indicate. Puoi impostare più parole, separatamente, su ogni riga." +prohibitedWordsDescription2: "Gli spazi creano la relazione \"E\" tra parole (questo E quello). Racchiudere una parola nelle slash \"/\" la trasforma in Espressione Regolare." hiddenTags: "Hashtag nascosti" hiddenTagsDescription: "Impedire la visualizzazione del tag impostato nei trend. Puoi impostare più valori, uno per riga." notesSearchNotAvailable: "Non è possibile cercare tra le Note." @@ -1053,6 +1067,8 @@ limitWidthOfReaction: "Limita la larghezza delle reazioni e ridimensionale" noteIdOrUrl: "ID della Nota o URL" video: "Video" videos: "Video" +audio: "Audio" +audioFiles: "Audio" dataSaver: "Risparmia dati" accountMigration: "Migrazione del profilo" accountMoved: "Questo profilo ha migrato altrove:" @@ -1156,6 +1172,13 @@ hideRepliesToOthersInTimelineAll: "Nascondi le risposte dei tuoi follow nella TL confirmShowRepliesAll: "Questa è una attività irreversibile. Vuoi davvero includere tutte le risposte dei following in TL?" confirmHideRepliesAll: "Questa è una attività irreversibile. Vuoi davvero escludere tutte le risposte dei following in TL?" externalServices: "Servizi esterni" +sourceCode: "Codice sorgente" +sourceCodeIsNotYetProvided: "" +repositoryUrl: "URL della repository" +repositoryUrlDescription: "Se esiste un repository il cui il codice sorgente è disponibile pubblicamente, inserisci il suo URL. Se stai utilizzando Misskey così com'è (senza alcuna modifica al codice sorgente), inserisci https://github.com/misskey-dev/misskey." +repositoryUrlOrTarballRequired: "Se non disponi di un repository pubblico, dovrai fornire un file tarball (tar). Vedere .config/example.yml per i dettagli." +feedback: "Feedback" +feedbackUrl: "URL di feedback" impressum: "Dichiarazione di proprietà " impressumUrl: "URL della dichiarazione di proprietà " impressumDescription: "La dichiarazione di proprietà , è obbligatoria in alcuni paesi come la Germania (Impressum)." @@ -1183,6 +1206,28 @@ remainingN: "Rimangono: {n}" overwriteContentConfirm: "Vuoi davvero sostituire l'attuale contenuto?" seasonalScreenEffect: "Schermate in base alla stagione" decorate: "Decora" +addMfmFunction: "Aggiungi decorazioni" +enableQuickAddMfmFunction: "Attiva il selettore di funzioni MFM" +bubbleGame: "Bubble Game" +sfx: "Effetti sonori" +soundWillBePlayed: "Con musica ed effetti sonori" +showReplay: "Vedi i replay" +replay: "Replay" +replaying: "Replay in corso" +ranking: "Classifica" +lastNDays: "Ultimi {n} giorni" +backToTitle: "Torna al titolo" +hemisphere: "Geolocalizzazione" +withSensitive: "Mostra le Note con allegati espliciti" +userSaysSomethingSensitive: "Note da {name} con allegati espliciti" +enableHorizontalSwipe: "Trascina per invertire i tab" +surrender: "Annulla" +_bubbleGame: + howToPlay: "Come giocare" + _howToPlay: + section1: "Scegli la posizione e rilascia l'oggetto nel contenitore." + section2: "Se due oggetti dello stesso tipo si toccano, si trasformano in un oggetto diverso, aumentando il punteggio." + section3: "Se gli oggetti escono dal limite superiore del contenitore, il gioco finisce. Cerca di ottenere un punteggio elevato fondendo gli oggetti, evitando che escano dal contenitore!" _announcement: forExistingUsers: "Solo ai profili attuali" forExistingUsersDescription: "L'annuncio sarà visibile solo ai profili esistenti in questo momento. Se disabilitato, sarà visibile anche ai profili che verranno creati dopo la pubblicazione di questo annuncio." @@ -1553,6 +1598,13 @@ _achievements: _tutorialCompleted: title: "Attestato di partecipazione al corso per principianti di Misskey" description: "Ha completato il tutorial" + _bubbleGameExplodingHead: + title: "🤯" + description: "Estrai l'oggetto più grande dal Bubble Game" + _bubbleGameDoubleExplodingHead: + title: "Doppio 🤯" + description: "Due oggetti più grossi contemporaneamente nel Bubble Game" + flavor: "Ha le dimensioni di una bento-box 🤯 🤯" _role: new: "Nuovo ruolo" edit: "Modifica ruolo" @@ -1643,6 +1695,7 @@ _emailUnavailable: disposable: "Indirizzo email non utilizzabile" mx: "Server email non corretto" smtp: "Il server email non risponde" + banned: "Non puoi registrarti con questo indirizzo email" _ffVisibility: public: "Pubblica" followers: "Mostra solo ai follower" @@ -1715,6 +1768,8 @@ _aboutMisskey: contributors: "Principali sostenitori" allContributors: "Tutti i sostenitori" source: "Codice sorgente" + original: "Originale" + thisIsModifiedVersion: "{name} sta usando una versione modificata diversa da Misskey originale." translation: "Tradurre Misskey" donate: "Sostieni Misskey" morePatrons: "Apprezziamo sinceramente il supporto di tante altre persone. Grazie mille! 🥰" @@ -1935,6 +1990,55 @@ _permissions: "write:flash": "Modifica Play" "read:flash-likes": "Visualizza lista di Play piaciuti" "write:flash-likes": "Modifica lista di Play piaciuti" + "read:admin:abuse-user-reports": "Mostra i report dai profili utente" + "write:admin:delete-account": "Elimina l'account utente" + "write:admin:delete-all-files-of-a-user": "Elimina i file dell'account utente" + "read:admin:index-stats": "Visualizza informazioni sugli indici del database" + "read:admin:table-stats": "Visualizza informazioni sulle tabelle del database" + "read:admin:user-ips": "Visualizza indirizzi IP degli account" + "read:admin:meta": "Visualizza i metadati dell'istanza" + "write:admin:reset-password": "Ripristina la password dell'account utente" + "write:admin:resolve-abuse-user-report": "Risolvere le segnalazioni dagli account utente" + "write:admin:send-email": "Spedire email" + "read:admin:server-info": "Vedere le informazioni sul server" + "read:admin:show-moderation-log": "Vedere lo storico di moderazione" + "read:admin:show-user": "Vedere le informazioni private degli account utente" + "read:admin:show-users": "Vedere le informazioni private degli account utente" + "write:admin:suspend-user": "Sospendere i profili" + "write:admin:unset-user-avatar": "Rimuovere la foto profilo dai profili" + "write:admin:unset-user-banner": "Rimuovere l'immagine testata dai profili" + "write:admin:unsuspend-user": "Togliere la sospensione ai profili" + "write:admin:meta": "Modificare i metadati dell'istanza" + "write:admin:user-note": "Scrivere annotazioni di moderazione" + "write:admin:roles": "Gestire i ruoli" + "read:admin:roles": "Vedere i ruoli" + "write:admin:relays": "Gestire i Relay" + "read:admin:relays": "Vedere i Relay" + "write:admin:invite-codes": "Gestire codici di invito" + "read:admin:invite-codes": "Vedere codici di invito" + "write:admin:announcements": "Gestire gli annunci" + "read:admin:announcements": "Leggere gli annunci" + "write:admin:avatar-decorations": "Gestire le decorazioni" + "read:admin:avatar-decorations": "Vedere le decorazioni" + "write:admin:federation": "Gestire la federazione" + "write:admin:account": "Vedere la federazione" + "read:admin:account": "Vedere le utenze" + "write:admin:emoji": "Gestire le emoji personalizzate" + "read:admin:emoji": "Vedere le emoji personalizzate" + "write:admin:queue": "Gestire la coda di attività " + "read:admin:queue": "Vedere la coda di attività " + "write:admin:promo": "Gestire le promozioni" + "write:admin:drive": "Gestire il Drive degli account" + "read:admin:drive": "Vedere il Drive degli account" + "read:admin:stream": "Usare le API Websocket" + "write:admin:ad": "Gestire i banner pubblicitari" + "read:admin:ad": "Vedere i banner pubblicitari" + "write:invite-codes": "Creare codici di invito" + "read:invite-codes": "Vedere i codici di invito" + "write:clip-favorite": "Impostare Clip preferite" + "read:clip-favorite": "Vedere Clip preferite" + "read:federation": "Vedere la federazione" + "write:report-abuse": "Inviare segnalazioni" _auth: shareAccessTitle: "Permessi dell'applicazione" shareAccess: "Vuoi autorizzare {name} ad accedere al tuo profilo?" @@ -1979,7 +2083,7 @@ _widgets: postForm: "Finestra di pubblicazione" slideshow: "Diapositive" button: "Pulsante" - onlineUsers: "Persone online" + onlineUsers: "Persone attive adesso" jobQueue: "Coda di lavoro" serverMetric: "Statistiche server" aiscript: "Console AiScript" @@ -2056,6 +2160,7 @@ _profile: _exportOrImport: allNotes: "Tutte le note" favoritedNotes: "Note preferite" + clips: "Clip" followingList: "Follow" muteList: "Elenco profili silenziati" blockingList: "Elenco profili bloccati" @@ -2174,6 +2279,7 @@ _notification: pollEnded: "Risultati del sondaggio." newNote: "Nuove Note" unreadAntennaNote: "Antenna {name}" + roleAssigned: "Ruolo assegnato" emptyPushNotificationMessage: "Le notifiche push sono state aggiornate." achievementEarned: "Obiettivo raggiunto" testNotification: "Prova la notifica" @@ -2195,6 +2301,7 @@ _notification: pollEnded: "Sondaggio chiuso." receiveFollowRequest: "Richiesta di follow ricevuta" followRequestAccepted: "Richiesta di follow accettata" + roleAssigned: "Ruolo concesso" achievementEarned: "Risultato raggiunto" app: "Notifiche da applicazioni" _actions: @@ -2353,3 +2460,53 @@ _dataSaver: _code: title: "Codice evidenziato" description: "Impedire che il codice sorgente sia automaticamente evidenziato. Evidenziare il codice richiede il caricamento di un file per ogni linguaggio. Puoi evidenziare soltanto il codice che intendi leggere e ridurre il traffico inutilizzato." +_hemisphere: + N: "Emisfero boreale" + S: "Emisfero australe" + caption: "Utile per alcune impostazioni del client, per determinare la stagione." +_reversi: + reversi: "Reversi" + gameSettings: "Impostazioni di gioco" + chooseBoard: "Segli la tavola" + blackOrWhite: "Neri / Bianchi" + blackIs: "{name} muove i Neri" + rules: "Regole del gioco" + thisGameIsStartedSoon: "Il gioco sta per iniziare" + waitingForOther: "Attendere l'avversario" + waitingForMe: "Ti stanno aspettando" + waitingBoth: "Preparatevi" + ready: "Pronti" + cancelReady: "Riprendere la preparazione" + opponentTurn: "Turno avversario" + myTurn: "Tocca a te" + turnOf: "Tocca a {name}" + pastTurnOf: "Turno di {name}" + surrender: "Mi arrendo" + surrendered: "Ha ceduto" + timeout: "Tempo scaduto" + drawn: "Pareggio" + won: "Ha vinto {name}" + black: "Neri" + white: "Bianchi" + total: "Totale" + turnCount: "Turno N. {count}" + myGames: "Le mie sfide" + allGames: "Tutte le sfide" + ended: "Conclusione" + playing: "In gioco" + isLlotheo: "Vince chi ha meno pietre (Roseo)" + loopedMap: "Mappa ricorsiva" + canPutEverywhere: "Modalità che può essere posizionata ovunque" + timeLimitForEachTurn: "Tempo limite per turno" + freeMatch: "Sfida libera" + lookingForPlayer: "Alla ricerca di un avversario" + gameCanceled: "Sfida cancellata" + shareToTlTheGameWhenStart: "Pubblica l'inizio della partita sulla tua Timeline" + iStartedAGame: "Inizia la sfida! #MisskeyReversi" + opponentHasSettingsChanged: "L'avversario ha cambiato configurazione" + allowIrregularRules: "Regole inconsuete (completamente libere)" + disallowIrregularRules: "Impedire le regole inconsuete" +_offlineScreen: + title: "Scollegato. Impossibile connettersi al server" + header: "Impossibile connettersi al server" + diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index b632fbad63ce8949e464b08e2e533a80ba2a7a02..57f52c64b21bbda98ec8b528f6a964c521cbae60 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -15,7 +15,7 @@ gotIt: "ã‚ã‹ã£ãŸ" cancel: "ã‚ャンセル" noThankYou: "ã‚„ã‚ã¦ãŠã" enterUsername: "ユーザーåを入力" -renotedBy: "{user}ãŒãƒªãƒŽãƒ¼ãƒˆ" +renotedBy: "{user}ãŒãƒ–ースト" noNotes: "ノートã¯ã‚ã‚Šã¾ã›ã‚“" noNotifications: "通知ã¯ã‚ã‚Šã¾ã›ã‚“" instance: "サーãƒãƒ¼" @@ -46,16 +46,16 @@ pin: "ピン留ã‚" unpin: "ピン留ã‚解除" copyContent: "内容をコピー" copyLink: "リンクをコピー" -copyLinkRenote: "リノートã®ãƒªãƒ³ã‚¯ã‚’コピー" +copyLinkRenote: "ブーストã®ãƒªãƒ³ã‚¯ã‚’コピー" delete: "削除" deleteAndEdit: "削除ã—ã¦ç·¨é›†" -deleteAndEditConfirm: "ã“ã®ãƒŽãƒ¼ãƒˆã‚’削除ã—ã¦ã‚‚ã†ä¸€åº¦ç·¨é›†ã—ã¾ã™ã‹ï¼Ÿã“ã®ãƒŽãƒ¼ãƒˆã¸ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã€ãƒªãƒŽãƒ¼ãƒˆã€è¿”ä¿¡ã‚‚å…¨ã¦å‰Šé™¤ã•ã‚Œã¾ã™ã€‚" +deleteAndEditConfirm: "ã“ã®ãƒŽãƒ¼ãƒˆã‚’削除ã—ã¦ã‚‚ã†ä¸€åº¦ç·¨é›†ã—ã¾ã™ã‹ï¼Ÿã“ã®ãƒŽãƒ¼ãƒˆã¸ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã€ãƒ–ーストã€è¿”ä¿¡ã‚‚å…¨ã¦å‰Šé™¤ã•ã‚Œã¾ã™ã€‚" addToList: "リストã«è¿½åŠ " addToAntenna: "アンテナã«è¿½åŠ " sendMessage: "メッセージをé€ä¿¡" copyRSS: "RSSをコピー" copyUsername: "ユーザーåをコピー" -openRemoteProfile: "リモートプãƒãƒ•ã‚¡ã‚¤ãƒ«ã‚’é–‹ã" +openRemoteProfile: "リモートプãƒãƒ•ã‚£ãƒ¼ãƒ«ã‚’é–‹ã" copyUserId: "ユーザーIDをコピー" copyNoteId: "ノートIDをコピー" copyFileId: "ファイルIDをコピー" @@ -107,15 +107,15 @@ followRequests: "フォãƒãƒ¼ç”³è«‹" unfollow: "フォãƒãƒ¼è§£é™¤" followRequestPending: "フォãƒãƒ¼è¨±å¯å¾…ã¡" enterEmoji: "絵文å—を入力" -renote: "リノート" -unrenote: "リノート解除" -renoted: "ブースト。" +renote: "ブースト" +unrenote: "ブースト解除" +renoted: "ブーストã—ã¾ã—ãŸã€‚" quoted: "引用。" -rmboost: "アンブースト。" -cantRenote: "ã“ã®æŠ•ç¨¿ã¯ãƒªãƒŽãƒ¼ãƒˆã§ãã¾ã›ã‚“。" -cantReRenote: "リノートをリノートã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。" +rmboost: "ブースト解除ã—ã¾ã—ãŸã€‚" +cantRenote: "ã“ã®æŠ•ç¨¿ã¯ãƒ–ーストã§ãã¾ã›ã‚“。" +cantReRenote: "ブーストをブーストã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。" quote: "引用" -inChannelRenote: "ãƒãƒ£ãƒ³ãƒãƒ«å†…リノート" +inChannelRenote: "ãƒãƒ£ãƒ³ãƒãƒ«å†…ブースト" inChannelQuote: "ãƒãƒ£ãƒ³ãƒãƒ«å†…引用" pinnedNote: "ピン留ã‚ã•ã‚ŒãŸãƒŽãƒ¼ãƒˆ" pinned: "ピン留ã‚" @@ -134,13 +134,14 @@ overwriteFromPinnedEmojis: "全般è¨å®šã‹ã‚‰ä¸Šæ›¸ãã™ã‚‹" reactionSettingDescription2: "ドラッグã—ã¦ä¸¦ã³æ›¿ãˆã€ã‚¯ãƒªãƒƒã‚¯ã—ã¦å‰Šé™¤ã€ï¼‹ã‚’押ã—ã¦è¿½åŠ ã—ã¾ã™ã€‚" rememberNoteVisibility: "公開範囲を記憶ã™ã‚‹" attachCancel: "添付å–り消ã—" +deleteFile: "ファイルを削除" markAsSensitive: "センシティブã¨ã—ã¦è¨å®š" unmarkAsSensitive: "センシティブを解除ã™ã‚‹" enterFileName: "ファイルåを入力" mute: "ミュート" unmute: "ミュート解除" -renoteMute: "リノートをミュート" -renoteUnmute: "リノートã®ãƒŸãƒ¥ãƒ¼ãƒˆã‚’解除" +renoteMute: "ブーストをミュート" +renoteUnmute: "ブーストã®ãƒŸãƒ¥ãƒ¼ãƒˆã‚’解除" block: "ブãƒãƒƒã‚¯" unblock: "ブãƒãƒƒã‚¯è§£é™¤" markAsNSFW: "ユーザーã®ã™ã¹ã¦ã®ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’NSFWã¨ã—ã¦ãƒžãƒ¼ã‚¯ã™ã‚‹" @@ -209,8 +210,8 @@ charts: "ãƒãƒ£ãƒ¼ãƒˆ" perHour: "1時間ã”ã¨" perDay: "1æ—¥ã”ã¨" stopActivityDelivery: "アクティビティã®é…é€ã‚’åœæ¢" -blockThisInstance: "ã“ã®ã‚µãƒ¼ãƒãƒ¼ã‚’ブãƒãƒƒã‚¯" -silenceThisInstance: "サーãƒãƒ¼ã‚’サイレンス" +blockThisInstance: "ã“ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã‚’ブãƒãƒƒã‚¯" +silenceThisInstance: "インスタンスをサイレンス" operations: "æ“作" software: "ソフトウェア" version: "ãƒãƒ¼ã‚¸ãƒ§ãƒ³" @@ -231,7 +232,7 @@ clearCachedFilesConfirm: "ã‚ャッシュã•ã‚ŒãŸãƒªãƒ¢ãƒ¼ãƒˆãƒ•ã‚¡ã‚¤ãƒ«ã‚’ã™ blockedInstances: "ブãƒãƒƒã‚¯ã—ãŸã‚µãƒ¼ãƒãƒ¼" blockedInstancesDescription: "ブãƒãƒƒã‚¯ã—ãŸã„サーãƒãƒ¼ã®ãƒ›ã‚¹ãƒˆã‚’改行ã§åŒºåˆ‡ã£ã¦è¨å®šã—ã¾ã™ã€‚ブãƒãƒƒã‚¯ã•ã‚ŒãŸã‚µãƒ¼ãƒãƒ¼ã¯ã€ã“ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã¨ã‚„ã‚Šå–ã‚Šã§ããªããªã‚Šã¾ã™ã€‚" silencedInstances: "サイレンスã—ãŸã‚µãƒ¼ãƒãƒ¼" -silencedInstancesDescription: "サイレンスã—ãŸã„サーãƒãƒ¼ã®ãƒ›ã‚¹ãƒˆã‚’改行ã§åŒºåˆ‡ã£ã¦è¨å®šã—ã¾ã™ã€‚サイレンスã•ã‚ŒãŸã‚µãƒ¼ãƒãƒ¼ã«æ‰€å±žã™ã‚‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ã™ã¹ã¦ã€Œã‚µã‚¤ãƒ¬ãƒ³ã‚¹ã€ã¨ã—ã¦æ‰±ã‚ã‚Œã€ãƒ•ã‚©ãƒãƒ¼ãŒã™ã¹ã¦ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«ãªã‚Šã€ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã§ãªã„ãƒãƒ¼ã‚«ãƒ«ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ã¯ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ã§ããªããªã‚Šã¾ã™ã€‚ブãƒãƒƒã‚¯ã—ãŸã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã«ã¯å½±éŸ¿ã—ã¾ã›ã‚“。" +silencedInstancesDescription: "サイレンスã—ãŸã„インスタンスã®ãƒ›ã‚¹ãƒˆã‚’改行ã§åŒºåˆ‡ã£ã¦è¨å®šã—ã¾ã™ã€‚サイレンスã•ã‚ŒãŸã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã«æ‰€å±žã™ã‚‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ã™ã¹ã¦ã€Œã‚µã‚¤ãƒ¬ãƒ³ã‚¹ã€ã¨ã—ã¦æ‰±ã‚ã‚Œã€ãƒ•ã‚©ãƒãƒ¼ãŒã™ã¹ã¦ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«ãªã‚Šã€ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã§ãªã„ãƒãƒ¼ã‚«ãƒ«ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ã¯ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ã§ããªããªã‚Šã¾ã™ã€‚ブãƒãƒƒã‚¯ã—ãŸã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã«ã¯å½±éŸ¿ã—ã¾ã›ã‚“。" muteAndBlock: "ミュートã¨ãƒ–ãƒãƒƒã‚¯" mutedUsers: "ミュートã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼" blockedUsers: "ブãƒãƒƒã‚¯ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼" @@ -390,6 +391,11 @@ hcaptcha: "hCaptcha" enableHcaptcha: "hCaptchaを有効ã«ã™ã‚‹" hcaptchaSiteKey: "サイトã‚ー" hcaptchaSecretKey: "シークレットã‚ー" +mcaptcha: "mCaptcha" +enableMcaptcha: "mCaptchaを有効ã«ã™ã‚‹" +mcaptchaSiteKey: "サイトã‚ー" +mcaptchaSecretKey: "シークレットã‚ー" +mcaptchaInstanceUrl: "mCaptchaã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã®URL" recaptcha: "reCAPTCHA" enableRecaptcha: "reCAPTCHAを有効ã«ã™ã‚‹" recaptchaSiteKey: "サイトã‚ー" @@ -550,6 +556,8 @@ objectStorageUseProxy: "Proxyを利用ã™ã‚‹" objectStorageUseProxyDesc: "API接続ã«proxyを利用ã—ãªã„å ´åˆã¯ã‚ªãƒ•ã«ã—ã¦ãã ã•ã„" objectStorageSetPublicRead: "アップãƒãƒ¼ãƒ‰æ™‚ã«'public-read'ã‚’è¨å®šã™ã‚‹" s3ForcePathStyleDesc: "s3ForcePathStyleを有効ã«ã™ã‚‹ã¨ã€ãƒã‚±ãƒƒãƒˆåã‚’URLã®ãƒ›ã‚¹ãƒˆåã§ã¯ãªãパスã®ä¸€éƒ¨ã¨ã—ã¦æŒ‡å®šã™ã‚‹ã“ã¨ã‚’強制ã—ã¾ã™ã€‚セルフホストã•ã‚ŒãŸMinioãªã©ã®ä½¿ç”¨æ™‚ã«æœ‰åŠ¹ã«ã™ã‚‹å¿…è¦ãŒã‚ã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ã€‚" +deeplFreeMode: "DeepLX-JS を使用ã™ã‚‹ (èªè¨¼ã‚ーãªã—)" +deeplFreeModeDescription: "ヘルプãŒå¿…è¦ã§ã™ã‹? DeepLX-JSã®ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—方法ã«ã¤ã„ã¦ã¯ã€ãƒ‰ã‚ュメントをå‚ç…§ã—ã¦ãã ã•ã„。" serverLogs: "サーãƒãƒ¼ãƒã‚°" deleteAll: "å…¨ã¦å‰Šé™¤" showFixedPostForm: "タイムライン上部ã«æŠ•ç¨¿ãƒ•ã‚©ãƒ¼ãƒ を表示ã™ã‚‹" @@ -640,6 +648,7 @@ medium: "ä¸" small: "å°" generateAccessToken: "アクセストークンã®ç™ºè¡Œ" permission: "権é™" +adminPermission: "管ç†è€…権é™" enableAll: "å…¨ã¦æœ‰åŠ¹ã«ã™ã‚‹" disableAll: "å…¨ã¦ç„¡åŠ¹ã«ã™ã‚‹" tokenRequested: "アカウントã¸ã®ã‚¢ã‚¯ã‚»ã‚¹è¨±å¯" @@ -683,13 +692,14 @@ useGlobalSettingDesc: "オンã«ã™ã‚‹ã¨ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®é€šçŸ¥è¨å®šãŒä½¿ other: "ãã®ä»–" regenerateLoginToken: "ãƒã‚°ã‚¤ãƒ³ãƒˆãƒ¼ã‚¯ãƒ³ã‚’å†ç”Ÿæˆ" regenerateLoginTokenDescription: "ãƒã‚°ã‚¤ãƒ³ã«ä½¿ç”¨ã•ã‚Œã‚‹å†…部トークンをå†ç”Ÿæˆã—ã¾ã™ã€‚通常ã“ã®æ“作を行ã†å¿…è¦ã¯ã‚ã‚Šã¾ã›ã‚“。å†ç”Ÿæˆã™ã‚‹ã¨ã€å…¨ã¦ã®ãƒ‡ãƒã‚¤ã‚¹ã§ãƒã‚°ã‚¢ã‚¦ãƒˆã•ã‚Œã¾ã™ã€‚" +theKeywordWhenSearchingForCustomEmoji: "カスタム絵文å—を検索ã™ã‚‹æ™‚ã®ã‚ーワードã«ãªã‚Šã¾ã™ã€‚" setMultipleBySeparatingWithSpace: "スペースã§åŒºåˆ‡ã£ã¦è¤‡æ•°è¨å®šã§ãã¾ã™ã€‚" fileIdOrUrl: "ファイルIDã¾ãŸã¯URL" behavior: "動作" sample: "サンプル" abuseReports: "é€šå ±" reportAbuse: "é€šå ±" -reportAbuseRenote: "ãƒªãƒŽãƒ¼ãƒˆã‚’é€šå ±" +reportAbuseRenote: "ãƒ–ãƒ¼ã‚¹ãƒˆã‚’é€šå ±" reportAbuseOf: "{name}ã‚’é€šå ±ã™ã‚‹" fillAbuseReportDescription: "é€šå ±ç†ç”±ã®è©³ç´°ã‚’記入ã—ã¦ãã ã•ã„。対象ã®ãƒŽãƒ¼ãƒˆãŒã‚ã‚‹å ´åˆã¯ãã®URLも記入ã—ã¦ãã ã•ã„。" abuseReported: "内容ãŒé€ä¿¡ã•ã‚Œã¾ã—ãŸã€‚ã”å ±å‘Šã‚ã‚ŠãŒã¨ã†ã”ã–ã„ã¾ã—ãŸã€‚" @@ -723,9 +733,9 @@ manageAccessTokens: "アクセストークンã®ç®¡ç†" accountInfo: "ã‚¢ã‚«ã‚¦ãƒ³ãƒˆæƒ…å ±" notesCount: "ノートã®æ•°" repliesCount: "返信ã—ãŸæ•°" -renotesCount: "リノートã—ãŸæ•°" +renotesCount: "ブーストã—ãŸæ•°" repliedCount: "返信ã•ã‚ŒãŸæ•°" -renotedCount: "リノートã•ã‚ŒãŸæ•°" +renotedCount: "ブーストã•ã‚ŒãŸæ•°" followingCount: "フォãƒãƒ¼æ•°" followersCount: "フォãƒãƒ¯ãƒ¼æ•°" sentReactionsCount: "リアクションã—ãŸæ•°" @@ -959,6 +969,10 @@ numberOfPageCache: "ページã‚ャッシュ数" numberOfPageCacheDescription: "多ãã™ã‚‹ã¨åˆ©ä¾¿æ€§ãŒå‘上ã—ã¾ã™ãŒã€è² è·ã¨ãƒ¡ãƒ¢ãƒªä½¿ç”¨é‡ãŒå¢—ãˆã¾ã™ã€‚" numberOfReplies: "スレッド内ã®è¿”ä¿¡æ•°" numberOfRepliesDescription: "ã“ã®æ•°å€¤ã‚’大ããã™ã‚‹ã¨ã€ã‚ˆã‚Šå¤šãã®è¿”ä¿¡ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ã“ã®å€¤ã‚’大ããã—ã™ãŽã‚‹ã¨ã€è¿”ä¿¡ãŒçª®å±ˆã«ãªã‚Šã€èªã‚ãªããªã‚‹ã“ã¨ãŒã‚ã‚Šã¾ã™ã€‚" +boostSettings: "ブーストè¨å®š" +showVisibilitySelectorOnBoost: "å¯è¦–性セレクタを表示" +showVisibilitySelectorOnBoostDescription: "無効ã®å ´åˆã€ä»¥ä¸‹ã§å®šç¾©ã•ã‚Œã‚‹ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã®å¯è¦–性ãŒä½¿ç”¨ã•ã‚Œã€ã‚»ãƒ¬ã‚¯ã‚¿ã¯è¡¨ç¤ºã•ã‚Œã¾ã›ã‚“。" +visibilityOnBoost: "デフォルトã®ãƒ–ーストå¯è¦–性ã®è¨å®š" logoutConfirm: "ãƒã‚°ã‚¢ã‚¦ãƒˆã—ã¾ã™ã‹ï¼Ÿ" lastActiveDate: "最終利用日時" statusbar: "ステータスãƒãƒ¼" @@ -1010,6 +1024,8 @@ neverShow: "今後表示ã—ãªã„" remindMeLater: "ã¾ãŸå¾Œã§" didYouLikeMisskey: "Sharkeyã‚’æ°—ã«å…¥ã£ã¦ã„ãŸã ã‘ã¾ã—ãŸã‹ï¼Ÿ" pleaseDonate: "Sharkeyã¯{host}ãŒä½¿ç”¨ã—ã¦ã„ã‚‹ç„¡æ–™ã®ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã§ã™ã€‚ã“ã‚Œã‹ã‚‰ã‚‚開発を続ã‘られるよã†ã«ã€ãœã²å¯„付をãŠé¡˜ã„ã—ã¾ã™ï¼" +pleaseDonateInstance: "インスタンス管ç†è€…ã¸ã®å¯„付ã«ã‚ˆã£ã¦{host}を直接サãƒãƒ¼ãƒˆã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚" +correspondingSourceIsAvailable: "対応ã™ã‚‹ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã¯{anchor}ã‹ã‚‰åˆ©ç”¨å¯èƒ½ã§ã™ã€‚" roles: "ãƒãƒ¼ãƒ«" role: "ãƒãƒ¼ãƒ«" noRole: "ãƒãƒ¼ãƒ«ã¯ã‚ã‚Šã¾ã›ã‚“" @@ -1036,7 +1052,10 @@ thisPostMayBeAnnoying: "ã“ã®æŠ•ç¨¿ã¯è¿·æƒ‘ã«ãªã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ thisPostMayBeAnnoyingHome: "ホームã«æŠ•ç¨¿" thisPostMayBeAnnoyingCancel: "ã‚„ã‚ã‚‹" thisPostMayBeAnnoyingIgnore: "ã“ã®ã¾ã¾æŠ•ç¨¿" -collapseRenotes: "見ãŸã“ã¨ã®ã‚るリノートをçœç•¥ã—ã¦è¡¨ç¤º" +thisPostIsMissingAltTextCancel: "ã‚„ã‚ã‚‹" +thisPostIsMissingAltTextIgnore: "ã“ã®ã¾ã¾æŠ•ç¨¿" +thisPostIsMissingAltText: "ã“ã®æŠ•ç¨¿ã«æ·»ä»˜ã•ã‚ŒãŸãƒ•ã‚¡ã‚¤ãƒ«ã® 1 ã¤ã«ä»£æ›¿ãƒ†ã‚ストãŒã‚ã‚Šã¾ã›ã‚“。ã™ã¹ã¦ã®æ·»ä»˜ãƒ•ã‚¡ã‚¤ãƒ«ã«ä»£æ›¿ãƒ†ã‚ストãŒå«ã¾ã‚Œã¦ã„ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。" +collapseRenotes: "見ãŸã“ã¨ã®ã‚るブーストをçœç•¥ã—ã¦è¡¨ç¤º" collapseFiles: "ファイルを折りãŸãŸã‚€" autoloadConversation: "返信ã«ä¼šè©±ã‚’èªã¿è¾¼ã‚€" internalServerError: "サーãƒãƒ¼å†…部エラー" @@ -1063,6 +1082,9 @@ resetPasswordConfirm: "パスワードリセットã—ã¾ã™ã‹ï¼Ÿ" sensitiveWords: "センシティブワード" sensitiveWordsDescription: "è¨å®šã—ãŸãƒ¯ãƒ¼ãƒ‰ãŒå«ã¾ã‚Œã‚‹ãƒŽãƒ¼ãƒˆã®å…¬é–‹ç¯„囲をホームã«ã—ã¾ã™ã€‚改行ã§åŒºåˆ‡ã£ã¦è¤‡æ•°è¨å®šã§ãã¾ã™ã€‚" sensitiveWordsDescription2: "スペースã§åŒºåˆ‡ã‚‹ã¨AND指定ã«ãªã‚Šã€ã‚ーワードをスラッシュã§å›²ã‚€ã¨æ£è¦è¡¨ç¾ã«ãªã‚Šã¾ã™ã€‚" +prohibitedWords: "ç¦æ¢ãƒ¯ãƒ¼ãƒ‰" +prohibitedWordsDescription: "è¨å®šã—ãŸãƒ¯ãƒ¼ãƒ‰ãŒå«ã¾ã‚Œã‚‹ãƒŽãƒ¼ãƒˆã‚’投稿ã—よã†ã¨ã—ãŸéš›ã€ã‚¨ãƒ©ãƒ¼ã¨ãªã‚‹ã‚ˆã†ã«ã—ã¾ã™ã€‚改行ã§åŒºåˆ‡ã£ã¦è¤‡æ•°è¨å®šã§ãã¾ã™ã€‚" +prohibitedWordsDescription2: "スペースã§åŒºåˆ‡ã‚‹ã¨AND指定ã«ãªã‚Šã€ã‚ーワードをスラッシュã§å›²ã‚€ã¨æ£è¦è¡¨ç¾ã«ãªã‚Šã¾ã™ã€‚" hiddenTags: "éžè¡¨ç¤ºãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°" hiddenTagsDescription: "è¨å®šã—ãŸã‚¿ã‚°ã‚’トレンドã«è¡¨ç¤ºã•ã›ãªã„よã†ã«ã—ã¾ã™ã€‚改行ã§åŒºåˆ‡ã£ã¦è¤‡æ•°è¨å®šã§ãã¾ã™ã€‚" notesSearchNotAvailable: "ノート検索ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。" @@ -1081,16 +1103,19 @@ limitWidthOfReaction: "リアクションã®æœ€å¤§æ¨ªå¹…を制é™ã—ã€ç¸®å°ã— noteIdOrUrl: "ノートIDã¾ãŸã¯URL" video: "å‹•ç”»" videos: "å‹•ç”»" +audio: "音声" +audioFiles: "音声" dataSaver: "データセーãƒãƒ¼" accountMigration: "アカウントã®ç§»è¡Œ" accountMoved: "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯æ–°ã—ã„アカウントã«ç§»è¡Œã—ã¾ã—ãŸï¼š" accountMovedShort: "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ç§»è¡Œã•ã‚Œã¦ã„ã¾ã™" operationForbidden: "ã“ã®æ“作ã¯ã§ãã¾ã›ã‚“" forceShowAds: "常ã«åºƒå‘Šã‚’表示ã™ã‚‹" +oneko: "猫å‹é” :3" addMemo: "ãƒ¡ãƒ¢ã‚’è¿½åŠ " editMemo: "メモを編集" reactionsList: "リアクション一覧" -renotesList: "リノート一覧" +renotesList: "ブースト一覧" notificationDisplay: "通知ã®è¡¨ç¤º" leftTop: "左上" rightTop: "å³ä¸Š" @@ -1132,9 +1157,9 @@ installed: "インストール済ã¿" branding: "ブランディング" enableServerMachineStats: "サーãƒãƒ¼ã®ãƒžã‚·ãƒ³æƒ…å ±ã‚’å…¬é–‹ã™ã‚‹" enableAchievements: "実績を有効ã«ã™ã‚‹" -turnOffAchievements: "ã“れをオフã«ã™ã‚‹ã¨ã€é”æˆã‚·ã‚¹ãƒ†ãƒ ã¯ç„¡åŠ¹ã«ãªã‚Šã¾ã™ã€‚" -enableBotTrending: "ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ã«ãƒœãƒƒãƒˆã‚’è¿½åŠ ã™ã‚‹" -turnOffBotTrending: "ã“れをオフã«ã™ã‚‹ã¨ã€ãƒœãƒƒãƒˆãŒãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ã‚’入力ã—ãªããªã‚Šã¾ã™ã€‚" +turnOffAchievements: "オフã«ã™ã‚‹ã¨å®Ÿç¸¾ã‚·ã‚¹ãƒ†ãƒ ã¯ç„¡åŠ¹ã«ãªã‚Šã¾ã™ã€‚" +enableBotTrending: "botã®ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°è¿½åŠ を許å¯ã™ã‚‹" +turnOffBotTrending: "オフã«ã™ã‚‹ã¨ãƒœãƒƒãƒˆãŒãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ã‚’入力ã—ãªããªã‚Šã¾ã™ã€‚" enableIdenticonGeneration: "ユーザーã”ã¨ã®Identicon生æˆã‚’有効ã«ã™ã‚‹" turnOffToImprovePerformance: "オフã«ã™ã‚‹ã¨ãƒ‘フォーマンスãŒå‘上ã—ã¾ã™ã€‚" createInviteCode: "招待コードを作æˆ" @@ -1165,7 +1190,7 @@ pastAnnouncements: "éŽåŽ»ã®ãŠçŸ¥ã‚‰ã›" youHaveUnreadAnnouncements: "未èªã®ãŠçŸ¥ã‚‰ã›ãŒã‚ã‚Šã¾ã™ã€‚" useSecurityKey: "ブラウザã¾ãŸã¯ãƒ‡ãƒã‚¤ã‚¹ã®æŒ‡ç¤ºã«å¾“ã£ã¦ã€ã‚»ã‚ュリティã‚ーã¾ãŸã¯ãƒ‘スã‚ーを使用ã—ã¦ãã ã•ã„。" replies: "返信" -renotes: "リノート" +renotes: "ブースト" loadReplies: "返信を見る" loadConversation: "会話を見る" pinnedList: "ピン留ã‚ã•ã‚ŒãŸãƒªã‚¹ãƒˆ" @@ -1178,10 +1203,11 @@ unnotifyNotes: "投稿ã®é€šçŸ¥ã‚’解除" authentication: "èªè¨¼" authenticationRequiredToContinue: "続ã‘ã‚‹ã«ã¯èªè¨¼ã‚’è¡Œã£ã¦ãã ã•ã„" dateAndTime: "日時" -showRenotes: "リノートを表示" +showRenotes: "ブーストを表示" edited: "編集済ã¿" notificationRecieveConfig: "通知ã®å—ä¿¡è¨å®š" mutualFollow: "相互フォãƒãƒ¼" +followingOrFollower: "フォãƒãƒ¼ä¸ã¾ãŸã¯ãƒ•ã‚©ãƒãƒ¯ãƒ¼" fileAttachedOnly: "ファイル付ãã®ã¿" showRepliesToOthersInTimeline: "TLã«ä»–ã®äººã¸ã®è¿”ä¿¡ã‚’å«ã‚ã‚‹" hideRepliesToOthersInTimeline: "TLã«ä»–ã®äººã¸ã®è¿”ä¿¡ã‚’å«ã‚ãªã„" @@ -1190,12 +1216,21 @@ hideRepliesToOthersInTimelineAll: "TLã«ç¾åœ¨ãƒ•ã‚©ãƒãƒ¼ä¸ã®äººå…¨å“¡ã®è¿” confirmShowRepliesAll: "ã“ã®æ“作ã¯å…ƒã«æˆ»ã›ã¾ã›ã‚“。本当ã«TLã«ç¾åœ¨ãƒ•ã‚©ãƒãƒ¼ä¸ã®äººå…¨å“¡ã®è¿”ä¿¡ã‚’å«ã‚るよã†ã«ã—ã¾ã™ã‹ï¼Ÿ" confirmHideRepliesAll: "ã“ã®æ“作ã¯å…ƒã«æˆ»ã›ã¾ã›ã‚“。本当ã«TLã«ç¾åœ¨ãƒ•ã‚©ãƒãƒ¼ä¸ã®äººå…¨å“¡ã®è¿”ä¿¡ã‚’å«ã‚ãªã„よã†ã«ã—ã¾ã™ã‹ï¼Ÿ" externalServices: "外部サービス" +sourceCode: "ソースコード" +sourceCodeIsNotYetProvided: "ソースコードã¯ã¾ã æä¾›ã•ã‚Œã¦ã„ã¾ã›ã‚“。ã“ã®å•é¡Œã®ä¿®æ£ã«ã¤ã„ã¦ç®¡ç†è€…ã«å•ã„åˆã‚ã›ã¦ãã ã•ã„。" +repositoryUrl: "リãƒã‚¸ãƒˆãƒªURL" +repositoryUrlDescription: "ソースコードãŒå…¬é–‹ã•ã‚Œã¦ã„るリãƒã‚¸ãƒˆãƒªãŒã‚ã‚‹å ´åˆã€ãã®URLを記入ã—ã¾ã™ã€‚Misskeyã‚’ç¾çŠ¶ã®ã¾ã¾ï¼ˆã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã«ã„ã‹ãªã‚‹å¤‰æ›´ã‚‚åŠ ãˆãšã«ï¼‰ä½¿ç”¨ã—ã¦ã„ã‚‹å ´åˆã¯ https://github.com/misskey-dev/misskey ã¨è¨˜å…¥ã—ã¾ã™ã€‚" +repositoryUrlOrTarballRequired: "リãƒã‚¸ãƒˆãƒªã‚’公開ã—ã¦ã„ãªã„å ´åˆã€ä»£ã‚ã‚Šã«tarballã‚’æä¾›ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚詳細ã¯.config/example.ymlã‚’å‚ç…§ã—ã¦ãã ã•ã„。" +feedback: "フィードãƒãƒƒã‚¯" +feedbackUrl: "フィードãƒãƒƒã‚¯URL" impressum: "é‹å–¶è€…æƒ…å ±" impressumUrl: "é‹å–¶è€…æƒ…å ±URL" impressumDescription: "ドイツãªã©ã®ä¸€éƒ¨ã®å›½ã¨åœ°åŸŸã§ã¯è¡¨ç¤ºãŒç¾©å‹™ä»˜ã‘られã¦ã„ã¾ã™(Impressum)。" privacyPolicy: "プライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼" privacyPolicyUrl: "プライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼URL" tosAndPrivacyPolicy: "利用è¦ç´„・プライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼" +donation: "寄付ã™ã‚‹" +donationUrl: "寄付URL" avatarDecorations: "アイコンデコレーション" attach: "付ã‘ã‚‹" detach: "外ã™" @@ -1219,6 +1254,40 @@ seasonalScreenEffect: "å£ç¯€ã«å¿œã˜ãŸç”»é¢ã®æ¼”出" decorate: "デコる" addMfmFunction: "è£…é£¾ã‚’è¿½åŠ " enableQuickAddMfmFunction: "高度ãªMFMã®ãƒ”ッカーを表示ã™ã‚‹" +bubbleGame: "ãƒãƒ–ルゲーム" +sfx: "効果音" +soundWillBePlayed: "サウンドãŒå†ç”Ÿã•ã‚Œã¾ã™" +showReplay: "リプレイを見る" +replay: "リプレイ" +replaying: "リプレイä¸" +endReplay: "リプレイを終了" +copyReplayData: "リプレイデータをコピー" +ranking: "ランã‚ング" +lastNDays: "ç›´è¿‘{n}æ—¥" +backToTitle: "タイトルã¸" +hemisphere: "ãŠä½ã¾ã„ã®åœ°åŸŸ" +withSensitive: "センシティブãªãƒ•ã‚¡ã‚¤ãƒ«ã‚’å«ã‚€ãƒŽãƒ¼ãƒˆã‚’表示" +userSaysSomethingSensitive: "{name}ã®ã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ãªãƒ•ã‚¡ã‚¤ãƒ«ã‚’å«ã‚€æŠ•ç¨¿" +enableHorizontalSwipe: "スワイプã—ã¦ã‚¿ãƒ–を切り替ãˆã‚‹" +loading: "èªã¿è¾¼ã¿ä¸" +surrender: "ã‚„ã‚ã‚‹" +gameRetry: "リトライ" + +_bubbleGame: + howToPlay: "éŠã³æ–¹" + hold: "ホールド" + _score: + score: "スコア" + scoreYen: "稼ã„ã 金é¡" + highScore: "ãƒã‚¤ã‚¹ã‚³ã‚¢" + maxChain: "最大ãƒã‚§ãƒ¼ãƒ³æ•°" + yen: "{yen}円" + estimatedQty: "{qty}個分" + scoreSweets: "ãŠã«ãŽã‚Š {onigiriQtyWithUnit}" + _howToPlay: + section1: "ä½ç½®ã‚’調整ã—ã¦ãƒã‚³ã«ãƒ¢ãƒŽã‚’è½ã¨ã—ã¾ã™ã€‚" + section2: "åŒã˜ç¨®é¡žã®ãƒ¢ãƒŽãŒãã£ã¤ãã¨åˆ¥ã®ãƒ¢ãƒŽã«å¤‰åŒ–ã—ã¦ã€ã‚¹ã‚³ã‚¢ãŒå¾—られã¾ã™ã€‚" + section3: "モノãŒãƒã‚³ã‹ã‚‰ã‚ãµã‚Œã‚‹ã¨ã‚²ãƒ¼ãƒ オーãƒãƒ¼ã§ã™ã€‚ãƒã‚³ã‹ã‚‰ã‚ãµã‚Œãªã„よã†ã«ã—ã¤ã¤ãƒ¢ãƒŽã‚’èžåˆã•ã›ã¦ãƒã‚¤ã‚¹ã‚³ã‚¢ã‚’目指ãã†ï¼" _announcement: forExistingUsers: "æ—¢å˜ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã¿" @@ -1229,7 +1298,7 @@ _announcement: tooManyActiveAnnouncementDescription: "アクティブãªãŠçŸ¥ã‚‰ã›ãŒå¤šã„ãŸã‚ã€UXãŒä½Žä¸‹ã™ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚終了ã—ãŸãŠçŸ¥ã‚‰ã›ã¯ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–ã™ã‚‹ã“ã¨ã‚’検討ã—ã¦ãã ã•ã„。" readConfirmTitle: "æ—¢èªã«ã—ã¾ã™ã‹ï¼Ÿ" readConfirmText: "「{title}ã€ã®å†…容をèªã¿ã€æ—¢èªã«ã—ã¾ã™ã€‚" - shouldNotBeUsedToPresentPermanentInfo: "特ã«æ–°è¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®UXã‚’æãã‚‹å¯èƒ½æ€§ãŒé«˜ã„ãŸã‚ã€ã‚¹ãƒˆãƒƒã‚¯æƒ…å ±ã§ã¯ãªãフãƒãƒ¼æƒ…å ±ã®æŽ²ç¤ºã«ãŠçŸ¥ã‚‰ã›ã‚’使用ã™ã‚‹ã“ã¨ã‚’推奨ã—ã¾ã™ã€‚" + shouldNotBeUsedToPresentPermanentInfo: "特ã«æ–°è¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®UXã‚’æãã‚‹å¯èƒ½æ€§ãŒé«˜ã„ãŸã‚ã€å¸¸æ™‚掲示ã™ã‚‹ãŸã‚ã®æƒ…å ±ã§ã¯ãªãã€å³æ™‚性ãŒæ±‚ã‚ã‚‰ã‚Œã‚‹æƒ…å ±ã®æŽ²ç¤ºã®ãŸã‚ã«ãŠçŸ¥ã‚‰ã›ã‚’使用ã™ã‚‹ã“ã¨ã‚’推奨ã—ã¾ã™ã€‚" dialogAnnouncementUxWarn: "ダイアãƒã‚°å½¢å¼ã®ãŠçŸ¥ã‚‰ã›ãŒåŒæ™‚ã«2ã¤ä»¥ä¸Šã‚ã‚‹å ´åˆã€UXã«æ‚ªå½±éŸ¿ã‚’åŠã¼ã™å¯èƒ½æ€§ãŒéžå¸¸ã«é«˜ã„ãŸã‚ã€ä½¿ç”¨ã¯æ…Žé‡ã«è¡Œã†ã“ã¨ã‚’推奨ã—ã¾ã™ã€‚" silence: "éžé€šçŸ¥" silenceDescription: "オンã«ã™ã‚‹ã¨ã€ã“ã®ãŠçŸ¥ã‚‰ã›ã¯é€šçŸ¥ã•ã‚Œãšã€æ—¢èªã«ã™ã‚‹å¿…è¦ã‚‚ãªããªã‚Šã¾ã™ã€‚" @@ -1288,8 +1357,8 @@ _initialTutorial: _visibility: description: "ノートを表示ã§ãる相手を制é™ã§ãã¾ã™ã€‚" public: "ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«å…¬é–‹ã€‚" - home: "ホームタイムラインã®ã¿ã«å…¬é–‹ã€‚フォãƒãƒ¯ãƒ¼ãƒ»ãƒ—ãƒãƒ•ã‚£ãƒ¼ãƒ«ã‚’見ã«æ¥ãŸäººãƒ»ãƒªãƒŽãƒ¼ãƒˆã‹ã‚‰ã€ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚‚見るã“ã¨ãŒã§ãã¾ã™ã€‚" - followers: "フォãƒãƒ¯ãƒ¼ã«ã®ã¿å…¬é–‹ã€‚本人以外ãŒãƒªãƒŽãƒ¼ãƒˆã™ã‚‹ã“ã¨ã¯ã§ããšã€ã¾ãŸãƒ•ã‚©ãƒãƒ¯ãƒ¼ä»¥å¤–ã¯é–²è¦§ã§ãã¾ã›ã‚“。" + home: "ホームタイムラインã®ã¿ã«å…¬é–‹ã€‚フォãƒãƒ¯ãƒ¼ãƒ»ãƒ—ãƒãƒ•ã‚£ãƒ¼ãƒ«ã‚’見ã«æ¥ãŸäººãƒ»ãƒ–ーストã‹ã‚‰ã€ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚‚見るã“ã¨ãŒã§ãã¾ã™ã€‚" + followers: "フォãƒãƒ¯ãƒ¼ã«ã®ã¿å…¬é–‹ã€‚本人以外ãŒãƒ–ーストã™ã‚‹ã“ã¨ã¯ã§ããšã€ã¾ãŸãƒ•ã‚©ãƒãƒ¯ãƒ¼ä»¥å¤–ã¯é–²è¦§ã§ãã¾ã›ã‚“。" direct: "指定ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã®ã¿å…¬é–‹ã•ã‚Œã€ã¾ãŸç›¸æ‰‹ã«é€šçŸ¥ãŒå…¥ã‚Šã¾ã™ã€‚ダイレクトメッセージã®ã‹ã‚ã‚Šã«ãŠä½¿ã„ã„ãŸã ã‘ã¾ã™ã€‚" doNotSendConfidencialOnDirect1: "æ©Ÿå¯†æƒ…å ±ã¯é€ä¿¡ã™ã‚‹éš›ã¯æ³¨æ„ã—ã¦ãã ã•ã„。" doNotSendConfidencialOnDirect2: "é€ä¿¡å…ˆã®ã‚µãƒ¼ãƒãƒ¼ã®ç®¡ç†è€…ã¯æŠ•ç¨¿å†…容を見るã“ã¨ãŒå¯èƒ½ãªã®ã§ã€ä¿¡é ¼ã§ããªã„サーãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆæŠ•ç¨¿ã‚’é€ä¿¡ã™ã‚‹å ´åˆã¯ã€æ©Ÿå¯†æƒ…å ±ã®æ‰±ã„ã«æ³¨æ„ãŒå¿…è¦ã§ã™ã€‚" @@ -1597,6 +1666,13 @@ _achievements: _tutorialCompleted: title: "Sharkeyåˆå¿ƒè€…講座 修了証" description: "ãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ«ã‚’完了ã—ãŸ" + _bubbleGameExplodingHead: + title: "🤯" + description: "ãƒãƒ–ルゲームã§æœ€ã‚‚大ãã„モノを出ã—ãŸ" + _bubbleGameDoubleExplodingHead: + title: "ダブル🤯" + description: "ãƒãƒ–ルゲームã§æœ€ã‚‚大ãã„モノを2ã¤åŒæ™‚ã«å‡ºã—ãŸ" + flavor: "ã“ã‚Œãらã„ã®ã€€ãŠã¹ã‚“ã¨ã°ã“ã«ã€€ðŸ¤¯ã€€ðŸ¤¯ã€€ã¡ã‚‡ã£ã¨ã¤ã‚ã¦" _role: new: "ãƒãƒ¼ãƒ«ã®ä½œæˆ" @@ -1636,10 +1712,11 @@ _role: high: "高" _options: gtlAvailable: "ã‚°ãƒãƒ¼ãƒãƒ«ã‚¿ã‚¤ãƒ ラインã®é–²è¦§" - btlAvailable: "ãƒãƒ–ルã®ã‚¿ã‚¤ãƒ ラインを見るã“ã¨ãŒã§ãã‚‹" + btlAvailable: "ãƒãƒ–ルタイムラインã®é–²è¦§" ltlAvailable: "ãƒãƒ¼ã‚«ãƒ«ã‚¿ã‚¤ãƒ ラインã®é–²è¦§" canPublicNote: "パブリック投稿ã®è¨±å¯" canImportNotes: "ノートã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆãŒå¯èƒ½" + mentionMax: "ノート内ã®æœ€å¤§ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³æ•°" canInvite: "サーãƒãƒ¼æ‹›å¾…コードã®ç™ºè¡Œ" inviteLimit: "招待コードã®ä½œæˆå¯èƒ½æ•°" inviteLimitCycle: "招待コードã®ç™ºè¡Œé–“éš”" @@ -1663,6 +1740,7 @@ _role: canUseTranslator: "翻訳機能ã®åˆ©ç”¨" avatarDecorationLimit: "アイコンデコレーションã®æœ€å¤§å–付個数" _condition: + roleAssignedTo: "マニュアルãƒãƒ¼ãƒ«ã«ã‚¢ã‚µã‚¤ãƒ³æ¸ˆã¿" isLocal: "ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼" isRemote: "リモートユーザー" createdLessThan: "アカウント作æˆã‹ã‚‰ï½žä»¥å†…" @@ -1774,12 +1852,16 @@ _registry: createKey: "ã‚ーを作æˆ" _aboutMisskey: - about: "Sharkeyã¯ã€2014å¹´ã‹ã‚‰syuiloã«ã‚ˆã£ã¦é–‹ç™ºã•ã‚Œã¦ã„ã‚‹Misskeyをベースã«ã—ãŸã‚ªãƒ¼ãƒ—ンソースã®ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã§ã™ã€‚" + about: "Sharkeyã¯ã€Misskeyをベースã«ã—ãŸã‚ªãƒ¼ãƒ—ンソースã®ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã§ã™ã€‚" contributors: "主ãªã‚³ãƒ³ãƒˆãƒªãƒ“ューター" allContributors: "å…¨ã¦ã®ã‚³ãƒ³ãƒˆãƒªãƒ“ューター" source: "ソースコード" + original: "Misskey オリジナル" + original_sharkey: "Sharkey オリジナル" + thisIsModifiedVersion: "{name}ã¯ã‚ªãƒªã‚¸ãƒŠãƒ«ã®Sharkeyを改変ã—ãŸãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’使用ã—ã¦ã„ã¾ã™ã€‚" translation: "Sharkeyを翻訳" - donate: "Sharkeyã«å¯„付" + donate: "Misskeyã«å¯„付" + donate_sharkey: "Sharkeyã«å¯„付" morePatrons: "ä»–ã«ã‚‚多ãã®æ–¹ãŒæ”¯æ´ã—ã¦ãã‚Œã¦ã„ã¾ã™ã€‚ã‚ã‚ŠãŒã¨ã†ã”ã–ã„ã¾ã™ðŸ¥°" patrons: "支æ´è€…" projectMembers: "プãƒã‚¸ã‚§ã‚¯ãƒˆãƒ¡ãƒ³ãƒãƒ¼" @@ -1812,7 +1894,7 @@ _channel: notesCount: "{n}投稿ãŒã‚ã‚Šã¾ã™" nameAndDescription: "åå‰ã¨èª¬æ˜Ž" nameOnly: "åå‰ã®ã¿" - allowRenoteToExternal: "ãƒãƒ£ãƒ³ãƒãƒ«å¤–ã¸ã®ãƒªãƒŽãƒ¼ãƒˆã¨å¼•ç”¨ãƒªãƒŽãƒ¼ãƒˆã‚’許å¯ã™ã‚‹" + allowRenoteToExternal: "ãƒãƒ£ãƒ³ãƒãƒ«å¤–ã¸ã®ãƒ–ーストã¨å¼•ç”¨ãƒ–ーストを許å¯ã™ã‚‹" _menuDisplay: sideFull: "横" @@ -1826,7 +1908,7 @@ _wordMute: muteWordsDescription2: "ã‚ーワードをスラッシュã§å›²ã‚€ã¨æ£è¦è¡¨ç¾ã«ãªã‚Šã¾ã™ã€‚" _instanceMute: - instanceMuteDescription: "ミュートã—ãŸã‚µãƒ¼ãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¸ã®è¿”ä¿¡ã‚’å«ã‚ã¦ã€è¨å®šã—ãŸã‚µãƒ¼ãƒãƒ¼ã®å…¨ã¦ã®ãƒŽãƒ¼ãƒˆã¨Renoteをミュートã—ã¾ã™ã€‚" + instanceMuteDescription: "ミュートã—ãŸã‚µãƒ¼ãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¸ã®è¿”ä¿¡ã‚’å«ã‚ã¦ã€è¨å®šã—ãŸã‚µãƒ¼ãƒãƒ¼ã®å…¨ã¦ã®ãƒŽãƒ¼ãƒˆã¨ãƒ–ーストをミュートã—ã¾ã™ã€‚" instanceMuteDescription2: "改行ã§åŒºåˆ‡ã£ã¦è¨å®šã—ã¾ã™" title: "è¨å®šã—ãŸã‚µãƒ¼ãƒãƒ¼ã®ãƒŽãƒ¼ãƒˆã‚’éš ã—ã¾ã™ã€‚" heading: "ミュートã™ã‚‹ã‚µãƒ¼ãƒãƒ¼" @@ -1880,7 +1962,7 @@ _theme: hashtag: "ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°" mention: "メンション" mentionMe: "ã‚ãªãŸå®›ã¦ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³" - renote: "Renote" + renote: "Boost" modalBg: "モーダルã®èƒŒæ™¯" divider: "分割線" scrollbarHandle: "スクãƒãƒ¼ãƒ«ãƒãƒ¼ã®å–ã£æ‰‹" @@ -2190,13 +2272,18 @@ _profile: metadataContent: "内容" changeAvatar: "アイコン画åƒã‚’変更" changeBanner: "ãƒãƒŠãƒ¼ç”»åƒã‚’変更" + updateBanner: "æ›´æ–°ãƒãƒŠãƒ¼" + removeBanner: "ãƒãƒŠãƒ¼ã‚’削除" changeBackground: "背景を変更ã™ã‚‹" + updateBackground: "背景を更新ã™ã‚‹" + removeBackground: "背景を削除ã™ã‚‹" verifiedLinkDescription: "内容ã«URLã‚’è¨å®šã™ã‚‹ã¨ã€ãƒªãƒ³ã‚¯å…ˆã®Webサイトã«è‡ªåˆ†ã®ãƒ—ãƒãƒ•ã‚£ãƒ¼ãƒ«ã¸ã®ãƒªãƒ³ã‚¯ãŒå«ã¾ã‚Œã¦ã„ã‚‹å ´åˆã«æ‰€æœ‰è€…確èªæ¸ˆã¿ã‚¢ã‚¤ã‚³ãƒ³ã‚’表示ã•ã›ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚" avatarDecorationMax: "最大{max}ã¤ã¾ã§ãƒ‡ã‚³ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã‚’付ã‘られã¾ã™ã€‚" _exportOrImport: allNotes: "å…¨ã¦ã®ãƒŽãƒ¼ãƒˆ" favoritedNotes: "ãŠæ°—ã«å…¥ã‚Šã«ã—ãŸãƒŽãƒ¼ãƒˆ" + clips: "クリップ" followingList: "フォãƒãƒ¼" muteList: "ミュート" blockingList: "ブãƒãƒƒã‚¯" @@ -2316,11 +2403,12 @@ _notification: youGotMention: "{name}ã‹ã‚‰ã®ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³" youGotReply: "{name}ã‹ã‚‰ã®ãƒªãƒ—ライ" youGotQuote: "{name}ã«ã‚ˆã‚‹å¼•ç”¨" - youRenoted: "{name}ãŒRenoteã—ã¾ã—ãŸ" + youRenoted: "{name}ãŒBoostã—ã¾ã—ãŸ" youWereFollowed: "フォãƒãƒ¼ã•ã‚Œã¾ã—ãŸ" youReceivedFollowRequest: "フォãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒæ¥ã¾ã—ãŸ" yourFollowRequestAccepted: "フォãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒæ‰¿èªã•ã‚Œã¾ã—ãŸ" pollEnded: "アンケートã®çµæžœãŒå‡ºã¾ã—ãŸ" + edited: "投稿ãŒç·¨é›†ã•ã‚Œã¾ã—ãŸ" newNote: "æ–°ã—ã„投稿" unreadAntennaNote: "アンテナ {name}" roleAssigned: "ãƒãƒ¼ãƒ«ãŒä»˜ä¸Žã•ã‚Œã¾ã—ãŸ" @@ -2331,8 +2419,9 @@ _notification: sendTestNotification: "テスト通知をé€ä¿¡ã™ã‚‹" notificationWillBeDisplayedLikeThis: "通知ã¯ã“ã®ã‚ˆã†ã«è¡¨ç¤ºã•ã‚Œã¾ã™" reactedBySomeUsers: "{n}人ãŒãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã—ã¾ã—ãŸ" - renotedBySomeUsers: "{n}人ãŒãƒªãƒŽãƒ¼ãƒˆã—ã¾ã—ãŸ" + renotedBySomeUsers: "{n}人ãŒãƒ–ーストã—ã¾ã—ãŸ" followedBySomeUsers: "{n}人ã«ãƒ•ã‚©ãƒãƒ¼ã•ã‚Œã¾ã—ãŸ" + flushNotification: "通知ã®å±¥æ´ã‚’リセットã™ã‚‹" _types: all: "ã™ã¹ã¦" @@ -2340,7 +2429,7 @@ _notification: follow: "フォãƒãƒ¼" mention: "メンション" reply: "リプライ" - renote: "Renote" + renote: "Boost" quote: "引用" reaction: "リアクション" pollEnded: "アンケートãŒçµ‚了" @@ -2353,7 +2442,7 @@ _notification: _actions: followBack: "フォãƒãƒ¼ãƒãƒƒã‚¯" reply: "返信" - renote: "Renote" + renote: "Boost" _deck: alwaysShowMainColumn: "常ã«ãƒ¡ã‚¤ãƒ³ã‚«ãƒ©ãƒ を表示" @@ -2411,7 +2500,7 @@ _webhookSettings: followed: "フォãƒãƒ¼ã•ã‚ŒãŸã¨ã" note: "ノートを投稿ã—ãŸã¨ã" reply: "返信ã•ã‚ŒãŸã¨ã" - renote: "Renoteã•ã‚ŒãŸã¨ã" + renote: "Boostã•ã‚ŒãŸã¨ã" reaction: "リアクションãŒã‚ã£ãŸã¨ã" mention: "メンションã•ã‚ŒãŸã¨ã" @@ -2428,7 +2517,7 @@ _moderationLogTypes: updateCustomEmoji: "カスタム絵文å—æ›´æ–°" deleteCustomEmoji: "カスタム絵文å—削除" updateServerSettings: "サーãƒãƒ¼è¨å®šæ›´æ–°" - updateUserNote: "モデレーションノート更新" + updateUserNote: "ユーザーã®ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãƒŽãƒ¼ãƒˆæ›´æ–°" deleteDriveFile: "ファイルを削除" deleteNote: "ノートを削除" createGlobalAnnouncement: "全体ã®ãŠçŸ¥ã‚‰ã›ã‚’作æˆ" @@ -2440,6 +2529,7 @@ _moderationLogTypes: resetPassword: "パスワードをリセット" suspendRemoteInstance: "リモートサーãƒãƒ¼ã‚’åœæ¢" unsuspendRemoteInstance: "リモートサーãƒãƒ¼ã‚’å†é–‹" + updateRemoteInstanceNote: "リモートサーãƒãƒ¼ã®ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãƒŽãƒ¼ãƒˆæ›´æ–°" markSensitiveDriveFile: "ファイルをセンシティブ付与" unmarkSensitiveDriveFile: "ファイルをセンシティブ解除" resolveAbuseReport: "é€šå ±ã‚’è§£æ±º" @@ -2508,15 +2598,15 @@ _animatedMFM: play: "MFMアニメーションをå†ç”Ÿ" stop: "MFMアニメーションåœæ¢" _alert: - text: "アニメーションMFMã«ã¯ã€ç‚¹æ»…ã™ã‚‹ãƒ©ã‚¤ãƒˆã‚„高速ã§å‹•ãテã‚ストï¼çµµæ–‡å—ã‚’å«ã‚ã‚‹ã“ã¨ãŒã§ãる。" - confirm: "アニメイト" + text: "MFMアニメーションã«ã¯ã€ç‚¹æ»…ã™ã‚‹ãƒ©ã‚¤ãƒˆã‚„高速ã§å‹•ãテã‚スト/絵文å—ã‚’å«ã¾ã‚Œã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ã€‚" + confirm: "å†ç”Ÿã™ã‚‹" _dataRequest: - title: "リクエストデータ" - warn: "データã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã¯3æ—¥ã”ã¨ã«ã—ã‹ã§ããªã„。" - text: "データã®ãƒ€ã‚¦ãƒ³ãƒãƒ¼ãƒ‰ãŒå®Œäº†ã™ã‚‹ã¨ã€ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ç™»éŒ²ã•ã‚Œã¦ã„ã‚‹Eメールアドレスã«EメールãŒé€ä¿¡ã•ã‚Œã¾ã™ã€‚" + title: "データリクエスト" + warn: "データリクエストã¯3æ—¥ã”ã¨ã«å¯èƒ½ã§ã™ã€‚" + text: "データã®ä¿å˜ãŒå®Œäº†ã™ã‚‹ã¨ã€ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ç™»éŒ²ã•ã‚Œã¦ã„ã‚‹Eメールアドレスã«ãƒ¡ãƒ¼ãƒ«ãŒé€ä¿¡ã•ã‚Œã¾ã™ã€‚" button: "リクエスト" - + _dataSaver: _media: title: "メディアã®èªã¿è¾¼ã¿" @@ -2530,3 +2620,57 @@ _dataSaver: _code: title: "コードãƒã‚¤ãƒ©ã‚¤ãƒˆ" description: "MFMãªã©ã§ã‚³ãƒ¼ãƒ‰ãƒã‚¤ãƒ©ã‚¤ãƒˆè¨˜æ³•ãŒä½¿ã‚ã‚Œã¦ã„ã‚‹å ´åˆã€ã‚¿ãƒƒãƒ—ã™ã‚‹ã¾ã§èªã¿è¾¼ã¾ã‚Œãªããªã‚Šã¾ã™ã€‚コードãƒã‚¤ãƒ©ã‚¤ãƒˆã§ã¯ãƒã‚¤ãƒ©ã‚¤ãƒˆã™ã‚‹è¨€èªžã”ã¨ã«ãã®å®šç¾©ãƒ•ã‚¡ã‚¤ãƒ«ã‚’èªã¿è¾¼ã‚€å¿…è¦ãŒã‚ã‚Šã¾ã™ãŒã€ãれらãŒè‡ªå‹•ã§èªã¿è¾¼ã¾ã‚Œãªããªã‚‹ãŸã‚ã€é€šä¿¡é‡ã®å‰Šæ¸›ãŒè¦‹è¾¼ã‚ã¾ã™ã€‚" + +_hemisphere: + N: "北åŠçƒ" + S: "å—åŠçƒ" + caption: "一部ã®ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆè¨å®šã§ã€å£ç¯€ã‚’判定ã™ã‚‹ãŸã‚ã«ä½¿ç”¨ã—ã¾ã™ã€‚" + +_reversi: + reversi: "リãƒãƒ¼ã‚·" + gameSettings: "対局ã®è¨å®š" + chooseBoard: "ボードをé¸æŠž" + blackOrWhite: "先行/後攻" + blackIs: "{name}ãŒé»’(先行)" + rules: "ルール" + thisGameIsStartedSoon: "対局ã¯ã¾ã‚‚ãªã開始ã•ã‚Œã¾ã™" + waitingForOther: "相手ã®æº–å‚™ãŒå®Œäº†ã™ã‚‹ã®ã‚’å¾…ã£ã¦ã„ã¾ã™" + waitingForMe: "ã‚ãªãŸã®æº–å‚™ãŒå®Œäº†ã™ã‚‹ã®ã‚’å¾…ã£ã¦ã„ã¾ã™" + waitingBoth: "準備ã—ã¦ãã ã•ã„" + ready: "準備完了" + cancelReady: "準備をå†é–‹" + opponentTurn: "相手ã®ã‚¿ãƒ¼ãƒ³ã§ã™" + myTurn: "ã‚ãªãŸã®ã‚¿ãƒ¼ãƒ³ã§ã™" + turnOf: "{name}ã®ã‚¿ãƒ¼ãƒ³ã§ã™" + pastTurnOf: "{name}ã®ã‚¿ãƒ¼ãƒ³" + surrender: "投了" + surrendered: "投了ã«ã‚ˆã‚Š" + timeout: "時間切れ" + drawn: "引ã分ã‘" + won: "{name}ã®å‹ã¡" + black: "é»’" + white: "白" + total: "åˆè¨ˆ" + turnCount: "{count}ターン目" + myGames: "自分ã®å¯¾å±€" + allGames: "ã¿ã‚“ãªã®å¯¾å±€" + ended: "終了" + playing: "対局ä¸" + isLlotheo: "石ã®å°‘ãªã„æ–¹ãŒå‹ã¡(ãƒã‚»ã‚ª)" + loopedMap: "ループマップ" + canPutEverywhere: "ã©ã“ã§ã‚‚ç½®ã‘るモード" + timeLimitForEachTurn: "1ターンã®æ™‚間制é™" + freeMatch: "フリーマッãƒ" + lookingForPlayer: "対戦相手を探ã—ã¦ã„ã¾ã™" + gameCanceled: "対局ãŒã‚ャンセルã•ã‚Œã¾ã—ãŸ" + shareToTlTheGameWhenStart: "開始時ã«å¯¾å±€ã‚’タイムラインã«æŠ•ç¨¿" + iStartedAGame: "対局を開始ã—ã¾ã—ãŸï¼ #MisskeyReversi" + opponentHasSettingsChanged: "相手ãŒè¨å®šã‚’変更ã—ã¾ã—ãŸ" + allowIrregularRules: "å¤‰å‰‡è¨±å¯ (完全フリー)" + disallowIrregularRules: "変則ãªã—" + showBoardLabels: "盤é¢ã«è¡Œãƒ»åˆ—番å·ã‚’表示" + useAvatarAsStone: "石をアイコンã«ã™ã‚‹" + +_offlineScreen: + title: "オフライン - サーãƒãƒ¼ã«æŽ¥ç¶šã§ãã¾ã›ã‚“" + header: "サーãƒãƒ¼ã«æŽ¥ç¶šã§ãã¾ã›ã‚“" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 1a78c1ec46cfad38f177e69c33e10dabfa5c624d..d4c7eb09182d74adc52a160c547589c7bb9e69e6 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -15,7 +15,7 @@ gotIt: "ã»ã„" cancel: "ã‚„ã‚ã¨ã" noThankYou: "ã‚„ã‚ã¨ã" enterUsername: "ユーザーåを入れã¦ã‚„" -renotedBy: "{user}ãŒãƒªãƒŽãƒ¼ãƒˆã—ãŸã§" +renotedBy: "{user}ãŒãƒ–ーストã—ãŸã§" noNotes: "ノートã¯ã‚らã¸ã‚“" noNotifications: "通知ã¯ã‚らã¸ã‚“" instance: "サーãƒãƒ¼" @@ -45,10 +45,10 @@ pin: "ピン留ã‚ã—ã¨ã" unpin: "ã‚„ã£ã±ãƒ”ン留ã‚ã›ã‚“" copyContent: "内容をコピー" copyLink: "リンクをコピー" -copyLinkRenote: "リノートã®ãƒªãƒ³ã‚¯ã‚’コピーã™ã‚‹ã§ï¼Ÿ" +copyLinkRenote: "ブーストã®ãƒªãƒ³ã‚¯ã‚’コピーã™ã‚‹ã§ï¼Ÿ" delete: "ã»ã‹ã™" deleteAndEdit: "ã»ã‹ã—ã¦ç›´ã™" -deleteAndEditConfirm: "ã“ã®ãƒŽãƒ¼ãƒˆã‚’ã»ã‹ã—ã¦ã‚‚ã£ã‹ã„ç›´ã™ï¼Ÿã“ã®ãƒŽãƒ¼ãƒˆã¸ã®ãƒ„ッコミã€ãƒªãƒŽãƒ¼ãƒˆã€è¿”信も全部消ãˆã‚‹ã‚“ã‚„ã‘ã©ãã‚Œã§ã‚‚ãˆãˆã‚“?" +deleteAndEditConfirm: "ã“ã®ãƒŽãƒ¼ãƒˆã‚’ã»ã‹ã—ã¦ã‚‚ã£ã‹ã„ç›´ã™ï¼Ÿã“ã®ãƒŽãƒ¼ãƒˆã¸ã®ãƒ„ッコミã€ãƒ–ーストã€è¿”信も全部消ãˆã‚‹ã‚“ã‚„ã‘ã©ãã‚Œã§ã‚‚ãˆãˆã‚“?" addToList: "リストã«å…¥ã‚ŒãŸã‚‹" addToAntenna: "アンテナã«å…¥ã‚Œã‚‹" sendMessage: "メッセージをé€ã‚‹" @@ -105,13 +105,13 @@ followRequests: "フォãƒãƒ¼ç”³è«‹" unfollow: "フォãƒãƒ¼ã‚„ã‚ã‚‹" followRequestPending: "フォãƒãƒ¼è¨±ã—ã¦ãれるん待ã£ã¨ã‚‹" enterEmoji: "絵文å—を入れã¦ã‚„" -renote: "リノート" -unrenote: "リノートやã‚ã‚‹" -renoted: "リノートã—ãŸã§ã€‚" -cantRenote: "ã“ã®æŠ•ç¨¿ã¯ãƒªãƒŽãƒ¼ãƒˆã§ãã¸ã‚“ã£ã½ã„。" -cantReRenote: "リノート自体ã¯ãƒªãƒŽãƒ¼ãƒˆã§ãã¸ã‚“ã§ã€‚" +renote: "ブースト" +unrenote: "ブーストやã‚ã‚‹" +renoted: "ブーストã—ãŸã§ã€‚" +cantRenote: "ã“ã®æŠ•ç¨¿ã¯ãƒ–ーストã§ãã¸ã‚“ã£ã½ã„。" +cantReRenote: "ブースト自体ã¯ãƒ–ーストã§ãã¸ã‚“ã§ã€‚" quote: "引用" -inChannelRenote: "ãƒãƒ£ãƒ³ãƒãƒ«ã®ä¸ã§ãƒªãƒŽãƒ¼ãƒˆ" +inChannelRenote: "ãƒãƒ£ãƒ³ãƒãƒ«ã®ä¸ã§ãƒ–ースト" inChannelQuote: "ãƒãƒ£ãƒ³ãƒãƒ«å†…引用" pinnedNote: "ピン留ã‚ã•ã‚Œã¨ã‚‹ãƒŽãƒ¼ãƒˆ" pinned: "ピン留ã‚ã—ã¨ã" @@ -130,13 +130,14 @@ overwriteFromPinnedEmojis: "全般è¨å®šã‹ã‚‰ä¸Šæ›¸ãã™ã‚‹" reactionSettingDescription2: "ドラッグã§ä¸¦ã³æ›¿ãˆã€ã‚¯ãƒªãƒƒã‚¯ã§å‰Šé™¤ã€ï¼‹ã‚’押ã—ã¦è¿½åŠ ã‚„ã§ã€‚" rememberNoteVisibility: "公開範囲覚ãˆã¨ã„ã¦" attachCancel: "ã®ã£ã‘ã‚‹ã®ã‚„ã‚ã‚‹" +deleteFile: "ファイルをã»ã‹ã™" markAsSensitive: "ã¡ã‚‡ã£ã¨ã“ã‚Œã¯ã‚¢ã‚«ãƒ³" unmarkAsSensitive: "ãã“ã¾ã§ã‚¢ã‚«ãƒ³ã“ã¨ãªã„ã‚„ã‚" enterFileName: "ファイルåを入れã¦ã‚„" mute: "ミュート" unmute: "ミュートやã‚ãŸã‚‹" -renoteMute: "リノートã¯è¦‹ã„ã²ã‚“" -renoteUnmute: "リノートもやã£ã±è¦‹ã‚‹ã‚" +renoteMute: "ブーストã¯è¦‹ã„ã²ã‚“" +renoteUnmute: "ブーストもやã£ã±è¦‹ã‚‹ã‚" block: "ブãƒãƒƒã‚¯" unblock: "ブãƒãƒƒã‚¯ã‚„ã‚ãŸã‚‹" suspend: "å‡çµ" @@ -381,6 +382,11 @@ hcaptcha: "hCaptcha(ã‚ャプãƒãƒ£ï¼‰" enableHcaptcha: "hCaptcha(ã‚ャプãƒãƒ£ï¼‰ã‚’ã¤ã‘ã¨ã" hcaptchaSiteKey: "サイトã‚ー" hcaptchaSecretKey: "シークレットã‚ー" +mcaptcha: "mCaptcha" +enableMcaptcha: "hCaptcha(ã‚ャプãƒãƒ£ï¼‰ã‚’ã¤ã‘ã¨ã" +mcaptchaSiteKey: "サイトã‚ー" +mcaptchaSecretKey: "シークレットã‚ー" +mcaptchaInstanceUrl: "mCaptchaã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã®URL" recaptcha: "reCAPTCHA" enableRecaptcha: "reCAPTCHA(リã‚ャプãƒãƒ£ï¼‰ã‚’有効ã«ã™ã‚‹" recaptchaSiteKey: "サイトã‚ー" @@ -628,6 +634,7 @@ medium: "ä¸" small: "å°" generateAccessToken: "アクセストークンã®ç™ºè¡Œ" permission: "権é™" +adminPermission: "管ç†è€…権é™" enableAll: "全部使ãˆã‚‹ã‚ˆã†ã«ã™ã‚‹" disableAll: "全部使ãˆã¸ã‚“よã†ã«ã™ã‚‹" tokenRequested: "アカウントã¸ã®ã‚¢ã‚¯ã‚»ã‚¹è¨±ã—ã¦ã‚„ã£ãŸã‚‰ã©ã†ã‚„" @@ -671,13 +678,14 @@ useGlobalSettingDesc: "オンã«ã™ã‚‹ã¨ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®é€šçŸ¥è¨å®šãŒä½¿ other: "ãã®ä»–" regenerateLoginToken: "ãƒã‚°ã‚¤ãƒ³ãƒˆãƒ¼ã‚¯ãƒ³ã‚’å†ç”Ÿæˆ" regenerateLoginTokenDescription: "ãƒã‚°ã‚¤ãƒ³ã«ä½¿ã‚れる内部トークンをもã£ã‹ã„作るã§ã€‚ã„ã¤ã‚‚ãªã‚‰ã“れをやる必è¦ã¯ãªã„ã§ã€‚ã‚‚ã£ã‹ã„作るã¨ã€å…¨éƒ¨ã®ãƒ‡ãƒã‚¤ã‚¹ã§ãƒã‚°ã‚¢ã‚¦ãƒˆã•ã‚Œã‚‹ã§æ°—ãƒã¤ã‘ã¦ãªãƒ¼ã€‚" +theKeywordWhenSearchingForCustomEmoji: "カスタム絵文å—を探ã™ã¨ãã®ã‚ーワードã«ãªã‚‹ã§ã€‚" setMultipleBySeparatingWithSpace: "スペースã§åŒºåˆ‡ã£ã¦ä½•å€‹ã§ã‚‚è¨å®šã§ãã‚‹ã§ã€‚" fileIdOrUrl: "ファイルIDã‹URL" behavior: "動作" sample: "サンプル" abuseReports: "é€šå ±" reportAbuse: "é€šå ±" -reportAbuseRenote: "リノート苦情ã ã™ã§ï¼Ÿ" +reportAbuseRenote: "ブースト苦情ã ã™ã§ï¼Ÿ" reportAbuseOf: "{name}ã‚’é€šå ±ã™ã‚‹" fillAbuseReportDescription: "ç´°ã‹ã„é€šå ±ç†ç”±ã‚’書ã„ã¦ãªãƒ¼ã€‚対象ノートãŒã‚る時ã¯ãã®URLも書ã„ã¨ã„ã¦ãªãƒ¼ã€‚" abuseReported: "無事内容ãŒé€ä¿¡ã•ã‚ŒãŸã¿ãŸã„ã‚„ã§ã€‚ãŠãŠãã«ã€œã€‚" @@ -711,9 +719,9 @@ manageAccessTokens: "アクセストークンã®ç®¡ç†" accountInfo: "ã‚¢ã‚«ã‚¦ãƒ³ãƒˆæƒ…å ±" notesCount: "ノートã®æ•°ã‚„ã§" repliesCount: "返信ã—ãŸæ•°ã‚„ã§" -renotesCount: "リノートã—ãŸæ•°ã‚„ã§" +renotesCount: "ブーストã—ãŸæ•°ã‚„ã§" repliedCount: "返信ã•ã‚ŒãŸæ•°ã‚„ã§" -renotedCount: "リノートã•ã‚ŒãŸæ•°ã‚„ã§" +renotedCount: "ブーストã•ã‚ŒãŸæ•°ã‚„ã§" followingCount: "フォãƒãƒ¼æ•°ã‚„ã§" followersCount: "フォãƒãƒ¯ãƒ¼æ•°ã‚„ã§" sentReactionsCount: "ツッコんã æ•°" @@ -883,6 +891,8 @@ makeReactionsPublicDescription: "ã‚ã‚“ãŸãŒã—ãŸãƒ„ッコミ一覧を誰㧠classic: "クラシック" muteThread: "スレッドをミュート" unmuteThread: "スレッドã®ãƒŸãƒ¥ãƒ¼ãƒˆã‚’解除" +followingVisibility: "フォãƒãƒ¼ã®å…¬é–‹ç¯„囲" +followersVisibility: "フォãƒãƒ¯ãƒ¼ã®å…¬é–‹ç¯„囲" continueThread: "ã•ã‚‰ã«ã‚¹ãƒ¬ãƒƒãƒ‰ã‚’見るã§" deleteAccountConfirm: "アカウントを消ã™ã§ï¼Ÿãˆãˆã‚“ã‹ï¼Ÿ" incorrectPassword: "パスワードãŒã¡ã‚ƒã†ã‚。" @@ -983,6 +993,7 @@ neverShow: "今後表示ã—ãªã„" remindMeLater: "ã¾ãŸå¾Œã§" didYouLikeMisskey: "Sharkeyæ°—ã«å…¥ã£ã¦ãã‚ŒãŸï¼Ÿ" pleaseDonate: "Sharkeyã¯{host}ãŒä½¿ã†ã¨ã‚‹ç„¡æ–™ã®ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã‚„ã§ã€‚ã“ã‚Œã‹ã‚‰ã‚‚開発を続ã‘れるよã†ã«ã€å¯„付ã—ãŸã£ã¦ãªï½žã€‚" +correspondingSourceIsAvailable: "{anchor}" roles: "ãƒãƒ¼ãƒ«" role: "ãƒãƒ¼ãƒ«" noRole: "ãƒãƒ¼ãƒ«ã¯ã‚ã‚Šã¾ã¸ã‚“" @@ -1009,7 +1020,7 @@ thisPostMayBeAnnoying: "ã“ã®æŠ•ç¨¿ã¯è¿·æƒ‘ã‹ã‚‚ã—らんã§ã€‚" thisPostMayBeAnnoyingHome: "ホームã«æŠ•ç¨¿" thisPostMayBeAnnoyingCancel: "ã‚„ã‚ã¨ã" thisPostMayBeAnnoyingIgnore: "ã“ã®ã¾ã¾æŠ•ç¨¿" -collapseRenotes: "見ãŸã“ã¨ã‚るリノートã¯é£›ã°ã—ã¦è¡¨ç¤ºã™ã‚‹ã§" +collapseRenotes: "見ãŸã“ã¨ã‚るブーストã¯é£›ã°ã—ã¦è¡¨ç¤ºã™ã‚‹ã§" internalServerError: "サーãƒãƒ¼å†…部エラー" internalServerErrorDescription: "サーãƒãƒ¼ã§ãªã‚“ã‹å¤‰ãªã“ã¨èµ·ã“ã£ã¨ã‚‹ã‚。" copyErrorInfo: "ã‚¨ãƒ©ãƒ¼æƒ…å ±ã‚’ã‚³ãƒ”ã‚‹ã§" @@ -1033,6 +1044,7 @@ resetPasswordConfirm: "パスワード作り直ã™ã‚“ã§ãˆãˆãªï¼Ÿ" sensitiveWords: "ã‘ã£ãŸã„ãªå˜èªž" sensitiveWordsDescription: "è¨å®šã—ãŸå˜èªžãŒå…¥ã£ã¨ã‚‹ãƒŽãƒ¼ãƒˆã®å…¬é–‹ç¯„囲をホームã«ã—ãŸã‚‹ã‚。改行ã§åŒºåˆ‡ã£ãŸã‚‰è¤‡æ•°è¨å®šã§ãã‚‹ã§ã€‚" sensitiveWordsDescription2: "スペースã§åŒºåˆ‡ã‚‹ã¨AND指定ã€ã‚ーワードをスラッシュã§å›²ã‚“ã らæ£è¦è¡¨ç¾ã‚„。" +prohibitedWordsDescription2: "スペースã§åŒºåˆ‡ã‚‹ã¨AND指定ã€ã‚ーワードをスラッシュã§å›²ã‚“ã らæ£è¦è¡¨ç¾ã‚„。" hiddenTags: "見ãˆã¦ã¸ã‚“ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°" hiddenTagsDescription: "è¨å®šã—ãŸã‚¿ã‚°ã‚’最近æµè¡Œã‚Šã®ã¨ã“ã«è¦‹ãˆã‚“よã†ã«ã™ã‚“ã§ã€‚複数è¨å®šã™ã‚‹ã¨ãã¯æ”¹è¡Œã§åŒºåˆ‡ã£ã¦ãªã€‚" notesSearchNotAvailable: "ãªã‚“ã‹ãƒŽãƒ¼ãƒˆæŽ¢ã›ã¸ã‚“。" @@ -1051,6 +1063,8 @@ limitWidthOfReaction: "ツッコミã®æœ€å¤§æ¨ªå¹…を制é™ã—ã¦ã€ã¡ã£ã•ã noteIdOrUrl: "ノートIDã‹URL" video: "å‹•ç”»" videos: "å‹•ç”»" +audio: "音声" +audioFiles: "音声" dataSaver: "データケãƒã‚±ãƒ" accountMigration: "アカウントã®ãŠå¼•ã£è¶Šã—" accountMoved: "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã•ã‚‰ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«å¼•ã£è¶Šã—ãŸã§ï¼š" @@ -1060,7 +1074,7 @@ forceShowAds: "ã„ã£ã¤ã‚‚åºƒå‘Šã‚’æ˜ ã™" addMemo: "メモを足ã™" editMemo: "メモをã„らã†" reactionsList: "ツッコミ一覧" -renotesList: "リノート一覧" +renotesList: "ブースト一覧" notificationDisplay: "通知見ã›ã‚‹" leftTop: "左上" rightTop: "å³ä¸Š" @@ -1131,7 +1145,7 @@ pastAnnouncements: "éŽåŽ»ã®ãŠçŸ¥ã‚‰ã›ã‚„ã§" youHaveUnreadAnnouncements: "ã‚ã‚“ãŸã¾ã ã“ã®ãŠçŸ¥ã‚‰ã›èªã‚“ã©ã‚‰ã‚“ã‚„ã‚。" useSecurityKey: "ブラウザã¾ãŸã¯ãƒ‡ãƒã‚¤ã‚¹ã®è¨€ã†é€šã‚Šã«ã€ã‚»ã‚ュリティã‚ーã¾ãŸã¯ãƒ‘スã‚ーを使ã£ã¦ã‚„。" replies: "返事" -renotes: "リノート" +renotes: "ブースト" loadReplies: "返信を見るã§" loadConversation: "会話を見るã§" pinnedList: "ピン留ã‚ã—ã¯ã£ãŸãƒªã‚¹ãƒˆ" @@ -1142,7 +1156,7 @@ unnotifyNotes: "投稿ã®é€šçŸ¥ã‚„ã‚ã‚‹" authentication: "èªè¨¼" authenticationRequiredToContinue: "続ã‘ã‚‹ã‚“ãªã‚‰èªè¨¼ã—ã¦ã‚„。" dateAndTime: "日時" -showRenotes: "リノート出ã™" +showRenotes: "ブースト出ã™" edited: "ã„ã˜ã£ãŸã‚„ã¤" notificationRecieveConfig: "通知もらã†ã‹ã®è¨å®š" mutualFollow: "ãŠäº’ã„フォãƒãƒ¼ã—ã¦ã‚“ã§" @@ -1154,6 +1168,7 @@ hideRepliesToOthersInTimelineAll: "タイムラインã«ä»Šãƒ•ã‚©ãƒãƒ¼ã—ã¨ã‚‹ confirmShowRepliesAll: "ã“ã‚Œã¯å…ƒã«æˆ»ã›ã¸ã‚“ã‹ã‚‰æ…Žé‡ã«æ±ºã‚ã¦ã‚„。本当ã«ã‚¿ã‚¤ãƒ ラインã«ä»Šãƒ•ã‚©ãƒãƒ¼ã—ã¨ã‚‹å…¨å“¡ã®è¿”信を入れるã‹ï¼Ÿ" confirmHideRepliesAll: "ã“ã‚Œã¯å…ƒã«æˆ»ã›ã¸ã‚“ã‹ã‚‰æ…Žé‡ã«æ±ºã‚ã¦ã‚„。本当ã«ã‚¿ã‚¤ãƒ ラインã«ä»Šãƒ•ã‚©ãƒãƒ¼ã—ã¨ã‚‹å…¨å“¡ã®è¿”信を入れã¸ã‚“ã®ã‹ï¼Ÿ" externalServices: "ä»–ã®ã‚µã‚¤ãƒˆã®ã‚µãƒ¼ãƒ“ス" +sourceCode: "ソースコード" impressum: "é‹å–¶è€…ã®æƒ…å ±" impressumUrl: "é‹å–¶è€…ã®æƒ…å ±URL" impressumDescription: "ドイツã¨ã‹ã®ä¸€éƒ¨ã‚“ã¨ã“ã‚ã§ã¯ãªã€è¡¨ç¤ºãŒç¾©å‹™ä»˜ã‘られã¦ã‚“ãã‚“(Impressum)。" @@ -1181,6 +1196,28 @@ remainingN: "残り:{n}" overwriteContentConfirm: "今ã®å†…容ã«ä¸Šæ›¸ãã•ã‚Œã‚‹ã‘ã©ã„ã„?" seasonalScreenEffect: "å£ç¯€ã«ã‚ã£ãŸç”»é¢ã®å‹•ã" decorate: "デコる" +addMfmFunction: "装飾ã¤ã‘ã‚‹" +enableQuickAddMfmFunction: "ã‚„ã‚„ã“ã—ã„MFMã®ãƒ”ッカーを出ã™" +bubbleGame: "ãƒãƒ–ルゲーム" +sfx: "効果音" +soundWillBePlayed: "サウンドãŒå†ç”Ÿã•ã‚Œã‚‹ã§" +showReplay: "リプレイ見る" +replay: "リプレイ" +replaying: "リプレイä¸" +ranking: "ランã‚ング" +lastNDays: "ç›´è¿‘{n}æ—¥" +backToTitle: "タイトルã¸" +hemisphere: "ä½ã‚“ã§ã‚‹åœ°åŸŸ" +withSensitive: "センシティブãªãƒ•ã‚¡ã‚¤ãƒ«ã‚’å«ã‚€ãƒŽãƒ¼ãƒˆã‚’表示" +userSaysSomethingSensitive: "{name}ã®ã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ãªãƒ•ã‚¡ã‚¤ãƒ«ã‚’å«ã‚€æŠ•ç¨¿" +enableHorizontalSwipe: "スワイプã—ã¦ã‚¿ãƒ–を切り替ãˆã‚‹" +surrender: "ã‚„ã‚ã¨ã" +_bubbleGame: + howToPlay: "éŠã³æ–¹" + _howToPlay: + section1: "ä½ç½®ã‚’調整ã—ã¦ãƒã‚³ã«ãƒ¢ãƒŽã‚’è½ã¨ã™ã§ã€‚" + section2: "åŒã˜ã‚‚ã‚“ãŒãã£ã¤ã„ãŸã‚‰åˆ¥ã®ã‚„ã¤ã«ãªã£ã¦ã€ã‚¹ã‚³ã‚¢ãŒã‚‚らãˆã‚‹ã§ã€‚" + section3: "モノãŒãƒã‚³ã‹ã‚‰ã‚ãµã‚ŒãŸã‚‰ã‚²ãƒ¼ãƒ オーãƒãƒ¼ã‚„。ãƒã‚³ã‹ã‚‰ã‚ãµã‚Œã‚“よã†ã«ã—ãªãŒã‚‰ãƒ¢ãƒŽã‚’èžåˆã•ã›ã¦ãƒã‚¤ã‚¹ã‚³ã‚¢ã‚’目指ã—ã„ã‚„ï¼" _announcement: forExistingUsers: "ã‚‚ã†ãŠã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã¿" forExistingUsersDescription: "オンã«ã—ãŸã‚‰ã“ã®ãŠçŸ¥ã‚‰ã›ãŒã§ããŸæ™‚点ã§ãŠã‚‹äººã‚‰ã«ã ã‘ãŠçŸ¥ã‚‰ã›ãŒè¡Œãã§ã€‚切ã£ãŸã‚‰ã“ã®çŸ¥ã‚‰ã›ãŒè¡Œã£ãŸã‚ã¨ã«ã‚¢ã‚«ã‚¦ãƒ³ãƒˆä½œã£ãŸäººã«ã‚‚ã¡ã‚ƒã‚“ã¨ãŠçŸ¥ã‚‰ã›ãŒè¡Œãã§ã€‚" @@ -1247,8 +1284,8 @@ _initialTutorial: _visibility: description: "ノートを見れる相手を制é™ã§ãã‚‹ã‚。" public: "ã¿ã‚“ãªã«è¦‹ã›ã‚‹ã§ã€‚" - home: "ホームタイムラインã«ã ã‘見ã›ã‚‹ã§ã€‚フォãƒãƒ¯ãƒ¼ã¨ã‹ã€ãƒ—ãƒãƒ•ã‚£ãƒ¼ãƒ«ã‚’見ã«æ¥ãŸäººã€ãƒªãƒŽãƒ¼ãƒˆã‹ã‚‰ã‚‚見れるã‹ã‚‰ã€å®Ÿè³ªã¯å…¨å“¡è¦‹ã‚Œã‚‹ã‘ã©ãªã€‚ã‚ã‚“ã¾ã—広ãŒã‚Šã«ãã„ã£ã¦ã“ã¨ã‚„。" - followers: "フォãƒãƒ¯ãƒ¼ã«ã ã‘見ã›ã‚‹ã§ã€‚自分以外ã¯ãƒªãƒŽãƒ¼ãƒˆã§ãã¸ã‚“ã—ã€ãƒ•ã‚©ãƒãƒ¯ãƒ¼ä»¥å¤–ã¯çµ¶å¯¾ã«è¦‹ã‚Œã¸ã‚“。" + home: "ホームタイムラインã«ã ã‘見ã›ã‚‹ã§ã€‚フォãƒãƒ¯ãƒ¼ã¨ã‹ã€ãƒ—ãƒãƒ•ã‚£ãƒ¼ãƒ«ã‚’見ã«æ¥ãŸäººã€ãƒ–ーストã‹ã‚‰ã‚‚見れるã‹ã‚‰ã€å®Ÿè³ªã¯å…¨å“¡è¦‹ã‚Œã‚‹ã‘ã©ãªã€‚ã‚ã‚“ã¾ã—広ãŒã‚Šã«ãã„ã£ã¦ã“ã¨ã‚„。" + followers: "フォãƒãƒ¯ãƒ¼ã«ã ã‘見ã›ã‚‹ã§ã€‚自分以外ã¯ãƒ–ーストã§ãã¸ã‚“ã—ã€ãƒ•ã‚©ãƒãƒ¯ãƒ¼ä»¥å¤–ã¯çµ¶å¯¾ã«è¦‹ã‚Œã¸ã‚“。" direct: "指定ã—ãŸäººã«ã ã‘公開ã•ã‚Œã¦ã€ã¤ã„ã§ã«é€šçŸ¥ã‚‚é€ã‚‹ã§ã€‚ダイレクトメールã®ä»£ã‚ã‚Šã¨ã—ã¦ä½¿ã£ã¦ãªã€‚" doNotSendConfidencialOnDirect1: "æ©Ÿå¯†æƒ…å ±ã‚’é€ã‚‹ã¨ãã¯å分注æ„ã›ãˆã‚ˆã€‚" doNotSendConfidencialOnDirect2: "é€ä¿¡å…ˆã®ã‚µãƒ¼ãƒãƒ¼ã®ç®¡ç†è€…ã¯æŠ•ç¨¿å†…容ãŒè¦‹ã‚Œã‚‹ã‹ã‚‰ã€ä¿¡ç”¨ã§ãã¸ã‚“サーãƒãƒ¼ã®ã²ã¨ã«ãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆæŠ•ç¨¿ã™ã‚‹ã¨ãã«ã¯ã€ã‚ã£ã¡ã‚ƒç”¨å¿ƒã—ã¨ãã‚“ã‚„ã§ã€‚" @@ -1551,6 +1588,13 @@ _achievements: _tutorialCompleted: title: "Sharkeyã²ã‚ˆã£ã“講座 修了証" description: "ãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ«å…¨éƒ¨ã‚„ã£ãŸ" + _bubbleGameExplodingHead: + title: "🤯" + description: "ãƒãƒ–ルゲームã§æœ€ã‚‚大ãã„モノを出ã—ãŸ" + _bubbleGameDoubleExplodingHead: + title: "ダブル🤯" + description: "ãƒãƒ–ルゲームã§æœ€ã‚‚大ãã„モノを2ã¤åŒæ™‚ã«å‡ºã—ãŸ" + flavor: "ã“ã‚Œãらã„ã®ã€€ãŠã¹ã‚“ã¨ã°ã“ã«ã€€ðŸ¤¯ã€€ðŸ¤¯ã€€ã¡ã‚‡ã£ã¨ã¤ã‚ã¦" _role: new: "ãƒãƒ¼ãƒ«ã®ä½œæˆ" edit: "ãƒãƒ¼ãƒ«ã®ç·¨é›†" @@ -1641,6 +1685,7 @@ _emailUnavailable: disposable: "ãšãƒ¼ã£ã¨ä½¿ãˆã‚‹ã‚¢ãƒ‰ãƒ¬ã‚¹ã˜ã‚ƒãªã„ã¿ãŸã„ã‚„" mx: "æ£ã—ã„メールサーãƒãƒ¼ã˜ã‚ƒãªã„ã£ã½ã„ã‚" smtp: "メールサーãƒãƒ¼ãŒã†ã‚“ã¨ã‚‚ã™ã‚“ã¨ã‚‚言ã‚ã¸ã‚“" + banned: "ã“ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã¯ã‚ã‹ã‚“" _ffVisibility: public: "公開" followers: "フォãƒãƒ¯ãƒ¼ã ã‘ã«å…¬é–‹" @@ -1709,7 +1754,7 @@ _registry: domain: "ドメイン" createKey: "ã‚ーを作る" _aboutMisskey: - about: "Sharkeyã¯ã€syuiloãŒ2014å¹´ã‹ã‚‰ãšã£ã¨ä½œã£ã¦ã¯ã‚‹ã€Misskeyをベースã«ã—ãŸã‚ªãƒ¼ãƒ—ンソースãªã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã‚„。" + about: "Sharkeyã¯ã€Misskeyをベースã«ã—ãŸã‚ªãƒ¼ãƒ—ンソースãªã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã‚„。" contributors: "主ãªè²¢çŒ®è€…" allContributors: "å…¨ã¦ã®è²¢çŒ®è€…" source: "ソースコード" @@ -1742,7 +1787,7 @@ _channel: notesCount: "{n}ã“投稿ãŒã‚ã‚‹ã§" nameAndDescription: "åå‰ã¨èª¬æ˜Ž" nameOnly: "åå‰ã ã‘" - allowRenoteToExternal: "ãƒãƒ£ãƒ³ãƒãƒ«ã®å¤–ã«ãƒªãƒŽãƒ¼ãƒˆã§ãるよã†ã«ã™ã‚‹" + allowRenoteToExternal: "ãƒãƒ£ãƒ³ãƒãƒ«ã®å¤–ã«ãƒ–ーストã§ãるよã†ã«ã™ã‚‹" _menuDisplay: sideFull: "横" sideIcon: "横(アイコン)" @@ -1932,6 +1977,55 @@ _permissions: "write:flash": "Playã‚’æ“作ã™ã‚‹" "read:flash-likes": "Playã®ãˆãˆã‚„ã‚“ï¼ã‚’見る" "write:flash-likes": "Playã®ãˆãˆã‚„ã‚“ï¼ã‚’見る" + "read:admin:abuse-user-reports": "ユーザーã‹ã‚‰ã®é€šå ±ã‚’見る" + "write:admin:delete-account": "ユーザーアカウント消ã™" + "write:admin:delete-all-files-of-a-user": "ユーザーã®ãƒ•ã‚¡ã‚¤ãƒ«å…¨éƒ¨ã»ã‹ã™" + "read:admin:index-stats": "データベースインデックスã®æƒ…å ±è¦‹ã‚‹" + "read:admin:table-stats": "データベーステーブルã®æƒ…å ±è¦‹ã‚‹" + "read:admin:user-ips": "ユーザーã®IPアドレスを見る" + "read:admin:meta": "インスタンスã®ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿è¦‹ã‚‹" + "write:admin:reset-password": "ユーザーã®ãƒ‘スワードをリセット" + "write:admin:resolve-abuse-user-report": "ユーザーã‹ã‚‰ã®é€šå ±ã‚’解決ã™ã‚‹" + "write:admin:send-email": "メールé€ã‚‹" + "read:admin:server-info": "サーãƒãƒ¼ã®æƒ…å ±è¦‹ã‚‹" + "read:admin:show-moderation-log": "モデレーションãƒã‚°è¦‹ã‚‹" + "read:admin:show-user": "ユーザーã®ãƒ—ライベートãªæƒ…å ±è¦‹ã‚‹" + "read:admin:show-users": "ユーザーã®ãƒ—ライベートãªæƒ…å ±è¦‹ã‚‹" + "write:admin:suspend-user": "ユーザーをå‡çµ" + "write:admin:unset-user-avatar": "ユーザーã®ã‚¢ãƒã‚¿ãƒ¼ã‚’削除" + "write:admin:unset-user-banner": "ユーザーã®ãƒãƒŠãƒ¼ã‚’削除" + "write:admin:unsuspend-user": "ユーザーã®å‡çµè§£é™¤" + "write:admin:meta": "インスタンスã®ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã„ã˜ã‚‹" + "write:admin:user-note": "モデレーションノートã„ã˜ã‚‹" + "write:admin:roles": "ãƒãƒ¼ãƒ«ã‚’ã„ã˜ã‚‹" + "read:admin:roles": "ãƒãƒ¼ãƒ«è¦‹ã‚‹" + "write:admin:relays": "リレーã„ã˜ã‚‹" + "read:admin:relays": "リレー見る" + "write:admin:invite-codes": "招待コードã„ã˜ã‚‹" + "read:admin:invite-codes": "招待コード見る" + "write:admin:announcements": "ãŠçŸ¥ã‚‰ã›ã„ã˜ã‚‹" + "read:admin:announcements": "ãŠçŸ¥ã‚‰ã›è¦‹ã‚‹" + "write:admin:avatar-decorations": "ã‚¢ãƒã‚¿ãƒ¼ãƒ‡ã‚³ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã‚’ã„ã˜ã‚‹" + "read:admin:avatar-decorations": "ã‚¢ãƒã‚¿ãƒ¼ãƒ‡ã‚³ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³è¦‹ã‚‹" + "write:admin:federation": "連åˆã®æƒ…å ±ã„ã˜ã‚‹" + "write:admin:account": "ユーザーアカウントã„ã˜ã‚‹" + "read:admin:account": "ユーザーã®æƒ…å ±è¦‹ã‚‹" + "write:admin:emoji": "絵文å—ã„ã˜ã‚‹" + "read:admin:emoji": "絵文å—見る" + "write:admin:queue": "ジョブã‚ューã„ã˜ã‚‹" + "read:admin:queue": "ジョブã‚ューã®æƒ…å ±è¦‹ã‚‹" + "write:admin:promo": "プãƒãƒ¢ãƒ¼ã‚·ãƒ§ãƒ³ãƒŽãƒ¼ãƒˆã„ã˜ã‚‹" + "write:admin:drive": "ユーザーã®ãƒ‰ãƒ©ã‚¤ãƒ–ã„ã˜ã‚‹" + "read:admin:drive": "ユーザーã®ãƒ‰ãƒ©ã‚¤ãƒ–ã®æƒ…å ±è¦‹ã‚‹" + "read:admin:stream": "管ç†è€…用ã®Websocket API使ã†" + "write:admin:ad": "広告ã„ã˜ã‚‹" + "read:admin:ad": "広告見る" + "write:invite-codes": "招待コード作る" + "read:invite-codes": "招待コードå–å¾—" + "write:clip-favorite": "クリップã®ã„ã„ãã„ã˜ã‚‹" + "read:clip-favorite": "クリップã®ã„ã„ã見る" + "read:federation": "連åˆã®æƒ…å ±å–å¾—" + "write:report-abuse": "é•åå ±å‘Š" _auth: shareAccessTitle: "アプリã¸ã®ã‚¢ã‚¯ã‚»ã‚¹è¨±ã—ã¦ã‚„ã£ãŸã‚‰ã©ã†ã‚„" shareAccess: "「{name}ã€ãŒã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã“ã¨ã‚’許å¯ã—ã¦ãˆãˆã‹ï¼Ÿ" @@ -1945,9 +2039,9 @@ _auth: _antennaSources: all: "ã¿ã‚“ãªã®ãƒŽãƒ¼ãƒˆ" homeTimeline: "フォãƒãƒ¼ã—ã¨ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆ" - users: "é¸ã‚‰ã‚“ã 一人ã‹è¤‡æ•°ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆ" + users: "é¸ã‚“ã 一人ã‹è¤‡æ•°ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆ" userList: "é¸ã‚“ã リストã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆ" - userBlacklist: "é¸ã‚“ã 1人ã‹è¤‡æ•°ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆ" + userBlacklist: "é¸ã‚“ã 一人ã‹è¤‡æ•°ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’除ã„ãŸå…¨ã¦ã®ãƒŽãƒ¼ãƒˆ" _weekday: sunday: "日曜日" monday: "月曜日" @@ -2053,6 +2147,7 @@ _profile: _exportOrImport: allNotes: "å…¨ã¦ã®ãƒŽãƒ¼ãƒˆ" favoritedNotes: "ãŠæ°—ã«å…¥ã‚Šã«ã—ãŸãƒŽãƒ¼ãƒˆ" + clips: "クリップ" followingList: "フォãƒãƒ¼" muteList: "ミュート" blockingList: "ブãƒãƒƒã‚¯" @@ -2164,13 +2259,14 @@ _notification: youGotMention: "{name}ã‹ã‚‰ã®ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³" youGotReply: "{name}ã‹ã‚‰ã®ãƒªãƒ—ライ" youGotQuote: "{name}ã«ã‚ˆã‚‹å¼•ç”¨" - youRenoted: "{name}ãŒãƒªãƒŽãƒ¼ãƒˆã—ãŸã¿ãŸã„ã‚„ã§" + youRenoted: "{name}ãŒãƒ–ーストã—ãŸã¿ãŸã„ã‚„ã§" youWereFollowed: "フォãƒãƒ¼ã•ã‚ŒãŸã§" youReceivedFollowRequest: "フォãƒãƒ¼è¨±å¯ã—ã¦ã»ã—ã„ã¿ãŸã„ã‚„ãª" yourFollowRequestAccepted: "フォãƒãƒ¼ã•ã›ã¦ã‚‚ã‚ãŸã§" pollEnded: "アンケートã®çµæžœãŒå‡ºãŸã¿ãŸã„ã‚„" newNote: "ã•ã‚‰ã®æŠ•ç¨¿" unreadAntennaNote: "アンテナ {name}" + roleAssigned: "ãƒãƒ¼ãƒ«ãŒä»˜ä¸Žã•ã‚ŒãŸã§" emptyPushNotificationMessage: "プッシュ通知ã®æ›´æ–°ã‚’ã—ã¨ã„ãŸã§" achievementEarned: "実績をç²å¾—ã—ã¨ã‚‹ã§" testNotification: "通知テスト" @@ -2178,7 +2274,7 @@ _notification: sendTestNotification: "テスト通知をé€ä¿¡ã™ã‚‹ã§" notificationWillBeDisplayedLikeThis: "通知ã¯ã“ã®ã‚ˆã†ã«è¡¨ç¤ºã•ã‚Œã‚‹ã§" reactedBySomeUsers: "{n}人ãŒãƒ„ッコんã ã§" - renotedBySomeUsers: "{n}人ãŒãƒªãƒŽãƒ¼ãƒˆã—ãŸã§" + renotedBySomeUsers: "{n}人ãŒãƒ–ーストã—ãŸã§" followedBySomeUsers: "{n}人ã«ãƒ•ã‚©ãƒãƒ¼ã•ã‚ŒãŸã§" _types: all: "ã™ã¹ã¦" @@ -2192,6 +2288,7 @@ _notification: pollEnded: "アンケートãŒçµ‚了ã—ãŸã§" receiveFollowRequest: "フォãƒãƒ¼è¨±å¯ã—ã¦ã»ã—ã„ã¿ãŸã„ã‚„ã§" followRequestAccepted: "フォãƒãƒ¼ãŒå—ç†ã•ã‚ŒãŸã§" + roleAssigned: "ãƒãƒ¼ãƒ«ãŒä»˜ä¸Žã•ã‚ŒãŸ" achievementEarned: "実績ã®ç²å¾—" app: "連æºã‚¢ãƒ—リã‹ã‚‰ã®é€šçŸ¥ã‚„" _actions: @@ -2249,7 +2346,7 @@ _webhookSettings: followed: "フォãƒãƒ¼ã‚‚らã£ãŸã¨ã~ï¼" note: "ノートを投稿ã—ãŸã¨ã~ï¼" reply: "返信ãŒã‚ã‚‹ã¨ã~ï¼" - renote: "リノートã•ã‚Œã‚‹ã¨ã~ï¼" + renote: "ブーストã•ã‚Œã‚‹ã¨ã~ï¼" reaction: "ツッコã¾ã‚ŒãŸã¨ã~ï¼" mention: "メンションãŒã‚ã‚‹ã¨ã~ï¼" _moderationLogTypes: @@ -2350,3 +2447,52 @@ _dataSaver: _code: title: "コードãƒã‚¤ãƒ©ã‚¤ãƒˆ" description: "MFMã¨ã‹ã§ã‚³ãƒ¼ãƒ‰ãƒã‚¤ãƒ©ã‚¤ãƒˆè¨˜æ³•ãŒä½¿ã‚ã‚Œã¦ã‚‹ã¨ãã€ã‚¿ãƒƒãƒ—ã™ã‚‹ã¾ã§èªã¿è¾¼ã¾ã‚Œã¸ã‚“ããªã‚‹ã§ã€‚コードãƒã‚¤ãƒ©ã‚¤ãƒˆã§ã¯ãƒã‚¤ãƒ©ã‚¤ãƒˆã™ã‚‹è¨€èªžã”ã¨ã«ãã®æ±ºã‚ã¦ã‚‹ãƒ•ã‚¡ã‚¤ãƒ«ã‚’èªã‚€å¿…è¦ã¯ã‚ã‚“ãã‚“ãªã€‚ã‘ã©ãªã€ãã‚Œã¯è‡ªå‹•ã§èªã¿è¾¼ã¾ã‚Œãªããªã‚‹ã‹ã‚‰ã€é€šä¿¡é‡ã‚’å°‘ãªãã§ãã‚‹ã“ã¨ãŒã§ãã‚‹ãん。" +_hemisphere: + N: "北åŠçƒ" + S: "å—åŠçƒ" + caption: "一部ã®ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆè¨å®šã§ã€å£ç¯€ã‚’判定ã™ã‚‹ã®ã«ä½¿ç”¨ã™ã‚‹ã§ã€‚" +_reversi: + reversi: "リãƒãƒ¼ã‚·" + gameSettings: "対局ã®è¨å®š" + chooseBoard: "ボードをé¸æŠž" + blackOrWhite: "先行/後攻" + blackIs: "{name}ãŒé»’(先行)" + rules: "ルール" + thisGameIsStartedSoon: "対局ã€ãã‚ãã‚開始ã•ã‚Œã‚‹ã§ã€‚" + waitingForOther: "相手ã®æº–å‚™ãŒå®Œäº†ã™ã‚‹ã®ã‚’å¾…ã£ã¦ã‚“ã§ã€‚" + waitingForMe: "ã‚ã‚“ã•ã‚“ã®æº–å‚™ãŒå®Œäº†ã™ã‚“ã®ã‚’å¾…ã£ã¦ã‚“ã§" + waitingBoth: "準備ã—ã¦ãªãƒ¼" + ready: "準備完了" + cancelReady: "準備をå†é–‹" + opponentTurn: "相手ã®ã‚¿ãƒ¼ãƒ³ã‚„ã§" + myTurn: "ã‚ã‚“ã•ã‚“ã®ã‚¿ãƒ¼ãƒ³ã‚„" + turnOf: "{name}ã®ã‚¿ãƒ¼ãƒ³ã‚„ã§" + pastTurnOf: "{name}ã®ã‚¿ãƒ¼ãƒ³" + surrender: "投了" + surrendered: "投了ã«ã‚ˆã‚Š" + timeout: "時間切れ" + drawn: "引ã分ã‘" + won: "{name}ã®å‹ã¡" + black: "é»’" + white: "白" + total: "åˆè¨ˆ" + turnCount: "{count}ターン目" + myGames: "自分ã®å¯¾å±€" + allGames: "ã¿ã‚“ãªã®å¯¾å±€" + ended: "終了" + playing: "対局ä¸" + isLlotheo: "石ã®å°‘ãªã„æ–¹ãŒå‹ã¡(ãƒã‚»ã‚ª)" + loopedMap: "ループマップ" + canPutEverywhere: "ã©ã“ã§ã‚‚ç½®ã‘るモード" + timeLimitForEachTurn: "1ターンã®æ™‚間制é™" + freeMatch: "フリーマッãƒ" + lookingForPlayer: "対戦相手を探ã—ã¦ã‚‹ã§" + gameCanceled: "対局ãŒã‚ャンセルã•ã‚ŒãŸã‚" + shareToTlTheGameWhenStart: "åˆã‚ã®æ™‚ã«å¯¾å±€ã‚’タイムラインã«æŠ•ç¨¿ã™ã‚‹ã§" + iStartedAGame: "対局ã—始ã‚ãŸã§ï¼ #MisskeyReversi" + opponentHasSettingsChanged: "相手ãŒè¨å®šå¤‰ãˆãŸã§" + allowIrregularRules: "å¤‰å‰‡è¨±å¯ (完全フリー)" + disallowIrregularRules: "変則ãªã—" +_offlineScreen: + title: "オフライン - サーãƒãƒ¼ã«æŽ¥ç¶šã§ãã²ã‚“ã§" + header: "サーãƒãƒ¼ã«æŽ¥ç¶šã§ãã¸ã‚“ã‚" diff --git a/locales/jbo-EN.yml b/locales/jbo-EN.yml index d4fea291d790413387600682d8b3f62a434143ee..297ca53dd7d6ba34baed5fabc6369e569acc555c 100644 --- a/locales/jbo-EN.yml +++ b/locales/jbo-EN.yml @@ -1,3 +1,4 @@ --- _lang_: "la .lojban." headlineMisskey: "lo se tcana noi jorne fi loi notci" + diff --git a/locales/kab-KAB.yml b/locales/kab-KAB.yml index 22e24d3baac0f6f51bd684a234699a31b12b90be..b976f028f098f18cc2c6ab5138424345c2249079 100644 --- a/locales/kab-KAB.yml +++ b/locales/kab-KAB.yml @@ -104,3 +104,4 @@ _deck: _columns: notifications: "IlÉ£uyen" list: "Tibdarin" + diff --git a/locales/kn-IN.yml b/locales/kn-IN.yml index b3ad46f2b16a9b964c56420a50d93f290940f358..bb6d1ee242484723fb1776cbc39ffdd4f2ccf568 100644 --- a/locales/kn-IN.yml +++ b/locales/kn-IN.yml @@ -84,3 +84,4 @@ _deck: notifications: "ಅಧಿಸೂಚನೆಗಳà³" tl: "ಸಮಯಸಾಲà³" mentions: "ಹೆಸರಿಸಿದ" + diff --git a/locales/ko-GS.yml b/locales/ko-GS.yml index 566667ba79ce7def4fddf0597e6988aee77f1e55..39492d902ffee13b125c9f04139b5a44735033c9 100644 --- a/locales/ko-GS.yml +++ b/locales/ko-GS.yml @@ -40,7 +40,7 @@ favorites: "질겨찾기" unfavorite: "질겨찾기서 á„‹á…¥á‡ì• 기" favorited: "ì§ˆê²¨ì°¾ê¸°ì— ë‹´ì•—ì‹ë‹ˆë‹¤." alreadyFavorited: "벌시로 ì§ˆê²¨ì°¾ê¸°ì— ë‹´ê¸° 잇ì‹ë‹ˆë‹¤." -cantFavorite: "ì§ˆê²¨ì°¾ê¸°ì— ëª¬ 담았ì‹ë‹ˆë‹¤." +cantFavorite: "ì§ˆê²¨ì°¾ê¸°ì— ëª¬ ë‹´ì•—ì‹ë‹ˆë‹¤." pin: "í”„ë¡œí•„ì— ë¶™ì´ê¸°" unpin: "프로필서 ë 기" copyContent: "ë‚´ìš© 복사하기" @@ -124,6 +124,7 @@ reactions: "반엉" reactionSettingDescription2: "꺼시서 ë‘ê³ , 누질ë¼ì„œ ë‰ìºê³ , ‘+’럴 누질ë¼ì„œ 옇ì‹ë‹ˆë‹¤." rememberNoteVisibility: "공개 범위럴 기억하기" attachCancel: "붙임 빼기" +deleteFile: "íŒŒì¼ ë‰ìºê¸°" markAsSensitive: "수ᇚ힘 ì„¤ì •" unmarkAsSensitive: "수ᇚ힘 무루기" enterFileName: "íŒŒì¼ ì´ëŸ¼ 서기" @@ -373,6 +374,8 @@ hcaptcha: "ì—ì´ì¹˜ìº¡ì°¨" enableHcaptcha: "ì—ì´ì¹˜ìº¡ì°¨ 키기" hcaptchaSiteKey: "사ì´íŠ¸í‚¤" hcaptchaSecretKey: "ì‹œí¬ë¦¿í‚¤" +mcaptchaSiteKey: "사ì´íŠ¸í‚¤" +mcaptchaSecretKey: "ì‹œí¬ë¦¿í‚¤" recaptcha: "리캡차" enableRecaptcha: "리캡차 키기" recaptchaSiteKey: "사ì´íŠ¸í‚¤" @@ -461,6 +464,8 @@ onlyOneFileCanBeAttached: "메시지엔 íŒŒì¼ í•˜ë‚˜ê¹Œì œë°–ì— ëª¬ ë„£ì‹ë‹ˆ invitations: "초대하기" invitationCode: "초대장" checking: "í•™ì¸í•˜ê³ 잇ì‹ë‹ˆë‹¤" +tooShort: "억수로 짜립니다" +tooLong: "억수로 집니다" passwordMatched: "맞ì‹ë‹ˆë‹¤" passwordNotMatched: "안 맞ì‹ë‹ˆë‹¤" signinFailed: "ë¡œê·¸ì¸ ëª¬ í–ˆì‹ë‹ˆë‹¤. ê³ ì´ë¦„ì´ëž‘ 비밀번호 ì œëŒ€ë¡œ ì¼ëŠ”ê°€ 확ì¸í•´ 주ì´ì†Œ." @@ -513,7 +518,7 @@ objectStoragePrefixDesc: "ìš” Prefix ë””ë ‰í† ë¦¬ 안ì—다가 파ì¼ì´ 들어 objectStorageEndpoint: "Endpoint" objectStorageEndpointDesc: "AWS S3ì„ ì“¸ë¼ë©˜ 요는 비워ë‘ê³ , ì•„ì´ë©˜ì€ ê·¸ 서비스 ê°€ì´ë“œì— 맞게 endpoint를 넣어 주ì´ì†Œ. '<host>' 내지 '<host>:<port>'처럼 ë„£ì‹ë‹ˆë‹¤." objectStorageRegion: "Region" -objectStorageRegionDesc: "'xx-east-1' ê°™ì€ region ì´ë¦„ì„ ì˜‡ì–´ 주ì´ì†Œ. ì¨ë¨¹ì„ ì„œë¹„ìŠ¤ì— region ê°œë… ê°™ì€ ê²Œ ìŽë‹¤! ì¹´ë©´ì€ ëŒ€ì‹ ì— 'us-east-1'ì„ ì˜‡ì–´ 놓으ì´ì†Œ. AWS ì„¤ì • 파ì¼ì´ë‚˜ 환경 변수를 갖다 ëŒì–´ë‹¤ 쓸 ê±°ë©´ì€ ìš”ëŠ” 비워 ë‘ì´ì†Œ." +objectStorageRegionDesc: "'xx-east-1' ê°™ì€ region ì´ë¦„ì„ ì˜‡ì–´ 주ì´ì†Œ. ë§Œì•½ì— ë‚´ 서비스엔 region ê°™ì€ ê°œë…ì´ ìŽë‹¤, ì¹´ë©´ì€ ëŒ€ì‹ ì— 'us-east-1'ë¼ê³ í•´ ë‘ì´ì†Œ. AWS ì„¤ì • 파ì¼ì´ë‚˜ 환경 변수를 ëŒì–´ë‹¤ ì“°ê² ë‹¤ë¯„ 요는 비워 ë‘ì´ì†Œ." objectStorageUseSSL: "SSL 쓰기" objectStorageUseSSLDesc: "API í˜¸ì¶œí• ë•Œ HTTPS 안 ì“¸ê±°ë©´ì€ êº¼ ë‘ì´ì†Œ" objectStorageUseProxy: "ì—°ê²°ì— í”„ë½ì‹œ 사용" @@ -536,7 +541,7 @@ volume: "ìŒëŸ‰" masterVolume: "대빵 ìŒëŸ‰" notUseSound: "ìŒì†Œê±°í•˜ê¸°" useSoundOnlyWhenActive: "Misskeyê°€ 활성화ë˜ì–´ ìžˆì„ ë•Œë§Œ 소리 내기" -details: "좀 ë”" +details: "ìžì„¸ížˆ" chooseEmoji: "ì´ëª¨ì§€ ì„ íƒ" unableToProcess: "ìž‘ì—… 다 몬 í–ˆì‹ë‹ˆë‹¤" recentUsed: "최근 ì“´ 놈" @@ -569,7 +574,11 @@ userSilenced: "ìš” ê²Œì •ì€... 수ᇚ혀 있ì‹ë‹ˆë‹¤." relays: "ë¦´ë ˆì´" addRelay: "ë¦´ë ˆì´ ì˜‡ê¸°" addedRelays: "ì˜‡ì€ ë¦´ë ˆì´" +deletedNote: "ë‰ìº” 걸" enableInfiniteScroll: "알아서 ë” ë³´ê¸°" +useCw: "ë‚´ìš© 수ᇚ후기" +description: "설멩" +describeFile: "캡션 옇기" author: "ë§¨ë˜ ì‚¬ëžŒ" manage: "간리" emailServer: "ì „ìžìš°íŽœ 서버" @@ -598,6 +607,7 @@ renotesCount: "리노트한 수" renotedCount: "ë¦¬ë…¸íŠ¸ë´ ìˆ˜" followingCount: "팔로우 수" followersCount: "팔로워 수" +noteFavoritesCount: "질겨찾기한 노트 수" clips: "í´ë¦½ 맨걸기" clearCache: "ìºì‹œ 비우기" unlikeConfirm: "좋네예럴 무룹니꺼?" @@ -606,6 +616,7 @@ user: "사용ìž" administration: "간리" on: "í‚´" off: "껌" +hide: "수ᇚ후기" clickToFinishEmailVerification: "[{ok}]럴 누질ë¼ì„œ ì „ìžìš°íŽœ ì •ë©©ì–¼ 껕내ì´ì†Œ." searchByGoogle: "찾기" tenMinutes: "ì‹ ë¶„" @@ -624,9 +635,12 @@ role: "ì˜‰í• " noRole: "ì˜‰í• ì´ ì—†ì‹ë‹ˆë‹¤" thisPostMayBeAnnoyingCancel: "ì•„ì´ì˜ˆ" likeOnly: "좋네예마" +myClips: "ë‚´ í´ë¦½" icon: "아바타" replies: "답하기" renotes: "리노트" +attach: "옇기" +surrender: "ì•„ì´ì˜ˆ" _initialAccountSetting: startTutorial: "길ë¼ìž¡ì´ 하기" _initialTutorial: @@ -639,9 +653,52 @@ _initialTutorial: title: "길ë¼ìž¡ì´ê°€ 껕낫ì‹ë‹ˆë‹¤!🎉" _achievements: _types: + _notes1: + description: "첫 노트럴 섯어예" + _notes10: + description: "노트럴 10번 섰어예" + _notes100: + description: "노트럴 100번 섰어예" + _notes500: + description: "노트럴 500번 섰어예" + _notes1000: + description: "노트럴 1,000번 섰어예" + _notes5000: + description: "노트럴 5,000번 섰어예" + _notes10000: + description: "노트럴 10,000번 섰어예" + _notes20000: + description: "노트럴 20,000번 섰어예" + _notes30000: + description: "노트럴 30,000번 섰어예" + _notes40000: + description: "노트럴 40,000번 섰어예" + _notes50000: + description: "노트럴 50,000번 섰어예" + _notes60000: + description: "노트럴 60,000번 섰어예" + _notes70000: + description: "노트럴 70,000번 섰어예" + _notes80000: + description: "노트럴 80,000번 섰어예" + _notes90000: + description: "노트럴 90,000번 섰어예" + _notes100000: + description: "노트럴 100,000번 섰어예" + _noteClipped1: + description: "첫 노트럴 í´ë¦½í–‡ì–´ì˜ˆ" + _noteFavorited1: + description: "첫 노트럴 ì§ˆê²¨ì°¾ê¸°ì— ë‹´ì•—ì–´ì˜ˆ" + _myNoteFavorited1: + description: "다런 ì‚¬ëžŒì´ ë‚´ 노트럴 ì§ˆê²¨ì°¾ê¸°ì— ë‹´ì•—ì‹ë‹ˆë‹¤" + _iLoveMisskey: + description: "“I ⤠#Misskeyâ€ëŸ´ 섰어예" + _postedAt0min0sec: + description: "0분 0ì´ˆì— ë…¸íŠ¸ë¥¼ 섰어예" _tutorialCompleted: description: "길ë¼ìž¡ì´ëŸ´ 껕냇ì‹ë‹ˆë‹¤" _gallery: + my: "ë‚´ 걸" liked: "좋네예한 걸" like: "좋네예!" unlike: "좋네예 무루기" @@ -652,7 +709,12 @@ _serverDisconnectedBehavior: reload: "알아서 새로곤침" _channel: removeBanner: "배너 ë‰ìºê¸°" + usersCount: "{n}명 참여" + notesCount: "노트 {n}ê°œ" +_menuDisplay: + hide: "수ᇚ후기" _theme: + description: "설멩" keys: mention: "멘션" _sfx: @@ -661,6 +723,9 @@ _sfx: _2fa: step3Title: "í•™ì¸ ê¸°í˜¸ëŸ´ 서기" renewTOTPCancel: "뎃어예" +_permissions: + "read:favorites": "질겨찾기 보기" + "write:favorites": "질겨찾기 곤치기" _widgets: profile: "프로필" instanceInfo: "서버 ì •ë³´" @@ -672,7 +737,10 @@ _widgets: _userList: chooseList: "리스트 개리기" _cw: + hide: "수ᇚ후기" show: "ë” ë³¼ëž˜ì˜ˆ" + chars: "ê±¸ìž {count}ê°œ" + files: "íŒŒì¼ {count}ê°œ" _visibility: home: "ëœë¨¸ë¦¬" followers: "팔로워" @@ -680,6 +748,8 @@ _profile: name: "ì´ëŸ¼" username: "ì‚¬ìš©ìž ì´ëŸ¼" _exportOrImport: + favoritedNotes: "질겨찾기한 노트" + clips: "í´ë¦½ 맨걸기" followingList: "팔로잉" muteList: "수ᇚ후기" blockingList: "차단하기" @@ -689,16 +759,20 @@ _charts: _timelines: home: "ëœë¨¸ë¦¬" _play: + my: "ë‚´ í”Œë ˆì´" script: "스í¬ë¦½íŠ¸" + summary: "설멩" _pages: like: "좋네예" unlike: "좋네예 무루기" + my: "ë‚´ 페ì´ì§€" blocks: image: "ì´ë¯¸ì§€" _note: id: "노트 ì•„ì´ë””" _notification: youWereFollowed: "새 팔로워가 잇ì‹ë‹ˆë‹¤" + newNote: "새 걸" _types: follow: "팔로잉" mention: "멘션" @@ -721,3 +795,6 @@ _moderationLogTypes: deleteUserAnnouncement: "ì‚¬ìš©ìž ê³µì§€ 걸 ë‰ìºê¸°" resetPassword: "비밀번호 ìž¬ì„¤ì •" resolveAbuseReport: "ì‹ ê³ í•´ê²”í•˜ê¸°" +_reversi: + total: "합계" + diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 4a13012eed9dff9df902210bf4c76a93e586ea13..877ae6b217be9e7b705341b823fd92774f0c1c28 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -130,6 +130,7 @@ overwriteFromPinnedEmojis: "ì¼ë°˜ ì„¤ì •ì„ ë®ì–´ì“°ê¸°" reactionSettingDescription2: "ëŒì–´ì„œ 순서 변경, í´ë¦í•´ì„œ ì‚ì œ, +를 눌러서 ì¶”ê°€í• ìˆ˜ 있습니다." rememberNoteVisibility: "공개 범위를 기억하기" attachCancel: "첨부 취소" +deleteFile: "íŒŒì¼ ì‚ì œ" markAsSensitive: "열람주ì˜ë¡œ ì„¤ì •" unmarkAsSensitive: "ì—´ëžŒì£¼ì˜ í•´ì œ" enterFileName: "파ì¼ëª…ì„ ìž…ë ¥" @@ -278,7 +279,7 @@ uploadFromUrl: "URL 업로드" uploadFromUrlDescription: "ì—…ë¡œë“œí•˜ë ¤ëŠ” 파ì¼ì˜ URL" uploadFromUrlRequested: "업로드를 ìš”ì²í–ˆìŠµë‹ˆë‹¤" uploadFromUrlMayTakeTime: "업로드가 완료ë 때까지 ì‹œê°„ì´ ì†Œìš”ë 수 있습니다." -explore: "발견하기" +explore: "둘러보기" messageRead: "ì½ìŒ" noMoreHistory: "ì´ê²ƒë³´ë‹¤ ê³¼ê±°ì˜ ê¸°ë¡ì´ 없습니다" startMessaging: "대화 시작하기" @@ -379,6 +380,11 @@ hcaptcha: "hCaptcha" enableHcaptcha: "hCaptcha 활성화" hcaptchaSiteKey: "사ì´íŠ¸ 키" hcaptchaSecretKey: "ì‹œí¬ë¦¿ 키" +mcaptcha: "mCaptcha" +enableMcaptcha: "mCaptcha 활성화" +mcaptchaSiteKey: "사ì´íŠ¸ 키" +mcaptchaSecretKey: "ì‹œí¬ë¦¿ 키" +mcaptchaInstanceUrl: "mCaptcha ì¸ìŠ¤í„´ìŠ¤ URL" recaptcha: "reCAPTCHA" enableRecaptcha: "reCAPTCHA 활성화" recaptchaSiteKey: "사ì´íŠ¸ 키" @@ -626,6 +632,7 @@ medium: "보통" small: "작게" generateAccessToken: "액세스 í† í° ìƒì„±" permission: "권한" +adminPermission: "ê´€ë¦¬ìž ê¶Œí•œ" enableAll: "ì „ì²´ ì„ íƒ" disableAll: "ì „ì²´ í•´ì œ" tokenRequested: "ê³„ì • ì ‘ê·¼ 허용" @@ -669,6 +676,7 @@ useGlobalSettingDesc: "활성화하면 ê³„ì •ì˜ ì•Œë¦¼ ì„¤ì •ì´ ì ìš©ë©ë‹ˆ other: "기타" regenerateLoginToken: "ë¡œê·¸ì¸ í† í°ì„ 재ìƒì„±" regenerateLoginTokenDescription: "로그ì¸í• ë•Œ 사용ë˜ëŠ” 내부 í† í°ì„ 재ìƒì„±í•©ë‹ˆë‹¤. ì¼ë°˜ì 으로 ì´ ìž‘ì—…ì„ ì‹¤í–‰í• í•„ìš”ëŠ” 없습니다. ì´ ê¸°ëŠ¥ì„ ì‚¬ìš©í•˜ë©´ ì´ ê³„ì •ìœ¼ë¡œ 로그ì¸í•œ ëª¨ë“ ê¸°ê¸°ì—ì„œ 로그아웃ë©ë‹ˆë‹¤." +theKeywordWhenSearchingForCustomEmoji: "맞춤 ì´ëª¨í‹°ì½˜ì„ ê²€ìƒ‰í• ë•Œ 키워드가 ë©ë‹ˆë‹¤." setMultipleBySeparatingWithSpace: "공백으로 구분하여 여러 ê°œ ì„¤ì •í• ìˆ˜ 있습니다." fileIdOrUrl: "íŒŒì¼ ID ë˜ëŠ” URL" behavior: "ë™ìž‘" @@ -983,6 +991,7 @@ neverShow: "다시 보지 않기" remindMeLater: "ë‚˜ì¤‘ì— ì•Œë¦¼" didYouLikeMisskey: "Misskeyê°€ 마ìŒì— 드시나요?" pleaseDonate: "Misskey는 {host} ì„œë²„ì˜ ë¬´ë£Œ 소프트웨어입니다. ì•žìœ¼ë¡œë„ ê°œë°œì„ ì´ì–´ ë‚˜ê°€ë ¤ë©´ 후ì›ì´ ì ˆì‹¤ížˆ 필요합니다!" +correspondingSourceIsAvailable: "소스 코드는 {anchor}ì—ì„œ 받아보실 수 있습니다." roles: "ì—í• " role: "ì—í• " noRole: "ì—í• ì´ ì—†ìŠµë‹ˆë‹¤" @@ -1014,7 +1023,7 @@ internalServerError: "내부 서버 오류" internalServerErrorDescription: "내부 서버ì—ì„œ 예기치 ì•Šì€ ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤." copyErrorInfo: "오류 ì •ë³´ 복사" joinThisServer: "ì´ ì„œë²„ì— ê°€ìž…" -exploreOtherServers: "다른 서버 둘러보기" +exploreOtherServers: "다른 서버 찾기" letsLookAtTimeline: "타임ë¼ì¸ 구경하기" disableFederationConfirm: "ì •ë§ë¡œ ì—°í•©ì„ ë„ì‹œê² ìŠµë‹ˆê¹Œ?" disableFederationConfirmWarn: "ì—°í•©ì„ ë„ë”ë¼ë„ ê²Œì‹œë¬¼ì´ ë¹„ê³µê°œë¡œ ì „í™˜ë˜ëŠ” ê²ƒì€ ì•„ë‹™ë‹ˆë‹¤. ëŒ€ë¶€ë¶„ì˜ ê²½ìš° ì—°í•©ì„ ë¹„í™œì„±í™”í• í•„ìš”ê°€ 없습니다." @@ -1033,6 +1042,9 @@ resetPasswordConfirm: "비밀번호를 ìž¬ì„¤ì •í•˜ì‹œê² ìŠµë‹ˆê¹Œ?" sensitiveWords: "민ê°í•œ 단어" sensitiveWordsDescription: "ì„¤ì •í•œ 단어가 í¬í•¨ëœ ë…¸íŠ¸ì˜ ê³µê°œ 범위를 '홈'으로 ê°•ì œí•©ë‹ˆë‹¤. 개행으로 구분하여 여러 개를 ì§€ì •í• ìˆ˜ 있습니다." sensitiveWordsDescription2: "공백으로 구분하면 AND ì§€ì •ì´ ë˜ë©°, 키워드를 슬래시로 둘러싸면 ì •ê·œ 표현ì‹ì´ ë©ë‹ˆë‹¤." +prohibitedWords: "금지 워드" +prohibitedWordsDescription: "ì„¤ì •ëœ ì›Œë“œê°€ í¬í•¨ë˜ëŠ” 노트를 ìž‘ì„±í•˜ë ¤ê³ í•˜ë©´, ì—러가 ë°œìƒí•˜ë„ë¡ í•©ë‹ˆë‹¤. 줄바꿈으로 구분지어 복수 ì„¤ì •í• ìˆ˜ 있습니다." +prohibitedWordsDescription2: "공백으로 구분하면 AND ì§€ì •ì´ ë˜ë©°, 키워드를 슬래시로 둘러싸면 ì •ê·œ 표현ì‹ì´ ë©ë‹ˆë‹¤." hiddenTags: "숨긴 해시태그" hiddenTagsDescription: "ì„¤ì •í•œ 태그를 íŠ¸ë Œë“œì— í‘œì‹œí•˜ì§€ ì•Šë„ë¡ í•©ë‹ˆë‹¤. 줄 바꿈으로 하나씩 ë‚˜ëˆ ì„œ ì„¤ì •í• ìˆ˜ 있습니다." notesSearchNotAvailable: "노트 ê²€ìƒ‰ì„ ì´ìš©í•˜ì‹¤ 수 없습니다." @@ -1051,6 +1063,8 @@ limitWidthOfReaction: "ë¦¬ì•¡ì…˜ì˜ ìµœëŒ€ íì„ ì œí•œí•˜ê³ ìž‘ê²Œ 표시하 noteIdOrUrl: "노트 ID ë° URL" video: "ë™ì˜ìƒ" videos: "ë™ì˜ìƒ" +audio: "소리" +audioFiles: "소리" dataSaver: "ë°ì´í„° ì ˆì•½ 모드" accountMigration: "ê³„ì • ì´ë™" accountMoved: "ì´ ì‚¬ìš©ìžëŠ” ë‹¤ìŒ ê³„ì •ìœ¼ë¡œ ì´ì‚¬í–ˆìŠµë‹ˆë‹¤:" @@ -1151,9 +1165,16 @@ showRepliesToOthersInTimeline: "타임ë¼ì¸ì— 다른 사람ì—게 보내는 hideRepliesToOthersInTimeline: "타임ë¼ì¸ì— 다른 사람ì—게 보내는 ë‹µê¸€ì„ í¬í•¨í•˜ì§€ ì•ŠìŒ" showRepliesToOthersInTimelineAll: "타임ë¼ì¸ì— 현재 팔로우 ì¤‘ì¸ ì‚¬ëžŒ ì „ì›ì˜ ë‹µê¸€ì„ í¬í•¨í•˜ê²Œ 하기" hideRepliesToOthersInTimelineAll: "타임ë¼ì¸ì— 현재 팔로우 ì¤‘ì¸ ì‚¬ëžŒ ì „ì›ì˜ ë‹µê¸€ì´ ë‚˜ì˜¤ì§€ 않게 하기" -confirmShowRepliesAll: "ì´ ì¡°ìž‘ì€ ë˜ëŒë¦´ 수 없습니다. ì •ë§ë¡œ 타임ë¼ì¸ì— 현재 팔로우 ì¤‘ì¸ ì‚¬ëžŒ ì „ì›ì˜ ë‹µê¸€ì´ ë‚˜ì˜¤ì§€ 않게 í•˜ì‹œê² ìŠµë‹ˆê¹Œ?" +confirmShowRepliesAll: "ì´ ì¡°ìž‘ì€ ë˜ëŒë¦´ 수 없습니다. ì •ë§ë¡œ 타임ë¼ì¸ì— 현재 팔로우 ì¤‘ì¸ ì‚¬ëžŒ ì „ì›ì˜ ë‹µê¸€ì´ ë‚˜ì˜¤ê²Œ í•˜ì‹œê² ìŠµë‹ˆê¹Œ?" confirmHideRepliesAll: "ì´ ì¡°ìž‘ì€ ë˜ëŒë¦´ 수 없습니다. ì •ë§ë¡œ 타임ë¼ì¸ì— 현재 팔로우 ì¤‘ì¸ ì‚¬ëžŒ ì „ì›ì˜ ë‹µê¸€ì´ ë‚˜ì˜¤ì§€ 않게 í•˜ì‹œê² ìŠµë‹ˆê¹Œ?" externalServices: "외부 서비스" +sourceCode: "소스 코드" +sourceCodeIsNotYetProvided: "소스 코드를 ì•„ì§ ì œê³µí•˜ì§€ 않습니다. ì´ ë¬¸ì œë¥¼ í•´ê²°í•˜ë ¤ë©´ 관리ìžì—게 문ì˜í•´ 주세요." +repositoryUrl: "ì €ìž¥ì†Œ URL" +repositoryUrlDescription: "소스 코드를 공개한 ì €ìž¥ì†Œê°€ 있는 경우, ê·¸ URLì„ ì 습니다. Misskey를 ì›ë³¸ 그대로 (소스 코드를 ì–´ë–¤ ì‹ìœ¼ë¡œë„ 변경하지 ì•Šê³ ) ì“°ê³ ìžˆëŠ” 경우 https://github.com/misskey-dev/misskey ë¼ê³ ì 습니다." +repositoryUrlOrTarballRequired: "ì €ìž¥ì†Œë¥¼ 공개하지 ì•Šì€ ê²½ìš° ëŒ€ì‹ tarballì„ ì œê³µí• í•„ìš”ê°€ 있습니다. 세부사í•ì€ .config/example.ymlì„ ì°¸ì¡°í•´ 주세요." +feedback: "피드백" +feedbackUrl: "피드백 URL" impressum: "ìš´ì˜ìž ì •ë³´" impressumUrl: "ìš´ì˜ìž ì •ë³´ URL" impressumDescription: "ë…ì¼ ë“±ì˜ ì¼ë¶€ 나ë¼ì™€ 지ì—ì—서는 ê¼ í‘œì‹œí•´ì•¼ 합니다(Impressum)." @@ -1183,6 +1204,26 @@ seasonalScreenEffect: "ê³„ì ˆì— ë”°ë¥¸ 효과 ë³´ì´ê¸°" decorate: "장ì‹í•˜ê¸°" addMfmFunction: "ìž¥ì‹ ì¶”ê°€í•˜ê¸°" enableQuickAddMfmFunction: "ìƒê¸‰ìžìš© MFM ì„ íƒê¸° 표시하기" +bubbleGame: "버블 게임" +sfx: "효과ìŒ" +soundWillBePlayed: "소리가 재ìƒë©ë‹ˆë‹¤" +showReplay: "ë¦¬í”Œë ˆì´ ë³´ê¸°" +replay: "ë¦¬í”Œë ˆì´" +replaying: "ë¦¬í”Œë ˆì´ ì¤‘" +ranking: "ëží‚¹" +lastNDays: "최근 {n}ì¼" +backToTitle: "타ì´í‹€ë¡œ 가기" +hemisphere: "거주 지ì—" +withSensitive: "민ê°í•œ 파ì¼ì´ í¬í•¨ëœ 노트 보기" +userSaysSomethingSensitive: "{name}ì˜ ë¯¼ê°í•œ 파ì¼ì´ í¬í•¨ëœ 게시물" +enableHorizontalSwipe: "스와ì´í”„하여 íƒ ì „í™˜" +surrender: "그만ë‘기" +_bubbleGame: + howToPlay: "설명" + _howToPlay: + section1: "위치를 ì¡°ì •í•˜ì—¬ ìƒìžì— ë¬¼ê±´ì„ ë–¨ì–´ëœ¨ë¦½ë‹ˆë‹¤." + section2: "ê°™ì€ ì¢…ë¥˜ì˜ ë¬¼ê±´ì´ ë¶™ìœ¼ë©´ 다른 물건으로 바뀌면서 ì 수를 얻게 ë©ë‹ˆë‹¤." + section3: "ìƒìžì—ì„œ ë¬¼ê±´ì´ ë„˜ì¹˜ë©´ 게임 오버입니다. ìƒìžì—ì„œ ë¬¼ê±´ì´ ë„˜ì¹˜ì§€ ì•Šë„ë¡ í•˜ë©´ì„œ ë¬¼ê±´ì„ ìœµí•©í•˜ì—¬ ë†’ì€ ì 수를 íšë“하세요!" _announcement: forExistingUsers: "기존 ìœ ì €ì—게만 알림" forExistingUsersDescription: "활성화하면 ì´ ê³µì§€ì‚¬í•ì„ 게시한 ì‹œì ì—ì„œ ì´ë¯¸ 가입한 ìœ ì €ì—게만 표시합니다. 비활성화하면 게시 í›„ì— ê°€ìž…í•œ ìœ ì €ì—ê²Œë„ í‘œì‹œí•©ë‹ˆë‹¤." @@ -1553,6 +1594,13 @@ _achievements: _tutorialCompleted: title: "Misskey ìž…ë¬¸ìž ê³¼ì • 수료ì¦" description: "íŠœí† ë¦¬ì–¼ì„ ì™„ë£Œí–ˆìŠµë‹ˆë‹¤" + _bubbleGameExplodingHead: + title: "🤯" + description: "버블 게임ì—ì„œ 가장 í° ë¬¼ê±´ì„ ë‚´ë†“ì•˜ë‹¤" + _bubbleGameDoubleExplodingHead: + title: "ë”블 🤯" + description: "버블게임ì—ì„œ 가장 í° ë¬¼ê±´ 2개를 ë™ì‹œì— 내놓았다." + flavor: "ì´ ì •ë„만 ë„ì‹œë½í†µì— 🤯 🤯 조금만 ë”" _role: new: "새 ì—í• ìƒì„±" edit: "ì—í• ìˆ˜ì •" @@ -1716,6 +1764,8 @@ _aboutMisskey: contributors: "주요 기여ìž" allContributors: "ëª¨ë“ ê¸°ì—¬ìž" source: "소스 코드" + original: "ì›ë³¸" + thisIsModifiedVersion: "{name}ì—서는 ì›ë³¸ 미스키를 ìˆ˜ì •í•œ ë²„ì „ì„ ì‚¬ìš©í•˜ê³ ìžˆìŠµë‹ˆë‹¤." translation: "Misskey를 번ì—하기" donate: "Misskeyì— ê¸°ë¶€í•˜ê¸°" morePatrons: "ì´ ì™¸ì—ë„ ë‹¤ë¥¸ ë§Žì€ ë¶„ë“¤ì´ ë„ì›€ì„ ì£¼ì‹œê³ ê³„ì‹ë‹ˆë‹¤. ê°ì‚¬í•©ë‹ˆë‹¤ðŸ¥°" @@ -1761,7 +1811,7 @@ _instanceMute: title: "ì§€ì •í•œ ì„œë²„ì˜ ë…¸íŠ¸ë¥¼ 숨ê¹ë‹ˆë‹¤." heading: "ë®¤íŠ¸í• ì„œë²„" _theme: - explore: "테마 찾아보기" + explore: "테마 둘러보기" install: "테마 설치" manage: "테마 관리" code: "테마 코드" @@ -1986,7 +2036,7 @@ _permissions: "write:report-abuse": "위반 ë‚´ìš© ì‹ ê³ í•˜ê¸°" _auth: shareAccessTitle: "어플리케ì´ì…˜ì˜ ì ‘ê·¼ 허가" - shareAccess: "\"{name}\" ì´ ê³„ì •ì— ì ‘ê·¼í•˜ëŠ” ê²ƒì„ í—ˆìš©í•˜ì‹œê² ìŠµë‹ˆê¹Œ?" + shareAccess: "‘{name}’ì—ì„œ ê³„ì •ì— ì ‘ê·¼í•˜ëŠ” ê²ƒì„ í—ˆìš©í•˜ì‹œê² ìŠµë‹ˆê¹Œ?" shareAccessAsk: "ì´ ì• í”Œë¦¬ì¼€ì´ì…˜ì´ ê³„ì •ì— ì ‘ê·¼í•˜ëŠ” ê²ƒì„ í—ˆìš©í•˜ì‹œê² ìŠµë‹ˆê¹Œ?" permission: "{name}ì—ì„œ ë‹¤ìŒ ê¶Œí•œì„ ìš”ì²í•˜ì˜€ìŠµë‹ˆë‹¤" permissionAsk: "ì´ ì•±ì€ ë‹¤ìŒì˜ ê¶Œí•œì„ ìš”ì²í•©ë‹ˆë‹¤" @@ -2105,6 +2155,7 @@ _profile: _exportOrImport: allNotes: "ëª¨ë“ ë…¸íŠ¸" favoritedNotes: "ì¦ê²¨ì°¾ê¸°í•œ 노트" + clips: "í´ë¦½" followingList: "팔로잉" muteList: "뮤트" blockingList: "차단" @@ -2330,6 +2381,7 @@ _moderationLogTypes: resetPassword: "비밀번호 ìž¬ì„¤ì •" suspendRemoteInstance: "리모트 서버를 ì •ì§€" unsuspendRemoteInstance: "리모트 ì„œë²„ì˜ ì •ì§€ë¥¼ í•´ì œ" + updateRemoteInstanceNote: "리모트 ì„œë²„ì˜ ì¡°ì • ê¸°ë¡ ê°±ì‹ " markSensitiveDriveFile: "파ì¼ì— 열람주ì˜ë¥¼ ì„¤ì •" unmarkSensitiveDriveFile: "파ì¼ì— 열람주ì˜ë¥¼ í•´ì œ" resolveAbuseReport: "ì‹ ê³ ì²˜ë¦¬" @@ -2404,3 +2456,53 @@ _dataSaver: _code: title: "문ìžì—´ ê°•ì¡°" description: "MFM 등으로 문ìžì—´ ê°•ì¡° ê¸°ë²•ì„ ì‚¬ìš©í• ë•Œ 누르기 ì „ì—는 불러오지 않습니다. 문ìžì—´ ê°•ì¡°ì—서는 ê°•ì¡°í• ì–¸ì–´ë§ˆë‹¤ ê·¸ ì •ì˜ íŒŒì¼ì„ 불러와야 하지만 ì´ë¥¼ ìžë™ìœ¼ë¡œ 불러오지 않으므로 ë°ì´í„° ì‚¬ìš©ëŸ‰ì„ ì¤„ì¼ ìˆ˜ 있습니다." +_hemisphere: + N: "ë¶ë°˜êµ¬" + S: "남반구" + caption: "ì¼ë¶€ í´ë¼ì´ì–¸íŠ¸ ì„¤ì •ì—ì„œ ê³„ì ˆì„ íŒë‹¨í•˜ê¸° 위해 사용합니다." +_reversi: + reversi: "리버시" + gameSettings: "ëŒ€êµ ì„¤ì •" + chooseBoard: "ë³´ë“œ ì„ íƒ" + blackOrWhite: "ì„ ê³µ/후공" + blackIs: "{name}ë‹˜ì´ í‘(ì„ ê³µ)" + rules: "규칙" + thisGameIsStartedSoon: "대êµì´ 곧 시작ë©ë‹ˆë‹¤" + waitingForOther: "ìƒëŒ€ë°©ì˜ 준비가 완료ë˜ê¸°ë¥¼ ê¸°ë‹¤ë¦¬ê³ ìžˆìŠµë‹ˆë‹¤." + waitingForMe: "ë‹¹ì‹ ì˜ ì¤€ë¹„ê°€ 완료ë˜ê¸°ë¥¼ ê¸°ë‹¤ë¦¬ê³ ìžˆìŠµë‹ˆë‹¤." + waitingBoth: "준비하세요" + ready: "준비 완료" + cancelReady: "준비 다시 시작" + opponentTurn: "ìƒëŒ€ì˜ 차례입니다" + myTurn: "ë‹¹ì‹ ì˜ ì°¨ë¡€ìž…ë‹ˆë‹¤" + turnOf: "{name}ì˜ ì°¨ë¡€ìž…ë‹ˆë‹¤" + pastTurnOf: "{name}ì˜ ì°¨ë¡€" + surrender: "기권" + surrendered: "ê¸°ê¶Œì— ì˜í•´" + timeout: "시간 초과" + drawn: "무승부" + won: "{name}ì˜ ìŠ¹ë¦¬" + black: "í‘" + white: "ë°±" + total: "합계" + turnCount: "{count}í„´ 째" + myGames: "ë‚´ 대êµ" + allGames: "모ë‘ì˜ ëŒ€êµ" + ended: "종료" + playing: "ëŒ€êµ ì¤‘" + isLlotheo: "ëŒì´ ì ì€ ì‚¬ëžŒì´ ìŠ¹ë¦¬ (로세오)" + loopedMap: "루프 지ë„" + canPutEverywhere: "ì–´ë””ì—ë„ ë‘˜ 수 있는 모드" + timeLimitForEachTurn: "1í„´ì˜ ì‹œê°„ ì œí•œ" + freeMatch: "프리매치" + lookingForPlayer: "ìƒëŒ€ë¥¼ ì°¾ê³ ìžˆìŠµë‹ˆë‹¤" + gameCanceled: "대êµì´ 취소ë˜ì—ˆìŠµë‹ˆë‹¤" + shareToTlTheGameWhenStart: "ëŒ€êµ ì‹œìž‘ ì‹œ 타임ë¼ì¸ì— 대êµì„ 게시" + iStartedAGame: "대êµì´ 시작ë˜ì—ˆìŠµë‹ˆë‹¤! #MisskeyReversi" + opponentHasSettingsChanged: "ìƒëŒ€ë°©ì´ ì„¤ì •ì„ ë³€ê²½í–ˆìŠµë‹ˆë‹¤" + allowIrregularRules: "규칙변경 허가 (ì™„ì „ ìžìœ )" + disallowIrregularRules: "규칙변경 ì—†ìŒ" +_offlineScreen: + title: "오프ë¼ì¸ - ì„œë²„ì— ì ‘ì†í• 수 없습니다" + header: "ì„œë²„ì— ì ‘ì†í• 수 없습니다" + diff --git a/locales/lo-LA.yml b/locales/lo-LA.yml index c9e5aea1edd5fe24b0ba659bc6c72e14602e848e..6f03c914fd25afac8e0dcf8309ce046898c49e51 100644 --- a/locales/lo-LA.yml +++ b/locales/lo-LA.yml @@ -1,9 +1,9 @@ --- _lang_: "ພາສາລາວ" -headlineMisskey: "ເຊື່àºàº¡àº•à»à»ˆà»€àº„ືàºàº‚່າàºà»‚ດàºàº«àº¡àº²àºà»€àº«àº”" -introMisskey: "àºàº´àº™àº”ີຕ້àºàº™àº®àº±àºš! Misskey ເປັນà»àº«àº¼à»ˆàº‡à»€àº›àºµàº”, àºàº²àº™àºšà»àº¥àº´àºàº²àº™ microblogging àºàº°àºˆàº²àº\nສ້າງ \"ບັນທຶàº\" ເພື່àºà»àºšà»ˆàº‡àº›àº±àº™àº„ວາມຄິດຂàºàº‡àº—່ານàºàº±àºšàº—ຸàºà»†àº„ົນທີ່ຢູ່àºà»‰àºàº¡àº®àºàºšàº—່ານ 📡\nດ້ວຠ\"ປະຕິàºàº´àº¥àº´àºàº²\", ທ່ານàºàº±àº‡àºªàº²àº¡àº²àº”ສະà»àº”ງຄວາມຮູ້ສຶàºàº‚àºàº‡àº—່ານຢ່າງໄວວາàºà»ˆàº½àº§àºàº±àºšàºšàº±àº™àº—ຶàºàº‚àºàº‡àº—ຸàºà»†àº„ົນ ðŸ‘\nມາສຳຫຼວດໂລàºà»ƒà»à»ˆ! 🚀" +headlineMisskey: "ເຊື່àºàº¡àº•à»à»ˆà»€àº„ືàºàº‚່າàºà»‚ດຠnote" +introMisskey: "àºàº´àº™àº”ີຕ້àºàº™àº®àº±àºš! Misskey ເປັນຊàºàºŸà»àº§opensource, ສຳລັບບà»àº¥àº´àºàº²àº™ microblogging à»àºšàºš decentralized\nສ້າງ “note†ເພື່àºà»àºšà»ˆàº‡àº›àº±àº™àº„ວາມຄິດຂàºàº‡àº—່ານàºàº±àºšàº—ຸàºà»† ຄົນທີ່ຢູ່àºà»‰àºàº¡àº®àºàºšàº—່ານ 📡\nຢ່າລືມ “reaction†ໂນຕຂàºàº‡àº¥àº²àº§à»€àºžàº·à»ˆàºàºªàº°à»àº”ງຄວາມຮູ້ສຶຠðŸ‘\nມາສຳຫຼວດໂລàºà»ƒà»à»ˆà»àº™! 🚀" poweredByMisskeyDescription: "{name} à»àº¡à»ˆàº™àºªà»ˆàº§àº™à»œàº¶à»ˆàº‡àº‚àºàº‡àºàº²àº™àºšà»àº¥àº´àºàº²àº™àº—ີ່ຂັບເຄື່àºàº™à»‚ດàºà»àºžàº¥àº”ຟàºàº¡ open source. <b>Misskey</b> (ເàºàºµà»‰àº™àº§à»ˆàº² \"Misskey instance\")" -monthAndDay: "{ເດືàºàº™}/{ມື້}" +monthAndDay: "ເດືàºàº™{month} / ວັນ{day}" search: "ຄົ້ນຫາ" notifications: "àºàº²àº™à»àºˆà»‰àº‡à»€àº•àº·àºàº™" username: "ຊື່ຜູ້ໃຊ້" @@ -15,25 +15,25 @@ gotIt: "ເຂົ້າໃຈà»àº¥à»‰àº§!" cancel: "àºàº»àºà»€àº¥àºµàº" noThankYou: "ບà»à»ˆâ€‹à»àº¡à»ˆàº™â€‹àº•àºàº™â€‹àº™àºµà»‰" enterUsername: "ປ້àºàº™àºŠàº·à»ˆàºœàº¹à»‰à»ƒàºŠà»‰" -renotedBy: "Renoted ໂດຠ{ຜູ້ໃຊ້}" -noNotes: "ບà»à»ˆàº¡àºµàº«àº¡àº²àºà»€àº«àº”" +renotedBy: "Renoted ໂດຠ{user}" +noNotes: "ບà»à»ˆàº¡àºµ note" noNotifications: "ບà»à»ˆàº¡àºµàºàº²àº™à»àºˆà»‰àº‡à»€àº•àº·àºàº™" instance: "àºàºµàº™àºªàº°à»àº•àº™" settings: "àºàº³àº™àº»àº”ຄ່າ" notificationSettings: "ຕັ້ງຄ່າàºàº²àº™à»àºˆà»‰àº‡à»€àº•àº·àºàº™" basicSettings: "àºàº²àº™àº•àº±à»‰àº‡àº„່າພື້ນຖານ" otherSettings: "àºàº²àº™àº•àº±à»‰àº‡àº„່າàºàº·à»ˆàº™à»†" -openInWindow: "ເປີດຢູ່ໃນປ່àºàº‡àº¢à»‰àº½àº¡" +openInWindow: "ເປີດໃນປ່àºàº‡àº¢à»‰àº½àº¡" profile: "ໂພຼຟາàº" -timeline: "​ເສັ້ນàºàº³â€‹àº™àº»àº”​ເວ​ລາ​" +timeline: "ໄທມ໌ໄລນ໌" noAccountDescription: "ຜູ້ໃຊ້ນີ້àºàº±àº‡àºšà»à»ˆà»„ດ້ຂຽນໃນຊີວະປະຫວັດຂàºàº‡à»€àº‚ົາເຈົ້າເທື່àº" login: "ເຂົ້າ​ສູ່​ລະ​ບົບ" loggingIn: "àºàº³àº¥àº±àº‡à»€àº‚ົ້າສູ່ລະບົບ..." logout: "àºàºàºâ€‹àºˆàº²àºâ€‹àº¥àº°â€‹àºšàº»àºš" signup: "ລົງ​ທະ​ບຽນ" -uploading: "àºàº²àº™àºàº±àºšà»‚ຫຼດ..." +uploading: "àºàº³àº¥àº±àº‡àºàº±àºšà»‚ຫຼດ..." save: "ບັນທຶàº" -users: "ຜູ້ໃຊ້ຕ່າງໆ" +users: "ຜູ້ໃຊ້" addUser: "ເພີ່ມຜູ້ໃຊ້" favorite: "ເພີ່ມໃສ່ລາàºàºàº²àº™àº—ີ່ມັàº" favorites: "ລາàºàºàº²àº™àº—ີ່ມັàº" @@ -41,13 +41,14 @@ unfavorite: "ລຶບàºàºàºàºˆàº²àºàº¥àº²àºàºàº²àº™àº—ີ່ມັàº" favorited: "ເພີ່ມໃສ່ລາàºàºàº²àº™àº—ີ່ມັàºà»àº¥à»‰àº§" alreadyFavorited: "ເພີ່ມເຂົ້າໃນລາàºàºàº²àº™àº—ີ່ມັàºà»àº¥à»‰àº§." cantFavorite: "ບà»à»ˆàºªàº²àº¡àº²àº”ເພີ່ມໃສ່ລາàºàºàº²àº™àº—ີ່ມັàºà»„ດ້." -pin: "ປັàºà»àº¸àº”ໄປຫາໂປຣໄຟລ໌" -unpin: "ຖàºàº”ປັàºà»àº¸àº”àºàºàºàºˆàº²àºà»‚ປຣໄຟລ໌" +pin: "ປັàºà»àº¸àº”" +unpin: "ຖàºàº”ປັàºà»àº¸àº”àºàºàº" copyContent: "ຄັດລàºàºà»€àº™àº·à»‰àºàº«àº²" -copyLink: "ສຳເນົາລິ້ງ" +copyLink: "ຄັດລàºàºàº¥àº´à»‰àº‡" +copyLinkRenote: "ຄັດລàºàºàº¥àº´à»‰àº‡àº‚àºàº‡ renote" delete: "ລຶບ" -deleteAndEdit: "ລົບ​à»àº¥àº°â€‹à»àºà»‰â€‹à»„ຂ​" -deleteAndEditConfirm: "ເຈົ້າ​à»àº™à»ˆâ€‹à»ƒàºˆâ€‹àºšà»à»ˆ? ທີ່ທ່ານຕ້àºàº‡àºàº²àº™àº—ີ່ຈະລຶບບັນທຶàºàº™àºµà»‰à»àº¥àº°à»àºà»‰à»„ຂມັນ ທ່ານàºàº²àº”ຈະສູນເສàºàºàº²àº™à»‚ຕ້ຕàºàºš, ບັນທຶàº, à»àº¥àº°àºàº²àº™àº•àºàºšàºàº±àºšàº—ັງà»àº»àº”" +deleteAndEdit: "ລຶບ​à»àº¥àº°â€‹à»àºà»‰â€‹à»„ຂ​" +deleteAndEditConfirm: "ເຈົ້າ​à»àº™à»ˆâ€‹à»ƒàºˆâ€‹àºšà»à»ˆ? ທີ່ທ່ານຕ້àºàº‡àºàº²àº™àº—ີ່ຈະລຶບ note ນີ້ à»àº¥àº°à»àºà»‰à»„ຂມັນ ທ່ານàºàº²àº”ຈະສູນເສຠreaction, renote, à»àº¥àº°àºàº²àº™àº•àºàºšàºàº±àºšàº—ັງà»àº»àº”" addToList: "ເພີ່ມໃສ່ລາàºàºŠàº·à»ˆ" addToAntenna: "ເພີ່ມໃສ່ເສົາàºàº²àºàº²àº”" sendMessage: "ສົ່ງຂà»à»‰àº„ວາມ" @@ -66,15 +67,15 @@ showLess: "ປິດ" youGotNewFollower: "ໄດ້ຕິດຕາມທ່ານ" receiveFollowRequest: "ປະຕິບັດຕາມຄà»àº²àº®à»‰àºàº‡àº‚à»àº—ີ່ໄດ້ຮັບ" followRequestAccepted: "ຜູ້ຕິດຕາມໄດ້àºàºàº¡àº®àº±àºšàº„à»àº²àº®à»‰àºàº‡àº‚à»àº‚àºàº‡àº—່ານ" -mention: "ໄດ້àºà»ˆàº²àº§àº¡àº²" +mention: "àºà»ˆàº²àº§àº–ືງ" mentions: "àºà»ˆàº²àº§à»€àº–ິງ" -directNotes: "ໂດàºàºàº»àº‡àº«àº¡àº²àºà»€àº«àº”" +directNotes: "ໂພສ Direct note" importAndExport: "ນà»àº²à»€àº‚ົ້າ / ສົ່ງàºàºàº" import: "ນຳເຂົ້າ" -export: "ນຳàºàºàº" +export: "ສົ່ງàºàºàº" files: "ໄຟລ໌" download: "ດາວໂຫລດ" -driveFileDeleteConfirm: "ທ່ານà»àº™à»ˆà»ƒàºˆàºšà»à»ˆàº§à»ˆàº²àº•à»‰àºàº‡àºàº²àº™àº¥àº¶àºšà»„ຟລ໌ \"{name}\"? ບັນທຶàºàº—ີ່ມີໄຟລ໌à»àº™àºšàº™àºµà»‰àºˆàº°àº–ືàºàº¥àº¶àºšàº–ິ້ມ" +driveFileDeleteConfirm: "ທ່ານà»àº™à»ˆà»ƒàºˆàºšà»à»ˆàº§à»ˆàº²àº•à»‰àºàº‡àºàº²àº™àº¥àº¶àºšà»„ຟລ໌ \"{name}\"? note ທີ່ມີໄຟລ໌à»àº™àºšàº™àºµà»‰àºˆàº°àº–ືàºàº¥àº¶àºšàº–ິ້ມ" unfollowConfirm: "ທ່ານà»àº™à»ˆà»ƒàºˆàºšà»à»ˆàº§à»ˆàº²àº•à»‰àºàº‡àºàº²àº™à»€àºŠàº»àº²àº•àº´àº”ຕາມ {name}?" exportRequested: "ໃນເວລາທີ່ທ່ານໄດ້ຮ້àºàº‡àº‚à»àºàº²àº™àºªàº»à»ˆàº‡àºàºàº ມັນàºàº²àº”ຈະໃຊ້ເວລາບາງເວລາ à»àº¥àº°àº¡àº±àº™àºˆàº°àº–ືàºà»€àºžàºµà»ˆàº¡à»ƒàºªà»ˆ drive ຂàºàº‡àº—່ານເມື່àºàº¡àº±àº™àºªàº³à»€àº¥àº±àº”à»àº¥à»‰àº§" importRequested: "ໃນເວລາທີ່ທ່ານໄດ້ຮ້àºàº‡àº‚à»àºàº²àº™àº™à»àº²à»€àº‚ົ້າ ມັນàºàº²àº”ຈະໃຊ້ເວລາບາງເວລາ" @@ -86,7 +87,7 @@ following: "àºàº³àº¥àº±àº‡àº•àº´àº”ຕາມ" followers: "ຜູ້ຕິດຕາມ" followsYou: "ຕິດ​ຕາມ​ເຈົ້າ" createList: "ສ້າງລາàºàºŠàº·à»ˆ" -manageLists: "àºàº²àº™àºšà»àº¥àº´àº«àº²àº™àºšàº±àº™àºŠàºµàº¥àº²àºàºàº²àº™" +manageLists: "ຈັດàºàº²àº™àº¥àº²àºàºŠàº·à»ˆ" error: "ຂà»à»‰àºœàº´àº”ພາດ" somethingHappened: "​àºàº¸àº, ມີ​ບາງ​ຢ່າງ​ຜິ​ດ​ພາດ" retry: "ລàºàº‡à»ƒàº«àº¡à»ˆ" @@ -96,30 +97,30 @@ serverIsDead: "ເຊີບເວີນີ້ບà»à»ˆàº•àºàºšàºªàº°à»œàºàº‡ youShouldUpgradeClient: "ເພື່àºà»€àºšàº´à»ˆàº‡à»œà»‰àº²àº™àºµà»‰, àºàº°àº¥àº¸àº™àº²à»‚ຫຼດຂà»à»‰àº¡àº¹àº™àº„ືນໃà»à»ˆà»€àºžàº·à»ˆàºàºàº±àºšà»€àº”ດລູàºàº„້າຂàºàº‡àº—່ານ" enterListName: "ໃສ່ຊື່ສຳລັບລາàºàºŠàº·à»ˆ" privacy: "ຄວາມເປັນສ່ວນຕົວ" -makeFollowManuallyApprove: "ປະຕິບັດຕາມàºàº²àº™àº®à»‰àºàº‡àº‚à»àº®àº½àºàº®à»‰àºàº‡à»ƒàº«à»‰àº¡àºµàºàº²àº™àºàº°àº™àº¸àº¡àº±àº”" -defaultNoteVisibility: "ເປັນຄ່າເລີ່ມຕົ້ນ" +makeFollowManuallyApprove: "ຕິດຕາມຄຳຂà»àº—ີ່ຕ້àºàº‡à»„ດ້ຮັບàºàº²àº™àºàº°àº™àº¸àº¡àº±àº”" +defaultNoteVisibility: "àºàº²àº™à»€àºšàº´à»ˆàº‡à»€àº«àº±àº™àº—ີ່ເປັນຄ່າເລີ່ມຕົ້ນ" follow: "àºàº³àº¥àº±àº‡àº•àº´àº”ຕາມ" -followRequest: "ສົ່ງ​àºàº²àº™â€‹àº®à»‰àºàº‡â€‹àº‚à»â€‹àº›àº°â€‹àº•àº´â€‹àºšâ€‹àº•àº²àº¡â€‹" -followRequests: "ປະຕິບັດຕາມຄà»àº²àº®à»‰àºàº‡àº‚à»" +followRequest: "ສົ່ງ​ຄຳຂà»â€‹àº•àº´â€‹àº”​ຕາມ​" +followRequests: "ສົ່ງ​ຄຳຂà»â€‹àº•àº´â€‹àº”​ຕາມ​" unfollow: "ເຊົາຕິດຕາມ" -followRequestPending: "ປະຕິບັດຕາມຄà»àº²àº®à»‰àºàº‡àº‚à»àº—ີ່ລà»àº–້າຢູ່" -enterEmoji: "ປ້àºàº™àºàºµà»‚ມຈິ" +followRequestPending: "ລà»àº–້າàºàº²àº™àºàº°àº™àº¸àº¡àº±àº”ໃຫ້ຕິດຕາມ" +enterEmoji: "ປ້àºàº™à»€àºà»‚ມຈິ" renote: "Renote" unrenote: "ເລີຠRenote" -renoted: "ເàºàº±àºšàºšàº±àº™àº—ຶàºà»„ວ້" -cantRenote: "ໂພສນີ້ບà»à»ˆàºªàº²àº¡àº²àº”ຖືàºàºšàº±àº™àº—ຶàºà»„ວ້ຄືນໃà»à»ˆà»„ດ້" +renoted: "renote à»àº¥à»‰àº§" +cantRenote: "ໂພສນີ້ບà»à»ˆàºªàº²àº¡àº²àº” renote ໃà»à»ˆà»„ດ້" cantReRenote: "ບà»à»ˆàºªàº²àº¡àº²àº”ບັນທຶàºàº„ືນໃà»à»ˆà»„ດ້" -quote: "ລວມຂà»à»‰àº„ວາມàºà»‰àº²àº‡àºàºµàº‡" -inChannelRenote: "ຊ່àºàº‡àºžàº½àº‡à»àº•à»ˆ Renote" -inChannelQuote: "ຊ່àºàº‡à»€àº—ົ່ານັ້ນ Quote" -pinnedNote: "ບັນທຶàºàº—ີ່ປັàºà»àº¸àº”ໄວ້" -pinned: "ປັàºà»àº¸àº”ໄປຫາໂປຣໄຟລ໌" +quote: "àºà»‰àº²àº‡àºàºµàº‡" +inChannelRenote: "Renote ໃນ channel ເທົ່ານັ້ນ" +inChannelQuote: "àºà»‰àº²àº‡àºàº´àº‡à»ƒàº™ channel ເທົ່ານັ້ນ" +pinnedNote: "note ທີ່ປັàºà»àº¸àº”ໄວ້" +pinned: "ປັàºà»àº¸àº”" you: "ເຈົ້າ" clickToShow: "àºàº»àº”ເພື່àºàºªàº°à»àº”ງໃຫ້ເຫັນ" sensitive: "NSFW" add: "ເພີ່ມ" -reaction: "ປະຕິàºàº´àº¥àº´àºàº²" -reactions: "ປະຕິàºàº´àº¥àº´àºàº²" +reaction: "reaction" +reactions: "reaction" attachCancel: "ເàºàº»àº²à»„ຟລ໌à»àº™àºš" mute: "ປີດສຽງ" unmute: "ເປີດສຽງ" @@ -306,6 +307,8 @@ basicInfo: "ຂà»à»‰àº¡àº¸àº™à»€àºšàº·à»‰àºàº‡àº•àº»à»‰àº™" pinnedNotes: "ບັນທຶàºàº—ີ່ປັàºà»àº¸àº”ໄວ້" hcaptchaSiteKey: "àºàº°à»àºˆà»„ຊທ໌" hcaptchaSecretKey: "àºàº°à»àºˆàº¥àº±àºš" +mcaptchaSiteKey: "àºàº°à»àºˆà»„ຊທ໌" +mcaptchaSecretKey: "àºàº°à»àºˆàº¥àº±àºš" recaptcha: "reCAPTCHA" enableRecaptcha: "ເປີດໃຊ້ງານລີà»àº„໋ບຈາ" recaptchaSiteKey: "àºàº°à»àºˆà»„ຊທ໌" @@ -463,3 +466,4 @@ _webhookSettings: name: "ຊື່" _moderationLogTypes: suspend: "ລະງັບ" + diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml index 1e96a1aa9812eb9b70f09830c73e6050a70b2bc4..42fbf183bef91cf4ba18f48920e038ed06127520 100644 --- a/locales/nl-NL.yml +++ b/locales/nl-NL.yml @@ -348,6 +348,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Inschakelen hCaptcha" hcaptchaSiteKey: "Site sleutel" hcaptchaSecretKey: "Geheime sleutel" +mcaptchaSiteKey: "Site sleutel" +mcaptchaSecretKey: "Geheime sleutel" recaptcha: "reCAPTCHA" enableRecaptcha: "Inschakelen reCAPTCHA" recaptchaSiteKey: "Site sleutel" @@ -495,3 +497,4 @@ _webhookSettings: _moderationLogTypes: suspend: "Opschorten" resetPassword: "Wachtwoord terugzetten" + diff --git a/locales/no-NO.yml b/locales/no-NO.yml index 195b1d071779e8a394a992324ecf63114a3cb0f3..098faa8addd75f871e925bccaa5c50a8f98b03be 100644 --- a/locales/no-NO.yml +++ b/locales/no-NO.yml @@ -463,6 +463,7 @@ options: "Alternativ" icon: "Avatar" replies: "Svar" renotes: "Renote" +surrender: "Avbryt" _initialAccountSetting: theseSettingsCanEditLater: "Du kan endre disse innstillingene senere." _achievements: @@ -720,3 +721,4 @@ _webhookSettings: name: "Navn" _moderationLogTypes: suspend: "Suspender" + diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index b0f9f4923dcafe3fc6823312654e53b5e10772c7..3f9fa5f5f1bcd5dc607f73325c4d2d6da78165f2 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -345,6 +345,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "WÅ‚Ä…cz hCaptcha" hcaptchaSiteKey: "Klucz strony" hcaptchaSecretKey: "Tajny klucz" +mcaptchaSiteKey: "Klucz strony" +mcaptchaSecretKey: "Tajny klucz" recaptcha: "reCAPTCHA" enableRecaptcha: "WÅ‚Ä…cz reCAPTCHA" recaptchaSiteKey: "Klucz strony" @@ -869,6 +871,7 @@ youFollowing: "Åšledzeni" icon: "Awatar" replies: "Odpowiedzi" renotes: "UdostÄ™pnieÅ„" +sourceCode: "Kod źródÅ‚owy" flip: "Odwróć" _role: priority: "Priorytet" @@ -1232,6 +1235,7 @@ _profile: _exportOrImport: allNotes: "Wszystkie wpisy" favoritedNotes: "Ulubione wpisy" + clips: "Klip" followingList: "Obserwowani" muteList: "Wycisz" blockingList: "Zablokuj" @@ -1394,3 +1398,6 @@ _webhookSettings: _moderationLogTypes: suspend: "ZawieÅ›" resetPassword: "Zresetuj hasÅ‚o" +_reversi: + total: "ÅÄ…cznie" + diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml index 1fd2fd57e1cb59cefce9cecceff57aafeeea7142..26657e7b52958446b4935e17e793a566b4261ac5 100644 --- a/locales/pt-PT.yml +++ b/locales/pt-PT.yml @@ -368,6 +368,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Ativar hCaptcha" hcaptchaSiteKey: "Chave do sÃtio ‘web’" hcaptchaSecretKey: "Chave secreta" +mcaptchaSiteKey: "Chave do sÃtio ‘web’" +mcaptchaSecretKey: "Chave secreta" recaptcha: "reCAPTCHA" enableRecaptcha: "Habilitar reCAPTCHA" recaptchaSiteKey: "Chave do sÃtio ‘web’" @@ -1008,6 +1010,8 @@ replies: "Respostas" renotes: "Repostagens" keepScreenOn: "Manter a tela do dispositivo sempre ligada" flip: "Inversão" +lastNDays: "Últimos {n} dias" +surrender: "Cancelar" _initialAccountSetting: followUsers: "Siga usuários que lhe interessam para criar a sua linha do tempo." _serverSettings: @@ -1400,6 +1404,7 @@ _profile: username: "Nome de usuário" _exportOrImport: favoritedNotes: "Notas nos favoritos" + clips: "Clipe" followingList: "Seguindo" muteList: "Silenciar" blockingList: "Bloquear" @@ -1494,3 +1499,6 @@ _webhookSettings: _moderationLogTypes: suspend: "Suspender" resetPassword: "Redefinir senha" +_reversi: + total: "Total" + diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml index bf8787413b0fa563fc9613f32f3ec99f8a902881..e45b8b75ec1beb66735e49bb1dad1e23c7308470 100644 --- a/locales/ro-RO.yml +++ b/locales/ro-RO.yml @@ -359,6 +359,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Activează hCaptcha" hcaptchaSiteKey: "Site key" hcaptchaSecretKey: "Secret key" +mcaptchaSiteKey: "Site key" +mcaptchaSecretKey: "Secret key" recaptcha: "reCAPTCHA" enableRecaptcha: "Activează reCAPTCHA" recaptchaSiteKey: "Site key" @@ -725,3 +727,6 @@ _webhookSettings: _moderationLogTypes: suspend: "Suspendă" resetPassword: "Resetează parola" +_reversi: + total: "Total" + diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 25f409df922ebbe0ae7d1b8101ff58944e6a63d8..d666b694905de612fbb57a3afc9b25efe068e1fc 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -53,8 +53,8 @@ addToAntenna: "Добавить к антенне" sendMessage: "Отправить Ñообщение" copyRSS: "Скопировать RSS" copyUsername: "Скопировать Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ" -copyUserId: "Скопировать ID пользователÑ" -copyNoteId: "Скопировать ID заметки" +copyUserId: "Скопировать идентификатор пользователÑ" +copyNoteId: "Скопировать идентификатор заметки" copyFileId: "Скопировать ID файла" copyFolderId: "Скопировать ID папки" copyProfileUrl: "Скопировать URL Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ " @@ -134,8 +134,8 @@ unmarkAsSensitive: "СнÑÑ‚ÑŒ отметку «не Ð´Ð»Ñ Ð²Ñех»" enterFileName: "Введите Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°" mute: "Скрыть" unmute: "Отменить Ñкрытие" -renoteMute: "Заглушить репоÑÑ‚Ñ‹" -renoteUnmute: "Включить репоÑÑ‚Ñ‹" +renoteMute: "Скрыть репоÑÑ‚Ñ‹" +renoteUnmute: "Открыть репоÑÑ‚Ñ‹" block: "Заблокировать" unblock: "Разблокировать" suspend: "Заморозить" @@ -161,8 +161,8 @@ addEmoji: "Добавить Ñмодзи" settingGuide: "Рекомендуемые наÑтройки" cacheRemoteFiles: "Кешировать внешние файлы" cacheRemoteFilesDescription: "Когда Ñта наÑтройка отключена, файлы Ñ Ð´Ñ€ÑƒÐ³Ð¸Ñ… Ñайтов будут загружатьÑÑ Ð¿Ñ€Ñмо оттуда. Ðто ÑÑкономит меÑто на Ñервере, но увеличит трафик, так как не будут ÑоздаватьÑÑ ÑÑкизы." -cacheRemoteSensitiveFiles: "Кешировать внешние файлы" -cacheRemoteSensitiveFilesDescription: "ОпиÑание удаленных внешних файлов в кÑше" +cacheRemoteSensitiveFiles: "КÑшировать внешние файлы «не Ð´Ð»Ñ Ð²Ñех»" +cacheRemoteSensitiveFilesDescription: "ЕÑли отключено, файлы «не Ð´Ð»Ñ Ð²Ñех» загружаютÑÑ Ð½ÐµÐ¿Ð¾ÑредÑтвенно Ñ ÑƒÐ´Ð°Ð»Ñ‘Ð½Ð½Ñ‹Ñ… Ñерверов, не кÑшируÑÑÑŒ." flagAsBot: "Ðккаунт бота" flagAsBotDescription: "Включите, еÑли Ñтот аккаунт управлÑетÑÑ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¼Ð¾Ð¹. Ðто позволит ÑиÑтеме Misskey учитывать Ñто, а также поможет разработчикам других ботов предотвратить беÑконечные циклы взаимодейÑтвиÑ." flagAsCat: "Ðккаунт кота" @@ -261,6 +261,7 @@ removed: "Удалено" removeAreYouSure: "Хотите удалить «{x}»?" deleteAreYouSure: "Хотите удалить «{x}»?" resetAreYouSure: "Ðа Ñамом деле ÑброÑить?" +areYouSure: "Ð’Ñ‹ уверены?" saved: "Сохранено" messaging: "СообщениÑ" upload: "Загрузить" @@ -278,7 +279,7 @@ noMoreHistory: "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð·Ð°ÐºÐ¾Ð½Ñ‡Ð¸Ð»Ð°ÑÑŒ" startMessaging: "Ðачать общение" nUsersRead: "Прочитали {n}" agreeTo: "Я ÑоглашаюÑÑŒ Ñ {0}" -agree: "СоглаÑитьÑÑ" +agree: "СоглаÑен" agreeBelow: "СоглаÑен Ñо Ñледующими" basicNotesBeforeCreateAccount: "ЗапиÑи, перед Ñозданием аккаунта" termsOfService: "УÑÐ»Ð¾Ð²Ð¸Ñ Ð¸ÑпользованиÑ" @@ -324,7 +325,7 @@ copyUrl: "Копировать ÑÑылку" rename: "Переименовать" avatar: "Ðватар" banner: "Шапка" -displayOfSensitiveMedia: "Определение деликатного контента" +displayOfSensitiveMedia: "Отображение Ñодержимого не Ð´Ð»Ñ Ð²Ñех" whenServerDisconnected: "Когда Ñоединение Ñ Ñервером потерÑно" disconnectedFromServer: "Разорвано Ñоединение Ñ Ñервером" reload: "Перезагрузить" @@ -372,6 +373,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Включить hCaptcha" hcaptchaSiteKey: "Ключ Ñайта" hcaptchaSecretKey: "Секретный ключ" +mcaptchaSiteKey: "Ключ Ñайта" +mcaptchaSecretKey: "Секретный ключ" recaptcha: "reCAPTCHA" enableRecaptcha: "Включить reCAPTCHA" recaptchaSiteKey: "Ключ Ñайта" @@ -413,7 +416,7 @@ about: "ОпиÑание" aboutMisskey: "О Misskey" administrator: "ÐдминиÑтратор" token: "Токен" -2fa: "2-Ñ… Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð°Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ" +2fa: "Ð”Ð²ÑƒÑ…Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð°Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ" setupOf2fa: "ÐаÑтроить двухфакторную аутентификацию" totp: "Приложение-аутентификатор" totpDescription: "ОпиÑание приложениÑ-аутентификатора" @@ -477,7 +480,7 @@ aboutX: "ОпиÑание {x}" emojiStyle: "Стиль Ñмодзи" native: "СиÑтемные" disableDrawer: "Ðе иÑпользовать выдвижные меню" -showNoteActionsOnlyHover: "Показывать кнопки ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ¾Ð¹ только при наведении" +showNoteActionsOnlyHover: "Показывать кнопки у заметок только при наведении" noHistory: "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð¿Ð¾ÐºÐ° пуÑта" signinHistory: "Журнал поÑещений" enableAdvancedMfm: "Включить раÑширенный MFM" @@ -490,8 +493,8 @@ createAccount: "ÐÐ¾Ð²Ð°Ñ ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ" existingAccount: "СущеÑÑ‚Ð²ÑƒÑŽÑ‰Ð°Ñ ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ" regenerate: "Создать повторно" fontSize: "Размер шрифта" -mediaListWithOneImageAppearance: "Показывать ÑпиÑок медиа только одним изображением" -limitTo: "Обрезать до {x}" +mediaListWithOneImageAppearance: "Вид изображениÑ, еÑли оно единÑтвенное в ÑпиÑке" +limitTo: "Ограничить до {x}" noFollowRequests: "ÐераÑÑмотренные запроÑÑ‹ на подпиÑку отÑутÑтвуют" openImageInNewTab: "Открыть изображение в новой вкладке" dashboard: "Панель управлениÑ" @@ -525,7 +528,7 @@ objectStorageUseSSLDesc: "Отключите, еÑли не ÑÐ¾Ð±Ð¸Ñ€Ð°ÐµÑ‚ÐµÑ objectStorageUseProxy: "ИÑпользовать прокÑи" objectStorageUseProxyDesc: "Отключите, еÑли не будете иÑпоьзовать прокÑи Ð´Ð»Ñ Ñоединений по протоколу ObjectStorage." objectStorageSetPublicRead: "УÑтанавливать public-read при загрузке на Ñервер" -s3ForcePathStyleDesc: "Включение s3ForcePathStyle принудительно указывает Ð¸Ð¼Ñ ÐºÐ¾Ñ€Ð·Ð¸Ð½Ñ‹ как чаÑÑ‚ÑŒ пути в URL-адреÑе вмеÑто имени хоÑта. Может потребоватьÑÑ Ð°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ð¸Ñ Ð¿Ñ€Ð¸ иÑпользовании таких вещей, как локальный Minio." +s3ForcePathStyleDesc: "Включение s3ForcePathStyle приводит к тому, что Ð¸Ð¼Ñ ÐºÐ¾Ñ€Ð·Ð¸Ð½Ñ‹ указываетÑÑ ÐºÐ°Ðº чаÑÑ‚ÑŒ пути в URL, а не в имени хоÑта. Может потребоватьÑÑ Ð²ÐºÐ»ÑŽÑ‡Ð¸Ñ‚ÑŒ при иÑпользовании локального Minio или чего-то подобного." serverLogs: "Журнал Ñервера" deleteAll: "Удалить вÑÑ‘" showFixedPostForm: "Показывать поле Ð´Ð»Ñ Ð²Ð²Ð¾Ð´Ð° новой заметки наверху ленты" @@ -569,7 +572,7 @@ yourAccountSuspendedTitle: "Ðта ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ заблокир yourAccountSuspendedDescription: "Ðта ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ была заблокирована из-за Ð½Ð°Ñ€ÑƒÑˆÐµÐ½Ð¸Ñ ÑƒÑловий предоÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÑƒÑлуг Ñервера. СвÑжитеÑÑŒ Ñ Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñтратором, еÑли вы хотите узнать более подробную причину. ПожалуйÑта, не Ñоздавайте новую учетную запиÑÑŒ." tokenRevoked: "Токен недейÑтвителен" tokenRevokedDescription: "Срок дейÑÑ‚Ð²Ð¸Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ токена входа иÑтек. ПожалуйÑта, войдите Ñнова." -accountDeleted: "Ðта ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ удалена" +accountDeleted: "Ð£Ñ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ удалена" accountDeletedDescription: "Ðта ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ удалена" menu: "Меню" divider: "ЛиниÑ-разделитель" @@ -651,6 +654,7 @@ useGlobalSettingDesc: "ЕÑли включено, будут иÑпользов other: "Другие" regenerateLoginToken: "Создать новый токен Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ð°" regenerateLoginTokenDescription: "Создаёт новый токен, иÑпользуемый внутри программы во Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ…Ð¾Ð´Ð°. Обычно в Ñтом нет необходимоÑти. При Ñоздании вÑе уÑтройÑтва будут отключены." +theKeywordWhenSearchingForCustomEmoji: "Ðто ключевое Ñлово будет иÑпользовано при поиÑке Ñмодзи." setMultipleBySeparatingWithSpace: "Можно напиÑать неÑколько через пробел" fileIdOrUrl: "Идентификатор файла или ÑÑылка" behavior: "Поведение" @@ -721,7 +725,7 @@ useSystemFont: "ИÑпользовать шрифт, предлагаемый Ñ clips: "Подборки" experimentalFeatures: "ÐкÑпериментальные функции" experimental: "ÐкÑпериментальные" -thisIsExperimentalFeature: "Ðто ÑкÑÐ¿ÐµÑ€Ð¸Ð¼ÐµÐ½Ñ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ. ТехничеÑкие характериÑтики могут изменитьÑÑ Ð¸Ð»Ð¸ он может работать неправильно." +thisIsExperimentalFeature: "Ðто ÑкÑÐ¿ÐµÑ€Ð¸Ð¼ÐµÐ½Ñ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ. Её поведение, вероÑтно, поменÑетÑÑ Ð² Ñледующей верÑии, а ещё она может работать не так, как задумано." developer: "Разработчик" makeExplorable: "Опубликовать профиль в «Обзоре»." makeExplorableDescription: "ЕÑли выключить, ваш профиль не будет показан в разделе «Обзор»." @@ -806,7 +810,7 @@ noMaintainerInformationWarning: "Ðе заполнены ÑÐ²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾Ð± noBotProtectionWarning: "Ботозащита не наÑтроена" configure: "ÐаÑтроить" postToGallery: "Опубликовать в галерею" -postToHashtag: "Опубликовать поÑÑ‚ Ñ Ñтим хештегом" +postToHashtag: "ÐапиÑать заметку Ñ Ñтим Ñ…Ñштегом" gallery: "ГалереÑ" recentPosts: "Ðедавние публикации" popularPosts: "ПопулÑрные публикации" @@ -835,7 +839,7 @@ useBlurEffect: "Размытие в интерфейÑе" learnMore: "Подробнее" misskeyUpdated: "Misskey обновилÑÑ!" whatIsNew: "Что новенького?" -translate: "Перевод" +translate: "ПеревеÑти" translatedFrom: "Перевод. Язык оригинала — {x}" accountDeletionInProgress: "Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÑетÑÑ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ðµ учетной запиÑи" usernameInfo: "ИмÑ, которое отличает вашу учетную запиÑÑŒ от других на Ñтом Ñервере. Ð’Ñ‹ можете иÑпользовать алфавит (a~z, A~Z), цифры (0~9) или Ñимволы Ð¿Ð¾Ð´Ñ‡ÐµÑ€ÐºÐ¸Ð²Ð°Ð½Ð¸Ñ (_). Имена пользователей не могут быть изменены позже." @@ -847,11 +851,11 @@ lastCommunication: "ПоÑледнее Ñообщение" resolved: "Решено" unresolved: "Без решениÑ" breakFollow: "ОтпиÑка" -breakFollowConfirm: "Удалить из подпиÑок Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ ?" +breakFollowConfirm: "ДейÑтвительно удалить Ñтого подпиÑчика?" itsOn: "Включено" itsOff: "Выключено" -on: "Вкл" -off: "Выкл" +on: "Вкл." +off: "Выкл." emailRequiredForSignup: "Ð”Ð»Ñ Ñ€ÐµÐ³Ð¸Ñтрации учётной запиÑи нужен Ð°Ð´Ñ€ÐµÑ Ñлектронной почты" unread: "Ðепрочитанное" filter: "Фильтры" @@ -880,7 +884,7 @@ numberOfColumn: "КоличеÑтво Ñтолбцов" searchByGoogle: "ПоиÑк" instanceDefaultLightTheme: "Ð¡Ð²ÐµÑ‚Ð»Ð°Ñ Ñ‚ÐµÐ¼Ð° по умолчанию" instanceDefaultDarkTheme: "Ð¢ÐµÐ¼Ð½Ð°Ñ Ñ‚ÐµÐ¼Ð° по умолчанию" -instanceDefaultThemeDescription: "ОпиÑание темы по умолчанию Ð´Ð»Ñ Ð¸Ð½ÑтанÑа" +instanceDefaultThemeDescription: "Введите код темы в формате объекта." mutePeriod: "ПродолжительноÑÑ‚ÑŒ ÑкрытиÑ" period: "ÐžÐ¿Ñ€Ð¾Ñ Ð´Ð»Ð¸Ñ‚ÑÑ" indefinitely: "вечно" @@ -904,7 +908,7 @@ thereIsUnresolvedAbuseReportWarning: "ОÑталиÑÑŒ нерешённые жа recommended: "Рекомендуем" check: "Проверить" driveCapOverrideLabel: "Изменение лимита диÑкового проÑтранÑтва Ð´Ð»Ñ Ñтого пользователÑ" -driveCapOverrideCaption: "Укажите меньше или равное нулю Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹" +driveCapOverrideCaption: "Введите нуль или меньше, чтобы иÑпользовать значение по умолчанию." requireAdminForView: "Ð”Ð»Ñ Ð¿Ñ€Ð¾Ñмотра необходимо иметь аккаунт админиÑтратора" isSystemAccount: "Ð”Ð°Ð½Ð½Ð°Ñ ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ Ñоздана автоматичеÑки и управлÑетÑÑ ÑиÑтемой" typeToConfirm: "Введите {x} Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ñ" @@ -924,7 +928,7 @@ type: "Тип" speed: "СкороÑÑ‚ÑŒ" slow: "МедленнаÑ" fast: "БыÑтраÑ" -sensitiveMediaDetection: "Определение Ñодержимого деликатного характера" +sensitiveMediaDetection: "РаÑпознание Ñодержимого не Ð´Ð»Ñ Ð²Ñех" localOnly: "Локально" remoteOnly: "Только удалённо" failedToUpload: "Сбой выгрузки" @@ -1001,15 +1005,17 @@ invitationRequiredToRegister: "Ðтот Ñервер в наÑтоÑщее вр emailNotSupported: "ДоÑтавка почты не поддерживаетÑÑ Ð½Ð° Ñтом Ñервере" postToTheChannel: "Отправить в канал" cannotBeChangedLater: "Ðто Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ð·Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ позже" -reactionAcceptance: "ПринÑтие реакций" -likeOnly: "Только лайки" -likeOnlyForRemote: "Только лайки Ñ ÑƒÐ´Ð°Ð»Ñ‘Ð½Ð½Ñ‹Ñ… Ñерверов" -nonSensitiveOnly: "БезопаÑный Ñерфинг" +reactionAcceptance: "ДопуÑтимые реакции" +likeOnly: "Только «нравитÑÑ!»" +likeOnlyForRemote: "Ð’ÑÑ‘ (Ñ Ð´Ñ€ÑƒÐ³Ð¸Ñ… Ñерверов только «нравитÑÑ!»)" +nonSensitiveOnly: "Только безопаÑные" +nonSensitiveOnlyForLocalLikeOnlyForRemote: "Только безопаÑные (Ñ Ð´Ñ€ÑƒÐ³Ð¸Ñ… Ñерверов только «нравитÑÑ!»)" rolesAssignedToMe: "Мои роли" resetPasswordConfirm: "СброÑить пароль?" sensitiveWords: "ЧувÑтвительные Ñлова" sensitiveWordsDescription: "УÑтановите общедоÑтупный диапазон заметки, Ñодержащей заданное Ñлово, на домашний. Можно Ñделать неÑколько наÑтроек, разделив их переноÑами Ñтрок." sensitiveWordsDescription2: "Разделение пробелом Ñоздаёт Ñпецификацию AND, а разделение коÑой чертой Ñоздаёт регулÑрное выражение." +prohibitedWordsDescription2: "Разделение пробелом Ñоздаёт Ñпецификацию AND, а разделение коÑой чертой Ñоздаёт регулÑрное выражение." notesSearchNotAvailable: "ПоиÑк заметок недоÑтупен" license: "ЛицензиÑ" unfavoriteConfirm: "Удалить избранное?" @@ -1024,20 +1030,20 @@ noteIdOrUrl: "ID или ÑÑылка на заметку" video: "Видео" videos: "Видео" dataSaver: "ÐÐºÐ¾Ð½Ð¾Ð¼Ð¸Ñ Ñ‚Ñ€Ð°Ñ„Ð¸ÐºÐ°" -accountMigration: "ПеренеÑти учётную запиÑÑŒ" -accountMoved: "Ð£Ñ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ перенеÑена" +accountMigration: "ÐŸÐµÑ€ÐµÐ½Ð¾Ñ ÑƒÑ‡Ñ‘Ñ‚Ð½Ð¾Ð¹ запиÑи" +accountMoved: "Ð£Ñ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ перенеÑена" accountMovedShort: "Ðта ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ перемещена" -operationForbidden: "Ðта Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð½ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð°." +operationForbidden: "Ðто дейÑтвие запрещено" forceShowAds: "Ð’Ñегда отображать рекламу" -addMemo: "Добавить заметку" -editMemo: "Редактировать заметку" -reactionsList: "Реакции" +addMemo: "Добавить памÑтку" +editMemo: "Изменить памÑтку" +reactionsList: "СпиÑок реакций" renotesList: "РепоÑÑ‚Ñ‹" -notificationDisplay: "Отображение уведомлениÑ" -leftTop: "Верхний левый угол" -rightTop: "Сверху Ñправа" -leftBottom: "Снизу Ñлева" -rightBottom: "Снизу Ñправа" +notificationDisplay: "Отображение уведомлений" +leftTop: "Влево вверх" +rightTop: "Вправо вверх" +leftBottom: "Влево вниз" +rightBottom: "Вправо вниз" vertical: "ВертикальнаÑ" horizontal: "Сбоку" position: "ПозициÑ" @@ -1076,7 +1082,10 @@ icon: "Ðватар" replies: "Ответы" renotes: "РепоÑÑ‚" loadReplies: "Показать ответы" +sourceCode: "ИÑходный код" flip: "Переворот" +lastNDays: "ПоÑледние {n} Ñут" +surrender: "Ðтот поÑÑ‚ не может быть отменен." _initialAccountSetting: accountCreated: "Ðккаунт уÑпешно Ñоздан!" letsStartAccountSetup: "Давайте наÑтроим вашу учётную запиÑÑŒ." @@ -1693,7 +1702,7 @@ _weekday: _widgets: profile: "Профиль" instanceInfo: "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾Ð± инÑтанÑе" - memo: "ÐапоминаниÑ" + memo: "ПамÑтки" notifications: "УведомлениÑ" timeline: "Лента" calendar: "Календарь" @@ -1784,6 +1793,7 @@ _profile: _exportOrImport: allNotes: "Ð’Ñе заметки\n" favoritedNotes: "Избранное" + clips: "Подборка" followingList: "ПодпиÑки" muteList: "Скрытые" blockingList: "Заблокированные" @@ -1959,4 +1969,10 @@ _webhookSettings: active: "Вкл." _moderationLogTypes: suspend: "Заморозить" + addCustomEmoji: "Добавлено Ñмодзи" + updateCustomEmoji: "Изменено Ñмодзи" + deleteCustomEmoji: "Удалено Ñмодзи" resetPassword: "Ð¡Ð±Ñ€Ð¾Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ:" +_reversi: + total: "Ð’Ñего" + diff --git a/locales/si-LK.yml b/locales/si-LK.yml index ed97d539c095cf1413af30cc23dea272095b97dd..cd21505a47e530a967e3c44bd2a772d1b8d08bd7 100644 --- a/locales/si-LK.yml +++ b/locales/si-LK.yml @@ -1 +1,2 @@ --- + diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index ccd9767695d515302eb660cfa16af472d36e9377..f280f91270291dd18a7387bebfc2b9bbfdc17005 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -349,6 +349,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Zapnúť hCaptchu" hcaptchaSiteKey: "Site key" hcaptchaSecretKey: "Secret key" +mcaptchaSiteKey: "Site key" +mcaptchaSecretKey: "Secret key" recaptcha: "reCAPTCHA" enableRecaptcha: "Zapnúť ReCAPTCHA" recaptchaSiteKey: "Site key" @@ -917,7 +919,9 @@ youFollowing: "Sledované" icon: "Avatar" replies: "Odpovede" renotes: "PreposlaÅ¥" +sourceCode: "Zdrojový kód" flip: "PreklopiÅ¥" +lastNDays: "Posledných {n} dnÃ" _role: priority: "Priorita" _priority: @@ -1284,6 +1288,7 @@ _profile: changeBanner: "ZmeniÅ¥ banner" _exportOrImport: allNotes: "VÅ¡etky poznámky" + clips: "Klip" followingList: "Sledujete" muteList: "Vypnúť zvuk" blockingList: "ZablokovaÅ¥" @@ -1441,3 +1446,6 @@ _webhookSettings: _moderationLogTypes: suspend: "ZmraziÅ¥" resetPassword: "ResetovaÅ¥ heslo" +_reversi: + total: "Celkom" + diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml index 4defa3b11f60544922d6465355e68b662ecde7cf..76b9bc90b75bc0cf2b2bae4c308bbe06e9122f4e 100644 --- a/locales/sv-SE.yml +++ b/locales/sv-SE.yml @@ -345,6 +345,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Aktivera hCaptcha" hcaptchaSiteKey: "Webbplatsnyckel" hcaptchaSecretKey: "Hemlig nyckel" +mcaptchaSiteKey: "Webbplatsnyckel" +mcaptchaSecretKey: "Hemlig nyckel" recaptcha: "reCAPTCHA" enableRecaptcha: "Aktivera reCAPTCHA" recaptchaSiteKey: "Webbplatsnyckel" @@ -574,3 +576,4 @@ _webhookSettings: _moderationLogTypes: suspend: "Suspendera" resetPassword: "Ã…terställ Lösenord" + diff --git a/locales/th-TH.yml b/locales/th-TH.yml index d94cfbfc5123e507bb181bc62ba0c3bb9215fdde..60c34d84b9f775c9f08b0c8c734adaaeeb53098b 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -1,20 +1,20 @@ --- _lang_: "ภาษาไทย" -headlineMisskey: "เชื่à¸à¸¡à¸•à¹ˆà¸à¸£à¸°à¸šà¸š Network ด้วย Note" -introMisskey: "ยินดีต้à¸à¸™à¸£à¸±à¸šà¸—ุà¸à¸„นจ้า! Misskey คืภบริà¸à¸²à¸£à¹„มโครบล็à¸à¸à¸à¸´à¹‰à¸‡ (MicroBlogging) à¹à¸šà¸šà¸à¸£à¸°à¸ˆà¸²à¸¢à¸¨à¸¹à¸™à¸¢à¹Œà¸à¸³à¸™à¸²à¸ˆ (Decentralized) \n\nเขียน \"โน้ต (Note)\" เพื่à¸à¸ªà¹ˆà¸‡à¸•à¹ˆà¸à¹€à¸£à¸·à¹ˆà¸à¸‡à¸£à¸²à¸§à¸‚à¸à¸‡à¸„ุณให้ทั้งโลà¸à¹„ด้รับรู้📡\nà¹à¸¥à¸°à¸à¸¢à¹ˆà¸²à¸¥à¸·à¸¡à¸—ี่จะ \"React\" à¸à¸±à¸šà¹€à¸£à¸·à¹ˆà¸à¸‡à¸£à¸²à¸§à¸‚à¸à¸‡à¸„นà¸à¸·à¹ˆà¸™ ๆ ด้วย! ðŸ‘\n\nมุ่งสู่โลà¸à¹ƒà¸šà¹ƒà¸«à¸¡à¹ˆà¸à¸±à¸™à¹€à¸–à¸à¸°ðŸš€" +headlineMisskey: "เชื่à¸à¸¡à¸•à¹ˆà¸à¹€à¸„รืà¸à¸‚่ายโดยโน้ต" +introMisskey: "ยินดีต้à¸à¸™à¸£à¸±à¸šà¸—ุà¸à¸„นจ้า! Misskey คืภซà¸à¸Ÿà¸•à¹Œà¹à¸§à¸£à¹Œà¹‚à¸à¹€à¸žà¸™à¸‹à¸à¸£à¹Œà¸ªà¸ªà¸³à¸«à¸£à¸±à¸šà¸šà¸£à¸´à¸à¸²à¸£à¹„มโครบล็à¸à¸à¸à¸´à¹‰à¸‡ (MicroBlogging) à¹à¸šà¸šà¸à¸£à¸°à¸ˆà¸²à¸¢à¸¨à¸¹à¸™à¸¢à¹Œà¸à¸³à¸™à¸²à¸ˆ (Decentralized) \n\nเขียน “โน้ต (Note)†เพื่à¸à¸ªà¹ˆà¸‡à¸•à¹ˆà¸à¹€à¸£à¸·à¹ˆà¸à¸‡à¸£à¸²à¸§à¸‚à¸à¸‡à¸„ุณให้ทั้งโลà¸à¹„ด้รับรู้📡\nà¹à¸¥à¸°à¸à¸¢à¹ˆà¸²à¸¥à¸·à¸¡à¸—ี่จะ “รีà¹à¸à¸„ชั่น†à¸à¸±à¸šà¹€à¸£à¸·à¹ˆà¸à¸‡à¸£à¸²à¸§à¸‚à¸à¸‡à¸„นà¸à¸·à¹ˆà¸™ ๆ ด้วยนะ! ðŸ‘\n\nท่à¸à¸‡à¸ªà¸³à¸£à¸§à¸ˆà¹‚ลà¸à¹ƒà¸šà¹ƒà¸«à¸¡à¹ˆà¸à¸±à¸™à¹€à¸–à¸à¸°ðŸš€" poweredByMisskeyDescription: "{name} เป็นส่วนหนึ่งในบริà¸à¸²à¸£à¸—ี่ถูà¸à¸‚ับเคลื่à¸à¸™à¹‚ดยà¹à¸žà¸¥à¸•à¸Ÿà¸à¸£à¹Œà¸¡à¹‚à¸à¹€à¸žà¹ˆà¸™à¸‹à¸à¸£à¹Œà¸ª <b>Misskey</b> (เรียà¸à¸§à¹ˆà¸² \"à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ Misskey\")" monthAndDay: "{month}/{day}" search: "ค้นหา" notifications: "à¸à¸²à¸£à¹€à¹€à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™" username: "ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" password: "รหัสผ่าน" -forgotPassword: "ลืมรหัสผ่านใช่ไหม" -fetchingAsApObject: "à¸à¸³à¸¥à¸±à¸‡à¸”ึงข้à¸à¸¡à¸¹à¸¥ จาภเฟดิเวิร์ส..." -ok: "โà¸à¹€à¸„" +forgotPassword: "ลืมรหัสผ่าน" +fetchingAsApObject: "à¸à¸³à¸¥à¸±à¸‡à¸”ึงข้à¸à¸¡à¸¹à¸¥à¸ˆà¸²à¸à¸ªà¸«à¸žà¸±à¸™à¸˜à¹Œ..." +ok: "ตà¸à¸¥à¸‡" gotIt: "เข้าใจà¹à¸¥à¹‰à¸§ !" cancel: "ยà¸à¹€à¸¥à¸´à¸" -noThankYou: "ไม่เป็นไร" -enterUsername: "ใส่ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" +noThankYou: "ไม่เà¸à¸²à¸”ีà¸à¸§à¹ˆà¸²" +enterUsername: "à¸à¸£à¸à¸à¸Šà¸·à¹ˆà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" renotedBy: "รีโน้ตโดย {user}" noNotes: "ไม่มีโน้ต" noNotifications: "ไม่มีà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™" @@ -26,30 +26,30 @@ otherSettings: "à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าà¸à¸·à¹ˆà¸™à¹†" openInWindow: "เปิดในหน้าต่าง" profile: "โปรไฟล์" timeline: "ไทม์ไลน์" -noAccountDescription: "ผู้ใช้รายนี้ยังไม่ได้เขียนลงประวัติขà¸à¸‡à¸žà¸§à¸à¹€à¸‚า" +noAccountDescription: "ผู้ใช้รายนี้ยังไม่ได้เขียนคำà¹à¸™à¸°à¸™à¸³à¸•à¸±à¸§" login: "เข้าสู่ระบบ" loggingIn: "à¸à¸³à¸¥à¸±à¸‡à¹€à¸‚้าสู่ระบบ" logout: "à¸à¸à¸à¸ˆà¸²à¸à¸£à¸°à¸šà¸š" signup: "สร้างบัà¸à¸Šà¸µà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" -uploading: "à¸à¸³à¸¥à¸±à¸‡à¸à¸±à¸žà¹‚หลด..." +uploading: "à¸à¸³à¸¥à¸±à¸‡à¸à¸±à¸›à¹‚หลด" save: "บันทึà¸" users: "ผู้ใช้งาน" addUser: "เพิ่มผู้ใช้" favorite: "รายà¸à¸²à¸£à¹‚ปรด" favorites: "รายà¸à¸²à¸£à¹‚ปรด" unfavorite: "ลบà¸à¸à¸à¸ˆà¸²à¸à¸£à¸²à¸¢à¸à¸²à¸£à¹‚ปรด" -favorited: "เพิ่มà¹à¸¥à¹‰à¸§à¹ƒà¸™à¸£à¸²à¸¢à¸à¸²à¸£à¹‚ปรด" -alreadyFavorited: "เพิ่มในรายà¸à¸²à¸£à¹‚ปรดà¸à¸¢à¸¹à¹ˆà¹à¸¥à¹‰à¸§" -cantFavorite: "ไม่สามารถเพิ่มในรายà¸à¸²à¸£à¹‚ปรดได้" -pin: "ปัà¸à¸«à¸¡à¸¸à¸”ไปยังโปรไฟล์" -unpin: "เลิà¸à¸›à¸±à¸à¸«à¸¡à¸¸à¸”จาà¸à¹‚ปรไฟล์" +favorited: "เพิ่มลงรายà¸à¸²à¸£à¹‚ปรดà¹à¸¥à¹‰à¸§" +alreadyFavorited: "เพิ่มลงรายà¸à¸²à¸£à¹‚ปรดà¸à¸¢à¸¹à¹ˆà¹à¸¥à¹‰à¸§" +cantFavorite: "ไม่สามารถเพิ่มลงรายà¸à¸²à¸£à¹‚ปรดได้" +pin: "ปัà¸à¸«à¸¡à¸¸à¸”" +unpin: "เลิà¸à¸›à¸±à¸à¸«à¸¡à¸¸à¸”" copyContent: "คัดลà¸à¸à¹€à¸™à¸·à¹‰à¸à¸«à¸²" copyLink: "คัดลà¸à¸à¸¥à¸´à¸‡à¸à¹Œ" copyLinkRenote: "คัดลà¸à¸à¸¥à¸´à¸‡à¸à¹Œà¸£à¸µà¹‚น้ต" delete: "ลบ" deleteAndEdit: "ลบà¹à¸¥à¸°à¹à¸à¹‰à¹„ข" -deleteAndEditConfirm: "นายà¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¹€à¸«à¸£à¸? ว่าต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¹‚น้ตนี้à¹à¸¥à¸°à¹à¸à¹‰à¹„ข คุณà¸à¸²à¸ˆà¸ˆà¸°à¸ªà¸¹à¸à¹€à¸ªà¸µà¸¢à¸à¸²à¸£à¹‚ต้ตà¸à¸š, โน้ต, à¹à¸¥à¸°à¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¸—ั้งหมดได้นะ" -addToList: "เพิ่มในลิสต์" +deleteAndEditConfirm: "คุณต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¹‚น้ตนี้à¹à¸¥à¸°à¹à¸à¹‰à¹„ขใหม่ใช่ไหม? รีà¹à¸à¸„ชั่น รีโน้ต à¹à¸¥à¸°à¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¸•à¹ˆà¸à¹‚น้ตนี้ทั้งหมดจะถูà¸à¸¥à¸šà¸à¸à¸à¸”้วย" +addToList: "เพิ่มลงรายชื่à¸" addToAntenna: "เพิ่มไปยังเสาà¸à¸²à¸à¸²à¸¨" sendMessage: "ส่งข้à¸à¸„วาม" copyRSS: "คัดลà¸à¸ RSS" @@ -59,35 +59,35 @@ copyNoteId: "คัดลà¸à¸ ID โน้ต " copyFileId: "คัดลà¸à¸à¹„ฟล์ ID" copyFolderId: "คัดลà¸à¸à¹‚ฟลเดà¸à¸£à¹Œ ID" copyProfileUrl: "คัดลà¸à¸à¹‚ปรไฟล์ URL" -searchUser: "ค้นหาผู้ใช้งาน" +searchUser: "ค้นหาผู้ใช้" reply: "ตà¸à¸šà¸à¸¥à¸±à¸š" -loadMore: "โหลดเพิ่มเติม" +loadMore: "à¹à¸ªà¸”งเพิ่มเติม" showMore: "à¹à¸ªà¸”งเพิ่มเติม" showLess: "ปิด" youGotNewFollower: "ได้ติดตามคุณ" -receiveFollowRequest: "คำขà¸à¸œà¸¹à¹‰à¸•à¸´à¸”ตามที่ได้รับ" -followRequestAccepted: "ผู้ติดตามได้ตà¸à¸šà¸£à¸±à¸šà¸„ำขà¸à¸£à¹‰à¸à¸‡à¸‚à¸à¸‡à¸„ุณà¹à¸¥à¹‰à¸§" +receiveFollowRequest: "มีคำขà¸à¸•à¸´à¸”ตามส่งมาหา" +followRequestAccepted: "à¸à¸²à¸£à¸•à¸´à¸”ตามได้รับà¸à¸²à¸£à¸à¸™à¸¸à¸¡à¸±à¸•à¸´à¹à¸¥à¹‰à¸§" mention: "à¸à¸¥à¹ˆà¸²à¸§à¸–ึง" mentions: "พูดถึง" -directNotes: "ไดเร็คโน้ต" +directNotes: "โพสต์à¹à¸šà¸šà¹„ดเร็à¸à¸•à¹Œ" importAndExport: "นำเข้า / ส่งà¸à¸à¸" import: "นำเข้า" -export: "นำà¸à¸à¸" +export: "ส่งà¸à¸à¸" files: "ไฟล์" download: "ดาวน์โหลด" -driveFileDeleteConfirm: "คุณต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¹„ฟล์ \"{name}\" ใช่หรืà¸à¹„ม่? โน้ตย่à¸à¸—ี่à¹à¸™à¸šà¸¡à¸²à¸à¸±à¸šà¹„ฟล์นี้à¸à¹‡à¸ˆà¸°à¸–ูà¸à¸¥à¸šà¹„ปด้วย" -unfollowConfirm: "นายà¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¸«à¸£à¸à¸§à¹ˆà¸²à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸•à¸´à¸”ตาม {name}?" -exportRequested: "เมื่à¸à¸„ุณได้ร้à¸à¸‡à¸‚à¸à¸à¸²à¸£à¸ªà¹ˆà¸‡à¸à¸à¸ à¸à¸²à¸ˆà¸ˆà¸°à¸•à¹‰à¸à¸‡à¹ƒà¸Šà¹‰à¹€à¸§à¸¥à¸²à¸ªà¸±à¸à¸„รู่ à¹à¸¥à¸°à¸ˆà¸°à¸–ูà¸à¹€à¸žà¸´à¹ˆà¸¡à¹ƒà¸™à¹„ดรฟ์ขà¸à¸‡à¸„ุณเมื่à¸à¹€à¸ªà¸£à¹‡à¸ˆà¸ªà¸´à¹‰à¸™à¹à¸¥à¹‰à¸§" -importRequested: "เมื่à¸à¸„ุณได้ร้à¸à¸‡à¸‚à¸à¸à¸²à¸£à¸™à¸³à¹€à¸‚้า à¸à¸²à¸ˆà¸ˆà¸°à¸•à¹‰à¸à¸‡à¹ƒà¸Šà¹‰à¹€à¸§à¸¥à¸²à¸ªà¸±à¸à¸„รู่นะ" -lists: "รายà¸à¸²à¸£" -noLists: "คุณไม่มีลิสต์ใด ๆ" +driveFileDeleteConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¹„ฟล์ “{name}†ใช่ไหม? โน้ตที่à¹à¸™à¸šà¸¡à¸²à¸à¸±à¸šà¹„ฟล์นี้à¸à¹‡à¸ˆà¸°à¸–ูà¸à¸¥à¸šà¹„ปด้วย" +unfollowConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸•à¸´à¸”ตาม {name} ใช่ไหม?" +exportRequested: "คุณได้ร้à¸à¸‡à¸‚à¸à¸à¸²à¸£à¸ªà¹ˆà¸‡à¸à¸à¸ à¸à¸²à¸ˆà¹ƒà¸Šà¹‰à¹€à¸§à¸¥à¸²à¸ªà¸±à¸à¸„รู่ à¹à¸¥à¸°à¸ˆà¸°à¸–ูà¸à¹€à¸žà¸´à¹ˆà¸¡à¹ƒà¸™à¹„ดรฟ์ขà¸à¸‡à¸„ุณเมื่à¸à¹€à¸ªà¸£à¹‡à¸ˆà¸ªà¸´à¹‰à¸™à¹à¸¥à¹‰à¸§" +importRequested: "คุณได้ร้à¸à¸‡à¸‚à¸à¸à¸²à¸£à¸™à¸³à¹€à¸‚้า à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¸à¸²à¸ˆà¹ƒà¸Šà¹‰à¹€à¸§à¸¥à¸²à¸ªà¸±à¸à¸„รู่" +lists: "รายชื่à¸" +noLists: "คุณไม่มีรายชื่à¸à¹ƒà¸”ๆ" note: " โน้ต" -notes: "ตัวโน้ต" +notes: " โน้ต" following: "à¸à¸³à¸¥à¸±à¸‡à¸•à¸´à¸”ตาม" followers: "ผู้ติดตาม" followsYou: "ติดตามคุณ" -createList: "สร้างลิสต์" -manageLists: "จัดà¸à¸²à¸£à¸¥à¸´à¸ªà¸•à¹Œ" +createList: "สร้างรายชื่à¸" +manageLists: "จัดà¸à¸²à¸£à¸£à¸²à¸¢à¸Šà¸·à¹ˆà¸" error: "ผิดพลาด!" somethingHappened: "à¸à¸¸à¹Šà¸¢ ! มีà¸à¸°à¹„รบางà¸à¸¢à¹ˆà¸²à¸‡à¸œà¸´à¸”พลาด" retry: "ลà¸à¸‡à¹ƒà¸«à¸¡à¹ˆà¸à¸µà¸à¸„รั้ง" @@ -95,74 +95,81 @@ pageLoadError: "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¹‚ห pageLoadErrorDescription: "โดยปà¸à¸•à¸´à¹à¸¥à¹‰à¸§à¸¡à¸±à¸à¸ˆà¸°à¹€à¸à¸´à¸”จาà¸à¸‚้à¸à¸œà¸´à¸”พลาดขà¸à¸‡à¹€à¸„รืà¸à¸‚่ายหรืà¸à¹à¸„ชขà¸à¸‡à¹€à¸šà¸£à¸²à¸§à¹Œà¹€à¸‹à¸à¸£à¹Œ ลà¸à¸‡à¸¥à¹‰à¸²à¸‡à¹à¸„ชà¹à¸¥à¹‰à¸§à¸¥à¸à¸‡à¹ƒà¸«à¸¡à¹ˆà¸à¸µà¸à¸„รั้งหลังจาà¸à¸£à¸à¸ªà¸±à¸à¸„รู่ " serverIsDead: "เซิร์ฟเวà¸à¸£à¹Œà¸™à¸µà¹‰à¹„ม่มีà¸à¸²à¸£à¸•à¸à¸šà¸ªà¸™à¸à¸‡ โปรดà¸à¸£à¸¸à¸“ารà¸à¸ªà¸±à¸à¸„รู่à¹à¸¥à¹‰à¸§à¸¥à¸à¸‡à¹ƒà¸«à¸¡à¹ˆà¸à¸µà¸à¸„รั้ง" youShouldUpgradeClient: "หาà¸à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸”ูหน้านี้ à¸à¸£à¸¸à¸“าโหลดหน้าใหม่เพื่à¸à¸à¸±à¸›à¹€à¸”ตไคลเà¸à¹‡à¸™à¸•à¹Œà¸‚à¸à¸‡à¸„ุณ" -enterListName: "ใส่ชื่à¸à¸ªà¸³à¸«à¸£à¸±à¸šà¸£à¸²à¸¢à¸à¸²à¸£à¸¥à¸´à¸ªà¸•à¹Œ" +enterListName: "ป้à¸à¸™à¸™à¸²à¸¡à¹€à¸£à¸µà¸¢à¸à¸‚à¸à¸‡à¸£à¸²à¸¢à¸Šà¸·à¹ˆà¸à¸Šà¸¸à¸”นี้" privacy: "ความเป็นส่วนตัว" -makeFollowManuallyApprove: "ติดตามคำขà¸à¸—ี่ต้à¸à¸‡à¹„ด้รับà¸à¸²à¸£à¸à¸™à¸¸à¸¡à¸±à¸•à¸´" +makeFollowManuallyApprove: "à¸à¸™à¸¸à¸¡à¸±à¸•à¸´à¸„ำขà¸à¸•à¸´à¸”ตามด้วยตนเà¸à¸‡" defaultNoteVisibility: "à¸à¸²à¸£à¸¡à¸à¸‡à¹€à¸«à¹‡à¸™à¸—ี่เป็นค่าเริ่มต้น" follow: "ติดตาม" followRequest: "ส่งคำขà¸à¸•à¸´à¸”ตาม" followRequests: "ส่งคำขà¸à¸•à¸´à¸”ตาม" unfollow: "เลิà¸à¸•à¸´à¸”ตาม" -followRequestPending: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸à¸”ำเนินà¸à¸²à¸£à¸£à¹‰à¸à¸‡à¸‚à¸à¸•à¸´à¸”ตาม" -enterEmoji: "ใส่à¸à¸µà¹‚มจิ" +followRequestPending: "รà¸à¸à¸™à¸¸à¸¡à¸±à¸•à¸´à¸„ำขà¸à¸•à¸´à¸”ตาม" +enterEmoji: "พิมพ์เà¸à¹‚มจิ" renote: "รีโน้ต" unrenote: "เลิà¸à¸£à¸µà¹‚น้ต" renoted: "รีโน้ตà¹à¸¥à¹‰à¸§" -cantRenote: "โพสต์นี้ไม่สามารถรีโน้ตไว้ใหม่ได้นะ" -cantReRenote: "ไม่สามารถรีโน้ตเà¸à¸²à¹„ว้ใหม่ได้นะ" +cantRenote: "โพสต์นี้ไม่สามารถรีโน้ตใหม่ได้" +cantReRenote: "รีโน้ตไม่สามารถรีโน้ตซ้ำได้" quote: "à¸à¹‰à¸²à¸‡à¸à¸´à¸‡" -inChannelRenote: "รีโน้ตช่à¸à¸‡à¹à¸Šà¸¥à¹à¸™à¸¥à¹€à¸—่านั้น" -inChannelQuote: "à¸à¹‰à¸²à¸‡à¸Šà¹ˆà¸à¸‡à¹€à¸—่านั้น" -pinnedNote: "โน้ตที่ปัà¸à¸«à¸¡à¸¸à¸”เà¸à¸²à¹„ว้" -pinned: "ปัà¸à¸«à¸¡à¸¸à¸”ไปยังโปรไฟล์" +inChannelRenote: "รีโน้ตในช่à¸à¸‡à¹€à¸—่านั้น" +inChannelQuote: "à¸à¹‰à¸²à¸‡à¸à¸´à¸‡à¹ƒà¸™à¸Šà¹ˆà¸à¸‡à¹€à¸—่านั้น" +pinnedNote: "โน้ตที่ปัà¸à¸«à¸¡à¸¸à¸”ไว้" +pinned: "ปัà¸à¸«à¸¡à¸¸à¸”" you: "คุณ" clickToShow: "คลิà¸à¹€à¸žà¸·à¹ˆà¸à¹à¸ªà¸”ง" -sensitive: "เนื้à¸à¸«à¸²à¸—ี่ละเà¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™ NSFW" +sensitive: "เนื้à¸à¸«à¸²à¸—ี่ละเà¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" add: "เพิ่ม" reaction: "รีà¹à¸à¸„ชั่น" reactions: "รีà¹à¸à¸„ชั่น" -reactionSettingDescription2: "à¸à¸”ลาà¸à¹€à¸žà¸·à¹ˆà¸à¸ˆà¸±à¸”ลำดับใหม่ à¸à¸”คลิà¸à¹€à¸žà¸·à¹ˆà¸à¸¥à¸š à¸à¸” \"+\" เพื่à¸à¹€à¸žà¸´à¹ˆà¸¡" -rememberNoteVisibility: "จดจำà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าà¸à¸²à¸£à¸¡à¸à¸‡à¹€à¸«à¹‡à¸™à¸•à¸±à¸§à¹‚น้ต" -attachCancel: "ลบไฟล์à¸à¸à¸à¸—ี่à¹à¸™à¸šà¸¡à¸²" -markAsSensitive: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" -unmarkAsSensitive: "ยà¸à¹€à¸¥à¸´à¸à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹€à¸›à¹‡à¸™ NSFW" +emojiPicker: "ตัวจิ้มเà¸à¹‚มจิ" +pinnedEmojisForReactionSettingDescription: "ตรึงเà¸à¹‚มจิไว้ด้านบนสำหรับรีà¹à¸à¸„ชั่นà¸à¸¢à¹ˆà¸²à¸‡à¹€à¸£à¹ˆà¸‡à¸”่วน" +pinnedEmojisSettingDescription: "ตรึงเà¸à¹‚มจิไว้ด้านบนสำหรับพิมพ์เà¸à¹‚มจิà¸à¸¢à¹ˆà¸²à¸‡à¹€à¸£à¹ˆà¸‡à¸”่วน" +emojiPickerDisplay: "à¹à¸ªà¸”งตัวจิ้มเà¸à¹‚มจิ" +overwriteFromPinnedEmojisForReaction: "เขียนทับà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่ารีà¹à¸à¸„ชั่น" +overwriteFromPinnedEmojis: "เขียนทับà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าทั่วไป" +reactionSettingDescription2: "ลาà¸à¹€à¸žà¸·à¹ˆà¸à¸ˆà¸±à¸”ลำดับใหม่ คลิà¸à¸—ี่เà¸à¹‚มจินั้นเพื่à¸à¸¥à¸š à¸à¸” “+†เพื่à¸à¹€à¸žà¸´à¹ˆà¸¡" +rememberNoteVisibility: "จำà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าà¸à¸²à¸£à¸¡à¸à¸‡à¹€à¸«à¹‡à¸™à¹‚น้ต" +attachCancel: "ยà¸à¹€à¸¥à¸´à¸à¹à¸™à¸šà¹„ฟล์" +deleteFile: "ลบไฟล์à¸à¸à¸" +markAsSensitive: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" +unmarkAsSensitive: "ยà¸à¹€à¸¥à¸´à¸à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" enterFileName: "พิมพ์ชื่à¸à¹„ฟล์" mute: "ปิดเสียง" unmute: "ยà¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¸›à¸´à¸”เสียง" renoteMute: "ปิดเสียงรีโน้ต" renoteUnmute: "เปิดเสียง รีโน้ต" -block: "บล็à¸à¸„" -unblock: "เลิà¸à¸›à¸´à¸”à¸à¸±à¹‰à¸™" -suspend: "ถูà¸à¸£à¸°à¸‡à¸±à¸š" -unsuspend: "ยà¸à¹€à¸¥à¸´à¸à¸£à¸°à¸‡à¸±à¸š" -blockConfirm: "คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¹€à¸«à¸£à¸? ว่าต้à¸à¸‡à¸à¸²à¸£à¸šà¸¥à¹‡à¸à¸à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰" -unblockConfirm: "คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¹€à¸«à¸£à¸? ว่าต้à¸à¸‡à¸à¸²à¸£à¸›à¸¥à¸”บล็à¸à¸„บัà¸à¸Šà¸µà¸™à¸µà¹‰" -suspendConfirm: "à¹à¸™à¹ˆà¹ƒà¸ˆà¸§à¹ˆà¸²à¸„ุณต้à¸à¸‡à¸à¸²à¸£à¸£à¸°à¸‡à¸±à¸šà¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰?" -unsuspendConfirm: "นายà¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¸«à¸£à¸? ว่าต้à¸à¸‡à¸à¸²à¸£à¸¢à¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¸£à¸°à¸‡à¸±à¸šà¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰" -selectList: "เลืà¸à¸à¸£à¸²à¸¢à¸à¸²à¸£" -editList: "à¹à¸à¹‰à¹„ขรายà¸à¸²à¸£" -selectChannel: "เลืà¸à¸à¹à¸Šà¸™à¹à¸™à¸¥" +block: "บล็à¸à¸" +unblock: "เลิà¸à¸šà¸¥à¹‡à¸à¸" +suspend: "ระงับ" +unsuspend: "เลิà¸à¸£à¸°à¸‡à¸±à¸š" +blockConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸šà¸¥à¹‡à¸à¸à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹ƒà¸Šà¹ˆà¹„หม?" +unblockConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸šà¸¥à¹‡à¸à¸à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹ƒà¸Šà¹ˆà¹„หม?" +suspendConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸£à¸°à¸‡à¸±à¸šà¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹ƒà¸Šà¹ˆà¹„หม?" +unsuspendConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¢à¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¸£à¸°à¸‡à¸±à¸šà¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹ƒà¸Šà¹ˆà¹„หม?" +selectList: "เลืà¸à¸à¸£à¸²à¸¢à¸Šà¸·à¹ˆà¸" +editList: "à¹à¸à¹‰à¹„ขรายชื่à¸" +selectChannel: "เลืà¸à¸à¸Šà¹ˆà¸à¸‡" selectAntenna: "เลืà¸à¸à¹€à¸ªà¸²à¸à¸²à¸à¸²à¸¨" editAntenna: "à¹à¸à¹‰à¹„ขเสาà¸à¸²à¸à¸²à¸¨" selectWidget: "เลืà¸à¸à¸§à¸´à¸”เจ็ต" editWidgets: "à¹à¸à¹‰à¹„ขวิดเจ็ต" editWidgetsExit: "เรียบร้à¸à¸¢" -customEmojis: "à¸à¸³à¸«à¸™à¸”à¸à¸µà¹‚มจิเà¸à¸‡" -emoji: "à¸à¸µà¹‚มจิ" +customEmojis: "เà¸à¹‚มจิที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡" +emoji: "เà¸à¹‚มจิ" emojis: "à¸à¸µà¹‚มจิ" -emojiName: "ชื่à¸à¸à¸´à¹‚มจิ" -emojiUrl: "à¸à¸´à¹‚มจิ URL" -addEmoji: "à¹à¸—รà¸à¸à¸µà¹‚มจิ" +emojiName: "ชื่à¸à¹€à¸à¹‚มจิ" +emojiUrl: "URL ขà¸à¸‡à¹€à¸à¹‚มจิ" +addEmoji: "à¹à¸—รà¸à¹€à¸à¹‚มจิ" settingGuide: "à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าที่à¹à¸™à¸°à¸™à¸³" cacheRemoteFiles: "à¹à¸„ชไฟล์ระยะไà¸à¸¥" -cacheRemoteFilesDescription: "เมื่à¸à¸›à¸´à¸”ใช้งานà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่านี้ ไฟล์ระยะไà¸à¸¥à¸™à¸±à¹‰à¸™à¸ˆà¸°à¸–ูà¸à¹‚หลดโดยตรงจาà¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥ à¹à¸•à¹ˆà¸à¸£à¸“ีà¸à¸²à¸£à¸›à¸´à¸”ใช้งานนี้จะช่วยลดปริมาณà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸žà¸·à¹‰à¸™à¸—ี่จัดเà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥ à¹à¸•à¹ˆà¹€à¸žà¸´à¹ˆà¸¡à¸›à¸£à¸´à¸¡à¸²à¸“à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ เพราะเนื่à¸à¸‡à¸ˆà¸²à¸à¸ˆà¸°à¹„ม่มีà¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¸ าพขนาดย่à¸" +cacheRemoteFilesDescription: "หาà¸à¹€à¸›à¸´à¸”ใช้งาน ไฟล์ระยะไà¸à¸¥à¸ˆà¸°à¸–ูà¸à¹à¸„ชไว้ ทำให้à¹à¸ªà¸”งภาพเร็วขึ้น à¹à¸•à¹ˆà¸à¹‡à¹ƒà¸Šà¹‰à¸žà¸·à¹‰à¸™à¸—ี่เà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥à¸‚à¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸¡à¸²à¸à¸‚ึ้นเช่นà¸à¸±à¸™ สำหรับขีดจำà¸à¸±à¸”ที่ผู้ใช้ระยะไà¸à¸¥à¸–ูà¸à¹à¸„ชไว้จะขึ้นà¸à¸¢à¸¹à¹ˆà¸à¸±à¸šà¸„วามจุไดรฟ์ตามบทบาทขà¸à¸‡à¹€à¸‚า เมื่à¸à¹€à¸à¸´à¸™à¹à¸¥à¹‰à¸§à¹„ฟล์เà¸à¹ˆà¸²à¸ˆà¸°à¸–ูà¸à¸¥à¸šà¸à¸à¸à¹à¸¥à¸°à¹€à¸à¹‡à¸šà¹€à¸›à¹‡à¸™à¸¥à¸´à¸‡à¸à¹Œà¹à¸—น หาà¸à¸›à¸´à¸”ใช้งาน ไฟล์ระยะไà¸à¸¥à¸ˆà¸°à¸–ูà¸à¹€à¸à¹‡à¸šà¹€à¸›à¹‡à¸™à¸¥à¸´à¸‡à¸à¹Œà¸•à¸±à¹‰à¸‡à¹à¸•à¹ˆà¸•à¹‰à¸™ เราà¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¸•à¸±à¹‰à¸‡à¸„่า proxyRemoteFiles ใน default.yml เป็น true เพื่à¸à¸ªà¸£à¹‰à¸²à¸‡à¸˜à¸±à¸¡à¸šà¹Œà¹€à¸™à¸¥à¹à¸¥à¸°à¸›à¸à¸›à¹‰à¸à¸‡à¸„วามเป็นส่วนตัวขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" youCanCleanRemoteFilesCache: "คุณสามารถล้างà¹à¸„ชได้โดยคลิà¸à¸—ี่ปุ่ม ðŸ—‘ï¸ à¹ƒà¸™à¸¡à¸¸à¸¡à¸¡à¸à¸‡à¸à¸²à¸£à¸ˆà¸±à¸”à¸à¸²à¸£à¹„ฟล์" -cacheRemoteSensitiveFiles: "ไฟล์ระยะไà¸à¸¥à¸—ี่มีความละเà¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™à¹à¸„ช" -cacheRemoteSensitiveFilesDescription: "เมื่à¸à¸›à¸´à¸”à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¹à¸¥à¹‰à¸§à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่านี้ ไฟล์รีโมตที่มีความละเà¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™à¸™à¸±à¹‰à¸™à¸ˆà¸°à¸–ูà¸à¹‚หลดโดยตรงจาà¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥à¹‚ดยที่ไม่มีà¸à¸²à¸£à¹à¸„ช" -flagAsBot: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸šà¸à¸à¸§à¹ˆà¸²à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¸šà¸à¸—" +cacheRemoteSensitiveFiles: "à¹à¸„ชไฟล์ระยะไà¸à¸¥à¸—ี่มีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" +cacheRemoteSensitiveFilesDescription: "เมื่à¸à¸›à¸´à¸”à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่านี้ ไฟล์ระยะไà¸à¸¥à¸—ี่มีเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™à¸™à¸±à¹‰à¸™à¸ˆà¸°à¸–ูà¸à¹‚หลดโดยตรงจาà¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥à¹‚ดยที่ไม่มีà¸à¸²à¸£à¹à¸„ช" +flagAsBot: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸šà¸à¸à¸§à¹ˆà¸²à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¸šà¸à¸•" flagAsBotDescription: "à¸à¸²à¸£à¹€à¸›à¸´à¸”ใช้งานตัวเลืà¸à¸à¸™à¸µà¹‰à¸«à¸²à¸à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¸–ูà¸à¸„วบคุมโดยนัà¸à¹€à¸‚ียนโปรà¹à¸à¸£à¸¡ หรืภถ้าหาà¸à¹€à¸›à¸´à¸”ใช้งาน มันจะทำหน้าที่เป็นà¹à¸Ÿà¸¥à¹‡à¸à¸ªà¸³à¸«à¸£à¸±à¸šà¸™à¸±à¸à¸žà¸±à¸’นารายà¸à¸·à¹ˆà¸™à¹† à¹à¸¥à¸°à¹€à¸žà¸·à¹ˆà¸à¸›à¹‰à¸à¸‡à¸à¸±à¸™à¸à¸²à¸£à¹‚ต้ตà¸à¸šà¹à¸šà¸šà¹„ม่มีที่สิ้นสุดà¸à¸±à¸šà¸šà¸à¸—ตัวà¸à¸·à¹ˆà¸™à¹† à¹à¸¥à¸°à¸¢à¸±à¸‡à¸ªà¸²à¸¡à¸²à¸£à¸–ปรับเปลี่ยนระบบภายในขà¸à¸‡ Misskey เพื่à¸à¸›à¸à¸´à¸šà¸±à¸•à¸´à¸•à¹ˆà¸à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¸šà¸à¸—" -flagAsCat: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸šà¸à¸à¸§à¹ˆà¸²à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¹à¸¡à¸§" -flagAsCatDescription: "à¸à¸²à¸£à¹€à¸›à¸´à¸”ใช้งานตัวเลืà¸à¸à¸™à¸µà¹‰à¹€à¸žà¸·à¹ˆà¸à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸šà¸à¸à¸§à¹ˆà¸²à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¹à¸¡à¸§" +flagAsCat: "เมี้ยววววววววววววววว!!!!!!!!!!!" +flagAsCatDescription: "เหมียวเหมียวเมี้ยว??" flagShowTimelineReplies: "à¹à¸ªà¸”งตà¸à¸šà¸à¸¥à¸±à¸š ในไทม์ไลน์" flagShowTimelineRepliesDescription: "à¹à¸ªà¸”งà¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¸‚à¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¹„ปยังโน้ตขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸£à¸²à¸¢à¸à¸·à¹ˆà¸™à¹†à¹ƒà¸™à¹„ทม์ไลน์หาà¸à¹„ด้เปิดเà¸à¸²à¹„ว้" autoAcceptFollowed: "à¸à¸™à¸¸à¸¡à¸±à¸•à¸´à¸„ำขà¸à¸•à¸´à¸”ตามโดยà¸à¸±à¸•à¹‚นมัติทันที จาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸—ี่คุณà¸à¸³à¸¥à¸±à¸‡à¸•à¸´à¸”ตาม" @@ -171,22 +178,22 @@ reloadAccountsList: "รีโหลดรายà¸à¸²à¸£à¸šà¸±à¸à¸Šà¸µà¹ƒà¸« loginFailed: "à¸à¸²à¸£à¹€à¸‚้าสู่ระบบไม่สำเร็จ" showOnRemote: "ดูบนà¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥" general: "ทั่วไป" -wallpaper: "วà¸à¸¥à¸¥à¹Œà¹€à¸›à¹€à¸›à¸à¸£à¹Œ" -setWallpaper: "ตั้งวà¸à¸¥à¹€à¸›à¹€à¸›à¸à¸£à¹Œ" -removeWallpaper: "นำวà¸à¸¥à¹€à¸›à¹€à¸›à¸à¸£à¹Œà¸à¸à¸" +wallpaper: "ภาพพื้นหลัง" +setWallpaper: "ตั้งค่าภาพพื้นหลัง" +removeWallpaper: "นำภาพพื้นหลังà¸à¸à¸" searchWith: "ค้นหา: {q}" -youHaveNoLists: "คุณไม่มีลิสต์ใด ๆ " -followConfirm: "คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¸«à¸£à¸à¸§à¹ˆà¸²à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸—ี่จะติดตาม {name}?" -proxyAccount: "บัà¸à¸Šà¸µ พร็à¸à¸à¸‹à¸µà¹ˆ" +youHaveNoLists: "คุณไม่มีรายชื่à¸à¹ƒà¸”ๆ " +followConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸•à¸´à¸”ตาม {name} ใช่ไหม?" +proxyAccount: "บัà¸à¸Šà¸µà¸žà¸£à¹‡à¸à¸à¸‹à¸µà¹ˆ" proxyAccountDescription: "บัà¸à¸Šà¸µà¸žà¸£à¹‡à¸à¸à¸‹à¸µà¹ˆ คืภบัà¸à¸Šà¸µà¸—ี่จะทำหน้าที่เป็นผู้ติดตามระยะไà¸à¸¥à¸ªà¸³à¸«à¸£à¸±à¸šà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸—ี่à¸à¸¢à¸¹à¹ˆà¸ ายใต้ด้วยเงื่à¸à¸™à¹„ขบางà¸à¸¢à¹ˆà¸²à¸‡ ยà¸à¸•à¸±à¸§à¸à¸¢à¹ˆà¸²à¸‡ เช่น เมื่à¸à¸¡à¸µà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸™à¸±à¹‰à¸™à¹„ด้เพิ่มผู้ใช้งานจาà¸à¸£à¸°à¸¢à¸°à¹„à¸à¸¥à¸¥à¸‡à¹ƒà¸™à¸£à¸²à¸¢à¸à¸²à¸£ à¹à¸•à¹ˆà¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸‚à¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¹ƒà¸™à¸£à¸°à¸¢à¸°à¹„à¸à¸¥à¸™à¸±à¹‰à¸™à¸ˆà¸°à¹„ม่ถูà¸à¸ªà¹ˆà¸‡à¹„ปยังà¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸«à¸²à¸à¹„ม่มีผู้ใช้งานในพื้นที่ติดตามผู้ใช้รายนั้น ดังนั้นบัà¸à¸Šà¸µà¸žà¸£à¹‡à¸à¸à¸‹à¸µà¸™à¸µà¹‰à¸ˆà¸°à¸•à¸´à¸”ตามà¹à¸—น" host: "โฮสต์" selectUser: "เลืà¸à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" recipient: "ผู้รับ" -annotation: "ความคิดเห็น" -federation: "เฟดิเวิร์ส" -instances: "Server" -registeredAt: "จดทะเบียนที่" -latestRequestReceivedAt: "ได้รับคำขà¸à¸¥à¹ˆà¸²à¸ªà¸¸à¸”ไปà¹à¸¥à¹‰à¸§" +annotation: "หมายเหตุประà¸à¸à¸š" +federation: "สหพันธ์" +instances: "à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ" +registeredAt: "วันที่ลงทะเบียน" +latestRequestReceivedAt: "คำขà¸à¸¥à¹ˆà¸²à¸ªà¸¸à¸”ที่ได้รับ" latestStatus: "สถานะล่าสุด" storageUsage: "พื้นที่จัดเà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥à¸—ี่ใช้ไป" charts: "โดดเด่น" @@ -194,33 +201,34 @@ perHour: "ทุà¸à¸Šà¸±à¹ˆà¸§à¹‚มง" perDay: "ต่à¸à¸§à¸±à¸™" stopActivityDelivery: "หยุดส่งà¸à¸´à¸ˆà¸à¸£à¸£à¸¡" blockThisInstance: "บล็à¸à¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸™à¸µà¹‰" -silenceThisInstance: "ปà¸à¸›à¸´à¸”à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸™à¸µà¹‰" +silenceThisInstance: "ปิดปาà¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸™à¸µà¹‰" operations: "ดำเนินà¸à¸²à¸£" software: "ซà¸à¸Ÿà¸•à¹Œà¹à¸§à¸£à¹Œ" version: "เวà¸à¸£à¹Œà¸Šà¸±à¹ˆà¸™" metadata: "Metadata" -withNFiles: "{n} ไฟล์(s)" +withNFiles: "{n} ไฟล์" monitor: "มà¸à¸™à¸´à¹€à¸•à¸à¸£à¹Œ" jobQueue: "คิวงาน" cpuAndMemory: "ซีพียู à¹à¸¥à¸° หน่วยความจำ" -network: "เน็ตเวิร์à¸" +network: "เครืà¸à¸‚่าย" disk: "ดิสà¸à¹Œ" -instanceInfo: "ข้à¸à¸¡à¸¹à¸¥ à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ" +instanceInfo: "ข้à¸à¸¡à¸¹à¸¥à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ" statistics: "สถิติà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" clearQueue: "ล้างคิว" -clearQueueConfirmTitle: "คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¸«à¸£à¸à¸§à¹ˆà¸²à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸—ี่จะล้างคิว?" -clearQueueConfirmText: "บันทึà¸à¸¢à¹ˆà¸à¸—ี่ยังไม่ได้ส่งที่เหลืà¸à¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¸„ิวนั้นมัà¸à¸ˆà¸° ไม่ถูà¸à¸£à¸§à¸¡à¹€à¸‚้าด้วยà¸à¸±à¸™ โดยปà¸à¸•à¸´à¹à¸¥à¹‰à¸§à¹„ม่จำเป็นต้à¸à¸‡à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰" +clearQueueConfirmTitle: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¹‰à¸²à¸‡à¸„ิวใช่ไหม?" +clearQueueConfirmText: "โพสต์ที่ยังค้างในคิวจะไม่ถูà¸à¸ˆà¸±à¸”ส่งà¸à¸µà¸à¸•à¹ˆà¸à¹„ป โดยปà¸à¸•à¸´à¹à¸¥à¹‰à¸§à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¹„ม่จำเป็น" clearCachedFiles: "ล้างà¹à¸„ช" -clearCachedFilesConfirm: "นายà¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¸«à¸£à¸à¸§à¹ˆà¸²à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸—ี่จะลบไฟล์ระยะไà¸à¸¥à¸—ี่à¹à¸„ชไว้ทั้งหมด?" -blockedInstances: "à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸—ี่ ถูà¸à¸šà¸¥à¹‡à¸à¸" +clearCachedFilesConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¹„ฟล์ระยะไà¸à¸¥à¸—ี่à¹à¸„ชไว้ทั้งหมดใช่ไหม?" +blockedInstances: "à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸—ี่ถูà¸à¸šà¸¥à¹‡à¸à¸" blockedInstancesDescription: "ระบุชื่à¸à¹‚ฮสต์ขà¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸—ี่คุณต้à¸à¸‡à¸à¸²à¸£à¸šà¸¥à¹‡à¸à¸ à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸—ี่à¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¸£à¸²à¸¢à¸à¸²à¸£à¸™à¸±à¹‰à¸™à¸ˆà¸°à¹„ม่สามารถพูดคุยà¸à¸±à¸šà¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸™à¸µà¹‰à¹„ด้à¸à¸µà¸à¸•à¹ˆà¸à¹„ป" -silencedInstances: "ปà¸à¸›à¸´à¸”à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸™à¸µà¹‰à¹à¸¥à¹‰à¸§" +silencedInstances: "ปิดปาà¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸™à¸µà¹‰à¹à¸¥à¹‰à¸§" +silencedInstancesDescription: "ตั้งค่ารายชื่à¸à¹‚ฮสต์ขà¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸—ี่คุณต้à¸à¸‡à¸à¸²à¸£à¸›à¸´à¸”ปาภบัà¸à¸Šà¸µà¸—ั้งหมดขà¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸—ี่à¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¸£à¸²à¸¢à¸Šà¸·à¹ˆà¸à¸™à¸±à¹‰à¸™à¹† จะถืà¸à¸§à¹ˆà¸²à¸–ูà¸à¸›à¸´à¸”ปาà¸à¹€à¸Šà¹ˆà¸™à¸à¸±à¸™ ทำได้เฉพาะคำขà¸à¸•à¸´à¸”ตามเท่านั้น à¹à¸¥à¸°à¹„ม่สามารถà¸à¸¥à¹ˆà¸²à¸§à¸–ึงบัà¸à¸Šà¸µà¹ƒà¸™à¸žà¸·à¹‰à¸™à¸—ี่ได้หาà¸à¹„ม่ได้ติดตาม | สิ่งนี้ไม่มีผลต่à¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸—ี่ถูà¸à¸šà¸¥à¹‡à¸à¸" muteAndBlock: "ปิดเสียงà¹à¸¥à¸°à¸šà¸¥à¹‡à¸à¸" mutedUsers: "ผู้ใช้ที่ถูà¸à¸›à¸´à¸”เสียง" blockedUsers: "ผู้ใช้ที่ถูà¸à¸šà¸¥à¹‡à¸à¸" noUsers: "ไม่พบผู้ใช้งาน" editProfile: "à¹à¸à¹‰à¹„ขโปรไฟล์" -noteDeleteConfirm: "นายà¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¸«à¸£à¸à¸§à¹ˆà¸²à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¹‚น้ตนี้นะ?" +noteDeleteConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¹‚น้ตนี้ใช่ไหม?" pinLimitExceeded: "คุณไม่สามารถปัà¸à¸«à¸¡à¸¸à¸”โน้ตเพิ่มเติมใดๆได้à¸à¸µà¸" intro: "à¸à¸²à¸£à¸•à¸´à¸”ตั้ง Misskey เสร็จสิ้นà¹à¸¥à¹‰à¸§à¸™à¸°! โปรดสร้างผู้ใช้งานที่เป็นผู้ดูà¹à¸¥à¸£à¸°à¸šà¸š" done: "เสร็จสิ้น" @@ -228,8 +236,8 @@ processing: "à¸à¸³à¸¥à¸±à¸‡à¸›à¸£à¸°à¸¡à¸§à¸¥à¸œà¸¥..." preview: "à¹à¸ªà¸”งตัวà¸à¸¢à¹ˆà¸²à¸‡" default: "ค่าเริ่มต้น" defaultValueIs: "ค่าเริ่มต้น: {value}" -noCustomEmojis: "ไม่มีà¸à¸µà¹‚มจิ" -noJobs: "ไม่มีชิ้นงาน" +noCustomEmojis: "ไม่มีเà¸à¹‚มจิ" +noJobs: "ไม่มีงาน" federating: "สหพันธ์" blocked: "ถูà¸à¸šà¸¥à¹‡à¸à¸" suspended: "ถูà¸à¸£à¸°à¸‡à¸±à¸š" @@ -237,42 +245,43 @@ all: "ทั้งหมด" subscribing: "สมัครà¹à¸¥à¹‰à¸§" publishing: "à¸à¸³à¸¥à¸±à¸‡à¹€à¸œà¸¢à¹à¸žà¸£à¹ˆ" notResponding: "ไม่มีà¸à¸²à¸£à¸•à¸à¸šà¸ªà¸™à¸à¸‡" -instanceFollowing: "à¸à¸³à¸¥à¸±à¸‡à¸•à¸´à¸”ตาม บน à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ" +instanceFollowing: "à¸à¸³à¸¥à¸±à¸‡à¸•à¸´à¸”ตามบนà¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ" instanceFollowers: "ผู้ติดตามขà¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ" instanceUsers: "ผู้ใช้งานขà¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸™à¸µà¹‰" changePassword: "เปลี่ยนรหัสผ่าน" security: "ความปลà¸à¸”ภัย" -retypedNotMatch: "à¸à¸´à¸™à¸žà¸¸à¸•à¹„ม่ตรงà¸à¸±à¸™à¸™à¸°" +retypedNotMatch: "ทั้งสà¸à¸‡à¸›à¹‰à¸à¸™à¸‚้à¸à¸¡à¸¹à¸¥à¹„ม่สà¸à¸”คล้à¸à¸‡à¸à¸±à¸™" currentPassword: "รหัสผ่านปัจจุบัน" newPassword: "รหัสผ่านใหม่" newPasswordRetype: "ใส่รหัสผ่านใหม่à¸à¸µà¸à¸„รั้ง" attachFile: "à¹à¸™à¸šà¹„ฟล์" -more: "เพิ่มเติม" +more: "เพิ่มเติม!" featured: "ไฮไลท์" usernameOrUserId: "ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸«à¸£à¸·à¸à¸£à¸«à¸±à¸ªà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" noSuchUser: "ไม่พบผู้ใช้" lookup: "à¸à¸²à¸£à¸„้นหา" announcements: "ประà¸à¸²à¸¨" -imageUrl: "url รูปภาพ" +imageUrl: "URL รูปภาพ" remove: "ลบ" removed: "ถูà¸à¸¥à¸šà¹„ปà¹à¸¥à¹‰à¸§" -removeAreYouSure: "นายà¹à¸™à¹ˆà¹ƒà¸ˆà¸ˆà¸£à¸´à¸‡à¸«à¸£à¸à¸§à¹ˆà¸²à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸—ี่จะลบà¸à¸à¸ \"{x}\"" -deleteAreYouSure: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸š {x} หรืà¸à¹„ม่คะ?" -resetAreYouSure: "รีเซ็ตเลยไหม" +removeAreYouSure: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸š “{x}†ใช่ไหม?" +deleteAreYouSure: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸š “{x}†ใช่ไหม?" +resetAreYouSure: "รีเซ็ตเลยไหม?" +areYouSure: "à¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¹ƒà¸Šà¹ˆà¹„หมคะ?" saved: "บันทึà¸à¹à¸¥à¹‰à¸§" messaging: "à¹à¸Šà¸—" -upload: "à¸à¸±à¸žà¹‚หลด" +upload: "à¸à¸±à¸›à¹‚หลด" keepOriginalUploading: "เà¸à¹‡à¸šà¸ าพต้นฉบับ" -keepOriginalUploadingDescription: "บันทึà¸à¸£à¸¹à¸›à¸ าพที่à¸à¸±à¸žà¹‚หลดต้นฉบับตามที่เป็นà¸à¸¢à¸¹à¹ˆ ถ้าหาà¸à¸›à¸´à¸”à¸à¸¢à¸¹à¹ˆ ระบบจะสร้างเวà¸à¸£à¹Œà¸Šà¸±à¹ˆà¸™à¸—ี่จะà¹à¸ªà¸”งบนเว็บเมื่à¸à¸à¸±à¸žà¹‚หลดนะ" +keepOriginalUploadingDescription: "เà¸à¹‡à¸šà¸ าพต้นฉบับไว้เมื่à¸à¸à¸±à¸›à¹‚หลดภาพ หาà¸à¸›à¸´à¸” รูปภาพสำหรับà¸à¸²à¸£à¹€à¸œà¸¢à¹à¸žà¸£à¹ˆà¸—างเว็บจะถูà¸à¸ªà¸£à¹‰à¸²à¸‡à¸‚ึ้นในเบราว์เซà¸à¸£à¹Œà¹€à¸¡à¸·à¹ˆà¸à¸—ำà¸à¸²à¸£à¸à¸±à¸›à¹‚หลด" fromDrive: "จาà¸à¹„ดรฟ์" fromUrl: "จาภURL" -uploadFromUrl: "à¸à¸±à¸žà¹‚หลดจาภURL" +uploadFromUrl: "à¸à¸±à¸›à¹‚หลดจาภURL" uploadFromUrlDescription: "URL ขà¸à¸‡à¹„ฟล์ที่คุณต้à¸à¸‡à¸à¸²à¸£à¸à¸±à¸›à¹‚หลด" -uploadFromUrlRequested: "à¸à¸±à¸žà¹‚หลดที่ร้à¸à¸‡à¸‚à¸" -uploadFromUrlMayTakeTime: "มันà¸à¸²à¸ˆà¸ˆà¸°à¸•à¹‰à¸à¸‡à¹ƒà¸Šà¹‰à¹€à¸§à¸¥à¸²à¸ªà¸±à¸à¸„รู่จนà¸à¸§à¹ˆà¸²à¸à¸²à¸£à¸à¸±à¸žà¹‚หลดจะเสร็จสมบูรณ์นะ" +uploadFromUrlRequested: "ร้à¸à¸‡à¸‚à¸à¸à¸²à¸£à¸à¸±à¸›à¹‚หลดà¹à¸¥à¹‰à¸§" +uploadFromUrlMayTakeTime: "à¸à¸²à¸£à¸à¸±à¸›à¹‚หลดà¸à¸²à¸ˆà¹ƒà¸Šà¹‰à¹€à¸§à¸¥à¸²à¸ªà¸±à¸à¸„รู่จึงจะเสร็จสมบูรณ์" explore: "สำรวจ" messageRead: "à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" -noMoreHistory: "ในนั้นไม่มีประวัติà¸à¸µà¸à¸•à¹ˆà¸à¹„ปà¹à¸¥à¹‰à¸§à¸™à¸°" +noMoreHistory: "ไม่มีประวัติเพิ่มเติม" startMessaging: "เริ่มà¸à¸²à¸£à¸ªà¸™à¸—นา" nUsersRead: "à¸à¹ˆà¸²à¸™à¹‚ดย {n}" agreeTo: "ฉันยà¸à¸¡à¸£à¸±à¸šà¸—ี่จะ {0}" @@ -280,9 +289,9 @@ agree: "ยà¸à¸¡à¸£à¸±à¸š" agreeBelow: "ฉันยà¸à¸¡à¸£à¸±à¸šà¸–ึงด้านล่าง" basicNotesBeforeCreateAccount: "หมายเหตุสำคัà¸" termsOfService: "เงื่à¸à¸™à¹„ขà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£" -start: "เริ่มต้น​ใช้งาน​" +start: "เริ่ม" home: "หน้าà¹à¸£à¸" -remoteUserCaution: "เนื่à¸à¸‡à¸ˆà¸²à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸£à¸²à¸¢à¸™à¸µà¹‰à¸™à¸±à¹‰à¸™ มาจาà¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥ ข้à¸à¸¡à¸¹à¸¥à¸—ี่à¹à¸ªà¸”งดังà¸à¸¥à¹ˆà¸²à¸§à¸™à¸±à¹‰à¸™à¸à¸²à¸ˆà¸ˆà¸°à¹„ม่สมบูรณ์à¸à¹‡à¹„ด้นะ" +remoteUserCaution: "ข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸ˆà¹„ม่สมบูรณ์เนื่à¸à¸‡à¸ˆà¸²à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸£à¸²à¸¢à¸™à¸µà¹‰à¸¡à¸²à¸ˆà¸²à¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥" activity: "à¸à¸´à¸ˆà¸à¸£à¸£à¸¡" images: "รูปภาพ" image: "รูปภาพ" @@ -291,13 +300,13 @@ yearsOld: "{age} ปี" registeredDate: "วันที่สมัครสมาชิà¸" location: "ตำà¹à¸«à¸™à¹ˆà¸‡à¸—ี่ตั้ง" theme: "ธีม" -themeForLightMode: "ธีมที่จะใช้ในโหมดà¹à¸ªà¸‡" +themeForLightMode: "ธีมที่จะใช้ในโหมดสว่าง" themeForDarkMode: "ธีมที่จะใช้ในโหมดมืด" light: "สว่าง" dark: "มืด" lightThemes: "ธีมสว่าง" darkThemes: "ธีมมืด" -syncDeviceDarkMode: "ซิงค์โหมดมืดด้วยà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าà¸à¸±à¸šà¸à¸¸à¸›à¸à¸£à¸“์" +syncDeviceDarkMode: "ซิงค์โหมดมืดà¸à¸±à¸šà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าà¸à¸¸à¸›à¸à¸£à¸“์ขà¸à¸‡à¸„ุณ" drive: "ไดรฟ์" fileName: "ชื่à¸à¹„ฟล์" selectFile: "เลืà¸à¸à¹„ฟล์" @@ -305,46 +314,47 @@ selectFiles: "เลืà¸à¸à¹„ฟล์" selectFolder: "เลืà¸à¸à¹‚ฟลเดà¸à¸£à¹Œ" selectFolders: "เลืà¸à¸à¹‚ฟลเดà¸à¸£à¹Œ" renameFile: "เปลี่ยนชื่à¸à¹„ฟล์" -folderName: "ชื่à¸à¹à¸Ÿà¹‰à¸¡" +folderName: "ชื่à¸à¹‚ฟลเดà¸à¸£à¹Œ" createFolder: "สร้างโฟลเดà¸à¸£à¹Œ" renameFolder: "เปลี่ยนชื่à¸à¹‚ฟลเดà¸à¸£à¹Œ" deleteFolder: "ลบโฟลเดà¸à¸£à¹Œ" +folder: "โฟลเดà¸à¸£à¹Œ" addFile: "เพิ่มไฟล์" emptyDrive: "ไดรฟ์ขà¸à¸‡à¸„ุณว่างเปล่านะ" emptyFolder: "โฟลเดà¸à¸£à¹Œà¸™à¸µà¹‰à¸§à¹ˆà¸²à¸‡à¹€à¸›à¸¥à¹ˆà¸²" unableToDelete: "ไม่สามารถลบà¸à¸à¸à¹„ด้" -inputNewFileName: "ป้à¸à¸™à¸Šà¸·à¹ˆà¸à¹„ฟล์ใหม่นะ" +inputNewFileName: "ป้à¸à¸™à¸Šà¸·à¹ˆà¸à¹„ฟล์ใหม่" inputNewDescription: "à¸à¸£à¸¸à¸“าใส่à¹à¸„ปชั่นใหม่" -inputNewFolderName: "à¸à¸£à¸¸à¸“าใส่ชื่à¸à¹‚ฟลเดà¸à¸£à¹Œà¹ƒà¸«à¸¡à¹ˆà¸™à¸°\n" -circularReferenceFolder: "โฟลเดà¸à¸£à¹Œà¸›à¸¥à¸²à¸¢à¸—าง คืภโฟลเดà¸à¸£à¹Œà¸¢à¹ˆà¸à¸¢à¸‚à¸à¸‡à¹‚ฟลเดà¸à¸£à¹Œà¸—ี่คุณต้à¸à¸‡à¸à¸²à¸£à¸—ี่จะย้ายล่ะนะ" -hasChildFilesOrFolders: "เนื่à¸à¸‡à¸ˆà¸²à¸à¹‚ฟลเดà¸à¸£à¹Œà¸™à¸µà¹‰à¹„ม่ว่างเปล่า จึงไม่สามารถลบได้นะ" +inputNewFolderName: "à¸à¸£à¸¸à¸“าใส่ชื่à¸à¹‚ฟลเดà¸à¸£à¹Œà¹ƒà¸«à¸¡à¹ˆ" +circularReferenceFolder: "โฟลเดà¸à¸£à¹Œà¸›à¸¥à¸²à¸¢à¸—างคืà¸à¹‚ฟลเดà¸à¸£à¹Œà¸¢à¹ˆà¸à¸¢à¸‚à¸à¸‡à¹‚ฟลเดà¸à¸£à¹Œà¸—ี่คุณà¸à¸³à¸¥à¸±à¸‡à¸¢à¹‰à¸²à¸¢" +hasChildFilesOrFolders: "เนื่à¸à¸‡à¸ˆà¸²à¸à¹‚ฟลเดà¸à¸£à¹Œà¸™à¸µà¹‰à¹„ม่ว่างเปล่า จึงไม่สามารถลบ" copyUrl: "คัดลà¸à¸ URL" rename: "เปลี่ยนชื่à¸" avatar: "ไà¸à¸„à¸à¸™" banner: "à¹à¸šà¸™à¹€à¸™à¸à¸£à¹Œ" -displayOfSensitiveMedia: "à¹à¸ªà¸”งผลสื่à¸à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" -whenServerDisconnected: "สูà¸à¹€à¸ªà¸µà¸¢à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•à¹ˆà¸à¸à¸±à¸šà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" -disconnectedFromServer: "ถูà¸à¸•à¸±à¸”à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•à¹ˆà¸à¸à¸à¸à¸ˆà¸²à¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" +displayOfSensitiveMedia: "à¹à¸ªà¸”งสื่à¸à¸—ี่มีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" +whenServerDisconnected: "เมื่à¸à¸ªà¸¹à¸à¹€à¸ªà¸µà¸¢à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•à¹ˆà¸à¸à¸±à¸šà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" +disconnectedFromServer: "à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•à¹ˆà¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸–ูà¸à¸•à¸±à¸”" reload: "รีโหลด" doNothing: "เมิน" -reloadConfirm: "นายต้à¸à¸‡à¸à¸²à¸£à¸£à¸µà¹€à¸Ÿà¸£à¸Šà¹„ทม์ไลน์หรืà¸à¸›à¹ˆà¸²à¸§?" +reloadConfirm: "รีโหลดเลยไหม?" watch: "ดู" unwatch: "หยุดดู" accept: "ยà¸à¸¡à¸£à¸±à¸š" reject: "ปà¸à¸´à¹€à¸ªà¸˜" normal: "ปà¸à¸•à¸´" -instanceName: "ชื่ภà¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ" +instanceName: "ชื่à¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ" instanceDescription: "คำà¸à¸˜à¸´à¸šà¸²à¸¢à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ" maintainerName: "ผู้ดูà¹à¸¥" -maintainerEmail: "à¸à¸µà¹€à¸¡à¸¥à¹Œà¹à¸à¸”มิน" -tosUrl: "เงื่à¸à¸™à¹„ขà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£ URL" +maintainerEmail: "à¸à¸µà¹€à¸¡à¸¥à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¸£à¸°à¸šà¸š" +tosUrl: "URL เงื่à¸à¸™à¹„ขà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£" thisYear: "ปีนี้" thisMonth: "เดืà¸à¸™à¸™à¸µà¹‰" today: "วันนี้" dayX: "{day}" monthX: "เดืà¸à¸™ {month}" yearX: "{year}" -pages: "หน้า" +pages: "หน้าเพจ" integration: "รวบรวม" connectService: "เชื่à¸à¸¡à¸•à¹ˆà¸" disconnectService: "ตัดà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•à¹ˆà¸" @@ -353,23 +363,28 @@ enableGlobalTimeline: "เปิดใช้งานไทม์ไลน์ท disablingTimelinesInfo: "ผู้ดูà¹à¸¥à¸£à¸°à¸šà¸šà¹à¸¥à¸°à¸œà¸¹à¹‰à¸„วบคุมจะสามารถเข้าถึงไทม์ไลน์ทั้งหมด ถึงà¹à¸¡à¹‰à¸§à¹ˆà¸²à¸ˆà¸°à¹„ม่ได้เปิดใช้งานà¸à¹‡à¸•à¸²à¸¡" registration: "ลงทะเบียน" enableRegistration: "เปิดใช้งานà¸à¸²à¸£à¸¥à¸‡à¸—ะเบียนผู้ใช้ใหม่" -invite: "เชิà¸à¸Šà¸§à¸™" +invite: "คำเชิà¸" driveCapacityPerLocalAccount: "ความจุขà¸à¸‡à¹„ดรฟ์ต่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸ ายในเครื่à¸à¸‡" driveCapacityPerRemoteAccount: "ความจุขà¸à¸‡à¹„ดรฟ์ต่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸£à¸°à¸¢à¸°à¹„à¸à¸¥" inMb: "เป็นเมà¸à¸°à¹„บต์" bannerUrl: "URL รูปภาพà¹à¸šà¸™à¹€à¸™à¸à¸£à¹Œ" backgroundImageUrl: "URL ภาพพื้นหลัง" basicInfo: "ข้à¸à¸¡à¸¹à¸¥à¹€à¸šà¸·à¹‰à¸à¸‡à¸•à¹‰à¸™" -pinnedUsers: "ผู้ใช้งานที่ได้รับà¸à¸²à¸£à¸›à¸±à¸à¸«à¸¡à¸¸à¸”" -pinnedUsersDescription: "ลิสต์ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¹‚ดยคั่นด้วยà¸à¸²à¸£à¸‚ึ้นบรรทัดใหม่เพื่à¸à¸›à¸±à¸à¸«à¸¡à¸¸à¸”ในà¹à¸—็บ \"สำรวจ\"" -pinnedPages: "หน้าที่ปัà¸à¸«à¸¡à¸¸à¸”" -pinnedPagesDescription: "ป้à¸à¸™à¹€à¸ªà¹‰à¸™à¸—างขà¸à¸‡à¸«à¸™à¹‰à¸²à¸—ี่คุณต้à¸à¸‡à¸à¸²à¸£à¸•à¸£à¸¶à¸‡à¹„ว้ที่หน้าà¹à¸£à¸à¸‚à¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸™à¸µà¹‰ โดยคั่นด้วยตัวà¹à¸šà¹ˆà¸‡à¸šà¸£à¸£à¸—ัด" +pinnedUsers: "ผู้ใช้ที่ถูà¸à¸›à¸±à¸à¸«à¸¡à¸¸à¸”" +pinnedUsersDescription: "ป้à¸à¸™à¸Šà¸·à¹ˆà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่คุณต้à¸à¸‡à¸à¸²à¸£à¸›à¸±à¸à¸«à¸¡à¸¸à¸”ในหน้า “ค้นพบ†ฯลฯ คั่นด้วยà¸à¸²à¸£à¸‚ึ้นบรรทัดใหม่" +pinnedPages: "หน้าเพจที่ปัà¸à¸«à¸¡à¸¸à¸”" +pinnedPagesDescription: "ป้à¸à¸™à¹€à¸ªà¹‰à¸™à¸—างขà¸à¸‡à¸«à¸™à¹‰à¸²à¹€à¸žà¸ˆà¸—ี่คุณต้à¸à¸‡à¸à¸²à¸£à¸›à¸±à¸à¸«à¸¡à¸¸à¸”ไว้ที่หน้าà¹à¸£à¸à¸‚à¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸™à¸µà¹‰ คั่นด้วยขึ้นบรรทัดใหม่" pinnedClipId: "ID ขà¸à¸‡à¸„ลิปที่จะปัà¸à¸«à¸¡à¸¸à¸”" pinnedNotes: "โน้ตที่ปัà¸à¸«à¸¡à¸¸à¸”ไว้" hcaptcha: "hCaptcha" enableHcaptcha: "เปิดใช้ hCaptcha" hcaptchaSiteKey: "คีย์ไซต์" hcaptchaSecretKey: "คีย์ลับ" +mcaptcha: "mCaptcha" +enableMcaptcha: "เปิดใช้ mCaptcha" +mcaptchaSiteKey: "คีย์ไซต์" +mcaptchaSecretKey: "คีย์ลับ" +mcaptchaInstanceUrl: "URL ขà¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸‚à¸à¸‡ mCaptcha" recaptcha: "reCAPTCHA" enableRecaptcha: "เปิดใช้ reCAPTCHA" recaptchaSiteKey: "คีย์ไซต์" @@ -385,26 +400,26 @@ name: "ชื่à¸" antennaSource: "à¹à¸«à¸¥à¹ˆà¸‡à¹€à¸ªà¸²à¸à¸²à¸à¸²à¸¨" antennaKeywords: "คีย์เวิร์ดที่ควรฟัง" antennaExcludeKeywords: "คีย์เวิร์ดที่จะยà¸à¹€à¸§à¹‰à¸™" -antennaKeywordsDescription: "คั่นด้วยช่à¸à¸‡à¸§à¹ˆà¸²à¸‡à¸ªà¸³à¸«à¸£à¸±à¸šà¹€à¸‡à¸·à¹ˆà¸à¸™à¹„ข AND หรืà¸à¸”้วยà¸à¸²à¸£à¸‚ึ้นบรรทัดใหม่สำหรับเงื่à¸à¸™à¹„ข OR นะ" +antennaKeywordsDescription: "คั่นด้วยช่à¸à¸‡à¸§à¹ˆà¸²à¸‡à¸ªà¸³à¸«à¸£à¸±à¸šà¹€à¸‡à¸·à¹ˆà¸à¸™à¹„ข AND หรืà¸à¸”้วยà¸à¸²à¸£à¸‚ึ้นบรรทัดใหม่สำหรับเงื่à¸à¸™à¹„ข OR" notifyAntenna: "à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¹‚น้ตใหม่" withFileAntenna: "เฉพาะโน้ตที่มีไฟล์" -enableServiceworker: "เปิดใช้งาน à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¹à¸šà¸šà¸žà¸¸à¸Šà¸ªà¸³à¸«à¸£à¸±à¸šà¹€à¸šà¸£à¸²à¸§à¹Œà¹€à¸‹à¸à¸£à¹Œà¸‚à¸à¸‡à¸„ุณ" +enableServiceworker: "เปิดใช้งานà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¹à¸šà¸šà¸žà¸¸à¸Šà¹„ปยังเบราว์เซà¸à¸£à¹Œà¸‚à¸à¸‡à¸„ุณ" antennaUsersDescription: "ระบุหนึ่งชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸•à¹ˆà¸à¸šà¸£à¸£à¸—ัด" -caseSensitive: "à¸à¸£à¸“ีที่สำคัà¸" +caseSensitive: "à¸à¸±à¸à¸©à¸£à¸žà¸´à¸¡à¸žà¹Œà¹ƒà¸«à¸à¹ˆ-พิมพ์เล็à¸à¸„วามหมายต่างà¸à¸±à¸™" withReplies: "รวมตà¸à¸šà¸à¸¥à¸±à¸š" connectedTo: "บัà¸à¸Šà¸µà¸”ังต่à¸à¹„ปนี้มีà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•à¹ˆà¸à¸à¸±à¸™" notesAndReplies: "โพสต์à¹à¸¥à¸°à¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸š" -withFiles: "รวบรวมไฟล์" +withFiles: "มีไฟล์" silence: "ถูà¸à¸›à¸´à¸”ปาà¸" -silenceConfirm: "นายà¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¸«à¸£à¸à¸§à¹ˆà¸²à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸—ี่จะ ปิดปาภผู้ใช้งานรายนี้?" +silenceConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸›à¸´à¸”ปาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸£à¸²à¸¢à¸™à¸µà¹‰à¹ƒà¸Šà¹ˆà¹„หม?" unsilence: "ยà¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¸›à¸´à¸”ปาà¸" -unsilenceConfirm: "นายà¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¸«à¸£à¸à¸§à¹ˆà¸²à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸—ี่จะยà¸à¹€à¸¥à¸´à¸à¸›à¸´à¸”ปาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸£à¸²à¸¢à¸™à¸µà¹‰?" +unsilenceConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸›à¸´à¸”ปาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸£à¸²à¸¢à¸™à¸µà¹‰à¹ƒà¸Šà¹ˆà¹„หม?" popularUsers: "ผู้ใช้ที่เป็นที่นิยม" recentlyUpdatedUsers: "ผู้ใช้ที่เพิ่งใช้งานล่าสุด" recentlyRegisteredUsers: "ผู้ใช้ที่เข้าร่วมใหม่" recentlyDiscoveredUsers: "ผู้ใช้ที่เพิ่งค้นพบใหม่" -exploreUsersCount: "มีผู้ใช้ {จำนวน} ราย" -exploreFediverse: "สำรวจเฟดดิเวิร์ส" +exploreUsersCount: "มีผู้ใช้ {count} ราย" +exploreFediverse: "สำรวจสหพันธ์" popularTags: "à¹à¸—็à¸à¸¢à¸à¸”นิยม" userList: "ลิสต์" about: "เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸š" @@ -419,8 +434,8 @@ moderator: "ผู้ควบคุม" moderation: "à¸à¸²à¸£à¸à¸¥à¸±à¹ˆà¸™à¸à¸£à¸à¸‡" moderationNote: "โน้ตà¸à¸²à¸£à¸à¸¥à¸±à¹ˆà¸™à¸à¸£à¸à¸‡" addModerationNote: "เพิ่มโน้ตà¸à¸²à¸£à¸à¸¥à¸±à¹ˆà¸™à¸à¸£à¸à¸‡" -moderationLogs: "บันทึà¸à¸à¸²à¸£à¸à¸¥à¸±à¹ˆà¸™à¸à¸£à¸à¸‡" -nUsersMentioned: "à¸à¸¥à¹ˆà¸²à¸§à¸–ึงโดยผู้ใช้ {n} รายนี้" +moderationLogs: "ปูมà¸à¸²à¸£à¹à¸à¹‰à¹„ข" +nUsersMentioned: "à¸à¸¥à¹ˆà¸²à¸§à¸–ึงโดยผู้ใช้ {n} ราย" securityKeyAndPasskey: "ความปลà¸à¸”ภัยà¹à¸¥à¸°à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™" securityKey: "à¸à¸¸à¸à¹à¸ˆà¸„วามปลà¸à¸”ภัย" lastUsed: "ใช้ล่าสุด" @@ -429,19 +444,19 @@ unregister: "เลิà¸à¸•à¸´à¸”ตาม" passwordLessLogin: "เข้าสู่ระบบà¹à¸šà¸šà¹„ม่ใช้รหัสผ่าน" passwordLessLoginDescription: "à¸à¸™à¸¸à¸à¸²à¸•à¹ƒà¸«à¹‰à¹€à¸‚้าสู่ระบบโดยไม่ต้à¸à¸‡à¹ƒà¸Šà¹‰à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¹‚ดยใช้รหัสรัà¸à¸©à¸²à¸„วามปลà¸à¸”ภัยหรืà¸à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¹€à¸—่านั้น" resetPassword: "รีเซ็ตรหัสผ่าน" -newPasswordIs: "รหัสผ่านใหม่คืภ\"{password}\"" +newPasswordIs: "รหัสผ่านใหม่คืภ“{password}â€" reduceUiAnimation: "ลดภาพเคลื่à¸à¸™à¹„หว UI" -share: "à¹à¸Šà¸£à¹Œ" +share: "à¹à¸šà¹ˆà¸‡à¸›à¸±à¸™" notFound: "ไม่พบหน้าที่ต้à¸à¸‡à¸à¸²à¸£" -notFoundDescription: "ไม่พบหน้าที่สà¸à¸”คล้à¸à¸‡à¸•à¸£à¸‡à¸à¸±à¸™à¸à¸±à¸š URL นี้นะ" -uploadFolder: "โฟลเดà¸à¸£à¹Œà¹€à¸£à¸´à¹ˆà¸¡à¸•à¹‰à¸™à¸ªà¸³à¸«à¸£à¸±à¸šà¸à¸±à¸žà¹‚หลด" +notFoundDescription: "ไม่พบหน้าตาม URL ที่ระบุ" +uploadFolder: "โฟลเดà¸à¸£à¹Œà¹€à¸£à¸´à¹ˆà¸¡à¸•à¹‰à¸™à¸ªà¸³à¸«à¸£à¸±à¸šà¸à¸±à¸›à¹‚หลด" markAsReadAllNotifications: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸—ั้งหมดว่าà¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" markAsReadAllUnreadNotes: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹‚น้ตทั้งหมดว่าà¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" markAsReadAllTalkMessages: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸‚้à¸à¸„วามทั้งหมดว่าà¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" help: "ช่วยเหลืà¸" inputMessageHere: "พิมพ์ข้à¸à¸„วามที่นี่" close: "ปิด" -invites: "เชิà¸à¸Šà¸§à¸™" +invites: "คำเชิà¸" members: "สมาชิà¸" transfer: "ถ่ายโà¸à¸™" title: "หัวข้à¸" @@ -449,15 +464,15 @@ text: "ข้à¸à¸„วาม" enable: "เปิดใช้งาน" next: "ถัด​ไป" retype: "พิมพ์รหัสà¸à¸µà¸à¸„รั้ง" -noteOf: "โน้ต โดย {user}" +noteOf: "โน้ตขà¸à¸‡ {user}" quoteAttached: "à¸à¹‰à¸²à¸‡à¸à¸´à¸‡" -quoteQuestion: "นายต้à¸à¸‡à¸à¸²à¸£à¸—ี่จะà¸à¹‰à¸²à¸‡à¸à¸´à¸‡à¸«à¸£à¸?" -noMessagesYet: "ยังไม่มีข้à¸à¸„วามนะ" +quoteQuestion: "ต้à¸à¸‡à¸à¸²à¸£à¸—ี่จะà¹à¸™à¸šà¸¡à¸±à¸™à¹€à¸žà¸·à¹ˆà¸à¸à¹‰à¸²à¸‡à¸à¸´à¸‡à¹ƒà¸Šà¹ˆà¹„หม?" +noMessagesYet: "ยังไม่มีข้à¸à¸„วาม" newMessageExists: "คุณมีข้à¸à¸„วามใหม่" -onlyOneFileCanBeAttached: "คุณสามารถà¹à¸™à¸šà¹„ฟล์à¸à¸±à¸šà¸‚้à¸à¸„วามได้เพียงไฟล์เดียวเท่านั้นนะ" -signinRequired: "à¸à¸£à¸¸à¸“าลงทะเบียนหรืà¸à¸¥à¸‡à¸Šà¸·à¹ˆà¸à¹€à¸‚้าใช้à¸à¹ˆà¸à¸™à¸”ำเนินà¸à¸²à¸£à¸•à¹ˆà¸à¸™à¸°" -invitations: "เชิà¸à¸Šà¸§à¸™" -invitationCode: "รหัสคำเชิà¸" +onlyOneFileCanBeAttached: "สามารถà¹à¸™à¸šà¹„ฟล์ได้เพียงไฟล์เดียวต่ภ1 ข้à¸à¸„วาม" +signinRequired: "à¸à¸£à¸¸à¸“าลงทะเบียนหรืà¸à¸¥à¸‡à¸Šà¸·à¹ˆà¸à¹€à¸‚้าใช้à¸à¹ˆà¸à¸™à¸”ำเนินà¸à¸²à¸£à¸•à¹ˆà¸" +invitations: "คำเชิà¸" +invitationCode: "รหัสเชิà¸" checking: "Checking" available: "พร้à¸à¸¡à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" unavailable: "ไม่พร้à¸à¸¡à¹ƒà¸Šà¹‰" @@ -475,11 +490,11 @@ or: "หรืà¸" language: "ภาษา" uiLanguage: "ภาษาà¸à¸´à¸™à¹€à¸—à¸à¸£à¹Œà¹€à¸Ÿà¸‹à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" aboutX: "เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸š {x}" -emojiStyle: "สไตล์à¸à¸´à¹‚มจิ" +emojiStyle: "สไตล์เà¸à¹‚มจิ" native: "ภาษาà¹à¸¡à¹ˆ" disableDrawer: "à¸à¸¢à¹ˆà¸²à¹ƒà¸Šà¹‰à¸¥à¸´à¹‰à¸™à¸Šà¸±à¸à¸ªà¹„ตล์เมนู" showNoteActionsOnlyHover: "à¹à¸ªà¸”งà¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¹€à¸‰à¸žà¸²à¸°à¹‚น้ตเมื่à¸à¹‚ฮเวà¸à¸£à¹Œ" -noHistory: "ไม่มีรายà¸à¸²à¸£" +noHistory: "ไม่มีประวัติ" signinHistory: "ประวัติà¸à¸²à¸£à¹€à¸‚้าสู่ระบบ" enableAdvancedMfm: "เปิดใช้งาน MFM ขั้นสูง" enableAnimatedMfm: "เปิดà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ MFM ด้วยà¹à¸à¸™à¸´à¹€à¸¡à¸Šà¸±à¹ˆà¸™" @@ -491,7 +506,7 @@ createAccount: "สร้างบัà¸à¸Šà¸µ" existingAccount: "บัà¸à¸Šà¸µà¸—ี่มีà¸à¸¢à¸¹à¹ˆ" regenerate: "สร้างà¸à¸µà¸à¸„รั้ง" fontSize: "ขนาดตัวà¸à¸±à¸à¸©à¸£" -mediaListWithOneImageAppearance: "ความสูงขà¸à¸‡à¸¥à¸´à¸ªà¸•à¹Œà¸ªà¸·à¹ˆà¸à¸ˆà¸°à¸•à¹‰à¸à¸‡à¸¡à¸µà¸£à¸¹à¸›à¸ าพเดียวเท่านั้น" +mediaListWithOneImageAppearance: "ความสูงขà¸à¸‡à¸£à¸²à¸¢à¸à¸²à¸£à¸ªà¸·à¹ˆà¸à¸—ี่มีเพียงรูปเดียว" limitTo: "จำà¸à¸±à¸”ไว้ที่ {x}" noFollowRequests: "คุณไม่มีคำขà¸à¸•à¸´à¸”ตามที่รà¸à¸”ำเนินà¸à¸²à¸£" openImageInNewTab: "เปิดรูปภาพในà¹à¸—็บใหม่" @@ -509,14 +524,14 @@ promote: "โปรโมท" numberOfDays: "จำนวนวัน" hideThisNote: "ซ่à¸à¸™à¹‚น้ตนี้" showFeaturedNotesInTimeline: "à¹à¸ªà¸”งโน้ตเด่นในไทม์ไลน์" -objectStorage: "à¸à¹‡à¸à¸šà¹€à¸ˆà¹‡à¸à¸•à¹Œ ที่จัดเà¸à¹‡à¸š" -useObjectStorage: "ใช้ à¸à¹‡à¸à¸šà¹€à¸ˆà¹‡à¸à¸•à¹Œ ที่จัดเà¸à¹‡à¸š" -objectStorageBaseUrl: "URL à¸à¸²à¸™" +objectStorage: "à¸à¸²à¸£à¸ˆà¸±à¸”เà¸à¹‡à¸šà¹ƒà¸™à¸£à¸¹à¸›à¹à¸šà¸šà¸à¹‡à¸à¸šà¹€à¸ˆà¸à¸•à¹Œ" +useObjectStorage: "ใช้à¸à¸²à¸£à¸ˆà¸±à¸”เà¸à¹‡à¸šà¹ƒà¸™à¸£à¸¹à¸›à¹à¸šà¸šà¸à¹‡à¸à¸šà¹€à¸ˆà¸à¸•à¹Œ" +objectStorageBaseUrl: "Base URL" objectStorageBaseUrlDesc: "URL ที่ใช้เป็นข้à¸à¸¡à¸¹à¸¥à¸à¹‰à¸²à¸‡à¸à¸´à¸‡ ระบุ URL ขà¸à¸‡ CDN หรืภProxy ถ้าหาà¸à¸„ุณใช้à¸à¸¢à¹ˆà¸²à¸‡à¹ƒà¸”à¸à¸¢à¹ˆà¸²à¸‡à¸«à¸™à¸¶à¹ˆà¸‡\n สำหรับà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ S3 'https://<bucket>.s3.amazonaws.com' à¹à¸¥à¸°à¸ªà¸³à¸«à¸£à¸±à¸š GCS หรืà¸à¸šà¸£à¸´à¸à¸²à¸£à¸—ี่เทียบเท่าใช้ 'https://storage.googleapis.com/<bucket>', เป็นต้น" objectStorageBucket: "Bucket" objectStorageBucketDesc: "โปรดระบุชื่à¸à¸—ี่เà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥à¸—ี่ใช้à¸à¸±à¸šà¸œà¸¹à¹‰à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£à¸‚à¸à¸‡à¸„ุณ" objectStoragePrefix: "คำนำหน้า" -objectStoragePrefixDesc: "ไฟล์ทั้งหมดจะถูà¸à¹€à¸à¹‡à¸šà¹„ว้ภายใต้ไดเร็à¸à¸—à¸à¸£à¸µà¸—ี่มีคำนำหน้านี้นะ" +objectStoragePrefixDesc: "ไฟล์ทั้งหมดจะถูà¸à¹€à¸à¹‡à¸šà¹„ว้ภายใต้ไดเร็à¸à¸—à¸à¸£à¸µà¸—ี่มีคำนำหน้านี้" objectStorageEndpoint: "ปลายทาง" objectStorageEndpointDesc: "เว้นว่างไว้หาà¸à¸„ุณใช้ AWS S3 หรืà¸à¸£à¸°à¸šà¸¸à¸›à¸¥à¸²à¸¢à¸—างเป็น '<host>' หรืภ'<host>:<port>' ทั้งนี้ขึ้นà¸à¸¢à¸¹à¹ˆà¸à¸±à¸šà¸œà¸¹à¹‰à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£à¸—ี่คุณใช้à¸à¸¢à¸¹à¹ˆà¸”้วย" objectStorageRegion: "ภูมิภาค" @@ -525,12 +540,13 @@ objectStorageUseSSL: "ใช้ SSL" objectStorageUseSSLDesc: "ปิดà¸à¸²à¸£à¸—ำงานนี้ไว้ ถ้าหาà¸à¸„ุณจะไม่ใช้ HTTPS สำหรับà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•à¹ˆà¸ API" objectStorageUseProxy: "เชื่à¸à¸¡à¸•à¹ˆà¸à¸œà¹ˆà¸²à¸™à¸žà¸£à¹‡à¸à¸à¸‹à¸µ" objectStorageUseProxyDesc: "ปิดสิ่งนี้ไว้ถ้าหาà¸à¸„ุณจะไม่ใช้ Proxy สำหรับà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•à¹ˆà¸ API" -objectStorageSetPublicRead: "ตั้งค่า \"public-read\" ในà¸à¸²à¸£à¸à¸±à¸›à¹‚หลด" +objectStorageSetPublicRead: "ตั้งค่าเป็น “public-read†เมื่à¸à¸à¸±à¸›à¹‚หลด" s3ForcePathStyleDesc: "ถ้าหาà¸à¹€à¸›à¸´à¸”ใช้งาน s3ForcePathStyle ชื่à¸à¸šà¸±à¸„เà¸à¹‡à¸•à¸™à¸±à¹‰à¸™à¸à¸²à¸ˆà¸ˆà¸°à¸•à¹‰à¸à¸‡à¸£à¸§à¸¡à¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¹€à¸ªà¹‰à¸™à¸—างขà¸à¸‡ URL ซึ่งตรงข้ามà¸à¸±à¸šà¸Šà¸·à¹ˆà¸à¹‚ฮสต์ขà¸à¸‡ URL คุณà¸à¸²à¸ˆà¸ˆà¸°à¸•à¹‰à¸à¸‡à¹€à¸›à¸´à¸”ใช้งานà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่านี้เมื่à¸à¹ƒà¸Šà¹‰à¸šà¸£à¸´à¸à¸²à¸£à¸•à¹ˆà¸²à¸‡à¹† เช่น à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ Minio ที่โฮสต์เà¸à¸‡à¸™à¸°" -serverLogs: "บันทึà¸à¸‚à¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" +serverLogs: "ปูมขà¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" deleteAll: "ลบทั้งหมด" showFixedPostForm: "à¹à¸ªà¸”งà¹à¸šà¸šà¸Ÿà¸à¸£à¹Œà¸¡à¸à¸²à¸£à¹‚พสต์ที่ด้านบนสุดขà¸à¸‡à¹„ทม์ไลน์" -showFixedPostFormInChannel: "à¹à¸ªà¸”งà¹à¸šà¸šà¸Ÿà¸à¸£à¹Œà¸¡à¸à¸³à¸¥à¸±à¸‡à¹‚พสต์ที่ด้านบนขà¸à¸‡à¹„ทม์ไลน์ (à¹à¸Šà¸™à¹à¸™à¸¥)" +showFixedPostFormInChannel: "à¹à¸ªà¸”งà¹à¸šà¸šà¸Ÿà¸à¸£à¹Œà¸¡à¸à¸²à¸£à¹‚พสต์ที่ด้านบนขà¸à¸‡à¹„ทม์ไลน์ (ช่à¸à¸‡)" +withRepliesByDefaultForNewlyFollowed: "à¹à¸ªà¸”งà¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¸ˆà¸²à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่คุณเพิ่งติดตามลงไทม์ไลน์ตามค่าเริ่มต้น" newNoteRecived: "มีโน้ตใหม่" sounds: "เสียง" sound: "เสียง" @@ -538,10 +554,12 @@ listen: "ฟัง" none: "ไม่มี" showInPage: "à¹à¸ªà¸”งในเพจ" popout: "ป๊à¸à¸›à¹€à¸à¸²à¸•à¹Œ" -volume: "ความดัง" -masterVolume: "มาสเตà¸à¸£à¹Œà¸§à¸à¸¥à¸¸à¹ˆà¸¡" +volume: "ระดับเสียง" +masterVolume: "ระดับเสียงหลัà¸" +notUseSound: "ไม่ใช้เสียง" +useSoundOnlyWhenActive: "มีเสียงà¸à¸à¸à¹€à¸‰à¸žà¸²à¸°à¸•à¸à¸™à¸à¸³à¸¥à¸±à¸‡à¹ƒà¸Šà¹‰ Misskey à¸à¸¢à¸¹à¹ˆà¹€à¸—่านั้น" details: "รายละเà¸à¸µà¸¢à¸”" -chooseEmoji: "เลืà¸à¸à¹‚มจิขà¸à¸‡à¹€à¸˜à¸" +chooseEmoji: "เลืà¸à¸à¹€à¸à¹‚มจิ" unableToProcess: "ไม่สามารถดำเนินà¸à¸²à¸£à¹ƒà¸«à¹‰à¹€à¸ªà¸£à¹‡à¸ˆà¸ªà¸´à¹‰à¸™à¹„ด้" recentUsed: "ใช้ล่าสุด" install: "ติดตั้ง" @@ -552,24 +570,24 @@ installedDate: "วันที่ติดตั้ง" lastUsedDate: "ใช้งานครั้งล่าสุด" state: "สถานะ" sort: "เรียงลำดับ" -ascendingOrder: "เรียงจาà¸à¸™à¹‰à¸à¸¢à¹„ปมาà¸" -descendingOrder: "เรียงจาà¸à¸¡à¸²à¸à¹„ปน้à¸à¸¢" -scratchpad: "à¸à¸£à¸°à¸”านทดลà¸à¸‡" +ascendingOrder: "เรียงลำดับขึ้น" +descendingOrder: "เรียงลำดับลง" +scratchpad: "Scratchpad" scratchpadDescription: "Scratchpad เป็นà¸à¸²à¸£à¸ˆà¸±à¸”เตรียมสภาพà¹à¸§à¸”ล้à¸à¸¡à¸ªà¸³à¸«à¸£à¸±à¸šà¸à¸²à¸£à¸—ดลà¸à¸‡ AiScript à¹à¸•à¹ˆà¸„ุณสามารถเขียน ดำเนินà¸à¸²à¸£ à¹à¸¥à¸°à¸•à¸£à¸§à¸ˆà¸ªà¸à¸šà¸œà¸¥à¸¥à¸±à¸žà¸˜à¹Œà¸‚à¸à¸‡à¸à¸²à¸£à¹‚ต้ตà¸à¸šà¸à¸±à¸š Misskey มันได้ด้วยนะ" output: "เà¸à¸²à¸—์พุต" script: "สคริปต์" disablePagesScript: "ปิดà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ AiScript บนเพจ" updateRemoteUser: "à¸à¸±à¸›à¹€à¸”ตข้à¸à¸¡à¸¹à¸¥à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸£à¸°à¸¢à¸°à¹„à¸à¸¥" unsetUserAvatar: "เลิà¸à¸•à¸±à¹‰à¸‡à¸à¸§à¸•à¸²à¸£" -unsetUserAvatarConfirm: "คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸·à¸à¹„ม่ว่าต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸•à¸±à¹‰à¸‡à¸à¸§à¸•à¸²à¸£?" +unsetUserAvatarConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸•à¸±à¹‰à¸‡à¸à¸§à¸•à¸²à¸£à¹ƒà¸‚่ไหม?" unsetUserBanner: "เลิà¸à¸•à¸±à¹‰à¸‡à¹à¸šà¸™à¹€à¸™à¸à¸£à¹Œ" -unsetUserBannerConfirm: "คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸·à¸à¹„ม่ว่าต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸•à¸±à¹‰à¸‡à¹à¸šà¸™à¹€à¸™à¸à¸£à¹Œà¹€à¸¥à¸¢à¸¡à¸±à¹‰à¸¢?" +unsetUserBannerConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸•à¸±à¹‰à¸‡à¹à¸šà¸™à¹€à¸™à¸à¸£à¹Œ?" deleteAllFiles: "ลบไฟล์ทั้งหมด" -deleteAllFilesConfirm: "นายà¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¸«à¸£à¸à¸§à¹ˆà¸²à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸—ี่จะลบไฟล์ทั้งหมด?" +deleteAllFilesConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¹„ฟล์ทั้งหมดใช่ไหม?" removeAllFollowing: "เลิà¸à¸•à¸´à¸”ตามผู้ใช้ที่ติดตามทั้งหมด" -removeAllFollowingDescription: "à¸à¸²à¸£à¸—ี่คุณดำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¸ˆà¸°à¹€à¸¥à¸´à¸à¸•à¸´à¸”ตามบัà¸à¸Šà¸µà¸—ั้งหมดจาภ{host} โปรดเรียà¸à¹ƒà¸Šà¹‰à¸„ำสั่งสิ่งนี้หาà¸à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸¢à¸à¹€à¸¥à¸´à¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ เช่น ไม่มีà¸à¸¢à¸¹à¹ˆà¹à¸¥à¹‰à¸§" +removeAllFollowingDescription: "เลิà¸à¸•à¸´à¸”ตามทั้งหมดจาภ{host} โปรดเรียà¸à¹ƒà¸Šà¹‰à¸ªà¸´à¹ˆà¸‡à¸™à¸µà¹‰à¹€à¸¡à¸·à¹ˆà¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸”ังà¸à¸¥à¹ˆà¸²à¸§à¹„ด้สูà¸à¸«à¸²à¸¢à¸•à¸²à¸¢à¸ˆà¸²à¸à¹„ปà¹à¸¥à¹‰à¸§" userSuspended: "ผู้ใช้รายนี้ถูà¸à¸£à¸°à¸‡à¸±à¸šà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" -userSilenced: "ผู้ใช้รายนี้à¸à¸³à¸¥à¸±à¸‡à¸–ูà¸à¸›à¸´à¸”à¸à¸±à¹‰à¸™" +userSilenced: "ผู้ใช้รายนี้ถูà¸à¸›à¸´à¸”ปาà¸à¸à¸¢à¸¹à¹ˆ" yourAccountSuspendedTitle: "บัà¸à¸Šà¸µà¸™à¸µà¹‰à¸™à¸±à¹‰à¸™à¸–ูà¸à¸£à¸°à¸‡à¸±à¸š" yourAccountSuspendedDescription: "บัà¸à¸Šà¸µà¸™à¸µà¹‰à¸–ูà¸à¸£à¸°à¸‡à¸±à¸š เนื่à¸à¸‡à¸ˆà¸²à¸à¸¥à¸°à¹€à¸¡à¸´à¸”ข้à¸à¸à¸³à¸«à¸™à¸”ในà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£à¸‚à¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸«à¸£à¸·à¸à¸à¸²à¸ˆà¸ˆà¸°à¸¥à¸°à¹€à¸¡à¸´à¸”หลัà¸à¹€à¸à¸“ฑ์ชุมชน หรืภà¸à¸²à¸ˆà¸ˆà¸°à¹‚ดนร้à¸à¸‡à¹€à¸£à¸µà¸¢à¸™à¹€à¸£à¸·à¹ˆà¸à¸‡à¸à¸²à¸£à¸¥à¸°à¹€à¸¡à¸´à¸”ลิขสิทธิ์à¹à¸¥à¸°à¸à¸·à¹ˆà¸™à¹†à¸à¸¢à¹ˆà¸²à¸‡à¸•à¹ˆà¸à¹€à¸™à¸·à¹ˆà¸à¸‡à¸‹à¹‰à¸³à¹† หาà¸à¸„ุณคิดว่าไม่ได้ทำผิดจริงๆหรืà¸à¸•à¸±à¸”สินผิดพลาด ได้โปรดà¸à¸£à¸¸à¸“าติดต่à¸à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¸£à¸°à¸šà¸šà¸«à¸²à¸à¸„ุณต้à¸à¸‡à¸à¸²à¸£à¸—ราบเหตุผลโดยละเà¸à¸µà¸¢à¸”เพิ่มเติม à¹à¸¥à¸°à¸‚à¸à¸„วามà¸à¸£à¸¸à¸“าà¸à¸¢à¹ˆà¸²à¸ªà¸£à¹‰à¸²à¸‡à¸šà¸±à¸à¸Šà¸µà¹ƒà¸«à¸¡à¹ˆ" tokenRevoked: "โทเค็นไม่ถูà¸à¸•à¹‰à¸à¸‡" @@ -582,7 +600,7 @@ addItem: "เพิ่มรายà¸à¸²à¸£" rearrange: "จัดใหม่" relays: "รีเลย์" addRelay: "เพิ่มรีเลย์" -inboxUrl: "à¸à¸´à¸™à¸šà¹‡à¸à¸à¸‹à¹Œ URL" +inboxUrl: "URL ขà¸à¸‡à¸à¸´à¸™à¸šà¹‡à¸à¸à¸‹à¹Œ" addedRelays: "เพิ่มรีเลย์à¹à¸¥à¹‰à¸§" serviceworkerInfo: "ต้à¸à¸‡à¹€à¸›à¸´à¸”ใช้งานสำหรับà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¹à¸šà¸šà¸žà¸¸à¸Š" deletedNote: "โน้ตที่ถูà¸à¸¥à¸š" @@ -599,14 +617,14 @@ description: "รายละเà¸à¸µà¸¢à¸”" describeFile: "เพิ่มà¹à¸„ปชั่น" enterFileDescription: "ใส่à¹à¸„ปชั่น" author: "ผู้เขียน" -leaveConfirm: "คุณมีà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸—ี่ไม่ได้บันทึà¸à¸™à¸° นายต้à¸à¸‡à¸à¸²à¸£à¸—ิ้งà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¹€à¸«à¸¥à¹ˆà¸²à¸™à¸±à¹‰à¸™à¸«à¸£à¸?" +leaveConfirm: "มีà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸—ี่ยังไม่ได้บันทึภต้à¸à¸‡à¸à¸²à¸£à¸¥à¸°à¸—ิ้งมันใช่ไหม?" manage: "à¸à¸²à¸£à¸ˆà¸±à¸”à¸à¸²à¸£" plugins: "ปลั๊à¸à¸à¸´à¸™" preferencesBackups: "ตั้งค่าà¸à¸²à¸£à¸ªà¸³à¸£à¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥" deck: "เด็ค" undeck: "à¸à¸à¸à¸ˆà¸²à¸à¹€à¸”็ค" useBlurEffectForModal: "ใช้เà¸à¸Ÿà¹€à¸Ÿà¸à¸•à¹Œà¹€à¸šà¸¥à¸à¸ªà¸³à¸«à¸£à¸±à¸šà¹‚มดà¸à¸¥" -useFullReactionPicker: "ใช้เครื่à¸à¸‡à¸¡à¸·à¸à¹€à¸¥à¸·à¸à¸à¸›à¸à¸´à¸à¸´à¸£à¸´à¸¢à¸²à¸‚นาดเต็ม" +useFullReactionPicker: "ใช้ตัวจิ้มรีà¹à¸à¸„ชั่นà¸à¸¢à¹ˆà¸²à¸‡à¹€à¸•à¹‡à¸¡à¸£à¸¹à¸›à¹à¸šà¸š" width: "ความà¸à¸§à¹‰à¸²à¸‡" height: "ความสูง" large: "ใหà¸à¹ˆ" @@ -614,17 +632,18 @@ medium: "ปานà¸à¸¥à¸²à¸‡" small: "เล็à¸" generateAccessToken: "สร้างà¸à¸²à¸£à¹€à¸‚้าถึงโทเค็น" permission: "à¸à¸²à¸£à¸à¸™à¸¸à¸à¸²à¸•" +adminPermission: "สิทธิ์ขà¸à¸‡à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¸£à¸°à¸šà¸š" enableAll: "เปิดใช้งานทั้งหมด" disableAll: "ปิดà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸—ั้งหมด" tokenRequested: "ให้สิทธิ์à¸à¸²à¸£à¹€à¸‚้าถึงบัà¸à¸Šà¸µ" pluginTokenRequestedDescription: "ปลั๊à¸à¸à¸´à¸™à¸™à¸µà¹‰à¸ˆà¸°à¸ªà¸²à¸¡à¸²à¸£à¸–ใช้à¸à¸²à¸£à¸à¸™à¸¸à¸à¸²à¸•à¸—ี่ตั้งค่าไว้ที่นี่นะ" notificationType: "ประเภทà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™" edit: "à¹à¸à¹‰à¹„ข" -emailServer: "à¸à¸µà¹€à¸¡à¸¥à¹Œà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" +emailServer: "à¸à¸µà¹€à¸¡à¸¥à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" enableEmail: "เปิดใช้งานà¸à¸²à¸£à¸à¸£à¸°à¸ˆà¸²à¸¢à¸à¸µà¹€à¸¡à¸¥" emailConfigInfo: "ใช้เพื่à¸à¸¢à¸·à¸™à¸¢à¸±à¸™à¸à¸µà¹€à¸¡à¸¥à¸‚à¸à¸‡à¸„ุณระหว่างà¸à¸²à¸£à¸ªà¸¡à¸±à¸„รหรืà¸à¸–้าหาà¸à¸„ุณลืมรหัสผ่าน" -email: "à¸à¸µà¹€à¸¡à¸¥à¹Œ" -emailAddress: "ที่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¹Œ" +email: "à¸à¸µà¹€à¸¡à¸¥" +emailAddress: "ที่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥" smtpConfig: "à¸à¸³à¸«à¸™à¸”ค่าเซิร์ฟเวà¸à¸£à¹Œ SMTP" smtpHost: "โฮสต์" smtpPort: "พà¸à¸£à¹Œà¸•" @@ -645,41 +664,42 @@ display: "à¹à¸ªà¸”งผล" copy: "คัดลà¸à¸" metrics: "เมตริà¸" overview: "ภาพรวม" -logs: "บันทึà¸à¸‚้à¸à¸¡à¸¹à¸¥à¸£à¸°à¸šà¸š" +logs: "ปูม" delayed: "ดีเลย์" database: "à¸à¸²à¸™à¸‚้à¸à¸¡à¸¹à¸¥" -channel: "à¹à¸Šà¸™à¹à¸™à¸¥" +channel: "ช่à¸à¸‡" create: "สร้าง" notificationSetting: "ตั้งค่าà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™" notificationSettingDesc: "เลืà¸à¸à¸›à¸£à¸°à¹€à¸ ทà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸—ี่ต้à¸à¸‡à¸à¸²à¸£à¸ˆà¸°à¹à¸ªà¸”ง" useGlobalSetting: "ใช้à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าส่วนà¸à¸¥à¸²à¸‡" -useGlobalSettingDesc: "หาà¸à¹€à¸›à¸´à¸”ไว้ ระบบจะใช้à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸‚à¸à¸‡à¸šà¸±à¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณ หาà¸à¸›à¸´à¸”à¸à¸¢à¸¹à¹ˆ สามารถทำà¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าà¹à¸•à¹ˆà¸¥à¸°à¸£à¸²à¸¢à¸à¸²à¸£à¹„ด้นะ" +useGlobalSettingDesc: "เมื่à¸à¹€à¸›à¸´à¸”ใช้งาน ใช้à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸ˆà¸²à¸à¸šà¸±à¸à¸Šà¸µà¸„ุณ เมื่à¸à¸›à¸´à¸”ใช้งาน สามารถตั้งค่าได้à¸à¸¢à¹ˆà¸²à¸‡à¸à¸´à¸ªà¸£à¸°" other: "à¸à¸·à¹ˆà¸™ ๆ" regenerateLoginToken: "สร้างโทเค็นà¸à¸²à¸£à¹€à¸‚้าสู่ระบบà¸à¸µà¸à¸„รั้ง" regenerateLoginTokenDescription: "สร้างโทเค็นใหม่ที่ใช้ภายในระหว่างà¸à¸²à¸£à¹€à¸‚้าสู่ระบบ โดยตามหลัà¸à¸›à¸à¸•à¸´à¹à¸¥à¹‰à¸§à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¹„ม่จำเป็น หาà¸à¸ªà¸£à¹‰à¸²à¸‡à¹ƒà¸«à¸¡à¹ˆ à¸à¸¸à¸›à¸à¸£à¸“์ทั้งหมดจะถูà¸à¸à¸à¸à¸ˆà¸²à¸à¸£à¸°à¸šà¸šà¸™à¸°" +theKeywordWhenSearchingForCustomEmoji: "คีย์เวิร์ดสำหรับใช้ค้นหาเà¸à¹‚มจิที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡" setMultipleBySeparatingWithSpace: "คั่นหลายรายà¸à¸²à¸£à¸”้วยช่à¸à¸‡à¸§à¹ˆà¸²à¸‡" -fileIdOrUrl: "ไฟล์ ID หรืภURL" +fileIdOrUrl: "ID ขà¸à¸‡à¹„ฟล์ หรืภURL" behavior: "พฤติà¸à¸£à¸£à¸¡" sample: "ตัวà¸à¸¢à¹ˆà¸²à¸‡" abuseReports: "รายงาน" reportAbuse: "รายงาน" reportAbuseRenote: "รายงานรีโน้ต" -reportAbuseOf: "รายงาน {ชื่à¸}" +reportAbuseOf: "รายงาน {name}" fillAbuseReportDescription: "à¸à¸£à¸¸à¸“าà¸à¸£à¸à¸à¸£à¸²à¸¢à¸¥à¸°à¹€à¸à¸µà¸¢à¸”เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸£à¸²à¸¢à¸‡à¸²à¸™à¸™à¸µà¹‰ หาà¸à¹€à¸›à¹‡à¸™à¹€à¸£à¸·à¹ˆà¸à¸‡à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¹‚น้ตโดยเฉพาะ ได้โปรดระบุ URL" abuseReported: "เราได้ส่งรายงานขà¸à¸‡à¸„ุณไปà¹à¸¥à¹‰à¸§ ขà¸à¸šà¸„ุณมาà¸à¹†à¸™à¸°" -reporter: "นัà¸à¸‚่าว" +reporter: "ผู้รายงาน" reporteeOrigin: "รายงานต้นทาง" -reporterOrigin: "นัà¸à¸‚่าวต้นทาง" +reporterOrigin: "à¹à¸«à¸¥à¹ˆà¸‡à¸œà¸¹à¹‰à¸£à¸²à¸¢à¸‡à¸²à¸™" forwardReport: "ส่งต่à¸à¸£à¸²à¸¢à¸‡à¸²à¸™à¹„ปยังà¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥" -forwardReportIsAnonymous: "à¹à¸—นที่จะเป็นบัà¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณ บัà¸à¸Šà¸µà¸£à¸°à¸šà¸šà¸—ี่ไม่ระบุตัวตนจะà¹à¸ªà¸”งเป็นนัà¸à¸‚่าวที่à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥" +forwardReportIsAnonymous: "ข้à¸à¸¡à¸¹à¸¥à¸‚à¸à¸‡à¸„ุณจะไม่ปราà¸à¸à¸šà¸™à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥à¹à¸¥à¸°à¸›à¸£à¸²à¸à¸à¹€à¸›à¹‡à¸™à¸šà¸±à¸à¸Šà¸µà¸£à¸°à¸šà¸šà¸—ี่ไม่ระบุชื่à¸" send: "ส่ง" abuseMarkAsResolved: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸£à¸²à¸¢à¸‡à¸²à¸™à¸§à¹ˆà¸²à¹à¸à¹‰à¹„ขà¹à¸¥à¹‰à¸§" openInNewTab: "เปิดในà¹à¸—็บใหม่" openInSideView: "เปิดในมุมมà¸à¸‡à¸”้านข้าง" defaultNavigationBehaviour: "พฤติà¸à¸£à¸£à¸¡à¸à¸²à¸£à¸™à¸³à¸—างที่เป็นค่าเริ่มต้น" editTheseSettingsMayBreakAccount: "à¸à¸²à¸£à¹à¸à¹‰à¹„ขà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าเหล่านี้à¸à¸²à¸ˆà¸—ำให้บัà¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณเสียหายนะ" -instanceTicker: "ข้à¸à¸¡à¸¹à¸¥à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸‚à¸à¸‡à¸šà¸±à¸™à¸—ึà¸à¸¢à¹ˆà¸" -waitingFor: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸à¸„à¸à¸¢ {x}" +instanceTicker: "ข้à¸à¸¡à¸¹à¸¥à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸‚à¸à¸‡à¹‚น้ต" +waitingFor: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸ {x}" random: "สุ่มค่า" system: "ระบบ" switchUi: "สลับ UI" @@ -689,7 +709,7 @@ createNew: "สร้างใหม่" optional: "ไม่บังคับ" createNewClip: "สร้างคลิปใหม่" unclip: "ลบคลิป" -confirmToUnclipAlreadyClippedNote: "โน้ตนี้เป็นส่วนหนึ่งขà¸à¸‡à¸„ลิป \"{name}\" à¹à¸¥à¹‰à¸§ คุณต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¸à¸à¸à¸ˆà¸²à¸à¸„ลิปนี้à¹à¸—นà¸à¸¢à¹ˆà¸²à¸‡à¸‡à¸±à¹‰à¸™à¸«à¸£à¸?" +confirmToUnclipAlreadyClippedNote: "โน้ตนี้เป็นส่วนหนึ่งขà¸à¸‡à¸„ลิป “{name}†à¸à¸¢à¸¹à¹ˆà¹à¸¥à¹‰à¸§ ต้à¸à¸‡à¸à¸²à¸£à¸™à¸³à¸¡à¸±à¸™à¸à¸à¸à¸ˆà¸²à¸à¸„ลิปใช่ไหม?" public: "สาธารณะ" private: "ส่วนตัว" i18nInfo: "Misskey à¸à¸³à¸¥à¸±à¸‡à¹„ด้รับà¸à¸²à¸£à¹à¸›à¸¥à¹€à¸›à¹‡à¸™à¸ าษาต่างๆ โดยà¸à¸²à¸ªà¸²à¸ªà¸¡à¸±à¸„ร คุณสามารถช่วยเหลืà¸à¹„ด้ที่ {link}" @@ -702,8 +722,8 @@ repliedCount: "จำนวนขà¸à¸‡à¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¸—ี่ renotedCount: "จำนวนรีโน้ตที่ได้รับà¹à¸¥à¹‰à¸§" followingCount: "จำนวนบัà¸à¸Šà¸µà¸—ี่ติดตาม" followersCount: "จำนวนผู้ติดตาม" -sentReactionsCount: "จำนวนปà¸à¸´à¸à¸´à¸£à¸´à¸¢à¸²à¸—ี่ส่ง" -receivedReactionsCount: "จำนวนปà¸à¸´à¸à¸´à¸£à¸´à¸¢à¸²à¸—ี่ได้รับ" +sentReactionsCount: "จำนวนรีà¹à¸à¸„ชั่นที่ส่ง" +receivedReactionsCount: "จำนวนรีà¹à¸à¸„ชั่นที่ได้รับ" pollVotesCount: "จำนวนโหวตที่ส่งไป" pollVotedCount: "จำนวนโหวตที่ได้รับ" yes: "ใช่" @@ -711,17 +731,17 @@ no: "ไม่" driveFilesCount: "จำนวนไฟล์ไดรฟ์" driveUsage: "à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸žà¸·à¹‰à¸™à¸—ี่ไดรฟ์" noCrawle: "ปà¸à¸´à¹€à¸ªà¸˜à¸à¸²à¸£à¸ˆà¸±à¸”ทำดัชนีขà¸à¸‡à¹‚ปรà¹à¸à¸£à¸¡à¸£à¸§à¸šà¸£à¸§à¸¡à¸‚้à¸à¸¡à¸¹à¸¥" -noCrawleDescription: "ขà¸à¹ƒà¸«à¹‰à¹€à¸„รื่à¸à¸‡à¸¡à¸·à¸à¸„้นหาไม่จัดทำดัชนีหน้าโปรไฟล์ บันทึà¸à¸¢à¹ˆà¸ หน้า ฯลฯ" -lockedAccountInfo: "เว้นà¹à¸•à¹ˆà¸§à¹ˆà¸²à¸„ุณจะต้à¸à¸‡à¸•à¸±à¹‰à¸‡à¸„่าà¸à¸²à¸£à¹€à¸›à¸´à¸”เผยโน้ตเป็น \"ผู้ติดตามเท่านั้น\" โน้ตย่à¸à¸‚à¸à¸‡à¸„ุณจะปราà¸à¸à¹à¸à¹ˆà¸—ุà¸à¸„น ถึงà¹à¸¡à¹‰à¸§à¹ˆà¸²à¸„ุณจะเป็นà¸à¸³à¸«à¸™à¸”ให้ผู้ติดตามต้à¸à¸‡à¹„ด้รับà¸à¸²à¸£à¸à¸™à¸¸à¸¡à¸±à¸•à¸´à¸”้วยตนเà¸à¸‡à¸à¹‡à¸•à¸²à¸¡" -alwaysMarkSensitive: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹€à¸›à¹‡à¸™ NSFW เป็นค่าเริ่มต้น" +noCrawleDescription: "ขà¸à¹ƒà¸«à¹‰à¹€à¸„รื่à¸à¸‡à¸¡à¸·à¸à¸„้นหาไม่จัดทำดัชนีหน้าโปรไฟล์ โน้ต หน้าเพจ ฯลฯ" +lockedAccountInfo: "à¹à¸¡à¹‰à¸§à¹ˆà¸²à¸à¸²à¸£à¸à¸™à¸¸à¸¡à¸±à¸•à¸´à¸à¸²à¸£à¸•à¸´à¸”ตามถูà¸à¹€à¸›à¸´à¸”ใช้งานà¸à¸¢à¸¹à¹ˆà¸—ุà¸à¸„นà¸à¹‡à¸¢à¸±à¸‡à¸„งสามารถเห็นโน้ตขà¸à¸‡à¸„ุณได้ เว้นà¹à¸•à¹ˆà¸§à¹ˆà¸²à¸„ุณจะเปลี่ยนà¸à¸²à¸£à¹€à¸›à¸´à¸”เผยโน้ตขà¸à¸‡à¸„ุณเป็น “เฉพาะผู้ติดตามâ€" +alwaysMarkSensitive: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™à¹€à¸›à¹‡à¸™à¸„่าเริ่มต้น" loadRawImages: "โหลดภาพต้นฉบับà¹à¸—นà¸à¸²à¸£à¹à¸ªà¸”งภาพขนาดย่à¸" disableShowingAnimatedImages: "ไม่ต้à¸à¸‡à¹€à¸¥à¹ˆà¸™à¸ าพเคลื่à¸à¸™à¹„หว" -highlightSensitiveMedia: "ไฮไลท์สื่à¸à¸—ี่ละเà¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" +highlightSensitiveMedia: "ไฮไลท์สื่à¸à¸—ี่มีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" verificationEmailSent: "ส่งà¸à¸µà¹€à¸¡à¸¥à¸¢à¸·à¸™à¸¢à¸±à¸™à¹à¸¥à¹‰à¸§à¸™à¸° ได้โปรดà¸à¸£à¸¸à¸“าไปที่ลิงà¸à¹Œà¸—ี่รวมไว้เพื่à¸à¸—ำà¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ªà¸à¸šà¹ƒà¸«à¹‰à¹€à¸ªà¸£à¹‡à¸ˆà¸ªà¸´à¹‰à¸™" notSet: "ไม่ได้ตั้งค่า" emailVerified: "à¸à¸µà¹€à¸¡à¸¥à¹„ด้รับà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¹à¸¥à¹‰à¸§" noteFavoritesCount: "จำนวนโน้ตที่ชื่นชà¸à¸š" -pageLikesCount: "จำนวนเพจที่ชà¸à¸š" +pageLikesCount: "จำนวนเพจที่ถูà¸à¹ƒà¸ˆ" pageLikedCount: "จำนวนà¸à¸²à¸£à¸à¸”ถูà¸à¹ƒà¸ˆà¹€à¸žà¸ˆà¸—ี่ได้รับà¹à¸¥à¹‰à¸§" contact: "ติดต่à¸" useSystemFont: "ใช้ฟà¸à¸™à¸•à¹Œà¹€à¸£à¸´à¹ˆà¸¡à¸•à¹‰à¸™à¸‚à¸à¸‡à¸£à¸°à¸šà¸š" @@ -730,15 +750,15 @@ experimentalFeatures: "ฟังà¸à¹Œà¸Šà¸±à¹ˆà¸™à¸—ดสà¸à¸š" experimental: "ทดลà¸à¸‡" thisIsExperimentalFeature: "นี่คืà¸à¸Ÿà¸µà¹€à¸ˆà¸à¸£à¹Œà¸—ดลà¸à¸‡à¸™à¸°à¸„่ะ ฟังà¸à¹Œà¸Šà¸±à¸™à¸à¸²à¸£à¸—ำงานบางà¸à¸¢à¹ˆà¸²à¸‡à¸à¸²à¸ˆà¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¹„ด้ à¹à¸¥à¸°à¸à¸²à¸ˆà¹„ม่ทำงานหรืà¸à¹„ม่เสถียรตามที่ตั้งใจไว้นะ" developer: "สำหรับนัà¸à¸žà¸±à¸’นา" -makeExplorable: "ทำให้บัà¸à¸Šà¸µà¸¡à¸à¸‡à¹€à¸«à¹‡à¸™à¹ƒà¸™ \"สำรวจ\"" -makeExplorableDescription: "ถ้าหาà¸à¸„ุณปิดà¸à¸²à¸£à¸—ำงานนี้ บัà¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณนั้นจะไม่à¹à¸ªà¸”งในส่วน \"สำรวจ\" นะ" +makeExplorable: "ทำให้บัà¸à¸Šà¸µà¸¡à¸à¸‡à¹€à¸«à¹‡à¸™à¹ƒà¸™ “สำรวจâ€" +makeExplorableDescription: "ถ้าหาà¸à¸„ุณปิดà¸à¸²à¸£à¸—ำงานนี้ บัà¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณนั้นจะไม่à¹à¸ªà¸”งในส่วน “สำรวจâ€" showGapBetweenNotesInTimeline: "à¹à¸ªà¸”งช่à¸à¸‡à¸§à¹ˆà¸²à¸‡à¸£à¸°à¸«à¸§à¹ˆà¸²à¸‡à¹‚พสต์บนไทม์ไลน์" duplicate: "ทำซ้ำ" left: "ซ้าย" -center: "ศูนย์à¸à¸¥à¸²à¸‡" +center: "à¸à¸¶à¹ˆà¸‡à¸à¸¥à¸²à¸‡" wide: "à¸à¸§à¹‰à¸²à¸‡" narrow: "ชิด" -reloadToApplySetting: "à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่านี้จะมีผลหลังจาà¸à¹‚หลดหน้าซ้ำเท่านั้น ต้à¸à¸‡à¸à¸²à¸£à¸—ี่จะโหลดใหม่เลยมั้ย" +reloadToApplySetting: "à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่านี้จะมีผลหลังจาà¸à¹‚หลดหน้าซ้ำเท่านั้น ต้à¸à¸‡à¸à¸²à¸£à¸—ี่จะโหลดใหม่เลยไหม?" needReloadToApply: "จำเป็นต้à¸à¸‡à¹‚หลดซ้ำถึงจะมีผลนะ" showTitlebar: "à¹à¸ªà¸”งà¹à¸–บชื่à¸" clearCache: "ล้างà¹à¸„ช" @@ -748,57 +768,57 @@ nNotes: "{n} โน้ต" sendErrorReports: "ส่งรายงานว่าข้à¸à¸œà¸´à¸”พลาด" sendErrorReportsDescription: "เมื่à¸à¹€à¸›à¸´à¸”ใช้งาน ข้à¸à¸¡à¸¹à¸¥à¸‚้à¸à¸œà¸´à¸”พลาดโดยรายละเà¸à¸µà¸¢à¸”นั้นจะถูà¸à¹à¸Šà¸£à¹Œà¹ƒà¸«à¹‰à¸à¸±à¸š Misskey เมื่à¸à¹€à¸à¸´à¸”ปัà¸à¸«à¸² ซึ่งช่วยปรับปรุงคุณภาพขà¸à¸‡ Misskey\nซึ่งจะรวมถึงข้à¸à¸¡à¸¹à¸¥ เช่น เวà¸à¸£à¹Œà¸Šà¸±à¹ˆà¸™à¸‚à¸à¸‡à¸£à¸°à¸šà¸šà¸›à¸à¸´à¸šà¸±à¸•à¸´à¸à¸²à¸£ เบราว์เซà¸à¸£à¹Œà¸—ี่คุณใช้ à¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸‚à¸à¸‡à¸„ุณใน Misskey เป็นต้น" myTheme: "ธีมขà¸à¸‡à¸‰à¸±à¸™" -backgroundColor: "ภาพพื้นหลัง" -accentColor: "รูปà¹à¸šà¸šà¸ªà¸µ" +backgroundColor: "สีพื้นหลัง" +accentColor: "สีหลัà¸" textColor: "สีข้à¸à¸„วาม" saveAs: "บันทึà¸à¹€à¸›à¹‡à¸™..." advanced: "ขั้นสูง" advancedSettings: "à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าขั้นสูง" value: "ค่า" createdAt: "สร้างเมื่à¸" -updatedAt: "à¸à¸±à¸žà¹€à¸”ทล่าสุด" +updatedAt: "à¸à¸±à¸›à¹€à¸”ตล่าสุด" saveConfirm: "บันทึà¸à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸¡à¸±à¹‰à¸¢?" deleteConfirm: "ลบจริงๆเหรà¸?" invalidValue: "ค่านี้ไม่ถูà¸à¸•à¹‰à¸à¸‡" registry: "ทะเบียน" closeAccount: "ปิด บัà¸à¸Šà¸µ" currentVersion: "เวà¸à¸£à¹Œà¸Šà¸±à¹ˆà¸™à¸›à¸±à¸ˆà¸ˆà¸¸à¸šà¸±à¸™" -latestVersion: "รุ่นปัจจุบัน" +latestVersion: "เวà¸à¸£à¹Œà¸Šà¸±à¹ˆà¸™à¸¥à¹ˆà¸²à¸ªà¸¸à¸”" youAreRunningUpToDateClient: "คุณà¸à¸³à¸¥à¸±à¸‡à¹ƒà¸Šà¹‰à¹„คลเà¸à¹‡à¸™à¸•à¹Œà¹€à¸§à¸à¸£à¹Œà¸Šà¸±à¸™à¹ƒà¸«à¸¡à¹ˆà¸¥à¹ˆà¸²à¸ªà¸¸à¸”นะ" newVersionOfClientAvailable: "มีไคลเà¸à¹‡à¸™à¸•à¹Œà¹€à¸§à¸à¸£à¹Œà¸Šà¸±à¸™à¹ƒà¸«à¸¡à¹ˆà¸à¸§à¹ˆà¸²à¸‚à¸à¸‡à¸„ุณพร้à¸à¸¡à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸™à¸°" usageAmount: "à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" capacity: "ความจุ" inUse: "ใช้à¹à¸¥à¹‰à¸§" editCode: "à¹à¸à¹‰à¹„ขโค้ด" -apply: "ตà¸à¸¥à¸‡" +apply: "นำไปใช้" receiveAnnouncementFromInstance: "รับà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸ˆà¸²à¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸™à¸µà¹‰" -emailNotification: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸—างà¸à¸µà¹€à¸¡à¸¥à¹Œ" +emailNotification: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸—างà¸à¸µà¹€à¸¡à¸¥" publish: "เผยà¹à¸žà¸£à¹ˆ" inChannelSearch: "ค้นหาในช่à¸à¸‡" -useReactionPickerForContextMenu: "เปิดตัวเลืà¸à¸à¸›à¸à¸´à¸à¸´à¸£à¸´à¸¢à¸²à¹€à¸¡à¸·à¹ˆà¸à¸„ลิà¸à¸‚วา" -typingUsers: "{users} à¸à¸³à¸¥à¸±à¸‡/à¸à¸³à¸¥à¸±à¸‡à¸žà¸´à¸¡à¸žà¹Œ..." +useReactionPickerForContextMenu: "คลิà¸à¸‚วาเพื่à¸à¹€à¸›à¸´à¸”ตัวจิ้มรีà¹à¸à¸„ชั่น" +typingUsers: "{users} à¸à¸³à¸¥à¸±à¸‡à¸žà¸´à¸¡à¸žà¹Œ..." jumpToSpecifiedDate: "ข้ามไปยังวันที่เฉพาะเจาะจง" showingPastTimeline: "à¸à¸³à¸¥à¸±à¸‡à¹à¸ªà¸”งผลไทม์ไลน์เà¸à¹ˆà¸²" clear: "ล้าง" markAllAsRead: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸—ั้งหมดว่าà¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" goBack: "ย้à¸à¸™à¸à¸¥à¸±à¸š" -unlikeConfirm: "ลบไลค์ขà¸à¸‡à¸„ุณà¸à¸à¸à¸ˆà¸£à¸´à¸‡à¹†à¸«à¸£à¸" +unlikeConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸–ูà¸à¹ƒà¸ˆà¹ƒà¸Šà¹ˆà¹„หม?" fullView: "มุมมà¸à¸‡à¹à¸šà¸šà¹€à¸•à¹‡à¸¡" quitFullView: "à¸à¸à¸à¸ˆà¸²à¸à¸¡à¸¸à¸¡à¸¡à¸à¸‡à¹à¸šà¸šà¹€à¸•à¹‡à¸¡" addDescription: "เพิ่มคำà¸à¸˜à¸´à¸šà¸²à¸¢" -userPagePinTip: "คุณสามารถà¹à¸ªà¸”งผลโน้ตย่à¸à¹„ด้ที่นี่โดยเลืà¸à¸ \"ปัà¸à¸«à¸¡à¸¸à¸”ที่โปรไฟล์\" จาà¸à¹€à¸¡à¸™à¸¹à¸‚à¸à¸‡à¹‚น้ตย่à¸à¹à¸•à¹ˆà¸¥à¸°à¸£à¸²à¸¢à¸à¸²à¸£à¸™à¸°" +userPagePinTip: "ปัà¸à¸«à¸¡à¸¸à¸”โน้ตให้à¹à¸ªà¸”งที่นี่ได้โดยเลืà¸à¸à¹€à¸¡à¸™à¸¹ “ปัà¸à¸«à¸¡à¸¸à¸”†ขà¸à¸‡à¹‚น้ตนั้นๆ" notSpecifiedMentionWarning: "โน้ตนี้มีà¸à¸²à¸£à¸à¸¥à¹ˆà¸²à¸§à¸–ึงผู้ใช้งานที่ไม่รวมà¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¸œà¸¹à¹‰à¸£à¸±à¸š" info: "เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸š" userInfo: "ข้à¸à¸¡à¸¹à¸¥à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" unknown: "ไม่ทราบสถานะ" onlineStatus: "สถานะà¸à¸à¸™à¹„ลน์" hideOnlineStatus: "ซ่à¸à¸™à¸ªà¸–านะà¸à¸à¸™à¹„ลน์" -hideOnlineStatusDescription: "à¸à¸²à¸£à¸‹à¹ˆà¸à¸™à¸ªà¸–านะà¸à¸à¸™à¹„ลน์ขà¸à¸‡à¸„ุณช่วยลดความสะดวà¸à¸‚à¸à¸‡à¸„ุณสมบัติบางà¸à¸¢à¹ˆà¸²à¸‡ เช่น à¸à¸²à¸£à¸„้นหา à¸à¹ˆà¸°à¸™à¸°" +hideOnlineStatusDescription: "à¸à¸²à¸£à¸‹à¹ˆà¸à¸™à¸ªà¸–านะà¸à¸à¸™à¹„ลน์à¸à¸²à¸ˆà¸—ำให้ฟังà¸à¹Œà¸Šà¸±à¸™à¸šà¸²à¸‡à¸à¸¢à¹ˆà¸²à¸‡ เช่น à¸à¸²à¸£à¸„้นหา สะดวà¸à¸™à¹‰à¸à¸¢à¸¥à¸‡" online: "à¸à¸à¸™à¹„ลน์" active: "ใช้งานà¸à¸¢à¸¹à¹ˆ" offline: "à¸à¸à¸Ÿà¹„ลน์" -notRecommended: "ไม่ใช้งาน" -botProtection: "à¸à¸²à¸£à¸›à¹‰à¸à¸‡à¸à¸±à¸™ Bot (or AI)" +notRecommended: "ไม่à¹à¸™à¸°à¸™à¸³" +botProtection: "à¸à¸²à¸£à¸›à¹‰à¸à¸‡à¸à¸±à¸™ Bot" instanceBlocking: "à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸—ี่ถูà¸à¸šà¸¥à¹‡à¸à¸" selectAccount: "เลืà¸à¸à¸šà¸±à¸à¸Šà¸µ" switchAccount: "สลับบัà¸à¸Šà¸µà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" @@ -820,19 +840,19 @@ popularPosts: "โพสต์ติดà¸à¸±à¸™à¸”ับ" shareWithNote: "à¹à¸šà¹ˆà¸‡à¸›à¸±à¸™à¸”้วยโน้ต" ads: "โฆษณา" expiration: "à¸à¸³à¸«à¸™à¸”เวลา" -startingperiod: "เริ่ม" -memo: "ข้à¸à¸„วรจำ" +startingperiod: "เริ่มเมื่à¸" +memo: "เมโม" priority: "ลำดับความสำคัà¸" high: "สูง" middle: "ปานà¸à¸¥à¸²à¸‡" low: "ต่ำ" -emailNotConfiguredWarning: "ไม่ได้ตั้งค่าที่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸™à¸°" +emailNotConfiguredWarning: "ยังไม่ได้ตั้งค่าที่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥" ratio: "à¸à¸±à¸•à¸£à¸²à¸ªà¹ˆà¸§à¸™" previewNoteText: "à¹à¸ªà¸”งตัวà¸à¸¢à¹ˆà¸²à¸‡" customCss: "CSS ที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡" -customCssWarn: "ควรใช้à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่านี้เฉพาะต่à¸à¹€à¸¡à¸·à¹ˆà¸à¸„ุณรู้ว่าà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่านี้ใช้ทำà¸à¸°à¹„ร à¸à¸²à¸£à¸›à¹‰à¸à¸™à¸„่าที่ไม่เหมาะสมà¸à¸²à¸ˆà¸—ำให้ไคลเà¸à¹‡à¸™à¸•à¹Œà¸«à¸¢à¸¸à¸”ทำงานตามปà¸à¸•à¸´à¹„ด้นะ" +customCssWarn: "ควรใช้à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่านี้เฉพาะต่à¸à¹€à¸¡à¸·à¹ˆà¸à¸„ุณรู้มันใช้ทำà¸à¸°à¹„ร à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าที่ไม่เหมาะสมà¸à¸²à¸ˆà¸—ำให้ไคลเà¸à¹‡à¸™à¸•à¹Œà¹„ม่สามารถใช้งานได้à¸à¸¢à¹ˆà¸²à¸‡à¸–ูà¸à¸•à¹‰à¸à¸‡" global: "ทั่วโลà¸" -squareAvatars: "à¹à¸ªà¸”งผลà¸à¸§à¸•à¸²à¸£à¸ªà¸µà¹ˆà¹€à¸«à¸¥à¸µà¹ˆà¸¢à¸¡" +squareAvatars: "à¹à¸ªà¸”งผลà¸à¸§à¸•à¸²à¸£à¹€à¸›à¹‡à¸™à¸ªà¸µà¹ˆà¹€à¸«à¸¥à¸µà¹ˆà¸¢à¸¡" sent: "ส่ง" received: "ได้รับà¹à¸¥à¹‰à¸§" searchResult: "ผลà¸à¸²à¸£à¸„้นหา" @@ -849,10 +869,10 @@ usernameInfo: "ชื่à¸à¸—ี่ระบุบัà¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุ aiChanMode: "โหมด Ai " devMode: "โหมดนัà¸à¸žà¸±à¸’นา" keepCw: "เà¸à¹‡à¸šà¸„ำเตืà¸à¸™à¹€à¸™à¸·à¹‰à¸à¸«à¸²" -pubSub: "บัà¸à¸Šà¸µà¸œà¸±à¸š/ย่à¸à¸¢" +pubSub: "บัà¸à¸Šà¸µ Pub/Sub" lastCommunication: "à¸à¸²à¸£à¸ªà¸·à¹ˆà¸à¸ªà¸²à¸£à¸„รั้งสุดท้ายล่าสุด" resolved: "คลี่คลายà¹à¸¥à¹‰à¸§" -unresolved: "รà¸à¸à¸²à¸£à¹€à¸‰à¸¥à¸¢" +unresolved: "ยังไม่ได้รับà¸à¸²à¸£à¹à¸à¹‰à¹„ข" breakFollow: "ลบผู้ติดตาม" breakFollowConfirm: "ลบผู้ติดตามนี้à¸à¸à¸à¸ˆà¸£à¸´à¸‡à¸«à¸£à¸?" itsOn: "เปิดใช้งาน" @@ -860,36 +880,38 @@ itsOff: "ปิดใช้งาน" on: "เปิด" off: "ปิด" emailRequiredForSignup: "จำเป็นต้à¸à¸‡à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸—ี่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸ªà¸³à¸«à¸£à¸±à¸šà¸à¸²à¸£à¸ªà¸¡à¸±à¸„ร" -unread: "ไม่ได้à¸à¹ˆà¸²à¸™" +unread: "ยังไม่ได้à¸à¹ˆà¸²à¸™" filter: "à¸à¸£à¸à¸‡" controlPanel: "à¹à¸œà¸‡à¸„วบคุม" manageAccounts: "จัดà¸à¸²à¸£à¸šà¸±à¸à¸Šà¸µ" -makeReactionsPublic: "ตั้งค่าประวัติปà¸à¸´à¸à¸´à¸£à¸´à¸¢à¸²à¸•à¹ˆà¸à¸ªà¸²à¸˜à¸²à¸£à¸“ะ" -makeReactionsPublicDescription: "à¸à¸²à¸£à¸—ำเช่นนี้จะทำให้รายà¸à¸²à¸£à¸›à¸à¸´à¸à¸´à¸£à¸´à¸¢à¸²à¸—ี่ผ่านมาขà¸à¸‡à¸„ุณจะปราà¸à¸à¸•à¹ˆà¸à¸ªà¸²à¸˜à¸²à¸£à¸“ะนะ" +makeReactionsPublic: "ตั้งค่าประวัติà¸à¸²à¸£à¸£à¸µà¹à¸à¸„ชั่นเป็นสาธารณะ" +makeReactionsPublicDescription: "à¸à¸²à¸£à¸—ำเช่นนี้จะทำให้รายà¸à¸²à¸£à¸£à¸µà¹à¸à¸„ชั่นขà¸à¸‡à¸„ุณที่ผ่านมาทั้งหมดปราà¸à¸à¸•à¹ˆà¸à¸ªà¸²à¸˜à¸²à¸£à¸“ะ" classic: "คลาสสิค" muteThread: "ปิดเสียงเธรด" -unmuteThread: "เปิดเสียงเธรด" +unmuteThread: "เลิà¸à¸›à¸´à¸”เสียงเธรด" +followingVisibility: "à¸à¸²à¸£à¸¡à¸à¸‡à¹€à¸«à¹‡à¸™à¸—ี่เราà¸à¸³à¸¥à¸±à¸‡à¸•à¸´à¸”ตาม" +followersVisibility: "à¸à¸²à¸£à¸¡à¸à¸‡à¹€à¸«à¹‡à¸™à¸œà¸¹à¹‰à¸—ี่à¸à¸³à¸¥à¸±à¸‡à¸•à¸´à¸”ตามเรา" continueThread: "ดูความต่à¸à¹€à¸™à¸·à¹ˆà¸à¸‡à¹€à¸˜à¸£à¸”" deleteAccountConfirm: "à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¸ˆà¸°à¸¥à¸šà¸šà¸±à¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณà¸à¸¢à¹ˆà¸²à¸‡à¸–าวรเลยนะ à¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸à¸”ำเนินà¸à¸²à¸£?" incorrectPassword: "รหัสผ่านไม่ถูà¸à¸•à¹‰à¸à¸‡" -voteConfirm: "ยืนยันà¸à¸²à¸£à¹‚หวต \"{choice}\" มั้ย?" +voteConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹‚หวต “{choice}†ใช่ไหม?" hide: "ซ่à¸à¸™" -useDrawerReactionPickerForMobile: "à¹à¸ªà¸”งผล ตัวเลืà¸à¸à¸›à¸à¸´à¸à¸´à¸£à¸´à¸¢à¸²à¹€à¸›à¹‡à¸™à¸¥à¸´à¹‰à¸™à¸Šà¸±à¸à¸šà¸™à¸¡à¸·à¸à¸–ืà¸" -welcomeBackWithName: "ยินดีต้à¸à¸™à¸£à¸±à¸šà¸à¸²à¸£à¸à¸¥à¸±à¸šà¸¡à¸²à¸™à¸°à¸„ะ, {name}" -clickToFinishEmailVerification: "à¸à¸£à¸¸à¸“าคลิภ[{ok}] เพื่à¸à¸”ำเนินà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸à¸µà¹€à¸¡à¸¥à¹ƒà¸«à¹‰à¹€à¸ªà¸£à¹‡à¸ˆà¸ªà¸¡à¸šà¸¹à¸£à¸“์นะ" +useDrawerReactionPickerForMobile: "à¹à¸ªà¸”ง ตัวจิ้มรีà¹à¸à¸„ชั่น เป็นà¹à¸šà¸šà¸¥à¸´à¹‰à¸™à¸Šà¸±à¸ เมื่à¸à¹ƒà¸Šà¹‰à¸šà¸™à¸¡à¸·à¸à¸–ืà¸" +welcomeBackWithName: "ยินดีต้à¸à¸™à¸£à¸±à¸šà¸à¸²à¸£à¸à¸¥à¸±à¸šà¸¡à¸²à¸™à¸°à¸„ะ, คุณ{name}" +clickToFinishEmailVerification: "à¸à¸£à¸¸à¸“าคลิภ[{ok}] เพื่à¸à¸”ำเนินà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸à¸µà¹€à¸¡à¸¥à¹ƒà¸«à¹‰à¹€à¸ªà¸£à¹‡à¸ˆà¸ªà¸¡à¸šà¸¹à¸£à¸“์" overridedDeviceKind: "ประเภทà¸à¸¸à¸›à¸à¸£à¸“์" smartphone: "สมาร์ทโฟน" tablet: "à¹à¸—็บเล็ต" auto: "à¸à¸±à¸•à¹‚นมัติ" -themeColor: "à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ Ticker Color" +themeColor: "สีธีม" size: "ขนาด" numberOfColumn: "จำนวนคà¸à¸¥à¸±à¸¡à¸™à¹Œ" searchByGoogle: "ค้นหา" -instanceDefaultLightTheme: "ธีมสว่างค่าเริ่มต้นสำหรับà¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ" -instanceDefaultDarkTheme: "ธีมมืดค่าเริ่มต้นà¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ" +instanceDefaultLightTheme: "ธีมสว่างตามค่าเริ่มต้นขà¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ" +instanceDefaultDarkTheme: "ธีมมืดตามค่าเริ่มต้นขà¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ" instanceDefaultThemeDescription: "ป้à¸à¸™à¸£à¸«à¸±à¸ªà¸˜à¸µà¸¡à¹ƒà¸™à¸£à¸¹à¸›à¹à¸šà¸šà¸à¸à¸šà¹€à¸ˆà¹‡à¸à¸•à¹Œ" mutePeriod: "ระยะเวลาปิดเสียง" -period: "สิ้นสุดà¸à¸²à¸£à¸ªà¸³à¸£à¸§à¸ˆà¸„วามคิดเห็น" +period: "ระยะเวลา" indefinitely: "ตลà¸à¸”ไป" tenMinutes: "10 นาที" oneHour: "1 ชั่วโมง" @@ -919,28 +941,28 @@ deleteAccount: "ลบบัà¸à¸Šà¸µ" document: "เà¸à¸à¸ªà¸²à¸£" numberOfPageCache: "จำนวนหน้าเพจที่à¹à¸„ช" numberOfPageCacheDescription: "à¸à¸²à¸£à¹€à¸žà¸´à¹ˆà¸¡à¸ˆà¸³à¸™à¸§à¸™à¸™à¸µà¹‰à¸ˆà¸°à¸Šà¹ˆà¸§à¸¢à¹€à¸žà¸´à¹ˆà¸¡à¸„วามสะดวà¸à¹ƒà¸«à¹‰à¸à¸±à¸šà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ à¹à¸•à¹ˆà¸ˆà¸°à¸—ำให้เซิร์ฟเวà¸à¸£à¹Œà¹‚หลดมาà¸à¸‚ึ้นà¹à¸¥à¸°à¸•à¹‰à¸à¸‡à¹ƒà¸Šà¹‰à¸«à¸™à¹ˆà¸§à¸¢à¸„วามจำมาà¸à¸‚ึ้นà¸à¸µà¸à¸”้วย" -logoutConfirm: "คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¸§à¹ˆà¸²à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸à¸à¸à¸ˆà¸²à¸à¸£à¸°à¸šà¸š?" -lastActiveDate: "ใช้งานล่าสุดที่" -statusbar: "ไà¸à¸„à¸à¸™à¸šà¸™à¹à¸–บสถานะ" +logoutConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸à¸à¸à¸ˆà¸²à¸à¸£à¸°à¸šà¸šà¹ƒà¸Šà¹ˆà¹„หม?" +lastActiveDate: "ใช้งานล่าสุดเมื่à¸" +statusbar: "à¹à¸–บสถานะ" pleaseSelect: "ตัวเลืà¸à¸" -reverse: "ย้à¸à¸™à¸à¸¥à¸±à¸š" +reverse: "พลิà¸" colored: "สี" -refreshInterval: "รà¸à¸šà¸à¸²à¸£à¸à¸±à¸žà¹€à¸”ต" +refreshInterval: "ความถี่ในà¸à¸²à¸£à¸à¸±à¸›à¹€à¸”ต" label: "ป้ายชื่à¸" type: "รูปà¹à¸šà¸š" speed: "ความเร็ว" slow: "ช้า" fast: "เร็ว" -sensitiveMediaDetection: "à¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ˆà¸±à¸šà¸‚à¸à¸‡à¸ªà¸·à¹ˆà¸ NSFW" +sensitiveMediaDetection: "à¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ˆà¸±à¸šà¸ªà¸·à¹ˆà¸à¸—ี่มีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" localOnly: "เฉพาะท้à¸à¸‡à¸–ิ่น" -remoteOnly: "รีโมทเท่านั้น" +remoteOnly: "ระยะไà¸à¸¥à¹€à¸—่านั้น" failedToUpload: "à¸à¸²à¸£à¸à¸±à¸›à¹‚หลดล้มเหลว" cannotUploadBecauseInappropriate: "ไม่สามารถà¸à¸±à¸›à¹‚หลดไฟล์นี้ได้เนื่à¸à¸‡à¸ˆà¸²à¸à¸£à¸°à¸šà¸šà¸•à¸£à¸§à¸ˆà¸žà¸šà¸šà¸²à¸‡à¸ªà¹ˆà¸§à¸™à¸‚à¸à¸‡à¹„ฟล์ว่านี้à¸à¸²à¸ˆà¸ˆà¸°à¹€à¸›à¹‡à¸™ NSFW" -cannotUploadBecauseNoFreeSpace: "à¸à¸²à¸£à¸à¸±à¸›à¹‚หลดนั้นล้มเหลวเนื่à¸à¸‡à¸ˆà¸²à¸à¹„ม่มีความจุขà¸à¸‡à¹„ดรฟ์" +cannotUploadBecauseNoFreeSpace: "ไม่สามารถà¸à¸±à¸›à¹‚หลดได้เนื่à¸à¸‡à¸ˆà¸²à¸à¹„ม่มีพื้นที่ว่างในไดรฟ์เหลืà¸à¹à¸¥à¹‰à¸§" cannotUploadBecauseExceedsFileSizeLimit: "ไม่สามารถà¸à¸±à¸›à¹‚หลดไฟล์นี้ได้à¹à¸¥à¹‰à¸§à¹€à¸™à¸·à¹ˆà¸à¸‡à¸ˆà¸²à¸à¹€à¸à¸´à¸™à¸‚ีดจำà¸à¸±à¸”ขà¸à¸‡à¸‚นาดไฟล์à¹à¸¥à¹‰à¸§" beta: "เบต้า" -enableAutoSensitive: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢ NSFW à¸à¸±à¸•à¹‚นมัติ" -enableAutoSensitiveDescription: "à¸à¸™à¸¸à¸à¸²à¸•à¹ƒà¸«à¹‰à¸•à¸£à¸§à¸ˆà¸«à¸²à¹à¸¥à¸°à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸ªà¸·à¹ˆà¸ NSFW โดยà¸à¸±à¸•à¹‚นมัติผ่านà¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸™à¸£à¸¹à¹‰à¸‚à¸à¸‡à¹€à¸„รื่à¸à¸‡à¸«à¸²à¸à¹€à¸›à¹‡à¸™à¹„ปได้ à¹à¸¡à¹‰à¸§à¹ˆà¸²à¸•à¸±à¸§à¹€à¸¥à¸·à¸à¸à¸™à¸µà¹‰à¸ˆà¸°à¸–ูà¸à¸›à¸´à¸”ใช้งาน à¹à¸•à¹ˆà¸à¹‡à¸ªà¸²à¸¡à¸²à¸£à¸–เปิดใช้งานได้ทั้งà¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸™à¸µà¹‰" +enableAutoSensitive: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸—ี่ละเà¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™à¹‚ดยà¸à¸±à¸•à¹‚นมัติ" +enableAutoSensitiveDescription: "à¸à¸™à¸¸à¸à¸²à¸•à¹ƒà¸«à¹‰à¸•à¸£à¸§à¸ˆà¸«à¸²à¹à¸¥à¸°à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸ªà¸·à¹ˆà¸à¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¹‚ดยละเà¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™à¹‚ดยà¸à¸±à¸•à¹‚นมัติ ผ่าน Machine Learning หาà¸à¹€à¸›à¹‡à¸™à¹„ปได้ à¹à¸¡à¹‰à¸§à¹ˆà¸²à¸„ุณจะปิดคุณสมบัตินี้ à¸à¹‡à¸à¸²à¸ˆà¸–ูà¸à¸•à¸±à¹‰à¸‡à¸„่าโดยà¸à¸±à¸•à¹‚นมัติ ทั้งนี้ขึ้นà¸à¸¢à¸¹à¹ˆà¸à¸±à¸šà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" activeEmailValidationDescription: "เปิดใช้งานà¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ªà¸à¸šà¸—ี่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¹ƒà¸«à¹‰à¸¡à¸µà¸„วามเข้มงวดยิ่งขึ้น ซึ่งà¸à¸²à¸ˆà¸ˆà¸°à¸£à¸§à¸¡à¹„ปถึงà¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ªà¸à¸šà¸—ี่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¹Œà¸—ี่ใช้à¹à¸¥à¹‰à¸§à¸—ิ้งà¹à¸¥à¸°à¹‚ดยให้พิจารณาว่าสามารถสื่à¸à¸ªà¸²à¸£à¸”้วยได้หรืà¸à¹„ม่ เมื่à¸à¹„ม่เลืà¸à¸à¸£à¸°à¸šà¸šà¸ˆà¸°à¸•à¸£à¸§à¸ˆà¸ªà¸à¸šà¹€à¸‰à¸žà¸²à¸°à¸£à¸¹à¸›à¹à¸šà¸šà¸‚à¸à¸‡à¸à¸µà¹€à¸¡à¸¥à¹€à¸—่านั้น" navbar: "à¹à¸–บนำทาง" shuffle: "สลับ" @@ -952,32 +974,33 @@ unsubscribePushNotification: "ปิดà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¹ pushNotificationAlreadySubscribed: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¹à¸šà¸šà¸žà¸¸à¸Šà¹„ด้เปิดใช้งานà¹à¸¥à¹‰à¸§" pushNotificationNotSupported: "เบราว์เซà¸à¸£à¹Œà¸«à¸£à¸·à¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸‚à¸à¸‡à¸„ุณนั้นไม่รà¸à¸‡à¸£à¸±à¸šà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¹à¸šà¸šà¸žà¸¸à¸Š" sendPushNotificationReadMessage: "ลบà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¹à¸šà¸šà¸žà¸¸à¸Šà¹€à¸¡à¸·à¹ˆà¸à¸à¹ˆà¸²à¸™à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸«à¸£à¸·à¸à¸‚้à¸à¸„วามที่เà¸à¸µà¹ˆà¸¢à¸§à¸‚้à¸à¸‡à¹à¸¥à¹‰à¸§" -sendPushNotificationReadMessageCaption: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸—ี่มีข้à¸à¸„วาม \"{emptyPushNotificationMessage}\" จะà¹à¸ªà¸”งขึ้นมาในช่วงระยะเวลาสั้นๆ à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¸à¸²à¸ˆà¸—ำให้เพิ่มà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¹à¸šà¸•à¹€à¸•à¸à¸£à¸µà¹ˆà¸‚à¸à¸‡à¸à¸¸à¸›à¸à¸£à¸“์ถ้าหาà¸à¸¡à¸µà¸™à¸°" -windowMaximize: "ขยายใหà¸à¹ˆà¸ªà¸¸à¸”à¹à¸¥à¹‰à¸§" +sendPushNotificationReadMessageCaption: "à¸à¸²à¸ˆà¸—ำให้à¸à¸¸à¸›à¸à¸£à¸“์ขà¸à¸‡à¸„ุณใช้พลังงานมาà¸à¸‚ึ้น" +windowMaximize: "ขยายใหà¸à¹ˆà¸ªà¸¸à¸”" windowMinimize: "ย่à¸à¹€à¸¥à¹‡à¸à¸—ี่สุด" windowRestore: "เลิà¸à¸—ำ" -caption: "รายละเà¸à¸µà¸¢à¸”" +caption: "คำà¸à¸˜à¸´à¸šà¸²à¸¢" loggedInAsBot: "ล็à¸à¸à¸à¸´à¸™à¹€à¸›à¹‡à¸™à¸šà¸à¸•à¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¸‚ณะนี้" tools: "เครื่à¸à¸‡à¸¡à¸·à¸" cannotLoad: "ไม่สามารถโหลดได้" numberOfProfileView: "มุมมà¸à¸‡à¹‚ปรไฟล์" -like: "ชื่นชà¸à¸š" -unlike: "ไม่ชà¸à¸š" -numberOfLikes: "จำนวนไลค์" +like: "ถูà¸à¹ƒà¸ˆ!" +unlike: "เลิà¸à¸–ูà¸à¹ƒà¸ˆ" +numberOfLikes: "จำนวนยà¸à¸”ถูà¸à¹ƒà¸ˆ" show: "à¹à¸ªà¸”งผล" neverShow: "ไม่ต้à¸à¸‡à¹à¸ªà¸”งข้à¸à¸„วามนี้à¸à¸µà¸" remindMeLater: "ไว้ครั้งหน้าà¹à¸¥à¹‰à¸§à¸à¸±à¸™" -didYouLikeMisskey: "คุณเคยชà¸à¸š Misskey ไหม?" +didYouLikeMisskey: "คุณชà¸à¸š Misskey ไหม?" pleaseDonate: "Misskey เป็นซà¸à¸Ÿà¸•à¹Œà¹à¸§à¸£à¹Œà¸Ÿà¸£à¸µà¸—ี่ใช้งานโดย {host} เราขà¸à¸‚à¸à¸šà¸„ุณà¸à¸²à¸£à¸ªà¸™à¸±à¸šà¸ªà¸™à¸¸à¸™à¸‚à¸à¸‡à¸„ุณà¸à¸¢à¹ˆà¸²à¸‡à¸ªà¸¹à¸‡à¹€à¸žà¸·à¹ˆà¸à¹ƒà¸«à¹‰à¸à¸²à¸£à¸žà¸±à¸’นา Misskey สามารถดำเนินต่à¸à¹„ปได้!" +correspondingSourceIsAvailable: "ซà¸à¸£à¹Œà¸ªà¹‚ค้ดที่เà¸à¸µà¹ˆà¸¢à¸§à¸‚้à¸à¸‡à¸¡à¸µà¸à¸¢à¸¹à¹ˆà¸—ี่ {anchor}" roles: "บทบาท" role: "บทบาท" noRole: "ไม่พบบทบาท" normalUser: "ผู้ใช้มาตรà¸à¸²à¸™" undefined: "ไม่ได้à¸à¸³à¸«à¸™à¸”" -assign: "à¸à¸³à¸«à¸™à¸”" -unassign: "ยังไม่มà¸à¸šà¸«à¸¡à¸²à¸¢" +assign: "มà¸à¸šà¸«à¸¡à¸²à¸¢" +unassign: "เลิà¸à¸¡à¸à¸šà¸«à¸¡à¸²à¸¢" color: "สี" -manageCustomEmojis: "จัดà¸à¸²à¸£à¸à¸µà¹‚มจิà¹à¸šà¸šà¸à¸³à¸«à¸™à¸”เà¸à¸‡" +manageCustomEmojis: "จัดà¸à¸²à¸£à¹€à¸à¹‚มจิที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡" manageAvatarDecorations: "จัดà¸à¸²à¸£à¸•à¸à¹à¸•à¹ˆà¸‡à¸à¸§à¸•à¸²à¸£" youCannotCreateAnymore: "คุณถึงขีดจà¹à¸²à¸à¸±à¸”à¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¹à¸¥à¹‰à¸§à¸™à¸°" cannotPerformTemporary: "ไม่สามารถใช้à¸à¸²à¸£à¹„ด้ชั่วคราว" @@ -992,33 +1015,38 @@ achievements: "ความสำเร็จ" gotInvalidResponseError: "à¸à¸²à¸£à¸•à¸à¸šà¸ªà¸™à¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¹„ม่ถูà¸à¸•à¹‰à¸à¸‡" gotInvalidResponseErrorDescription: "เซิร์ฟเวà¸à¸£à¹Œà¸à¸²à¸ˆà¹„ม่สามารถเข้าถึงได้หรืà¸à¸à¸²à¸ˆà¸ˆà¸°à¸à¸³à¸¥à¸±à¸‡à¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¸£à¸°à¸«à¸§à¹ˆà¸²à¸‡à¸›à¸£à¸±à¸šà¸›à¸£à¸¸à¸‡ à¸à¸£à¸¸à¸“าลà¸à¸‡à¹ƒà¸«à¸¡à¹ˆà¸à¸µà¸à¸„รั้งในภายหลังนะคะ" thisPostMayBeAnnoying: "โน้ตนี้à¸à¸²à¸ˆà¸ˆà¸°à¹€à¸›à¹‡à¸™à¸à¸²à¸£à¸£à¸šà¸à¸§à¸™à¸œà¸¹à¹‰à¸à¸·à¹ˆà¸™à¸™à¸°à¸„ะ" -thisPostMayBeAnnoyingHome: "โพสต์ไปยังบ้านไทม์ไลน์" +thisPostMayBeAnnoyingHome: "โพสต์ไปยังไทม์ไลน์หน้าà¹à¸£à¸" thisPostMayBeAnnoyingCancel: "เลิà¸" thisPostMayBeAnnoyingIgnore: "โพสต์ยังไงà¸à¹‡à¹à¸¥à¹‰à¸§à¹à¸•à¹ˆ" -collapseRenotes: "ยุบ renotes ที่คุณได้เห็นà¹à¸¥à¹‰à¸§" +collapseRenotes: "ยุบรีโน้ตที่คุณเคยเห็นà¹à¸¥à¹‰à¸§" internalServerError: "เซิร์ฟเวà¸à¸£à¹Œà¸ ายในเà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาด" internalServerErrorDescription: "เซิร์ฟเวà¸à¸£à¹Œà¸£à¸±à¸™à¸„้นพบข้à¸à¸œà¸´à¸”พลาดที่ไม่คาดคิด" copyErrorInfo: "คัดลà¸à¸à¸£à¸²à¸¢à¸¥à¸°à¹€à¸à¸µà¸¢à¸”ข้à¸à¸œà¸´à¸”พลาด" joinThisServer: "ลงชื่à¸à¸ªà¸¡à¸±à¸„รใช้ในà¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸™à¸µà¹‰" exploreOtherServers: "มà¸à¸‡à¸«à¸²à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸à¸·à¹ˆà¸™" -letsLookAtTimeline: "ลà¸à¸‡à¸”ูที่ไทม์ไลน์" +letsLookAtTimeline: "มาดูไทม์ไลน์à¸à¸±à¸™" disableFederationConfirm: "ปิดใช้งานสหพันธ์จริงๆหรà¸à¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¸™à¸°?" disableFederationConfirmWarn: "โพสต์จะยังคงเป็นสาธารณะต่à¸à¹„ป เว้นà¹à¸•à¹ˆà¸ˆà¸°à¸•à¸±à¹‰à¸‡à¸„่าเป็นà¸à¸¢à¹ˆà¸²à¸‡à¸à¸·à¹ˆà¸™" disableFederationOk: "ปิดà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" -invitationRequiredToRegister: "à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¹à¸šà¸šà¸£à¸±à¸šà¹€à¸Šà¸´à¸à¹€à¸—่านั้น คุณต้à¸à¸‡à¸›à¹‰à¸à¸™à¸£à¸«à¸±à¸ªà¹€à¸Šà¸´à¸ เพื่à¸à¸‡à¸¥à¸‡à¸—ะเบียนเข้าใช้งาน" +invitationRequiredToRegister: "à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¹à¸šà¸šà¸£à¸±à¸šà¹€à¸Šà¸´à¸ เฉพาะผู้ที่มีรหัสเชิà¸à¹€à¸—่านั้นที่สามารถลงทะเบียนได้" emailNotSupported: "à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸™à¸µà¹‰à¹„ม่รà¸à¸‡à¸£à¸±à¸šà¸à¸²à¸£à¸ªà¹ˆà¸‡à¸à¸µà¹€à¸¡à¸¥" postToTheChannel: "โพสต์ลงช่à¸à¸‡" cannotBeChangedLater: "สิ่งนี้ไม่สามารถเปลี่ยนà¹à¸›à¸¥à¸‡à¹„ด้ในภายหลังนะ" reactionAcceptance: "à¸à¸²à¸£à¸¢à¸à¸¡à¸£à¸±à¸šà¸£à¸µà¹à¸à¸„ชั่น" -likeOnly: "ที่ชà¸à¸šà¹€à¸—่านั้น" -likeOnlyForRemote: "ไลค์สำหรับà¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥à¹€à¸—่านั้น" -nonSensitiveOnly: "ไม่มีความà¸à¹ˆà¸à¸™à¹„หวเท่านั้น" -nonSensitiveOnlyForLocalLikeOnlyForRemote: "ไม่มีความà¸à¹ˆà¸à¸™à¹„หวเท่านั้น (เฉพาะไลค์จาà¸à¸£à¸°à¸¢à¸°à¹„à¸à¸¥à¹€à¸—่านั้น)" +likeOnly: "ที่ถูà¸à¹ƒà¸ˆà¹€à¸—่านั้น" +likeOnlyForRemote: "ทั้งหมด (เฉพาะà¸à¸²à¸£à¸–ูà¸à¹ƒà¸ˆà¸ˆà¸²à¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥)" +nonSensitiveOnly: "เฉพาะไม่มีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" +nonSensitiveOnlyForLocalLikeOnlyForRemote: "เฉพาะไม่มีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™ (เฉพาะà¸à¸²à¸£à¸–ูà¸à¹ƒà¸ˆà¸ˆà¸²à¸à¸£à¸°à¸¢à¸°à¹„à¸à¸¥à¹€à¸—่านั้น)" rolesAssignedToMe: "บทบาทที่ได้รับมà¸à¸šà¸«à¸¡à¸²à¸¢à¹ƒà¸«à¹‰à¸‰à¸±à¸™" resetPasswordConfirm: "รีเซ็ตรหัสผ่านขà¸à¸‡à¸„ุณจริงๆหรà¸?" -sensitiveWords: "คำที่ละเà¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" +sensitiveWords: "คำที่มีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" sensitiveWordsDescription: "à¸à¸²à¸£à¹€à¸›à¸´à¸”เผยโน้ตทั้งหมดที่มีคำที่à¸à¸³à¸«à¸™à¸”ค่าไว้จะถูà¸à¸•à¸±à¹‰à¸‡à¸„่าเป็น \"หน้าà¹à¸£à¸\" โดยà¸à¸±à¸•à¹‚นมัติ คุณยังสามารถà¹à¸ªà¸”งหลายรายà¸à¸²à¸£à¹„ด้โดยà¹à¸¢à¸à¸£à¸²à¸¢à¸à¸²à¸£à¹‚ดยใช้ตัวà¹à¸šà¹ˆà¸‡à¸šà¸£à¸£à¸—ัดได้นะ" sensitiveWordsDescription2: "à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸Šà¹ˆà¸à¸‡à¸§à¹ˆà¸²à¸‡à¸™à¸±à¹‰à¸™à¸à¸²à¸ˆà¸ˆà¸°à¸ªà¸£à¹‰à¸²à¸‡à¸™à¸´à¸žà¸ˆà¸™à¹Œ AND à¹à¸¥à¸°à¸„ำหลัà¸à¸—ี่มีเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸—ับล้à¸à¸¡à¸£à¸à¸šà¸ˆà¸°à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹€à¸›à¹‡à¸™à¸™à¸´à¸žà¸ˆà¸™à¹Œà¸—ั่วไปนะ" +prohibitedWords: "คำต้à¸à¸‡à¸«à¹‰à¸²à¸¡" +prohibitedWordsDescription: "จะà¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸§à¹ˆà¸²à¹€à¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดเมื่à¸à¸žà¸¢à¸²à¸¢à¸²à¸¡à¹‚พสต์โน้ตที่มีคำที่à¸à¸³à¸«à¸™à¸”ไว้ สามารถตั้งได้หลายคำด้วยà¸à¸²à¸£à¸‚ึ้นบรรทัดใหม่" +prohibitedWordsDescription2: "à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸Šà¹ˆà¸à¸‡à¸§à¹ˆà¸²à¸‡à¸™à¸±à¹‰à¸™à¸à¸²à¸ˆà¸ˆà¸°à¸ªà¸£à¹‰à¸²à¸‡à¸™à¸´à¸žà¸ˆà¸™à¹Œ AND à¹à¸¥à¸°à¸„ำหลัà¸à¸—ี่มีเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸—ับล้à¸à¸¡à¸£à¸à¸šà¸ˆà¸°à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹€à¸›à¹‡à¸™à¸™à¸´à¸žà¸ˆà¸™à¹Œà¸—ั่วไปนะ" +hiddenTags: "à¹à¸®à¸Šà¹à¸—็à¸à¸—ี่ซ่à¸à¸™à¸à¸¢à¸¹à¹ˆ" +hiddenTagsDescription: "เลืà¸à¸à¹à¸—็à¸à¸—ี่จะไม่à¹à¸ªà¸”งในรายà¸à¸²à¸£à¹€à¸—รนด์ สามารถลงทะเบียนหลายà¹à¸—็à¸à¹„ด้โดยขึ้นบรรทัดใหม่" notesSearchNotAvailable: "à¸à¸²à¸£à¸„้นหาโน้ตไม่พร้à¸à¸¡à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" license: "ใบà¸à¸™à¸¸à¸à¸²à¸•" unfavoriteConfirm: "ลบà¸à¸à¸à¸ˆà¸²à¸à¸£à¸²à¸¢à¸à¸²à¸£à¹‚ปรดà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸?" @@ -1029,21 +1057,24 @@ retryAllQueuesConfirmTitle: "ลà¸à¸‡à¹ƒà¸«à¸¡à¹ˆà¸—ั้งหมดจริ retryAllQueuesConfirmText: "สิ่งนี้จะเพิ่มà¸à¸²à¸£à¹‚หลดเซิร์ฟเวà¸à¸£à¹Œà¸Šà¸±à¹ˆà¸§à¸„ราวนะ" enableChartsForRemoteUser: "สร้างà¹à¸œà¸™à¸ ูมิข้à¸à¸¡à¸¹à¸¥à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸£à¸°à¸¢à¸°à¹„à¸à¸¥" enableChartsForFederatedInstances: "สร้างà¹à¸œà¸™à¸ ูมิข้à¸à¸¡à¸¹à¸¥à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥" -showClipButtonInNoteFooter: "เพิ่ม \"คลิป\" เพื่à¸à¸šà¸±à¸™à¸—ึà¸à¹€à¸¡à¸™à¸¹à¸à¸²à¸£à¸—ำงาน" -reactionsDisplaySize: "รีà¹à¸à¸„ชั่นà¹à¸ªà¸”งผลขนาด" -noteIdOrUrl: "โน้ต ID หรืภURL" +showClipButtonInNoteFooter: "เพิ่ม “คลิป†ไปยังเมนูสั่งà¸à¸²à¸£à¸‚à¸à¸‡à¹‚น้ต" +reactionsDisplaySize: "ขนาดขà¸à¸‡à¸£à¸µà¹à¸à¸„ชั่น" +limitWidthOfReaction: "จำà¸à¸±à¸”ความà¸à¸§à¹‰à¸²à¸‡à¸ªà¸¹à¸‡à¸ªà¸¸à¸”ขà¸à¸‡à¸£à¸µà¹à¸à¸„ชั่นà¹à¸¥à¸°à¹à¸ªà¸”งให้เล็à¸à¸¥à¸‡" +noteIdOrUrl: "ID ขà¸à¸‡à¹‚น้ต หรืภURL" video: "วีดีโà¸" videos: "วีดีโà¸" +audio: "เสียง" +audioFiles: "เสียง" dataSaver: "ประหยัดข้à¸à¸¡à¸¹à¸¥" -accountMigration: "à¸à¸²à¸£à¹‚ยà¸à¸¢à¹‰à¸²à¸¢à¸šà¸±à¸à¸Šà¸µ" +accountMigration: "โยà¸à¸¢à¹‰à¸²à¸¢à¸šà¸±à¸à¸Šà¸µ" accountMoved: "ผู้ใช้รายนี้ได้ย้ายไปยังบัà¸à¸Šà¸µà¹ƒà¸«à¸¡à¹ˆà¹à¸¥à¹‰à¸§:" accountMovedShort: "บัà¸à¸Šà¸µà¸™à¸µà¹‰à¸–ูà¸à¹‚à¸à¸™à¸¢à¹‰à¸²à¸¢à¹„ปà¹à¸¥à¹‰à¸§à¸„่ะ" -operationForbidden: "ห้ามดำเนินà¸à¸²à¸£" +operationForbidden: "à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸–ูà¸à¸«à¹‰à¸²à¸¡" forceShowAds: "à¹à¸ªà¸”งโฆษณาเสมà¸" -addMemo: "เพิ่มมีโม" -editMemo: "à¹à¸à¹‰à¹„ขมีโม" -reactionsList: "ปà¸à¸´à¸à¸´à¸£à¸´à¸¢à¸²" -renotesList: "Renotes รีโน้ต" +addMemo: "เพิ่มเมโม" +editMemo: "à¹à¸à¹‰à¹„ขเมโม" +reactionsList: "รายà¸à¸²à¸£à¸£à¸µà¹à¸à¸„ชั่น" +renotesList: "รายà¸à¸²à¸£à¸£à¸µà¹‚น้ต" notificationDisplay: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™" leftTop: "บนซ้าย" rightTop: "บนขวา" @@ -1051,96 +1082,112 @@ leftBottom: "ล่างซ้าย" rightBottom: "ล่างขวา" stackAxis: "ทิศทางà¸à¸²à¸£à¸‹à¹‰à¸à¸™" vertical: "à¹à¸™à¸§à¸•à¸±à¹‰à¸‡" -horizontal: "ด้านข้าง" +horizontal: "à¹à¸™à¸§à¸™à¸à¸™" position: "ตำà¹à¸«à¸™à¹ˆà¸‡" -serverRules: "à¸à¸Žà¸‚à¸à¸‡à¹€à¸‹à¸´à¸Ÿà¹€à¸§à¸à¸£à¹Œ" +serverRules: "à¸à¸Žà¸‚à¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" pleaseConfirmBelowBeforeSignup: "โปรดยืนยันที่ด้านล่างà¸à¹ˆà¸à¸™à¸ªà¸¡à¸±à¸„รใช้งาน" pleaseAgreeAllToContinue: "คุณต้à¸à¸‡à¸¢à¸à¸¡à¸£à¸±à¸šà¸—ุà¸à¸Šà¹ˆà¸à¸‡à¸•à¸£à¸‡à¸”้านบนเพื่à¸à¸”ำเนินà¸à¸²à¸£à¸•à¹ˆà¸à¸„่ะ" continue: "ดำเนินà¸à¸²à¸£à¸•à¹ˆà¸" preservedUsernames: "ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่สงวนไว้" -preservedUsernamesDescription: "ลิสต์ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่จะสำรà¸à¸‡à¹‚ดยคั่นด้วยà¸à¸²à¸£à¹à¸šà¹ˆà¸‡à¸šà¸£à¸£à¸—ัดนั้น เพราะสิ่งเหล่านี้จะไม่สามารถทำได้ในระหว่างà¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¸šà¸±à¸à¸Šà¸µà¸•à¸²à¸¡à¸›à¸à¸•à¸´ บัà¸à¸Šà¸µà¸—ี่มีà¸à¸¢à¸¹à¹ˆà¹à¸¥à¹‰à¸§à¸™à¸±à¹‰à¸™à¹‚ดยใช้ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¹€à¸«à¸¥à¹ˆà¸²à¸™à¸µà¹‰à¸ˆà¸°à¹„ม่ได้รับผลà¸à¸£à¸°à¸—บà¸à¸°à¹„ร" +preservedUsernamesDescription: "ระบุชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่จะสงวนชื่à¸à¹„ว้ คั่นด้วยà¸à¸²à¸£à¸‚ึ้นบรรทัดใหม่ ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่ระบุที่นี่จะไม่สามารถใช้งานได้à¸à¸µà¸à¸•à¹ˆà¸à¹„ปเมื่à¸à¸ªà¸£à¹‰à¸²à¸‡à¸šà¸±à¸à¸Šà¸µà¹ƒà¸«à¸¡à¹ˆ ยà¸à¹€à¸§à¹‰à¸™à¹€à¸¡à¸·à¹ˆà¸à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¸£à¸°à¸šà¸šà¸ªà¸£à¹‰à¸²à¸‡à¸šà¸±à¸à¸Šà¸µ นà¸à¸à¸ˆà¸²à¸à¸™à¸µà¹‰ บัà¸à¸Šà¸µà¸—ี่มีà¸à¸¢à¸¹à¹ˆà¹à¸¥à¹‰à¸§à¸ˆà¸°à¹„ม่ได้รับผลà¸à¸£à¸°à¸—บ" createNoteFromTheFile: "เรียบเรียงโน้ตจาà¸à¹„ฟล์นี้" archive: "เà¸à¹‡à¸šà¸–าวร" -channelArchiveConfirmTitle: "เà¸à¹‡à¸šà¸–าวรจริงๆ {name} มั้ย?" -channelArchiveConfirmDescription: "ช่à¸à¸‡à¸—ี่ถูà¸à¹€à¸à¹‡à¸šà¸–าวรà¹à¸¥à¹‰à¸§à¸™à¸±à¹‰à¸™à¸ˆà¸°à¹„ม่ปราà¸à¸à¹ƒà¸™à¸£à¸²à¸¢à¸à¸²à¸£à¸Šà¹ˆà¸à¸‡à¸«à¸£à¸·à¸à¸œà¸¥à¸à¸²à¸£à¸„้นหานั้นà¸à¸µà¸à¸•à¹ˆà¸à¹„ปไม่สามารถเพิ่มโพสต์ใหม่ได้à¸à¸µà¸à¸•à¹ˆà¸à¹„ปนะ" +channelArchiveConfirmTitle: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸à¹‡à¸šà¸–าวรเจ้า {name} ใช่ไหม?" +channelArchiveConfirmDescription: "เมื่à¸à¹€à¸à¹‡à¸šà¸–าวรà¹à¸¥à¹‰à¸§ จะไม่ปราà¸à¸à¹ƒà¸™à¸£à¸²à¸¢à¸à¸²à¸£à¸Šà¹ˆà¸à¸‡à¸«à¸£à¸·à¸à¸œà¸¥à¸à¸²à¸£à¸„้นหาà¸à¸µà¸à¸•à¹ˆà¸à¹„ป à¹à¸¥à¸°à¸ˆà¸°à¹„ม่สามารถโพสต์ใหม่ได้à¸à¸µà¸à¸•à¹ˆà¸à¹„ป" thisChannelArchived: "ช่à¸à¸‡à¸™à¸µà¹‰à¸–ูà¸à¹€à¸à¹‡à¸šà¸–าวรà¹à¸¥à¹‰à¸§à¸™à¸°" displayOfNote: "à¸à¸²à¸£à¹à¸ªà¸”งโน้ต" initialAccountSetting: "ตั้งค่าโปรไฟล์" youFollowing: "ติดตามà¹à¸¥à¹‰à¸§" -preventAiLearning: "ปà¸à¸´à¹€à¸ªà¸˜à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ ในà¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸™à¸£à¸¹à¹‰à¸‚à¸à¸‡à¹€à¸„รื่à¸à¸‡ (Generative AI)" -preventAiLearningDescription: "à¸à¸²à¸£à¸ªà¹ˆà¸‡à¸„ำร้à¸à¸‡à¸‚à¸à¹‚ปรà¹à¸à¸£à¸¡à¸£à¸§à¸šà¸£à¸§à¸¡à¸‚้à¸à¸¡à¸¹à¸¥à¹„ม่ให้ใช้ข้à¸à¸„วามที่โพสต์หรืà¸à¸£à¸¹à¸›à¸ าพ ฯลฯ ในชุดข้à¸à¸¡à¸¹à¸¥à¹à¸¡à¸Šà¸Šà¸µà¸™à¹€à¸¥à¸´à¸£à¹Œà¸™à¸™à¸´à¸‡ (Predictive / Generative AI) สิ่งนี้นั้นทำได้โดยà¸à¸²à¸£à¹€à¸žà¸´à¹ˆà¸¡à¹à¸Ÿà¸¥à¹‡à¸à¸à¸²à¸£à¸•à¸à¸šà¸ªà¸™à¸à¸‡ \"noai\" HTML ให้à¸à¸±à¸šà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸—ี่เà¸à¸µà¹ˆà¸¢à¸§à¸‚้à¸à¸‡ à¹à¸•à¹ˆà¸à¸¢à¹ˆà¸²à¸‡à¹„รà¸à¹‡à¸•à¸²à¸¡à¹à¸¥à¹‰à¸§ à¸à¸²à¸£à¸›à¹‰à¸à¸‡à¸à¸±à¸™à¹‚ดยสมบูรณ์นั้นไม่สามารถทำได้ผ่านà¹à¸Ÿà¸¥à¹‡à¸à¸™à¸µà¹‰à¹€à¸™à¸·à¹ˆà¸à¸‡à¸ˆà¸²à¸à¸à¸²à¸ˆà¸ˆà¸°à¸—ำให้ถูà¸à¹€à¸žà¸´à¸à¹€à¸‰à¸¢à¹„ด้" +preventAiLearning: "ปà¸à¸´à¹€à¸ªà¸˜à¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸™à¸£à¸¹à¹‰à¸”้วย generative AI" +preventAiLearningDescription: "ส่งคำร้à¸à¸‡à¸‚à¸à¹„ม่ให้ใช้ ข้à¸à¸„วามในโน้ตที่โพสต์, หรืà¸à¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸£à¸¹à¸›à¸ าพ ฯลฯ ในà¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸™à¸£à¸¹à¹‰à¸‚à¸à¸‡à¹€à¸„รื่à¸à¸‡(machine learning) / Predictive AI / Generative AI โดยà¸à¸²à¸£à¹€à¸žà¸´à¹ˆà¸¡à¹à¸Ÿà¸¥à¹‡à¸ “noai†ลง HTML-Response ให้à¸à¸±à¸šà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸—ี่เà¸à¸µà¹ˆà¸¢à¸§à¸‚้à¸à¸‡ à¹à¸•à¹ˆà¸—ั้งนี้ ไม่ได้ป้à¸à¸‡à¸à¸±à¸™ AI จาà¸à¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸™à¸£à¸¹à¹‰à¹„ด้à¸à¸¢à¹ˆà¸²à¸‡à¸ªà¸¡à¸šà¸¹à¸£à¸“์ เนื่à¸à¸‡à¸ˆà¸²à¸à¸¡à¸µ AI บางตัวเท่านั้นที่จะเคารพคำขà¸à¸”ังà¸à¸¥à¹ˆà¸²à¸§" options: "ตัวเลืà¸à¸à¸šà¸—บาท" specifyUser: "ผู้ใช้เฉพาะ" failedToPreviewUrl: "ไม่สามารถดูตัวà¸à¸¢à¹ˆà¸²à¸‡à¹„ด้" update: "à¸à¸±à¸›à¹€à¸”ต" -rolesThatCanBeUsedThisEmojiAsReaction: "บทบาทที่สามารถใช้à¸à¸´à¹‚มจินี้เป็นรีà¹à¸à¸„ชั่นได้" -rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "ถ้าหาà¸à¹„ม่ได้ระบุบทบาท ทุà¸à¸„นนั้นà¸à¹‡à¸ªà¸²à¸¡à¸²à¸£à¸–ใช้à¸à¸´à¹‚มจินี้เป็นà¸à¸²à¸£à¹à¸ªà¸”งความรู้สึà¸à¹„ด้นะ" +rolesThatCanBeUsedThisEmojiAsReaction: "บทบาทที่สามารถใช้เà¸à¹‚มจินี้เป็นรีà¹à¸à¸„ชั่นได้" +rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "ถ้าหาà¸à¹„ม่ได้ระบุบทบาท ใคร ๆ à¸à¹‡à¸ªà¸²à¸¡à¸²à¸£à¸–ใช้เà¸à¹‚มจินี้เพื่à¸à¸£à¸µà¹à¸à¸„ชั่นได้" rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "บทบาทเหล่านี้ต้à¸à¸‡à¹€à¸›à¹‡à¸™à¸ªà¸²à¸˜à¸²à¸£à¸“ะ" -cancelReactionConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¸£à¸µà¹à¸à¸„ชั่นขà¸à¸‡à¸„ุณจริงๆหรà¸?" -changeReactionConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸£à¸µà¹à¸à¸„ชั่นขà¸à¸‡à¸„ุณจริงๆหรà¸?" +cancelReactionConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¸£à¸µà¹à¸à¸„ชั่นใช่ไหม?" +changeReactionConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸£à¸µà¹à¸à¸„ชั่นใช่ไหม?" later: "ไว้ทีหลัง" goToMisskey: "ถึง Misskey" -additionalEmojiDictionary: "พจนานุà¸à¸£à¸¡à¸à¸µà¹‚มจิเพิ่มเติม" +additionalEmojiDictionary: "พจนานุà¸à¸£à¸¡à¹€à¸à¹‚มจิเพิ่มเติม" installed: "ติดตั้งà¹à¸¥à¹‰à¸§" branding: "à¹à¸šà¸£à¸™à¸”ิ้ง" enableServerMachineStats: "เผยà¹à¸žà¸£à¹ˆà¸ªà¸–านะฮาร์ดà¹à¸§à¸£à¹Œà¸‚à¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" enableIdenticonGeneration: "เปิดใช้งานผู้ใช้สร้างตัวระบุ" turnOffToImprovePerformance: "à¸à¸²à¸£à¸›à¸´à¸”ส่วนนี้สามารถเพิ่มประสิทธิภาพได้" -createInviteCode: "สร้างคำเชิà¸" +createInviteCode: "สร้างรหัสเชิà¸" createWithOptions: "สร้างด้วยตัวเลืà¸à¸" -createCount: "จำนวนà¸à¸²à¸£à¹€à¸Šà¸´à¸" -inviteCodeCreated: "สร้างคำเชิà¸à¹à¸¥à¹‰à¸§" -inviteLimitExceeded: "คุณสร้างคำเชิà¸à¹€à¸à¸´à¸™à¸–ึงขีดจำà¸à¸±à¸”à¹à¸¥à¹‰à¸§à¸™à¸°" -createLimitRemaining: "ขีดจำà¸à¸±à¸”à¸à¸²à¸£à¹€à¸Šà¸´à¸: {limit} ที่เหลืà¸à¸à¸¢à¸¹à¹ˆ" -inviteLimitResetCycle: "ขีดจำà¸à¸±à¸”นี้จะถูà¸à¸£à¸µà¹€à¸‹à¹‡à¸•à¹€à¸›à¹‡à¸™ {limit} ที่ {time}." +createCount: "จำนวนรหัสเชิà¸" +inviteCodeCreated: "สร้างรหัสเชิà¸à¹à¸¥à¹‰à¸§" +inviteLimitExceeded: "จำนวนรหัสเชิà¸à¸—ี่สามารถสร้างได้ถึงขีดจำà¸à¸±à¸”à¹à¸¥à¹‰à¸§" +createLimitRemaining: "รหัสเชิà¸à¸—ี่สามารถสร้างได้: เหลืà¸à¸à¸¢à¸¹à¹ˆ {limit} รหัส" +inviteLimitResetCycle: "สามารถสร้างรหัสเชิà¸à¹„ด้à¸à¸µà¸à¸ªà¸¹à¸‡à¸ªà¸¸à¸” {limit} รหัส ภายใน {time}" expirationDate: "วันที่หมดà¸à¸²à¸¢à¸¸" noExpirationDate: "ไม่มีหมดà¸à¸²à¸¢à¸¸" -inviteCodeUsedAt: "รหัสคำเชิà¸à¹ƒà¸Šà¹‰à¹à¸¥à¹‰à¸§à¸—ี่" -registeredUserUsingInviteCode: "ใช้คำเชิà¸à¹à¸¥à¹‰à¸§à¹‚ดย" +inviteCodeUsedAt: "วันเวลาที่ใช้รหัสเชิà¸" +registeredUserUsingInviteCode: "ผู้ใช้ที่ใช้รหัสเชิà¸" waitingForMailAuth: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸à¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸à¸µà¹€à¸¡à¸¥" -inviteCodeCreator: "สร้างà¸à¸²à¸£à¹€à¸Šà¸´à¸à¹à¸¥à¹‰à¸§à¹‚ดย" -usedAt: "ใช้à¹à¸¥à¹‰à¸§à¸—ี่" -unused: "ไม่ใช้à¹à¸¥à¹‰à¸§" -used: "ใช้à¹à¸¥à¹‰à¸§" +inviteCodeCreator: "ผู้ใช้ที่สร้างรหัสเชิà¸" +usedAt: "วันเวลาที่ถูà¸à¹ƒà¸Šà¹‰" +unused: "ยังไม่ได้ใช้" +used: "ถูà¸à¹ƒà¸Šà¹‰à¹à¸¥à¹‰à¸§" expired: "หมดà¸à¸²à¸¢à¸¸à¹à¸¥à¹‰à¸§" -doYouAgree: "ยà¸à¸¡à¸£à¸±à¸šà¸¡à¸±à¹‰à¸¢?" +doYouAgree: "ยà¸à¸¡à¸£à¸±à¸šà¹„หม?" beSureToReadThisAsItIsImportant: "à¸à¸£à¸¸à¸“าà¸à¹ˆà¸²à¸™à¸‚้à¸à¸¡à¸¹à¸¥à¸—ี่สำคัà¸à¸à¸±à¸™à¸™à¸µà¹‰" -iHaveReadXCarefullyAndAgree: "ฉันได้à¸à¹ˆà¸²à¸™à¸‚้à¸à¸„วาม \"{x}\" à¹à¸¥à¸°à¸¢à¸´à¸™à¸¢à¸à¸¡" +iHaveReadXCarefullyAndAgree: "ฉันได้à¸à¹ˆà¸²à¸™à¹à¸¥à¸°à¸¢à¸´à¸™à¸¢à¸à¸¡à¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸‚à¸à¸‡ “{x}â€" dialog: "ไดà¸à¸°à¸¥à¹‡à¸à¸" icon: "ไà¸à¸„à¸à¸™" forYou: "สำหรับคุณ" currentAnnouncements: "ประà¸à¸²à¸¨à¹ƒà¸™à¸›à¸±à¸ˆà¸ˆà¸¸à¸šà¸±à¸™" pastAnnouncements: "ประà¸à¸²à¸¨à¸—ี่ผ่านมา" youHaveUnreadAnnouncements: "มีà¸à¸²à¸£à¸›à¸£à¸°à¸à¸²à¸¨à¸—ี่ยังไม่ได้à¸à¹ˆà¸²à¸™" +useSecurityKey: "โปรดปà¸à¸´à¸šà¸±à¸•à¸´à¸•à¸²à¸¡à¸„ำà¹à¸™à¸°à¸™à¸³à¸‚à¸à¸‡à¹€à¸šà¸£à¸²à¸§à¹Œà¹€à¸‹à¸à¸£à¹Œà¸«à¸£à¸·à¸à¸à¸¸à¸›à¸à¸£à¸“์ขà¸à¸‡à¸„ุณเพื่à¸à¹ƒà¸Šà¹‰ security key หรืภpasskey" replies: "ตà¸à¸šà¸à¸¥à¸±à¸š" renotes: "รีโน้ต" loadReplies: "à¹à¸ªà¸”งà¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸š" loadConversation: "à¹à¸ªà¸”งบทสนทนา" -pinnedList: "รายà¸à¸²à¸£à¸—ี่ปัà¸à¸«à¸¡à¸¸à¸”ไว้à¹à¸¥à¹‰à¸§" -keepScreenOn: "เปิดหน้าจà¸à¹„ว้" +pinnedList: "รายชื่à¸à¸—ี่ปัà¸à¸«à¸¡à¸¸à¸”ไว้" +keepScreenOn: "เปิดหน้าจà¸à¸à¸¸à¸›à¸à¸£à¸“์ค้างไว้" +verifiedLink: "ความเป็นเจ้าขà¸à¸‡à¸¥à¸´à¸‡à¸à¹Œà¹„ด้รับà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¹à¸¥à¹‰à¸§" notifyNotes: "à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¹‚พสต์ใหม่" unnotifyNotes: "หยุดà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¹‚น้ตใหม่" authentication: "à¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ªà¸à¸šà¸ªà¸´à¸—ธิ์" -authenticationRequiredToContinue: "à¸à¸£à¸¸à¸“าตรวจสà¸à¸šà¸à¸²à¸£à¸£à¸±à¸šà¸£à¸à¸‡à¸„วามถูà¸à¸•à¹‰à¸à¸‡à¹€à¸žà¸·à¹ˆà¸à¸”ำเนินà¸à¸²à¸£à¸•à¹ˆà¸" +authenticationRequiredToContinue: "à¸à¸£à¸¸à¸“ายืนยันตัวตนทางà¸à¸´à¹€à¸¥à¹‡à¸à¸—รà¸à¸™à¸´à¸à¸ªà¹Œà¹€à¸žà¸·à¹ˆà¸à¸”ำเนินà¸à¸²à¸£à¸•à¹ˆà¸" dateAndTime: "เวลาประทับ" showRenotes: "à¹à¸ªà¸”งรีโน้ต" edited: "à¹à¸à¹‰à¹„ขà¹à¸¥à¹‰à¸§" notificationRecieveConfig: "à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™" mutualFollow: "ติดตามซึ่งà¸à¸±à¸™à¹à¸¥à¸°à¸à¸±à¸™" +followingOrFollower: "à¸à¸³à¸¥à¸±à¸‡à¸•à¸´à¸”ตามหรืà¸à¸œà¸¹à¹‰à¸•à¸´à¸”ตาม" fileAttachedOnly: "เฉพาะโน้ตที่มีไฟล์เท่านั้น" -showRepliesToOthersInTimeline: "à¹à¸ªà¸”งà¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¹„ปยังà¸à¸·à¹ˆà¸™à¹†à¹ƒà¸™à¹„ทม์ไลน์" -hideRepliesToOthersInTimeline: "ซ่à¸à¸™à¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¹„ปยังà¸à¸·à¹ˆà¸™à¹†à¸ˆà¸²à¸à¹„ทม์ไลน์" +showRepliesToOthersInTimeline: "à¹à¸ªà¸”งà¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¸œà¸¹à¹‰à¸à¸·à¹ˆà¸™à¸¥à¸‡à¹ƒà¸™à¹„ทม์ไลน์" +hideRepliesToOthersInTimeline: "ไม่à¹à¸ªà¸”งà¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¸œà¸¹à¹‰à¸à¸·à¹ˆà¸™à¸¥à¸‡à¹ƒà¸™à¹„ทม์ไลน์" +showRepliesToOthersInTimelineAll: "รวมตà¸à¸šà¸à¸¥à¸±à¸šà¸ˆà¸²à¸à¸—ุà¸à¸„นที่คุณติดตามไว้ในไทม์ไลน์ขà¸à¸‡à¸„ุณ" +hideRepliesToOthersInTimelineAll: "ซ่à¸à¸™à¸•à¸à¸šà¸à¸¥à¸±à¸šà¸ˆà¸²à¸à¸—ุà¸à¸„นที่คุณติดตามไปจาà¸à¹„ทม์ไลน์ขà¸à¸‡à¸„ุณ" +confirmShowRepliesAll: "à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¹„ม่สามารถย้à¸à¸™à¸à¸¥à¸±à¸šà¹„ด้ คุณต้à¸à¸‡à¸à¸²à¸£à¹à¸ªà¸”งà¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¸œà¸¹à¹‰à¸à¸·à¹ˆà¸™à¸ˆà¸²à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ุà¸à¸„นที่คุณติดตามà¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¹„ทม์ไลน์ขà¸à¸‡à¸„ุณหรืà¸à¹„ม่?" +confirmHideRepliesAll: "à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¹„ม่สามารถย้à¸à¸™à¸à¸¥à¸±à¸šà¹„ด้ คุณต้à¸à¸‡à¸à¸²à¸£à¸‹à¹ˆà¸à¸™à¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¸œà¸¹à¹‰à¸à¸·à¹ˆà¸™à¸ˆà¸²à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ุà¸à¸„นที่คุณติดตามà¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¹„ทม์ไลน์ขà¸à¸‡à¸„ุณหรืà¸à¹„ม่?" externalServices: "บริà¸à¸²à¸£à¸ ายนà¸à¸" +sourceCode: "ซà¸à¸£à¹Œà¸ªà¹‚ค้ด" +sourceCodeIsNotYetProvided: "ซà¸à¸£à¹Œà¸ªà¹‚ค้ดยังไม่พร้à¸à¸¡à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ โปรดติดต่à¸à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¸£à¸°à¸šà¸šà¸‚à¸à¸‡à¸„ุณเพื่à¸à¹à¸à¹‰à¹„ขปัà¸à¸«à¸²à¸™à¸µà¹‰" +repositoryUrl: "URL ขà¸à¸‡ repository" +repositoryUrlDescription: "หาà¸à¸¡à¸µà¸—ี่เà¸à¹‡à¸šà¸‹à¸à¸£à¹Œà¸ªà¹‚ค้ดที่เปิดเผยต่à¸à¸ªà¸²à¸˜à¸²à¸£à¸“ะ ให้ป้à¸à¸™ URL ที่เà¸à¹‡à¸šà¸‹à¸à¸£à¹Œà¸ªà¹‚ค้ดนั้น à¹à¸•à¹ˆà¸«à¸²à¸à¸„ุณใช้ Misskey ตามต้นฉบับ (ไม่มีà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸‹à¸à¸£à¹Œà¸ªà¹‚ค้ด) ให้ป้à¸à¸™ https://github.com/misskey-dev/misskey" +repositoryUrlOrTarballRequired: "หาà¸à¸„ุณไม่มี repository สาธารณะ คุณจะต้à¸à¸‡à¸ˆà¸±à¸”เตรียม tarball à¹à¸—น ดู .config/example.yml สำหรับรายละเà¸à¸µà¸¢à¸”" +feedback: "ฟีดà¹à¸šà¹‡à¸" +feedbackUrl: "URLขà¸à¸‡à¸Ÿà¸µà¸”à¹à¸šà¹‡à¸" impressum: "à¸à¸´à¸¡à¹€à¸žà¸£à¸ªà¸Šà¸±à¹ˆà¸™" impressumUrl: "URL à¸à¸´à¸¡à¹€à¸žà¸£à¸ªà¸Šà¸±à¹ˆà¸™" +impressumDescription: "à¸à¸²à¸£à¸•à¸´à¸”ป้ายà¸à¸³à¸à¸±à¸š (Impressum) มีผลบังคับใช้ในบางประเทศà¹à¸¥à¸°à¸ ูมิภาค เช่น ประเทศเยà¸à¸£à¸¡à¸™à¸µ" privacyPolicy: "นโยบายความเป็นส่วนตัว" privacyPolicyUrl: "URL นโยบายความเป็นส่วนตัว" tosAndPrivacyPolicy: "เงื่à¸à¸™à¹„ขในà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£à¹à¸¥à¸°à¸™à¹‚ยบายความเป็นส่วนตัว" avatarDecorations: "à¸à¸²à¸£à¸•à¸à¹à¸•à¹ˆà¸‡à¸à¸§à¸•à¸²à¸£" attach: "à¹à¸™à¸š" detach: "นำà¸à¸à¸" +detachAll: "เà¸à¸²à¸à¸à¸à¸—ั้งหมด" angle: "à¹à¸à¸‡à¹€à¸à¸´à¸¥" -flip: "ย้à¸à¸™à¸à¸¥à¸±à¸š" +flip: "พลิà¸" showAvatarDecorations: "à¹à¸ªà¸”งตà¸à¹à¸•à¹ˆà¸‡à¸à¸§à¸•à¸²à¸£" releaseToRefresh: "ปล่à¸à¸¢à¹€à¸žà¸·à¹ˆà¸à¸£à¸µà¹€à¸Ÿà¸£à¸Š" refreshing: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸µà¹€à¸Ÿà¸£à¸Š..." @@ -1148,17 +1195,62 @@ pullDownToRefresh: "ดึงลงเพื่à¸à¸£à¸µà¹€à¸Ÿà¸£à¸Š" disableStreamingTimeline: "ปิดใช้งานà¸à¸±à¸›à¹€à¸”ตไทม์ไลน์à¹à¸šà¸šà¹€à¸£à¸µà¸¢à¸¥à¹„ทม์" useGroupedNotifications: "à¹à¸ªà¸”งผลà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¹à¸šà¸šà¸à¸¥à¸¸à¹ˆà¸¡à¹à¸¥à¹‰à¸§" signupPendingError: "มีปัà¸à¸«à¸²à¹ƒà¸™à¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ªà¸à¸šà¸—ี่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸¥à¸´à¸‡à¸à¹Œà¸à¸²à¸ˆà¸«à¸¡à¸”à¸à¸²à¸¢à¸¸à¹à¸¥à¹‰à¸§" +cwNotationRequired: "หาà¸à¹€à¸›à¸´à¸”ใช้งาน “ซ่à¸à¸™à¹€à¸™à¸·à¹‰à¸à¸«à¸²â€ จะต้à¸à¸‡à¸£à¸°à¸šà¸¸à¸„ำà¸à¸˜à¸´à¸šà¸²à¸¢" doReaction: "เพิ่มรีà¹à¸à¸„ชั่น" +code: "โค้ด" +reloadRequiredToApplySettings: "จำเป็นต้à¸à¸‡à¸¡à¸µà¸à¸²à¸£à¹‚หลดซ้ำเพื่à¸à¹ƒà¸«à¹‰à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่ามีผล" +remainingN: "เหลืภ: {n}" +overwriteContentConfirm: "à¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸·à¸à¹„ม่ว่าต้à¸à¸‡à¸à¸²à¸£à¹€à¸‚ียนทับเนื้à¸à¸«à¸²à¸›à¸±à¸ˆà¸ˆà¸¸à¸šà¸±à¸™?" +seasonalScreenEffect: "เà¸à¸Ÿà¹€à¸Ÿà¸à¸•à¹Œà¸«à¸™à¹‰à¸²à¸ˆà¸à¸•à¸²à¸¡à¸¤à¸”ูà¸à¸²à¸¥" +decorate: "ตà¸à¹à¸•à¹ˆà¸‡" +addMfmFunction: "เพิ่มà¸à¸²à¸£à¸•à¸à¹à¸•à¹ˆà¸‡" +enableQuickAddMfmFunction: "à¹à¸ªà¸”งตัวจิ้มเลืà¸à¸ MFM ขั้นสูง" +bubbleGame: "เà¸à¸¡à¸šà¸±à¸šà¹€à¸šà¸´à¹‰à¸¥" +sfx: "เสียงเà¸à¸Ÿà¹€à¸Ÿà¹‡à¸à¸•à¹Œ" +soundWillBePlayed: "จะมีà¸à¸²à¸£à¹€à¸¥à¹ˆà¸™à¹€à¸à¸Ÿà¹€à¸Ÿà¸à¸•à¹Œà¹€à¸ªà¸µà¸¢à¸‡" +showReplay: "ดูรีเพลย์" +replay: "รีเพลย์" +replaying: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸µà¹€à¸žà¸¥à¸¢à¹Œ" +endReplay: "à¸à¸à¸à¸ˆà¸²à¸à¸£à¸µà¹€à¸žà¸¥à¸¢à¹Œ" +copyReplayData: "คัดลà¸à¸à¸‚้à¸à¸¡à¸¹à¸¥à¸£à¸µà¹€à¸žà¸¥à¸¢à¹Œ" +ranking: "à¸à¸±à¸™à¸”ับ" +lastNDays: "ล่าสุด {n} วันที่à¹à¸¥à¹‰à¸§" +backToTitle: "à¸à¸¥à¸±à¸šà¹„ปหน้าไตเติ้ล" +hemisphere: "พื้นที่ที่à¸à¸²à¸¨à¸±à¸¢à¸à¸¢à¸¹à¹ˆ" +withSensitive: "à¹à¸ªà¸”งโน้ตที่มีไฟล์เนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" +userSaysSomethingSensitive: "โพสต์ที่มีไฟล์เนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™à¸‚à¸à¸‡ {name}" +enableHorizontalSwipe: "ปัดเพื่à¸à¸ªà¸¥à¸±à¸šà¹à¸—็บ" +loading: "à¸à¸³à¸¥à¸±à¸‡à¹‚หลด" +surrender: "ยà¸à¸¡à¹à¸žà¹‰" +gameRetry: "เริ่มเà¸à¸¡à¹ƒà¸«à¸¡à¹ˆ" +_bubbleGame: + howToPlay: "วิธีเล่น" + hold: "หยุดชั่วคราว" + _score: + score: "คะà¹à¸™à¸™" + scoreYen: "จำนวนเงินที่ได้รับ" + highScore: "คะà¹à¸™à¸™à¸ªà¸¹à¸‡à¸ªà¸¸à¸”" + maxChain: "จำนวน chain สูงสุด" + yen: "{yen} เยน" + estimatedQty: "{qty} à¸à¸±à¸™" + scoreSweets: "โà¸à¸™à¸´à¸‡à¸´à¸£à¸´ {onigiriQtyWithUnit}" + _howToPlay: + section1: "ขยับตำà¹à¸«à¸™à¹ˆà¸‡à¹à¸¥à¸°à¸§à¸²à¸‡à¸§à¸±à¸•à¸–ุลงในà¸à¸¥à¹ˆà¸à¸‡" + section2: "เมื่à¸à¸§à¸±à¸•à¸–ุประเภทเดียวà¸à¸±à¸™à¸¡à¸²à¸£à¸§à¸¡à¸à¸±à¸™ พวà¸à¸¡à¸±à¸™à¸ˆà¸°à¸à¸¥à¸²à¸¢à¹€à¸›à¹‡à¸™à¸§à¸±à¸•à¸–ุใหม่à¹à¸¥à¸°à¸„ุณจะได้รับคะà¹à¸™à¸™" + section3: "หาà¸à¸§à¸±à¸•à¸–ุล้นà¸à¸à¸à¸¡à¸²à¸ˆà¸²à¸à¸à¸¥à¹ˆà¸à¸‡ เà¸à¸¡à¸à¹‡à¸ˆà¸°à¸ˆà¸šà¸¥à¸‡ ตั้งเป้าทำคะà¹à¸™à¸™à¹ƒà¸«à¹‰à¸ªà¸¹à¸‡à¸”้วยà¸à¸²à¸£à¸«à¸¥à¸à¸¡à¸§à¸±à¸•à¸–ุต่าง ๆ โดยไม่ทำให้ล้นà¸à¸¥à¹ˆà¸à¸‡!" _announcement: forExistingUsers: "ผู้ใช้งานที่มีà¸à¸¢à¸¹à¹ˆà¹€à¸—่านั้น" forExistingUsersDescription: "à¸à¸²à¸£à¸›à¸£à¸°à¸à¸²à¸¨à¸™à¸µà¹‰à¸ˆà¸°à¹à¸ªà¸”งต่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่มีà¸à¸¢à¸¹à¹ˆ ณ จุดที่เผยà¹à¸žà¸£à¹ˆà¸™à¸±à¹‰à¸™à¹†à¸–้าหาà¸à¹€à¸›à¸´à¸”ใช้งาน ถ้าหาà¸à¸›à¸´à¸”ใช้งานผู้ที่à¸à¸³à¸¥à¸±à¸‡à¸ªà¸¡à¸±à¸„รใหม่หลังจาà¸à¹‚พสต์à¹à¸¥à¹‰à¸§à¸™à¸±à¹‰à¸™à¸à¹‡à¸ˆà¸°à¹€à¸«à¹‡à¸™à¹€à¸Šà¹ˆà¸™à¸à¸±à¸™" - needConfirmationToRead: "จำเป็นต้à¸à¸‡à¸¢à¸·à¸™à¸¢à¸±à¸™à¹€à¸žà¸·à¹ˆà¸à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸šà¸à¸à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" - needConfirmationToReadDescription: "ข้à¸à¸„วามà¹à¸ˆà¹‰à¸‡à¹à¸¢à¸ ถ้าหาà¸à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¹€à¸žà¸·à¹ˆà¸à¸¢à¸·à¸™à¸¢à¸±à¸™à¸§à¹ˆà¸²à¸à¸³à¸¥à¸±à¸‡à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸›à¸£à¸°à¸à¸²à¸¨à¸™à¸µà¹‰à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§à¸ˆà¸°à¹à¸ªà¸”งขึ้นถ้าหาà¸à¹€à¸›à¸´à¸”ใช้งาน à¸à¸²à¸£à¸›à¸£à¸°à¸à¸²à¸¨à¸™à¸±à¹‰à¸™à¸ˆà¸°à¹„ม่รวมà¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¸Ÿà¸±à¸‡à¸à¹Œà¸Šà¸±à¹ˆà¸™à¸§à¹ˆà¸² \"ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸—ั้งหมดว่าà¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§\"" - end: "ประà¸à¸²à¸¨à¹€à¸à¹‡à¸šà¸–าวร" + needConfirmationToRead: "จำเป็นต้à¸à¸‡à¸¢à¸·à¸™à¸¢à¸±à¸™à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" + needConfirmationToReadDescription: "à¸à¸¥à¹ˆà¸à¸‡à¹‚ต้ตà¸à¸šà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸ˆà¸°à¸›à¸£à¸²à¸à¸à¸‚ึ้นเมื่à¸à¸ˆà¸°à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§ นà¸à¸à¸ˆà¸²à¸à¸™à¸µà¹‰à¸¢à¸±à¸‡à¸—ำให้ประà¸à¸²à¸¨à¸™à¸µà¹‰à¸¢à¸±à¸‡à¹„ม่ถูà¸à¸à¹ˆà¸²à¸™à¹€à¸¡à¸·à¹ˆà¸à¹ƒà¸Šà¹‰à¸Ÿà¸±à¸‡à¸à¹Œà¸Šà¸±à¹ˆà¸™ “ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸¯ ทั้งหมดว่าà¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§â€" + end: "เà¸à¹‡à¸šà¸›à¸£à¸°à¸à¸²à¸¨" tooManyActiveAnnouncementDescription: "à¸à¸²à¸£à¸¡à¸µà¸›à¸£à¸°à¸à¸²à¸¨à¸—ี่ใช้งานมาà¸à¹€à¸à¸´à¸™à¹„ปนั้นà¸à¸²à¸ˆà¸ˆà¸°à¸—ำให้ประสบà¸à¸²à¸£à¸“์ขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸™à¸±à¹‰à¸™à¸”ูà¹à¸¢à¹ˆà¸¥à¸‡ โปรดà¸à¸£à¸¸à¸“าพิจารณาà¸à¸²à¸£à¹€à¸à¹‡à¸šà¸›à¸£à¸°à¸à¸²à¸¨à¸—ี่ล้าสมัยด้วยนะค่ะ" - readConfirmTitle: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸šà¸à¸à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§à¹€à¸¥à¸¢à¸¡à¸±à¹‰à¸¢?" - readConfirmText: "à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¸ˆà¸°à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸‚à¸à¸‡ \"{title}\" บà¸à¸à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§à¸™à¸°" + readConfirmTitle: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§à¹€à¸¥à¸¢à¹„หม?" + readConfirmText: "จะทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹ƒà¸ªà¹ˆ “{title}†ว่าà¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" + shouldNotBeUsedToPresentPermanentInfo: "เราขà¸à¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¹ƒà¸Šà¹‰à¸›à¸£à¸°à¸à¸²à¸¨à¹€à¸žà¸·à¹ˆà¸à¹‚พสต์ข้à¸à¸¡à¸¹à¸¥à¹à¸šà¸š flow มาà¸à¸à¸§à¹ˆà¸²à¸‚้à¸à¸¡à¸¹à¸¥à¹à¸šà¸š stock เนื่à¸à¸‡à¸ˆà¸²à¸à¸¡à¸µà¹à¸™à¸§à¹‚น้มที่จะส่งผลเสียต่ภUX โดยเฉพาะสำหรับผู้ใช้ใหม่" + dialogAnnouncementUxWarn: "เราขà¸à¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¹ƒà¸Šà¹‰à¸”้วยความระมัดระวัง เนื่à¸à¸‡à¸ˆà¸²à¸à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¹à¸šà¸šà¸à¸¥à¹ˆà¸à¸‡à¹‚ต้ตà¸à¸šà¸•à¸±à¹‰à¸‡à¹à¸•à¹ˆ 2 รายà¸à¸²à¸£à¸‚ึ้นไปพร้à¸à¸¡à¸à¸±à¸™à¸à¸²à¸ˆà¸ªà¹ˆà¸‡à¸œà¸¥à¹€à¸ªà¸µà¸¢à¸•à¹ˆà¸ UX ได้à¸à¸¢à¹ˆà¸²à¸‡à¸¡à¸²à¸" silence: "ไม่มีà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™" + silenceDescription: "หาà¸à¹€à¸›à¸´à¸”ใช้งาน จะไม่มีà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸›à¸£à¸°à¸à¸²à¸¨à¸™à¸µà¹‰ à¹à¸¥à¸°à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸ˆà¸°à¹„ม่จำเป็นต้à¸à¸‡à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§" _initialAccountSetting: accountCreated: "คุณได้สร้างบัà¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณสำเร็จเรียบร้à¸à¸¢à¹à¸¥à¹‰à¸§!" letsStartAccountSetup: "สำหรับผู้เริ่มต้นมาตั้งค่าโปรไฟล์ขà¸à¸‡à¸„ุณà¸à¸±à¸™à¹€à¸–à¸à¸°" @@ -1170,7 +1262,8 @@ _initialAccountSetting: followUsers: "ลà¸à¸‡à¸•à¸´à¸”ตามผู้ใช้บางคนที่คุณà¸à¸²à¸ˆà¸ˆà¸°à¸ªà¸™à¹ƒà¸ˆà¹€à¸žà¸·à¹ˆà¸à¸ªà¸£à¹‰à¸²à¸‡à¹„ทม์ไลน์ขà¸à¸‡à¸„ุณสิ !" pushNotificationDescription: "à¸à¸³à¸¥à¸±à¸‡à¹€à¸›à¸´à¸”ใช้งานà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¹à¸šà¸šà¸žà¸¸à¸Šà¸ˆà¸°à¸Šà¹ˆà¸§à¸¢à¹ƒà¸«à¹‰à¸„ุณได้รับà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸ˆà¸²à¸ {name} โดยตรงบนà¸à¸¸à¸›à¸à¸£à¸“์ขà¸à¸‡à¸„ุณนะ" initialAccountSettingCompleted: "ตั้งค่าโปรไฟล์เสร็จสมบูรณ์à¹à¸¥à¹‰à¸§!" - haveFun: "ขà¸à¹ƒà¸«à¹‰à¸ªà¸™à¸¸à¸ {name}!" + haveFun: "ขà¸à¹ƒà¸«à¹‰à¸ªà¸™à¸¸à¸à¸à¸±à¸š {name}!" + youCanContinueTutorial: "คุณสามารถดำเนินà¸à¸²à¸£à¸•à¹ˆà¸à¸”้วยบทช่วยสà¸à¸™à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸§à¸´à¸˜à¸µà¹ƒà¸Šà¹‰ {name} (Misskey) หรืà¸à¸à¸à¸à¸ˆà¸²à¸à¸šà¸—ช่วยสà¸à¸™à¹à¸¥à¹‰à¸§à¹€à¸£à¸´à¹ˆà¸¡à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¹„ด้ทันที" startTutorial: "เริ่มà¸à¸²à¸£à¸à¸¶à¸à¸ªà¸à¸™" skipAreYouSure: "ต้à¸à¸‡à¸à¸²à¸£à¸‚้ามà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าโปรไฟล์จริงๆà¹à¸šà¸šà¸™à¸±à¹‰à¸™à¸«à¸£à¸?" laterAreYouSure: "ต้à¸à¸‡à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าโปรไฟล์ในภายหลังจริงๆà¸à¸¢à¹ˆà¸²à¸‡à¸‡à¸±à¹‰à¸™à¸«à¸£à¸?" @@ -1181,29 +1274,80 @@ _initialTutorial: skipAreYouSure: "ต้à¸à¸‡à¸à¸²à¸£à¸à¸à¸à¸ˆà¸²à¸à¸šà¸—ช่วยสà¸à¸™à¹ƒà¸Šà¹ˆà¹„หม?" _landing: title: "ยินดีต้à¸à¸™à¸£à¸±à¸šà¸ªà¸¹à¹ˆà¸šà¸—ช่วยสà¸à¸™" + description: "คุณสามารถตรวจสà¸à¸šà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¹à¸¥à¸°à¸Ÿà¸±à¸‡à¸à¹Œà¸Šà¸±à¹ˆà¸™à¸žà¸·à¹‰à¸™à¸à¸²à¸™à¸‚à¸à¸‡ Misskey ได้ที่นี่" _note: title: "โน้ตคืà¸à¸à¸°à¹„ร?" + description: "โพสต์ใน Misskey เรียà¸à¸§à¹ˆà¸² “โน้ต†ซึ่งจะจัดเรียงตามลำดับเวลาบนไทม์ไลน์à¹à¸¥à¸°à¸à¸±à¸›à¹€à¸”ตà¹à¸šà¸šà¹€à¸£à¸µà¸¢à¸¥à¹„ทม์" + reply: "คุณสามารถตà¸à¸šà¸à¸¥à¸±à¸šà¹„ด้ à¹à¸¥à¸°à¸„ุณยังสามารถตà¸à¸šà¸à¸¥à¸±à¸šà¹ƒà¸ªà¹ˆà¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¹€à¸žà¸·à¹ˆà¸à¸ªà¸™à¸—นาต่à¸à¹„ด้เสมืà¸à¸™à¸”ั่งเธรด" + renote: "คุณสามารถà¹à¸Šà¸£à¹Œà¹‚น้ตไปยังไทม์ไลน์ขà¸à¸‡à¸„ุณเà¸à¸‡ คุณยังสามารถเพิ่มข้à¸à¸„วามà¹à¸¥à¸°à¹€à¸„รื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸„ำพูดได้" + reaction: "คุณสามารถเพิ่มรีà¹à¸à¸„ชั่นได้ รายละเà¸à¸µà¸¢à¸”จะà¸à¸˜à¸´à¸šà¸²à¸¢à¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¸«à¸™à¹‰à¸²à¸–ัดไป" + menu: "คุณสามารถดูรายละเà¸à¸µà¸¢à¸”โน้ต คัดลà¸à¸à¸¥à¸´à¸‡à¸à¹Œ à¹à¸¥à¸°à¸”ำเนินà¸à¸²à¸£à¸à¸·à¹ˆà¸™à¹† ได้" _reaction: title: "รีà¹à¸à¸„ชั่นคืà¸à¸à¸°à¹„ร?" + description: "โน้ตสามารถ“รีà¹à¸à¸„ชั่นâ€à¸”้วยเà¸à¹‚มจิต่างๆ ซึ่งทำให้สามารถà¹à¸ªà¸”งความà¹à¸•à¸à¸•à¹ˆà¸²à¸‡à¹€à¸¥à¹‡à¸à¹† น้à¸à¸¢à¹† ที่à¸à¸²à¸ˆà¹„ม่สามารถสื่à¸à¸à¸à¸à¸¡à¸²à¹„ด้ด้วยà¸à¸²à¸£à¹à¸„่à¸à¸²à¸£à¸à¸” “ถูà¸à¹ƒà¸ˆâ€" + letsTryReacting: "คุณสามารถเพิ่มรีà¹à¸à¸„ชั่นได้ด้วยà¸à¸²à¸£à¸„ลิà¸à¸›à¸¸à¹ˆà¸¡ “+†บนโน้ต ลà¸à¸‡à¸£à¸µà¹à¸à¸„ชั่นโน้ตตัวà¸à¸¢à¹ˆà¸²à¸‡à¸™à¸µà¹‰à¸”ูสิ!" + reactToContinue: "เพิ่มรีà¹à¸à¸„ชั่นเพื่à¸à¸”ำเนินà¸à¸²à¸£à¸•à¹ˆà¸" + reactNotification: "คุณจะได้รับà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¹à¸šà¸šà¹€à¸£à¸µà¸¢à¸¥à¹„ทม์เมื่à¸à¸¡à¸µà¸„นตà¸à¸šà¸£à¸µà¹à¸à¸„ชั่นโน้ตขà¸à¸‡à¸„ุณ" + reactDone: "คุณสามารถยà¸à¹€à¸¥à¸´à¸à¸£à¸µà¹à¸à¸„ชั่นได้โดยà¸à¸²à¸£à¸à¸”ปุ่ม “-â€" _timeline: title: "à¹à¸™à¸§à¸„ิดเรื่à¸à¸‡à¸‚à¸à¸‡à¹„ทม์ไลน์" + description1: "Misskey มีหลายไทม์ไลน์ขึ้นà¸à¸¢à¸¹à¹ˆà¸à¸±à¸šà¸§à¸´à¸˜à¸µà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸‚à¸à¸‡à¸„ุณ (บางไทม์ไลน์à¸à¸²à¸ˆà¹„ม่สามารถใช้ได้ขึ้นà¸à¸¢à¸¹à¹ˆà¸à¸±à¸šà¸™à¹‚ยบายขà¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ)" + home: "คุณสามารถดูโพสต์จาà¸à¸šà¸±à¸à¸Šà¸µà¸—ี่คุณติดตามได้" + local: "คุณสามารถดูโพสต์จาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ั้งหมดบนเซิร์ฟเวà¸à¸£à¹Œà¸™à¸µà¹‰" + social: "โพสต์จาà¸à¸—ั้งไทม์ไลน์หน้าà¹à¸£à¸à¹à¸¥à¸°à¹„ทม์ไลน์ในพื้นที่ขà¸à¸‡à¸„ุณจะปราà¸à¸à¸‚ึ้น" + global: "คุณสามารถดูโพสต์จาà¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸—ี่เชื่à¸à¸¡à¸•à¹ˆà¸à¸à¸·à¹ˆà¸™à¹† ทั้งหมดได้" + description2: "คุณสามารถสลับระหว่างà¹à¸•à¹ˆà¸¥à¸°à¹„ทม์ไลน์ได้ตลà¸à¸”เวลาได้ที่บริเวณด้านบนขà¸à¸‡à¸«à¸™à¹‰à¸²à¸ˆà¸" + description3: "นà¸à¸à¸ˆà¸²à¸à¸™à¸µà¹‰à¸¢à¸±à¸‡à¸¡à¸µà¸£à¸²à¸¢à¸à¸²à¸£à¹„ทม์ไลน์ ไทม์ไลน์ขà¸à¸‡à¸Šà¹ˆà¸à¸‡ ฯลฯ โปรดดู {link} สำหรับรายละเà¸à¸µà¸¢à¸”เพิ่มเติม" _postNote: - title: "ตั้งค่าà¸à¸³à¸¥à¸±à¸‡à¹‚พสต์โน้ต" + title: "ตั้งค่าà¸à¸²à¸£à¹‚พสต์โน้ต" + description1: "เมื่à¸à¹‚พสต์โน้ตบน Misskey คุณสามารถตั้งค่าตัวเลืà¸à¸à¸•à¹ˆà¸²à¸‡à¹† ได้ à¹à¸šà¸šà¸Ÿà¸à¸£à¹Œà¸¡à¸à¸²à¸£à¸ªà¹ˆà¸‡à¸¡à¸µà¸¥à¸±à¸à¸©à¸“ะดังนี้" _visibility: description: "คุณสามารถจำà¸à¸±à¸”ผู้ที่สามารถดูโน้ตขà¸à¸‡à¸„ุณได้นะ" public: "โน้ตขà¸à¸‡à¸„ุณนั้นจะปราà¸à¸à¹à¸à¹ˆà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸—ุà¸à¸„น" + home: "เผยà¹à¸žà¸£à¹ˆà¸šà¸™à¹„ทม์ไลน์หน้าà¹à¸£à¸à¹€à¸—่านั้น ผู้คนที่เข้าชมโปรไฟล์ขà¸à¸‡à¸„ุณ ผ่านผู้ติดตาม à¹à¸¥à¸°à¸œà¹ˆà¸²à¸™à¸à¸²à¸£à¸£à¸µà¹‚น้ตสามารถเห็นได้" + followers: "มà¸à¸‡à¹€à¸«à¹‡à¸™à¹„ด้เฉพาะผู้ติดตามเท่านั้น ไม่มีใครà¸à¸·à¹ˆà¸™à¸™à¸à¸à¸ˆà¸²à¸à¸•à¸±à¸§à¸„ุณเà¸à¸‡à¸—ี่สามารถรีโน้ตได้ à¹à¸¥à¸°à¸¡à¸µà¹€à¸žà¸µà¸¢à¸‡à¸œà¸¹à¹‰à¸•à¸´à¸”ตามขà¸à¸‡à¸„ุณเท่านั้นที่สามารถดูได้" + direct: "เปิดให้เห็นเฉพาะผู้ใช้ที่ระบุเท่านั้น à¹à¸¥à¸°à¸žà¸§à¸à¹€à¸‚าจะได้รับà¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸”้วย คุณสามารถใช้มันà¹à¸—นข้à¸à¸„วามโดยตรง (dm)" + doNotSendConfidencialOnDirect1: "โปรดใช้ความระมัดระวังในà¸à¸²à¸£à¸ªà¹ˆà¸‡à¸‚้à¸à¸¡à¸¹à¸¥à¸—ี่ละเà¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" + doNotSendConfidencialOnDirect2: "ผู้ดูà¹à¸¥à¸£à¸°à¸šà¸šà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸›à¸¥à¸²à¸¢à¸—างสามารถดูเนื้à¸à¸«à¸²à¸—ี่โพสต์ได้ ดังนั้นหาà¸à¸„ุณส่งโพสต์โดยตรงไปยังผู้ใช้บนเซิร์ฟเวà¸à¸£à¹Œà¸—ี่ไม่น่าเชื่à¸à¸–ืภคุณจะต้à¸à¸‡à¹ƒà¸Šà¹‰à¸„วามระมัดระวังในà¸à¸²à¸£à¸ˆà¸±à¸”à¸à¸²à¸£à¸‚้à¸à¸¡à¸¹à¸¥à¸—ี่เป็นความลับ" + localOnly: "à¸à¸²à¸£à¹‚พสต์ด้วย flag นี้จะไม่รวมโน้ตไปยังเซิร์ฟเวà¸à¸£à¹Œà¸à¸·à¹ˆà¸™ ผู้ใช้บนเซิร์ฟเวà¸à¸£à¹Œà¸à¸·à¹ˆà¸™à¸ˆà¸°à¹„ม่สามารถดูโน้ตเหล่านี้ได้โดยตรง โดยไม่คำนึงถึงà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าà¸à¸²à¸£à¹à¸ªà¸”งผลข้างต้น" _cw: title: "คำเตืà¸à¸™à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¹€à¸™à¸·à¹‰à¸à¸«à¸²" + description: "เนื้à¸à¸«à¸²à¸—ี่เขียนด้วย “คำà¸à¸˜à¸´à¸šà¸²à¸¢à¸›à¸£à¸°à¸à¸à¸šâ€ จะà¹à¸ªà¸”งà¹à¸—นข้à¸à¸„วามหลัภคลิภ“ดูเพิ่มเติม†เพื่à¸à¹à¸ªà¸”งข้à¸à¸„วามเต็ม" _exampleNote: cw: "นี่à¸à¸²à¸ˆà¸ˆà¸°à¸—ำให้คุณหิวà¸à¸¢à¹ˆà¸²à¸‡à¹à¸™à¹ˆà¸™à¸à¸™!" + note: "เพิ่งไปà¸à¸´à¸™à¹‚ดนัทเคลืà¸à¸šà¸Šà¹‡à¸à¸„โà¸à¹à¸¥à¸•à¸¡à¸² ðŸ©ðŸ˜‹" + useCases: "ใช้สิ่งนี้เพื่à¸à¸£à¸°à¸šà¸¸à¹‚น้ตที่ต้à¸à¸‡à¸•à¸²à¸¡à¹à¸™à¸§à¸—างปà¸à¸´à¸šà¸±à¸•à¸´à¸‚à¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ หรืà¸à¹€à¸žà¸·à¹ˆà¸à¸„วบคุมà¸à¸²à¸£à¸ªà¸›à¸à¸¢à¸¥à¹Œà¹à¸¥à¸°à¸‚้à¸à¸„วามที่ละเà¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™à¸”้วยตนเà¸à¸‡" + _howToMakeAttachmentsSensitive: + title: "จะทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹„ฟล์à¹à¸™à¸šà¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™à¹„ด้à¸à¸¢à¹ˆà¸²à¸‡à¹„ร?" + description: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹„ฟล์à¹à¸™à¸šà¸§à¹ˆà¸² “มีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™â€ เมื่à¸à¸ˆà¸³à¹€à¸›à¹‡à¸™à¸•à¸²à¸¡à¹à¸™à¸§à¸—างขà¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ หรืà¸à¹€à¸¡à¸·à¹ˆà¸à¹„ฟล์à¹à¸™à¸šà¹„ม่ควรปราà¸à¸à¹ƒà¸«à¹‰à¹€à¸«à¹‡à¸™" + tryThisFile: "ลà¸à¸‡à¸—ำให้รูปภาพที่à¹à¸™à¸šà¸¡à¸²à¸à¸±à¸šà¹à¸šà¸šà¸Ÿà¸à¸£à¹Œà¸¡à¸™à¸µà¹‰à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™!" + _exampleNote: + note: "à¸à¸¸à¹‰à¸¢ นัตโตะ à¸à¸²à¹€à¸›à¸´à¸”เละเทะ..." + method: "หาà¸à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸—ำให้ไฟล์à¹à¸™à¸šà¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™ ให้คลิà¸à¹„ฟล์เพื่à¸à¹€à¸›à¸´à¸”เมนูà¹à¸¥à¹‰à¸§à¸„ลิภ“ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™â€" + sensitiveSucceeded: "เมื่à¸à¹à¸™à¸šà¹„ฟล์ โปรดตั้งค่าเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™à¸•à¸²à¸¡à¹à¸™à¸§à¸—างขà¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" + doItToContinue: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸à¸±à¸šà¸£à¸¹à¸›à¸ าพว่ามีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™ เพื่à¸à¸”ำเนินà¸à¸²à¸£à¸•à¹ˆà¸" + _done: + title: "บทเรียนจบลงà¹à¸¥à¹‰à¸§à¸ˆà¹‰à¸² เย่เย่เย่ 🎉" + description: "คุณสมบัติที่à¹à¸™à¸°à¸™à¸³à¹ƒà¸™à¸—ี่นี่เป็นเพียงบางส่วนเท่านั้น หาà¸à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸™à¸£à¸¹à¹‰à¹€à¸žà¸´à¹ˆà¸¡à¹€à¸•à¸´à¸¡à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸§à¸´à¸˜à¸µà¹ƒà¸Šà¹‰ Misskey โปรดไปที่ {link}" +_timelineDescription: + home: "บนไทม์ไลน์หน้าà¹à¸£à¸ คุณสามารถดูโพสต์จาà¸à¸šà¸±à¸à¸Šà¸µà¸—ี่คุณติดตามได้" + local: "ไทม์ไลน์ในพื้นที่ช่วยให้คุณเห็นโพสต์จาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ั้งหมดบนเซิร์ฟเวà¸à¸£à¹Œà¸™à¸µà¹‰" + social: "ไทม์ไลน์โซเชียลจะà¹à¸ªà¸”งโพสต์จาà¸à¸—ั้งไทม์ไลน์หน้าà¹à¸£à¸à¹à¸¥à¸°à¹„ทม์ไลน์ในพื้นที่" + global: "ในไทม์ไลน์ทั่วโลภคุณสามารถดูโน้ตจาà¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸—ี่เชื่à¸à¸¡à¸•à¹ˆà¸à¸—ั้งหมดได้" _serverRules: description: "ชุดขà¸à¸‡à¸à¸Žà¸—ี่จะà¹à¸ªà¸”งà¸à¹ˆà¸à¸™à¸à¸²à¸£à¸¥à¸‡à¸—ะเบียนเราขà¸à¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¸•à¸±à¹‰à¸‡à¸„่าสรุปข้à¸à¸à¸³à¸«à¸™à¸”ในà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£" _serverSettings: - iconUrl: "ไà¸à¸„à¸à¸™ URL" + iconUrl: "URL ไà¸à¸„à¸à¸™" + appIconDescription: "ระบุไà¸à¸„à¸à¸™à¸—ี่จะใช้เมื่ภ{host} à¹à¸ªà¸”งเป็นà¹à¸à¸›" appIconUsageExample: "E.g. เป็น PWA หรืà¸à¹€à¸¡à¸·à¹ˆà¸à¹à¸ªà¸”งผลเป็นบุ๊à¸à¸¡à¸²à¸£à¹Œà¸à¸«à¸™à¹‰à¸²à¸ˆà¸à¸«à¸¥à¸±à¸à¸šà¸™à¹‚ทรศัพท์" + appIconStyleRecommendation: "เนื่à¸à¸‡à¸ˆà¸²à¸à¹„à¸à¸„à¸à¸™à¸à¸²à¸ˆà¸–ูà¸à¸„รà¸à¸šà¸•à¸±à¸”เป็นสี่เหลี่ยมจัตุรัสหรืà¸à¸§à¸‡à¸à¸¥à¸¡ จึงà¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¹ƒà¸Šà¹‰à¹„à¸à¸„à¸à¸™à¸—ี่มีขà¸à¸šà¸ªà¸µà¸£à¸à¸šà¹† เนื้à¸à¸«à¸²" appIconResolutionMustBe: "ความละเà¸à¸µà¸¢à¸”ขั้นต่ำไว้คืภ{resolution}." - manifestJsonOverride: "manifest.json โà¸à¹€à¸§à¸à¸£à¹Œà¸¥à¸²à¸¢" + manifestJsonOverride: "เขียนทับ manifest.json" shortName: "ชื่à¸à¸¢à¹ˆà¸" + shortNameDescription: "ตัวย่à¸à¸«à¸£à¸·à¸à¸Šà¸·à¹ˆà¸à¸—ั่วไปที่สามารถà¹à¸ªà¸”งà¹à¸—นชื่à¸à¸à¸¢à¹ˆà¸²à¸‡à¹€à¸›à¹‡à¸™à¸—างà¸à¸²à¸£à¹à¸šà¸šà¸¢à¸²à¸§à¸‚à¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" + fanoutTimelineDescription: "เพิ่มประสิทธิภาพà¸à¸²à¸£à¸”ึงข้à¸à¸¡à¸¹à¸¥à¹„ทม์ไลน์à¸à¸¢à¹ˆà¸²à¸‡à¸¡à¸²à¸ à¹à¸¥à¸°à¸¥à¸”ภาระในà¸à¸²à¸™à¸‚้à¸à¸¡à¸¹à¸¥à¹€à¸¡à¸·à¹ˆà¸à¹€à¸›à¸´à¸”ใช้งาน ในทางà¸à¸¥à¸±à¸šà¸à¸±à¸™ à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸«à¸™à¹ˆà¸§à¸¢à¸„วามจำขà¸à¸‡ Redis จะเพิ่มขึ้น ลà¸à¸‡à¸›à¸´à¸”à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸™à¸µà¹‰à¹ƒà¸™à¸à¸£à¸“ีที่หน่วยความจำเซิร์ฟเวà¸à¸£à¹Œà¹€à¸«à¸¥à¸·à¸à¸™à¹‰à¸à¸¢à¸«à¸£à¸·à¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¹„ม่เสถียร" + fanoutTimelineDbFallback: "ฟà¸à¸¥à¹à¸šà¹Šà¸à¸à¸¥à¸±à¸šà¸à¸²à¸™à¸‚้à¸à¸¡à¸¹à¸¥" + fanoutTimelineDbFallbackDescription: "เมื่à¸à¹€à¸›à¸´à¸”ใช้งาน หาà¸à¹„ม่ได้à¹à¸„ชไทม์ไลน์ ไทม์ไลน์จะฟà¸à¸¥à¹à¸šà¹Šà¸à¹„ปยังà¸à¸²à¸™à¸‚้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸«à¸£à¸±à¸šà¸à¸²à¸£ query เพิ่มเติม à¸à¸²à¸£à¸›à¸´à¸”ใช้งานจะช่วยลดภาระขà¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸”้วยà¸à¸²à¸£à¸à¸³à¸ˆà¸±à¸”à¸à¸£à¸°à¸šà¸§à¸™à¸Ÿà¸à¸¥à¹à¸šà¹Šà¸ à¹à¸•à¹ˆà¸¡à¸±à¸™à¸à¹‡à¸ˆà¸°à¸ˆà¸³à¸à¸±à¸”ช่วงเวลาไทม์ไลน์ที่สามารถดึงข้à¸à¸¡à¸¹à¸¥à¹„ด้" _accountMigration: moveFrom: "ย้ายข้à¸à¸¡à¸¹à¸¥à¸šà¸±à¸à¸Šà¸µà¸à¸·à¹ˆà¸™à¹„ปยังà¸à¸µà¸à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¸«à¸™à¸¶à¹ˆà¸‡" moveFromSub: "สร้างนามà¹à¸à¸‡à¹„ปยังบัà¸à¸Šà¸µà¸à¸·à¹ˆà¸™" @@ -1212,7 +1356,7 @@ _accountMigration: moveTo: "ย้ายข้à¸à¸¡à¸¹à¸¥à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹„ปยังบัà¸à¸Šà¸µà¸à¸µà¸à¸«à¸™à¸¶à¹ˆà¸‡" moveToLabel: "บัà¸à¸Šà¸µà¸—ี่จะย้ายไปที่:" moveCannotBeUndone: "ไม่สามารถยà¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¹‚à¸à¸™à¸¢à¹‰à¸²à¸¢à¸šà¸±à¸à¸Šà¸µà¹„ด้" - moveAccountDescription: "à¸à¸²à¸£à¸à¸£à¸°à¸—ำนี้ไม่สามารถย้à¸à¸™à¸à¸¥à¸±à¸šà¹„ด้นะ ขั้นตà¸à¸™à¹à¸£à¸ ต้à¸à¸‡à¸ªà¸£à¹‰à¸²à¸‡à¸™à¸²à¸¡à¹à¸à¸‡à¸ªà¸³à¸«à¸£à¸±à¸šà¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹ƒà¸™à¸šà¸±à¸à¸Šà¸µà¸—ี่คุณต้à¸à¸‡à¸à¸²à¸£à¸¢à¹‰à¸²à¸¢à¹„ป หลังจาà¸à¸™à¸±à¹‰à¸™à¹à¸¥à¹‰à¸§ ป้à¸à¸™à¸šà¸±à¸à¸Šà¸µà¸—ี่จะย้ายไปในรูปà¹à¸šà¸šà¸”ังต่à¸à¹„ปนี้: @person@instance.com" + moveAccountDescription: "à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¸ˆà¸°à¸¢à¹‰à¸²à¸¢à¸šà¸±à¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณไปยังบัà¸à¸Šà¸µà¸à¸·à¹ˆà¸™\n・ผู้ที่à¸à¸³à¸¥à¸±à¸‡à¸•à¸´à¸”ตามคุณจาà¸à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¸ˆà¸°à¸–ูà¸à¸¢à¹‰à¸²à¸¢à¹„ปยังบัà¸à¸Šà¸µà¹ƒà¸«à¸¡à¹ˆà¹‚ดยà¸à¸±à¸•à¹‚นมัติ\n・บัà¸à¸Šà¸µà¸™à¸µà¹‰à¸ˆà¸°à¹€à¸¥à¸´à¸à¸•à¸´à¸”ตามผู้ใช้ทั้งหมดที่à¸à¸³à¸¥à¸±à¸‡à¸•à¸´à¸”ตามà¸à¸¢à¸¹à¹ˆ\n・คุณจะไม่สามารถสร้างโน้ต ฯลฯ ในบัà¸à¸Šà¸µà¸™à¸µà¹‰à¹„ด้\n\nà¹à¸¡à¹‰à¸§à¹ˆà¸²à¸à¸²à¸£à¸¢à¹‰à¸²à¸¢à¸œà¸¹à¹‰à¸—ี่ติดตามคุณจะเป็นไปโดยà¸à¸±à¸•à¹‚นมัติ à¹à¸•à¹ˆà¸„ุณต้à¸à¸‡à¹€à¸•à¸£à¸µà¸¢à¸¡à¸‚ั้นตà¸à¸™à¸šà¸²à¸‡à¸à¸¢à¹ˆà¸²à¸‡à¸”้วยตนเà¸à¸‡ เพื่à¸à¸¢à¹‰à¸²à¸¢à¸£à¸²à¸¢à¸Šà¸·à¹ˆà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่คุณà¸à¸³à¸¥à¸±à¸‡à¸•à¸´à¸”ตาม โดยดำเนินà¸à¸²à¸£à¸ªà¹ˆà¸‡à¸à¸à¸à¸£à¸²à¸¢à¸Šà¸·à¹ˆà¸à¹à¸¥à¹‰à¸§à¸„่à¸à¸¢à¸™à¸³à¹€à¸‚้ามาภายหลังในเมนูà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าขà¸à¸‡à¸šà¸±à¸à¸Šà¸µà¹ƒà¸«à¸¡à¹ˆ ใช้ขั้นตà¸à¸™à¹€à¸”ียวà¸à¸±à¸™à¸™à¸µà¹‰à¹ƒà¸Šà¹‰à¸£à¸²à¸¢à¸Šà¸·à¹ˆà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่ถูà¸à¸›à¸´à¸”เสียงà¹à¸¥à¸°à¸–ูà¸à¸šà¸¥à¹‡à¸à¸\n\n(คำà¸à¸˜à¸´à¸šà¸²à¸¢à¸™à¸µà¹‰à¹ƒà¸Šà¹‰à¸à¸±à¸š Misskey v13.12.0 ขึ้นไป, ซà¸à¸Ÿà¸•à¹Œà¹à¸§à¸£à¹Œ ActivityPub à¸à¸·à¹ˆà¸™à¹† เช่น Mastodon à¸à¸²à¸ˆà¸—ำงานà¹à¸•à¸à¸•à¹ˆà¸²à¸‡à¸à¸à¸à¹„ป)" moveAccountHowTo: "หาà¸à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸¢à¹‰à¸²à¸¢à¸‚้à¸à¸¡à¸¹à¸¥à¸à¹ˆà¸à¸™à¸à¸·à¹ˆà¸™à¹ƒà¸«à¹‰à¸ªà¸£à¹‰à¸²à¸‡à¸Šà¸·à¹ˆà¸à¹à¸—นสำหรับบัà¸à¸Šà¸µà¸™à¸µà¹‰ ในบัà¸à¸Šà¸µà¸—ี่จะต้à¸à¸‡à¸à¸²à¸£à¸¢à¹‰à¸²à¸¢à¹„ป\nหลังจาà¸à¸—ี่คุณสร้างนามà¹à¸à¸‡à¸™à¸±à¹‰à¸™à¹à¸¥à¹‰à¸§ ให้ป้à¸à¸™à¸šà¸±à¸à¸Šà¸µà¸—ี่ต้à¸à¸‡à¸à¸²à¸£à¸ˆà¸°à¸¢à¹‰à¸²à¸¢à¹„ปในรูปà¹à¸šà¸šà¸”ังต่à¸à¹„ปนี้: @username@server.example.com" startMigration: "โà¸à¸™à¸¢à¹‰à¸²à¸¢" migrationConfirm: "ยืนยันà¸à¸²à¸£à¸¢à¹‰à¸²à¸¢à¸‚้à¸à¸¡à¸¹à¸¥à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹„ปที่ {account} เมื่à¸à¹€à¸£à¸´à¹ˆà¸¡à¹à¸¥à¹‰à¸§à¸ˆà¸°à¹„ม่สามารถหยุดหรืà¸à¸™à¸³à¸à¸¥à¸±à¸šà¸„ืนมาได้ à¹à¸¥à¸°à¸„ุณจะไม่สามารถใช้บัà¸à¸Šà¸µà¸™à¸µà¹‰à¹ƒà¸™à¸ªà¸–านะดั้งเดิมได้à¸à¸µà¸à¸•à¹ˆà¸à¹„ป\n\nนà¸à¸à¸ˆà¸²à¸à¸™à¸µà¹‰ คุณจำเป็นต้à¸à¸‡à¸ªà¸£à¹‰à¸²à¸‡à¸šà¸±à¸à¸Šà¸µà¸ªà¸³à¸£à¸à¸‡à¸ªà¸³à¸«à¸£à¸±à¸šà¸à¸²à¸£à¸¢à¹‰à¸²à¸¢à¸šà¸±à¸à¸Šà¸µ" @@ -1227,31 +1371,31 @@ _achievements: description: "โพสต์โน้ตà¹à¸£à¸à¸‚à¸à¸‡à¸„ุณ" flavor: "ขà¸à¹ƒà¸«à¹‰à¸¡à¸µà¸Šà¹ˆà¸§à¸‡à¹€à¸§à¸¥à¸²à¸—ี่ดีà¸à¸±à¸š Misskey นะคะ!" _notes10: - title: "โน้ตบางà¸à¸¢à¹ˆà¸²à¸‡" + title: "โน้ตไม่à¸à¸µà¹ˆà¸Šà¸´à¹‰à¸™" description: "โพสต์ 10 โน้ต" _notes100: - title: "โน้ตจำนวนมาà¸" + title: "โน้ตเยà¸à¸°à¸à¸¢à¸¹à¹ˆ" description: "โพสต์ 100 โน้ต" _notes500: - title: "ครà¸à¸šà¸„ลุมในโน้ต" + title: "จมคาà¸à¸à¸‡à¹‚น้ต" description: "โพสต์ 500 โน้ต" _notes1000: title: "ภูเขาà¹à¸«à¹ˆà¸‡à¹‚น้ต" description: "โพสต์ 1,000 โน้ต" _notes5000: - title: "โน้ตล้น" + title: "โน้ตล้นไปà¹à¸¥à¹‰à¸§" description: "โพสต์ 5,000 โน้ต" _notes10000: title: "ซุปเปà¸à¸£à¹Œà¹‚น้ต" description: "โพสต์ 10,000 โน้ต" _notes20000: - title: "ต้à¸à¸‡à¸à¸²à¸£... เพิ่มเติม... โน้ต..." + title: "ต้ ภง ภา ร โ น้ ต เ พิ่ ม à¸à¸µ ภ!" description: "โพสต์ 20,000 โน้ต" _notes30000: title: "โน้ต โน้ต โน้ต!" description: "โพสต์ 30,000 โน้ต" _notes40000: - title: "โน้ตโรงงาน" + title: "โรงงานผลิตโน้ต" description: "โพสต์ 40,000 โน้ต" _notes50000: title: "ดาวเคราะห์à¹à¸«à¹ˆà¸‡à¹‚น้ต" @@ -1260,26 +1404,26 @@ _achievements: title: "โน้ตควà¸à¸‹à¸²à¸£à¹Œ" description: "โพสต์ 60,000 โน้ต" _notes70000: - title: "โน้ตหลุมดำ" + title: "หลุม-โน้ต-ดำ" description: "โพสต์ 70,000 โน้ต" _notes80000: - title: "โน้ต à¸à¸²à¹à¸¥à¹‡à¸à¸‹à¸µà¹ˆ" + title: "ดาราจัà¸à¸£à¹‚น้ต" description: "โพสต์ 80,000 โน้ต" _notes90000: - title: "โน้ต จัà¸à¸£à¸§à¸²à¸¥" + title: "จัà¸à¸£à¸§à¸²à¸¥à¹‚น้ต" description: "โพสต์ 90,000 โน้ต" _notes100000: title: "ALL YOUR NOTE ARE BELONG TO US" description: "โพสต์ 100,000 โน้ต" - flavor: "นายà¹à¸™à¹ˆà¹ƒà¸ˆà¸¥à¹ˆà¸°à¸à¹‡ มีà¸à¸°à¹„รพูดมาได้นะ" + flavor: "มีเรื่à¸à¸‡à¸ˆà¸°à¹€à¸‚ียนมาà¸à¸‚นาดนั้นเลยเหรà¸à¸™à¸±à¹ˆà¸™?" _login3: title: "มืà¸à¹ƒà¸«à¸¡à¹ˆ I" description: "เข้าสู่ระบบเป็นเวลารวม 3 วัน" - flavor: "เริ่มตั้งà¹à¸•à¹ˆà¸§à¸±à¸™à¸™à¸µà¹‰ เรียà¸à¸‰à¸±à¸™à¸§à¹ˆà¸²à¸¡à¸´à¸ªà¸„ิสต์" + flavor: "ตั้งà¹à¸•à¹ˆà¸§à¸±à¸™à¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¸•à¹‰à¸™à¹„ป ฉันคืà¸à¸¡à¸´à¸ªà¸„ิสต์" _login7: title: "มืà¸à¹ƒà¸«à¸¡à¹ˆ II" description: "เข้าสู่ระบบเป็นเวลารวม 7 วัน" - flavor: "รู้สึà¸à¹€à¸«à¸¡à¸·à¸à¸™à¸„ุณได้à¹à¸‚วนขà¸à¸‡à¸ªà¸´à¹ˆà¸‡à¸•à¹ˆà¸²à¸‡à¹† หรืà¸à¸¢à¸±à¸‡à¸„ะ?" + flavor: "ชินà¸à¸±à¸šà¸¡à¸±à¸™à¹à¸¥à¹‰à¸§à¸«à¸£à¸·à¸à¸¢à¸±à¸‡?" _login15: title: "มืà¸à¹ƒà¸«à¸¡à¹ˆ III" description: "เข้าสู่ระบบเป็นเวลารวม 15 วัน" @@ -1292,7 +1436,7 @@ _achievements: _login100: title: "มิสคิสท์ III" description: "เข้าสู่ระบบเป็นเวลารวม 100 วัน" - flavor: "ความรุนà¹à¸£à¸‡ Misskist" + flavor: "มิสคิสต์หัวรุนà¹à¸£à¸‡" _login200: title: "ลูà¸à¸„้าประจำ I" description: "เข้าสู่ระบบเป็นเวลารวม 200 วัน" @@ -1305,7 +1449,7 @@ _achievements: _login500: title: "ผู้เชี่ยวชาภI" description: "เข้าสู่ระบบเป็นเวลารวม 500 วัน" - flavor: "เพื่à¸à¸™à¸‚à¸à¸‡à¸œà¸¡à¸™à¸°à¸¡à¸±à¸à¸ˆà¸°à¸à¸¥à¹ˆà¸²à¸§à¸§à¹ˆà¸²à¸œà¸¡à¸™à¸°à¸Šà¸à¸šà¸ˆà¸”โน้ต" + flavor: "ทุà¸à¸—่าน ผมชà¸à¸šà¹‚น้ต (à¸à¸¥à¹ˆà¸²à¸§à¹‚ดย เดà¸à¸° เ_เ_à¸à¸£à¹Œ)" _login600: title: "ผู้เชี่ยวชาภII" description: "เข้าสู่ระบบเป็นเวลารวม 600 วัน" @@ -1323,7 +1467,7 @@ _achievements: description: "เข้าสู่ระบบเป็นเวลารวม 1,000 วัน" flavor: "ขà¸à¸šà¸„ุณที่ใช้ Misskey นะ !" _noteClipped1: - title: "จะต้à¸à¸‡... คลิป..." + title: "à¸à¸”ไม่ได้ที่จะต้à¸à¸‡à¸„ลิปมันเà¸à¸²à¹„ว้" description: "คลิปโน้ตตัวà¹à¸£à¸à¸‚à¸à¸‡à¸„ุณ" _noteFavorited1: title: "สตาร์เà¸à¹€à¸‹à¸à¸£à¹Œ" @@ -1332,15 +1476,15 @@ _achievements: title: "à¹à¸ªà¸§à¸‡à¸«à¸²à¸”วงดาว" description: "มีคนà¸à¸·à¹ˆà¸™à¹†à¸—ี่ชื่นชà¸à¸šà¸«à¸™à¸¶à¹ˆà¸‡à¹ƒà¸™à¹‚น้ตขà¸à¸‡à¸„ุณ" _profileFilled: - title: "เตรียมไว้à¸à¸¢à¹ˆà¸²à¸‡à¸”ี" + title: "เตรียมตัวà¸à¸¢à¹ˆà¸²à¸‡à¸”ี" description: "ตั้งค่าโปรไฟล์ขà¸à¸‡à¸„ุณ" _markedAsCat: title: "ฉันเป็นà¹à¸¡à¸§" description: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸šà¸±à¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณว่าเป็นà¹à¸¡à¸§" - flavor: "ฉันจะให้ชื่à¸à¸„ุณภายหลังนะ" + flavor: "à¹à¸¡à¸§à¸™à¹‰à¸à¸¢à¹„ร้ชื่à¸" _following1: - title: "à¸à¸³à¸¥à¸±à¸‡à¸•à¸´à¸”ตามผู้ใช้คนà¹à¸£à¸à¸‚à¸à¸‡à¸„ุณ" - description: "ติดตามผู้ใช้" + title: "à¸à¹‰à¸²à¸§à¹à¸£à¸à¸ªà¸¹à¹ˆ...à¸à¸”ติดตาม" + description: "à¸à¸”ติดตามชาวบ้านครั้งà¹à¸£à¸" _following10: title: "ทำต่à¸à¹„ป... ทำต่à¸à¹„ป..." description: "ติดตาม 10 บัà¸à¸Šà¸µà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" @@ -1351,7 +1495,7 @@ _achievements: title: "เพื่à¸à¸™ 100 คน" description: "ติดตาม 100 บัà¸à¸Šà¸µ" _following300: - title: "เพื่à¸à¸™à¹‚à¸à¹€à¸§à¸à¸£à¹Œà¹‚หลด" + title: "มีเพื่à¸à¸™à¸¡à¸²à¸à¹€à¸à¸´à¸™à¹„ปละ" description: "ติดตาม 300 บัà¸à¸Šà¸µ" _followers1: title: "ผู้ติดตามคนà¹à¸£à¸" @@ -1378,12 +1522,12 @@ _achievements: title: "นัà¸à¸ªà¸°à¸ªà¸¡à¸„วามสำเร็จ" description: "ได้รับความสำเร็จ 30 ครั้ง" _viewAchievements3min: - title: "ชà¸à¸šà¸šà¸£à¸£à¸¥à¸¸à¸œà¸¥à¸ªà¹à¸²à¹€à¸£à¹‡à¸ˆ" + title: "ชà¸à¸šà¸šà¸£à¸£à¸¥à¸¸à¸„วามสà¹à¸²à¹€à¸£à¹‡à¸ˆ" description: "มà¸à¸‡à¸”ูรายà¸à¸²à¸£à¸„วามสำเร็จขà¸à¸‡à¸„ุณเป็นเวลาà¸à¸¢à¹ˆà¸²à¸‡à¸™à¹‰à¸à¸¢ 3 นาที" _iLoveMisskey: title: "ฉันรัภMisskey" - description: "โพสต์ \"I ⤠#Misskey\"" - flavor: "ขà¸à¸šà¸„ุณที่ใช้ Misskey! by ทีมผู้พัฒนา" + description: "โพสต์ “I ⤠#Misskeyâ€" + flavor: "ขà¸à¸šà¸„ุณพระคุณเป็นà¸à¸¢à¹ˆà¸²à¸‡à¸ªà¸¹à¸‡à¸—ี่ท่านใช้ Misskey นะคะ ! by ทีมผู้พัฒนา" _foundTreasure: title: "ล่าสมบัติ" description: "คุณพบสมบัติที่ซ่à¸à¸™à¸à¸¢à¸¹à¹ˆ" @@ -1391,25 +1535,25 @@ _achievements: title: "พัà¸à¸œà¹ˆà¸à¸™à¸ªà¸±à¸à¸«à¸™à¹ˆà¸à¸¢" description: "ใช้เวลา 30 นาทีบน Misskey" _client60min: - title: "ไม่มี \"Miss\" ใน Misskey " + title: "Misskey ต้à¸à¸‡à¹„ม่มีสิ่งใด “Missâ€" description: "เปิด Misskey ค้างไว้à¹à¸¥à¹‰à¸§à¸à¸¢à¹ˆà¸²à¸‡à¸™à¹‰à¸à¸¢ 60 นาที" _noteDeletedWithin1min: title: "ไม่เป็นไร" description: "ลบโน้ตภายในหนึ่งนาทีหลังจาà¸à¸—ี่โพสต์" _postedAtLateNight: - title: "à¸à¸¥à¸²à¸‡à¸„ืน" + title: "à¸à¸à¸à¸«à¸²à¸à¸´à¸™à¸¢à¸²à¸¡à¸”ึà¸à¸”ื่น" description: "โพสต์โน้ตตà¸à¸™à¸”ึà¸à¹†" flavor: "ได้เวลาเข้านà¸à¸™à¹à¸¥à¹‰à¸§à¸™à¸°" _postedAt0min0sec: - title: "นาฬิà¸à¸²à¸žà¸¹à¸”ได้" - description: "โพสต์บนโน้ตเมื่à¸à¹€à¸§à¸¥à¸² 00:00 น." - flavor: "คลิภคลิภคลิภà¹à¸à¸¥à¹Šà¸‡à¹†" + title: "นาฬิà¸à¸²à¹€à¸—ียบเวลา" + description: "โพสต์โน้ตเมื่à¸à¹€à¸§à¸¥à¸² 00:00 น." + flavor: "โป๊ะ โป๊ะ โป๊ะ ปิ้งงงงง" _selfQuote: title: "à¸à¹‰à¸²à¸‡à¸à¸´à¸‡à¸•à¸™à¹€à¸à¸‡" - description: "à¸à¹‰à¸²à¸‡à¹‚น้ตย่à¸à¸‚à¸à¸‡à¸„ุณเà¸à¸‡" + description: "à¸à¹‰à¸²à¸‡à¹‚น้ตขà¸à¸‡à¸„ุณเà¸à¸‡" _htl20npm: title: "ไทม์ไลน์ไหล" - description: "มีà¸à¸²à¸£à¸—ำความเร็วขà¸à¸‡à¹„ทม์ไลน์ที่บ้านขà¸à¸‡à¸„ุณเà¸à¸´à¸™ 20 npm (โน้ตต่à¸à¸™à¸²à¸—ี)" + description: "มีà¸à¸²à¸£à¸—ำความเร็วขà¸à¸‡à¹„ทม์ไลน์หน้าà¹à¸£à¸à¹€à¸à¸´à¸™ 20 npm (โน้ตต่à¸à¸™à¸²à¸—ี)" _viewInstanceChart: title: "วิเคราะห์" description: "ดูà¹à¸œà¸™à¸ ูมิà¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸‚à¸à¸‡à¸„ุณ" @@ -1426,14 +1570,14 @@ _achievements: title: "คุณà¸à¹ˆà¸²à¸™à¸¡à¸±à¸™à¸ˆà¸£à¸´à¸‡à¹†à¸«à¸£à¸·à¸à¹€à¸›à¸¥à¹ˆà¸²?" description: "มีà¸à¸²à¸£à¹‚ต้ตà¸à¸šà¸à¸±à¸šà¹‚น้ตที่มีความยาวมาà¸à¸à¸§à¹ˆà¸² 100 ตัวà¸à¸±à¸à¸©à¸£à¸ ายใน 3 วินาทีหลังจาà¸à¸—ี่โพสต์" _clickedClickHere: - title: "คลิ๊à¸à¸—ี่นี่" + title: "คลิà¸à¸—ี่นี่" description: "คุณได้คลิà¸à¸—ี่นี่" _justPlainLucky: title: "à¹à¸„่ลัคà¸à¸µà¹‰à¸˜à¸£à¸£à¸¡à¸”า" description: "มีโà¸à¸à¸²à¸ªà¸—ี่จะได้รับด้วยความน่าจะเป็นไปได้ 0.005% ทุภๆ 10 วินาที" _setNameToSyuilo: - title: "พระเจ้าคà¸à¸¡à¹€à¸žà¸¥à¹‡à¸à¸‹à¹Œ" - description: "ตั้งชื่à¸à¸‚à¸à¸‡à¸„ุณเป็น \"syuilo\"" + title: "คà¸à¸¡à¹€à¸žà¸¥à¹‡à¸à¸‹à¹Œà¸‚à¸à¸‡à¸žà¸£à¸°à¹€à¸ˆà¹‰à¸²" + description: "ตั้งชื่à¸à¸‚à¸à¸‡à¸„ุณเป็น “syuiloâ€" _passedSinceAccountCreated1: title: "ครบรà¸à¸šà¸«à¸™à¸¶à¹ˆà¸‡à¸›à¸µ" description: "ผ่านไปหนึ่งปีà¹à¸¥à¹‰à¸§à¸™à¸°à¸•à¸±à¹‰à¸‡à¹à¸•à¹ˆà¸šà¸±à¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณถูà¸à¸ªà¸£à¹‰à¸²à¸‡à¸‚ึ้นมาน่ะ" @@ -1453,7 +1597,7 @@ _achievements: _cookieClicked: title: "เà¸à¸¡à¸—ี่คุณคลิà¸à¸—ี่คุà¸à¸à¸µà¹‰" description: "คลิà¸à¸„ุà¸à¸à¸µà¹‰" - flavor: "เดี๋ยวà¸à¹ˆà¸à¸™à¸™à¸° คุณà¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¹€à¸§à¹‡à¸šà¹„ซต์ที่ถูà¸à¸•à¹‰à¸à¸‡à¹à¸™à¹ˆà¸à¸¢à¹ˆà¸²à¸‡à¸‡à¸±à¹‰à¸™à¹€à¸«à¸£à¸?" + flavor: "ใช่หรà¸? à¹à¸™à¹ˆà¹ƒà¸ˆà¸§à¹ˆà¸²à¸‹à¸à¸Ÿà¸•à¹Œà¹à¸§à¸£à¹Œà¸—ำงานถูà¸à¸•à¹‰à¸à¸‡à¸™à¸°?" _brainDiver: title: "Brain Diver" description: "โพสต์ลิงà¸à¹Œà¹„ปยัง Brain Diver" @@ -1461,35 +1605,47 @@ _achievements: _smashTestNotificationButton: title: "ทดสà¸à¸šà¹‚à¸à¹€à¸§à¸à¸£à¹Œà¹‚ฟลว์" description: "ทดสà¸à¸šà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸—ริà¸à¹€à¸à¸à¸£à¹Œà¸‹à¹‰à¸³à¹† ภายในระยะเวลาà¸à¸±à¸™à¸ªà¸±à¹‰à¸™à¹†" + _tutorialCompleted: + title: "ใบรับรà¸à¸‡à¸à¸²à¸£à¸ªà¸³à¹€à¸£à¹‡à¸ˆà¸«à¸¥à¸±à¸à¸ªà¸¹à¸•à¸£ Misskey มืà¸à¹ƒà¸«à¸¡à¹ˆ" + description: "เสร็จสิ้นà¸à¸²à¸£à¸ªà¸à¸™à¹à¸¥à¹‰à¸§" + _bubbleGameExplodingHead: + title: "🤯" + description: "สร้างวัตถุที่ใหà¸à¹ˆà¸—ี่สุดในเà¸à¸¡à¸šà¸±à¸šà¹€à¸šà¸´à¹‰à¸¥" + _bubbleGameDoubleExplodingHead: + title: "ดับเบิ้ล" + description: "สร้างวัตถุที่ใหà¸à¹ˆà¸—ี่สุดในเà¸à¸¡à¸šà¸±à¸šà¹€à¸šà¸´à¹‰à¸¥à¸ªà¸à¸‡à¸Šà¸´à¹‰à¸™à¹ƒà¸™à¹€à¸§à¸¥à¸²à¹€à¸”ียวà¸à¸±à¸™" + flavor: "ปิ่นโตขนาดนี้ น่าจะเพิ่ม 🤯 🤯 เข้าไปนิดหน่à¸à¸¢" _role: new: "บทบาทใหม่" edit: "à¹à¸à¹‰à¹„ขบทบาท" name: "ชื่à¸à¸šà¸—บาท" description: "คำà¸à¸˜à¸´à¸šà¸²à¸¢à¸šà¸—บาท" permission: "สิทธิ์ตามบทบาท" - descriptionOfPermission: "<b>ผู้ดูà¹à¸¥à¸à¸¥à¸±à¹ˆà¸™à¸à¸£à¸à¸‡à¹€à¸™à¸·à¹‰à¸à¸«à¸²</b> สามารถดำเนินà¸à¸²à¸£à¸”ูà¹à¸¥à¸‚ั้นพื้นà¸à¸²à¸™à¹„ด้นะ\n<b>ผู้ดูà¹à¸¥à¸£à¸°à¸šà¸š</b> สามารถเปลี่ยนà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าทั้งหมดขà¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¹„ด้นะ" + descriptionOfPermission: "<b>ผู้ควบคุม</b> สามารถดำเนินà¸à¸²à¸£à¸”ูà¹à¸¥à¸‚ั้นพื้นà¸à¸²à¸™à¹„ด้\n<b>ผู้ดูà¹à¸¥à¸£à¸°à¸šà¸š</b> สามารถเปลี่ยนà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าทั้งหมดขà¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¹„ด้" assignTarget: "มà¸à¸šà¸«à¸¡à¸²à¸¢" - descriptionOfAssignTarget: "<b>à¹à¸¡à¸™à¸™à¸§à¸¥</b> เพื่à¸à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸œà¸¹à¹‰à¸—ี่เป็นส่วนหนึ่งขà¸à¸‡à¸šà¸—บาทนี้à¹à¸¥à¸°à¹ƒà¸„รที่ไม่ใช่ด้วยตนเà¸à¸‡\n<b>เงื่à¸à¸™à¹„ข</b> เพื่à¸à¹ƒà¸«à¹‰à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¹„ด้รับà¸à¸²à¸£à¸à¸³à¸«à¸™à¸”à¹à¸¥à¸°à¸™à¸³à¸à¸à¸à¸ˆà¸²à¸à¸šà¸—บาทนี้โดยà¸à¸±à¸•à¹‚นมัติตามเงื่à¸à¸™à¹„ขชุดหนึ่ง" + descriptionOfAssignTarget: "à¹à¸šà¸š<b>ปรับเà¸à¸‡</b> เพิ่มถà¸à¸™à¸šà¸—บาทนี้à¹à¸à¹ˆà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸”้วยตัวเà¸à¸‡\nà¹à¸šà¸š<b>มีเงื่à¸à¸™à¹„ข</b> เพิ่มถà¸à¸™à¸šà¸—บาทนี้à¹à¸à¹ˆà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¹‚ดยà¸à¸±à¸•à¹‚นมัติหาà¸à¹€à¸‚้าเงื่à¸à¸™à¹„ขใดต่à¸à¹„ปนี้" manual: "ปรับเà¸à¸‡" + manualRoles: "บทบาทà¹à¸šà¸šà¸—ำมืà¸" conditional: "มีเงื่à¸à¸™à¹„ข" + conditionalRoles: "บทบาทà¹à¸šà¸šà¸¡à¸µà¹€à¸‡à¸·à¹ˆà¸à¸™à¹„ข" condition: "เงื่à¸à¸™à¹„ข" isConditionalRole: "นี่คืà¸à¸šà¸—บาทที่มีเงื่à¸à¸™à¹„ข" - isPublic: "บทบาทสาธารณะ" - descriptionOfIsPublic: "ทุà¸à¸„นสามารถดูได้ว่าผู้ใช้งานนั้นได้รับมà¸à¸šà¸«à¸¡à¸²à¸¢à¸šà¸—บาทด้วยหรืà¸à¹„ม่ \n\nบทบาทจะà¹à¸ªà¸”งในโปรไฟล์ขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸”้วย" + isPublic: "ทำให้บทบาทเปิดเผยต่à¸à¸ªà¸²à¸˜à¸²à¸£à¸“ะ" + descriptionOfIsPublic: "บทบาทจะปราà¸à¸à¸šà¸™à¹‚ปรไฟล์ขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¹à¸¥à¸°à¹€à¸›à¸´à¸”เผยต่à¸à¸ªà¸²à¸˜à¸²à¸£à¸“ะ (ทุà¸à¸„นสามารถเห็นได้ว่าผู้ใช้รายนี้มีบทบาทนี้)" options: "ตัวเลืà¸à¸à¸šà¸—บาท" policies: "นโยบาย" - baseRole: "บทบาทพื้นà¸à¸²à¸™" - useBaseValue: "ใช้บทบาทพื้นà¸à¸²à¸™à¹€à¸£à¸´à¹ˆà¸¡à¸•à¹‰à¸™" + baseRole: "เทมเพลตบทบาท" + useBaseValue: "ใช้ตามเทมเพลตบทบาท" chooseRoleToAssign: "เลืà¸à¸à¸šà¸—บาทที่ต้à¸à¸‡à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”" - iconUrl: "ไà¸à¸„à¸à¸™ URL" + iconUrl: "URL ไà¸à¸„à¸à¸™" asBadge: "à¹à¸ªà¸”งเป็นตรา" - descriptionOfAsBadge: "ไà¸à¸„à¸à¸™à¸‚à¸à¸‡à¸šà¸—บาทนี้จะปราà¸à¸à¸–ัดจาà¸à¸Šà¸·à¹ˆà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‚à¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸”้วยบทบาทนี้ถ้าหาà¸à¹€à¸›à¸´à¸”ใช้งาน" - isExplorable: "บทบาทไทม์ไลน์เป็นà¹à¸šà¸šà¸ªà¸²à¸˜à¸²à¸£à¸“ะ" - descriptionOfIsExplorable: "ไทม์ไลน์ขà¸à¸‡à¸šà¸—บาทนี้จะสามารถเข้าถึงได้à¹à¸šà¸šà¸ªà¸²à¸˜à¸²à¸£à¸“ะถ้าหาà¸à¹€à¸›à¸´à¸”ใช้งาน เส้นเวลาขà¸à¸‡à¸šà¸—บาทนั้นจะไม่ถูà¸à¹€à¸›à¸´à¸”เผยต่à¸à¸ªà¸²à¸˜à¸²à¸£à¸“ะ ถึงà¹à¸¡à¹‰à¸§à¹ˆà¸²à¸ˆà¸°à¹„ม่เปิดเผยต่à¸à¸ªà¸²à¸˜à¸²à¸£à¸“ะà¹à¸¡à¹‰à¹à¸•à¹ˆà¸§à¹ˆà¸²...จะตั้งค่าไว้ยังไงà¸à¹‡à¸•à¸²à¸¡" - displayOrder: "ตำà¹à¸«à¸™à¹ˆà¸‡" - descriptionOfDisplayOrder: "ยิ่งตัวเลขสูง ตำà¹à¸«à¸™à¹ˆà¸‡ UI à¸à¹‡à¸¢à¸´à¹ˆà¸‡à¸ªà¸¹à¸‡à¸‚ึ้นนะ" - canEditMembersByModerator: "à¸à¸™à¸¸à¸à¸²à¸•à¹ƒà¸«à¹‰à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¹à¸à¹‰à¹„ขสมาชิà¸" - descriptionOfCanEditMembersByModerator: "เมื่à¸à¹€à¸›à¸´à¸”ใช้ ผู้ดูà¹à¸¥à¸™à¸à¸à¹€à¸«à¸™à¸·à¸à¸ˆà¸²à¸à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¸£à¸°à¸šà¸šà¹à¸¥à¹‰à¸§ จะสามารถà¸à¸³à¸«à¸™à¸”à¹à¸¥à¸°à¸¢à¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¸¡à¸à¸šà¸«à¸¡à¸²à¸¢à¸šà¸—บาทนี้ให้à¸à¸±à¸šà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¹„ด้ เมื่à¸à¸›à¸´à¸” เฉพาะผู้ดูà¹à¸¥à¸£à¸°à¸šà¸šà¹€à¸—่านั้นที่จะสามารถà¸à¸³à¸«à¸™à¸”ผู้ใช้ได้นะ" + descriptionOfAsBadge: "เมื่à¸à¹€à¸›à¸´à¸”ใช้งาน ไà¸à¸„à¸à¸™à¸šà¸—บาทจะปราà¸à¸à¸–ัดจาà¸à¸Šà¸·à¹ˆà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + isExplorable: "ค้นหาผู้ใช้ได้ง่ายขึ้นโดยดูจาà¸à¸šà¸—บาท" + descriptionOfIsExplorable: "เมื่à¸à¹€à¸›à¸´à¸”ใช้งาน ไทมไลน์บทบาทนี้à¹à¸¥à¸°à¸ªà¸¡à¸²à¸Šà¸´à¸à¸—ี่มีบทบาทนี้จะเปิดเผยเป็นสาธารณะ" + displayOrder: "ลำดับà¸à¸²à¸£à¹à¸ªà¸”งผล" + descriptionOfDisplayOrder: "เลขที่สูงà¸à¸§à¹ˆà¸²à¸ˆà¸°à¹à¸ªà¸”งบน UI à¸à¹ˆà¸à¸™" + canEditMembersByModerator: "à¸à¸™à¸¸à¸à¸²à¸•à¹ƒà¸«à¹‰à¸œà¸¹à¹‰à¸„วบคุมà¹à¸à¹‰à¹„ขสมาชิà¸" + descriptionOfCanEditMembersByModerator: "เมื่à¸à¹€à¸›à¸´à¸”ใช้ นà¸à¸à¹€à¸«à¸™à¸·à¸à¸ˆà¸²à¸à¸œà¸¹à¹‰à¸„วบคุมà¹à¸¥à¸°à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¸£à¸°à¸šà¸šà¹à¸¥à¹‰à¸§ จะสามารถเพิ่มถà¸à¸™à¸šà¸—บาทนี้à¹à¸à¹ˆà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¹„ด้ à¹à¸•à¹ˆà¹€à¸¡à¸·à¹ˆà¸à¸›à¸´à¸”ใช้ จะมีเฉพาะผู้ดูà¹à¸¥à¸£à¸°à¸šà¸šà¹€à¸—่านั้นที่จะสามารถดำเนินà¸à¸²à¸£à¹„ด้" priority: "ลำดับความสำคัà¸" _priority: low: "ต่ำ" @@ -1498,12 +1654,13 @@ _role: _options: gtlAvailable: "à¸à¸²à¸£à¸”ูไทม์ไลน์ทั่วโลà¸" ltlAvailable: "à¸à¸²à¸£à¸”ูไทม์ไลน์ในท้à¸à¸‡à¸–ิ่น" - canPublicNote: "สามารถส่งโน้ตสาธารณะ" + canPublicNote: "สามารถโพสต์à¹à¸šà¸šà¸ªà¸²à¸˜à¸²à¸£à¸“ะ" + mentionMax: "จำนวนà¸à¸²à¸£à¸à¸¥à¹ˆà¸²à¸§à¸–ึงสูงสุดต่à¸à¹‚น้ต" canInvite: "สร้างรหัสเชิà¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ" inviteLimit: "จำà¸à¸±à¸”à¸à¸²à¸£à¹€à¸Šà¸´à¸" - inviteLimitCycle: "จำà¸à¸±à¸”à¸à¸²à¸£à¹€à¸Šà¸´à¸à¹„ว้คูลดาวน์" + inviteLimitCycle: "คูลดาวน์ในà¸à¸²à¸£à¹€à¸Šà¸´à¸" inviteExpirationTime: "วันหมดà¸à¸²à¸¢à¸¸à¸‚à¸à¸‡à¸£à¸«à¸±à¸ªà¸à¸²à¸£à¹€à¸Šà¸´à¸" - canManageCustomEmojis: "จัดà¸à¸²à¸£à¸à¸µà¹‚มจิà¹à¸šà¸šà¸à¸³à¸«à¸™à¸”เà¸à¸‡" + canManageCustomEmojis: "จัดà¸à¸²à¸£à¹€à¸à¹‚มจิที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡" canManageAvatarDecorations: "จัดà¸à¸²à¸£à¸•à¸à¹à¸•à¹ˆà¸‡à¸à¸§à¸•à¸²à¸£" driveCapacity: "ความจุขà¸à¸‡à¹„ดรฟ์" alwaysMarkNsfw: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹„ฟล์ว่าเป็น NSFW เสมà¸" @@ -1515,13 +1672,15 @@ _role: noteEachClipsMax: "จำนวนโน้ตสูงสุดภายในคลิป" userListMax: "จำนวนรายชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸ªà¸¹à¸‡à¸ªà¸¸à¸”" userEachUserListsMax: "จำนวนผู้ใช้สูงสุดภายในรายà¸à¸²à¸£à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" - rateLimitFactor: "ขีดจำà¸à¸±à¸”à¸à¸±à¸•à¸£à¸²" - descriptionOfRateLimitFactor: "ขีดจà¹à¸²à¸à¸±à¸”à¸à¸±à¸•à¸£à¸²à¸—ี่ต่ำà¸à¸§à¹ˆà¸²à¸¡à¸µà¸‚้à¸à¸ˆà¹à¸²à¸à¸±à¸”น้à¸à¸¢à¸à¸§à¹ˆà¸²à¸‚้à¸à¸ˆà¹à¸²à¸à¸±à¸”ที่สูงà¸à¸§à¹ˆà¸²" + rateLimitFactor: "à¸à¸±à¸•à¸£à¸²à¸à¸²à¸£à¸ˆà¸³à¸à¸±à¸”" + descriptionOfRateLimitFactor: "ยิ่งตัวเลขน้à¸à¸¢à¸à¹‡à¸¢à¸´à¹ˆà¸‡à¸ˆà¸³à¸à¸±à¸”น้à¸à¸¢ ยิ่งมาà¸à¸à¹‡à¸¢à¸´à¹ˆà¸‡à¹€à¸‚้มงวดมาà¸à¸‚ึ้น" canHideAds: "ซ่à¸à¸™à¹‚ฆษณา" canSearchNotes: "à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸à¸²à¸£à¸„้นหาโน้ต" canUseTranslator: "à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¹à¸›à¸¥" + avatarDecorationLimit: "จำนวนà¸à¸²à¸£à¸•à¸à¹à¸•à¹ˆà¸‡à¹„à¸à¸„à¸à¸™à¸ªà¸¹à¸‡à¸ªà¸¸à¸”ที่สามารถติดตั้งได้" _condition: - isLocal: "ผู้ใช้ภายใน" + roleAssignedTo: "มà¸à¸šà¸«à¸¡à¸²à¸¢à¹ƒà¸«à¹‰à¸¡à¸µà¸šà¸—บาทà¹à¸šà¸šà¸—ำมืà¸" + isLocal: "ผู้ใช้ในพื้นที่" isRemote: "ผู้ใช้ระยะไà¸à¸¥" createdLessThan: "สร้างน้à¸à¸¢à¸à¸§à¹ˆà¸²" createdMoreThan: "สร้างมาà¸à¸à¸§à¹ˆà¸²" @@ -1535,10 +1694,10 @@ _role: or: "หรืà¸" not: "ไม่" _sensitiveMediaDetection: - description: "ลดความพยายามในà¸à¸²à¸£à¸”ูà¹à¸¥à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸œà¹ˆà¸²à¸™à¸à¸²à¸£à¸ˆà¸”จำสื่ภNSFW โดยà¸à¸±à¸•à¹‚นมัติผ่านà¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸™à¸£à¸¹à¹‰à¸‚à¸à¸‡à¹€à¸„รื่à¸à¸‡ à¸à¸²à¸£à¸—ำสิ่งนี้à¸à¸²à¸ˆà¸ˆà¸°à¹€à¸žà¸´à¹ˆà¸¡à¸ าระบนเซิร์ฟเวà¸à¸£à¹Œà¹€à¸¥à¹‡à¸à¸™à¹‰à¸à¸¢" - sensitivity: "à¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ˆà¸±à¸šà¸„วามไว" - sensitivityDescription: "à¸à¸²à¸£à¸¥à¸”ความไวนั้นจะนำไปสู่à¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ˆà¸±à¸šà¸—ี่ผิดพลาดน้à¸à¸¢à¸¥à¸‡ (ผลบวà¸à¸—ี่ผิดพลาด) à¹à¸•à¹ˆà¹ƒà¸™à¸‚ณะที่à¸à¸²à¸£à¹€à¸žà¸´à¹ˆà¸¡à¸™à¸±à¹‰à¸™à¸ˆà¸°à¸™à¸³à¹„ปสู่à¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸«à¸²à¸—ี่พลาดน้à¸à¸¢à¸¥à¸‡ (ผลลบเท็จ)" - setSensitiveFlagAutomatically: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¹€à¸›à¹‡à¸™ NSFW" + description: "ใช้ Machine Learning เพื่à¸à¸•à¸£à¸§à¸ˆà¸ˆà¸±à¸šà¸ªà¸·à¹ˆà¸à¸—ี่มีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™à¹‚ดยà¸à¸±à¸•à¹‚นมัติà¹à¸¥à¸°à¹ƒà¸Šà¹‰à¹€à¸žà¸·à¹ˆà¸à¸à¸²à¸£à¸à¸¥à¸±à¹ˆà¸™à¸à¸£à¸à¸‡ ภาระขà¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸ˆà¸°à¹€à¸žà¸´à¹ˆà¸¡à¸‚ึ้นเล็à¸à¸™à¹‰à¸à¸¢" + sensitivity: "ความไวในà¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ˆà¸±à¸š" + sensitivityDescription: "เมื่à¸à¸„วามไวต่ำ Misdetection (ผลบวà¸à¸¥à¸§à¸‡) จะลดลง, เมื่à¸à¸„วามไวสูง Missed detection (ผลลบลวง) จะลดลง" + setSensitiveFlagAutomatically: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¡à¸µà¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" setSensitiveFlagAutomaticallyDescription: "ผลลัพธ์ขà¸à¸‡à¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ˆà¸±à¸šà¸ ายในนั้นจะยังคงà¸à¸¢à¸¹à¹ˆ ถึงà¹à¸¡à¹‰à¸§à¹ˆà¸²à¸ˆà¸°à¸›à¸´à¸”ตัวเลืà¸à¸à¸™à¸µà¹‰" analyzeVideos: "เปิดใช้งานวิเคราะห์ขà¸à¸‡à¸§à¸´à¸”ีโà¸" analyzeVideosDescription: "à¸à¸²à¸£à¸§à¸´à¹€à¸„ราะห์วิดีโà¸à¸™à¸à¸à¹€à¸«à¸™à¸·à¸à¸ˆà¸²à¸à¸£à¸¹à¸›à¸ าพนั้น à¸à¸²à¸£à¸—ำสิ่งนี้จะทำให้เพิ่มภาระบนเซิร์ฟเวà¸à¸£à¹Œà¹€à¸¥à¹‡à¸à¸™à¹‰à¸à¸¢" @@ -1548,18 +1707,19 @@ _emailUnavailable: disposable: "ไม่สามารถใช้à¸à¸µà¹€à¸¡à¸¥à¸Šà¸±à¹ˆà¸§à¸„ราวได้" mx: "เซิร์ฟเวà¸à¸£à¹Œà¸à¸µà¹€à¸¡à¸¥à¸™à¸µà¹‰à¹„ม่ถูà¸à¸•à¹‰à¸à¸‡" smtp: "เซิร์ฟเวà¸à¸£à¹Œà¸à¸µà¹€à¸¡à¸¥à¸™à¸µà¹‰à¹„ม่มีà¸à¸²à¸£à¸•à¸à¸šà¸ªà¸™à¸à¸‡" + banned: "คุณไม่สามารถลงทะเบียนด้วยที่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸™à¸µà¹‰à¹„ด้" _ffVisibility: - public: "เผยà¹à¸žà¸£à¹ˆ" + public: "สาธารณะ" followers: "ปราà¸à¸à¹ƒà¸«à¹‰à¹à¸à¹ˆà¸œà¸¹à¹‰à¸•à¸´à¸”ตามเท่านั้น" private: "ส่วนตัว" _signup: - almostThere: "เà¸à¸·à¸à¸šà¸ˆà¸°à¸¡à¸µ" - emailAddressInfo: "โปรดà¸à¸£à¸à¸à¸à¸µà¹€à¸¡à¸¥à¸‚à¸à¸‡à¸„ุณ มันจะไม่เปิดเผยต่à¸à¸ªà¸²à¸˜à¸²à¸£à¸“ะ" - emailSent: "เราได้ส่งà¸à¸µà¹€à¸¡à¸¥à¸¢à¸·à¸™à¸¢à¸±à¸™à¹„ปยังที่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸‚à¸à¸‡à¸„ุณà¹à¸¥à¹‰à¸§à¸™à¸° ({email}) โปรดคลิà¸à¸¥à¸´à¸‡à¸à¹Œà¸—ี่รวมไว้เพื่à¸à¸ªà¸£à¹‰à¸²à¸‡à¸šà¸±à¸à¸Šà¸µà¹ƒà¸«à¹‰à¹€à¸ªà¸£à¹‡à¸ˆà¸ªà¸´à¹‰à¸™" + almostThere: "เà¸à¸·à¸à¸šà¸ˆà¸°à¹€à¸ªà¸£à¹‡à¸ˆà¹à¸¥à¹‰à¸§" + emailAddressInfo: "à¸à¸£à¸¸à¸“าà¸à¸£à¸à¸à¸—ี่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸—ี่คุณใช้ ที่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸‚à¸à¸‡à¸„ุณจะไม่ถูà¸à¹€à¸œà¸¢à¹à¸žà¸£à¹ˆà¸ªà¸¹à¹ˆà¸ªà¸²à¸˜à¸²à¸£à¸“ชน" + emailSent: "à¸à¸µà¹€à¸¡à¸¥à¸¢à¸·à¸™à¸¢à¸±à¸™à¹„ด้ถูà¸à¸ªà¹ˆà¸‡à¹„ปยังที่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸—ี่คุณป้à¸à¸™ ({email}) à¹à¸¥à¹‰à¸§ à¸à¸£à¸¸à¸“าติดตามลิงà¸à¹Œà¹ƒà¸™à¸à¸µà¹€à¸¡à¸¥à¹€à¸žà¸·à¹ˆà¸à¸ªà¸£à¹‰à¸²à¸‡à¸šà¸±à¸à¸Šà¸µà¹ƒà¸«à¹‰à¹€à¸ªà¸£à¹‡à¸ˆà¸ªà¸¡à¸šà¸¹à¸£à¸“์ ลิงà¸à¹Œà¸—ี่ให้ไว้จะหมดà¸à¸²à¸¢à¸¸à¹ƒà¸™ 30 นาที" _accountDelete: accountDelete: "ลบบัà¸à¸Šà¸µà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" mayTakeTime: "เนื่à¸à¸‡à¸ˆà¸²à¸à¸à¸²à¸£à¸¥à¸šà¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¸ˆà¸°à¹€à¸›à¹‡à¸™à¸à¸£à¸°à¸šà¸§à¸™à¸à¸²à¸£à¸—ี่ต้à¸à¸‡à¹ƒà¸Šà¹‰à¸—รัพยาà¸à¸£à¸¡à¸²à¸ จึงà¸à¸²à¸ˆà¸ˆà¸°à¸•à¹‰à¸à¸‡à¹ƒà¸Šà¹‰à¹€à¸§à¸¥à¸²à¸ªà¸±à¸à¸„รู่ถึงจะเสร็จสมบูรณ์ ทั้งนี้ขึ้นà¸à¸¢à¸¹à¹ˆà¸à¸±à¸šà¸ˆà¸³à¸™à¸§à¸™à¹€à¸™à¸·à¹‰à¸à¸«à¸²à¸—ี่คุณสร้างà¹à¸¥à¸°à¸ˆà¸³à¸™à¸§à¸™à¹„ฟล์ที่คุณà¸à¸±à¸›à¹‚หลดนะ" - sendEmail: "เมื่à¸à¸à¸²à¸£à¸¥à¸šà¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹€à¸ªà¸£à¹‡à¸ˆà¸ªà¸´à¹‰à¸™ เราà¸à¸²à¸ˆà¸ˆà¸°à¸ªà¹ˆà¸‡à¸à¸µà¹€à¸¡à¸¥à¹„ปยังที่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸‚à¸à¸‡à¸„ุณที่เคยลงทะเบียนไว้à¸à¸±à¸šà¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¸™à¸°" + sendEmail: "เมื่à¸à¸à¸²à¸£à¸¥à¸šà¸šà¸±à¸à¸Šà¸µà¹€à¸ªà¸£à¹‡à¸ˆà¸ªà¸´à¹‰à¸™ à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸ˆà¸°à¸–ูà¸à¸ªà¹ˆà¸‡à¹„ปยังที่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸—ี่ลงทะเบียนไว้" requestAccountDelete: "ร้à¸à¸‡à¸‚à¸à¹ƒà¸«à¹‰à¸¥à¸šà¸šà¸±à¸à¸Šà¸µ" started: "à¸à¸²à¸£à¸¥à¸šà¹„ด้เริ่มต้นขึ้น" inProgress: "ปัจจุบันà¸à¸³à¸¥à¸±à¸‡à¸”ำเนินà¸à¸²à¸£à¸¥à¸šà¸à¸¢à¸¹à¹ˆ" @@ -1569,7 +1729,9 @@ _ad: hide: "ไม่ต้à¸à¸‡à¹à¸ªà¸”ง" timezoneinfo: "วันในสัปดาห์นี้จะถูà¸à¸à¸³à¸«à¸™à¸”จาà¸à¹‚ซนเวลาขà¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" adsSettings: "ตั้งค่าà¸à¸²à¸£à¹‚ฆษณา" + notesPerOneAd: "à¸à¸±à¸›à¹€à¸”ตช่วงเวลาตำà¹à¸«à¸™à¹ˆà¸‡à¹‚ฆษณาà¹à¸šà¸šà¹€à¸£à¸µà¸¢à¸¥à¹„ทม์ (จำนวนโน้ตต่à¸à¹‚ฆษณา)" setZeroToDisable: "ตั้งค่านี้ให้เป็น 0 เพื่à¸à¸›à¸´à¸”ใช้งานโฆษณาà¸à¸±à¸›à¹€à¸”ตà¹à¸šà¸šà¹€à¸£à¸µà¸¢à¸¥à¹„ทม์" + adsTooClose: "เนื่à¸à¸‡à¸ˆà¸²à¸à¸Šà¹ˆà¸§à¸‡à¹€à¸§à¸¥à¸²à¸à¸²à¸£à¹à¸ªà¸”งโฆษณาสั้นมาภประสบà¸à¸²à¸£à¸“์ผู้ใช้จึงà¸à¸²à¸ˆà¸¥à¸”ลงà¸à¸¢à¹ˆà¸²à¸‡à¸¡à¸²à¸" _forgotPassword: enterEmail: "ป้à¸à¸™à¸—ี่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸—ี่คุณเคยใช้ในà¸à¸²à¸£à¸¥à¸‡à¸—ะเบียนไว้ ลิงà¸à¹Œà¸—ี่คุณสามารถรีเซ็ตรหัสผ่านได้นั้นจะถูà¸à¸ªà¹ˆà¸‡à¹„ปนะ" ifNoEmail: "ถ้าหาà¸à¸„ุณไม่ได้ใช้à¸à¸µà¹€à¸¡à¸¥à¸£à¸°à¸«à¸§à¹ˆà¸²à¸‡à¸à¸²à¸£à¸¥à¸‡à¸—ะเบียน à¸à¸£à¸¸à¸“าติดต่à¸à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¸£à¸°à¸šà¸šà¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¹à¸—นนะ" @@ -1577,8 +1739,8 @@ _forgotPassword: _gallery: my: "à¹à¸à¸¥à¸¥à¸à¸£à¸µà¹ˆà¸‚à¸à¸‡à¸‰à¸±à¸™" liked: "โพสต์ที่ถูà¸à¹ƒà¸ˆ" - like: "ชื่นชà¸à¸š" - unlike: "ลบไลค์" + like: "ถูà¸à¹ƒà¸ˆ!" + unlike: "เลิà¸à¸–ูà¸à¹ƒà¸ˆ" _email: _follow: title: "ได้ติดตามคุณ" @@ -1591,7 +1753,7 @@ _plugin: viewSource: "ดูต้นฉบับ" _preferencesBackups: list: "สร้างà¸à¸²à¸£à¸ªà¸³à¸£à¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥" - saveNew: "บันทึà¸à¹ƒà¸«à¸¡à¹ˆ" + saveNew: "บันทึà¸à¸‚้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡à¹ƒà¸«à¸¡à¹ˆ" loadFile: "โหลดจาà¸à¹„ฟล์" apply: "นำไปใช้à¸à¸±à¸šà¸à¸¸à¸›à¸à¸£à¸“์นี้" save: "บันทึà¸" @@ -1601,8 +1763,8 @@ _preferencesBackups: applyConfirm: "คุณต้à¸à¸‡à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‚้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡ \"{name}\" à¸à¸±à¸šà¸à¸¸à¸›à¸à¸£à¸“์นี้à¸à¸¢à¹ˆà¸²à¸‡à¸‡à¸±à¹‰à¸™à¸ˆà¸£à¸´à¸‡à¸«à¸£à¸ à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าที่มีà¸à¸¢à¸¹à¹ˆà¸‚à¸à¸‡à¸à¸¸à¸›à¸à¸£à¸“์นี้จะถูà¸à¹€à¸‚ียนทับนะ" saveConfirm: "บันทึà¸à¸‚้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡à¹€à¸›à¹‡à¸™ {name} มั้ย?" deleteConfirm: "ลบข้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡ {name} มั้ย?" - renameConfirm: "เปลี่ยนชื่à¸à¸‚้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡à¸™à¸µà¹‰à¸ˆà¸²à¸ \"{old}\" เป็น \"{new}\" หรืà¸à¸›à¹ˆà¸²à¸§" - noBackups: "ไม่มีข้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡à¸™à¸° คุณสามารถสำรà¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าไคลเà¸à¸™à¸•à¹Œà¸‚à¸à¸‡à¸„ุณบนเซิร์ฟเวà¸à¸£à¹Œà¸™à¸µà¹‰à¹‚ดยใช้ \"สร้างà¸à¸²à¸£à¸ªà¸³à¸£à¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥à¹ƒà¸«à¸¡à¹ˆ\"ได้นะ" + renameConfirm: "ต้à¸à¸‡à¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸Šà¸·à¹ˆà¸à¸‚้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡à¸ˆà¸²à¸ “{old}†เป็น “{new}†ใช่ไหม?" + noBackups: "ไม่มีข้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡ สามารถบันทึà¸à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าไคลเà¸à¸™à¸•à¹Œà¸›à¸±à¸ˆà¸ˆà¸¸à¸šà¸±à¸™à¹„ปยังเซิร์ฟเวà¸à¸£à¹Œà¸”้วย “บันทึà¸à¸‚้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡à¹ƒà¸«à¸¡à¹ˆâ€" createdAt: "สร้างเมื่à¸: {date} {time}" updatedAt: "à¸à¸±à¸›à¹€à¸”ตเมื่à¸: {date} {time}" cannotLoad: "à¸à¸²à¸£à¹‚หลดล้มเหลว" @@ -1618,13 +1780,16 @@ _aboutMisskey: contributors: "ผู้สนับสนุนหลัà¸" allContributors: "ผู้มีส่วนร่วมทั้งหมด" source: "ซà¸à¸£à¹Œà¸ªà¹‚ค้ด" + original: "ต้นฉบับ" + thisIsModifiedVersion: "{name} ใช้ Misskey เวà¸à¸£à¹Œà¸Šà¸±à¸™à¸”ัดà¹à¸›à¸¥à¸‡" translation: "à¹à¸›à¸¥à¸ าษา Misskey" donate: "บริจาคให้à¸à¸±à¸š Misskey" - morePatrons: " ขà¸à¸šà¸„ุณทุà¸à¸—่านที่ร่วมà¸à¸±à¸™à¸Šà¹ˆà¸§à¸¢à¹€à¸«à¸¥à¸·à¸à¸•à¸¥à¸à¸”มานะคะ 🥰" - patrons: "สมาชิà¸à¸žà¸±à¸™à¸˜à¸¡à¸´à¸•à¸£" + morePatrons: "à¹à¸¥à¸°à¸à¸µà¸à¸«à¸¥à¸²à¸¢à¸—่านที่ไม่ได้เà¸à¹ˆà¸¢à¸™à¸²à¸¡ ขà¸à¸šà¸„ุณที่ร่วมช่วยเหลืà¸à¸•à¸¥à¸à¸”มานะคะ 🥰" + patrons: "ผู้à¸à¸¸à¸›à¸–ัมภ์" + projectMembers: "สมาชิà¸à¹ƒà¸™à¹‚ครงà¸à¸²à¸£" _displayOfSensitiveMedia: - respect: "ซ่à¸à¸™à¸ªà¸·à¹ˆà¸à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸šà¸à¸à¸§à¹ˆà¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" - ignore: "à¹à¸ªà¸”งผลสื่à¸à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸šà¸à¸à¸§à¹ˆà¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" + respect: "ซ่à¸à¸™à¸ªà¸·à¹ˆà¸à¸—ี่มีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" + ignore: "à¹à¸ªà¸”งสื่à¸à¸—ี่มีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" force: "ซ่à¸à¸™à¸ªà¸·à¹ˆà¸à¸—ั้งหมด" _instanceTicker: none: "ไม่ต้à¸à¸‡à¹à¸ªà¸”ง" @@ -1635,17 +1800,18 @@ _serverDisconnectedBehavior: dialog: "à¹à¸ªà¸”งà¸à¸¥à¹ˆà¸à¸‡à¹‚ต้ตà¸à¸šà¸„ำเตืà¸à¸™" quiet: "à¹à¸ªà¸”งคำเตืà¸à¸™à¸—ี่ไม่เป็นà¸à¸²à¸£à¸£à¸šà¸à¸§à¸™" _channel: - create: "สร้างà¹à¸Šà¸™à¹à¸™à¸¥à¹ƒà¸«à¸¡à¹ˆ" - edit: "à¹à¸à¹‰à¹„ขà¹à¸Šà¸™à¹à¸™à¸¥" + create: "สร้างช่à¸à¸‡à¹ƒà¸«à¸¡à¹ˆ" + edit: "à¹à¸à¹‰à¹„ขช่à¸à¸‡" setBanner: "เซตà¹à¸šà¸™à¹€à¸™à¸à¸£à¹Œ" removeBanner: "ลบà¹à¸šà¸™à¹€à¸™à¸à¸£à¹Œ" featured: "เทรนด์" owned: "เจ้าขà¸à¸‡" following: "ติดตามà¹à¸¥à¹‰à¸§" usersCount: "{n} ผู้เข้าร่วม" - notesCount: "{n} โน้ต" + notesCount: "มี {n} โน้ต" nameAndDescription: "ชื่à¸à¹à¸¥à¸°à¸„ำà¸à¸˜à¸´à¸šà¸²à¸¢" nameOnly: "ชื่à¸à¹€à¸—่านั้น" + allowRenoteToExternal: "à¸à¸™à¸¸à¸à¸²à¸•à¹ƒà¸«à¹‰à¸£à¸µà¹‚น้ตà¹à¸¥à¸°à¸à¹‰à¸²à¸‡à¸à¸´à¸‡à¸™à¸à¸à¸Šà¹ˆà¸à¸‡à¹„ด้" _menuDisplay: sideFull: "ด้านข้าง" sideIcon: "ด้านข้าง (ไà¸à¸„à¸à¸™)" @@ -1658,7 +1824,7 @@ _wordMute: _instanceMute: instanceMuteDescription: "à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¸ˆà¸°à¸›à¸´à¸”เสียง\"โน้ต/รีโน้ต\"จาà¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸—ี่à¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¸£à¸²à¸¢à¸à¸²à¸£ รวมถึงบันทึà¸à¸‚à¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่ตà¸à¸šà¸à¸¥à¸±à¸šà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸ˆà¸²à¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸—ี่ปิดเสียง" instanceMuteDescription2: "คั่นด้วยà¸à¸²à¸£à¸‚ึ้นบรรทัดใหม่" - title: "ซ่à¸à¸™à¹‚น้ตจาà¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸—ี่มีà¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¸£à¸²à¸¢à¸à¸²à¸£" + title: "ซ่à¸à¸™à¹‚น้ตจาà¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸—ี่มีà¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¸£à¸²à¸¢à¸Šà¸·à¹ˆà¸" heading: "รายชื่à¸à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸—ี่ถูà¸à¸›à¸´à¸”เสียง" _theme: explore: "สำรวจธีม" @@ -1691,8 +1857,8 @@ _theme: importInfo: "ถ้าหาà¸à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸›à¹‰à¸à¸™à¹‚ค้ดที่นี่ คุณยังสามารถนำเข้าไปยังโปรà¹à¸à¸£à¸¡à¹à¸à¹‰à¹„ขธีมได้" deleteConstantConfirm: "คุณต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¸„่าคงที่ {const} หรืà¸à¸›à¹ˆà¸²à¸§?" keys: - accent: "เน้น" - bg: "ภาพพื้นหลัง" + accent: "สีหลัà¸" + bg: "พื้นหลัง" fg: "ข้à¸à¸„วาม" focus: "โฟà¸à¸±à¸ª" indicator: "ตัวบ่งชี้" @@ -1728,15 +1894,23 @@ _theme: wallpaperOverlay: "วà¸à¸¥à¸¥à¹Œà¹€à¸›à¹€à¸›à¸à¸£à¹Œà¸‹à¹‰à¸à¸™à¸—ับ" badge: "ตรา" messageBg: "พื้นหลังà¹à¸Šà¸—" - accentDarken: "เน้น (มืด)" - accentLighten: "เน้น (สว่าง)" + accentDarken: "สีหลัภ(มืด)" + accentLighten: "สีหลัภ(สว่าง)" fgHighlighted: "ข้à¸à¸„วามที่ไฮไลต์" _sfx: - note: "หมายเหตุ" + note: "โน้ต" noteMy: "โน้ตขà¸à¸‡à¸•à¸±à¸§à¹€à¸à¸‡" notification: "à¸à¸²à¸£à¹€à¹€à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™" antenna: "เสาà¸à¸²à¸à¸²à¸¨" channel: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸Šà¹ˆà¸à¸‡" + reaction: "เมื่à¸à¹€à¸¥à¸·à¸à¸à¸£à¸µà¹à¸à¸„ชั่น" +_soundSettings: + driveFile: "ใช้เสียงจาà¸à¹„ดรฟ์" + driveFileWarn: "เลืà¸à¸à¹„ฟล์ในไดรฟ์ขà¸à¸‡à¸„ุณ" + driveFileTypeWarn: "ไม่รà¸à¸‡à¸£à¸±à¸šà¹„ฟล์นี้" + driveFileTypeWarnDescription: "à¸à¸£à¸¸à¸“าเลืà¸à¸à¹„ฟล์เสียง" + driveFileDurationWarn: "เสียงยาวเà¸à¸´à¸™à¹„ป" + driveFileDurationWarnDescription: "à¸à¸²à¸£à¹ƒà¸Šà¹‰à¹€à¸ªà¸µà¸¢à¸‡à¸—ี่ยาวà¸à¸²à¸ˆà¸£à¸šà¸à¸§à¸™à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ Misskey, ต้à¸à¸‡à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸•à¹ˆà¸à¸«à¸£à¸·à¸à¹„ม่?" _ago: future: "à¸à¸™à¸²à¸„ต" justNow: "เมื่à¸à¸à¸µà¹Šà¸™à¸µà¹‰" @@ -1748,6 +1922,14 @@ _ago: monthsAgo: "{n} เดืà¸à¸™à¸—ี่à¹à¸¥à¹‰à¸§" yearsAgo: "{n} ปีที่ผ่านมา" invalid: "ไม่พบผลลัพธ์" +_timeIn: + seconds: "ใน {n} วินาที" + minutes: "ใน {n} นาที" + hours: "ใน {n} ชั่วโมง" + days: "ใน {n} วัน" + weeks: "ใน {n} สัปดาห์" + months: "ใน {n} เดืà¸à¸™" + years: "ใน {n} ปี" _time: second: "วินาที" minute: "นาที" @@ -1777,7 +1959,9 @@ _2fa: renewTOTPConfirm: "วิธีà¸à¸²à¸£à¹à¸šà¸šà¸™à¸µà¹‰à¸ˆà¸°à¸—à¹à¸²à¹ƒà¸«à¹‰à¸£à¸«à¸±à¸ªà¸¢à¸·à¸™à¸¢à¸±à¸™à¸ˆà¸²à¸à¹à¸à¸žà¸à¹ˆà¸à¸™à¸«à¸™à¹‰à¸²à¸‚à¸à¸‡à¸„ุณหยุดทà¹à¸²à¸‡à¸²à¸™à¹€à¸¥à¸¢à¸™à¸°" renewTOTPOk: "ตั้งค่าคà¸à¸™à¸Ÿà¸´à¸à¹ƒà¸«à¸¡à¹ˆ" renewTOTPCancel: "ไม่เป็นไร" + checkBackupCodesBeforeCloseThisWizard: "โปรดตรวจสà¸à¸šà¸£à¸«à¸±à¸ªà¸ªà¸³à¸£à¸à¸‡à¸”้านล่างà¸à¹ˆà¸à¸™à¸—ี่จะปิดวิซาร์ดนี้" backupCodes: "รหัสสำรà¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥" + backupCodesDescription: "หาà¸à¹à¸à¸›à¸¢à¸·à¸™à¸¢à¸±à¸™à¸•à¸±à¸§à¸•à¸™à¸‚à¸à¸‡à¸„ุณไม่พร้à¸à¸¡à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ คุณสามารถใช้รหัสสำรà¸à¸‡à¸”้านล่างเพื่à¸à¹€à¸‚้าถึงบัà¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณได้ à¸à¸¢à¹ˆà¸²à¸¥à¸·à¸¡à¹€à¸à¹‡à¸šà¸£à¸«à¸±à¸ªà¹€à¸«à¸¥à¹ˆà¸²à¸™à¸µà¹‰à¹„ว้ในที่ปลà¸à¸”ภัย à¹à¸•à¹ˆà¸¥à¸°à¸£à¸«à¸±à¸ªà¸ªà¸²à¸¡à¸²à¸£à¸–ใช้ได้เพียงครั้งเดียวเท่านั้น" backupCodeUsedWarning: "มีà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸£à¸«à¸±à¸ªà¸ªà¸³à¸£à¸à¸‡à¹à¸¥à¹‰à¸§ โปรดà¸à¸£à¸¸à¸“าà¸à¸³à¸«à¸™à¸”ค่าà¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ªà¸à¸šà¸ªà¸´à¸—ธิ์à¹à¸šà¸šà¸ªà¸à¸‡à¸›à¸±à¸ˆà¸ˆà¸±à¸¢à¹‚ดยเร็วที่สุดถ้าหาà¸à¸„ุณยังไม่สามารถใช้งานได้à¸à¸µà¸" backupCodesExhaustedWarning: "รหัสสำรà¸à¸‡à¸—ั้งหมดถูà¸à¹ƒà¸Šà¹‰à¹à¸¥à¹‰à¸§ ถ้าหาà¸à¸„ุณยังสูà¸à¹€à¸ªà¸µà¸¢à¸à¸²à¸£à¹€à¸‚้าถึงà¹à¸à¸›à¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ªà¸à¸šà¸ªà¸´à¸—ธิ์à¹à¸šà¸šà¸ªà¸à¸‡à¸›à¸±à¸ˆà¸ˆà¸±à¸¢à¸„ุณจะยังไม่สามารถเข้าถึงบัà¸à¸Šà¸µà¸™à¸µà¹‰à¹„ด้ à¸à¸£à¸¸à¸“าà¸à¸³à¸«à¸™à¸”ค่าà¸à¸²à¸£à¸£à¸±à¸šà¸£à¸à¸‡à¸„วามถูà¸à¸•à¹‰à¸à¸‡à¸”้วยà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸ªà¸à¸‡à¸Šà¸±à¹‰à¸™" _permissions: @@ -1798,29 +1982,78 @@ _permissions: "write:notes": "เขียนหรืà¸à¸¥à¸šà¹‚น้ต" "read:notifications": "ดูà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸‚à¸à¸‡à¸„ุณ" "write:notifications": "จัดà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸‚à¸à¸‡à¸„ุณ" - "read:reactions": "ดูปà¸à¸´à¸à¸´à¸£à¸´à¸¢à¸²à¸‚à¸à¸‡à¸„ุณ" - "write:reactions": "à¹à¸à¹‰à¹„ขปà¸à¸´à¸à¸´à¸£à¸´à¸¢à¸²à¸‚à¸à¸‡à¸„ุณ" + "read:reactions": "ดูรีà¹à¸à¸„ชั่นขà¸à¸‡à¸„ุณ" + "write:reactions": "à¹à¸à¹‰à¹„ขรีà¹à¸à¸„ชั่นขà¸à¸‡à¸„ุณ" "write:votes": "โหวตบนสำรวจความคิดเห็น" - "read:pages": "ดูหน้า" + "read:pages": "ดูหน้าเพจ" "write:pages": "à¹à¸à¹‰à¹„ขหรืà¸à¸¥à¸šà¹€à¸žà¸ˆà¸‚à¸à¸‡à¸„ุณ" - "read:page-likes": "ดูไลค์ขà¸à¸‡à¸„ุณบนเพจ" - "write:page-likes": "à¹à¸à¹‰à¹„ขà¸à¸²à¸£à¸–ูà¸à¹ƒà¸ˆà¸‚à¸à¸‡à¸„ุณบนเพจ" + "read:page-likes": "ดูรายà¸à¸²à¸£à¹€à¸žà¸ˆà¸—ี่ถูà¸à¹ƒà¸ˆà¹„ว้" + "write:page-likes": "à¹à¸à¹‰à¹„ขรายà¸à¸²à¸£à¹€à¸žà¸ˆà¸—ี่ถูà¸à¹ƒà¸ˆ" "read:user-groups": "ดูà¸à¸¥à¸¸à¹ˆà¸¡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‚à¸à¸‡à¸„ุณ" "write:user-groups": "à¹à¸à¹‰à¹„ขหรืà¸à¸¥à¸šà¸à¸¥à¸¸à¹ˆà¸¡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‚à¸à¸‡à¸„ุณ" "read:channels": "ดูà¹à¸Šà¸™à¹à¸™à¸¥à¸‚à¸à¸‡à¸„ุณ" "write:channels": "à¹à¸à¹‰à¹„ขà¹à¸Šà¸™à¹à¸™à¸¥à¸‚à¸à¸‡à¸„ุณ" "read:gallery": "ดูà¹à¸à¸¥à¹€à¸¥à¸à¸£à¸µà¹ˆ" "write:gallery": "à¹à¸à¹‰à¹„ขà¹à¸à¸¥à¹€à¸¥à¸à¸£à¸µà¹ˆà¸‚à¸à¸‡à¸„ุณ" - "read:gallery-likes": "ดูรายà¸à¸²à¸£à¹‚พสต์ในà¹à¸à¸¥à¹€à¸¥à¸à¸£à¸µà¸—ี่ชà¸à¸šà¸‚à¸à¸‡à¸„ุณ" - "write:gallery-likes": "à¹à¸à¹‰à¹„ขรายà¸à¸²à¸£à¹‚พสต์ในà¹à¸à¸¥à¹€à¸¥à¸à¸£à¸µà¸—ี่ชà¸à¸šà¸‚à¸à¸‡à¸„ุณ" - "read:flash": "วิว เพลย์" - "write:flash": "à¹à¸à¹‰à¹„ขเพลย์" - "read:flash-likes": "ดูรายชื่à¸à¸‚à¸à¸‡à¹„ลค์ เพลย์" - "write:flash-likes": "à¹à¸à¹‰à¹„ขรายชื่à¸à¸‚à¸à¸‡à¹„ลค์ เพลย์" + "read:gallery-likes": "ดูรายà¸à¸²à¸£à¹‚พสต์à¹à¸à¸¥à¹€à¸¥à¸à¸£à¸µà¸—ี่ถูà¸à¹ƒà¸ˆà¹„ว้" + "write:gallery-likes": "à¹à¸à¹‰à¹„ขรายà¸à¸²à¸£à¹‚พสต์à¹à¸à¸¥à¹€à¸¥à¸à¸£à¸µà¸—ี่ถูà¸à¹ƒà¸ˆà¹„ว้" + "read:flash": "ดู Play" + "write:flash": "à¹à¸à¹‰à¹„ข Play" + "read:flash-likes": "ดูรายà¸à¸²à¸£ play ที่ถูà¸à¹ƒà¸ˆà¹„ว้" + "write:flash-likes": "à¹à¸à¹‰à¹„ขรายà¸à¸²à¸£ play ที่ถูà¸à¹ƒà¸ˆà¹„ว้" + "read:admin:abuse-user-reports": "ดูรายงานจาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + "write:admin:delete-account": "ลบบัà¸à¸Šà¸µà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + "write:admin:delete-all-files-of-a-user": "ลบไฟล์ทั้งหมดขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + "read:admin:index-stats": "ดูข้à¸à¸¡à¸¹à¸¥à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸”ัชนีà¸à¸²à¸™à¸‚้à¸à¸¡à¸¹à¸¥" + "read:admin:table-stats": "ดูข้à¸à¸¡à¸¹à¸¥à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸•à¸²à¸£à¸²à¸‡à¸à¸²à¸™à¸‚้à¸à¸¡à¸¹à¸¥" + "read:admin:user-ips": "ดูที่à¸à¸¢à¸¹à¹ˆ IP ขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + "read:admin:meta": "ดูข้à¸à¸¡à¸¹à¸¥à¹€à¸¡à¸•à¸²à¸‚à¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ" + "write:admin:reset-password": "รีเซ็ตรหัสผ่านขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + "write:admin:resolve-abuse-user-report": "à¹à¸à¹‰à¹„ขรายงานจาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + "write:admin:send-email": "ส่งà¸à¸µà¹€à¸¡à¸¥" + "read:admin:server-info": "ดูข้à¸à¸¡à¸¹à¸¥à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ" + "read:admin:show-moderation-log": "ดูปูมà¸à¸²à¸£à¹à¸à¹‰à¹„ข" + "read:admin:show-user": "ดูข้à¸à¸¡à¸¹à¸¥à¸ªà¹ˆà¸§à¸™à¸•à¸±à¸§à¸‚à¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + "read:admin:show-users": "ดูข้à¸à¸¡à¸¹à¸¥à¸ªà¹ˆà¸§à¸™à¸•à¸±à¸§à¸‚à¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + "write:admin:suspend-user": "ระงับผู้ใช้" + "write:admin:unset-user-avatar": "ลบà¸à¸§à¸•à¸²à¸£à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + "write:admin:unset-user-banner": "ลบà¹à¸šà¸™à¹€à¸™à¸à¸£à¹Œà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + "write:admin:unsuspend-user": "ยà¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¸£à¸°à¸‡à¸±à¸šà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + "write:admin:meta": "จัดà¸à¸²à¸£à¸‚้à¸à¸¡à¸¹à¸¥à¹€à¸¡à¸•à¸²à¸‚à¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œ" + "write:admin:user-note": "จัดà¸à¸²à¸£à¹‚น้ตà¸à¸²à¸£à¸à¸¥à¸±à¹ˆà¸™à¸à¸£à¸à¸‡" + "write:admin:roles": "จัดà¸à¸²à¸£à¸šà¸—บาท" + "read:admin:roles": "ดูบทบาท" + "write:admin:relays": "จัดà¸à¸²à¸£à¸£à¸µà¹€à¸¥à¸¢à¹Œ" + "read:admin:relays": "ดูรีเลย์" + "write:admin:invite-codes": "จัดà¸à¸²à¸£à¸£à¸«à¸±à¸ªà¹€à¸Šà¸´à¸" + "read:admin:invite-codes": "ดูรหัสเชิà¸" + "write:admin:announcements": "จัดà¸à¸²à¸£à¸›à¸£à¸°à¸à¸²à¸¨" + "read:admin:announcements": "ดูประà¸à¸²à¸¨" + "write:admin:avatar-decorations": "จัดà¸à¸²à¸£à¸à¸²à¸£à¸•à¸à¹à¸•à¹ˆà¸‡à¸à¸§à¸•à¸²à¸£" + "read:admin:avatar-decorations": "ดูà¸à¸²à¸£à¸•à¸à¹à¸•à¹ˆà¸‡à¸à¸§à¸•à¸²à¸£" + "write:admin:federation": "จัดà¸à¸²à¸£à¸‚้à¸à¸¡à¸¹à¸¥à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸ªà¸«à¸žà¸±à¸™à¸˜à¹Œ" + "write:admin:account": "จัดà¸à¸²à¸£à¸šà¸±à¸à¸Šà¸µà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + "read:admin:account": "ดูข้à¸à¸¡à¸¹à¸¥à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + "write:admin:emoji": "จัดà¸à¸²à¸£à¹€à¸à¹‚มจิ" + "read:admin:emoji": "ดูเà¸à¹‚มจิ" + "write:admin:queue": "จัดà¸à¸²à¸£à¸„ิวงาน" + "read:admin:queue": "ดูข้à¸à¸¡à¸¹à¸¥à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸„ิวงาน" + "write:admin:promo": "จัดà¸à¸²à¸£à¹‚น้ตโปรโมชั่น" + "write:admin:drive": "จัดà¸à¸²à¸£à¹„ดรฟ์ขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + "read:admin:drive": "ดูข้à¸à¸¡à¸¹à¸¥à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¹„ดรฟ์ขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + "read:admin:stream": "ใช้ Websocket API สำหรับผู้ดูà¹à¸¥à¸£à¸°à¸šà¸š" + "write:admin:ad": "จัดà¸à¸²à¸£à¹‚ฆษณา" + "read:admin:ad": "ดูโฆษณา" + "write:invite-codes": "สร้างรหัสเชิà¸" + "read:invite-codes": "รับรหัสเชิà¸" + "write:clip-favorite": "ควบคุมà¸à¸²à¸£à¸–ูà¸à¹ƒà¸ˆà¸‚à¸à¸‡à¸„ลิป" + "read:clip-favorite": "ดูà¸à¸²à¸£à¸–ูà¸à¹ƒà¸ˆà¸‚à¸à¸‡à¸„ลิป" + "read:federation": "รับข้à¸à¸¡à¸¹à¸¥à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸ªà¸«à¸žà¸±à¸™à¸˜à¹Œ" + "write:report-abuse": "รายงานà¸à¸²à¸£à¸¥à¸°à¹€à¸¡à¸´à¸”" _auth: shareAccessTitle: "à¸à¸²à¸£à¹ƒà¸«à¹‰à¸ªà¸´à¸—ธิ์à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชัน" shareAccess: "คุณต้à¸à¸‡à¸à¸²à¸£à¸à¸™à¸¸à¸à¸²à¸•à¹ƒà¸«à¹‰ \"{name}\" เข้าถึงบัà¸à¸Šà¸µà¸™à¸µà¹‰à¹€à¸¥à¸¢à¸¡à¸±à¹‰à¸¢?" - shareAccessAsk: "คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¸ˆà¸£à¸´à¸‡à¹†à¸«à¸£à¸à¸§à¹ˆà¸²à¸•à¹‰à¸à¸‡à¸à¸²à¸£à¸à¸™à¸¸à¸à¸²à¸•à¹ƒà¸«à¹‰à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันนี้เข้าถึงบัà¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณà¹à¸™à¹ˆà¹ƒà¸ˆà¹à¸¥à¹‰à¸§à¸«à¸£à¸?" + shareAccessAsk: "ต้à¸à¸‡à¸à¸²à¸£à¸à¸™à¸¸à¸à¸²à¸•à¹ƒà¸«à¹‰à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันนี้เข้าถึงบัà¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณหรืà¸à¹„ม่?" permission: "{name} ได้ขà¸à¸ªà¸´à¸—ธิ์à¸à¸²à¸£à¹€à¸‚้าถึงดังต่à¸à¹„ปนี้" permissionAsk: "à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันนี้ขà¸à¸ªà¸´à¸—ธิ์ดังต่à¸à¹„ปนี้" pleaseGoBack: "à¸à¸£à¸¸à¸“าà¸à¸¥à¸±à¸šà¹„ปที่à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชัน" @@ -1856,7 +2089,7 @@ _widgets: photos: "รูปภาพ" digitalClock: "นาฬิà¸à¸²à¸”ิจิตà¸à¸¥" unixClock: "นาฬิà¸à¸² UNIX" - federation: "Fediration" + federation: "สหพันธ์" instanceCloud: "à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸„ลาวด์" postForm: "à¹à¸šà¸šà¸Ÿà¸à¸£à¹Œà¸¡à¸à¸²à¸£à¹‚พสต์" slideshow: "à¹à¸ªà¸”งภาพนิ่ง" @@ -1864,13 +2097,14 @@ _widgets: onlineUsers: "ผู้ใช้ที่à¸à¸à¸™à¹„ลน์" jobQueue: "คิวงาน" serverMetric: "ตัวชี้วัดเซิร์ฟเวà¸à¸£à¹Œ" - aiscript: "AiScript คà¸à¸™à¹‚ซล" - aiscriptApp: "AiScript à¹à¸à¸ž" + aiscript: " คà¸à¸™à¹‚ซล AiScript" + aiscriptApp: "à¹à¸à¸› AiScript" aichan: "ไà¸" userList: "รายชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" _userList: - chooseList: "เลืà¸à¸à¸£à¸²à¸¢à¸à¸²à¸£" + chooseList: "เลืà¸à¸à¸£à¸²à¸¢à¸Šà¸·à¹ˆà¸" clicker: "คลิà¸à¹€à¸à¸à¸£à¹Œ" + birthdayFollowings: "วันเà¸à¸´à¸”ผู้ใช้ในวันนี้" _cw: hide: "ซ่à¸à¸™" show: "โหลดเพิ่มเติม" @@ -1878,15 +2112,15 @@ _cw: files: "{count} ไฟล์" _poll: noOnlyOneChoice: "จำเป็นต้à¸à¸‡à¸¡à¸µà¸à¸¢à¹ˆà¸²à¸‡à¸™à¹‰à¸à¸¢à¸ªà¸à¸‡à¸•à¸±à¸§à¹€à¸¥à¸·à¸à¸" - choiceN: "ตัวเลืà¸à¸ {n}" - noMore: "คุณไม่สามารถเพิ่มตัวเลืà¸à¸à¸à¸·à¹ˆà¸™à¹„ด้" + choiceN: "ตัวเลืà¸à¸à¸—ี่ {n}" + noMore: "เพิ่มตัวเลืà¸à¸à¸à¸µà¸à¹„ม่ได้à¹à¸¥à¹‰à¸§" canMultipleVote: "สามารถตà¸à¸šà¹„ด้หลายคำตà¸à¸š" - expiration: "สิ้นสุดà¸à¸²à¸£à¸ªà¸³à¸£à¸§à¸ˆà¸„วามคิดเห็น" - infinite: "ไม่ต้à¸à¸‡à¹€à¸¥à¸¢" - at: "จบที่..." - after: "สิ้นสุดหลัง..." + expiration: "สิ้นสุดโพล" + infinite: "ไม่à¸à¸³à¸«à¸™à¸”ระยะเวลา" + at: "ระบุวันเวลา" + after: "ระบุระยะเวลา" deadlineDate: "วันสิ้นสุด" - deadlineTime: "ชั่วโมง" + deadlineTime: "เวลา" duration: "ระยะเวลา" votesCount: "{n} คะà¹à¸™à¸™à¹€à¸ªà¸µà¸¢à¸‡" totalVotes: "{n} คะà¹à¸™à¸™à¹€à¸ªà¸µà¸¢à¸‡à¸—ั้งหมด" @@ -1894,37 +2128,37 @@ _poll: showResult: "ดูผลลัพธ์" voted: "โหวตà¹à¸¥à¹‰à¸§" closed: "สิ้นสุดà¹à¸¥à¹‰à¸§" - remainingDays: "{d} วัน(s) {h} ชั่วโมง(s) ที่เหลืà¸à¸à¸¢à¸¹à¹ˆ" - remainingHours: "{h} ชั่วโมง(s) {m} นาที(s) ที่เหลืà¸à¸à¸¢à¸¹à¹ˆ" - remainingMinutes: "{m} นาที(s) {s} วินาที(s) ที่เหลืà¸à¸à¸¢à¸¹à¹ˆ" - remainingSeconds: "{s} นาที(s) ที่เหลืà¸à¸à¸¢à¸¹à¹ˆ" + remainingDays: "เหลืà¸à¸à¸µà¸ {d} วัน {h} ชั่วโมง" + remainingHours: "เหลืà¸à¸à¸µà¸ {h} ชั่วโมง {m} นาที" + remainingMinutes: "เหลืà¸à¸à¸µà¸ {m} นาที {s} วินาที" + remainingSeconds: "เหลืà¸à¸à¸µà¸ {s} วินาที" _visibility: public: "สาธารณะ" publicDescription: "โน้ตขà¸à¸‡à¸„ุณจะปราà¸à¸à¹à¸à¹ˆà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ุà¸à¸„น" home: "หน้าà¹à¸£à¸" homeDescription: "โพสลงไทม์ไลน์ที่บ้านเท่านั้น" followers: "ผู้ติดตาม" - followersDescription: "ทำให้ผู้ติดตามนั้นมà¸à¸‡à¹€à¸«à¹‡à¸™à¹à¸„่คุณเท่านั้น" + followersDescription: "เฉพาะผู้ติดตามเท่านั้นที่มà¸à¸‡à¹€à¸«à¹‡à¸™à¹„ด้" specified: "ไดเร็ค" specifiedDescription: "ทำให้มà¸à¸‡à¹€à¸«à¹‡à¸™à¹„ด้เฉพาะผู้ใช้ที่ระบุเท่านั้น" - disableFederation: "ไม่มีสหภาพ" + disableFederation: "ไม่มีสหพันธ์" disableFederationDescription: "à¸à¸¢à¹ˆà¸²à¸ªà¹ˆà¸‡à¹„ปยังà¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸à¸·à¹ˆà¸™" _postForm: replyPlaceholder: "ตà¸à¸šà¸à¸¥à¸±à¸šà¹‚น้ตนี้..." quotePlaceholder: "à¸à¹‰à¸²à¸‡à¹‚น้ตนี้..." channelPlaceholder: "โพสต์ลงช่à¸à¸‡..." _placeholders: - a: "คุณเป็นà¸à¸°à¹„รไปหรà¸?" - b: "เà¸à¸´à¸”à¸à¸°à¹„รขึ้นรà¸à¸šà¸•à¸±à¸§à¸„ุณ?" - c: "คุณà¸à¸³à¸¥à¸±à¸‡à¸„ิดà¸à¸°à¹„รà¸à¸¢à¸¹à¹ˆ?" - d: "คุณต้à¸à¸‡à¸à¸²à¸£à¸ˆà¸°à¸žà¸¹à¸”à¸à¸°à¹„ร?" - e: "เริ่มเขียน..." + a: "ตà¸à¸™à¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¸¢à¸±à¸‡à¹„งบ้าง?" + b: "มีà¸à¸°à¹„รเà¸à¸´à¸”ขึ้นหรืà¸à¹€à¸›à¸¥à¹ˆà¸²?" + c: "à¸à¸³à¸¥à¸±à¸‡à¸„ิดà¸à¸°à¹„รà¸à¸¢à¸¹à¹ˆ?" + d: "ต้à¸à¸‡à¸à¸²à¸£à¸ˆà¸°à¸žà¸¹à¸”à¸à¸°à¹„รไหม?" + e: "มาเขียนà¸à¸±à¸™à¹€à¸–à¸à¸°" f: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸à¹ƒà¸«à¹‰à¸„ุณเขียน..." _profile: name: "ชื่à¸" username: "ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" - description: "ประวัติ" - youCanIncludeHashtags: "คุณยังสามารถใส่à¹à¸®à¸Šà¹à¸—็à¸à¹ƒà¸™à¸›à¸£à¸°à¸§à¸±à¸•à¸´à¸‚à¸à¸‡à¸„ุณได้นะ" + description: "à¹à¸™à¸°à¸™à¸³à¸•à¸±à¸§" + youCanIncludeHashtags: "คุณสามารถใส่à¹à¸®à¸Šà¹à¸—็à¸à¹ƒà¸™à¸ªà¹ˆà¸§à¸™à¹à¸™à¸°à¸™à¸³à¸•à¸±à¸§à¸‚à¸à¸‡à¸„ุณได้" metadata: "ข้à¸à¸¡à¸¹à¸¥à¹€à¸žà¸´à¹ˆà¸¡à¹€à¸•à¸´à¸¡" metadataEdit: "à¹à¸à¹‰à¹„ขข้à¸à¸¡à¸¹à¸¥à¹€à¸žà¸´à¹ˆà¸¡à¹€à¸•à¸´à¸¡" metadataDescription: "ใช้สิ่งเหล่านี้ คุณสามารถà¹à¸ªà¸”งฟิลด์ข้à¸à¸¡à¸¹à¸¥à¹€à¸žà¸´à¹ˆà¸¡à¹€à¸•à¸´à¸¡à¹ƒà¸™à¹‚ปรไฟล์ขà¸à¸‡à¸„ุณ" @@ -1932,14 +2166,16 @@ _profile: metadataContent: "เนื้à¸à¸«à¸²" changeAvatar: "เปลี่ยนà¸à¸§à¸²à¸•à¸²à¸£à¹Œ" changeBanner: "เปลี่ยนà¹à¸šà¸™à¹€à¸™à¸à¸£à¹Œ" - verifiedLinkDescription: "โดยà¸à¸²à¸£à¸›à¹‰à¸à¸™ URL ที่มีลิงà¸à¹Œà¹„ปยังโปรไฟล์ขà¸à¸‡à¸„ุณตรงนี้ ส่วนไà¸à¸„à¸à¸™à¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸„วามเป็นเจ้าขà¸à¸‡à¸™à¸±à¹‰à¸™à¸à¹‡à¸ªà¸²à¸¡à¸²à¸£à¸–à¹à¸ªà¸”งถัดจาà¸à¸Ÿà¸´à¸¥à¸”์ได้นะ" + verifiedLinkDescription: "หาà¸à¸›à¹‰à¸à¸™ URL ที่มีลิงà¸à¹Œà¹„ปยังโปรไฟล์ขà¸à¸‡à¸„ุณ ไà¸à¸„à¸à¸™à¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸„วามเป็นเจ้าขà¸à¸‡à¸ˆà¸°à¹à¸ªà¸”งถัดจาà¸à¸Ÿà¸´à¸¥à¸”์นั้น ๆ" + avatarDecorationMax: "คุณสามารถเพิ่มà¸à¸²à¸£à¸•à¸à¹à¸•à¹ˆà¸‡à¹„ด้สูงสุด {max}" _exportOrImport: allNotes: "โน้ตทั้งหมด" - favoritedNotes: "บันทึà¸à¸—ี่ชื่นชà¸à¸š" + favoritedNotes: "โน้ตที่ถูà¸à¹ƒà¸ˆà¹„ว้" + clips: "คลิป" followingList: "à¸à¸³à¸¥à¸±à¸‡à¸•à¸´à¸”ตาม" muteList: "ปิดเสียง" blockingList: "บล็à¸à¸„" - userLists: "รายà¸à¸²à¸£" + userLists: "รายชื่à¸" excludeMutingUsers: "ยà¸à¹€à¸§à¹‰à¸™à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่ปิดเสียง" excludeInactiveUsers: "ยà¸à¹€à¸§à¹‰à¸™à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่ไม่ได้ใช้งาน" withReplies: "รวมà¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¸ˆà¸²à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่นำเข้าไว้ในไทม์ไลน์" @@ -1975,16 +2211,16 @@ _timelines: social: "โซเชี่ยล" global: "ทั่วโลà¸" _play: - new: "สร้างà¸à¸²à¸£à¹€à¸¥à¹ˆà¸™" - edit: "à¹à¸à¹‰à¹„ขเล่น" - created: "สร้างà¸à¸²à¸£à¹€à¸¥à¹ˆà¸™à¹à¸¥à¹‰à¸§" - updated: "à¹à¸à¹‰à¹„ขà¸à¸²à¸£à¹€à¸¥à¹ˆà¸™à¹à¸¥à¹‰à¸§" - deleted: "ลบà¸à¸²à¸£à¹€à¸¥à¹ˆà¸™à¹à¸¥à¹‰à¸§" - pageSetting: "ตั้งค่าà¸à¸²à¸£à¹€à¸¥à¹ˆà¸™" + new: "สร้าง Play" + edit: "à¹à¸à¹‰à¹„ข Play" + created: "สร้าง Play à¹à¸¥à¹‰à¸§" + updated: "à¹à¸à¹‰à¹„ข Play à¹à¸¥à¹‰à¸§" + deleted: "ลบ Play à¹à¸¥à¹‰à¸§" + pageSetting: "ตั้งค่า Play" editThisPage: "à¹à¸à¹‰à¹„ข Play นี้" viewSource: "ดูต้นฉบับ" - my: "มาย เพลย์" - liked: "ไลค์ เพลย์" + my: "Play ขà¸à¸‡à¸‰à¸±à¸™" + liked: "Play ที่ถูà¸à¹ƒà¸ˆà¹„ว้" featured: "เป็นที่นิยม" title: "หัวข้à¸" script: "สคริปต์" @@ -1996,15 +2232,15 @@ _pages: created: "สร้างหน้าเพจสำเร็จเรียบร้à¸à¸¢à¹à¸¥à¹‰à¸§" updated: "à¹à¸à¹‰à¹„ขหน้าเพจสำเร็จเรียบร้à¸à¸¢à¹à¸¥à¹‰à¸§" deleted: "ลบหน้าเพจสำเร็จเรียบร้à¸à¸¢à¹à¸¥à¹‰à¸§" - pageSetting: "à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าหน้า" + pageSetting: "à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าหน้าเพจ" nameAlreadyExists: "URL ขà¸à¸‡à¸«à¸™à¹‰à¸²à¸—ี่ระบุนั้นมีà¸à¸¢à¸¹à¹ˆà¹à¸¥à¹‰à¸§" invalidNameTitle: "URL ขà¸à¸‡à¸«à¸™à¹‰à¸²à¸—ี่ระบุนั้นไม่ถูà¸à¸•à¹‰à¸à¸‡" invalidNameText: "ตรวจสà¸à¸šà¹ƒà¸«à¹‰à¹à¸™à¹ˆà¹ƒà¸ˆà¸™à¸°à¸§à¹ˆà¸²à¸Šà¸·à¹ˆà¸à¸«à¸™à¹‰à¸²à¹„ม่ว่างเปล่า" editThisPage: "à¹à¸à¹‰à¹„ขเพจนี้" viewSource: "ดูต้นฉบับ" - viewPage: "ดูหน้า" + viewPage: "ดูหน้าเพจ" like: "ถูà¸à¹ƒà¸ˆ" - unlike: "ลบไลค์" + unlike: "เลิà¸à¸–ูà¸à¹ƒà¸ˆ" my: "หน้าเพจขà¸à¸‡à¸‰à¸±à¸™" liked: "หน้าเพจที่ถูà¸à¹ƒà¸ˆ" featured: "เป็นที่นิยม" @@ -2017,7 +2253,7 @@ _pages: summary: "สรุปเพจ" alignCenter: "เซ็นเตà¸à¸£à¹Œ" hideTitleWhenPinned: "ซ่à¸à¸™à¸Šà¸·à¹ˆà¸à¸«à¸™à¹‰à¸²à¹€à¸žà¸ˆà¹€à¸¡à¸·à¹ˆà¸à¸›à¸±à¸à¸«à¸¡à¸¸à¸”ไว้ที่โปรไฟล์" - font: "ตัวà¸à¸±à¸à¸©à¸£" + font: "à¹à¸šà¸šà¸à¸±à¸à¸©à¸£" fontSerif: "Serif" fontSansSerif: "Sans Serif" eyeCatchingImageSet: "ตั้งค่าภาพขนาดย่à¸" @@ -2025,7 +2261,7 @@ _pages: chooseBlock: "เพิ่มบล็à¸à¸„" selectType: "เลืà¸à¸à¸Šà¸™à¸´à¸”" contentBlocks: "เนื้à¸à¸«à¸²" - inputBlocks: "à¸à¸´à¸™à¸žà¸¸à¸•" + inputBlocks: "ป้à¸à¸™à¸‚้à¸à¸¡à¸¹à¸¥" specialBlocks: "พิเศษ" blocks: text: "ข้à¸à¸„วาม" @@ -2043,23 +2279,28 @@ _relayStatus: accepted: "ได้รับà¸à¸²à¸£à¸à¸™à¸¸à¸¡à¸±à¸•à¸´" rejected: "ถูà¸à¸›à¸à¸´à¹€à¸ªà¸˜" _notification: - fileUploaded: "ไฟล์ถูà¸à¸à¸±à¸žà¹‚หลดà¹à¸¥à¹‰à¸§à¸™à¹ˆà¸°" + fileUploaded: "ไฟล์ถูà¸à¸à¸±à¸›à¹‚หลดà¹à¸¥à¹‰à¸§" youGotMention: "{name} à¸à¸¥à¹ˆà¸²à¸§à¸–ึงคุณ" youGotReply: "{name} ตà¸à¸šà¸à¸¥à¸±à¸šà¸–ึงคุณ" - youGotQuote: "{name} à¸à¹‰à¸²à¸‡à¸–ึงคุณ" + youGotQuote: "{name} à¸à¹‰à¸²à¸‡à¸à¸´à¸‡à¸„ุณ" youRenoted: "รีโน้ตจาภ{name}" youWereFollowed: "ได้ติดตามคุณ" - youReceivedFollowRequest: "คุณมีคำขà¸à¸•à¸´à¸”ตามใหม่น่ะ" - yourFollowRequestAccepted: "คำขà¸à¸•à¸´à¸”ตามขà¸à¸‡à¸„ุณได้รับà¸à¸²à¸£à¸¢à¸à¸¡à¸£à¸±à¸šà¹à¸¥à¹‰à¸§à¸™à¹ˆà¸°" - pollEnded: "โพลสำรวจความคิดเห็นผลลัพธ์มีพร้à¸à¸¡à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" + youReceivedFollowRequest: "ได้รับคำขà¸à¸•à¸´à¸”ตาม" + yourFollowRequestAccepted: "คำขà¸à¸•à¸´à¸”ตามได้รับà¸à¸²à¸£à¸à¸™à¸¸à¸¡à¸±à¸•à¸´à¹à¸¥à¹‰à¸§" + pollEnded: "ผลโพลà¸à¸à¸à¸¡à¸²à¹à¸¥à¹‰à¸§" newNote: "โพสต์ใหม่" unreadAntennaNote: "เสาà¸à¸²à¸à¸²à¸¨ {name}" - emptyPushNotificationMessage: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¹à¸šà¸šà¸žà¸¸à¸Šà¹„ด้รับà¸à¸²à¸£à¸à¸±à¸žà¹€à¸”ทà¹à¸¥à¹‰à¸§" + roleAssigned: "ได้รับบทบาท" + emptyPushNotificationMessage: "à¸à¸±à¸›à¹€à¸”ตà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¹à¸šà¸šà¸žà¸¸à¸Šà¹à¸¥à¹‰à¸§" achievementEarned: "รับความสำเร็จ" testNotification: "ทดสà¸à¸šà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™" - checkNotificationBehavior: "ตรวจสà¸à¸šà¸¥à¸±à¸à¸©à¸“ะที่ปราà¸à¸à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™" + checkNotificationBehavior: "à¸à¸”เพื่à¸à¸”ูลัà¸à¸©à¸“ะà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™" sendTestNotification: "ส่งทดสà¸à¸šà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™" notificationWillBeDisplayedLikeThis: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸¡à¸µà¸¥à¸±à¸à¸©à¸“ะà¹à¸šà¸šà¸™à¸µà¹‰" + reactedBySomeUsers: "ถูà¸à¸£à¸µà¹à¸à¸„ชั่นโดยผู้ใช้ {n} ราย" + renotedBySomeUsers: "รีโน้ตจาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰ {n} ราย" + followedBySomeUsers: "มีผู้ติดตาม {n} ราย" + flushNotification: "ล้างประวัติà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™" _types: all: "ทั้งหมด" note: "โน้ตใหม่" @@ -2069,9 +2310,10 @@ _notification: renote: "รีโน้ต" quote: "à¸à¹‰à¸²à¸‡à¸„ำพูด" reaction: "รีà¹à¸à¸„ชั่น" - pollEnded: "โพลนี้สิ้นสุดลงà¹à¸¥à¹‰à¸§" - receiveFollowRequest: "ได้รับคำขà¸à¸•à¸´à¸”ตาม\n" - followRequestAccepted: "ยà¸à¸¡à¸£à¸±à¸šà¸„ำขà¸à¸•à¸´à¸”ตาม" + pollEnded: "โพลสิ้นสุดà¹à¸¥à¹‰à¸§" + receiveFollowRequest: "ได้รับคำร้à¸à¸‡à¸‚à¸à¸•à¸´à¸”ตาม" + followRequestAccepted: "à¸à¸™à¸¸à¸¡à¸±à¸•à¸´à¹ƒà¸«à¹‰à¸•à¸´à¸”ตามà¹à¸¥à¹‰à¸§" + roleAssigned: "ให้บทบาท" achievementEarned: "ปลดล็à¸à¸à¸„วามสำเร็จà¹à¸¥à¹‰à¸§" app: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸à¸™à¸ˆà¸²à¸à¹à¸à¸›à¸—ี่มีลิงà¸à¹Œ" _actions: @@ -2105,9 +2347,9 @@ _deck: tl: "ไทม์ไลน์" antenna: "เสาà¸à¸²à¸à¸²à¸¨" list: "รายà¸à¸²à¸£" - channel: "à¹à¸Šà¸™à¹à¸™à¸¥" + channel: "ช่à¸à¸‡" mentions: "พูดถึง" - direct: "ไดเร็ค" + direct: "ไดเร็à¸à¸•à¹Œ" roleTimeline: "บทบาทไทม์ไลน์" _dialog: charactersExceeded: "คุณà¸à¸³à¸¥à¸±à¸‡à¸¡à¸µà¸•à¸±à¸§à¸à¸±à¸à¸‚ระเà¸à¸´à¸™à¸‚ีดจำà¸à¸±à¸”สูงสุดà¹à¸¥à¹‰à¸§à¸™à¸°! ปัจจุบันà¸à¸¢à¸¹à¹ˆà¸—ี่ {current} จาภ{max}" @@ -2138,11 +2380,11 @@ _moderationLogTypes: updateRole: "à¸à¸±à¸›à¹€à¸”ตบทบาทà¹à¸¥à¹‰à¸§" assignRole: "ได้รับมà¸à¸šà¸«à¸¡à¸²à¸¢à¸šà¸—บาท" unassignRole: "ถà¸à¸”à¸à¸à¸à¸ˆà¸²à¸à¸šà¸—บาทà¹à¸¥à¹‰à¸§" - suspend: "ถูà¸à¸£à¸°à¸‡à¸±à¸š" - unsuspend: "เลิà¸à¸–ูà¸à¸£à¸°à¸‡à¸±à¸š" - addCustomEmoji: "เพิ่มà¸à¸µà¹‚มจิที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡à¹à¸¥à¹‰à¸§" - updateCustomEmoji: "à¸à¸±à¸›à¹€à¸”ตà¸à¸µà¹‚มจิที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡à¹à¸¥à¹‰à¸§" - deleteCustomEmoji: "ลบà¸à¸µà¹‚มจิที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡à¸à¸à¸à¹à¸¥à¹‰à¸§" + suspend: "ระงับ" + unsuspend: "เลิà¸à¸£à¸°à¸‡à¸±à¸š" + addCustomEmoji: "เพิ่มเà¸à¹‚มจิที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡à¹à¸¥à¹‰à¸§" + updateCustomEmoji: "à¸à¸±à¸›à¹€à¸”ตเà¸à¹‚มจิที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡à¹à¸¥à¹‰à¸§" + deleteCustomEmoji: "ลบเà¸à¹‚มจิที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡à¸à¸à¸à¹à¸¥à¹‰à¸§" updateServerSettings: "à¸à¸±à¸›à¹€à¸”ตà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าเซิร์ฟเวà¸à¸£à¹Œà¹à¸¥à¹‰à¸§" updateUserNote: "à¸à¸±à¸›à¹€à¸”ตโน้ตà¸à¸²à¸£à¸à¸¥à¸±à¹ˆà¸™à¸à¸£à¸à¸‡à¹à¸¥à¹‰à¸§" deleteDriveFile: "ลบไฟล์à¸à¸à¸à¹à¸¥à¹‰à¸§" @@ -2154,15 +2396,21 @@ _moderationLogTypes: deleteGlobalAnnouncement: "ลบประà¸à¸²à¸¨à¸—ั่วโลà¸à¸à¸à¸à¹à¸¥à¹‰à¸§" deleteUserAnnouncement: "ลบประà¸à¸²à¸¨à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸à¸à¸à¹à¸¥à¹‰à¸§" resetPassword: "รีเซ็ตรหัสผ่าน" - suspendRemoteInstance: "à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥à¸–ูà¸à¸£à¸°à¸‡à¸±à¸š" - unsuspendRemoteInstance: "à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥à¹€à¸¥à¸´à¸à¸à¸²à¸£à¸£à¸°à¸‡à¸±à¸š" - markSensitiveDriveFile: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹„ฟล์บà¸à¸à¸§à¹ˆà¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" - unmarkSensitiveDriveFile: "ยà¸à¹€à¸¥à¸´à¸à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹„ฟล์ว่าละเà¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" + suspendRemoteInstance: "ระงับà¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥" + unsuspendRemoteInstance: "เลิà¸à¸£à¸°à¸‡à¸±à¸šà¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥" + updateRemoteInstanceNote: "à¸à¸±à¸›à¹€à¸”ตโน้ตà¸à¸²à¸£à¸à¸¥à¸±à¹ˆà¸™à¸à¸£à¸à¸‡à¸‚à¸à¸‡à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸£à¸°à¸¢à¸°à¹„à¸à¸¥à¹à¸¥à¹‰à¸§" + markSensitiveDriveFile: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹„ฟล์ว่ามีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" + unmarkSensitiveDriveFile: "ยà¸à¹€à¸¥à¸´à¸à¸—ำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¹„ฟล์ว่ามีเนื้à¸à¸«à¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™" resolveAbuseReport: "รายงานได้รับà¸à¸²à¸£à¹à¸à¹‰à¹„ขà¹à¸¥à¹‰à¸§" - createInvitation: "สร้างคำเชิà¸" + createInvitation: "สร้างรหัสเชิà¸" createAd: "สร้างโฆษณาà¹à¸¥à¹‰à¸§" deleteAd: "ลบโฆษณาà¸à¸à¸à¹à¸¥à¹‰à¸§" updateAd: "à¸à¸±à¸›à¹€à¸”ตโฆษณาà¹à¸¥à¹‰à¸§" + createAvatarDecoration: "สร้างà¸à¸²à¸£à¸•à¸à¹à¸•à¹ˆà¸‡à¹„à¸à¸„à¸à¸™à¹à¸¥à¹‰à¸§" + updateAvatarDecoration: "à¸à¸±à¸›à¹€à¸”ตà¸à¸²à¸£à¸•à¸à¹à¸•à¹ˆà¸‡à¹„à¸à¸„à¸à¸™à¹à¸¥à¹‰à¸§" + deleteAvatarDecoration: "ลบà¸à¸²à¸£à¸•à¸à¹à¸•à¹ˆà¸‡à¹„à¸à¸„à¸à¸™à¹à¸¥à¹‰à¸§" + unsetUserAvatar: "ลบไà¸à¸„à¸à¸™à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" + unsetUserBanner: "ลบà¹à¸šà¸™à¹€à¸™à¸à¸£à¹Œà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰" _fileViewer: title: "รายละเà¸à¸µà¸¢à¸”ไฟล์" type: "ประเภทไฟล์" @@ -2172,14 +2420,108 @@ _fileViewer: attachedNotes: "โน้ตที่à¹à¸™à¸šà¸¡à¸²à¸”้วย" thisPageCanBeSeenFromTheAuthor: "หน้าเพจนี้จะสามารถปราà¸à¸à¹„ด้โดยผู้ใช้ที่à¸à¸±à¸›à¹‚หลดไฟล์นี้เท่านั้น" _externalResourceInstaller: + title: "ติดตั้งจาà¸à¹„ซต์ภายนà¸à¸" + checkVendorBeforeInstall: "โปรดตรวจสà¸à¸šà¹ƒà¸«à¹‰à¹à¸™à¹ˆà¹ƒà¸ˆà¸§à¹ˆà¸²à¹à¸«à¸¥à¹ˆà¸‡à¹à¸ˆà¸à¸«à¸™à¹ˆà¸²à¸¢à¸¡à¸µà¸„วามน่าเชื่à¸à¸–ืà¸à¸à¹ˆà¸à¸™à¸—ำà¸à¸²à¸£à¸•à¸´à¸”ตั้ง" _plugin: + title: "ต้à¸à¸‡à¸à¸²à¸£à¸•à¸´à¸”ตั้งปลั๊à¸à¸à¸´à¸™à¸™à¸µà¹‰à¸«à¸£à¸·à¸à¹„ม่?" metaTitle: "ข้à¸à¸¡à¸¹à¸¥à¸ªà¹ˆà¸§à¸™à¹€à¸ªà¸£à¸´à¸¡" _theme: + title: "ต้à¸à¸‡à¸à¸²à¸£à¸•à¸´à¸”ตั้งธีมนี้หรืà¸à¹„ม่?" metaTitle: "ข้à¸à¸¡à¸¹à¸¥à¸˜à¸µà¸¡" + _meta: + base: "โทนสีพื้นà¸à¸²à¸™" _vendorInfo: title: "ข้à¸à¸¡à¸¹à¸¥à¸œà¸¹à¹‰à¸ˆà¸±à¸”จำหน่าย" + endpoint: "จุดà¸à¹‰à¸²à¸‡à¸à¸´à¸‡à¸›à¸¥à¸²à¸¢à¸—าง (Referenced endpoint)" + hashVerify: "à¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ªà¸à¸šà¹à¸®à¸Š (ความสมบูรณ์ขà¸à¸‡à¹„ฟล์)" _errors: + _invalidParams: + title: "พารามิเตà¸à¸£à¹Œà¹„ม่ถูà¸à¸•à¹‰à¸à¸‡" + description: "มีสารสนเทศไม่เพียงพà¸à¸—ี่จะโหลดข้à¸à¸¡à¸¹à¸¥à¸ˆà¸²à¸à¹„ซต์ภายนà¸à¸ โปรดยืนยัน URL ที่ป้à¸à¸™" + _resourceTypeNotSupported: + title: "ไม่รà¸à¸‡à¸£à¸±à¸šà¸—รัพยาà¸à¸£à¸ ายนà¸à¸à¸™à¸µà¹‰" + description: "ไม่รà¸à¸‡à¸£à¸±à¸šà¸›à¸£à¸°à¹€à¸ ทขà¸à¸‡à¸—รัพยาà¸à¸£à¸ ายนà¸à¸à¸™à¸µà¹‰ โปรดติดต่à¸à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¹€à¸§à¹‡à¸šà¹„ซต์" + _failedToFetch: + title: "รับข้à¸à¸¡à¸¹à¸¥à¸¥à¹‰à¸¡à¹€à¸«à¸¥à¸§" + fetchErrorDescription: "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸ªà¸·à¹ˆà¸à¸ªà¸²à¸£à¸à¸±à¸šà¹„ซต์ภายนà¸à¸ หาà¸à¸à¸²à¸£à¸¥à¸à¸‡à¸à¸µà¸à¸„รั้งไม่สามารถà¹à¸à¹‰à¹„ขปัà¸à¸«à¸²à¸™à¸µà¹‰à¹„ด้ โปรดติดต่à¸à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¹„ซต์" + parseErrorDescription: "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸›à¸£à¸°à¸¡à¸§à¸¥à¸œà¸¥à¸‚้à¸à¸¡à¸¹à¸¥à¸—ี่โหลดจาà¸à¹„ซต์ภายนà¸à¸ โปรดติดต่à¸à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¹€à¸§à¹‡à¸šà¹„ซต์" + _hashUnmatched: + title: "à¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™/ตรวจสà¸à¸šà¸‚้à¸à¸¡à¸¹à¸¥à¸¥à¹‰à¸¡à¹€à¸«à¸¥à¸§" + description: "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดในà¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ªà¸à¸šà¸„วามสมบูรณ์ขà¸à¸‡à¸‚้à¸à¸¡à¸¹à¸¥à¸—ี่ดึงมา เพื่à¸à¹€à¸›à¹‡à¸™à¸¡à¸²à¸•à¸£à¸à¸²à¸£à¸£à¸±à¸à¸©à¸²à¸„วามปลà¸à¸”ภัย à¸à¸²à¸£à¸•à¸´à¸”ตั้งไม่สามารถดำเนินà¸à¸²à¸£à¸•à¹ˆà¸à¹„ด้ โปรดติดต่à¸à¸œà¸¹à¹‰à¸”ูà¹à¸¥à¹€à¸§à¹‡à¸šà¹„ซต์" _pluginParseFailed: title: "ข้à¸à¸œà¸´à¸”พลาด AiScript" + description: "ดึงข้à¸à¸¡à¸¹à¸¥à¸—ี่ร้à¸à¸‡à¸‚à¸à¸ªà¸³à¹€à¸£à¹‡à¸ˆà¹à¸¥à¹‰à¸§ à¹à¸•à¹ˆà¸¡à¸µà¸‚้à¸à¸œà¸´à¸”พลาดเà¸à¸´à¸”ขึ้นระหว่างà¸à¸²à¸£à¹à¸¢à¸à¸§à¸´à¹€à¸„ราะห์ AiScript โปรดติดต่à¸à¸œà¸¹à¹‰à¹€à¸‚ียนปลั๊à¸à¸à¸´à¸™ รายละเà¸à¸µà¸¢à¸”ข้à¸à¸œà¸´à¸”พลาดสามารถดูได้ในคà¸à¸™à¹‚ซล Javascript" + _pluginInstallFailed: + title: "ติดตั้งปลั๊à¸à¸à¸´à¸™à¸¥à¹‰à¸¡à¹€à¸«à¸¥à¸§" + description: "เà¸à¸´à¸”ปัà¸à¸«à¸²à¸‚ณะติดตั้งปลั๊à¸à¸à¸´à¸™ à¸à¸£à¸¸à¸“าลà¸à¸‡à¸à¸µà¸à¸„รั้ง. โปรดดูคà¸à¸™à¹‚ซล Javascript สำหรับรายละเà¸à¸µà¸¢à¸”ข้à¸à¸œà¸´à¸”พลาด" _themeParseFailed: title: "à¸à¸²à¸£à¹à¸¢à¸à¸§à¸´à¹€à¸„ราะห์ธีมล้มเหลว" + description: "ดึงข้à¸à¸¡à¸¹à¸¥à¸—ี่ร้à¸à¸‡à¸‚à¸à¸ªà¸³à¹€à¸£à¹‡à¸ˆà¹à¸¥à¹‰à¸§ à¹à¸•à¹ˆà¸¡à¸µà¸‚้à¸à¸œà¸´à¸”พลาดเà¸à¸´à¸”ขึ้นระหว่างà¸à¸²à¸£à¹à¸¢à¸à¸§à¸´à¹€à¸„ราะห์ธีม โปรดติดต่à¸à¸œà¸¹à¹‰à¹€à¸‚ียนธีม รายละเà¸à¸µà¸¢à¸”ข้à¸à¸œà¸´à¸”พลาดสามารถดูได้ในคà¸à¸™à¹‚ซล Javascript" + _themeInstallFailed: + title: "ติดตั้งธีมล้มเหลว" + description: "เà¸à¸´à¸”ปัà¸à¸«à¸²à¸£à¸°à¸«à¸§à¹ˆà¸²à¸‡à¸à¸²à¸£à¸•à¸´à¸”ตั้งธีม à¸à¸£à¸¸à¸“าลà¸à¸‡à¸à¸µà¸à¸„รั้ง. รายละเà¸à¸µà¸¢à¸”ข้à¸à¸œà¸´à¸”พลาดสามารถดูได้ในคà¸à¸™à¹‚ซล Javascript" +_dataSaver: + _media: + title: "โหลดมีเดีย" + description: "à¸à¸±à¸™à¹„ม่ให้ภาพà¹à¸¥à¸°à¸§à¸´à¸”ีโà¸à¹‚หลดโดยà¸à¸±à¸•à¹‚นมัติ à¹à¸•à¸°à¸£à¸¹à¸›à¸ าพ/วิดีโà¸à¸—ี่ซ่à¸à¸™à¸à¸¢à¸¹à¹ˆà¹€à¸žà¸·à¹ˆà¸à¹‚หลด" + _avatar: + title: "รูปไà¸à¸„à¸à¸™" + description: "ระงับà¸à¸²à¸£à¹€à¸„ลื่à¸à¸™à¹„หวขà¸à¸‡à¸ าพไà¸à¸„à¸à¸™ ภาพเคลื่à¸à¸™à¹„หวà¸à¸²à¸ˆà¸¡à¸µà¸‚นาดไฟล์ใหà¸à¹ˆà¸à¸§à¹ˆà¸²à¸ าพปà¸à¸•à¸´ ดังนั้นจึงสามารถช่วยในà¸à¸²à¸£à¸¥à¸”à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‚้à¸à¸¡à¸¹à¸¥" + _urlPreview: + title: "ธัมบ์เนลà¹à¸ªà¸”งตัวà¸à¸¢à¹ˆà¸²à¸‡ URL" + description: "ธัมบ์เนลà¹à¸ªà¸”งตัวà¸à¸¢à¹ˆà¸²à¸‡ URL จะไม่โหลดโดยà¸à¸±à¸•à¹‚นมัติ" + _code: + title: "ไฮไลต์โค้ด" + description: "หาà¸à¹ƒà¸Šà¹‰à¸ªà¸±à¸à¸¥à¸±à¸à¸©à¸“์ไฮไลต์โค้ดใน MFM ฯลฯ สัà¸à¸¥à¸±à¸à¸©à¸“์เหล่านั้นจะไม่โหลดจนà¸à¸§à¹ˆà¸²à¸ˆà¸°à¹à¸•à¸° à¸à¸²à¸£à¹„ฮไลต์ไวยาà¸à¸£à¸“์(syntax)จำเป็นต้à¸à¸‡à¸”าวน์โหลดไฟล์คำจำà¸à¸±à¸”ความขà¸à¸‡à¹„ฮไลต์สำหรับà¹à¸•à¹ˆà¸¥à¸°à¸ าษา ดังนั้นà¸à¸²à¸£à¸›à¸´à¸”ใช้งานà¸à¸²à¸£à¹‚หลดไฟล์เหล่านี้โดยà¸à¸±à¸•à¹‚นมัติจึงคาดว่าจะช่วยลดปริมาณข้à¸à¸¡à¸¹à¸¥à¸à¸²à¸£à¸ªà¸·à¹ˆà¸à¸ªà¸²à¸£à¹„ด้" +_hemisphere: + N: "ซีà¸à¹‚ลà¸à¹€à¸«à¸™à¸·à¸" + S: "ซีà¸à¹‚ลà¸à¹ƒà¸•à¹‰" + caption: "ใช้เพื่à¸à¸à¸³à¸«à¸™à¸”ฤดูà¸à¸²à¸¥à¸‚à¸à¸‡à¹„คลเà¸à¹‡à¸™à¸•à¹Œ" +_reversi: + reversi: "รีเวà¸à¸£à¹Œà¸‹à¸µ" + gameSettings: "ตั้งค่าà¸à¸²à¸£à¹€à¸¥à¹ˆà¸™" + chooseBoard: "เลืà¸à¸à¸à¸£à¸°à¸”าน" + blackOrWhite: "ดำ/ขาว" + blackIs: "{name}เป็นสีดำ" + rules: "à¸à¸Ž" + thisGameIsStartedSoon: "à¸à¸²à¸£à¹€à¸¥à¹ˆà¸™à¸ˆà¸°à¹€à¸£à¸´à¹ˆà¸¡à¹à¸¥à¹‰à¸§" + waitingForOther: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸à¸à¸µà¸à¸à¹ˆà¸²à¸¢à¹€à¸•à¸£à¸µà¸¢à¸¡à¸•à¸±à¸§à¹ƒà¸«à¹‰à¹€à¸ªà¸£à¹‡à¸ˆ" + waitingForMe: "à¸à¸³à¸¥à¸±à¸‡à¸£à¸à¸à¹ˆà¸²à¸¢à¸„ุณเตรียมตัวให้เสร็จ" + waitingBoth: "à¸à¸£à¸¸à¸“าเตรียมตัว" + ready: "เตรียมตัวพร้à¸à¸¡à¹à¸¥à¹‰à¸§" + cancelReady: "ยà¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¹€à¸•à¸£à¸µà¸¢à¸¡à¸•à¸±à¸§à¸žà¸£à¹‰à¸à¸¡" + opponentTurn: "ตาà¸à¸µà¸à¸à¹ˆà¸²à¸¢" + myTurn: "ตาขà¸à¸‡à¸„ุณ" + turnOf: "ตาขà¸à¸‡{name}" + pastTurnOf: "ตาขà¸à¸‡{name}" + surrender: "ยà¸à¸¡à¹à¸žà¹‰" + surrendered: "ยà¸à¸¡à¹à¸žà¹‰à¹à¸¥à¹‰à¸§" + timeout: "หมดเวลาà¹à¸¥à¹‰à¸§" + drawn: "เสมà¸" + won: "{name}ชนะ" + black: "ดำ" + white: "ขาว" + total: "รวมทั้งหมด" + turnCount: "ตาที่{count}" + myGames: "à¸à¸²à¸£à¹€à¸¥à¹ˆà¸™à¸‚à¸à¸‡à¸•à¸±à¸§à¹€à¸à¸‡" + allGames: "à¸à¸²à¸£à¹€à¸¥à¹ˆà¸™à¸‚à¸à¸‡à¸—ุà¸à¸„น" + ended: "จบ" + playing: "à¸à¸³à¸¥à¸±à¸‡à¹€à¸¥à¹ˆà¸™" + isLlotheo: "คนที่มีตัวหมาà¸à¸™à¹‰à¸à¸¢à¸à¸§à¹ˆà¸²à¸Šà¸™à¸° (Roseo)" + loopedMap: "ลูปà¹à¸¡à¸›" + canPutEverywhere: "โหมดที่สามารถวางได้ทุà¸à¸—ี่" + timeLimitForEachTurn: "จำà¸à¸±à¸”เวลาต่à¸à¹à¸•à¹ˆà¸¥à¸°à¸•à¸²" + freeMatch: "ฟรีà¹à¸¡à¸•à¸Šà¹Œ" + lookingForPlayer: "à¸à¸³à¸¥à¸±à¸‡à¸¡à¸à¸‡à¸«à¸²à¸„ู่ต่à¸à¸ªà¸¹à¹‰à¸à¸¢à¸¹à¹ˆ" + gameCanceled: "ยà¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¹€à¸¥à¹ˆà¸™à¹à¸¥à¹‰à¸§" + shareToTlTheGameWhenStart: "โพสต์ลงไทม์ไลน์เมื่à¸à¹€à¸£à¸´à¹ˆà¸¡à¸à¸²à¸£à¹€à¸¥à¹ˆà¸™" + iStartedAGame: "เริ่มเล่นหมาà¸à¸£à¸µà¹€à¸§à¸à¸£à¹Œà¸‹à¸µà¹à¸¥à¹‰à¸§! #MisskeyReversi" + opponentHasSettingsChanged: "à¸à¸µà¸à¸à¹ˆà¸²à¸¢à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่า" + allowIrregularRules: "à¸à¸™à¸¸à¸à¸²à¸•à¸à¸Žà¸—ี่ไม่ปรà¸à¸•à¸´ (โหมดฟรีทุà¸à¸à¸¢à¹ˆà¸²à¸‡)" + disallowIrregularRules: "ไม่à¸à¸™à¸¸à¸à¸²à¸•à¸à¸Žà¸—ี่ไม่ปรà¸à¸•à¸´" + showBoardLabels: "à¹à¸ªà¸”งหมายเลขà¹à¸–ว/คà¸à¸¥à¸±à¸¡à¸™à¹Œà¸šà¸™à¸à¸£à¸°à¸”าน" + useAvatarAsStone: "ใช้รูปà¸à¸§à¸•à¸²à¸£à¹€à¸›à¹‡à¸™à¸«à¸¡à¸²à¸" +_offlineScreen: + title: "à¸à¸à¸Ÿà¹„ลน์ - ไม่สามารถเชื่à¸à¸¡à¸•à¹ˆà¸à¸à¸±à¸šà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¹„ด้" + header: "ไม่สามารถเชื่à¸à¸¡à¸•à¹ˆà¸à¸à¸±à¸šà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¹„ด้" + diff --git a/locales/tr-TR.yml b/locales/tr-TR.yml index 0793592d34026def38b1d78de1bf43f198934021..e93a6e43e129a775a7ff445abb84a8133c25a699 100644 --- a/locales/tr-TR.yml +++ b/locales/tr-TR.yml @@ -455,3 +455,4 @@ _deck: _moderationLogTypes: suspend: "askıya al" resetPassword: "Åžifre sıfırlama" + diff --git a/locales/ug-CN.yml b/locales/ug-CN.yml index e48f64511cf51651e6aa54f789986ef6f6dcf198..e06cee11a2c25e40c875364797c1ea4c62cd65bd 100644 --- a/locales/ug-CN.yml +++ b/locales/ug-CN.yml @@ -17,3 +17,4 @@ _2fa: renewTOTPCancel: "ئۇنى توختىتىÚ" _widgets: profile: "profile" + diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 9b609edebb83bb7d69d620b932e5bce1827375dc..df36f43c06c9152192fb34579ec5cf19d3fb1fc0 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -352,6 +352,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Увімкнути hCaptcha" hcaptchaSiteKey: "Ключ Ñайту" hcaptchaSecretKey: "Секретний ключ" +mcaptchaSiteKey: "Ключ Ñайту" +mcaptchaSecretKey: "Секретний ключ" recaptcha: "reCAPTCHA" enableRecaptcha: "Увімкнути reCAPTCHA" recaptchaSiteKey: "Ключ Ñайту" @@ -909,7 +911,9 @@ youFollowing: "ПідпиÑки" icon: "Ðватар" replies: "ВідповіÑти" renotes: "Поширити" +sourceCode: "Вихідний код" flip: "Перевернути" +lastNDays: "ОÑтанні {n} днів" _achievements: earnedAt: "Відкрито" _types: @@ -1468,6 +1472,7 @@ _profile: changeBanner: "Змінити банер" _exportOrImport: allNotes: "Ð’ÑÑ– нотатки" + clips: "Добірка" followingList: "ПідпиÑки" muteList: "Ігнорувати" blockingList: "Заблокувати" @@ -1616,3 +1621,6 @@ _webhookSettings: _moderationLogTypes: suspend: "Призупинити" resetPassword: "Скинути пароль" +_reversi: + total: "Ð’Ñього" + diff --git a/locales/uz-UZ.yml b/locales/uz-UZ.yml index 3d00e473948942c4739199db0c0cd0e6f583068a..a79a76066a7d7c6737febdf290fc4f061bb0e79f 100644 --- a/locales/uz-UZ.yml +++ b/locales/uz-UZ.yml @@ -366,6 +366,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "hCaptchani yoqish" hcaptchaSiteKey: "Sayt kaliti" hcaptchaSecretKey: "Mahfiy kalit" +mcaptchaSiteKey: "Sayt kaliti" +mcaptchaSecretKey: "Maxfiy kalit" recaptcha: "reCAPTCHA" enableRecaptcha: "reCAPTCHA ni yoqish" recaptchaSiteKey: "Sayt kaliti" @@ -973,6 +975,7 @@ _profile: changeBanner: "Bannerni o'zgartirish" _exportOrImport: allNotes: "Barcha qaydlar" + clips: "Klip" followingList: "Obuna bo‘lish" muteList: "Ovozni o‘chirish" blockingList: "Bloklangan foydalanuvchilar" @@ -1085,3 +1088,6 @@ _webhookSettings: _moderationLogTypes: suspend: "To'xtatish" resetPassword: "Parolni tiklash" +_reversi: + total: "Jami" + diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index e4f7d27423711bf08d0312921ace14448916dfcc..15530a5cd30b23cc3e9331f4f4b2be4c9dee7e10 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -368,6 +368,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Báºt hCaptcha" hcaptchaSiteKey: "Khóa của trang" hcaptchaSecretKey: "Khóa bà máºt" +mcaptchaSiteKey: "Khóa của trang" +mcaptchaSecretKey: "Khóa bà máºt" recaptcha: "reCAPTCHA" enableRecaptcha: "Báºt reCAPTCHA" recaptchaSiteKey: "Khóa của trang" @@ -1043,7 +1045,10 @@ loadReplies: "Hiển thị các trả lá»i" pinnedList: "Các mục đã được ghim" keepScreenOn: "Giữ mà n hình luôn báºt" verifiedLink: "Chúng tôi đã xác nháºn bạn là chủ sở hữu của Ä‘Æ°á»ng dẫn nà y" +sourceCode: "Mã nguồn" flip: "Láºt" +lastNDays: "{n} ngà y trÆ°á»›c" +surrender: "Từ chối" _announcement: forExistingUsers: "Chỉ những ngÆ°á»i dùng đã tồn tại" forExistingUsersDescription: "Nếu được báºt, thông báo nà y sẽ chỉ hiển thị vá»›i những ngÆ°á»i dùng đã tồn tại và o lúc thông báo được tạo. Nếu tắt Ä‘i, những tà i khoản má»›i đăng ký sau khi thông báo được đăng lên cÅ©ng sẽ thấy nó." @@ -1669,6 +1674,7 @@ _profile: _exportOrImport: allNotes: "Toà n bá»™ tút" favoritedNotes: "Bà i viết đã thÃch" + clips: "LÆ°u bà i viết" followingList: "Äang theo dõi" muteList: "Ẩn" blockingList: "Chặn" @@ -1846,3 +1852,6 @@ _webhookSettings: _moderationLogTypes: suspend: "Vô hiệu hóa" resetPassword: "Äặt lại máºt khẩu" +_reversi: + total: "Tổng cá»™ng" + diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index bfacc03e0af7a4f160027f65348882104f2d26ae..17ad6e715061dd91f31d2b48e1ddc671480e845e 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -11,7 +11,7 @@ password: "密ç " forgotPassword: "忘记密ç " fetchingAsApObject: "在è”邦宇宙查询ä¸..." ok: "OK" -gotIt: "我明白了" +gotIt: "好" cancel: "å–消" noThankYou: "ä¸ç”¨ï¼Œè°¢è°¢" enterUsername: "输入用户å" @@ -121,14 +121,21 @@ sensitive: "æ•æ„Ÿå†…容" add: "æ·»åŠ " reaction: "回应" reactions: "回应" +emojiPicker: "表情符å·é€‰æ‹©å™¨" +pinnedEmojisForReactionSettingDescription: "å¯ä»¥è®¾ç½®å‘表回应时置顶显示的表情符å·" +pinnedEmojisSettingDescription: "å¯ä»¥è®¾ç½®è¾“入表情符å·æ—¶ç½®é¡¶æ˜¾ç¤ºçš„表情符å·" +emojiPickerDisplay: "选择器显示设置" +overwriteFromPinnedEmojisForReaction: "从「置顶(回应)ã€è®¾ç½®è¦†ç›–" +overwriteFromPinnedEmojis: "从全局设置覆盖" reactionSettingDescription2: "拖动é‡æ–°æŽ’åºï¼Œå•å‡»åˆ 除,点击 + æ·»åŠ ã€‚" rememberNoteVisibility: "ä¿å˜ä¸Šæ¬¡è®¾ç½®çš„å¯è§æ€§" attachCancel: "åˆ é™¤é™„ä»¶" +deleteFile: "åˆ é™¤æ–‡ä»¶" markAsSensitive: "æ ‡è®°ä¸ºæ•æ„Ÿå†…容" unmarkAsSensitive: "å–æ¶ˆæ ‡è®°ä¸ºæ•æ„Ÿå†…容" enterFileName: "输入文件å" mute: "å±è”½" -unmute: "解除å±è”½" +unmute: "解除é™éŸ³" renoteMute: "å±è”½è½¬å¸–" renoteUnmute: "解除å±è”½è½¬å¸–" block: "拉黑" @@ -209,15 +216,15 @@ instanceInfo: "æœåŠ¡å™¨ä¿¡æ¯" statistics: "统计" clearQueue: "清除队列" clearQueueConfirmTitle: "确定清除队列?" -clearQueueConfirmText: "未é€è¾¾çš„帖åå°†ä¸ä¼šæŠ•é€’。 通常,您ä¸éœ€è¦è¿™æ ·åšã€‚" +clearQueueConfirmText: "未é€è¾¾çš„帖åå°†ä¸ä¼šè¢«æŠ•é€’。 é€šå¸¸æ— éœ€æ‰§è¡Œæ¤æ“作。" clearCachedFiles: "清除缓å˜" -clearCachedFilesConfirm: "确定è¦æ¸…除缓å˜æ–‡ä»¶ï¼Ÿ" +clearCachedFilesConfirm: "确定è¦æ¸…除所有缓å˜çš„远程文件?" blockedInstances: "被å°é”çš„æœåŠ¡å™¨" blockedInstancesDescription: "设定è¦å°é”çš„æœåŠ¡å™¨ï¼Œä»¥æ¢è¡Œæ¥è¿›è¡Œåˆ†å‰²ã€‚被å°é”çš„æœåŠ¡å™¨å°†æ— 法与本æœåŠ¡å™¨è¿›è¡Œäº¤æ¢é€šè®¯ã€‚å域å也åŒæ ·ä¼šè¢«å°é”。" -silencedInstances: "沉默的æœåŠ¡å™¨" -silencedInstancesDescription: "设置è¦é™éŸ³çš„æœåŠ¡å™¨çš„主机,以æ¢è¡Œç¬¦åˆ†éš”。属于é™é»˜æœåŠ¡å™¨çš„所有å¸æˆ·éƒ½å°†è¢«è§†ä¸ºâ€œé™é»˜â€ï¼Œæ‰€æœ‰å…³æ³¨éƒ½å°†æˆä¸ºè¯·æ±‚ï¼Œå¹¶ä¸”æ‚¨å°†æ— æ³•æåŠéžå…³æ³¨è€…的本地å¸æˆ·ã€‚被阻æ¢çš„实例ä¸å—å½±å“。" -muteAndBlock: "å±è”½/拉黑" -mutedUsers: "å·²å±è”½ç”¨æˆ·" +silencedInstances: "被é™éŸ³çš„æœåŠ¡å™¨" +silencedInstancesDescription: "设置è¦é™éŸ³çš„æœåŠ¡å™¨ï¼Œä»¥æ¢è¡Œç¬¦åˆ†éš”。被é™éŸ³çš„æœåŠ¡å™¨å†…所有的账户将默认处于「é™éŸ³ã€çŠ¶æ€ï¼Œä»…能å‘é€å…³æ³¨è¯·æ±‚,并且在未关注状æ€ä¸‹æ— 法æåŠæœ¬åœ°è´¦æˆ·ã€‚被阻æ¢çš„实例ä¸å—å½±å“。" +muteAndBlock: "é™éŸ³/拉黑" +mutedUsers: "å·²é™éŸ³ç”¨æˆ·" blockedUsers: "已拉黑的用户" noUsers: "æ— ç”¨æˆ·" editProfile: "编辑资料" @@ -260,6 +267,7 @@ removed: "å·²åˆ é™¤" removeAreYouSure: "è¦åˆ 掉「{x}ã€å—?" deleteAreYouSure: "è¦åˆ 掉「{x}ã€å—?" resetAreYouSure: "æ¢å¤é»˜è®¤è®¾ç½®ï¼Ÿ" +areYouSure: "ä½ ç¡®å®šå—?" saved: "å·²ä¿å˜" messaging: "èŠå¤©" upload: "æœ¬åœ°ä¸Šä¼ " @@ -328,7 +336,7 @@ displayOfSensitiveMedia: "显示æ•æ„Ÿåª’体" whenServerDisconnected: "与æœåŠ¡å™¨è¿žæŽ¥ä¸æ–æ—¶" disconnectedFromServer: "已和æœåŠ¡å™¨æ–开连接" reload: "é‡æ–°åŠ è½½" -doNothing: "å…³é—弹窗" +doNothing: "å…³é—" reloadConfirm: "确定è¦é‡æ–°åŠ è½½å—?" watch: "关注" unwatch: "å–消关注" @@ -352,7 +360,7 @@ connectService: "连接" disconnectService: "æ–开连接" enableLocalTimeline: "å¯ç”¨æœ¬åœ°æ—¶é—´çº¿" enableGlobalTimeline: "å¯ç”¨å…¨å±€æ—¶é—´çº¿" -disablingTimelinesInfo: "å³ä½¿æ—¶é—´çº¿åŠŸèƒ½è¢«ç¦ç”¨ï¼Œå‡ºäºŽæ–¹ä¾¿ï¼Œç®¡ç†å‘˜å’Œå作者也å¯ä»¥ç»§ç»ä½¿ç”¨ã€‚" +disablingTimelinesInfo: "å³ä½¿æ—¶é—´çº¿åŠŸèƒ½è¢«ç¦ç”¨ï¼Œå‡ºäºŽæ–¹ä¾¿ï¼Œç®¡ç†å‘˜å’Œç›‘察员也å¯ä»¥ç»§ç»ä½¿ç”¨ã€‚" registration: "注册" enableRegistration: "å…许任何人注册" invite: "邀请" @@ -372,15 +380,20 @@ hcaptcha: "hCaptcha" enableHcaptcha: "å¯ç”¨ hCaptcha" hcaptchaSiteKey: "网站密钥" hcaptchaSecretKey: "hCaptcha 密钥(SecretKey)" +mcaptcha: "mCaptcha" +enableMcaptcha: "å¯ç”¨ mCaptcha" +mcaptchaSiteKey: "网站密钥" +mcaptchaSecretKey: "mCaptcha 密钥(SecretKey)" +mcaptchaInstanceUrl: "mCaptcha 实例地å€" recaptcha: "reCAPTCHA" enableRecaptcha: "å¯ç”¨ reCAPTCHA\n(请注æ„, æ¤åŠŸèƒ½åœ¨ä¸å›½å¤§é™†ä¸å¯ç”¨. 如果å¯ç”¨, å¯èƒ½å¯¼è‡´æ— 法æ£å¸¸ä½¿ç”¨ç™»å½•æˆ–注册ç‰åŠŸèƒ½)" recaptchaSiteKey: "网站密钥" -recaptchaSecretKey: "reCAPTCHA 密钥(SecretKey)" +recaptchaSecretKey: "mCaptcha 密钥(SecretKey)" turnstile: "Turnstile" enableTurnstile: "å¯ç”¨ Turnstile" turnstileSiteKey: "网站密钥" turnstileSecretKey: "Turnstile 密钥(SecretKey)" -avoidMultiCaptchaConfirm: "使用多ç§éªŒè¯æ–¹å¼å¯èƒ½ä¼šé€ æˆå¹²æ‰°ï¼Œæ‚¨è¦ç¦ç”¨å…¶ä»–验è¯æ–¹å¼å—?您å¯ä»¥æŒ‰â€œå–消â€æŒ‰é’®ï¼Œç»§ç»ä¿æŒå¯ç”¨å¤šç§éªŒè¯æ–¹å¼ã€‚" +avoidMultiCaptchaConfirm: "使用多个 Captcha å¯èƒ½ä¼šäº’相干扰,您è¦ç¦ç”¨å…¶å®ƒ Captcha å—?您å¯ä»¥æŒ‰â€œå–消â€æŒ‰é’®ï¼Œç»§ç»ä¿æŒå¯ç”¨å¤šç§éªŒè¯æ–¹å¼ã€‚" antennas: "天线" manageAntennas: "天线管ç†" name: "å称" @@ -477,7 +490,7 @@ or: "或者" language: "è¯è¨€" uiLanguage: "显示è¯è¨€" aboutX: "关于 {x}" -emojiStyle: "emoji çš„æ ·å¼" +emojiStyle: "表情符å·çš„æ ·å¼" native: "原生" disableDrawer: "ä¸æ˜¾ç¤ºæŠ½å±‰èœå•" showNoteActionsOnlyHover: "仅在悬åœæ—¶æ˜¾ç¤ºå¸–åæ“作" @@ -514,7 +527,7 @@ showFeaturedNotesInTimeline: "在时间线上显示çƒé—¨æŽ¨è" objectStorage: "对象å˜å‚¨" useObjectStorage: "使用对象å˜å‚¨" objectStorageBaseUrl: "Base URL" -objectStorageBaseUrlDesc: "这里是用于å‚考的 URL,如果您æ£åœ¨ä½¿ç”¨ CDN 或åå‘代ç†ï¼Œè¯·æŒ‡å®šå…¶ URL,例如 S3:“https://<bucket>.s3.amazonaws.comâ€ï¼ŒGCS:“https://storage.googleapis.com/<bucket>â€" +objectStorageBaseUrlDesc: "用于å‚考的 URL,如果您æ£åœ¨ä½¿ç”¨ CDN 或 Proxy,请填入æœåŠ¡å•†æ供的 URLï¼›S3:“https://<bucket>.s3.amazonaws.comâ€ï¼›GCS:“https://storage.googleapis.com/<bucket>â€" objectStorageBucket: "å˜å‚¨æ¡¶" objectStorageBucketDesc: "请指定使用的对象å˜å‚¨æœåŠ¡çš„å˜å‚¨æ¡¶å称。" objectStoragePrefix: "å‰ç¼€" @@ -533,6 +546,7 @@ serverLogs: "æœåŠ¡å™¨æ—¥å¿—" deleteAll: "å…¨éƒ¨åˆ é™¤" showFixedPostForm: "在时间线顶部显示å‘帖框" showFixedPostFormInChannel: "在时间线顶部显示å‘帖对è¯æ¡†ï¼ˆé¢‘é“)" +withRepliesByDefaultForNewlyFollowed: "在时间线ä¸é»˜è®¤åŒ…å«æ–°å…³æ³¨ç”¨æˆ·çš„回å¤" newNoteRecived: "有新的帖å" sounds: "æ示音" sound: "æ示音" @@ -542,6 +556,8 @@ showInPage: "在页é¢ä¸æ˜¾ç¤º" popout: "弹窗" volume: "音é‡" masterVolume: "主音é‡" +notUseSound: "é™éŸ³" +useSoundOnlyWhenActive: "仅在 Misskey 活跃时输出声音" details: "详情" chooseEmoji: "选择表情符å·" unableToProcess: "æ“ä½œæ— æ³•å®Œæˆ" @@ -562,10 +578,14 @@ output: "输出" script: "脚本" disablePagesScript: "ç¦ç”¨é¡µé¢è„šæœ¬" updateRemoteUser: "更新远程用户信æ¯" +unsetUserAvatar: "清除头åƒ" +unsetUserAvatarConfirm: "è¦æ¸…除头åƒå—?" +unsetUserBanner: "清除横幅" +unsetUserBannerConfirm: "è¦æ¸…除横幅å—?" deleteAllFiles: "åˆ é™¤æ‰€æœ‰æ–‡ä»¶" deleteAllFilesConfirm: "è¦åˆ 除所有文件å—?" removeAllFollowing: "å–消所有关注" -removeAllFollowingDescription: "å–消 {host} 的所有关注者。当æœåŠ¡å™¨ä¸å†å˜åœ¨æ—¶æ‰§è¡Œã€‚" +removeAllFollowingDescription: "å–消æ¥è‡ª {host} 的所有关注者。当æœåŠ¡å™¨ä¸å†å˜åœ¨æ—¶æ‰§è¡Œã€‚" userSuspended: "该用户已被冻结。" userSilenced: "该用户已被ç¦è¨€ã€‚" yourAccountSuspendedTitle: "账户已被冻结" @@ -612,6 +632,7 @@ medium: "ä¸" small: "å°" generateAccessToken: "生æˆè®¿é—®ä»¤ç‰Œ" permission: "æƒé™" +adminPermission: "管ç†å‘˜æƒé™" enableAll: "å¯ç”¨å…¨éƒ¨" disableAll: "ç¦ç”¨å…¨éƒ¨" tokenRequested: "å…许访问账户" @@ -633,6 +654,7 @@ smtpSecure: "在 SMTP 连接ä¸ä½¿ç”¨éšå¼ SSL / TLS" smtpSecureInfo: "使用 STARTTLS 时关é—。" testEmail: "邮件å‘é€æµ‹è¯•" wordMute: "æ–‡å—å±è”½" +hardWordMute: "å±è”½å…³é”®è¯" regexpError: "æ£åˆ™è¡¨è¾¾å¼é”™è¯¯" regexpErrorDescription: "{tab} å±è”½æ–‡å—的第 {line} 行的æ£åˆ™è¡¨è¾¾å¼æœ‰é”™è¯¯ï¼š" instanceMute: "被å±è”½çš„æœåŠ¡å™¨" @@ -654,6 +676,7 @@ useGlobalSettingDesc: "å¯ç”¨æ—¶ï¼Œå°†ä½¿ç”¨è´¦æˆ·é€šçŸ¥è®¾ç½®ã€‚å…³é—时,则 other: "其他" regenerateLoginToken: "é‡æ–°ç”Ÿæˆç™»å½•ä»¤ç‰Œ" regenerateLoginTokenDescription: "é‡æ–°ç”Ÿæˆç”¨äºŽç™»å½•çš„内部令牌。通常您ä¸éœ€è¦è¿™æ ·åšã€‚é‡æ–°ç”ŸæˆåŽï¼Œæ‚¨å°†åœ¨æ‰€æœ‰è®¾å¤‡ä¸Šç™»å‡ºã€‚" +theKeywordWhenSearchingForCustomEmoji: "这将是æœç´ 自定义表情符å·æ—¶çš„关键è¯ã€‚" setMultipleBySeparatingWithSpace: "您å¯ä»¥ä½¿ç”¨ç©ºæ ¼åˆ†éš”多个项目。" fileIdOrUrl: "文件 ID 或者 URL" behavior: "行为" @@ -866,6 +889,8 @@ makeReactionsPublicDescription: "将您å‘表过的回应设置æˆå…¬å¼€å¯è§ classic: "ç»å…¸" muteThread: "å±è”½å¸–å列表" unmuteThread: "å–消å±è”½å¸–å列表" +followingVisibility: "关注的人的公开范围" +followersVisibility: "关注者的公开范围" continueThread: "查看更多帖å" deleteAccountConfirm: "å°†è¦åˆ 除账户。是å¦ç¡®è®¤ï¼Ÿ" incorrectPassword: "密ç 错误" @@ -949,7 +974,7 @@ unsubscribePushNotification: "åœç”¨æŽ¨é€é€šçŸ¥æ¶ˆæ¯" pushNotificationAlreadySubscribed: "推é€é€šçŸ¥æ¶ˆæ¯å·²å¯ç”¨" pushNotificationNotSupported: "æµè§ˆå™¨æˆ–æœåŠ¡å™¨ä¸æ”¯æŒæŽ¨é€é€šçŸ¥æ¶ˆæ¯" sendPushNotificationReadMessage: "åˆ é™¤å·²è¯»æŽ¨é€é€šçŸ¥æ¶ˆæ¯" -sendPushNotificationReadMessageCaption: "“{emptyPushNotificationMessage}â€çš„通知消æ¯å°†ä¼šæ˜¾ç¤ºã€‚æ‚¨ç»ˆç«¯è®¾å¤‡çš„ç”µæ± æ¶ˆè€—å¯èƒ½ä¼šå¢žåŠ 。" +sendPushNotificationReadMessageCaption: "æ‚¨ç»ˆç«¯è®¾å¤‡çš„ç”µæ± æ¶ˆè€—å¯èƒ½ä¼šå¢žåŠ 。" windowMaximize: "最大化" windowMinimize: "最å°åŒ–" windowRestore: "还原" @@ -966,6 +991,7 @@ neverShow: "ä¸å†æ˜¾ç¤º" remindMeLater: "ç¨åŽæ醒我" didYouLikeMisskey: "您喜欢 Misskey å—?" pleaseDonate: "Misskey 是 {host} 所使用的å…费软件。为了今åŽä¹Ÿèƒ½å¤Ÿç»´æŒ Misskey çš„å¼€å‘,请在有余力的情况下进行æ助ï¼" +correspondingSourceIsAvailable: "对应的æºä»£ç å¯åœ¨{anchor}找到" roles: "角色" role: "角色" noRole: "角色ä¸å˜åœ¨" @@ -975,6 +1001,7 @@ assign: "分é…" unassign: "å–消分é…" color: "颜色" manageCustomEmojis: "管ç†è‡ªå®šä¹‰è¡¨æƒ…符å·" +manageAvatarDecorations: "管ç†å¤´åƒæŒ‚件" youCannotCreateAnymore: "抱æ‰ï¼Œæ‚¨æ— 法å†åˆ›å»ºæ›´å¤šäº†ã€‚" cannotPerformTemporary: "æš‚æ—¶ä¸å¯ç”¨" cannotPerformTemporaryDescription: "å› æ“作过于频ç¹ï¼Œæš‚æ—¶ä¸å¯ç”¨ï¼Œè¯·ç¨åŽå†è¯•ã€‚" @@ -1015,6 +1042,11 @@ resetPasswordConfirm: "确定é‡ç½®å¯†ç ?" sensitiveWords: "æ•æ„Ÿè¯" sensitiveWordsDescription: "将包å«è®¾ç½®è¯çš„帖åçš„å¯è§èŒƒå›´è®¾ç½®ä¸ºé¦–页。å¯ä»¥é€šè¿‡ç”¨æ¢è¡Œç¬¦åˆ†éš”æ¥è®¾ç½®å¤šä¸ªã€‚" sensitiveWordsDescription2: "AND æ¡ä»¶ç”¨ç©ºæ ¼åˆ†éš”,æ£åˆ™è¡¨è¾¾å¼ç”¨æ–œçº¿åŒ…裹。" +prohibitedWords: "ç¦ç”¨è¯" +prohibitedWordsDescription: "å‘布包å«è®¾å®šè¯æ±‡çš„帖å时将出错。å¯ç”¨æ¢è¡Œè®¾å®šå¤šä¸ªå…³é”®å—" +prohibitedWordsDescription2: "AND æ¡ä»¶ç”¨ç©ºæ ¼åˆ†éš”,æ£åˆ™è¡¨è¾¾å¼ç”¨æ–œçº¿åŒ…裹。" +hiddenTags: "éšè—æ ‡ç¾" +hiddenTagsDescription: "è®¾å®šçš„æ ‡ç¾å°†ä¸ä¼šåœ¨æ—¶é—´çº¿ä¸Šæ˜¾ç¤ºã€‚å¯ä½¿ç”¨æ¢è¡Œæ¥è®¾ç½®å¤šä¸ªæ ‡ç¾ã€‚" notesSearchNotAvailable: "帖å检索ä¸å¯ç”¨" license: "许å¯ä¿¡æ¯" unfavoriteConfirm: "确定è¦å–消收è—å—?" @@ -1027,9 +1059,12 @@ enableChartsForRemoteUser: "生æˆè¿œç¨‹ç”¨æˆ·çš„图表" enableChartsForFederatedInstances: "生æˆè¿œç¨‹æœåŠ¡å™¨çš„图表" showClipButtonInNoteFooter: "在贴文下方显示便ç¾æŒ‰é’®" reactionsDisplaySize: "回应显示大å°" +limitWidthOfReaction: "é™åˆ¶å›žåº”的最大宽度,并将其缩å°æ˜¾ç¤º" noteIdOrUrl: "帖å ID 或 URL" video: "视频" videos: "视频" +audio: "音频" +audioFiles: "音频" dataSaver: "çœæµé‡æ¨¡å¼" accountMigration: "账户è¿ç§»" accountMoved: "æ¤ç”¨æˆ·å·²è¿ç§»è´¦æˆ·" @@ -1082,7 +1117,7 @@ branding: "å“牌" enableServerMachineStats: "公开æœåŠ¡å™¨ç¡¬ä»¶ç»Ÿè®¡ä¿¡æ¯" enableIdenticonGeneration: "å¯ç”¨ç”Ÿæˆç”¨æˆ· Identicon" turnOffToImprovePerformance: "å…³é—该选项å¯ä»¥æ高性能。" -createInviteCode: "å‘行邀请ç " +createInviteCode: "生æˆé‚€è¯·ç " createWithOptions: "使用选项æ¥åˆ›å»º" createCount: "å‘行数" inviteCodeCreated: "已创建邀请ç " @@ -1094,7 +1129,7 @@ noExpirationDate: "ä¸è®¾ç½®æœ‰æ•ˆæ—¥æœŸ" inviteCodeUsedAt: "邀请ç 被使用的日期和时间" registeredUserUsingInviteCode: "使用了邀请ç 的用户" waitingForMailAuth: "ç‰å¾…验è¯ç”µå邮件" -inviteCodeCreator: "å‘行邀请ç 的用户" +inviteCodeCreator: "生æˆé‚€è¯·ç 的用户" usedAt: "使用时间" unused: "未使用" used: "已使用" @@ -1125,20 +1160,96 @@ showRenotes: "显示转帖" edited: "已编辑" notificationRecieveConfig: "通知接收设置" mutualFollow: "互相关注" +followingOrFollower: "关注ä¸æˆ–关注者" fileAttachedOnly: "ä»…é™åª’体" -showRepliesToOthersInTimeline: "在时间线上显示给其他人的回å¤" -hideRepliesToOthersInTimeline: "在时间线上éšè—给其他人的回å¤" +showRepliesToOthersInTimeline: "在时间线ä¸åŒ…å«ç»™åˆ«äººçš„回å¤" +hideRepliesToOthersInTimeline: "在时间线ä¸éšè—给别人的回å¤" +showRepliesToOthersInTimelineAll: "在时间线ä¸åŒ…å«çŽ°åœ¨å…³æ³¨çš„所有人的回å¤" +hideRepliesToOthersInTimelineAll: "在时间线ä¸éšè—现在关注的所有人的回å¤" +confirmShowRepliesAll: "æ¤æ“作ä¸å¯æ’¤é”€ã€‚确认è¦åœ¨æ—¶é—´çº¿ä¸åŒ…å«çŽ°åœ¨å…³æ³¨çš„所有人的回å¤å—?" +confirmHideRepliesAll: "æ¤æ“作ä¸å¯æ’¤é”€ã€‚确认è¦åœ¨æ—¶é—´çº¿ä¸éšè—现在关注的所有人的回å¤å—?" +externalServices: "外部æœåŠ¡" +sourceCode: "æºä»£ç " +sourceCodeIsNotYetProvided: "还未æä¾›æºä»£ç 。è¦è§£å†³æ¤é—®é¢˜è¯·è”系管ç†å‘˜ã€‚" +repositoryUrl: "仓库地å€" +repositoryUrlDescription: "è‹¥æºä»£ç 所在的仓库是公开的,请填入对应的 URLã€‚è‹¥æ˜¯æŒ‰åŽŸæ ·ä½¿ç”¨ Misskeyï¼ˆå¹¶æœªè¿½åŠ æˆ–è€…ä¿®æ”¹ä»£ç )的情况请填入 https://github.com/misskey-dev/misskey。" +repositoryUrlOrTarballRequired: "若仓库并未公开,则需è¦æä¾› tarball 作为替代。详情请看 .config/example.yml。" +feedback: "å馈" +feedbackUrl: "å馈地å€" +impressum: "è¿è¥å•†ä¿¡æ¯" +impressumUrl: "è¿è¥å•†ä¿¡æ¯åœ°å€" +impressumDescription: "德国ç‰å›½å®¶å’Œåœ°åŒºæœ‰ä¹‰åŠ¡å±•ç¤ºæ¤ç±»ä¿¡æ¯ï¼ˆImpressum)。" +privacyPolicy: "éšç§æ”¿ç–" +privacyPolicyUrl: "éšç§æ”¿ç–地å€" +tosAndPrivacyPolicy: "æœåŠ¡æ¡æ¬¾åŠéšç§æ”¿ç–" avatarDecorations: "头åƒæŒ‚件" +attach: "佩戴" +detach: "å¸ä¸‹" +detachAll: "全部å¸ä¸‹" +angle: "角度" flip: "翻转" +showAvatarDecorations: "显示头åƒæŒ‚件" +releaseToRefresh: "æ¾å¼€ä»¥åˆ·æ–°" +refreshing: "刷新ä¸" +pullDownToRefresh: "下拉以刷新" +disableStreamingTimeline: "ç¦æ¢å®žæ—¶æ›´æ–°æ—¶é—´çº¿" +useGroupedNotifications: "分组显示通知" +signupPendingError: "确认电å邮件时出现错误。链接å¯èƒ½å·²è¿‡æœŸã€‚" +cwNotationRequired: "在å¯ç”¨ã€Œéšè—内容ã€æ—¶å¿…须输入注释" +doReaction: "回应" +code: "代ç " +reloadRequiredToApplySettings: "需è¦é‡æ–°è½½å…¥æ¥ä½¿è®¾ç½®ç”Ÿæ•ˆ" +remainingN: "剩余:{n}" +overwriteContentConfirm: "将覆盖现有内容。确定å—?" +seasonalScreenEffect: "应景的画é¢æ•ˆæžœ" +decorate: "装饰" +addMfmFunction: "æ·»åŠ è£…é¥°" +enableQuickAddMfmFunction: "显示高级 MFM 选择器" +bubbleGame: "泡泡游æˆ" +sfx: "音效" +soundWillBePlayed: "声音将会æ’放" +showReplay: "观看回放" +replay: "é‡æ’" +replaying: "é‡æ’ä¸" +endReplay: "结æŸå›žæ”¾" +copyReplayData: "å¤åˆ¶å›žæ”¾æ•°æ®" +ranking: "排行榜" +lastNDays: "最近 {n} 天" +backToTitle: "è¿”å›žæ ‡é¢˜" +hemisphere: "å±…ä½åœ°åŒº" +withSensitive: "显示包å«æ•æ„Ÿåª’体的帖å" +userSaysSomethingSensitive: "å« {name} æ•æ„Ÿæ–‡ä»¶çš„帖å" +enableHorizontalSwipe: "滑动切æ¢æ ‡ç¾é¡µ" +loading: "读å–ä¸" +surrender: "å–消" +gameRetry: "é‡è¯•" +_bubbleGame: + howToPlay: "游æˆè¯´æ˜Ž" + hold: "抓ä½" + _score: + score: "得分" + scoreYen: "赚到的钱" + highScore: "最高分" + maxChain: "最高连击数" + yen: "{yen} 日元" + estimatedQty: "约 {qty} 个" + _howToPlay: + section1: "对准ä½ç½®å°†Emoji投入盒å。" + section2: "相åŒçš„Emoji相互接触åˆæˆåŽä¼šå¾—到新的Emoji,以æ¤èŽ·å¾—分数。" + section3: "如果Emoji从箱åä¸æº¢å‡ºæ¸¸æˆå°†ä¼šç»“æŸã€‚在防æ¢Emoji溢出的åŒæ—¶ï¼Œä¸æ–åˆæˆæ–°çš„Emoji,æ¥èŽ·å–更高的分数å§ï¼" _announcement: forExistingUsers: "ä»…é™çŽ°æœ‰ç”¨æˆ·" forExistingUsersDescription: "è‹¥å¯ç”¨ï¼Œè¯¥å…¬å‘Šå°†ä»…对创建æ¤å…¬å‘Šæ—¶å˜åœ¨çš„用户å¯è§ã€‚ 如果ç¦ç”¨ï¼Œåˆ™åœ¨åˆ›å»ºæ¤å…¬å‘ŠåŽæ³¨å†Œçš„用户也å¯ä»¥çœ‹åˆ°è¯¥å…¬å‘Šã€‚" needConfirmationToRead: "需è¦ç¡®è®¤æ‰èƒ½æ ‡è®°ä¸ºå·²è¯»" needConfirmationToReadDescription: "è‹¥å¯ç”¨ï¼Œåˆ™ä¼šåœ¨æ ‡è®°å·²è¯»æ—¶ä¼šæ˜¾ç¤ºç¡®è®¤å¯¹è¯æ¡†ã€‚æ¤å¤–,它也会ä¸å—批é‡å·²è¯»æ“作的影å“。" end: "结æŸå…¬å‘Š" - tooManyActiveAnnouncementDescription: "若有大é‡æ´»åŠ¨å…¬å‘Šï¼Œå¯èƒ½ä¼šé€ æˆç”¨æˆ·ä½“验å¯èƒ½ä¸‹é™ã€‚请考虑归档已完æˆçš„公告。" + tooManyActiveAnnouncementDescription: "若有大é‡æ´»åŠ¨å…¬å‘Šï¼Œå¯èƒ½ä¼šé€ æˆç”¨æˆ·ä½“验下é™ã€‚请考虑归档已完æˆçš„公告。" readConfirmTitle: "æ ‡è®°ä¸ºå·²è¯»ï¼Ÿ" readConfirmText: "阅读“{title}â€çš„å†…å®¹å¹¶å°†å…¶æ ‡è®°ä¸ºå·²è¯»ã€‚" + shouldNotBeUsedToPresentPermanentInfo: "我们建议使用公告æ¥å‘布临时性的æµåŠ¨ä¿¡æ¯è€Œä¸æ˜¯å›ºå®šçš„常规信æ¯ï¼Œå› 为这å¯èƒ½æŸå®³ç”¨æˆ·ä½“验,尤其是对于新用户而言。" + dialogAnnouncementUxWarn: "åŒæ—¶å˜åœ¨ 2 个或以上的对è¯æ¡†å…¬å‘Šæžæœ‰å¯èƒ½å¯¹ç”¨æˆ·ä½“验产生负é¢çš„å½±å“,建议谨慎使用。" + silence: "ä¸å‘é€é€šçŸ¥" + silenceDescription: "å¼€å¯åŽï¼Œæ¤æ¡å…¬å‘Šå°†ä¸ä¼šå‘é€é€šçŸ¥ï¼Œä¹Ÿä¸å¼ºåˆ¶ç”¨æˆ·é˜…读。" _initialAccountSetting: accountCreated: "账户创建完æˆäº†ï¼" letsStartAccountSetup: "æ¥è¿›è¡Œå¸æˆ·çš„åˆå§‹è®¾ç½®å§ã€‚" @@ -1151,19 +1262,91 @@ _initialAccountSetting: pushNotificationDescription: "å¯ç”¨æŽ¨é€é€šçŸ¥çš„è¯ï¼Œå°±å¯ä»¥åœ¨è®¾å¤‡ä¸ŠæŽ¥æ”¶æ¥è‡ª {name} 的通知了。" initialAccountSettingCompleted: "åˆå§‹è®¾å®šå·²ç»å®Œæˆäº†ï¼" haveFun: "希望 {name} 在这里玩得开心ï¼" + youCanContinueTutorial: "您å¯ä»¥ç»§ç»äº†è§£ {name}(Misskey) 的使用教程,也å¯ä»¥åœ¨æ¤åœæ¢æ•™ç¨‹å¹¶ç«‹å³å¼€å§‹ä½¿ç”¨å®ƒã€‚\n" + startTutorial: "开始教å¦" skipAreYouSure: "è¦è·³è¿‡åˆå§‹è®¾ç½®å—?" laterAreYouSure: "è¦ç¨åŽå†è¿›è¡Œåˆå§‹è®¾å®šå—?" +_initialTutorial: + launchTutorial: "观看教å¦" + title: "æ•™å¦" + wellDone: "åšå¾—好" + skipAreYouSure: "是å¦é€€å‡ºæ•™å¦ï¼Ÿ" + _landing: + title: "欢迎æ¥åˆ°æ•™å¦" + description: "在这里,您å¯ä»¥æŸ¥çœ‹ Misskey 的基本使用方法和功能。" + _note: + title: "什么是帖å?" + description: "在 Misskey 上å‘è¡¨çš„æ–‡ç« ç§°ä¸ºã€Œå¸–åã€ã€‚帖å在时间线上按照时间顺åºæŽ’列,并实时更新。" + reply: "用æ¥å›žå¤å¸–å。å¯ä»¥å¯¹å›žå¤è¿›è¡Œå›žå¤ï¼Œä»Žè€Œå½¢æˆä¸€ä¸²å¯¹è¯ã€‚" + renote: "用æ¥å°†å¸–å共享到自己的时间线上。也å¯ä»¥åŠ 上自己的文å—然åŽå¼•ç”¨å®ƒã€‚" + reaction: "用æ¥æ·»åŠ 回应。详细信æ¯å°†åœ¨ä¸‹ä¸€é¡µè¿›è¡Œè¯´æ˜Žã€‚" + menu: "用æ¥è¿›è¡Œä¾‹å¦‚显示帖å详情ã€å¤åˆ¶é“¾æŽ¥ç‰å„ç§å„æ ·çš„æ“作。" + _reaction: + title: "什么是回应?" + description: "您å¯ä»¥åœ¨å¸–åä¸æ·»åŠ “回应â€ã€‚ 您å¯ä»¥ä½¿ç”¨å应轻æ¾åœ°è¡¨è¾¾ç‚¹â€œèµžâ€æ‰€æ— æ³•ä¼ è¾¾çš„ç»†å¾®å·®åˆ«ã€‚" + letsTryReacting: "回应å¯ä»¥é€šè¿‡ç‚¹å‡»å¸–åä¸çš„「+ã€æŒ‰é’®æ¥æ·»åŠ 。试ç€ç»™è¿™ä¸ªç¤ºä¾‹å¸–åæ·»åŠ ä¸€ä¸ªå›žåº”ï¼" + reactToContinue: "æ·»åŠ ä¸€ä¸ªå›žåº”æ¥ç»§ç»" + reactNotification: "当您的帖å被æŸäººæ·»åŠ 了回应时,将实时收到通知。" + reactDone: "通过按下「ーã€æŒ‰é’®ï¼Œå¯ä»¥å–消已ç»æ·»åŠ 的回应" + _timeline: + title: "时间线的è¿ä½œæ–¹å¼" + description1: "Misskey æ ¹æ®ä½¿ç”¨æ–¹å¼æä¾›äº†å¤šä¸ªæ—¶é—´çº¿ï¼ˆæ ¹æ®æœåŠ¡å™¨çš„设定,å¯èƒ½æœ‰ä¸€äº›è¢«ç¦ç”¨ï¼‰ã€‚" + home: "å¯ä»¥æŸ¥çœ‹æ‚¨å…³æ³¨çš„账户的帖å。" + local: "å¯ä»¥æŸ¥çœ‹è¿™ä¸ªæœåŠ¡å™¨ä¸Šæ‰€æœ‰ç”¨æˆ·å‘表的帖å。" + social: "å°†åŒæ—¶æ˜¾ç¤ºé¦–页时间线和本地时间线的内容。" + global: "å¯ä»¥æŸ¥çœ‹æ‰€æœ‰å·²è”åˆçš„æœåŠ¡å™¨ä¸Šçš„帖å。" + description2: "å¯ä»¥éšæ—¶åœ¨å±å¹•é¡¶éƒ¨åœ¨æ¯ä¸ªæ—¶é—´çº¿ä¹‹é—´åˆ‡æ¢ã€‚" + description3: "å¦å¤–,还有列表时间线和频é“时间线。请å‚阅{link}了解更多详细信æ¯ã€‚" + _postNote: + title: "帖åå‘布设置" + description1: "在 Misskey å‘布帖å时,您å¯ä»¥è®¾ç½®å„ç§é€‰é¡¹ã€‚å‘帖窗å£çœ‹èµ·æ¥æ˜¯è¿™æ ·çš„。\n" + _visibility: + description: "您å¯ä»¥é™åˆ¶è°å¯ä»¥çœ‹åˆ°æ‚¨çš„帖å。" + public: "å‘所有用户公开。\n" + home: "仅在首页时间线上å‘布。 关注者ã€ä»Žä¸ªäººèµ„料页查看过æ¥çš„用户ã€ä»¥åŠé€šè¿‡è½¬å¸–也能被别的用户看è§ã€‚" + followers: "仅对关注者å¯è§ã€‚ 除了您自己之外,没有人å¯ä»¥è½¬è´´ï¼Œå¹¶ä¸”åªæœ‰æ‚¨çš„关注者å¯ä»¥æŸ¥çœ‹å®ƒã€‚\n" + direct: "它将仅å‘指定用户公开,并且他们也会收到通知。 您å¯ä»¥ä½¿ç”¨å®ƒæ¥ä»£æ›¿ç§ä¿¡ã€‚\n" + doNotSendConfidencialOnDirect1: "å‘é€æ•æ„Ÿä¿¡æ¯æ—¶è¯·æ³¨æ„。\n" + doNotSendConfidencialOnDirect2: "ç›®æ ‡æœåŠ¡å™¨çš„管ç†å‘˜å¯ä»¥çœ‹åˆ°å‘å¸ƒçš„å†…å®¹ï¼Œå› æ¤å¦‚果您å‘ä¸å—信任的æœåŠ¡å™¨ä¸Šçš„用户å‘é€ç§ä¿¡ï¼Œåˆ™åœ¨å¤„ç†æ•æ„Ÿä¿¡æ¯æ—¶éœ€è¦å°å¿ƒã€‚" + localOnly: "ä¸å°†å¸–å推é€åˆ°å…¶å®ƒæœåŠ¡å™¨ã€‚ æ— è®ºä¸Šè¿°å…¬å¼€èŒƒå›´å¦‚ä½•ï¼Œå…¶å®ƒæœåŠ¡å™¨çš„ç”¨æˆ·å°†æ— æ³•çœ‹åˆ°é™„åŠ äº†æ¤è®¾å®šçš„帖å。\n" + _cw: + title: "éšè—内容 (CW)\n" + description: "显示「注解ã€é‡Œçš„内容而ä¸æ˜¯æ£æ–‡ã€‚点击「查看更多ã€å°†ä¼šæŠŠæ£æ–‡æ˜¾ç¤ºå‡ºæ¥ã€‚" + _exampleNote: + cw: "深夜报å¤ç¤¾ä¼š" + note: "茨了带巧克力的甜甜圈ðŸ©ðŸ˜‹" + useCases: "用于æœåŠ¡å™¨æ¡æ¬¾æ‰€è§„定的帖å,或对剧é€å†…容和æ•æ„Ÿå†…容进行自主规制。" + _howToMakeAttachmentsSensitive: + title: "å¦‚ä½•å°†é™„ä»¶æ ‡æ³¨ä¸ºæ•æ„Ÿå†…容?" + description: "对于æœåŠ¡å™¨æ–¹é’ˆæ‰€è¦æ±‚è¦æ±‚的,åˆæˆ–者ä¸é€‚åˆç›´æŽ¥å±•ç¤ºçš„é™„ä»¶ï¼Œè¯·æ·»åŠ ã€Œæ•æ„Ÿã€æ ‡è®°ã€‚\n" + tryThisFile: "è¯•è¯•çœ‹ï¼Œå°†é™„åŠ åˆ°æ¤çª—å£çš„图åƒæ ‡æ³¨ä¸ºæ•æ„Ÿï¼" + _exampleNote: + note: "拆纳豆包装时出错了…" + method: "è¦æ ‡æ³¨é™„件为æ•æ„Ÿå†…容,请å•å‡»è¯¥æ–‡ä»¶ä»¥æ‰“å¼€èœå•ï¼Œç„¶åŽå•å‡»â€œæ ‡è®°ä¸ºæ•æ„Ÿå†…容â€ã€‚" + sensitiveSucceeded: "é™„åŠ æ–‡ä»¶æ—¶ï¼Œè¯·éµå¾ªæœåŠ¡å™¨çš„æ¡æ¬¾æ¥è®¾ç½®æ£ç¡®æ•æ„Ÿè®¾å®šã€‚\n" + doItToContinue: "将图åƒæ ‡è®°ä¸ºæ•æ„ŸåŽæ‰èƒ½å¤Ÿç»§ç»" + _done: + title: "æ喜您,已ç»å®Œæˆäº†æ•™ç¨‹ðŸŽ‰\n" + description: "这里介ç»çš„åªæ˜¯å…¶ä¸ä¸€å°éƒ¨åˆ†çš„功能。 è¦äº†è§£æ›´å¤šæœ‰å…³å¦‚何使用 Misskey 的更多信æ¯ï¼Œè¯·è®¿é—® {link}。" +_timelineDescription: + home: "首页时间线å¯ä»¥æŸ¥çœ‹æ‚¨å…³æ³¨çš„账户的帖å。" + local: "本地时间线å¯ä»¥æŸ¥çœ‹è¿™ä¸ªæœåŠ¡å™¨ä¸Šæ‰€æœ‰ç”¨æˆ·å‘表的帖å。" + social: "社交时间线将åŒæ—¶æ˜¾ç¤ºé¦–页时间线和本地时间线的内容。" + global: "全局时间线å¯ä»¥æŸ¥çœ‹æ‰€æœ‰å·²è”åˆçš„æœåŠ¡å™¨ä¸Šçš„帖å。" _serverRules: description: "在新用户注册å‰æ˜¾ç¤ºæœåŠ¡å™¨çš„简å•è§„则。推è显示æœåŠ¡æ¡æ¬¾çš„主è¦å†…容。" _serverSettings: iconUrl: "å›¾æ ‡ URL" appIconDescription: "指定当 {host} 显示为 app æ—¶çš„å›¾æ ‡ã€‚" - appIconUsageExample: "例如:作为书ç¾æ·»åŠ 到 PWA 或手机主å±å¹•çš„时候" + appIconUsageExample: "如作为书ç¾æ·»åŠ 到 PWA 或手机主å±å¹•æ—¶" appIconStyleRecommendation: "å› ä¸ºæœ‰å¯èƒ½ä¼šè¢«è£åˆ‡ä¸ºåœ†å½¢æˆ–è€…åœ†è§’çŸ©å½¢ï¼Œå»ºè®®ä½¿ç”¨è¾¹ç¼˜å¸¦æœ‰ç•™ç™½èƒŒæ™¯çš„å›¾æ ‡ã€‚" appIconResolutionMustBe: "分辨率必须为 {resolution}。" manifestJsonOverride: "覆盖 manifest.json" shortName: "简称" shortNameDescription: "如果æœåŠ¡å™¨çš„æ£å¼å称很长,å¯ä»¥ç”¨ç®€ç§°æˆ–者別åæ¥æ›¿ä»£ã€‚" + fanoutTimelineDescription: "当å¯ç”¨æ—¶ï¼Œå¯æ˜¾è‘—æ高获å–å„ç§æ—¶é—´çº¿æ—¶çš„性能,并å‡è½»æ•°æ®åº“çš„è´Ÿè·ã€‚但是相对的 Redis 的内å˜ä½¿ç”¨é‡å°†ä¼šå¢žåŠ 。如果æœåŠ¡å™¨çš„内å˜ä¸æ˜¯å¾ˆå¤§ï¼Œåˆæˆ–者è¿è¡Œä¸ç¨³å®šçš„è¯å¯ä»¥æŠŠå®ƒå…³æŽ‰ã€‚" + fanoutTimelineDbFallback: "回退到数æ®åº“" + fanoutTimelineDbFallbackDescription: "当å¯ç”¨æ—¶ï¼Œè‹¥æ—¶é—´çº¿æœªè¢«ç¼“å˜ï¼Œåˆ™å°†é¢å¤–查询数æ®åº“。ç¦ç”¨è¯¥åŠŸèƒ½å¯é€šè¿‡ä¸æ‰§è¡Œå›žé€€å¤„ç†è¿›ä¸€æ¥å‡å°‘æœåŠ¡å™¨è´Ÿè½½ï¼Œä½†ä¼šé™åˆ¶å¯æ£€ç´¢çš„时间线范围。" _accountMigration: moveFrom: "从别的账å·è¿ç§»åˆ°æ¤è´¦æˆ·" moveFromSub: "为å¦ä¸€ä¸ªè´¦æˆ·å»ºç«‹åˆ«å" @@ -1390,7 +1573,7 @@ _achievements: description: "点了这里" _justPlainLucky: title: "è¶…é«˜æ ¡çº§çš„å¹¸è¿" - description: "æ¯ 10 秒有 0.01 的概率自动获得" + description: "æ¯ 10 秒有 0.005% 的概率自动获得" _setNameToSyuilo: title: "åƒç¥žä¸€æ ·å‘" description: "å°†å称设定为 syuilo" @@ -1421,6 +1604,15 @@ _achievements: _smashTestNotificationButton: title: "过度测试" description: "çŸæ—¶é—´å†…è¿žç»æµ‹è¯•é€šçŸ¥" + _tutorialCompleted: + title: "Misskey åˆå¦è€…课程 结业è¯ä¹¦" + description: "完æˆäº†æ•™å¦" + _bubbleGameExplodingHead: + title: "🤯" + description: "ä½ åˆæˆå‡ºäº†æ¸¸æˆé‡Œæœ€å¤§çš„Emoji" + _bubbleGameDoubleExplodingHead: + title: "两个🤯" + description: "ä½ åˆæˆå‡ºäº†2个游æˆé‡Œæœ€å¤§çš„Emoji" _role: new: "创建角色" edit: "编辑角色" @@ -1431,7 +1623,9 @@ _role: assignTarget: "授æƒå¯¹è±¡" descriptionOfAssignTarget: "<b>手动</b>指手动选择è°è¢«åŒ…括在这个角色ä¸ã€‚\n<b>符åˆæ¡ä»¶</b>指设置æ¡ä»¶ä»¥è‡ªåŠ¨åŒ…括符åˆæ¡ä»¶çš„用户。" manual: "手动" + manualRoles: "手动角色" conditional: "符åˆæ¡ä»¶" + conditionalRoles: "æ¡ä»¶è§’色" condition: "æ¡ä»¶" isConditionalRole: "这是一个æ¡ä»¶æŽ§åˆ¶çš„角色。" isPublic: "角色公开" @@ -1459,11 +1653,13 @@ _role: gtlAvailable: "查看全局时间线" ltlAvailable: "查看本地时间线" canPublicNote: "å…许公开å‘帖" + mentionMax: "帖å内最多æåŠæ•°" canInvite: "å‘放æœåŠ¡å™¨é‚€è¯·ç " - inviteLimit: "å¯å‘行邀请ç çš„æ•°é‡" + inviteLimit: "å¯ç”Ÿæˆé‚€è¯·ç çš„æ•°é‡" inviteLimitCycle: "邀请ç çš„å‘行间隔" inviteExpirationTime: "邀请ç 的有效日期" canManageCustomEmojis: "管ç†è‡ªå®šä¹‰è¡¨æƒ…符å·" + canManageAvatarDecorations: "管ç†å¤´åƒæŒ‚件" driveCapacity: "网盘容é‡" alwaysMarkNsfw: "æ€»æ˜¯å°†æ–‡ä»¶æ ‡è®°ä¸º NSFW" pinMax: "帖å置顶数é‡é™åˆ¶" @@ -1478,7 +1674,10 @@ _role: descriptionOfRateLimitFactor: "值越å°é™åˆ¶è¶Šå°‘,值越大é™åˆ¶è¶Šå¤šã€‚" canHideAds: "å¯ä»¥éšè—广告" canSearchNotes: "是å¦å¯ä»¥æœç´¢å¸–å" + canUseTranslator: "使用翻译功能" + avatarDecorationLimit: "å¯æ·»åŠ 头åƒæŒ‚件的最大个数" _condition: + roleAssignedTo: "已分é…给手动角色" isLocal: "是本地用户" isRemote: "是远程用户" createdLessThan: "账户创建时间少于" @@ -1506,6 +1705,7 @@ _emailUnavailable: disposable: "ä¸æ˜¯æ°¸ä¹…å¯ç”¨çš„地å€" mx: "邮件æœåŠ¡å™¨ä¸æ£ç¡®" smtp: "邮件æœåŠ¡å™¨æ²¡æœ‰å“应" + banned: "æ— æ³•ä½¿ç”¨æ¤é‚®ä»¶åœ°å€æ³¨å†Œ" _ffVisibility: public: "公开" followers: "åªæœ‰å…³æ³¨ä½ 的用户能看到" @@ -1526,6 +1726,10 @@ _ad: reduceFrequencyOfThisAd: "å‡å°‘æ¤å¹¿å‘Šçš„频率" hide: "ä¸æ˜¾ç¤º" timezoneinfo: "æ˜ŸæœŸå‡ æ˜¯ç”±æœåŠ¡å™¨çš„时区所指定的。" + adsSettings: "广告设置" + notesPerOneAd: "在实时更新时间线ä¸æ’入广告的间隔(帖å个数)" + setZeroToDisable: "设为 0 å°†ä¸åœ¨å®žæ—¶æ›´æ–°æ—¶é—´çº¿ä¸æŠ•æ”¾å¹¿å‘Š" + adsTooClose: "广告投放时间间隔过çŸå°†å¯èƒ½æ˜¾è‘—æŸå®³ç”¨æˆ·ä½“验。" _forgotPassword: enterEmail: "请输入您设置的电å邮箱地å€ï¼Œå¯†ç é‡ç½®é“¾æŽ¥å°†å‘é€è‡³è¯¥é‚®ç®±ä¸Šã€‚" ifNoEmail: "如果您没有设置电å邮件地å€ï¼Œè¯·è”系管ç†å‘˜ã€‚" @@ -1565,8 +1769,8 @@ _preferencesBackups: invalidFile: "æ— æ•ˆçš„çš„æ–‡ä»¶æ ¼å¼ã€‚" _registry: scope: "范围" - key: "主è¦" - keys: "主è¦" + key: "é”®" + keys: "é”®" domain: "域" createKey: "创建键" _aboutMisskey: @@ -1574,10 +1778,13 @@ _aboutMisskey: contributors: "主è¦è´¡çŒ®è€…" allContributors: "全体贡献者" source: "æºä»£ç " + original: "原版" + thisIsModifiedVersion: "{name}æ£åœ¨ä½¿ç”¨ä¿®æ”¹åŽçš„ Misskey。" translation: "翻译 Misskey" donate: "赞助 Misskey" morePatrons: "还有很多其它的人也在支æŒæˆ‘们,éžå¸¸æ„Ÿè°¢ðŸ¥°" patrons: "支æŒè€…" + projectMembers: "项目æˆå‘˜" _displayOfSensitiveMedia: respect: "éšè—æ•æ„Ÿåª’体" ignore: "显示æ•æ„Ÿåª’体" @@ -1602,6 +1809,7 @@ _channel: notesCount: "有 {n} 个帖å" nameAndDescription: "å称与æè¿°" nameOnly: "ä»…å称" + allowRenoteToExternal: "å…许在频é“外转帖åŠå¼•ç”¨" _menuDisplay: sideFull: "横å‘" sideIcon: "横å‘(å›¾æ ‡)" @@ -1693,6 +1901,14 @@ _sfx: notification: "通知" antenna: "天线接收" channel: "频é“通知" + reaction: "选择回应时" +_soundSettings: + driveFile: "使用网盘内的音频" + driveFileWarn: "选择网盘上的文件" + driveFileTypeWarn: "ä¸æ”¯æŒæ¤æ–‡ä»¶" + driveFileTypeWarnDescription: "请选择音频文件" + driveFileDurationWarn: "音频过长" + driveFileDurationWarnDescription: "使用长音频å¯èƒ½ä¼šå½±å“ Misskey 的使用。å³ä½¿è¿™æ ·ä¹Ÿè¦ç»§ç»å—?" _ago: future: "未æ¥" justNow: "最近" @@ -1706,7 +1922,12 @@ _ago: invalid: "没有" _timeIn: seconds: "{n}秒åŽ" + minutes: "{n} 分åŽ" + hours: "{n} å°æ—¶åŽ" days: "{n}天åŽ" + weeks: "{n} 周åŽ" + months: "{n} 月åŽ" + years: "{n} å¹´åŽ" _time: second: "秒" minute: "分" @@ -1778,6 +1999,55 @@ _permissions: "write:flash": "编辑 Play" "read:flash-likes": "查看 Play 的点赞" "write:flash-likes": "编辑 Play 的点赞列表" + "read:admin:abuse-user-reports": "查看æ¥è‡ªç”¨æˆ·çš„举报" + "write:admin:delete-account": "åˆ é™¤ç”¨æˆ·è´¦æˆ·" + "write:admin:delete-all-files-of-a-user": "åˆ é™¤ç”¨æˆ·æ‰€æœ‰çš„æ–‡ä»¶" + "read:admin:index-stats": "查看数æ®åº“索引相关的信æ¯" + "read:admin:table-stats": "查看数æ®åº“表相关的信æ¯" + "read:admin:user-ips": "查看用户 IP 地å€" + "read:admin:meta": "查看实例的元数æ®" + "write:admin:reset-password": "é‡ç½®ç”¨æˆ·å¯†ç " + "write:admin:resolve-abuse-user-report": "å°†æ¥è‡ªç”¨æˆ·çš„æŠ¥å‘Šæ ‡è®°ä¸ºã€Œå·²è§£å†³ã€" + "write:admin:send-email": "å‘é€é‚®ä»¶" + "read:admin:server-info": "查看æœåŠ¡å™¨ä¿¡æ¯" + "read:admin:show-moderation-log": "查看管ç†æ—¥å¿—" + "read:admin:show-user": "查看用户的éžå…¬å¼€ä¿¡æ¯" + "read:admin:show-users": "查看用户的éžå…¬å¼€ä¿¡æ¯" + "write:admin:suspend-user": "冻结用户" + "write:admin:unset-user-avatar": "åˆ é™¤ç”¨æˆ·å¤´åƒ" + "write:admin:unset-user-banner": "åˆ é™¤ç”¨æˆ·æ¨ªå¹…" + "write:admin:unsuspend-user": "解除用户冻结" + "write:admin:meta": "编辑实例元数æ®" + "write:admin:user-note": "编辑管ç†ç¬”è®°" + "write:admin:roles": "编辑角色" + "read:admin:roles": "查看角色" + "write:admin:relays": "编辑ä¸ç»§" + "read:admin:relays": "查看ä¸ç»§" + "write:admin:invite-codes": "编辑邀请ç " + "read:admin:invite-codes": "查看邀请ç " + "write:admin:announcements": "编辑公告" + "read:admin:announcements": "查看公告" + "write:admin:avatar-decorations": "编辑头åƒæŒ‚件" + "read:admin:avatar-decorations": "查看头åƒæŒ‚件" + "write:admin:federation": "编辑è”åˆç›¸å…³ä¿¡æ¯" + "write:admin:account": "编辑用户账户" + "read:admin:account": "查看用户相关情报" + "write:admin:emoji": "编辑表情文å—" + "read:admin:emoji": "查看表情文å—" + "write:admin:queue": "编辑作业队列" + "read:admin:queue": "查看作业队列相关情报" + "write:admin:promo": "è¿è¥æŽ¨å¹¿è¯´æ˜Ž" + "write:admin:drive": "编辑用户网盘" + "read:admin:drive": "查看用户网盘相关情报" + "read:admin:stream": "使用管ç†å‘˜ç”¨çš„ Websocket API" + "write:admin:ad": "编辑广告" + "read:admin:ad": "查看广告" + "write:invite-codes": "生æˆé‚€è¯·ç " + "read:invite-codes": "获å–å·²å‘行的邀请ç " + "write:clip-favorite": "编辑便ç¾çš„点赞" + "read:clip-favorite": "查看便ç¾çš„点赞" + "read:federation": "查看è”åˆç›¸å…³ä¿¡æ¯" + "write:report-abuse": "举报用户" _auth: shareAccessTitle: "应用程åºæŽˆæƒè®¸å¯" shareAccess: "您è¦æŽˆæƒå…许 “{name}†访问您的å¸æˆ·å—?" @@ -1832,6 +2102,7 @@ _widgets: _userList: chooseList: "选择列表" clicker: "点击器" + birthdayFollowings: "今天是他们的生日" _cw: hide: "éšè—" show: "查看更多" @@ -1894,15 +2165,18 @@ _profile: changeAvatar: "修改头åƒ" changeBanner: "修改横幅" verifiedLinkDescription: "如果将内容设置为 URL,当链接所指å‘的网页内包å«è‡ªå·±çš„个人资料链接时,å¯ä»¥æ˜¾ç¤ºä¸€ä¸ªå·²éªŒè¯å›¾æ ‡ã€‚" + avatarDecorationMax: "最多å¯æ·»åŠ {max} 个挂件" _exportOrImport: allNotes: "所有帖å" favoritedNotes: "收è—的帖å" + clips: "便ç¾" followingList: "关注ä¸" muteList: "å±è”½" blockingList: "拉黑" userLists: "列表" excludeMutingUsers: "排除å±è”½ç”¨æˆ·" excludeInactiveUsers: "排除ä¸æ´»è·ƒç”¨æˆ·" + withReplies: "在时间线ä¸åŒ…å«å¯¼å…¥ç”¨æˆ·çš„回å¤" _charts: federation: "è”åˆ" apRequest: "请求" @@ -2014,12 +2288,17 @@ _notification: pollEnded: "é—®å·è°ƒæŸ¥ç»“果已生æˆã€‚" newNote: "新的帖å" unreadAntennaNote: "天线 {name}" + roleAssigned: "授予的角色" emptyPushNotificationMessage: "推é€é€šçŸ¥å·²æ›´æ–°" achievementEarned: "获得æˆå°±" testNotification: "测试通知" checkNotificationBehavior: "检查通知显示" sendTestNotification: "å‘é€æµ‹è¯•é€šçŸ¥" notificationWillBeDisplayedLikeThis: "é€šçŸ¥å°†ä¼šè¿™æ ·è¡¨ç¤º" + reactedBySomeUsers: "{n} 人回应了" + renotedBySomeUsers: "{n} 人转å‘了" + followedBySomeUsers: "被 {n} 人关注" + flushNotification: "é‡ç½®é€šçŸ¥åŽ†å²" _types: all: "全部" note: "用户的新帖å" @@ -2032,6 +2311,7 @@ _notification: pollEnded: "é—®å·è°ƒæŸ¥ç»“æŸ" receiveFollowRequest: "收到关注请求" followRequestAccepted: "关注请求已通过" + roleAssigned: "授予的角色" achievementEarned: "å–å¾—çš„æˆå°±" app: "å…³è”应用的通知" _actions: @@ -2114,17 +2394,129 @@ _moderationLogTypes: deleteGlobalAnnouncement: "åˆ é™¤å…¨ä½“é€šçŸ¥" deleteUserAnnouncement: "åˆ é™¤ç”¨æˆ·é€šçŸ¥" resetPassword: "é‡ç½®å¯†ç " + suspendRemoteInstance: "åœæ¢è¿œç¨‹æœåŠ¡å™¨" + unsuspendRemoteInstance: "æ¢å¤è¿œç¨‹æœåŠ¡å™¨" + updateRemoteInstanceNote: "更新远程æœåŠ¡å™¨çš„管ç†ç¬”è®°" markSensitiveDriveFile: "æ ‡è®°ç½‘ç›˜æ–‡ä»¶ä¸ºæ•æ„Ÿåª’体" unmarkSensitiveDriveFile: "å–æ¶ˆæ ‡è®°ç½‘ç›˜æ–‡ä»¶ä¸ºæ•æ„Ÿåª’体" resolveAbuseReport: "处ç†ä¸¾æŠ¥" - createInvitation: "å‘行邀请ç " + createInvitation: "生æˆé‚€è¯·ç " createAd: "创建了广告" deleteAd: "åˆ é™¤äº†å¹¿å‘Š" updateAd: "更新了广告" + createAvatarDecoration: "新建头åƒæŒ‚件" + updateAvatarDecoration: "更新头åƒæŒ‚件" + deleteAvatarDecoration: "åˆ é™¤å¤´åƒæŒ‚件" + unsetUserAvatar: "清除用户头åƒ" + unsetUserBanner: "清除用户横幅" _fileViewer: + title: "文件信æ¯" + type: "文件类型" + size: "文件大å°" url: "URL" uploadedAt: "æ·»åŠ æ—¥æœŸ" + attachedNotes: "é™„åŠ åˆ°çš„å¸–å" + thisPageCanBeSeenFromTheAuthor: "æ¤é¡µåªèƒ½è¢«è¯¥æ–‡ä»¶çš„ä¸Šä¼ è€…æŸ¥çœ‹ã€‚" _externalResourceInstaller: + title: "从外部站点安装" + checkVendorBeforeInstall: "请在安装å‰ç¡®ä¿æ¥æºå¯é " + _plugin: + title: "è¦å®‰è£…æ¤æ’件å—?" + metaTitle: "æ’件信æ¯" + _theme: + title: "è¦å®‰è£…æ¤ä¸»é¢˜å—?" + metaTitle: "主题信æ¯" + _meta: + base: "基本é…色方案" + _vendorInfo: + title: "æ¥æºä¿¡æ¯" + endpoint: "å‚考端点" + hashVerify: "确认文件完整性" _errors: + _invalidParams: + title: "缺少å‚æ•°" + description: "缺少从外部站点获å–æ•°æ®æ‰€éœ€çš„ä¿¡æ¯ã€‚请检查 URL。" + _resourceTypeNotSupported: + title: "ä¸æ”¯æŒæ¤å¤–部资æº" + description: "ä¸æ”¯æŒä»Žæ¤å¤–部站点获å–的资æºç±»åž‹ã€‚请è”系站点管ç†å‘˜ã€‚" + _failedToFetch: + title: "获å–æ•°æ®å¤±è´¥" + fetchErrorDescription: "与外部站点的通信失败。 如果é‡è¯•åŽé—®é¢˜ä»ç„¶å˜åœ¨ï¼Œè¯·è”系站点管ç†å‘˜ã€‚" + parseErrorDescription: "æ— æ³•è¯»å–从外部站点å–å¾—çš„æ•°æ®ã€‚请è”系站点管ç†å‘˜ã€‚" + _hashUnmatched: + title: "æ— æ³•èŽ·å–æ£ç¡®æ•°æ®" + description: "æ— æ³•éªŒè¯æ•°æ®çš„完整性。安全起è§ï¼Œæ— 法继ç»å®‰è£…。请è”系站点管ç†å‘˜ã€‚" _pluginParseFailed: title: "AiScript 错误" + description: "虽然å–得了数æ®ï¼Œä½†æ˜¯ç”±äºŽ AiScript 解æžæ—¶å‡ºçŽ°é”™è¯¯ï¼Œæ— 法读å–æ•°æ®ã€‚请è”ç³»æ’件的作者。å¯åœ¨ Javascript 控制å°æŸ¥çœ‹é”™è¯¯è¯¦æƒ…。" + _pluginInstallFailed: + title: "æ’件安装失败" + description: "安装æ’件时出现错误。请å†è¯•ä¸€æ¬¡ã€‚å¯åœ¨ Javascript 控制å°æŸ¥çœ‹é”™è¯¯è¯¦æƒ…。" + _themeParseFailed: + title: "主题解æžé”™è¯¯" + description: "虽然å–得了主题文件,但是由于解æžæ—¶å‡ºçŽ°é”™è¯¯ï¼Œæ— æ³•åŠ è½½ä¸»é¢˜ã€‚è¯·è”系主题的作者。å¯åœ¨ Javascript 控制å°æŸ¥çœ‹é”™è¯¯è¯¦æƒ…。" + _themeInstallFailed: + title: "安装主题失败" + description: "安装主题时出错。请å†è¯•ä¸€æ¬¡ã€‚å¯åœ¨ Javascript 控制å°æŸ¥çœ‹é”™è¯¯è¯¦æƒ…。" +_dataSaver: + _media: + title: "åŠ è½½åª’ä½“" + description: "防æ¢è‡ªåŠ¨åŠ 载图åƒå’Œè§†é¢‘。 点击éšè—的图åƒ/视频å³å¯åŠ 载它们。\n" + _avatar: + title: "头åƒ" + description: "åœæ¢æ’放头åƒçš„动画。 由于动画图片的文件大å°å¯èƒ½æ¯”普通图åƒå¤§ï¼Œè¿™å¯ä»¥è¿›ä¸€æ¥å‡å°‘æ•°æ®æµé‡ã€‚" + _urlPreview: + title: "URL预览缩略图\n" + description: "å°†ä¸å†åŠ è½½ URL 预览缩略图。" + _code: + title: "代ç 高亮" + description: "如果使用了代ç é«˜äº®æ ‡è®°ï¼Œä¾‹å¦‚åœ¨ MFM ä¸ï¼Œåˆ™åœ¨ç‚¹å‡»ä¹‹å‰ä¸ä¼šåŠ 载。 代ç 高亮è¦æ±‚åŠ è½½æ¯ç§é«˜äº®è¯è¨€çš„定义文件,由于这些文件ä¸å†è‡ªåŠ¨åŠ è½½ï¼Œå› æ¤æœ‰æœ›å‡å°‘æ•°æ®ä¼ 输é‡ã€‚" +_hemisphere: + N: "北åŠçƒ" + S: "å—åŠçƒ" + caption: "在æŸäº›å®¢æˆ·ç«¯è®¾ç½®ä¸ç”¨æ¥ç¡®å®šå£èŠ‚" +_reversi: + reversi: "黑白棋" + gameSettings: "对局设置" + blackOrWhite: "先手/åŽæ‰‹" + blackIs: "{name}执黑(先手)" + rules: "规则" + thisGameIsStartedSoon: "对局å³å°†å¼€å§‹" + waitingForOther: "ç‰å¾…对手准备" + waitingForMe: "ç‰å¾…ä½ çš„å‡†å¤‡" + waitingBoth: "请准备" + ready: "准备就绪" + cancelReady: "é‡æ–°å‡†å¤‡" + opponentTurn: "对手的回åˆ" + myTurn: "ä½ çš„å›žåˆ" + turnOf: "{name}的回åˆ" + pastTurnOf: "{name}的回åˆ" + surrender: "认输" + surrendered: "已认输" + timeout: "超时" + drawn: "平局" + won: "{name}获胜" + black: "黑" + white: "白" + total: "总计" + turnCount: "第{count}回åˆ" + myGames: "我的对局" + allGames: "所有对局" + ended: "结æŸ" + playing: "对局ä¸" + canPutEverywhere: "æ— é™åˆ¶æ”¾ç½®æ¨¡å¼" + timeLimitForEachTurn: "1回åˆçš„时间é™åˆ¶" + freeMatch: "自由匹é…" + lookingForPlayer: "æ£åœ¨å¯»æ‰¾å¯¹æ‰‹" + gameCanceled: "对局被å–消了" + shareToTlTheGameWhenStart: "开始时在时间线å‘布对局" + iStartedAGame: "对局开始ï¼#MisskeyReversi" + opponentHasSettingsChanged: "对手更改了设定" + allowIrregularRules: "å…许éžå¸¸è§„规则(完全自由)" + disallowIrregularRules: "ç¦æ¢éžå¸¸è§„规则" + showBoardLabels: "显示行å·å’Œåˆ—å·" + useAvatarAsStone: "用头åƒä½œä¸ºæ£‹å" +_offlineScreen: + title: "ç¦»çº¿â€”â€”æ— æ³•è¿žæŽ¥åˆ°æœåŠ¡å™¨" + header: "æ— æ³•è¿žæŽ¥åˆ°æœåŠ¡å™¨" + diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 36b6e77e9bc4feb6b63b86b8d00638a7f42d281b..5cdecc10ac97b0d64f126d60658edd1d28116a3e 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -66,7 +66,7 @@ showMore: "載入更多" showLess: "關閉" youGotNewFollower: "您有新的追隨者" receiveFollowRequest: "您有新的追隨請求" -followRequestAccepted: "追隨請求已接å—" +followRequestAccepted: "追隨請求已被接å—" mention: "æåŠ" mentions: "æåŠ" directNotes: "ç§è¨Š" @@ -91,7 +91,7 @@ manageLists: "管ç†æ¸…å–®" error: "錯誤" somethingHappened: "發生錯誤" retry: "é‡è©¦" -pageLoadError: "載入é é¢å¤±æ•—" +pageLoadError: "無法載入é é¢ã€‚" pageLoadErrorDescription: "這通常是網路錯誤或ç€è¦½å™¨å¿«å–殘留而引起的。請先清除ç€è¦½å™¨å¿«å–,ç¨å¾Œå†é‡è©¦ã€‚" serverIsDead: "伺æœå™¨æ²’有回應。請ç¨ç‰ç‰‡åˆ»å†è©¦ã€‚" youShouldUpgradeClient: "è«‹é‡æ–°è¼‰å…¥ä»¥ä½¿ç”¨æ–°ç‰ˆå®¢æˆ¶ç«¯é¡¯ç¤ºæ¤é é¢ã€‚" @@ -130,6 +130,7 @@ overwriteFromPinnedEmojis: "從一般複寫è¨å®š" reactionSettingDescription2: "拖動以交æ›ï¼Œé»žæ“Šä»¥åˆªé™¤ï¼ŒæŒ‰ä¸‹ã€Œ+ã€ä»¥æ–°å¢žã€‚" rememberNoteVisibility: "記ä½è²¼æ–‡å¯è¦‹æ€§" attachCancel: "移除附件" +deleteFile: "刪除檔案" markAsSensitive: "標記為æ•æ„Ÿå…§å®¹" unmarkAsSensitive: "å–消標記為æ•æ„Ÿå…§å®¹" enterFileName: "請輸入檔案å稱" @@ -379,6 +380,11 @@ hcaptcha: "hCaptcha" enableHcaptcha: "啟用 hCaptcha" hcaptchaSiteKey: "網站金鑰" hcaptchaSecretKey: "金鑰" +mcaptcha: "mCaptcha" +enableMcaptcha: "啟用 mCaptcha" +mcaptchaSiteKey: "網站金鑰" +mcaptchaSecretKey: "金鑰" +mcaptchaInstanceUrl: "mCaptcha 的實例網å€" recaptcha: "reCAPTCHA" enableRecaptcha: "啟用 reCAPTCHA" recaptchaSiteKey: "網站金鑰" @@ -592,13 +598,13 @@ menu: "é¸å–®" divider: "分隔線" addItem: "æ–°å¢žé …ç›®" rearrange: "排åºæ–¹å¼" -relays: "ä¸ç¹¼" -addRelay: "新增ä¸ç¹¼" +relays: "ä¸ç¹¼å™¨" +addRelay: "新增ä¸ç¹¼å™¨" inboxUrl: "收件夾URL" -addedRelays: "å·²åŠ å…¥çš„ä¸ç¹¼" +addedRelays: "å·²åŠ å…¥çš„ä¸ç¹¼å™¨" serviceworkerInfo: "如è¦ä½¿ç”¨æŽ¨æ’通知,需è¦å•Ÿç”¨æ¤é¸é …並è¨å®šé‡‘鑰。" deletedNote: "已刪除的貼文" -invisibleNote: "ç§å¯†çš„貼文" +invisibleNote: "ç§äººè²¼æ–‡" enableInfiniteScroll: "啟用自動滾動é é¢æ¨¡å¼" visibility: "å¯è¦‹æ€§" poll: "票é¸æ´»å‹•" @@ -626,6 +632,7 @@ medium: "ä¸" small: "å°" generateAccessToken: "發行å˜å–權æ–" permission: "權é™" +adminPermission: "管ç†å“¡æ¬Šé™" enableAll: "啟用全部" disableAll: "åœç”¨å…¨éƒ¨" tokenRequested: "å…許å˜å–帳戶" @@ -669,6 +676,7 @@ useGlobalSettingDesc: "啟用時,將使用帳戶通知è¨å®šã€‚åœç”¨æ™‚,則 other: "其他" regenerateLoginToken: "é‡æ–°ç”¢ç”Ÿç™»å…¥æ¬Šæ–" regenerateLoginTokenDescription: "é‡æ–°ç”¢ç”Ÿç”¨æ–¼ç™»å…¥çš„內部權æ–。一般情æ³ä¸‹æ˜¯ä¸éœ€è¦é€™æ¨£åšçš„。é‡æ–°ç”¢ç”Ÿå¾Œï¼Œæ‰€æœ‰è£ç½®å°‡æœƒè¢«ç™»å‡ºã€‚" +theKeywordWhenSearchingForCustomEmoji: "這是æœå°‹è‡ªè¨‚表情符號時的關éµå—" setMultipleBySeparatingWithSpace: "您å¯ä»¥ä½¿ç”¨ç©ºæ ¼åˆ†éš”å¤šå€‹é …ç›®ã€‚" fileIdOrUrl: "檔案 ID 或 URL" behavior: "行為" @@ -682,7 +690,7 @@ abuseReported: "檢舉完æˆã€‚æ„Ÿè¬æ‚¨çš„å ±å‘Šã€‚" reporter: "檢舉者" reporteeOrigin: "檢舉來æº" reporterOrigin: "檢舉者來æº" -forwardReport: "å°‡å ±å‘Šè½‰é€çµ¦é 端實例" +forwardReport: "å°‡å ±å‘Šè½‰é€çµ¦é 端伺æœå™¨" forwardReportIsAnonymous: "在é 端實例上看ä¸åˆ°æ‚¨çš„è³‡è¨Šï¼Œé¡¯ç¤ºçš„å ±å‘Šè€…æ˜¯åŒ¿å的系统帳戶。" send: "發é€" abuseMarkAsResolved: "處ç†å®Œç•¢" @@ -690,7 +698,7 @@ openInNewTab: "在新分é ä¸é–‹å•Ÿ" openInSideView: "在å´æ¬„ä¸é–‹å•Ÿ" defaultNavigationBehaviour: "é è¨å°Žèˆª" editTheseSettingsMayBreakAccount: "修改這些è¨å®šå¯èƒ½æœƒæ¯€æ您的帳戶" -instanceTicker: "貼文的實例來æº" +instanceTicker: "貼文的伺æœå™¨è³‡è¨Š" waitingFor: "ç‰å¾…{x}" random: "隨機" system: "系統" @@ -811,7 +819,7 @@ active: "最近活èº" offline: "離線" notRecommended: "ä¸æŽ¨è–¦" botProtection: "Bot 防è·" -instanceBlocking: "å·²å°éŽ–的實例" +instanceBlocking: "å·²å°éŽ–或ç¦è¨€çš„伺æœå™¨" selectAccount: "é¸æ“‡å¸³æˆ¶" switchAccount: "切æ›å¸³æˆ¶" enabled: "已啟用" @@ -954,7 +962,7 @@ cannotUploadBecauseNoFreeSpace: "由於雲端硬碟沒有å¯ç”¨ç©ºé–“ï¼Œå› æ¤ cannotUploadBecauseExceedsFileSizeLimit: "由於超éŽäº†æª”案大å°çš„é™åˆ¶ï¼Œç„¡æ³•ä¸Šå‚³ã€‚" beta: "測試版" enableAutoSensitive: "自動 NSFW 判定" -enableAutoSensitiveDescription: "如果å¯ç”¨ï¼Œå®ƒå°‡ä½¿ç”¨æ©Ÿå™¨å¸ç¿’技術判斷檔案是å¦éœ€è¦æ¨™è¨˜ç‚ºæ•æ„Ÿã€‚å³ä½¿é—œé–‰æ¤åŠŸèƒ½ï¼Œä¹Ÿå¯èƒ½æœƒä¾å¯¦ä¾‹è¦å‰‡è€Œè‡ªå‹•å•Ÿç”¨ã€‚" +enableAutoSensitiveDescription: "如果å¯è¡Œï¼Œå®ƒå°‡ä½¿ç”¨æ©Ÿå™¨å¸ç¿’技術判斷檔案是å¦éœ€è¦æ¨™è¨˜ç‚ºæ•æ„Ÿã€‚å³ä½¿é—œé–‰æ¤åŠŸèƒ½ï¼Œä¹Ÿå¯èƒ½æœƒä¾ä¼ºæœå™¨è¦å‰‡è€Œè‡ªå‹•å•Ÿç”¨ã€‚" activeEmailValidationDescription: "主動地驗è‰ä½¿ç”¨è€…çš„é›»å郵件地å€ï¼Œä»¥ç¢ºå®šæ˜¯å¦æ˜¯ä¸€æ¬¡æ€§åœ°å€ä»¥åŠæ˜¯å¦å¯ä»¥çœŸæ£èˆ‡å…¶é€²è¡Œé€šè¨Šã€‚é—œé–‰æ™‚ï¼Œåƒ…æª¢æŸ¥æ ¼å¼æ˜¯å¦æ£ç¢ºã€‚" navbar: "導覽列" shuffle: "隨機" @@ -964,7 +972,7 @@ pushNotification: "推æ’通知" subscribePushNotification: "啟用推æ’通知" unsubscribePushNotification: "åœç”¨æŽ¨æ’通知" pushNotificationAlreadySubscribed: "推æ’通知啟用ä¸" -pushNotificationNotSupported: "ç€è¦½å™¨æˆ–實例ä¸æ”¯æ´æŽ¨æ’通知" +pushNotificationNotSupported: "ç€è¦½å™¨æˆ–伺æœå™¨ä¸æ”¯æ´æŽ¨æ’通知" sendPushNotificationReadMessage: "如果已閱讀通知與訊æ¯ï¼Œå°±åˆªé™¤æŽ¨æ’通知" sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}ã€é€šçŸ¥å°‡ç«‹åˆ»é¡¯ç¤ºã€‚å¯èƒ½æœƒæ›´æ¶ˆè€—è£ç½®é›»æ± 。" windowMaximize: "最大化" @@ -983,6 +991,7 @@ neverShow: "ä¸å†é¡¯ç¤º" remindMeLater: "以後å†èªª" didYouLikeMisskey: "æ‚¨å–œæ¡ Misskey 嗎?" pleaseDonate: "Misskey 是由 {host} 使用的å…費軟體。請贊助我們,讓開發得以æŒçºŒï¼" +correspondingSourceIsAvailable: "å°æ‡‰çš„原始碼å¯ä»¥åœ¨ {anchor} 處找到。" roles: "角色" role: "角色" noRole: "沒有角色" @@ -1033,6 +1042,9 @@ resetPasswordConfirm: "é‡è¨å¯†ç¢¼ï¼Ÿ" sensitiveWords: "æ•æ„Ÿè©ž" sensitiveWordsDescription: "å°‡å«æœ‰è¨å®šè©žå½™çš„貼文å¯è¦‹æ€§è¨ç‚ºç™¼é€è‡³é¦–é 。å¯ä»¥ç”¨æ›è¡Œä¾†é€²è¡Œè¤‡æ•¸çš„è¨å®šã€‚" sensitiveWordsDescription2: "ç©ºæ ¼ä»£è¡¨ã€Œä»¥åŠã€ï¼ˆAND),斜線包åœé—œéµå—代表使用æ£è¦è¡¨é”å¼ã€‚" +prohibitedWords: "ç¦èªž" +prohibitedWordsDescription: "當è¦ç™¼å¸ƒåŒ…å«ç¦èªžçš„貼文時,會出ç¾éŒ¯èª¤ã€‚å¯ä»¥ç”¨æ›è¡Œåˆ†éš”來è¨å®šå¤šå€‹ç¦èªžã€‚" +prohibitedWordsDescription2: "ç©ºæ ¼ä»£è¡¨ã€Œä»¥åŠã€ï¼ˆAND),斜線包åœé—œéµå—代表使用æ£è¦è¡¨é”å¼ã€‚" hiddenTags: "éš±è—標籤" hiddenTagsDescription: "è¨å®šçš„標籤ä¸æœƒåœ¨è¶¨å‹¢ä¸é¡¯ç¤ºï¼Œæ›è¡Œå¯ä»¥è¨å®šå¤šå€‹æ¨™ç±¤ã€‚" notesSearchNotAvailable: "無法使用æœå°‹è²¼æ–‡åŠŸèƒ½ã€‚" @@ -1051,6 +1063,8 @@ limitWidthOfReaction: "é™åˆ¶å應的最大寬度,並縮å°é¡¯ç¤ºå°ºå¯¸ã€‚" noteIdOrUrl: "貼文ID或URL" video: "影片" videos: "影片" +audio: "音效" +audioFiles: "音效檔案" dataSaver: "數據節çœæ¨¡å¼" accountMigration: "é·ç§»å¸³æˆ¶" accountMoved: "這個使用者已é·ç§»è‡³æ–°çš„帳戶:" @@ -1146,6 +1160,7 @@ showRenotes: "顯示其他人的轉發貼文" edited: "已編輯" notificationRecieveConfig: "接å—通知的è¨å®š" mutualFollow: "互相追隨" +followingOrFollower: "追隨ä¸æˆ–追隨者" fileAttachedOnly: "顯示包å«é™„件的貼文" showRepliesToOthersInTimeline: "顯示給其他人的回覆" hideRepliesToOthersInTimeline: "在時間軸上隱è—給其他人的回覆" @@ -1154,6 +1169,13 @@ hideRepliesToOthersInTimelineAll: "在時間軸ä¸åŒ…å«è¿½éš¨ä¸æ‰€æœ‰äººçš„回 confirmShowRepliesAll: "進行æ¤æ“作後無法復原。您真的希望時間軸「包å«ã€æ‚¨ç›®å‰è¿½éš¨çš„所有人的回覆嗎?" confirmHideRepliesAll: "進行æ¤æ“作後無法復原。您真的希望時間軸「ä¸åŒ…å«ã€æ‚¨ç›®å‰è¿½éš¨çš„所有人的回覆嗎?" externalServices: "外部æœå‹™" +sourceCode: "原始碼" +sourceCodeIsNotYetProvided: "尚未æ供原始碼,請洽詢管ç†å“¡è§£æ±ºé€™å€‹å•é¡Œã€‚" +repositoryUrl: "儲å˜åº« URL" +repositoryUrlDescription: "如果å˜åœ¨å¯å…¬é–‹å–得原始碼的儲å˜åº«ï¼Œè«‹è¼¸å…¥å…¶ URL。 如果您按原樣使用 Misskey(ä¸å°åŽŸå§‹ç¢¼é€²è¡Œä»»ä½•æ›´æ”¹ï¼‰ï¼Œè«‹è¼¸å…¥ https://github.com/misskey-dev/misskey。" +repositoryUrlOrTarballRequired: "如果儲å˜åº«ä¸æ˜¯å…¬é–‹çš„ï¼Œå‰‡å¿…é ˆæä¾› tarball。 詳細資訊請åƒé–± .config/example.yml。" +feedback: "æ„見回饋" +feedbackUrl: "æ„見回饋 URL" impressum: "營é‹è€…資訊" impressumUrl: "營é‹è€…資訊網å€" impressumDescription: "在德國與部份地å€å¿…é ˆè¦æ˜Žç¢ºé¡¯ç¤ºç‡Ÿé‹è€…資訊。" @@ -1182,7 +1204,40 @@ overwriteContentConfirm: "確定è¦è¦†è“‹ç›®å‰çš„內容嗎?" seasonalScreenEffect: "隨å£ç¯€è®Šæ›ç•«é¢çš„呈ç¾" decorate: "è¨ç½®é åƒè£é£¾" addMfmFunction: "æ’å…¥MFM功能語法" -enableQuickAddMfmFunction: "顯示高級MFMé¸æ“‡å™¨" +enableQuickAddMfmFunction: "顯示高級 MFM é¸æ“‡å™¨" +bubbleGame: "氣泡éŠæˆ²" +sfx: "音效" +soundWillBePlayed: "å°‡æ’放音效" +showReplay: "觀看é‡æ’" +replay: "é‡æ’" +replaying: "é‡æ’ä¸" +endReplay: "退出é‡æ’" +copyReplayData: "複製é‡æ’資料" +ranking: "排行榜" +lastNDays: "éŽåŽ» {n} 天" +backToTitle: "回到éŠæˆ²æ¨™é¡Œé " +hemisphere: "您居ä½çš„地å€" +withSensitive: "顯示包å«æ•æ„Ÿæª”案的貼文" +userSaysSomethingSensitive: "åŒ…å« {name} æ•æ„Ÿæª”案的貼文" +enableHorizontalSwipe: "滑動切æ›æ™‚間軸" +loading: "載入ä¸" +surrender: "退出" +gameRetry: "å†è©¦ä¸€æ¬¡" +_bubbleGame: + howToPlay: "玩法說明" + hold: "ä¿ç•™" + _score: + score: "分數" + scoreYen: "賺å–的金é¡" + highScore: "最高分" + maxChain: "最大çµåˆæ•¸" + yen: "{yen} 日圓" + estimatedQty: "{qty}個" + scoreSweets: "飯糰 {onigiriQtyWithUnit}" + _howToPlay: + section1: "調整ä½ç½®ä¸¦å°‡ç‰©é«”放入盒åä¸ã€‚" + section2: "當相åŒé¡žåž‹çš„物體é»åœ¨ä¸€èµ·æ™‚,它們會變æˆä¸åŒçš„物體,您就會得到分數。" + section3: "如果物體從盒å裡溢出,éŠæˆ²å°±çµæŸäº†ã€‚é€éŽèžåˆç‰©é«”而ä¸æº¢å‡ºç›’å來ç²å¾—高分ï¼" _announcement: forExistingUsers: "僅é™æ—¢æœ‰çš„使用者" forExistingUsersDescription: "啟用代表僅å‘ç¾å˜ä½¿ç”¨è€…顯示;åœç”¨ä»£è¡¨å¼µè²¼å¾Œè¨»å†Šçš„新使用者也會看到。" @@ -1192,7 +1247,7 @@ _announcement: tooManyActiveAnnouncementDescription: "有éŽå¤šå…¬å‘Šå¯èƒ½æœƒå½±éŸ¿ä½¿ç”¨è€…體驗。請考慮æ¸æª”å·²çµæŸçš„公告。" readConfirmTitle: "標記為已讀嗎?" readConfirmText: "閱讀「{title}ã€çš„內容並標記為已讀。" - shouldNotBeUsedToPresentPermanentInfo: "由於å¯èƒ½æœƒç ´å£žä½¿ç”¨è€…體驗,尤其是å°æ–¼æ–°ä½¿ç”¨è€…而言,我們建è°ä½¿ç”¨å…¬å‘Šä¾†ç™¼å¸ƒæœ‰æ™‚效性的資訊而ä¸æ˜¯å›ºå®šä¸è®Šçš„資訊。" + shouldNotBeUsedToPresentPermanentInfo: "為了é¿å…æ害新用戶的使用體驗,建è°ä½¿ç”¨å…¬å‘Šä¾†ç™¼å¸ƒå³æ™‚性的訊æ¯ï¼Œè€Œä¸æ˜¯ç”¨æ–¼å›ºå®šä¸è®Šçš„資訊。" dialogAnnouncementUxWarn: "如果åŒæ™‚有 2 個以上å°è©±æ–¹å¡Šå½¢å¼çš„公告å˜åœ¨ï¼Œå°æ–¼ä½¿ç”¨è€…體驗很å¯èƒ½æœƒæœ‰ä¸è‰¯çš„å½±éŸ¿ï¼Œå› æ¤å»ºè°è¬¹æ…Žä½¿ç”¨ã€‚" silence: "ä¸ç™¼é€é€šçŸ¥" silenceDescription: "啟用æ¤é¸é …後,將ä¸æœƒç™¼é€æ¤å…¬å‘Šçš„通知,並且無需將其標記為已讀。" @@ -1501,7 +1556,7 @@ _achievements: description: "首é 時間軸在一分é˜å…§å‡ºç¾è¶…éŽäºŒå篇貼文" _viewInstanceChart: title: "分æžå¸«" - description: "顯示了實例的圖表" + description: "顯示了伺æœå™¨çš„圖表" _outputHelloWorldOnScratchpad: title: "Hello, world!" description: "在 AiScript 控制臺輸出了「hello worldã€" @@ -1553,13 +1608,20 @@ _achievements: _tutorialCompleted: title: "Misskey新手講座 çµæ¥è‰æ›¸" description: "已完æˆæ•™å¸èª²ç¨‹" + _bubbleGameExplodingHead: + title: "🤯" + description: "氣泡éŠæˆ²ä¸æœ€å¤§çš„物體出ç¾äº†" + _bubbleGameDoubleExplodingHead: + title: "é›™é‡ðŸ¤¯" + description: "氣泡éŠæˆ²ä¸æœ€å¤§çš„物體åŒæ™‚出ç¾äº†å…©å€‹" + flavor: "這樣大å°çš„便當盒,用 🤯 🤯 ç¨å¾®è£æ»¿ä¸€äº›å§" _role: new: "建立角色" edit: "編輯角色" name: "角色å稱" description: "角色æè¿° " permission: "角色的權é™" - descriptionOfPermission: "<b>審查員</b>執行與審查相關的基本æ“作。\n<b>管ç†å“¡</b>能變更實例的全部è¨å®š" + descriptionOfPermission: "<b>審查員</b>執行與審查相關的基本æ“作。\n<b>管ç†å“¡</b>能變更伺æœå™¨çš„全部è¨å®šã€‚" assignTarget: "指派目標" descriptionOfAssignTarget: "<b>手動</b>是以手動管ç†é€™å€‹è§’色包å«çš„人員。\n<b>符åˆæ¢ä»¶</b>是è¨å®šæ¢ä»¶ä»¥è‡ªå‹•åŒ…å«ç¬¦åˆæ¢ä»¶çš„使用者。" manual: "手動" @@ -1575,7 +1637,7 @@ _role: baseRole: "基本角色" useBaseValue: "使用基本角色的值" chooseRoleToAssign: "é¸æ“‡è¦æŒ‡æ´¾çš„角色" - iconUrl: "圖示的URL" + iconUrl: "圖示的 URL" asBadge: "é¡¯ç¤ºç‚ºå¾½ç« " descriptionOfAsBadge: "開啟的話,角色圖示會顯示在使用者å稱æ—邊。" isExplorable: "讓使用者更容易找到您" @@ -1593,7 +1655,8 @@ _role: gtlAvailable: "ç€è¦½å…¨åŸŸæ™‚間軸" ltlAvailable: "ç€è¦½æœ¬åœ°æ™‚間軸" canPublicNote: "å…許公開貼文" - canInvite: "發行實例邀請碼" + mentionMax: "貼文內的最大æåŠæ•¸" + canInvite: "發行伺æœå™¨é‚€è«‹ç¢¼" inviteLimit: "å¯å»ºç«‹é‚€è«‹ç¢¼çš„數é‡" inviteLimitCycle: "邀請碼的發放間隔" inviteExpirationTime: "邀請碼的有效日期" @@ -1616,6 +1679,7 @@ _role: canUseTranslator: "使用翻è¯åŠŸèƒ½" avatarDecorationLimit: "é åƒè£é£¾çš„最大è¨ç½®é‡" _condition: + roleAssignedTo: "手動指派角色完æˆ" isLocal: "本地使用者" isRemote: "é 端使用者" createdLessThan: "å¸³æˆ¶åŠ å…¥æ™‚é–“ä¸è¶…éŽ" @@ -1671,7 +1735,7 @@ _ad: _forgotPassword: enterEmail: "請輸入您的帳戶註冊的電å郵件地å€ã€‚ 密碼é‡ç½®é€£çµå°‡è¢«ç™¼é€åˆ°è©²é›»å郵件地å€ã€‚" ifNoEmail: "如果您還沒有註冊您的電å郵件地å€ï¼Œè«‹è¯ç¹«ç®¡ç†å“¡ã€‚ " - contactAdmin: "æ¤å¯¦ä¾‹ä¸æ”¯æŒé›»å郵件,請è¯ç¹«æ‚¨çš„管ç†å“¡é‡ç½®æ‚¨çš„密碼。 " + contactAdmin: "本伺æœå™¨ä¸æ”¯æ´é›»å郵件,請è¯ç¹«æ‚¨çš„管ç†å“¡é‡ç½®æ‚¨çš„密碼。 " _gallery: my: "我的貼文" liked: "å–œæ¡çš„貼文" @@ -1716,6 +1780,8 @@ _aboutMisskey: contributors: "主è¦è²¢ç»è€…" allContributors: "全體貢ç»äººå“¡" source: "原始碼" + original: "原始" + thisIsModifiedVersion: "{name} 使用原始 Misskey 的修改版本。" translation: "ç¿»è¯ Misskey" donate: "贊助 Misskey" morePatrons: "還有許許多多幫助我們的其他人,éžå¸¸æ„Ÿè¬ä½ 們。 🥰" @@ -1758,8 +1824,8 @@ _wordMute: _instanceMute: instanceMuteDescription: "包括å°è¢«éœéŸ³ä¼ºæœå™¨ä¸Šçš„使用者的回覆,被è¨å®šçš„伺æœå™¨ä¸Šæ‰€æœ‰è²¼æ–‡åŠè½‰ç™¼éƒ½æœƒè¢«éœéŸ³ã€‚" instanceMuteDescription2: "è¨å®šæ™‚以æ›è¡Œé€²è¡Œåˆ†éš”" - title: "將隱è—被è¨å®šçš„實例貼文。" - heading: "將實例éœéŸ³" + title: "將隱è—被è¨å®šçš„伺æœå™¨è²¼æ–‡ã€‚" + heading: "將伺æœå™¨éœéŸ³" _theme: explore: "探索佈景主題" install: "安è£ä½ˆæ™¯ä¸»é¡Œ" @@ -1927,14 +1993,63 @@ _permissions: "write:user-groups": "編輯使用者群組" "read:channels": "å·²æŸ¥çœ‹çš„é »é“" "write:channels": "ç·¨è¼¯é »é“" - "read:gallery": "ç€è¦½åœ–庫" - "write:gallery": "æ“作圖庫" - "read:gallery-likes": "讀å–å–œæ¡çš„圖片" - "write:gallery-likes": "æ“作喜æ¡çš„圖片" + "read:gallery": "ç€è¦½ç›¸ç°¿" + "write:gallery": "編輯相簿" + "read:gallery-likes": "ç€è¦½ç›¸ç°¿çš„讚" + "write:gallery-likes": "編輯相簿的讚" "read:flash": "檢視 Play" "write:flash": "編輯 Play" "read:flash-likes": "檢視 Play 的讚" "write:flash-likes": "編輯 Play 的讚" + "read:admin:abuse-user-reports": "查看來自使用者的檢舉" + "write:admin:delete-account": "刪除使用者帳戶" + "write:admin:delete-all-files-of-a-user": "刪除使用者的所有檔案" + "read:admin:index-stats": "查看資料庫索引的相關資訊" + "read:admin:table-stats": "æŸ¥çœ‹è³‡æ–™åº«è¡¨æ ¼çš„ç›¸é—œè³‡è¨Š" + "read:admin:user-ips": "查看使用者的 IP ä½å€" + "read:admin:meta": "查看實例的元資料" + "write:admin:reset-password": "é‡è¨ä½¿ç”¨è€…的密碼" + "write:admin:resolve-abuse-user-report": "解決來自使用者的檢舉" + "write:admin:send-email": "發é€éƒµä»¶" + "read:admin:server-info": "查看伺æœå™¨çš„資訊" + "read:admin:show-moderation-log": "查看審查紀錄" + "read:admin:show-user": "查看使用者的ç§å¯†è³‡è¨Š" + "read:admin:show-users": "查看使用者的ç§å¯†è³‡è¨Š" + "write:admin:suspend-user": "å‡çµä½¿ç”¨è€…" + "write:admin:unset-user-avatar": "刪除使用者的é åƒ" + "write:admin:unset-user-banner": "刪除使用者的橫幅" + "write:admin:unsuspend-user": "解除å‡çµä½¿ç”¨è€…" + "write:admin:meta": "編輯實例的元資料" + "write:admin:user-note": "編輯審查ç†è¨˜" + "write:admin:roles": "編輯角色" + "read:admin:roles": "查看角色" + "write:admin:relays": "編輯ä¸ç¹¼å™¨" + "read:admin:relays": "查看ä¸ç¹¼å™¨" + "write:admin:invite-codes": "編輯邀請碼" + "read:admin:invite-codes": "查看邀請碼" + "write:admin:announcements": "編輯公告" + "read:admin:announcements": "查看公告" + "write:admin:avatar-decorations": "編輯é åƒè£é£¾" + "read:admin:avatar-decorations": "查看é åƒè£é£¾" + "write:admin:federation": "編輯站å°è¯é‚¦çš„相關資訊" + "write:admin:account": "編輯使用者帳戶" + "read:admin:account": "查看使用者的相關資訊" + "write:admin:emoji": "編輯表情符號" + "read:admin:emoji": "查看表情符號" + "write:admin:queue": "編輯工作佇列" + "read:admin:queue": "查看工作佇列的相關資訊" + "write:admin:promo": "編輯推廣貼文" + "write:admin:drive": "編輯使用者的雲端硬碟" + "read:admin:drive": "查看使用者雲端硬碟的相關資訊" + "read:admin:stream": "使用管ç†å“¡çš„ Websocket API" + "write:admin:ad": "編輯廣告" + "read:admin:ad": "查看廣告" + "write:invite-codes": "建立邀請碼" + "read:invite-codes": "å–得邀請碼" + "write:clip-favorite": "編輯摘錄的讚" + "read:clip-favorite": "查看摘錄的讚" + "read:federation": "查看站å°è¯é‚¦çš„相關資訊" + "write:report-abuse": "檢舉é•è¦è¡Œç‚º" _auth: shareAccessTitle: "應用程å¼çš„å˜å–權é™" shareAccess: "è¦æŽˆæ¬Šã€Œâ€œ{name}â€ã€å˜å–您的帳戶嗎?" @@ -1961,7 +2076,7 @@ _weekday: saturday: "週å…" _widgets: profile: "個人檔案" - instanceInfo: "實例資訊" + instanceInfo: "伺æœå™¨è³‡è¨Š" memo: "備忘錄" notifications: "通知" timeline: "時間軸" @@ -1975,7 +2090,7 @@ _widgets: digitalClock: "é›»å時é˜" unixClock: "UNIX 時間" federation: "è¯é‚¦å®‡å®™" - instanceCloud: "實例雲" + instanceCloud: "伺æœå™¨é›²" postForm: "發文視窗" slideshow: "幻燈片" button: "按鈕" @@ -2008,7 +2123,7 @@ _poll: deadlineTime: "å°æ™‚" duration: "時長" votesCount: "{n} 票" - totalVotes: "一共{n}票" + totalVotes: "åˆè¨ˆ {n} 票" vote: "投票" showResult: "顯示çµæžœ" voted: "已投票" @@ -2027,7 +2142,7 @@ _visibility: specified: "指定使用者" specifiedDescription: "僅發布至指定使用者" disableFederation: "åœç”¨è¯é‚¦" - disableFederationDescription: "ä¸è¦å‚³éžçµ¦å…¶ä»–實例" + disableFederationDescription: "ä¸ç™¼é€åˆ°å…¶ä»–伺æœå™¨" _postForm: replyPlaceholder: "回覆æ¤è²¼æ–‡..." quotePlaceholder: "引用æ¤è²¼æ–‡..." @@ -2056,6 +2171,7 @@ _profile: _exportOrImport: allNotes: "所有貼文" favoritedNotes: "「我的最愛ã€è²¼æ–‡" + clips: "摘錄" followingList: "追隨ä¸" muteList: "éœéŸ³" blockingList: "å°éŽ–" @@ -2184,6 +2300,7 @@ _notification: reactedBySomeUsers: "{n}人åšå‡ºäº†å應" renotedBySomeUsers: "{n}人åšäº†è½‰ç™¼" followedBySomeUsers: "被{n}人追隨了" + flushNotification: "é‡ç½®é€šçŸ¥æ·å²ç´€éŒ„" _types: all: "全部 " note: "使用者的最新貼文" @@ -2269,7 +2386,7 @@ _moderationLogTypes: updateCustomEmoji: "更新自訂表情符號" deleteCustomEmoji: "刪除自訂表情符號" updateServerSettings: "更新伺æœå™¨è¨å®š" - updateUserNote: "更新管ç†ç†è¨˜" + updateUserNote: "更新了使用者的管ç†ç†è¨˜" deleteDriveFile: "刪除檔案" deleteNote: "刪除貼文" createGlobalAnnouncement: "建立全網通知" @@ -2281,6 +2398,7 @@ _moderationLogTypes: resetPassword: "é‡è¨å¯†ç¢¼" suspendRemoteInstance: "å°éŽ–é 端伺æœå™¨" unsuspendRemoteInstance: "解除å°éŽ–é 端伺æœå™¨" + updateRemoteInstanceNote: "更新了é 端伺æœå™¨çš„管ç†ç†è¨˜" markSensitiveDriveFile: "標記為æ•æ„Ÿæª”案" unmarkSensitiveDriveFile: "撤銷標記為æ•æ„Ÿæª”案" resolveAbuseReport: "解決檢舉" @@ -2355,3 +2473,55 @@ _dataSaver: _code: title: "程å¼ç¢¼çªå‡ºé¡¯ç¤º" description: "如果使用了 MFM 的程å¼ç¢¼çªé¡¯æ¨™è¨˜ï¼Œå‰‡åœ¨é»žæ“Šä¹‹å‰ä¸æœƒè¼‰å…¥ã€‚程å¼ç¢¼çªé¡¯è¦æ±‚åŠ è¼‰æ¯ç¨®ç¨‹å¼èªžè¨€çš„çªé¡¯å®šç¾©æª”案,但由於這些檔案ä¸å†è‡ªå‹•è¼‰å…¥ï¼Œå› æ¤æœ‰æœ›æ¸›å°‘資料æµé‡ã€‚" +_hemisphere: + N: "北åŠçƒ" + S: "å—åŠçƒ" + caption: "在æŸäº›å®¢æˆ¶ç«¯çš„è¨å®šä¸ï¼Œç”¨æ–¼åˆ¤æ–·å£ç¯€ã€‚" +_reversi: + reversi: "黑白棋" + gameSettings: "å°å¼ˆè¨å®š" + chooseBoard: "é¸æ“‡æ£‹ç›¤" + blackOrWhite: "先手/後手" + blackIs: "{name} 為黑棋(先攻)" + rules: "è¦å‰‡" + thisGameIsStartedSoon: "å°å¼ˆå³å°‡é–‹å§‹" + waitingForOther: "ç‰å¾…å°æ‰‹æº–備就緒" + waitingForMe: "ç‰å¾…您準備就緒" + waitingBoth: "請準備" + ready: "準備就緒" + cancelReady: "é‡æ–°æº–å‚™" + opponentTurn: "å°æ‰‹çš„回åˆ" + myTurn: "您的回åˆ" + turnOf: "{name} 的回åˆ" + pastTurnOf: "{name} 的回åˆ" + surrender: "èªè¼¸" + surrendered: "å°æ‰‹èªè¼¸" + timeout: "時間到" + drawn: "平手" + won: "{name} ç²å‹" + black: "黑" + white: "白" + total: "åˆè¨ˆ" + turnCount: "{count} 回åˆ" + myGames: "我的å°å¼ˆ" + allGames: "所有å°å¼ˆ" + ended: "å·²çµæŸ" + playing: "æ£åœ¨å°å¼ˆ" + isLlotheo: "å較少的一方為å‹ï¼ˆé¡›å€’è¦å‰‡ï¼‰" + loopedMap: "循環棋盤" + canPutEverywhere: "隨æ„置放模å¼" + timeLimitForEachTurn: "æ¯å›žåˆçš„時間é™åˆ¶" + freeMatch: "自由å°æˆ°" + lookingForPlayer: "æ£åœ¨æœå°‹å°æ‰‹" + gameCanceled: "å°å¼ˆå·²è¢«å–消" + shareToTlTheGameWhenStart: "在éŠæˆ²é–‹å§‹æ™‚å°‡å°å¼ˆè³‡è¨Šç™¼å¸ƒåˆ°æ™‚間軸" + iStartedAGame: "å°å¼ˆé–‹å§‹äº†ï¼ #MisskeyReversi" + opponentHasSettingsChanged: "å°æ‰‹æ›´æ”¹äº†è¨å®š" + allowIrregularRules: "å…許異常è¦å‰‡ï¼ˆå®Œå…¨è‡ªç”±ï¼‰" + disallowIrregularRules: "ä¸å…許異常è¦å‰‡" + showBoardLabels: "在棋盤上顯示行ã€åˆ—號" + useAvatarAsStone: "用大é 貼當作棋å" +_offlineScreen: + title: "離線ï¼ç„¡æ³•é€£æŽ¥ä¼ºæœå™¨" + header: "無法連接伺æœå™¨" + diff --git a/package.json b/package.json index a7b105b3109ab0955a130846e07f4920d3584375..5e89cba218a9cc778f55af21b32db079a4699b2c 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,19 @@ { "name": "sharkey", - "version": "2023.12.1", + "version": "2024.3.1", "codename": "shonk", "repository": { "type": "git", - "url": "https://git.joinsharkey.org/Sharkey/Sharkey.git" + "url": "https://activitypub.software/TransFem-org/Sharkey.git" }, - "packageManager": "pnpm@8.12.1", + "packageManager": "pnpm@8.15.4", "workspaces": [ "packages/frontend", "packages/backend", - "packages/sw" + "packages/sw", + "packages/misskey-js", + "packages/misskey-reversi", + "packages/misskey-bubble-game" ], "private": true, "scripts": { @@ -18,7 +21,7 @@ "build-assets": "node ./scripts/build-assets.mjs", "build": "pnpm build-pre && pnpm -r build && pnpm build-assets", "build-storybook": "pnpm --filter frontend build-storybook", - "build-misskey-js-with-types": "pnpm --filter backend build && pnpm --filter backend generate-api-json && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api", + "build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api", "start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js", "start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js", "init": "pnpm migrate", @@ -45,20 +48,23 @@ "lodash": "4.17.21" }, "dependencies": { + "cssnano": "6.0.5", "execa": "8.0.1", - "cssnano": "6.0.2", + "fast-glob": "3.3.2", + "ignore-walk": "6.0.4", "js-yaml": "4.1.0", - "postcss": "8.4.32", - "terser": "5.26.0", + "postcss": "8.4.35", + "tar": "6.2.0", + "terser": "5.28.1", "typescript": "5.3.3" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "6.14.0", - "@typescript-eslint/parser": "6.14.0", + "@typescript-eslint/eslint-plugin": "7.1.0", + "@typescript-eslint/parser": "7.1.0", "cross-env": "7.0.3", - "cypress": "13.6.1", - "eslint": "8.56.0", - "start-server-and-test": "2.0.3", - "ncp": "2.0.0" + "cypress": "13.6.6", + "eslint": "8.57.0", + "ncp": "2.0.0", + "start-server-and-test": "2.0.3" } } diff --git a/packages/backend/check_connect.js b/packages/backend/check_connect.js index ea988a7f6985ba2230355f6d2a7961851b7200e3..d88e649c099de26d79febff0930ed1fa6554d06f 100644 --- a/packages/backend/check_connect.js +++ b/packages/backend/check_connect.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/generate_api_json.js b/packages/backend/generate_api_json.js index 5819c60a5ff49da9a4d25eb7f84209b089e6f493..4079b3bb0aac13c155ec87975614eed483854437 100644 --- a/packages/backend/generate_api_json.js +++ b/packages/backend/generate_api_json.js @@ -3,6 +3,6 @@ import { genOpenapiSpec } from './built/server/api/openapi/gen-spec.js' import { writeFileSync } from "node:fs"; const config = loadConfig(); -const spec = genOpenapiSpec(config); +const spec = genOpenapiSpec(config, true); writeFileSync('./built/api.json', JSON.stringify(spec), 'utf-8'); \ No newline at end of file diff --git a/packages/backend/jest.config.cjs b/packages/backend/jest.config.cjs index 97d777c86287093f8b1f7ccaa73be9dbbd473701..5a4aa4e15aa43b76cf101836b1dc49deffdf0163 100644 --- a/packages/backend/jest.config.cjs +++ b/packages/backend/jest.config.cjs @@ -160,7 +160,6 @@ module.exports = { testMatch: [ "<rootDir>/test/unit/**/*.ts", "<rootDir>/src/**/*.test.ts", - "<rootDir>/test/e2e/**/*.ts", ], // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped diff --git a/packages/backend/jest.config.e2e.cjs b/packages/backend/jest.config.e2e.cjs new file mode 100644 index 0000000000000000000000000000000000000000..4502da47df68adbedddfb19fdd336f33b6086a80 --- /dev/null +++ b/packages/backend/jest.config.e2e.cjs @@ -0,0 +1,15 @@ +/* +* For a detailed explanation regarding each configuration property and type check, visit: +* https://jestjs.io/docs/en/configuration.html +*/ + +const base = require('./jest.config.cjs') + +module.exports = { + ...base, + globalSetup: "<rootDir>/built-test/entry.js", + setupFilesAfterEnv: ["<rootDir>/test/jest.setup.ts"], + testMatch: [ + "<rootDir>/test/e2e/**/*.ts", + ], +}; diff --git a/packages/backend/jest.config.unit.cjs b/packages/backend/jest.config.unit.cjs new file mode 100644 index 0000000000000000000000000000000000000000..aa5992936b423e919ff6723dd689db0298ed3b46 --- /dev/null +++ b/packages/backend/jest.config.unit.cjs @@ -0,0 +1,14 @@ +/* +* For a detailed explanation regarding each configuration property and type check, visit: +* https://jestjs.io/docs/en/configuration.html +*/ + +const base = require('./jest.config.cjs') + +module.exports = { + ...base, + testMatch: [ + "<rootDir>/test/unit/**/*.ts", + "<rootDir>/src/**/*.test.ts", + ], +}; diff --git a/packages/backend/migration/1000000000000-Init.js b/packages/backend/migration/1000000000000-Init.js index 6f04b52ae1ec84fc3e6fc4f904c1fe892417ec5c..c06885fd4028a4632898cb5b56dc22ce8b6986e4 100644 --- a/packages/backend/migration/1000000000000-Init.js +++ b/packages/backend/migration/1000000000000-Init.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1556348509290-Pages.js b/packages/backend/migration/1556348509290-Pages.js index 05d801227b235283c2a228ec9525ac1893c1d154..c7542e808c468608e60f2e60df8efa8f23255943 100644 --- a/packages/backend/migration/1556348509290-Pages.js +++ b/packages/backend/migration/1556348509290-Pages.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1556746559567-UserProfile.js b/packages/backend/migration/1556746559567-UserProfile.js index 7cc1ba00837eb04e6e7ad26bae6300fdbccf4a01..13ff6ce6bf9f8431859aa32b1bcad756731690a6 100644 --- a/packages/backend/migration/1556746559567-UserProfile.js +++ b/packages/backend/migration/1556746559567-UserProfile.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1557476068003-PinnedUsers.js b/packages/backend/migration/1557476068003-PinnedUsers.js index 12f0b8fc6aa1775e99a19e9dce31c9a0f9a236c9..f2f1deae2f59b310062aad3723513cbdb5cef342 100644 --- a/packages/backend/migration/1557476068003-PinnedUsers.js +++ b/packages/backend/migration/1557476068003-PinnedUsers.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1557761316509-AddSomeUrls.js b/packages/backend/migration/1557761316509-AddSomeUrls.js index b83ce2ed5edcf0336b65e2e4fd17e730cad43ea6..c73f30c49fcb49e622dbc1d64b6fac0ed599d790 100644 --- a/packages/backend/migration/1557761316509-AddSomeUrls.js +++ b/packages/backend/migration/1557761316509-AddSomeUrls.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1557932705754-ObjectStorageSetting.js b/packages/backend/migration/1557932705754-ObjectStorageSetting.js index 736dcafaac3f3392cf45fa4848de7b38ada7ad22..0e1ef321ab59b5ce4ec1257fd52287980d4a36a3 100644 --- a/packages/backend/migration/1557932705754-ObjectStorageSetting.js +++ b/packages/backend/migration/1557932705754-ObjectStorageSetting.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1558072954435-PageLike.js b/packages/backend/migration/1558072954435-PageLike.js index d9502a6e0389e2450e806bb40685ec4bc3907ba6..a08f68a0e6913b3fef930336c7330fc6fbea3a59 100644 --- a/packages/backend/migration/1558072954435-PageLike.js +++ b/packages/backend/migration/1558072954435-PageLike.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1558103093633-UserGroup.js b/packages/backend/migration/1558103093633-UserGroup.js index b3cc6eb949d6aece5b535964e257eeb165ebbfaf..f762dc23715e0ee3d6afbddafe9e25a2dacfa615 100644 --- a/packages/backend/migration/1558103093633-UserGroup.js +++ b/packages/backend/migration/1558103093633-UserGroup.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1558257926829-UserGroupInvite.js b/packages/backend/migration/1558257926829-UserGroupInvite.js index a87173cdfeaaca29f82e3779fe48adf31e2f6b1a..853b52d17d65c3afc334410a487cc3dfe2e11818 100644 --- a/packages/backend/migration/1558257926829-UserGroupInvite.js +++ b/packages/backend/migration/1558257926829-UserGroupInvite.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1558266512381-UserListJoining.js b/packages/backend/migration/1558266512381-UserListJoining.js index bc94b7f4257ed95ff4a87e276955f77287de56eb..e161d52f12b8e9b1b3928610630ea92634384c45 100644 --- a/packages/backend/migration/1558266512381-UserListJoining.js +++ b/packages/backend/migration/1558266512381-UserListJoining.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1561706992953-webauthn.js b/packages/backend/migration/1561706992953-webauthn.js index fa9b1188cab589f7f3f332bfa619b47aac762b1b..4c81035ff15724ac883c7f94e6f3720edca9a608 100644 --- a/packages/backend/migration/1561706992953-webauthn.js +++ b/packages/backend/migration/1561706992953-webauthn.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1561873850023-ChartIndexes.js b/packages/backend/migration/1561873850023-ChartIndexes.js index c7e93ba7b72ceb48c614340aafb5a6674cb058d9..3f190ce143a378092c286ad1e118cd122988741e 100644 --- a/packages/backend/migration/1561873850023-ChartIndexes.js +++ b/packages/backend/migration/1561873850023-ChartIndexes.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1562422242907-PasswordLessLogin.js b/packages/backend/migration/1562422242907-PasswordLessLogin.js index 3df3a6f5f5a1fc83b6447567d2c5da26d045f377..4c0fbbbc9fca4ccfa54265a93a0aa934f99af4ba 100644 --- a/packages/backend/migration/1562422242907-PasswordLessLogin.js +++ b/packages/backend/migration/1562422242907-PasswordLessLogin.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1562444565093-PinnedPage.js b/packages/backend/migration/1562444565093-PinnedPage.js index 329d49bbed6a11b6681a5c1dfb716e2ebea1f051..89639399f0671cde6cc5790d8b6c9b843a66c36d 100644 --- a/packages/backend/migration/1562444565093-PinnedPage.js +++ b/packages/backend/migration/1562444565093-PinnedPage.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1562448332510-PageTitleHideOption.js b/packages/backend/migration/1562448332510-PageTitleHideOption.js index e41db08090dd7779ad90113c0555fc5eb2f43380..70d54aa7775a917ef9ea660b27bec47c7728d599 100644 --- a/packages/backend/migration/1562448332510-PageTitleHideOption.js +++ b/packages/backend/migration/1562448332510-PageTitleHideOption.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1562869971568-ModerationLog.js b/packages/backend/migration/1562869971568-ModerationLog.js index 2eb3015d5c771786c7f00cac0e9ede939020ca31..3dd9b22edf1fdc58df0ca16a24efcc1b9ea79914 100644 --- a/packages/backend/migration/1562869971568-ModerationLog.js +++ b/packages/backend/migration/1562869971568-ModerationLog.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1563757595828-UsedUsername.js b/packages/backend/migration/1563757595828-UsedUsername.js index 91d9d36b9dad67386a29a65026a320b068f992cc..258e5abab234bca5cc9b8efdd6f58302945b753f 100644 --- a/packages/backend/migration/1563757595828-UsedUsername.js +++ b/packages/backend/migration/1563757595828-UsedUsername.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1565634203341-room.js b/packages/backend/migration/1565634203341-room.js index c2e5fca863d2e1446fd59bdd8db16bde6b4a50d5..04c9749c1b059ce7ae3464a3868b85ab8d6de64b 100644 --- a/packages/backend/migration/1565634203341-room.js +++ b/packages/backend/migration/1565634203341-room.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1571220798684-CustomEmojiCategory.js b/packages/backend/migration/1571220798684-CustomEmojiCategory.js index f211af67bedf5ec48714f18ed8b312262a812c71..1fc78a65ff405cdc0e7205a3df62c095f91e6508 100644 --- a/packages/backend/migration/1571220798684-CustomEmojiCategory.js +++ b/packages/backend/migration/1571220798684-CustomEmojiCategory.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1572760203493-nodeinfo.js b/packages/backend/migration/1572760203493-nodeinfo.js index c281b0b2db5adc26282a2d8c888e288d9b6f2faa..ea7a67bc3e56d1ee4a54140174860f0e90632e02 100644 --- a/packages/backend/migration/1572760203493-nodeinfo.js +++ b/packages/backend/migration/1572760203493-nodeinfo.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1576269851876-TalkFederationId.js b/packages/backend/migration/1576269851876-TalkFederationId.js index 045f9ddb042acc0ea4841efc1d352c60e7f549b8..c49c716e7a0d66d3d4c1c6d077d40aec5e8583c8 100644 --- a/packages/backend/migration/1576269851876-TalkFederationId.js +++ b/packages/backend/migration/1576269851876-TalkFederationId.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1576869585998-ProxyRemoteFiles.js b/packages/backend/migration/1576869585998-ProxyRemoteFiles.js index 0dde1ae70cfc4d7f34fbbeb8dbe110655205464a..192dbe3485bd0d7243785831588631aecc8d6d9c 100644 --- a/packages/backend/migration/1576869585998-ProxyRemoteFiles.js +++ b/packages/backend/migration/1576869585998-ProxyRemoteFiles.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1579267006611-v12.js b/packages/backend/migration/1579267006611-v12.js index 86f9da7e7ade1d1efa1bb9051465f1db9f1de72e..9267be5630bdbf6d8cda3a95edee1604f0985cdc 100644 --- a/packages/backend/migration/1579267006611-v12.js +++ b/packages/backend/migration/1579267006611-v12.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1579270193251-v12-2.js b/packages/backend/migration/1579270193251-v12-2.js index 2593aca57399c7bb59d312d41efdc04a82872cf1..e2ca9709ea60b29dc7e77260214270bbde11f8fb 100644 --- a/packages/backend/migration/1579270193251-v12-2.js +++ b/packages/backend/migration/1579270193251-v12-2.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1579282808087-v12-3.js b/packages/backend/migration/1579282808087-v12-3.js index a816b2e82ea3b8972241fa32d5a0d64966f05175..4098f041c8040af628c784a4bbf962ce447c86b5 100644 --- a/packages/backend/migration/1579282808087-v12-3.js +++ b/packages/backend/migration/1579282808087-v12-3.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1579544426412-v12-4.js b/packages/backend/migration/1579544426412-v12-4.js index 600dc270a59205c8597d7cb04e340bbe2a8198d0..1153993f35540e9e59fd76faf65992eb2788fc53 100644 --- a/packages/backend/migration/1579544426412-v12-4.js +++ b/packages/backend/migration/1579544426412-v12-4.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1579977526288-v12-5.js b/packages/backend/migration/1579977526288-v12-5.js index 73f3343347f3f56b798ddd7df2988c81366cd869..d9e1b48bb2e481bd32f2afd45f13fb162576f2c0 100644 --- a/packages/backend/migration/1579977526288-v12-5.js +++ b/packages/backend/migration/1579977526288-v12-5.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1579993013959-v12-6.js b/packages/backend/migration/1579993013959-v12-6.js index 5009e0aa88cdf13da7b9434870161235a8ad68fa..9c249422a23d64801eab68dfce0daeb842f142ba 100644 --- a/packages/backend/migration/1579993013959-v12-6.js +++ b/packages/backend/migration/1579993013959-v12-6.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1580069531114-v12-7.js b/packages/backend/migration/1580069531114-v12-7.js index ff943ffa6b705bdf7f854cd1d47e2dbd90946292..ceee6b2031b8fbc9887a6e401b52ee6c75a5f81e 100644 --- a/packages/backend/migration/1580069531114-v12-7.js +++ b/packages/backend/migration/1580069531114-v12-7.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1580148575182-v12-8.js b/packages/backend/migration/1580148575182-v12-8.js index 20b77b391f684d52ee07594d09719ef763a1a9f5..6841dcc38f8ebce88a631602f904f5c5a1278465 100644 --- a/packages/backend/migration/1580148575182-v12-8.js +++ b/packages/backend/migration/1580148575182-v12-8.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1580154400017-v12-9.js b/packages/backend/migration/1580154400017-v12-9.js index f78dc47456e8d5403b308a47b4d3c6491e9b8b25..c01d8089d029f1b0e538af2c1f4077de28210cff 100644 --- a/packages/backend/migration/1580154400017-v12-9.js +++ b/packages/backend/migration/1580154400017-v12-9.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1580276619901-v12-10.js b/packages/backend/migration/1580276619901-v12-10.js index 09fa27ae83f870286882b6ec4a11494f0a40ebec..be6e467fab5fefa40c76c93a3ac8ccc1711772b0 100644 --- a/packages/backend/migration/1580276619901-v12-10.js +++ b/packages/backend/migration/1580276619901-v12-10.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1580331224276-v12-11.js b/packages/backend/migration/1580331224276-v12-11.js index f118c34937b1c97a37150cb83f1d9e48639c3837..af817a8c8a2c9b5f0107c54908e9dbe598dbb93d 100644 --- a/packages/backend/migration/1580331224276-v12-11.js +++ b/packages/backend/migration/1580331224276-v12-11.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1580508795118-v12-12.js b/packages/backend/migration/1580508795118-v12-12.js index 4fba933a0847b4602256f425ab91f593ee87acb2..4bd855f7ab2f54b20598dfc620c1b1d1ba9b278b 100644 --- a/packages/backend/migration/1580508795118-v12-12.js +++ b/packages/backend/migration/1580508795118-v12-12.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1580543501339-v12-13.js b/packages/backend/migration/1580543501339-v12-13.js index 9344516309f9cab7a9bbb339c4458f58a994bbdf..be76c02163a6288d5d8f2fef93bf53ffba7b1531 100644 --- a/packages/backend/migration/1580543501339-v12-13.js +++ b/packages/backend/migration/1580543501339-v12-13.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1580864313253-v12-14.js b/packages/backend/migration/1580864313253-v12-14.js index 5034492a70af8a8a847fdb5fb39e2c9e2c9f87a7..f8891a2b66209f13ba9c3ebfa4b4254dcf4a96c4 100644 --- a/packages/backend/migration/1580864313253-v12-14.js +++ b/packages/backend/migration/1580864313253-v12-14.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1581526429287-user-group-invitation.js b/packages/backend/migration/1581526429287-user-group-invitation.js index fc8181380744805e3304006636e37449987b0548..51703e2ba16d3d4ca91d95416d333561dc298b57 100644 --- a/packages/backend/migration/1581526429287-user-group-invitation.js +++ b/packages/backend/migration/1581526429287-user-group-invitation.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1581695816408-user-group-antenna.js b/packages/backend/migration/1581695816408-user-group-antenna.js index 8a212c092a0dd8af2d3577694b43e45c41930985..e6791ba1a4f3131b967899b0c37c837cb1963a67 100644 --- a/packages/backend/migration/1581695816408-user-group-antenna.js +++ b/packages/backend/migration/1581695816408-user-group-antenna.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1581708415836-drive-user-folder-id-index.js b/packages/backend/migration/1581708415836-drive-user-folder-id-index.js index 6594078db8cf85a90b80cf507b1af4ac84e4edc3..28ce4cc142962801d4226eb4b63c5fb757b7d92b 100644 --- a/packages/backend/migration/1581708415836-drive-user-folder-id-index.js +++ b/packages/backend/migration/1581708415836-drive-user-folder-id-index.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1581979837262-promo.js b/packages/backend/migration/1581979837262-promo.js index 585564a4000188e6f09e239349c166ce04cf454a..707c85fcb34d2623c3a51aa37fd2103bd8313b48 100644 --- a/packages/backend/migration/1581979837262-promo.js +++ b/packages/backend/migration/1581979837262-promo.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1582019042083-featured-injecttion.js b/packages/backend/migration/1582019042083-featured-injecttion.js index d270006277a9a76c4c4295d06003f8ac9429022f..f308f0a45412ee9c1e26e29e316dca61da993625 100644 --- a/packages/backend/migration/1582019042083-featured-injecttion.js +++ b/packages/backend/migration/1582019042083-featured-injecttion.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1582210532752-antenna-exclude.js b/packages/backend/migration/1582210532752-antenna-exclude.js index 12eee2364ccd3ac92198293a610426f05eae6421..9b87e3ff395086db34ad20479cb9d7d0be3fd9cb 100644 --- a/packages/backend/migration/1582210532752-antenna-exclude.js +++ b/packages/backend/migration/1582210532752-antenna-exclude.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1582875306439-note-reaction-length.js b/packages/backend/migration/1582875306439-note-reaction-length.js index a4413c95338442d5efdee4822726dd8ddb7f438f..e801d1ac446c94095434e9af0f7ce314fa3b1bb2 100644 --- a/packages/backend/migration/1582875306439-note-reaction-length.js +++ b/packages/backend/migration/1582875306439-note-reaction-length.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1585361548360-miauth.js b/packages/backend/migration/1585361548360-miauth.js index d073fa3d266bcd4deb03940c3569410d5f9949ec..d5932c6083d388dc9a4c370e805544a7cbc06abe 100644 --- a/packages/backend/migration/1585361548360-miauth.js +++ b/packages/backend/migration/1585361548360-miauth.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1585385921215-custom-notification.js b/packages/backend/migration/1585385921215-custom-notification.js index a3336e0eca344cc77f1985e36e43a3588025e959..35303b99e93b49d54854062b3e2b268b0e659faf 100644 --- a/packages/backend/migration/1585385921215-custom-notification.js +++ b/packages/backend/migration/1585385921215-custom-notification.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1585772678853-ap-url.js b/packages/backend/migration/1585772678853-ap-url.js index f67f5a4542a95f11f275929bd7aa24cef2b45200..f978fc80b4e9239969ece0a80daa05bbd5387deb 100644 --- a/packages/backend/migration/1585772678853-ap-url.js +++ b/packages/backend/migration/1585772678853-ap-url.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1586624197029-AddObjectStorageUseProxy.js b/packages/backend/migration/1586624197029-AddObjectStorageUseProxy.js index 16f7599b802e7e729a481491b7d3903f493615ff..fde8629bbac933df626b067b3b2a4dcb1f03f2c2 100644 --- a/packages/backend/migration/1586624197029-AddObjectStorageUseProxy.js +++ b/packages/backend/migration/1586624197029-AddObjectStorageUseProxy.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1586641139527-remote-reaction.js b/packages/backend/migration/1586641139527-remote-reaction.js index 666bb42ca63d02601e1de4163dc4876190cc204b..3e907af5f1a45801f2bdfc3564df8f3ec7554a36 100644 --- a/packages/backend/migration/1586641139527-remote-reaction.js +++ b/packages/backend/migration/1586641139527-remote-reaction.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1586708940386-pageAiScript.js b/packages/backend/migration/1586708940386-pageAiScript.js index 3d0d0ab915bad404705d57500d9aceb75a0b14ff..ce5007cea169d7b3ae51f6a60a5c24387d60e98a 100644 --- a/packages/backend/migration/1586708940386-pageAiScript.js +++ b/packages/backend/migration/1586708940386-pageAiScript.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1588044505511-hCaptcha.js b/packages/backend/migration/1588044505511-hCaptcha.js index 22cc6672c5175b7b4dd2cdf24f7d43a0bb38d306..aeacb653b3bbc9f8515fc71ea382729a21a108f5 100644 --- a/packages/backend/migration/1588044505511-hCaptcha.js +++ b/packages/backend/migration/1588044505511-hCaptcha.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1589023282116-pubRelay.js b/packages/backend/migration/1589023282116-pubRelay.js index ed010699e17e418f64a4831b2bba022c8706285c..8739adb7339043960f8d97c81daa22fe0922c5e3 100644 --- a/packages/backend/migration/1589023282116-pubRelay.js +++ b/packages/backend/migration/1589023282116-pubRelay.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1595075960584-blurhash.js b/packages/backend/migration/1595075960584-blurhash.js index 967676531ff21d5aa99411d55c6a9f5c4a5df502..9752625cd298d2dfd8abe40b0348ee134e78ece6 100644 --- a/packages/backend/migration/1595075960584-blurhash.js +++ b/packages/backend/migration/1595075960584-blurhash.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1595077605646-blurhash-for-avatar-banner.js b/packages/backend/migration/1595077605646-blurhash-for-avatar-banner.js index 7df079ac05a35e8dee42bc2fd569b4353c423c54..fdff8c633a89cc547989ee5abc256de048f1b6fb 100644 --- a/packages/backend/migration/1595077605646-blurhash-for-avatar-banner.js +++ b/packages/backend/migration/1595077605646-blurhash-for-avatar-banner.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1595676934834-instance-icon-url.js b/packages/backend/migration/1595676934834-instance-icon-url.js index 6bccff082bea5c9613f41326abf80ed562320926..5f834064c4464dcaa9fa3a8aeef303124a9ab594 100644 --- a/packages/backend/migration/1595676934834-instance-icon-url.js +++ b/packages/backend/migration/1595676934834-instance-icon-url.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1595771249699-word-mute.js b/packages/backend/migration/1595771249699-word-mute.js index cfd0a5ccc17009d57d8b89ba39e53233bcdf2c66..f4fa1227e309f6199b6e89ab21b6fcc1b3605b30 100644 --- a/packages/backend/migration/1595771249699-word-mute.js +++ b/packages/backend/migration/1595771249699-word-mute.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1595782306083-word-mute2.js b/packages/backend/migration/1595782306083-word-mute2.js index 64acf2b7218cec119f0f9a5675d802aeda7ffc4d..3c2062ec078fe36f612af303a405bcd9b706dd16 100644 --- a/packages/backend/migration/1595782306083-word-mute2.js +++ b/packages/backend/migration/1595782306083-word-mute2.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1596548170836-channel.js b/packages/backend/migration/1596548170836-channel.js index a26991d4d85fa4f41e96498b99078b1b15f470e3..ee6753a4760c8e77e31f4415b4c00319ffe548ff 100644 --- a/packages/backend/migration/1596548170836-channel.js +++ b/packages/backend/migration/1596548170836-channel.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1596786425167-channel2.js b/packages/backend/migration/1596786425167-channel2.js index 4e87b11bb53509b0b892f77f158eee4651041475..9e6ead4378fb884937a2aaa11bd6bfdd2048c0e2 100644 --- a/packages/backend/migration/1596786425167-channel2.js +++ b/packages/backend/migration/1596786425167-channel2.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1597230137744-objectStorageSetPublicRead.js b/packages/backend/migration/1597230137744-objectStorageSetPublicRead.js index 93e6f186d586da75ef4a920d733889ff83c61a20..bc32d4a052925a625a9522b1cdb9fa6ebee672e1 100644 --- a/packages/backend/migration/1597230137744-objectStorageSetPublicRead.js +++ b/packages/backend/migration/1597230137744-objectStorageSetPublicRead.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1597236229720-IncludingNotificationTypes.js b/packages/backend/migration/1597236229720-IncludingNotificationTypes.js index bda702d99934b7976b20143d8001595a6fd024a2..99686bd70e3f20bb19e1c1c2e4ca152c4585d7bf 100644 --- a/packages/backend/migration/1597236229720-IncludingNotificationTypes.js +++ b/packages/backend/migration/1597236229720-IncludingNotificationTypes.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1597385880794-add-sensitive-index.js b/packages/backend/migration/1597385880794-add-sensitive-index.js index ffb94895d7e720e4e652dfedd3f6970479375bf9..a67810880b2a6da2a0adc3bf23ada95f469dabcf 100644 --- a/packages/backend/migration/1597385880794-add-sensitive-index.js +++ b/packages/backend/migration/1597385880794-add-sensitive-index.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1597459042300-channel-unread.js b/packages/backend/migration/1597459042300-channel-unread.js index 5b94d8296a4a6adc12a25acec3aed61350f67c7b..ced9b5265a9e0dd264ada09253dee100ab35a007 100644 --- a/packages/backend/migration/1597459042300-channel-unread.js +++ b/packages/backend/migration/1597459042300-channel-unread.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1597893996136-ChannelNoteIdDescIndex.js b/packages/backend/migration/1597893996136-ChannelNoteIdDescIndex.js index 543e511404a420e8daa9aa426cb1ebb895661896..ca4eba385ebdfd8717cee207abe93bf859bad17d 100644 --- a/packages/backend/migration/1597893996136-ChannelNoteIdDescIndex.js +++ b/packages/backend/migration/1597893996136-ChannelNoteIdDescIndex.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1600353287890-mutingNotificationTypes.js b/packages/backend/migration/1600353287890-mutingNotificationTypes.js index 4e0b8ad6ebb2851b1b437b7bc36cac7639a48112..0996aa21f6b2f9ee12c21fd0ef8e82128a9c4d23 100644 --- a/packages/backend/migration/1600353287890-mutingNotificationTypes.js +++ b/packages/backend/migration/1600353287890-mutingNotificationTypes.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1603094348345-refine-abuse-user-report.js b/packages/backend/migration/1603094348345-refine-abuse-user-report.js index 4e052e07c28bfa2f043d1dab590e4adfb03ab826..354915b1652b128296435832de18a1731abf8ab6 100644 --- a/packages/backend/migration/1603094348345-refine-abuse-user-report.js +++ b/packages/backend/migration/1603094348345-refine-abuse-user-report.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1603095701770-refine-abuse-user-report2.js b/packages/backend/migration/1603095701770-refine-abuse-user-report2.js index 2eb205c6e066c8a32152c9f28470b7dd12ddfc52..75dd3513b5b2076e7d02ac1392337da26e8feb3d 100644 --- a/packages/backend/migration/1603095701770-refine-abuse-user-report2.js +++ b/packages/backend/migration/1603095701770-refine-abuse-user-report2.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1603776877564-instance-theme-color.js b/packages/backend/migration/1603776877564-instance-theme-color.js index 5f83bc14e6e24647185f56de3313e69b8df2d7da..c8ab89ab567e536071799ee5060c49fbf30cab16 100644 --- a/packages/backend/migration/1603776877564-instance-theme-color.js +++ b/packages/backend/migration/1603776877564-instance-theme-color.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1603781553011-instance-favicon.js b/packages/backend/migration/1603781553011-instance-favicon.js index 758b86408f64adb1bd44f28e849e946de8fb75a9..7d793d4f1f106f733d0de237e81c331f7803fffc 100644 --- a/packages/backend/migration/1603781553011-instance-favicon.js +++ b/packages/backend/migration/1603781553011-instance-favicon.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1604821689616-delete-auto-watch.js b/packages/backend/migration/1604821689616-delete-auto-watch.js index 917ef5b10c71fa8d7cc40f18125c3155a272c540..816087703812221144a901314969254f1ce2fe8e 100644 --- a/packages/backend/migration/1604821689616-delete-auto-watch.js +++ b/packages/backend/migration/1604821689616-delete-auto-watch.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1605408848373-clip-description.js b/packages/backend/migration/1605408848373-clip-description.js index fedc603b3c58e678657c87fb29271d90812a5d03..77a218791ccd582f5e4ed3126da7b65bf1d6efd7 100644 --- a/packages/backend/migration/1605408848373-clip-description.js +++ b/packages/backend/migration/1605408848373-clip-description.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1605408971051-comments.js b/packages/backend/migration/1605408971051-comments.js index 8ab16859d216ddf47b9f2641ef5f2126435fd277..494bfb79500966def59ae2e4802856bfc2e3adf5 100644 --- a/packages/backend/migration/1605408971051-comments.js +++ b/packages/backend/migration/1605408971051-comments.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1605585339718-instance-pinned-pages.js b/packages/backend/migration/1605585339718-instance-pinned-pages.js index 767139c9e558e9a393b14e818a378345c8db916d..8f4c80643941ede5b103ad9cf1233a6e54ad3bb8 100644 --- a/packages/backend/migration/1605585339718-instance-pinned-pages.js +++ b/packages/backend/migration/1605585339718-instance-pinned-pages.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1605965516823-instance-images.js b/packages/backend/migration/1605965516823-instance-images.js index 848b53f1ba3c2c493527cbe8d4573ad80141e63f..9cc2eb40328b006058087a0d5f28a49b5953bbe9 100644 --- a/packages/backend/migration/1605965516823-instance-images.js +++ b/packages/backend/migration/1605965516823-instance-images.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1606191203881-no-crawle.js b/packages/backend/migration/1606191203881-no-crawle.js index 5c878f5a24ee8ea596291c23e72067c7a241deef..af04566eaa520a8478c3104784e226bea2910c16 100644 --- a/packages/backend/migration/1606191203881-no-crawle.js +++ b/packages/backend/migration/1606191203881-no-crawle.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1607151207216-instance-pinned-clip.js b/packages/backend/migration/1607151207216-instance-pinned-clip.js index 67db39fede24cd307cbe195b3cf073ffc6fae34b..f85c3d42d71f60422da4df51ad7c90d34bc0f0b8 100644 --- a/packages/backend/migration/1607151207216-instance-pinned-clip.js +++ b/packages/backend/migration/1607151207216-instance-pinned-clip.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1607353487793-isExplorable.js b/packages/backend/migration/1607353487793-isExplorable.js index 95ee07e9170e295f720431e5282a1cba322fce09..e07fe6c30611453dc3438a9c324e7a123c006733 100644 --- a/packages/backend/migration/1607353487793-isExplorable.js +++ b/packages/backend/migration/1607353487793-isExplorable.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1610277136869-registry.js b/packages/backend/migration/1610277136869-registry.js index c5fe2c5a621f0ac7fe5d20abd882d6dbbfba6aa3..1a10f23590fe4d5e7a0050ebef87f9126ee73682 100644 --- a/packages/backend/migration/1610277136869-registry.js +++ b/packages/backend/migration/1610277136869-registry.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1610277585759-registry2.js b/packages/backend/migration/1610277585759-registry2.js index f734a235b00c71de51d6374661ee82b73433005c..46e56279f47f4f62d081f05a6cf944ef6409cca4 100644 --- a/packages/backend/migration/1610277585759-registry2.js +++ b/packages/backend/migration/1610277585759-registry2.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1610283021566-registry3.js b/packages/backend/migration/1610283021566-registry3.js index c94546c7329ce4300c1e4ab9cb49200f80054978..402040f38b503a83ef007f58a0ab8138fbc4fab1 100644 --- a/packages/backend/migration/1610283021566-registry3.js +++ b/packages/backend/migration/1610283021566-registry3.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1611354329133-followersUri.js b/packages/backend/migration/1611354329133-followersUri.js index 7e5f8c3093c3cc043df6477ba23efa47b1e9dbe9..15abb2a9d1a7c0636ad98f9691899a17b2968190 100644 --- a/packages/backend/migration/1611354329133-followersUri.js +++ b/packages/backend/migration/1611354329133-followersUri.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1611397665007-gallery.js b/packages/backend/migration/1611397665007-gallery.js index cd5c39cc10650d465b4c7ceb578c827afe89d28d..cbd2b62c566c83d8d9f2a4b4ebe40c2c27b7d731 100644 --- a/packages/backend/migration/1611397665007-gallery.js +++ b/packages/backend/migration/1611397665007-gallery.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1611547387175-objectStorageS3ForcePathStyle.js b/packages/backend/migration/1611547387175-objectStorageS3ForcePathStyle.js index c0b1da1e531b26081740b813d2049280676dcc77..c5440b7a481ac9ede0f32d16397924a0a7ba931c 100644 --- a/packages/backend/migration/1611547387175-objectStorageS3ForcePathStyle.js +++ b/packages/backend/migration/1611547387175-objectStorageS3ForcePathStyle.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1612619156584-announcement-email.js b/packages/backend/migration/1612619156584-announcement-email.js index f8277725f796e28487fef4f5cd5fdd17a2557008..ddacab322b679273c4428c20e09a8cf432b48943 100644 --- a/packages/backend/migration/1612619156584-announcement-email.js +++ b/packages/backend/migration/1612619156584-announcement-email.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1613155914446-emailNotificationTypes.js b/packages/backend/migration/1613155914446-emailNotificationTypes.js index 3afe491e48ccda6d0006cf77c615938c342b9e25..d34ba7e826619cdecb9cad1f811405293cfc46f8 100644 --- a/packages/backend/migration/1613155914446-emailNotificationTypes.js +++ b/packages/backend/migration/1613155914446-emailNotificationTypes.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1613181457597-user-lang.js b/packages/backend/migration/1613181457597-user-lang.js index 33e363477f139b1d3f3215e506f8f946100c2261..6ef5245953946ac98720b78fdca62f9c35c057c2 100644 --- a/packages/backend/migration/1613181457597-user-lang.js +++ b/packages/backend/migration/1613181457597-user-lang.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1613503367223-use-bigint-for-driveUsage.js b/packages/backend/migration/1613503367223-use-bigint-for-driveUsage.js index 9c75c0ae544e3251024c1719f56efffdcef323bb..8529ea32477f1768f72f510103a507d17677f1ef 100644 --- a/packages/backend/migration/1613503367223-use-bigint-for-driveUsage.js +++ b/packages/backend/migration/1613503367223-use-bigint-for-driveUsage.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1615965918224-chart-v2.js b/packages/backend/migration/1615965918224-chart-v2.js index 2c0cacd1d92c0045ef5df8670f1e3d57321fecbd..deecde7227374c777a50b3b4ffa4d2406feb0d17 100644 --- a/packages/backend/migration/1615965918224-chart-v2.js +++ b/packages/backend/migration/1615965918224-chart-v2.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1615966519402-chart-v2-2.js b/packages/backend/migration/1615966519402-chart-v2-2.js index 8d6ebf6a81c39c70201154a5c9f072b6a8a0d725..7842a271081e5ecdbeb4e98240ec3cb10417bc24 100644 --- a/packages/backend/migration/1615966519402-chart-v2-2.js +++ b/packages/backend/migration/1615966519402-chart-v2-2.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1618637372000-user-last-active-date.js b/packages/backend/migration/1618637372000-user-last-active-date.js index 8b4652898d7bcb3fce7508c2924b8b87132efc11..7caf179fa528e57cdc16f91e4d32c19ea228c52f 100644 --- a/packages/backend/migration/1618637372000-user-last-active-date.js +++ b/packages/backend/migration/1618637372000-user-last-active-date.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1618639857000-user-hide-online-status.js b/packages/backend/migration/1618639857000-user-hide-online-status.js index 1f19a7ebb4546e463a6ba13f1104b45ab76466d0..20129627420ac58ef38520aba3f57db5bed76dd7 100644 --- a/packages/backend/migration/1618639857000-user-hide-online-status.js +++ b/packages/backend/migration/1618639857000-user-hide-online-status.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1619942102890-password-reset.js b/packages/backend/migration/1619942102890-password-reset.js index 9898011774a4820e34b84d70fc9f0e2505118135..7784da2bce2bc60fd735b8fa9a3739baca4cdb31 100644 --- a/packages/backend/migration/1619942102890-password-reset.js +++ b/packages/backend/migration/1619942102890-password-reset.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1620019354680-ad.js b/packages/backend/migration/1620019354680-ad.js index 1ae66d71f4eb50f1bf563c02265be43da6c44efe..7630ed01a1d1a555e52efcf2e4df6f4b0d9cd385 100644 --- a/packages/backend/migration/1620019354680-ad.js +++ b/packages/backend/migration/1620019354680-ad.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1620364649428-ad2.js b/packages/backend/migration/1620364649428-ad2.js index b9b26be0767cc04f4aaa1956b33fb54a7940e888..7959185685124a173d94c84cb1feb2dc54b82e44 100644 --- a/packages/backend/migration/1620364649428-ad2.js +++ b/packages/backend/migration/1620364649428-ad2.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1621479946000-add-note-indexes.js b/packages/backend/migration/1621479946000-add-note-indexes.js index 299c1f6c02d6bbb98077af72fbb7f5a7a86f4659..f72bf8211eb50f3e2193f533f1b07be6455cb85a 100644 --- a/packages/backend/migration/1621479946000-add-note-indexes.js +++ b/packages/backend/migration/1621479946000-add-note-indexes.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1622679304522-user-profile-description-length.js b/packages/backend/migration/1622679304522-user-profile-description-length.js index 988456fe7d5b81841c538cdc901c1f489225f893..7324175b4680977f5ec80f7715a917edc33e1e2b 100644 --- a/packages/backend/migration/1622679304522-user-profile-description-length.js +++ b/packages/backend/migration/1622679304522-user-profile-description-length.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1622681548499-log-message-length.js b/packages/backend/migration/1622681548499-log-message-length.js index e1fa22c88bacfb1d595525c00b5d19ac41819528..b4d8d497e35ee976c72afc9e28b08c97fb89571f 100644 --- a/packages/backend/migration/1622681548499-log-message-length.js +++ b/packages/backend/migration/1622681548499-log-message-length.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1626509500668-fix-remote-file-proxy.js b/packages/backend/migration/1626509500668-fix-remote-file-proxy.js index 906e49cabb6d7436a915359c06ba8666074837a7..9145247ab1557e3e773d1012231a42483f5926f7 100644 --- a/packages/backend/migration/1626509500668-fix-remote-file-proxy.js +++ b/packages/backend/migration/1626509500668-fix-remote-file-proxy.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1629004542760-chart-reindex.js b/packages/backend/migration/1629004542760-chart-reindex.js index f1d08ecfe48c7b8a0c0214c997d886c462494165..072cdec3c1395c31c7d94191014380c4e8992fad 100644 --- a/packages/backend/migration/1629004542760-chart-reindex.js +++ b/packages/backend/migration/1629004542760-chart-reindex.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1629024377804-deepl-integration.js b/packages/backend/migration/1629024377804-deepl-integration.js index 465f1bcca91a0ce27ca1ed39fa0cb839ba7bbb50..5889196f158cdf82273d7caa53d8132f0eba210e 100644 --- a/packages/backend/migration/1629024377804-deepl-integration.js +++ b/packages/backend/migration/1629024377804-deepl-integration.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1629288472000-fix-channel-userId.js b/packages/backend/migration/1629288472000-fix-channel-userId.js index 9f946ad550fecc27753f5c77de829265034517e4..d7907d05bdea70f437da1640e9a7640c72593b6a 100644 --- a/packages/backend/migration/1629288472000-fix-channel-userId.js +++ b/packages/backend/migration/1629288472000-fix-channel-userId.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1629512953000-user-is-deleted.js b/packages/backend/migration/1629512953000-user-is-deleted.js index 78bbd8bbeec276daa6893ee4fc1b9279008c8427..94165e466b80dccbda9fe571263408b741ab9bf9 100644 --- a/packages/backend/migration/1629512953000-user-is-deleted.js +++ b/packages/backend/migration/1629512953000-user-is-deleted.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1629778475000-deepl-integration2.js b/packages/backend/migration/1629778475000-deepl-integration2.js index b719dcf57f4d3281b97acacc099ad7158939c000..a54daf8fb312f38976cefa6d3b43a340a07d8879 100644 --- a/packages/backend/migration/1629778475000-deepl-integration2.js +++ b/packages/backend/migration/1629778475000-deepl-integration2.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1629833361000-AddShowTLReplies.js b/packages/backend/migration/1629833361000-AddShowTLReplies.js index 00aef6aeb80af91db48440e7aa1ff385c01c1820..b80e2ef67fa2ff028e129d027d438cf88d8b09c7 100644 --- a/packages/backend/migration/1629833361000-AddShowTLReplies.js +++ b/packages/backend/migration/1629833361000-AddShowTLReplies.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1629968054000_userInstanceBlocks.js b/packages/backend/migration/1629968054000_userInstanceBlocks.js index e8168e372e961f5b2101cff21bc29716adf5343b..e88fa8aece3bf700968932a194496cfecc03d7f3 100644 --- a/packages/backend/migration/1629968054000_userInstanceBlocks.js +++ b/packages/backend/migration/1629968054000_userInstanceBlocks.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1633068642000-email-required-for-signup.js b/packages/backend/migration/1633068642000-email-required-for-signup.js index 230227d3648a56ca27f3bf3203b8aa7358acbe2b..d23db2052f8ab32c7cad71645f1fbc974596ec56 100644 --- a/packages/backend/migration/1633068642000-email-required-for-signup.js +++ b/packages/backend/migration/1633068642000-email-required-for-signup.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1633071909016-user-pending.js b/packages/backend/migration/1633071909016-user-pending.js index f0d037967ff2acc37d07ee71bfcfd513f604fdf9..db0f2fde1a37af1716a5048311ca001a2de7d824 100644 --- a/packages/backend/migration/1633071909016-user-pending.js +++ b/packages/backend/migration/1633071909016-user-pending.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1634486652000-user-public-reactions.js b/packages/backend/migration/1634486652000-user-public-reactions.js index 09870c79c6b700a7f3c9b64ca4346ef798313cc7..ce1818886a3c15bee00bef9fbb61f6f9c789107a 100644 --- a/packages/backend/migration/1634486652000-user-public-reactions.js +++ b/packages/backend/migration/1634486652000-user-public-reactions.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1634902659689-delete-log.js b/packages/backend/migration/1634902659689-delete-log.js index e4e625536be53894648dc0f65d94d173d8c08f60..2e2267f9f43593f3d44ddbaa6e7a2efe8ddc9bfd 100644 --- a/packages/backend/migration/1634902659689-delete-log.js +++ b/packages/backend/migration/1634902659689-delete-log.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1635500777168-note-thread-mute.js b/packages/backend/migration/1635500777168-note-thread-mute.js index 9f376c4795dc2ac0ca4bf5d58c573de6b1f28154..d5fca59594d62311966512d83dd6172668dd345b 100644 --- a/packages/backend/migration/1635500777168-note-thread-mute.js +++ b/packages/backend/migration/1635500777168-note-thread-mute.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1636197624383-ff-visibility.js b/packages/backend/migration/1636197624383-ff-visibility.js index aa089d42ac664a856032eaf1dd8757e31c9b60e1..27faae1c9213bc083dca58f1f1222ba503e9b9e6 100644 --- a/packages/backend/migration/1636197624383-ff-visibility.js +++ b/packages/backend/migration/1636197624383-ff-visibility.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1636697408073-remove-via-mobile.js b/packages/backend/migration/1636697408073-remove-via-mobile.js index c014ceb921826f4bfb65aa26c0984fdb2960dad4..81f0b634437329be5c191956fc1a47591f5ac6ec 100644 --- a/packages/backend/migration/1636697408073-remove-via-mobile.js +++ b/packages/backend/migration/1636697408073-remove-via-mobile.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1637320813000-forwarded-report.js b/packages/backend/migration/1637320813000-forwarded-report.js index 0d1f48beb435cb81bb90b04b7bdb8fb65379ac3e..8125468aaeb486f6f4d36eb12a69549a02a65783 100644 --- a/packages/backend/migration/1637320813000-forwarded-report.js +++ b/packages/backend/migration/1637320813000-forwarded-report.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1639325650583-chart-v3.js b/packages/backend/migration/1639325650583-chart-v3.js index e6209e2b706607e752234d4c0213e28b57de94bf..2255476394b86afe07d441403fbc9978a3c4a270 100644 --- a/packages/backend/migration/1639325650583-chart-v3.js +++ b/packages/backend/migration/1639325650583-chart-v3.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1642611822809-emoji-url.js b/packages/backend/migration/1642611822809-emoji-url.js index 212fc957ad60cd21b53584ee298ebeaf5c966808..421614b40805a8f9572fd5912923610b7385be0d 100644 --- a/packages/backend/migration/1642611822809-emoji-url.js +++ b/packages/backend/migration/1642611822809-emoji-url.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1642613870898-drive-file-webpublic-type.js b/packages/backend/migration/1642613870898-drive-file-webpublic-type.js index e50770fff3bc022404e896aa2e7e06e758a8876d..e61a3fc49e92daf3e3ad037f5b25498020bf4447 100644 --- a/packages/backend/migration/1642613870898-drive-file-webpublic-type.js +++ b/packages/backend/migration/1642613870898-drive-file-webpublic-type.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1643963705770-chart-v4.js b/packages/backend/migration/1643963705770-chart-v4.js index af0bd18e58ef0b5236f46ea2a5792afc52b3980e..77355cd7f34902262a3c72f8efc2959734089063 100644 --- a/packages/backend/migration/1643963705770-chart-v4.js +++ b/packages/backend/migration/1643963705770-chart-v4.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1643966656277-chart-v5.js b/packages/backend/migration/1643966656277-chart-v5.js index b3389a65392e172579f2100e31c28a73b560dbc8..54e4705e56946aed1f4579e0150a7dc70660aece 100644 --- a/packages/backend/migration/1643966656277-chart-v5.js +++ b/packages/backend/migration/1643966656277-chart-v5.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1643967331284-chart-v6.js b/packages/backend/migration/1643967331284-chart-v6.js index 1197bdd7177f195c1e6a523428ec65d666e9a7e5..aa64bc9faa0a403c6db57abfa1e059ad08edb6e5 100644 --- a/packages/backend/migration/1643967331284-chart-v6.js +++ b/packages/backend/migration/1643967331284-chart-v6.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1644010796173-convert-hard-mutes.js b/packages/backend/migration/1644010796173-convert-hard-mutes.js index 1a5316ac0573b5071d13818e2d8b6ccd73cf27f6..9aec21b5ff492ed1a8e66637556ce6f8fd1a013d 100644 --- a/packages/backend/migration/1644010796173-convert-hard-mutes.js +++ b/packages/backend/migration/1644010796173-convert-hard-mutes.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1644058404077-chart-v7.js b/packages/backend/migration/1644058404077-chart-v7.js index a850d5f48fcc48fbc0aa4910dd1cbcd32422a126..a09fff1bc71382d4ac8ab2881f2d1203e25cc85c 100644 --- a/packages/backend/migration/1644058404077-chart-v7.js +++ b/packages/backend/migration/1644058404077-chart-v7.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1644059847460-chart-v8.js b/packages/backend/migration/1644059847460-chart-v8.js index 2e20159ba9ec02cdd6d3cdf4fa0186ab0e5eb31b..43b95926b6110a94a13fc35e60fbab3b43161648 100644 --- a/packages/backend/migration/1644059847460-chart-v8.js +++ b/packages/backend/migration/1644059847460-chart-v8.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1644060125705-chart-v9.js b/packages/backend/migration/1644060125705-chart-v9.js index d1d9469ea2b9ef1149de6ea90058470fbeea1290..dc99f3c8f8ac805f310952ee60b37fcb39715fa8 100644 --- a/packages/backend/migration/1644060125705-chart-v9.js +++ b/packages/backend/migration/1644060125705-chart-v9.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1644073149413-chart-v10.js b/packages/backend/migration/1644073149413-chart-v10.js index 466ae598370d6d3de501effd18e12877fe58d9fd..4d36235729a60bcfc74d965713fe42f95321fbf8 100644 --- a/packages/backend/migration/1644073149413-chart-v10.js +++ b/packages/backend/migration/1644073149413-chart-v10.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1644095659741-chart-v11.js b/packages/backend/migration/1644095659741-chart-v11.js index 5c98e25d86c0f90639068fbc39883bc9d9c752db..80bacbf710b1801e297fc26d7b85abe10cc7f86d 100644 --- a/packages/backend/migration/1644095659741-chart-v11.js +++ b/packages/backend/migration/1644095659741-chart-v11.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1644328606241-chart-v12.js b/packages/backend/migration/1644328606241-chart-v12.js index 2a7272fd220ddd76204cbd800459f7834ae5be26..15c0dd9040489d065f1898d9ebba19c71f359d22 100644 --- a/packages/backend/migration/1644328606241-chart-v12.js +++ b/packages/backend/migration/1644328606241-chart-v12.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1644331238153-chart-v13.js b/packages/backend/migration/1644331238153-chart-v13.js index 7e33b0a8e91a6a5fca2348904f93db5923c32f88..0c2db66f2730d0f8b3276ecb8abb2f50214d41d0 100644 --- a/packages/backend/migration/1644331238153-chart-v13.js +++ b/packages/backend/migration/1644331238153-chart-v13.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1644344266289-chart-v14.js b/packages/backend/migration/1644344266289-chart-v14.js index 2050d54591230e94886574f38cb5e8d9bd345b19..0f4688ab772fcba7b76f0222230c92cb73105d67 100644 --- a/packages/backend/migration/1644344266289-chart-v14.js +++ b/packages/backend/migration/1644344266289-chart-v14.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1644395759931-instance-theme-color.js b/packages/backend/migration/1644395759931-instance-theme-color.js index ac842e4fe50629ec009c83430d41ef6c8841b73a..fd7356e68ac8c668c61ac2ee305205c21b11d949 100644 --- a/packages/backend/migration/1644395759931-instance-theme-color.js +++ b/packages/backend/migration/1644395759931-instance-theme-color.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1644481657998-chart-v15.js b/packages/backend/migration/1644481657998-chart-v15.js index ad5589df8b571505c6dfa72d2a082bb803c3f783..964bea3d07dc4bb143f73cd6b9178053d545cb7f 100644 --- a/packages/backend/migration/1644481657998-chart-v15.js +++ b/packages/backend/migration/1644481657998-chart-v15.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1644551208096-following-indexes.js b/packages/backend/migration/1644551208096-following-indexes.js index 795b8e900ea18526f16e18581b6c05103bafad68..8d1d4890dc8f330881f235ee896b693e8c26aabb 100644 --- a/packages/backend/migration/1644551208096-following-indexes.js +++ b/packages/backend/migration/1644551208096-following-indexes.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1645340161439-remove-max-note-text-length.js b/packages/backend/migration/1645340161439-remove-max-note-text-length.js index 84eaeddfa470c545abc0ed45cfe92a1ec863a055..1cf6b0801b984847e18c31ec5c695003d5c45650 100644 --- a/packages/backend/migration/1645340161439-remove-max-note-text-length.js +++ b/packages/backend/migration/1645340161439-remove-max-note-text-length.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1645599900873-federation-chart-pubsub.js b/packages/backend/migration/1645599900873-federation-chart-pubsub.js index 4f9f501cca361cb5c87998645dfb69d3b6596b2b..3042c8ecd96086bbf003c4c8a086ad9082f3fb52 100644 --- a/packages/backend/migration/1645599900873-federation-chart-pubsub.js +++ b/packages/backend/migration/1645599900873-federation-chart-pubsub.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1646143552768-instance-default-theme.js b/packages/backend/migration/1646143552768-instance-default-theme.js index 353291630457eaf9b7f7bf72d5a3cd6d91547db5..8f0755e3a200494199354c6e94a0cd00748b44af 100644 --- a/packages/backend/migration/1646143552768-instance-default-theme.js +++ b/packages/backend/migration/1646143552768-instance-default-theme.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1646387162108-mute-expires-at.js b/packages/backend/migration/1646387162108-mute-expires-at.js index 868f5c87ef5b07bb8aeb07a09e94976812667b57..412db1488166b3430be5a68c543446eb971f0fa0 100644 --- a/packages/backend/migration/1646387162108-mute-expires-at.js +++ b/packages/backend/migration/1646387162108-mute-expires-at.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1646549089451-poll-ended-notification.js b/packages/backend/migration/1646549089451-poll-ended-notification.js index fa7327ff9ccbe6a989182990b58cd3c2daf256c8..6c481c6ac6690614b5e19ef08fea1ac780a6737d 100644 --- a/packages/backend/migration/1646549089451-poll-ended-notification.js +++ b/packages/backend/migration/1646549089451-poll-ended-notification.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1646633030285-chart-federation-active.js b/packages/backend/migration/1646633030285-chart-federation-active.js index b9863746adce432e09dfd802af7a5823175a30a2..13d54c3180d117eea47508b376186be1d482356a 100644 --- a/packages/backend/migration/1646633030285-chart-federation-active.js +++ b/packages/backend/migration/1646633030285-chart-federation-active.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1646655454495-remove-instance-drive-columns.js b/packages/backend/migration/1646655454495-remove-instance-drive-columns.js index 8fd96ed4c69941f17d3180c0b46882c5a649e512..04d6fce8877a2f49eaaca299af138d43bc91cf26 100644 --- a/packages/backend/migration/1646655454495-remove-instance-drive-columns.js +++ b/packages/backend/migration/1646655454495-remove-instance-drive-columns.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1646732390560-chart-federation-active-sub-pub.js b/packages/backend/migration/1646732390560-chart-federation-active-sub-pub.js index 1b28d012ae9e2e9931a0048f73cb01ba8627bc49..289b929ad979236ad2f4bd940823d3fb06c6d7d2 100644 --- a/packages/backend/migration/1646732390560-chart-federation-active-sub-pub.js +++ b/packages/backend/migration/1646732390560-chart-federation-active-sub-pub.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1648548247382-webhook.js b/packages/backend/migration/1648548247382-webhook.js index fc2a6919188a0042a9509f3aded6f106ab253aa4..f31d3c5bb5b36bb45eaa8b4cd7d6674d9e1f8679 100644 --- a/packages/backend/migration/1648548247382-webhook.js +++ b/packages/backend/migration/1648548247382-webhook.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1648816172177-webhook-2.js b/packages/backend/migration/1648816172177-webhook-2.js index a7bccff82da2fd72d48301a1df97b1ab6c0ae994..4d1b293b2cd6e193ee6a7464d809cacb73570fa6 100644 --- a/packages/backend/migration/1648816172177-webhook-2.js +++ b/packages/backend/migration/1648816172177-webhook-2.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1651224615271-foreign-key.js b/packages/backend/migration/1651224615271-foreign-key.js index 12e464632988ace2cf33cae19c2d4014671b8d7c..fa51bb5e31aa5ed0cd7b4a140f9c90f68ec77b83 100644 --- a/packages/backend/migration/1651224615271-foreign-key.js +++ b/packages/backend/migration/1651224615271-foreign-key.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1652859567549-uniform-themecolor.js b/packages/backend/migration/1652859567549-uniform-themecolor.js index 422e63dfec14b54db8d116cdd0a96f94170e3d70..754e0898243317c7bff7e8c6e260788215b95950 100644 --- a/packages/backend/migration/1652859567549-uniform-themecolor.js +++ b/packages/backend/migration/1652859567549-uniform-themecolor.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1655368940105-nsfw-detection.js b/packages/backend/migration/1655368940105-nsfw-detection.js index ad37ff6f830043a2d423bef1b8d5c61d33f8dee0..d2d0d001173f3c4c8f18a50bd1f0191827e15882 100644 --- a/packages/backend/migration/1655368940105-nsfw-detection.js +++ b/packages/backend/migration/1655368940105-nsfw-detection.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1655371960534-nsfw-detection-2.js b/packages/backend/migration/1655371960534-nsfw-detection-2.js index e6cc2661786c3cad1a1fe402525fa049674efe6c..e5adbddca44de176821070922aa6c3a65f523172 100644 --- a/packages/backend/migration/1655371960534-nsfw-detection-2.js +++ b/packages/backend/migration/1655371960534-nsfw-detection-2.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1655388169582-nsfw-detection-3.js b/packages/backend/migration/1655388169582-nsfw-detection-3.js index 40362cc20c0a4d074ef96c6cc0ab582ffb715e7f..12fc2813276cf3d79eab655f6534305314df6656 100644 --- a/packages/backend/migration/1655388169582-nsfw-detection-3.js +++ b/packages/backend/migration/1655388169582-nsfw-detection-3.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1655393015659-nsfw-detection-4.js b/packages/backend/migration/1655393015659-nsfw-detection-4.js index d74fe9c929d688ac7b23ffed80d43cd5d1b9fd8a..39fb17567919be75b6105e524ee47fd0dd0e3db5 100644 --- a/packages/backend/migration/1655393015659-nsfw-detection-4.js +++ b/packages/backend/migration/1655393015659-nsfw-detection-4.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1655813815729-driveCapacityOverrideMb.js b/packages/backend/migration/1655813815729-driveCapacityOverrideMb.js index 7e97f9dc746893e50b73a70692d93773100a30fe..e64c8c1b82fe50bde78e8aea097f5c40a36f7098 100644 --- a/packages/backend/migration/1655813815729-driveCapacityOverrideMb.js +++ b/packages/backend/migration/1655813815729-driveCapacityOverrideMb.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1655918165614-user-ip.js b/packages/backend/migration/1655918165614-user-ip.js index ccb3ceb49da48fa274546b7ea2602376e0fc070e..668c6d909b84e363f7e78445d6571156c65ccd8c 100644 --- a/packages/backend/migration/1655918165614-user-ip.js +++ b/packages/backend/migration/1655918165614-user-ip.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1656122560740-file-ip.js b/packages/backend/migration/1656122560740-file-ip.js index dc02df0e68be5367397395e7dc5b7459b9278176..e5efaf3d9fce20cdf2328468daf065f6563d1e6b 100644 --- a/packages/backend/migration/1656122560740-file-ip.js +++ b/packages/backend/migration/1656122560740-file-ip.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1656251734807-nsfw-detection-5.js b/packages/backend/migration/1656251734807-nsfw-detection-5.js index 06da9251b1b3cdd95a29cd69ca276b3244b2f064..9b36bd76eb75d4d5dd74e9077b9968bdc91c3a42 100644 --- a/packages/backend/migration/1656251734807-nsfw-detection-5.js +++ b/packages/backend/migration/1656251734807-nsfw-detection-5.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1656328812281-ip-2.js b/packages/backend/migration/1656328812281-ip-2.js index 1b53e697de1ced88d2e8d2881cf8072c501141d0..39fcd1d83d9379f7a104bedac4f059d488bd8506 100644 --- a/packages/backend/migration/1656328812281-ip-2.js +++ b/packages/backend/migration/1656328812281-ip-2.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1656408772602-nsfw-detection-6.js b/packages/backend/migration/1656408772602-nsfw-detection-6.js index 0adc8bb7937196f73e67e785038bb45ccdd62685..efadd22e5d73219b03de856e4565872f2a16930a 100644 --- a/packages/backend/migration/1656408772602-nsfw-detection-6.js +++ b/packages/backend/migration/1656408772602-nsfw-detection-6.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1656772790599-user-moderation-note.js b/packages/backend/migration/1656772790599-user-moderation-note.js index 63a993851f30d938e1e12a8123d76f5457fc1ee6..ef2f0f652229f3b26f8a072ea6926eacec9615cb 100644 --- a/packages/backend/migration/1656772790599-user-moderation-note.js +++ b/packages/backend/migration/1656772790599-user-moderation-note.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1657346559800-active-email-validation.js b/packages/backend/migration/1657346559800-active-email-validation.js index 44b1f3f4fa0b477d6effa133809c3c419776218d..e8d5b29cdfeb8647aa215209bc0ea68603b0e6ac 100644 --- a/packages/backend/migration/1657346559800-active-email-validation.js +++ b/packages/backend/migration/1657346559800-active-email-validation.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1664694635394-turnstile.js b/packages/backend/migration/1664694635394-turnstile.js index 3ec6da913600382df1e20b7519df644d6cf48d22..a9baf4c657d3b122fcc5fdec5d2d37963de9121a 100644 --- a/packages/backend/migration/1664694635394-turnstile.js +++ b/packages/backend/migration/1664694635394-turnstile.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1665091090561-add-renote-muting.js b/packages/backend/migration/1665091090561-add-renote-muting.js index a22d7037f30e9bb425831aaa4ca602b1e7623a5a..5748572517450bac606997a80d981860f3de4a32 100644 --- a/packages/backend/migration/1665091090561-add-renote-muting.js +++ b/packages/backend/migration/1665091090561-add-renote-muting.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1669138716634-whetherPushNotifyToSendReadMessage.js b/packages/backend/migration/1669138716634-whetherPushNotifyToSendReadMessage.js index a317468ac970e10698225c9dfa0f04388fa5fd40..431241897d1cbf03b46ccc5823af42d57e9bd412 100644 --- a/packages/backend/migration/1669138716634-whetherPushNotifyToSendReadMessage.js +++ b/packages/backend/migration/1669138716634-whetherPushNotifyToSendReadMessage.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1671924750884-RetentionAggregation.js b/packages/backend/migration/1671924750884-RetentionAggregation.js index 5057bf1060a0b7d2de887c27ac4cf6c0580af0c6..67079bb7a1e778f2c8670bea95368111d069ec6c 100644 --- a/packages/backend/migration/1671924750884-RetentionAggregation.js +++ b/packages/backend/migration/1671924750884-RetentionAggregation.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1671926422832-RetentionAggregation2.js b/packages/backend/migration/1671926422832-RetentionAggregation2.js index 665e24d72132ed3bbaaae1108ba261e7ae06536c..f26e0f7d2e16b93e0479a1bcbfe0cbc02e562e04 100644 --- a/packages/backend/migration/1671926422832-RetentionAggregation2.js +++ b/packages/backend/migration/1671926422832-RetentionAggregation2.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1672562400597-PerUserPvChart.js b/packages/backend/migration/1672562400597-PerUserPvChart.js index 1fbe1eb14a24635d0c974afdff6ba17a272393eb..844f665a8bf0490cf131eb5902122ef783120d12 100644 --- a/packages/backend/migration/1672562400597-PerUserPvChart.js +++ b/packages/backend/migration/1672562400597-PerUserPvChart.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1672703171386-remove-latestRequestSentAt.js b/packages/backend/migration/1672703171386-remove-latestRequestSentAt.js index f053e5c20c61654155ce48a4993c5468a3edf0c8..fa73fc8977f0faf080bc3ee6af59608fb6694b57 100644 --- a/packages/backend/migration/1672703171386-remove-latestRequestSentAt.js +++ b/packages/backend/migration/1672703171386-remove-latestRequestSentAt.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1672704017999-remove-lastCommunicatedAt.js b/packages/backend/migration/1672704017999-remove-lastCommunicatedAt.js index b71f7e1306d0d622c335b1c335a8df93f5635e06..abf209162b9d79813ab3f04958507d7aa18f7685 100644 --- a/packages/backend/migration/1672704017999-remove-lastCommunicatedAt.js +++ b/packages/backend/migration/1672704017999-remove-lastCommunicatedAt.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1672704136584-remove-latestStatus.js b/packages/backend/migration/1672704136584-remove-latestStatus.js index f08ed96a4588124c19575ba4f5b57c069064fe92..d75344c0530f73ed93eeefc1cbfc82ff5d605b64 100644 --- a/packages/backend/migration/1672704136584-remove-latestStatus.js +++ b/packages/backend/migration/1672704136584-remove-latestStatus.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1672822262496-Flash.js b/packages/backend/migration/1672822262496-Flash.js index e45055b3cc62c20ccd02648dc8c9acc5edcfd9e7..fd3f77d893ed19182963e7c27c11ba133c1ea9ae 100644 --- a/packages/backend/migration/1672822262496-Flash.js +++ b/packages/backend/migration/1672822262496-Flash.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1673336077243-PollChoiceLength.js b/packages/backend/migration/1673336077243-PollChoiceLength.js index 8c4a5007e46c85bfdb2fd0594c4a9a5ec25315d2..7bd65149d62a8fe5f03dc45c328effc552ba73fb 100644 --- a/packages/backend/migration/1673336077243-PollChoiceLength.js +++ b/packages/backend/migration/1673336077243-PollChoiceLength.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1673500412259-Role.js b/packages/backend/migration/1673500412259-Role.js index 2bf6a7f4e868fef34cf38b4ad43d751307f14b6a..6bfb31e08e82c5f41b3e7a95d2f02063879c28bf 100644 --- a/packages/backend/migration/1673500412259-Role.js +++ b/packages/backend/migration/1673500412259-Role.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1673515526953-RoleColor.js b/packages/backend/migration/1673515526953-RoleColor.js index 693dcfb0b676808a46b76a73ce5a001f6b11def7..b856e4183b6f99214e21d6df6314287925a02067 100644 --- a/packages/backend/migration/1673515526953-RoleColor.js +++ b/packages/backend/migration/1673515526953-RoleColor.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1673522856499-RoleIroiro.js b/packages/backend/migration/1673522856499-RoleIroiro.js index 10a6eef1625f25a7d233c721cbb7d6f68f6d2f1b..40635e50d87f8a8bf992cb5794650f5459454e6a 100644 --- a/packages/backend/migration/1673522856499-RoleIroiro.js +++ b/packages/backend/migration/1673522856499-RoleIroiro.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1673524604156-RoleLastUsedAt.js b/packages/backend/migration/1673524604156-RoleLastUsedAt.js index 5bbd0c39ac5a4e75a7ff1789d4d387450062967e..3bbb8000d8ab9600e17d0ee27ffead66a6091c98 100644 --- a/packages/backend/migration/1673524604156-RoleLastUsedAt.js +++ b/packages/backend/migration/1673524604156-RoleLastUsedAt.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1673570377815-RoleConditional.js b/packages/backend/migration/1673570377815-RoleConditional.js index d2b25d121e7a773eb30e2c425e228bd829730ce2..354fd6c66adf142acdf77d624b55ad785352fad1 100644 --- a/packages/backend/migration/1673570377815-RoleConditional.js +++ b/packages/backend/migration/1673570377815-RoleConditional.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1673575973645-MetaClean.js b/packages/backend/migration/1673575973645-MetaClean.js index 7671785d947c0ec8dc08e2d5600cf00a66747099..684d62e8e98de2586e53dd2f8fcbd41dbcb837b3 100644 --- a/packages/backend/migration/1673575973645-MetaClean.js +++ b/packages/backend/migration/1673575973645-MetaClean.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1673783015567-Policies.js b/packages/backend/migration/1673783015567-Policies.js index 4f76752c9fe3bd9f26311a82a97320f8c97cb9d9..8674306620a6195d0ae20756d260111f0394388b 100644 --- a/packages/backend/migration/1673783015567-Policies.js +++ b/packages/backend/migration/1673783015567-Policies.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1673812883772-firstRetrievedAt.js b/packages/backend/migration/1673812883772-firstRetrievedAt.js index 82990e30b6e2e0e8a53d392af9f970d2bde3c4c3..4111cc4ad042d26c4a7f7e60479194a4671d2153 100644 --- a/packages/backend/migration/1673812883772-firstRetrievedAt.js +++ b/packages/backend/migration/1673812883772-firstRetrievedAt.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1674086433654-flashScriptLength.js b/packages/backend/migration/1674086433654-flashScriptLength.js index 996fe8c691a2771f1faccfea55271c1f0a78764f..cdfb812ba0c3a9f75849b4d533768bbc0cf1c8d1 100644 --- a/packages/backend/migration/1674086433654-flashScriptLength.js +++ b/packages/backend/migration/1674086433654-flashScriptLength.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1674118260469-achievement.js b/packages/backend/migration/1674118260469-achievement.js index 5d79dc669e2ebb78e2b08dcfc3c591ce996d843b..072cf81ec33fb1e695f73d12a009b7148b9921b9 100644 --- a/packages/backend/migration/1674118260469-achievement.js +++ b/packages/backend/migration/1674118260469-achievement.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1674255666603-loggedInDates.js b/packages/backend/migration/1674255666603-loggedInDates.js index a6cf4b400f436b4e9466eb5ff4df7d032e4e14e5..a2a217da95aa262ed88cae44cb06718168eb9a68 100644 --- a/packages/backend/migration/1674255666603-loggedInDates.js +++ b/packages/backend/migration/1674255666603-loggedInDates.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1675053125067-fixforeignkeyreports.js b/packages/backend/migration/1675053125067-fixforeignkeyreports.js index d24dc5ec5a409f5eade29e3d87059b89897b28d4..2ca383f563d5e14519537d71f1115c8ed789f459 100644 --- a/packages/backend/migration/1675053125067-fixforeignkeyreports.js +++ b/packages/backend/migration/1675053125067-fixforeignkeyreports.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1675404035646-cleanup.js b/packages/backend/migration/1675404035646-cleanup.js index c4e4332bbcf3bcc7ddc6e241d0cf4d238c64f7f0..5cd5f5534a8848314b5b559a1dc52ad3ff9edd1f 100644 --- a/packages/backend/migration/1675404035646-cleanup.js +++ b/packages/backend/migration/1675404035646-cleanup.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1675557528704-role-icon-badge.js b/packages/backend/migration/1675557528704-role-icon-badge.js index ee39c07a515bbc30b5805f79d96e5b40258965bd..48684075d12c167013707200240d832ebea15320 100644 --- a/packages/backend/migration/1675557528704-role-icon-badge.js +++ b/packages/backend/migration/1675557528704-role-icon-badge.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1676434944993-drop-group.js b/packages/backend/migration/1676434944993-drop-group.js index 1db2d5818f55f692a1ad7e458710571f4ca3987f..2df8a2d78944693e9d759e148b7de1f6b0e7a6c0 100644 --- a/packages/backend/migration/1676434944993-drop-group.js +++ b/packages/backend/migration/1676434944993-drop-group.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1676438468213-ad3.js b/packages/backend/migration/1676438468213-ad3.js index 8347f56b95ce1b6f7d992905c85c8c3047218e96..83ca5828e315046210630dbb7f4f091a7addccc2 100644 --- a/packages/backend/migration/1676438468213-ad3.js +++ b/packages/backend/migration/1676438468213-ad3.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1677054292210-ad4.js b/packages/backend/migration/1677054292210-ad4.js index 037e21059ca66bdd9efb5934e2672570544fc836..11c42dd354ad894e7ac67ff54bd43f34319a45e2 100644 --- a/packages/backend/migration/1677054292210-ad4.js +++ b/packages/backend/migration/1677054292210-ad4.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1677570181236-role-assignment-expires-at.js b/packages/backend/migration/1677570181236-role-assignment-expires-at.js index e44bca1d20ddc305c5661d19b461335af1008606..6fe32ffeb0d6b92abdad415b3d66711b5662c4b9 100644 --- a/packages/backend/migration/1677570181236-role-assignment-expires-at.js +++ b/packages/backend/migration/1677570181236-role-assignment-expires-at.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1678164627293-per-note-reaction-acceptance.js b/packages/backend/migration/1678164627293-per-note-reaction-acceptance.js index c85aafbd4c3f5949f3ac5503d89403c780ec2a51..44c807499c1c347392237501f4d88a188aabc559 100644 --- a/packages/backend/migration/1678164627293-per-note-reaction-acceptance.js +++ b/packages/backend/migration/1678164627293-per-note-reaction-acceptance.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1678426061773-tweak-varchar-length.js b/packages/backend/migration/1678426061773-tweak-varchar-length.js index 2541f99a190401c0cab49ac9444996aa55f4d69d..74c4fd671517ddfde33875207d53a0a767b11237 100644 --- a/packages/backend/migration/1678426061773-tweak-varchar-length.js +++ b/packages/backend/migration/1678426061773-tweak-varchar-length.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1678427401214-remove-unused.js b/packages/backend/migration/1678427401214-remove-unused.js index e2947034ea24eb40895bd1845e09229dc7009742..da9c252b192f2e5ea3161cf1629dbf5aca5c439e 100644 --- a/packages/backend/migration/1678427401214-remove-unused.js +++ b/packages/backend/migration/1678427401214-remove-unused.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1678602320354-role-display-order.js b/packages/backend/migration/1678602320354-role-display-order.js index 0ab7b0c3e2913f0fce254e1a6a8feb72e1efc915..d3cc9792caaed7e06c3fa2f2860adc69ee3cc8cb 100644 --- a/packages/backend/migration/1678602320354-role-display-order.js +++ b/packages/backend/migration/1678602320354-role-display-order.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1678694614599-sensitive-words.js b/packages/backend/migration/1678694614599-sensitive-words.js index 5f69424ecad5613d3431da4468aa072969bf9822..13361f597efc6d2e4d64ee2cff339527e749fb36 100644 --- a/packages/backend/migration/1678694614599-sensitive-words.js +++ b/packages/backend/migration/1678694614599-sensitive-words.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1678869617549-retention-date-key.js b/packages/backend/migration/1678869617549-retention-date-key.js index 55bf6248e6dc94fe47b25a23ec2bf0e44ea608e8..1b995385b019941fd270b761e54879c481c3eaf6 100644 --- a/packages/backend/migration/1678869617549-retention-date-key.js +++ b/packages/backend/migration/1678869617549-retention-date-key.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1678945242650-add-props-for-custom-emoji.js b/packages/backend/migration/1678945242650-add-props-for-custom-emoji.js index 0054e78f880831f0c3145fe23fe6afc869287cdc..5d1218be1282c6272408b47f593cdcb8e35d63ce 100644 --- a/packages/backend/migration/1678945242650-add-props-for-custom-emoji.js +++ b/packages/backend/migration/1678945242650-add-props-for-custom-emoji.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1678953978856-clip-favorite.js b/packages/backend/migration/1678953978856-clip-favorite.js index 13145497bb49c71f23b7b5aec21fa609ea5626c7..9d706c4daebb2d0ad056f4c17367134c7bbcca25 100644 --- a/packages/backend/migration/1678953978856-clip-favorite.js +++ b/packages/backend/migration/1678953978856-clip-favorite.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1679309757174-antenna-active.js b/packages/backend/migration/1679309757174-antenna-active.js index 0b2bcc69ffd358a29646fcf9f2b6a339cd238ee4..dadea25a7c8b4b25aacad36672889c2f8af211a6 100644 --- a/packages/backend/migration/1679309757174-antenna-active.js +++ b/packages/backend/migration/1679309757174-antenna-active.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1679639483253-enableChartsForRemoteUser.js b/packages/backend/migration/1679639483253-enableChartsForRemoteUser.js index 68576064f2bbe09985c9b9de38b4fb3359bb9228..f2a13100e2900f459889525ef4575b6f1b179453 100644 --- a/packages/backend/migration/1679639483253-enableChartsForRemoteUser.js +++ b/packages/backend/migration/1679639483253-enableChartsForRemoteUser.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1679651580149-cleanup.js b/packages/backend/migration/1679651580149-cleanup.js index 7049891cf0c2cd997aef1ba7b48c49c8f60504d3..efee339c4648add2e03e632f7872404f7e1f5b62 100644 --- a/packages/backend/migration/1679651580149-cleanup.js +++ b/packages/backend/migration/1679651580149-cleanup.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1679652081809-enableChartsForFederatedInstances.js b/packages/backend/migration/1679652081809-enableChartsForFederatedInstances.js index f3a07cbd1d5fa6ce9bcc603be6bf2283594b08c8..67be10e6fde2c5e77039dc2b985d058188017c79 100644 --- a/packages/backend/migration/1679652081809-enableChartsForFederatedInstances.js +++ b/packages/backend/migration/1679652081809-enableChartsForFederatedInstances.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1680228513388-channelFavorite.js b/packages/backend/migration/1680228513388-channelFavorite.js index 58eb7359f2096b155cb77b4c91a6c6932e704be7..866173305ed4e8336c85cbf3ff00fd1051bb49e3 100644 --- a/packages/backend/migration/1680228513388-channelFavorite.js +++ b/packages/backend/migration/1680228513388-channelFavorite.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1680238118084-channelNotePining.js b/packages/backend/migration/1680238118084-channelNotePining.js index f1f192d7bb5a7b66f9088988542c9770b38c2989..78bafc02375f966192c8eac586d3c01a25fd4997 100644 --- a/packages/backend/migration/1680238118084-channelNotePining.js +++ b/packages/backend/migration/1680238118084-channelNotePining.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1680491187535-cleanup.js b/packages/backend/migration/1680491187535-cleanup.js index 006b403bd1e79a49c95abee38c05542736f9c71e..f0b1bccdabbc07a36b254338a89d5fdce70f382e 100644 --- a/packages/backend/migration/1680491187535-cleanup.js +++ b/packages/backend/migration/1680491187535-cleanup.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1680582195041-cleanup.js b/packages/backend/migration/1680582195041-cleanup.js index 7d941be8cf0267c8a3958827089987104eb2e4f4..83d04b618693cfcbe5166ada5d9c5520adcac1b0 100644 --- a/packages/backend/migration/1680582195041-cleanup.js +++ b/packages/backend/migration/1680582195041-cleanup.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1680702787050-UserMemo.js b/packages/backend/migration/1680702787050-UserMemo.js index 104d66ce24712892d083fbf7a9af264745067561..3f7afe86573ed9a7f2ea904c23115186aaf14581 100644 --- a/packages/backend/migration/1680702787050-UserMemo.js +++ b/packages/backend/migration/1680702787050-UserMemo.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1680775031481-avatar-url-and-banner-url.js b/packages/backend/migration/1680775031481-avatar-url-and-banner-url.js index c613ee511e38cfac9d4f15cece9096eb376c4ff4..49295e70eb80bde152a68b358408d0ece050c74d 100644 --- a/packages/backend/migration/1680775031481-avatar-url-and-banner-url.js +++ b/packages/backend/migration/1680775031481-avatar-url-and-banner-url.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1680931179228-account-move.js b/packages/backend/migration/1680931179228-account-move.js index 203d838f57b439ccaf4c18dbab3903831a6360b8..a8b5e4df68a244ca1133e18d7cb3b5a5d6677da8 100644 --- a/packages/backend/migration/1680931179228-account-move.js +++ b/packages/backend/migration/1680931179228-account-move.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1681400427971-serverRules.js b/packages/backend/migration/1681400427971-serverRules.js index 70a74ebfff9eb8ef5a42c0525582cd3c12568f79..176783b50ac5fb24a0c67add87700f303c3676e5 100644 --- a/packages/backend/migration/1681400427971-serverRules.js +++ b/packages/backend/migration/1681400427971-serverRules.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1681870960239-RoleTLSetting.js b/packages/backend/migration/1681870960239-RoleTLSetting.js index 07b9bc4e35ffdffa48ca6f63e8f6a82934ecd57e..2999051a3ba4365740f429f2663983aa98c1b22a 100644 --- a/packages/backend/migration/1681870960239-RoleTLSetting.js +++ b/packages/backend/migration/1681870960239-RoleTLSetting.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1682190963894-movedAt.js b/packages/backend/migration/1682190963894-movedAt.js index cc33da874790ee6a9a88ca53dc7b4b168009eec2..852cf5896947aff39329c0c4bd19ed71b3d5edb9 100644 --- a/packages/backend/migration/1682190963894-movedAt.js +++ b/packages/backend/migration/1682190963894-movedAt.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1682754135458-preservedUsernames.js b/packages/backend/migration/1682754135458-preservedUsernames.js index 61723e4abd1a3e7e3b40ccb900fac21e658d32ce..8aae3c2054f1fa48df6750707e1275db5580876b 100644 --- a/packages/backend/migration/1682754135458-preservedUsernames.js +++ b/packages/backend/migration/1682754135458-preservedUsernames.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1682985520254-channelColor.js b/packages/backend/migration/1682985520254-channelColor.js index 43f1f4833491689a14a7cacbca332231a8977057..3c7f3101a5457ce99cd4e4ba825985d795c7f053 100644 --- a/packages/backend/migration/1682985520254-channelColor.js +++ b/packages/backend/migration/1682985520254-channelColor.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1683328299359-channelArchive.js b/packages/backend/migration/1683328299359-channelArchive.js index 759dcbfdae7627c3a21944b39c20849832b5d17f..10a87246dea4f295e08c27fed3f9c8e78e8a8d1a 100644 --- a/packages/backend/migration/1683328299359-channelArchive.js +++ b/packages/backend/migration/1683328299359-channelArchive.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1683682889948-prevent-ai-larning.js b/packages/backend/migration/1683682889948-prevent-ai-larning.js index 1dc3eec21f90582b2f280a32f8a82991bde2f0e7..167c9f71d2850d0111bfa981be82098ee2c7794e 100644 --- a/packages/backend/migration/1683682889948-prevent-ai-larning.js +++ b/packages/backend/migration/1683682889948-prevent-ai-larning.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1683683083083-public-reactions-default-true.js b/packages/backend/migration/1683683083083-public-reactions-default-true.js index 32cbe33b2f25361b13b17820e44de230999abe6f..f416e5ffa78038df968605d5140e1e93cc48a425 100644 --- a/packages/backend/migration/1683683083083-public-reactions-default-true.js +++ b/packages/backend/migration/1683683083083-public-reactions-default-true.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1683789676867-fix-typo.js b/packages/backend/migration/1683789676867-fix-typo.js index 5cd686e2f16447035151ae10f6403182aa01f145..d647d20e628415aba9b8e0f99f0a5ee753e7cca5 100644 --- a/packages/backend/migration/1683789676867-fix-typo.js +++ b/packages/backend/migration/1683789676867-fix-typo.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1683847157541-UserList.js b/packages/backend/migration/1683847157541-UserList.js index f9e79a43a169d6487739550fb0647c5f87baf120..14a52d64f8ac3c189b0b211197951c25647a19d5 100644 --- a/packages/backend/migration/1683847157541-UserList.js +++ b/packages/backend/migration/1683847157541-UserList.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1683869758873-UserListFavorites.js b/packages/backend/migration/1683869758873-UserListFavorites.js index aef4597a755a9ff19a9f30cfe63bae78c5c639c1..aae40568453d39e9550cc01fc3707649ceba1d77 100644 --- a/packages/backend/migration/1683869758873-UserListFavorites.js +++ b/packages/backend/migration/1683869758873-UserListFavorites.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1684206886988-remove-showTimelineReplies.js b/packages/backend/migration/1684206886988-remove-showTimelineReplies.js index a0798f85c67170171d62d75d7529b076553cfd13..398f9f08038993f6c032061fdecc96ad849303c6 100644 --- a/packages/backend/migration/1684206886988-remove-showTimelineReplies.js +++ b/packages/backend/migration/1684206886988-remove-showTimelineReplies.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1684386446061-emoji-improve.js b/packages/backend/migration/1684386446061-emoji-improve.js index 7bded84cc94a458f9bbd74d8adacca1b00d9a3e8..e7e94769b8c42b917210fd10606ac2b0bd3e01b9 100644 --- a/packages/backend/migration/1684386446061-emoji-improve.js +++ b/packages/backend/migration/1684386446061-emoji-improve.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1685973839966-errorImageUrl.js b/packages/backend/migration/1685973839966-errorImageUrl.js index c4a1567b9bbe213777b0205ea30cc17bfabf8b22..ca685ef088fef847767328dc15243ced2b975663 100644 --- a/packages/backend/migration/1685973839966-errorImageUrl.js +++ b/packages/backend/migration/1685973839966-errorImageUrl.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1688280713783-add-meta-options.js b/packages/backend/migration/1688280713783-add-meta-options.js index ade8378c005dfac7049f502f19df7dae524d4ca1..77d193492513ffd1f5209b9b21509bea1c1433f9 100644 --- a/packages/backend/migration/1688280713783-add-meta-options.js +++ b/packages/backend/migration/1688280713783-add-meta-options.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1688720440658-refactor-invite-system.js b/packages/backend/migration/1688720440658-refactor-invite-system.js index 20f178612db012bbc48df74f8a0c95233c289f71..ea192a1950ad9d14e10aba5a375956df1de9a82a 100644 --- a/packages/backend/migration/1688720440658-refactor-invite-system.js +++ b/packages/backend/migration/1688720440658-refactor-invite-system.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1688880985544-add-index-to-relations.js b/packages/backend/migration/1688880985544-add-index-to-relations.js index 6daac20329a63da1be9426ddd80df8b274bdb71a..c18903641cafe18aecd4572cb371f3ff37db3d22 100644 --- a/packages/backend/migration/1688880985544-add-index-to-relations.js +++ b/packages/backend/migration/1688880985544-add-index-to-relations.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1689102832143-nsfw-cache.js b/packages/backend/migration/1689102832143-nsfw-cache.js index 419588296ea17ec21c1cd1a99409bfb04ee22045..90d453418baeeff817146b1dc322ff57718d3e72 100644 --- a/packages/backend/migration/1689102832143-nsfw-cache.js +++ b/packages/backend/migration/1689102832143-nsfw-cache.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1690782653311-SensitiveChannel.js b/packages/backend/migration/1690782653311-SensitiveChannel.js index e76dda518073d5d4ffbc0d928c1d36ddfb9d9541..afec1a2153bc384014596665f110b6d77ce23cef 100644 --- a/packages/backend/migration/1690782653311-SensitiveChannel.js +++ b/packages/backend/migration/1690782653311-SensitiveChannel.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1690796169261-play-visibility.js b/packages/backend/migration/1690796169261-play-visibility.js index c57fa7a109868c75fcc728ff2d053b9a1a7340ad..5e5843bfee539bbe4e31b3d62f10b649c9202559 100644 --- a/packages/backend/migration/1690796169261-play-visibility.js +++ b/packages/backend/migration/1690796169261-play-visibility.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1691959191872-passkey-support.js b/packages/backend/migration/1691959191872-passkey-support.js index 55b571d60d8333ca8cdb6da386087aef76501bec..1da9bdb3632280a6a6fe90a292ed1df8d5ba786e 100644 --- a/packages/backend/migration/1691959191872-passkey-support.js +++ b/packages/backend/migration/1691959191872-passkey-support.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1694850832075-server-icons-and-manifest.js b/packages/backend/migration/1694850832075-server-icons-and-manifest.js index 1bd8979d9b908477bf5d63b47dfcaa125adfa7b9..235bf05744cb8044e02b45b4cc9d5723155079af 100644 --- a/packages/backend/migration/1694850832075-server-icons-and-manifest.js +++ b/packages/backend/migration/1694850832075-server-icons-and-manifest.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1694915420864-clipped-count.js b/packages/backend/migration/1694915420864-clipped-count.js index 1ad8e04ce083e14dde424b10f4397462f5b38a70..6d70aaecf14989cc99d3f44a4c47711e9b45d844 100644 --- a/packages/backend/migration/1694915420864-clipped-count.js +++ b/packages/backend/migration/1694915420864-clipped-count.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1695944637565-notificationRecieveConfig.js b/packages/backend/migration/1695944637565-notificationRecieveConfig.js index 42d3dce5d62bc95fc0b4da0465930063de481673..04a40993c05b3fb6ec9b3bfb932e3a15ef51205a 100644 --- a/packages/backend/migration/1695944637565-notificationRecieveConfig.js +++ b/packages/backend/migration/1695944637565-notificationRecieveConfig.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1696003580220-AddSomeUrls.js b/packages/backend/migration/1696003580220-AddSomeUrls.js index 683aa5eeed579521989e66bbb0536ae0e03d99fa..213e39e7afd2a51f349b829c1146444273fd4fc3 100644 --- a/packages/backend/migration/1696003580220-AddSomeUrls.js +++ b/packages/backend/migration/1696003580220-AddSomeUrls.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1696222183852-withReplies.js b/packages/backend/migration/1696222183852-withReplies.js index 9f65d5f6a1629a142daa40b9bee4b04ff6a093ff..84a5511d17b9dfe142b53b7d98534582dfd443a5 100644 --- a/packages/backend/migration/1696222183852-withReplies.js +++ b/packages/backend/migration/1696222183852-withReplies.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1696373953614-meta-cache-settings.js b/packages/backend/migration/1696373953614-meta-cache-settings.js index 8e24a1c5c650e866b3a732875ba2e046c28d2ad0..cef09b3eb7d5a628f6936019b05b50b13f9f6670 100644 --- a/packages/backend/migration/1696373953614-meta-cache-settings.js +++ b/packages/backend/migration/1696373953614-meta-cache-settings.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1696405744672-clean-up.js b/packages/backend/migration/1696405744672-clean-up.js index 5ec89b08f4fd5325700ebcee0cb93cf0ad6c3c06..4e1ee6cd6193492d20322d63962999b8ce8bbab0 100644 --- a/packages/backend/migration/1696405744672-clean-up.js +++ b/packages/backend/migration/1696405744672-clean-up.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1696569742153-clean-up.js b/packages/backend/migration/1696569742153-clean-up.js index de48fab5aa84dfb5bd7b93277e526bff65fb1b70..b7c981bab2c9d684714f43e12bdc183af5a5127e 100644 --- a/packages/backend/migration/1696569742153-clean-up.js +++ b/packages/backend/migration/1696569742153-clean-up.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1696581429196-clean-up.js b/packages/backend/migration/1696581429196-clean-up.js index da69b4e9de05f83eed88c77b6ee88160e4ef3ea0..b6723f3430e7ef9d8b73b63a7cd0623c4c30ca5e 100644 --- a/packages/backend/migration/1696581429196-clean-up.js +++ b/packages/backend/migration/1696581429196-clean-up.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1696743032098-AdsOnStream.js b/packages/backend/migration/1696743032098-AdsOnStream.js index c86ee84883283081926f7fba4c6a869d046845f1..43b9f83e66f729a89b43de134e77edb2c3fa8fb8 100644 --- a/packages/backend/migration/1696743032098-AdsOnStream.js +++ b/packages/backend/migration/1696743032098-AdsOnStream.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1696807733453-userListUserId.js b/packages/backend/migration/1696807733453-userListUserId.js index ab2ba07fb5f8011d7f62e04e498b35a10c4d066b..8f0ae2cd8765fc3b920e21d2f5fccec6435551a0 100644 --- a/packages/backend/migration/1696807733453-userListUserId.js +++ b/packages/backend/migration/1696807733453-userListUserId.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1696808725134-userListUserId-2.js b/packages/backend/migration/1696808725134-userListUserId-2.js index 5bcb5aedc2fbf4196934ec0b76d75384563b9ba4..cc504e761cd7942f0703972ed994edd3d2aa8d2b 100644 --- a/packages/backend/migration/1696808725134-userListUserId-2.js +++ b/packages/backend/migration/1696808725134-userListUserId-2.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1697247230117-InstanceSilence.js b/packages/backend/migration/1697247230117-InstanceSilence.js index 5fdbca3b2730cfe9c37533fafc7e10832d71b05f..309d817087c25db3a2bdf47053738b11a25e3f6e 100644 --- a/packages/backend/migration/1697247230117-InstanceSilence.js +++ b/packages/backend/migration/1697247230117-InstanceSilence.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1697420555911-deleteCreatedAt.js b/packages/backend/migration/1697420555911-deleteCreatedAt.js index 958d61a348cbfa14e183ae48f3eaf716df1577f4..407a5f449aa52c13ba32472c45e8a942b1839538 100644 --- a/packages/backend/migration/1697420555911-deleteCreatedAt.js +++ b/packages/backend/migration/1697420555911-deleteCreatedAt.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1697436246389-antenna-localOnly.js b/packages/backend/migration/1697436246389-antenna-localOnly.js index 02286732919a30d9df9a2b4a3fb2e2cf879961d0..d7c0ca6510fad3c5cb2ca17c77291b6baaa1d329 100644 --- a/packages/backend/migration/1697436246389-antenna-localOnly.js +++ b/packages/backend/migration/1697436246389-antenna-localOnly.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1697441463087-FollowRequestWithReplies.js b/packages/backend/migration/1697441463087-FollowRequestWithReplies.js index 214c6f6680cf4f8cbfa94d6a25028cdfff9ecf9d..58b61aff63f74a64008d11e1790eb3bf2952442b 100644 --- a/packages/backend/migration/1697441463087-FollowRequestWithReplies.js +++ b/packages/backend/migration/1697441463087-FollowRequestWithReplies.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1697673894459-note-reactionAndUserPairCache.js b/packages/backend/migration/1697673894459-note-reactionAndUserPairCache.js index fe0ea282d240b5a0c5d6568b8895164ee834063b..fab07fd3f423428971f6d9a39ce317a71cdcc26e 100644 --- a/packages/backend/migration/1697673894459-note-reactionAndUserPairCache.js +++ b/packages/backend/migration/1697673894459-note-reactionAndUserPairCache.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1697847397844-avatar-decoration.js b/packages/backend/migration/1697847397844-avatar-decoration.js index 1f221397466637a341fc1c703b3594b6d9a86a48..32ee47e96841572addf53b24cc4bd7580ebc0fd8 100644 --- a/packages/backend/migration/1697847397844-avatar-decoration.js +++ b/packages/backend/migration/1697847397844-avatar-decoration.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1697941908548-avatar-decoration2.js b/packages/backend/migration/1697941908548-avatar-decoration2.js index 9d15c1c3d0abedd2a8a1f09e8e99a2245b8b26e4..58344e2bb60832717b73b4ac7dbcca861e6a657e 100644 --- a/packages/backend/migration/1697941908548-avatar-decoration2.js +++ b/packages/backend/migration/1697941908548-avatar-decoration2.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1698041201306-enable-ftt.js b/packages/backend/migration/1698041201306-enable-ftt.js index 6769ed53b53e71e9360859bfa1332aacda64b862..c67dda6f5f19ac49bb2a3811f119cd037d995e71 100644 --- a/packages/backend/migration/1698041201306-enable-ftt.js +++ b/packages/backend/migration/1698041201306-enable-ftt.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1698840138000-add-allow-renote-to-external.js b/packages/backend/migration/1698840138000-add-allow-renote-to-external.js index 0edf298841e2b1ac5f63177fd6e6e39f3b886a43..8ce35b0f6951e93a22efc4f2a7b72246c5df704f 100644 --- a/packages/backend/migration/1698840138000-add-allow-renote-to-external.js +++ b/packages/backend/migration/1698840138000-add-allow-renote-to-external.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1699141698112-announcement-silence.js b/packages/backend/migration/1699141698112-announcement-silence.js index eef9b076fc13aee09ce21ac149c0f7e02204f885..f462d30b51111a0a06a2cad7cf31e00de52e97bd 100644 --- a/packages/backend/migration/1699141698112-announcement-silence.js +++ b/packages/backend/migration/1699141698112-announcement-silence.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1700096812223-enableFanoutTimelineDbFallback.js b/packages/backend/migration/1700096812223-enableFanoutTimelineDbFallback.js index 94fa588985d040aafc4f757ea3a5763b9dad46b8..2ab93624ce4a2dd862d95ce8872f2aa74d646976 100644 --- a/packages/backend/migration/1700096812223-enableFanoutTimelineDbFallback.js +++ b/packages/backend/migration/1700096812223-enableFanoutTimelineDbFallback.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1700303245007-supportVerifyMailApi.js b/packages/backend/migration/1700303245007-supportVerifyMailApi.js index 3ac59ec37adfc878ccc8d2bb7aefe8beff13b888..58ff7a69c485435a6b5dc25ab51137eea2a17179 100644 --- a/packages/backend/migration/1700303245007-supportVerifyMailApi.js +++ b/packages/backend/migration/1700303245007-supportVerifyMailApi.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1700902349231-add-bday-index.js b/packages/backend/migration/1700902349231-add-bday-index.js index 251526fc26f4f72ef7edfb680eecfdf7ff204bf1..c58165c70e9165ec2b7e4b86f36084209a120cbc 100644 --- a/packages/backend/migration/1700902349231-add-bday-index.js +++ b/packages/backend/migration/1700902349231-add-bday-index.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1702718871541-ffVisibility.js b/packages/backend/migration/1702718871541-ffVisibility.js index e9e820c89769cf7435230c56a3b059a72a18df59..164af00f25785df18c2cdeba43487ac04c7ae4ea 100644 --- a/packages/backend/migration/1702718871541-ffVisibility.js +++ b/packages/backend/migration/1702718871541-ffVisibility.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1703209889304-bannedEmailDomains.js b/packages/backend/migration/1703209889304-bannedEmailDomains.js index 5dc99c138fa6eb8ac7a62dcea150f0047025c14e..2fdd4e11834e5e237fe30f1ad67c69b6b38af78e 100644 --- a/packages/backend/migration/1703209889304-bannedEmailDomains.js +++ b/packages/backend/migration/1703209889304-bannedEmailDomains.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/migration/1703658526000-supportTrueMailApi.js b/packages/backend/migration/1703658526000-supportTrueMailApi.js new file mode 100644 index 0000000000000000000000000000000000000000..fb62653e40ba70691f47de4c49e3921784e29cbc --- /dev/null +++ b/packages/backend/migration/1703658526000-supportTrueMailApi.js @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class SupportTrueMailApi1703658526000 { + name = 'SupportTrueMailApi1703658526000' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "truemailInstance" character varying(1024)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "truemailAuthKey" character varying(1024)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "enableTruemailApi" boolean NOT NULL DEFAULT false`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableTruemailApi"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "truemailInstance"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "truemailAuthKey"`); + } +} diff --git a/packages/backend/migration/1704373210054-support-mcaptcha.js b/packages/backend/migration/1704373210054-support-mcaptcha.js new file mode 100644 index 0000000000000000000000000000000000000000..50b4801e14cbc5bf0e3b76e27a7535efc5f95823 --- /dev/null +++ b/packages/backend/migration/1704373210054-support-mcaptcha.js @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class SupportMcaptcha1704373210054 { + name = 'SupportMcaptcha1704373210054' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "enableMcaptcha" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`ALTER TABLE "meta" ADD "mcaptchaSitekey" character varying(1024)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "mcaptchaSecretKey" character varying(1024)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "mcaptchaInstanceUrl" character varying(1024)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "mcaptchaInstanceUrl"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "mcaptchaSecretKey"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "mcaptchaSitekey"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableMcaptcha"`); + } +} diff --git a/packages/backend/migration/1704744370000-add-donation-url.js b/packages/backend/migration/1704744370000-add-donation-url.js new file mode 100644 index 0000000000000000000000000000000000000000..c953b13cc985bbffa65482babf46b8ad17ac56ec --- /dev/null +++ b/packages/backend/migration/1704744370000-add-donation-url.js @@ -0,0 +1,10 @@ +export class AddDonationUrl1704744370000 { + name = 'AddDonationUrl1704744370000' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "donationUrl" character varying(1024)`); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "donationUrl"`); + } +} diff --git a/packages/backend/migration/1704959805077-bubble-game-record.js b/packages/backend/migration/1704959805077-bubble-game-record.js new file mode 100644 index 0000000000000000000000000000000000000000..6c4d7ab1a9ef153dd422139692748168238ec474 --- /dev/null +++ b/packages/backend/migration/1704959805077-bubble-game-record.js @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class BubbleGameRecord1704959805077 { + name = 'BubbleGameRecord1704959805077' + + async up(queryRunner) { + await queryRunner.query(`CREATE TABLE "bubble_game_record" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "seededAt" TIMESTAMP WITH TIME ZONE NOT NULL, "seed" character varying(1024) NOT NULL, "gameVersion" integer NOT NULL, "gameMode" character varying(128) NOT NULL, "score" integer NOT NULL, "logs" jsonb NOT NULL DEFAULT '[]', "isVerified" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_a75395fe404b392e2893b50d7ea" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE INDEX "IDX_75276757070d21fdfaf4c05290" ON "bubble_game_record" ("userId") `); + await queryRunner.query(`CREATE INDEX "IDX_4ae7053179014915d1432d3f40" ON "bubble_game_record" ("seededAt") `); + await queryRunner.query(`CREATE INDEX "IDX_26d4ee490b5a487142d35466ee" ON "bubble_game_record" ("score") `); + await queryRunner.query(`ALTER TABLE "bubble_game_record" ADD CONSTRAINT "FK_75276757070d21fdfaf4c052909" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "bubble_game_record" DROP CONSTRAINT "FK_75276757070d21fdfaf4c052909"`); + await queryRunner.query(`DROP INDEX "public"."IDX_26d4ee490b5a487142d35466ee"`); + await queryRunner.query(`DROP INDEX "public"."IDX_4ae7053179014915d1432d3f40"`); + await queryRunner.query(`DROP INDEX "public"."IDX_75276757070d21fdfaf4c05290"`); + await queryRunner.query(`DROP TABLE "bubble_game_record"`); + } +} diff --git a/packages/backend/migration/1705222772858-optimize-note-index-for-array-column.js b/packages/backend/migration/1705222772858-optimize-note-index-for-array-column.js new file mode 100644 index 0000000000000000000000000000000000000000..fe0a5a2bcffe3cb8024ad454ed9eaf9bf41593f3 --- /dev/null +++ b/packages/backend/migration/1705222772858-optimize-note-index-for-array-column.js @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class OptimizeNoteIndexForArrayColumns1705222772858 { + name = 'OptimizeNoteIndexForArrayColumns1705222772858' + + async up(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_796a8c03959361f97dc2be1d5c"`); + await queryRunner.query(`DROP INDEX "public"."IDX_54ebcb6d27222913b908d56fd8"`); + await queryRunner.query(`DROP INDEX "public"."IDX_88937d94d7443d9a99a76fa5c0"`); + await queryRunner.query(`DROP INDEX "public"."IDX_51c063b6a133a9cb87145450f5"`); + await queryRunner.query(`CREATE INDEX "IDX_NOTE_FILE_IDS" ON "note" using gin ("fileIds")`) + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_NOTE_FILE_IDS"`) + await queryRunner.query(`CREATE INDEX "IDX_51c063b6a133a9cb87145450f5" ON "note" ("fileIds") `); + await queryRunner.query(`CREATE INDEX "IDX_88937d94d7443d9a99a76fa5c0" ON "note" ("tags") `); + await queryRunner.query(`CREATE INDEX "IDX_54ebcb6d27222913b908d56fd8" ON "note" ("mentions") `); + await queryRunner.query(`CREATE INDEX "IDX_796a8c03959361f97dc2be1d5c" ON "note" ("visibleUserIds") `); + } +} diff --git a/packages/backend/migration/1705475608437-reversi.js b/packages/backend/migration/1705475608437-reversi.js new file mode 100644 index 0000000000000000000000000000000000000000..9921728457d5d657ecbd8a7595f4c328fe4e8172 --- /dev/null +++ b/packages/backend/migration/1705475608437-reversi.js @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class Reversi1705475608437 { + name = 'Reversi1705475608437' + + async up(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_b46ec40746efceac604142be1c"`); + await queryRunner.query(`DROP INDEX "public"."IDX_b604d92d6c7aec38627f6eaf16"`); + await queryRunner.query(`ALTER TABLE "reversi_game" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "reversi_matching" DROP COLUMN "createdAt"`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_matching" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "reversi_game" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`CREATE INDEX "IDX_b604d92d6c7aec38627f6eaf16" ON "reversi_matching" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_b46ec40746efceac604142be1c" ON "reversi_game" ("createdAt") `); + } +} diff --git a/packages/backend/migration/1705654039457-reversi-2.js b/packages/backend/migration/1705654039457-reversi-2.js new file mode 100644 index 0000000000000000000000000000000000000000..6685dca73bdd6a045d15148395103e011b06e7b7 --- /dev/null +++ b/packages/backend/migration/1705654039457-reversi-2.js @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class Reversi21705654039457 { + name = 'Reversi21705654039457' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_game" RENAME COLUMN "user1Accepted" TO "user1Ready"`); + await queryRunner.query(`ALTER TABLE "reversi_game" RENAME COLUMN "user2Accepted" TO "user2Ready"`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_game" RENAME COLUMN "user1Ready" TO "user1Accepted"`); + await queryRunner.query(`ALTER TABLE "reversi_game" RENAME COLUMN "user2Ready" TO "user2Accepted"`); + } +} diff --git a/packages/backend/migration/1705793785675-reversi-3.js b/packages/backend/migration/1705793785675-reversi-3.js new file mode 100644 index 0000000000000000000000000000000000000000..94b1e4fac99fc27b4fd95e7f041f2b206c0c1e42 --- /dev/null +++ b/packages/backend/migration/1705793785675-reversi-3.js @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class Reversi31705793785675 { + name = 'Reversi31705793785675' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_game" RENAME COLUMN "surrendered" TO "surrenderedUserId"`); + await queryRunner.query(`ALTER TABLE "reversi_game" ADD "timeoutUserId" character varying(32)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_game" DROP COLUMN "timeoutUserId"`); + await queryRunner.query(`ALTER TABLE "reversi_game" RENAME COLUMN "surrenderedUserId" TO "surrendered"`); + } +} diff --git a/packages/backend/migration/1705794768153-reversi-4.js b/packages/backend/migration/1705794768153-reversi-4.js new file mode 100644 index 0000000000000000000000000000000000000000..95119cabba3b3a0a246bfa2acddc20d52ff432d7 --- /dev/null +++ b/packages/backend/migration/1705794768153-reversi-4.js @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class Reversi41705794768153 { + name = 'Reversi41705794768153' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_game" ADD "endedAt" TIMESTAMP WITH TIME ZONE`); + await queryRunner.query(`COMMENT ON COLUMN "reversi_game"."endedAt" IS 'The ended date of the ReversiGame.'`); + } + + async down(queryRunner) { + await queryRunner.query(`COMMENT ON COLUMN "reversi_game"."endedAt" IS 'The ended date of the ReversiGame.'`); + await queryRunner.query(`ALTER TABLE "reversi_game" DROP COLUMN "endedAt"`); + } +} diff --git a/packages/backend/migration/1705798904141-reversi-5.js b/packages/backend/migration/1705798904141-reversi-5.js new file mode 100644 index 0000000000000000000000000000000000000000..f1a1a42d463d58ccdad867e000a3ba5522b215cb --- /dev/null +++ b/packages/backend/migration/1705798904141-reversi-5.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class Reversi51705798904141 { + name = 'Reversi51705798904141' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_game" ADD "timeLimitForEachTurn" smallint NOT NULL DEFAULT '90'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_game" DROP COLUMN "timeLimitForEachTurn"`); + } +} diff --git a/packages/backend/migration/1706081514499-reversi-6.js b/packages/backend/migration/1706081514499-reversi-6.js new file mode 100644 index 0000000000000000000000000000000000000000..0d9e5cbbf2a6d8d23bf3e6cea390674019bc792a --- /dev/null +++ b/packages/backend/migration/1706081514499-reversi-6.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class Reversi61706081514499 { + name = 'Reversi61706081514499' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_game" ADD "noIrregularRules" boolean NOT NULL DEFAULT false`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_game" DROP COLUMN "noIrregularRules"`); + } +} diff --git a/packages/backend/migration/1706232992000-deeplx.js b/packages/backend/migration/1706232992000-deeplx.js new file mode 100644 index 0000000000000000000000000000000000000000..5c763dbf8b20477e77096d07fbe563e7b5c76c57 --- /dev/null +++ b/packages/backend/migration/1706232992000-deeplx.js @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class Deeplx1706232992000 { + name = 'Deeplx1706232992000'; + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "deeplFreeMode" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`ALTER TABLE "meta" ADD "deeplFreeInstance" character varying(1024)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "deeplFreeMode"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "deeplFreeInstance"`); + } +} diff --git a/packages/backend/migration/1706791962000-fix-meta-disableRegistration.js b/packages/backend/migration/1706791962000-fix-meta-disableRegistration.js new file mode 100644 index 0000000000000000000000000000000000000000..1c45f3756d125c7622f8c4cb6f7c0ccf07734eeb --- /dev/null +++ b/packages/backend/migration/1706791962000-fix-meta-disableRegistration.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class FixMetaDisableRegistration1706791962000 { + name = 'FixMetaDisableRegistration1706791962000' + + async up(queryRunner) { + await queryRunner.query(`alter table meta alter column "disableRegistration" set default true;`); + } + + async down(queryRunner) { + await queryRunner.query(`alter table meta alter column "disableRegistration" set default false;`); + } +} diff --git a/packages/backend/migration/1707429690000-prohibited-words.js b/packages/backend/migration/1707429690000-prohibited-words.js new file mode 100644 index 0000000000000000000000000000000000000000..44e96cb160c499a5d2ffe7d227f98fa452f12d8a --- /dev/null +++ b/packages/backend/migration/1707429690000-prohibited-words.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class prohibitedWords1707429690000 { + name = 'prohibitedWords1707429690000' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "prohibitedWords" character varying(1024) array NOT NULL DEFAULT '{}'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "prohibitedWords"`); + } +} diff --git a/packages/backend/migration/1707808106310-MakeRepositoryUrlNullable.js b/packages/backend/migration/1707808106310-MakeRepositoryUrlNullable.js new file mode 100644 index 0000000000000000000000000000000000000000..335b14976c34e5fed0a75fbd0d3247a6d8c7d835 --- /dev/null +++ b/packages/backend/migration/1707808106310-MakeRepositoryUrlNullable.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class MakeRepositoryUrlNullable1707808106310 { + name = 'MakeRepositoryUrlNullable1707808106310' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "repositoryUrl" DROP NOT NULL`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "repositoryUrl" SET NOT NULL`); + } +} diff --git a/packages/backend/migration/1708266695091-repositoryUrl-from-syuilo-to-misskey-dev.js b/packages/backend/migration/1708266695091-repositoryUrl-from-syuilo-to-misskey-dev.js new file mode 100644 index 0000000000000000000000000000000000000000..e4dbaa16d0f198b425db95a4b30abea1f20b74b5 --- /dev/null +++ b/packages/backend/migration/1708266695091-repositoryUrl-from-syuilo-to-misskey-dev.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class RepositoryUrlFromSyuiloToMisskeyDev1708266695091 { + name = 'RepositoryUrlFromSyuiloToMisskeyDev1708266695091' + + async up(queryRunner) { + await queryRunner.query(`UPDATE "meta" SET "repositoryUrl" = 'https://github.com/misskey-dev/misskey' WHERE "repositoryUrl" = 'https://github.com/syuilo/misskey'`); + } + + async down(queryRunner) { + // no valid down migration + } +} diff --git a/packages/backend/migration/1708342829000-SharkeyRepositoryUrl.js b/packages/backend/migration/1708342829000-SharkeyRepositoryUrl.js new file mode 100644 index 0000000000000000000000000000000000000000..0853ed4edd88df5d6f608077aefe11702f69d574 --- /dev/null +++ b/packages/backend/migration/1708342829000-SharkeyRepositoryUrl.js @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: dakkar and other Sharkey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class SharkeyRepositoryUrl1708342829000 { + name = 'SharkeyRepositoryUrl1708342829000' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "repositoryUrl" SET DEFAULT 'https://activitypub.software/TransFem-org/Sharkey/'`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "feedbackUrl" SET DEFAULT 'https://activitypub.software/TransFem-org/Sharkey/-/issues/new'`); + await queryRunner.query(`UPDATE "meta" SET "repositoryUrl"=DEFAULT WHERE "repositoryUrl" IN ('https://git.joinsharkey.org/Sharkey/Sharkey','https://github.com/transfem-org/sharkey','https://github.com/misskey-dev/misskey')`); + await queryRunner.query(`UPDATE "meta" SET "feedbackUrl"=DEFAULT WHERE "feedbackUrl" IN ('https://git.joinsharkey.org/Sharkey/Sharkey/issues/new/choose','https://github.com/transfem-org/sharkey/issues/new','https://github.com/misskey-dev/misskey/issues/new')`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "repositoryUrl" SET DEFAULT 'https://git.joinsharkey.org/Sharkey/Sharkey'`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "feedbackUrl" SET DEFAULT 'https://git.joinsharkey.org/Sharkey/Sharkey/issues/new/choose'`); + await queryRunner.query(`UPDATE "meta" SET "repositoryUrl"=DEFAULT WHERE "repositoryUrl" IN ('https://git.joinsharkey.org/Sharkey/Sharkey','https://github.com/transfem-org/sharkey','https://github.com/misskey-dev/misskey')`); + await queryRunner.query(`UPDATE "meta" SET "feedbackUrl"=DEFAULT WHERE "feedbackUrl" IN ('https://git.joinsharkey.org/Sharkey/Sharkey/issues/new/choose','https://github.com/transfem-org/sharkey/issues/new','https://github.com/misskey-dev/misskey/issues/new')`); + } +} diff --git a/packages/backend/migration/1708399372194-per-instance-mod-note.js b/packages/backend/migration/1708399372194-per-instance-mod-note.js new file mode 100644 index 0000000000000000000000000000000000000000..339a4d7af9663f66a81d45acce46847aee281b7e --- /dev/null +++ b/packages/backend/migration/1708399372194-per-instance-mod-note.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class PerInstanceModNote1708399372194 { + name = 'PerInstanceModNote1708399372194' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "instance" ADD "moderationNote" character varying(16384) NOT NULL DEFAULT ''`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "moderationNote"`); + } +} diff --git a/packages/backend/package.json b/packages/backend/package.json index 2aa10b1c9619bf4d7e4fa65d7e04bd6ca49fd0f3..2ddb067afe76cee171396daf2591fe796afb906a 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -8,11 +8,12 @@ }, "scripts": { "start": "node ./built/boot/entry.js", - "start:test": "NODE_ENV=test node ./built/boot/entry.js", + "start:test": "cross-env NODE_ENV=test node ./built/boot/entry.js", "migrate": "pnpm typeorm migration:run -d ormconfig.js", "revert": "pnpm typeorm migration:revert -d ormconfig.js", "check:connect": "node ./check_connect.js", "build": "swc src -d built -D", + "build:test": "swc test-server -d built-test -D --config-file test-server/.swcrc", "watch:swc": "swc src -d built -D -w", "build:tsc": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json", "watch": "node watch.mjs", @@ -21,12 +22,16 @@ "typecheck": "pnpm --filter megalodon build && tsc --noEmit", "eslint": "eslint --quiet \"src/**/*.ts\"", "lint": "pnpm typecheck && pnpm eslint", - "jest": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit", - "jest-and-coverage": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit", + "jest": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --config jest.config.unit.cjs", + "jest:e2e": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --config jest.config.e2e.cjs", + "jest-and-coverage": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --config jest.config.unit.cjs", + "jest-and-coverage:e2e": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --config jest.config.e2e.cjs", "jest-clear": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --clearCache", "test": "pnpm jest", + "test:e2e": "pnpm build && pnpm build:test && pnpm jest:e2e", "test-and-coverage": "pnpm jest-and-coverage", - "generate-api-json": "node ./generate_api_json.js" + "test-and-coverage:e2e": "pnpm build && pnpm build:test && pnpm jest-and-coverage:e2e", + "generate-api-json": "pnpm build && node ./generate_api_json.js" }, "optionalDependencies": { "@swc/core-android-arm64": "1.3.11", @@ -60,87 +65,91 @@ "dependencies": { "@aws-sdk/client-s3": "3.412.0", "@aws-sdk/lib-storage": "3.412.0", - "@bull-board/api": "5.10.2", - "@bull-board/fastify": "5.10.2", - "@bull-board/ui": "5.10.2", + "@bull-board/api": "5.14.2", + "@bull-board/fastify": "5.14.2", + "@bull-board/ui": "5.14.2", "@discordapp/twemoji": "15.0.2", "@fastify/accepts": "4.3.0", - "@fastify/cookie": "9.2.0", + "@fastify/cookie": "9.3.1", "@fastify/cors": "8.5.0", "@fastify/express": "2.3.0", "@fastify/http-proxy": "9.3.0", - "@fastify/multipart": "8.0.0", + "@fastify/multipart": "8.1.0", "@fastify/static": "6.12.0", "@fastify/view": "8.2.0", - "@nestjs/common": "10.2.10", - "@nestjs/core": "10.2.10", - "@nestjs/testing": "10.2.10", + "@misskey-dev/sharp-read-bmp": "1.2.0", + "@misskey-dev/summaly": "5.0.3", + "@nestjs/common": "10.3.3", + "@nestjs/core": "10.3.3", + "@nestjs/testing": "10.3.3", "@peertube/http-signature": "1.7.0", - "@sharkey/sfm-js": "0.24.3", - "@simplewebauthn/server": "8.3.5", + "@simplewebauthn/server": "9.0.3", "@sinonjs/fake-timers": "11.2.2", "@smithy/node-http-handler": "2.1.10", "@swc/cli": "0.1.63", - "@swc/core": "1.3.100", + "@swc/core": "1.3.107", + "@transfem-org/sfm-js": "0.24.4", "@twemoji/parser": "15.0.0", "accepts": "1.3.8", "ajv": "8.12.0", "archiver": "6.0.1", - "argon2": "^0.31.1", - "async-mutex": "0.4.0", + "argon2": "^0.40.1", + "async-mutex": "0.4.1", "bcryptjs": "2.4.3", "blurhash": "2.0.5", "body-parser": "1.20.2", - "bullmq": "4.15.4", + "bullmq": "5.4.0", "cacheable-lookup": "7.0.0", - "cbor": "9.0.1", + "cbor": "9.0.2", "chalk": "5.3.0", "chalk-template": "1.1.0", - "chokidar": "3.5.3", + "chokidar": "3.6.0", "cli-highlight": "2.1.11", "color-convert": "2.0.1", "content-disposition": "0.5.4", "date-fns": "2.30.0", "deep-email-validator": "0.1.21", - "fastify": "4.24.3", + "fastify": "4.25.2", "fastify-multer": "^2.0.3", "fastify-raw-body": "4.3.0", "feed": "4.2.2", - "file-type": "18.7.0", + "file-type": "19.0.0", "fluent-ffmpeg": "2.1.2", "form-data": "4.0.0", "glob": "10.3.10", - "got": "14.0.0", + "got": "14.2.0", "happy-dom": "10.0.3", "hpagent": "1.2.0", - "http-link-header": "1.1.1", + "htmlescape": "1.1.1", + "http-link-header": "1.1.2", "ioredis": "5.3.2", "ip-cidr": "3.1.0", "ipaddr.js": "2.1.0", "is-svg": "5.0.0", "js-yaml": "4.1.0", - "jsdom": "23.0.1", + "jsdom": "23.2.0", "json5": "2.2.3", "jsonld": "8.3.2", - "jsrsasign": "10.9.0", - "meilisearch": "0.36.0", + "jsrsasign": "11.1.0", "megalodon": "workspace:*", + "meilisearch": "0.37.0", "microformats-parser": "2.0.2", "mime-types": "2.1.35", "misskey-js": "workspace:*", + "misskey-reversi": "workspace:*", "ms": "3.0.0-canary.1", - "nanoid": "5.0.4", + "nanoid": "5.0.6", "nested-property": "4.0.0", "node-fetch": "3.3.2", - "nodemailer": "6.9.7", + "nodemailer": "6.9.10", "oauth": "0.10.0", "oauth2orize": "1.12.0", "oauth2orize-pkce": "0.1.2", "os-utils": "0.0.14", - "otpauth": "9.2.1", + "otpauth": "9.2.2", "parse5": "7.1.2", "pg": "8.11.3", - "pkce-challenge": "4.0.1", + "pkce-challenge": "4.1.0", "probe-image-size": "7.2.3", "promise-limit": "2.7.0", "pug": "3.0.2", @@ -151,44 +160,44 @@ "ratelimiter": "3.4.1", "re2": "1.20.9", "redis-lock": "0.1.4", - "reflect-metadata": "0.1.14", + "reflect-metadata": "0.2.1", "rename": "1.0.4", "rss-parser": "3.13.0", "rxjs": "7.8.1", - "sanitize-html": "2.11.0", + "sanitize-html": "2.12.1", "secure-json-parse": "2.7.0", - "sharp": "0.32.6", - "sharp-read-bmp": "github:misskey-dev/sharp-read-bmp", + "sharp": "0.33.2", "slacc": "0.0.10", "strict-event-emitter-types": "2.0.0", "stringz": "2.1.0", - "summaly": "github:misskey-dev/summaly", - "systeminformation": "5.21.20", + "systeminformation": "5.22.0", "tinycolor2": "1.6.0", - "tmp": "0.2.1", + "tmp": "0.2.2", "tsc-alias": "1.8.8", "tsconfig-paths": "4.2.0", - "typeorm": "0.3.17", + "typeorm": "0.3.20", "typescript": "5.3.3", "ulid": "2.3.0", "uuid": "^9.0.1", "vary": "1.1.2", - "web-push": "3.6.6", - "ws": "8.15.1", + "web-push": "3.6.7", + "ws": "8.16.0", "xev": "3.0.2" }, "devDependencies": { "@jest/globals": "29.7.0", - "@simplewebauthn/typescript-types": "8.3.4", - "@swc/jest": "0.2.29", + "@misskey-dev/eslint-plugin": "1.0.0", + "@nestjs/platform-express": "10.3.3", + "@simplewebauthn/types": "9.0.1", + "@swc/jest": "0.2.31", "@types/accepts": "1.3.7", "@types/archiver": "6.0.2", "@types/bcryptjs": "2.4.6", "@types/body-parser": "1.19.5", - "@types/cbor": "6.0.0", "@types/color-convert": "2.0.3", "@types/content-disposition": "0.5.8", "@types/fluent-ffmpeg": "2.1.24", + "@types/htmlescape": "^1.1.3", "@types/http-link-header": "1.0.5", "@types/jest": "29.5.11", "@types/js-yaml": "4.0.9", @@ -197,22 +206,21 @@ "@types/jsrsasign": "10.5.12", "@types/mime-types": "2.1.4", "@types/ms": "0.7.34", - "@types/node": "20.10.5", + "@types/node": "20.11.22", "@types/node-fetch": "3.0.3", "@types/nodemailer": "6.4.14", "@types/oauth": "0.9.4", "@types/oauth2orize": "1.11.3", "@types/oauth2orize-pkce": "0.1.2", - "@types/pg": "8.10.9", + "@types/pg": "8.11.2", "@types/pug": "2.0.10", - "@types/punycode": "2.1.3", + "@types/punycode": "2.1.4", "@types/qrcode": "1.5.5", "@types/random-seed": "0.3.5", "@types/ratelimiter": "3.4.6", "@types/rename": "1.0.7", - "@types/sanitize-html": "2.9.5", - "@types/semver": "7.5.6", - "@types/sharp": "0.32.0", + "@types/sanitize-html": "2.11.0", + "@types/semver": "7.5.8", "@types/simple-oauth2": "5.0.7", "@types/sinonjs__fake-timers": "8.1.5", "@types/tinycolor2": "1.4.6", @@ -221,16 +229,18 @@ "@types/vary": "1.1.3", "@types/web-push": "3.6.3", "@types/ws": "8.5.10", - "@typescript-eslint/eslint-plugin": "6.14.0", - "@typescript-eslint/parser": "6.14.0", - "aws-sdk-client-mock": "3.0.0", + "@typescript-eslint/eslint-plugin": "7.1.0", + "@typescript-eslint/parser": "7.1.0", + "aws-sdk-client-mock": "3.0.1", "cross-env": "7.0.3", - "eslint": "8.56.0", + "eslint": "8.57.0", "eslint-plugin-import": "2.29.1", "execa": "8.0.1", + "fkill": "^9.0.0", "jest": "29.7.0", "jest-mock": "29.7.0", - "nodemon": "3.0.2", + "nodemon": "3.1.0", + "pid-port": "1.0.0", "simple-oauth2": "5.0.0" } } diff --git a/packages/backend/src/@types/hcaptcha.d.ts b/packages/backend/src/@types/hcaptcha.d.ts index 43e67dd340c8e0591ce5a08ed335ba65f8e5c6d4..e11dda46627addf4a3dcc80823a036ba603dcb65 100644 --- a/packages/backend/src/@types/hcaptcha.d.ts +++ b/packages/backend/src/@types/hcaptcha.d.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/@types/http-signature.d.ts b/packages/backend/src/@types/http-signature.d.ts index 1f3b48aa546c7c97385089b9768e17b8b657e67f..75b62e55f0fdf7847ca9887c34dd3977bbbdcaff 100644 --- a/packages/backend/src/@types/http-signature.d.ts +++ b/packages/backend/src/@types/http-signature.d.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/@types/os-utils.d.ts b/packages/backend/src/@types/os-utils.d.ts index 8c44232c149245b28eec3d0a8f01679edcc2ca0b..8943edddd1a0e50504e9f932a4770303421dd66e 100644 --- a/packages/backend/src/@types/os-utils.d.ts +++ b/packages/backend/src/@types/os-utils.d.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/@types/package.json.d.ts b/packages/backend/src/@types/package.json.d.ts index 197b4b6bf0aa09e1f4dbd0cb3098416a0cf0ac4f..52a2b356db3dc0719563405338a8082a1419c6a7 100644 --- a/packages/backend/src/@types/package.json.d.ts +++ b/packages/backend/src/@types/package.json.d.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/@types/probe-image-size.d.ts b/packages/backend/src/@types/probe-image-size.d.ts index 4d312cba34d20c6aabadd202d3e21a1cd2ca6d83..538836475caeac81d783731b270276cc788a3ab4 100644 --- a/packages/backend/src/@types/probe-image-size.d.ts +++ b/packages/backend/src/@types/probe-image-size.d.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/@types/redis-lock.d.ts b/packages/backend/src/@types/redis-lock.d.ts index c607d600d8374ed379fbde849d5c5bd0933e2a6a..b037cde5ee6db9086d44a2980f37a8b3930a7de0 100644 --- a/packages/backend/src/@types/redis-lock.d.ts +++ b/packages/backend/src/@types/redis-lock.d.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/GlobalModule.ts b/packages/backend/src/GlobalModule.ts index 3e9d19f82598d71c9e161aff176d76e062089379..09971e8ca022c2a20fa4fd08ed0f3d730cea753e 100644 --- a/packages/backend/src/GlobalModule.ts +++ b/packages/backend/src/GlobalModule.ts @@ -1,9 +1,8 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import { setTimeout } from 'node:timers/promises'; import { Global, Inject, Module } from '@nestjs/common'; import * as Redis from 'ioredis'; import { DataSource } from 'typeorm'; @@ -12,6 +11,7 @@ import { DI } from './di-symbols.js'; import { Config, loadConfig } from './config.js'; import { createPostgresDataSource } from './postgres.js'; import { RepositoryModule } from './models/RepositoryModule.js'; +import { allSettled } from './misc/promise-tracker.js'; import type { Provider, OnApplicationShutdown } from '@nestjs/common'; const $config: Provider = { @@ -33,7 +33,7 @@ const $meilisearch: Provider = { useFactory: (config: Config) => { if (config.meilisearch) { return new MeiliSearch({ - host: `${config.meilisearch.ssl ? 'https' : 'http' }://${config.meilisearch.host}:${config.meilisearch.port}`, + host: `${config.meilisearch.ssl ? 'https' : 'http'}://${config.meilisearch.host}:${config.meilisearch.port}`, apiKey: config.meilisearch.apiKey, }); } else { @@ -91,17 +91,12 @@ export class GlobalModule implements OnApplicationShutdown { @Inject(DI.redisForPub) private redisForPub: Redis.Redis, @Inject(DI.redisForSub) private redisForSub: Redis.Redis, @Inject(DI.redisForTimelines) private redisForTimelines: Redis.Redis, - ) {} + ) { } public async dispose(): Promise<void> { - if (process.env.NODE_ENV === 'test') { - // XXX: - // Shutting down the existing connections causes errors on Jest as - // Misskey has asynchronous postgres/redis connections that are not - // awaited. - // Let's wait for some random time for them to finish. - await setTimeout(5000); - } + // Wait for all potential DB queries + await allSettled(); + // And then disconnect from DB await Promise.all([ this.db.destroy(), this.redisClient.disconnect(), diff --git a/packages/backend/src/MainModule.ts b/packages/backend/src/MainModule.ts index 90aba0cc91665f1984575039cba8fc6b6a11428e..f86a0be93c6a041774af621ed2b60b294300ad08 100644 --- a/packages/backend/src/MainModule.ts +++ b/packages/backend/src/MainModule.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/NestLogger.ts b/packages/backend/src/NestLogger.ts index e18e9e88a7e1228b7e979e887f102c2c4bac934b..80f1f7a024d6a937f623b2cd58e52fb9052b6691 100644 --- a/packages/backend/src/NestLogger.ts +++ b/packages/backend/src/NestLogger.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/boot/common.ts b/packages/backend/src/boot/common.ts index df10ab1e3db416d334e803fab1bdcbb6a771c262..268c07582d9c8fc5b116467296fa5015b8ad1094 100644 --- a/packages/backend/src/boot/common.ts +++ b/packages/backend/src/boot/common.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/boot/entry.ts b/packages/backend/src/boot/entry.ts index bd0ed29fc2d209662f56b71f85478f9bf28aa898..ae74a43c846318643b1fdd4d80125264079c7960 100644 --- a/packages/backend/src/boot/entry.ts +++ b/packages/backend/src/boot/entry.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index a3f869464072a7bea8d589f0cf0b487b1e49a3c7..aafd43beead8427bb411d87261ecb9e6afdc10da 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/boot/worker.ts b/packages/backend/src/boot/worker.ts index 0399c9fe5cb04fe2a4ba4fe0916179b53db6555a..d4a7cd56e5cedfcdfc44919c5ddf6cd0d466c927 100644 --- a/packages/backend/src/boot/worker.ts +++ b/packages/backend/src/boot/worker.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index a550fdc3647c10621652220608448e0b8905afe4..c99bc7ae03541dc0a6643b8e896784231b9e2172 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -58,6 +58,8 @@ type Source = { scope?: 'local' | 'global' | string[]; }; + publishTarballInsteadOfProvideRepositoryUrl?: boolean; + proxy?: string; proxySmtp?: string; proxyBypassHosts?: string[]; @@ -76,10 +78,10 @@ type Source = { deliverJobConcurrency?: number; inboxJobConcurrency?: number; - relashionshipJobConcurrency?: number; + relationshipJobConcurrency?: number; deliverJobPerSec?: number; inboxJobPerSec?: number; - relashionshipJobPerSec?: number; + relationshipJobPerSec?: number; deliverJobMaxAttempts?: number; inboxJobMaxAttempts?: number; @@ -141,18 +143,19 @@ export type Config = { outgoingAddressFamily: 'ipv4' | 'ipv6' | 'dual' | undefined; deliverJobConcurrency: number | undefined; inboxJobConcurrency: number | undefined; - relashionshipJobConcurrency: number | undefined; + relationshipJobConcurrency: number | undefined; deliverJobPerSec: number | undefined; inboxJobPerSec: number | undefined; - relashionshipJobPerSec: number | undefined; + relationshipJobPerSec: number | undefined; deliverJobMaxAttempts: number | undefined; inboxJobMaxAttempts: number | undefined; proxyRemoteFiles: boolean | undefined; customMOTD: string[] | undefined; - signToActivityPubGet: boolean | undefined; + signToActivityPubGet: boolean; checkActivityPubGetSignature: boolean | undefined; version: string; + publishTarballInsteadOfProvideRepositoryUrl: boolean; host: string; hostname: string; scheme: string; @@ -224,6 +227,7 @@ export function loadConfig(): Config { return { version, + publishTarballInsteadOfProvideRepositoryUrl: !!config.publishTarballInsteadOfProvideRepositoryUrl, url: url.origin, port: config.port ?? parseInt(process.env.PORT ?? '', 10), socket: config.socket, @@ -257,15 +261,15 @@ export function loadConfig(): Config { outgoingAddressFamily: config.outgoingAddressFamily, deliverJobConcurrency: config.deliverJobConcurrency, inboxJobConcurrency: config.inboxJobConcurrency, - relashionshipJobConcurrency: config.relashionshipJobConcurrency, + relationshipJobConcurrency: config.relationshipJobConcurrency, deliverJobPerSec: config.deliverJobPerSec, inboxJobPerSec: config.inboxJobPerSec, - relashionshipJobPerSec: config.relashionshipJobPerSec, + relationshipJobPerSec: config.relationshipJobPerSec, deliverJobMaxAttempts: config.deliverJobMaxAttempts, inboxJobMaxAttempts: config.inboxJobMaxAttempts, proxyRemoteFiles: config.proxyRemoteFiles, customMOTD: config.customMOTD, - signToActivityPubGet: config.signToActivityPubGet, + signToActivityPubGet: config.signToActivityPubGet ?? true, checkActivityPubGetSignature: config.checkActivityPubGetSignature, mediaProxy: externalMediaProxy ?? internalMediaProxy, externalMediaProxyEnabled: externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy, diff --git a/packages/backend/src/const.ts b/packages/backend/src/const.ts index 6abc11ac936f033bf94b4293f9e5f0181fff9cd1..02c27779cab436a594488a631d193970b30b46e3 100644 --- a/packages/backend/src/const.ts +++ b/packages/backend/src/const.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts index 350aa6ba2495b0611910620703af097360b9061d..5bd885df406b0feab13b84879ab3b78831337fbd 100644 --- a/packages/backend/src/core/AccountMoveService.ts +++ b/packages/backend/src/core/AccountMoveService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -20,7 +20,6 @@ import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { CacheService } from '@/core/CacheService.js'; import { ProxyAccountService } from '@/core/ProxyAccountService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { MetaService } from '@/core/MetaService.js'; @@ -60,7 +59,6 @@ export class AccountMoveService { private instanceChart: InstanceChart, private metaService: MetaService, private relayService: RelayService, - private cacheService: CacheService, private queueService: QueueService, ) { } @@ -84,7 +82,7 @@ export class AccountMoveService { Object.assign(src, update); // Update cache - this.cacheService.uriPersonCache.set(srcUri, src); + this.globalEventService.publishInternalEvent('localUserUpdated', src); const srcPerson = await this.apRendererService.renderPerson(src); const updateAct = this.apRendererService.addContext(this.apRendererService.renderUpdate(srcPerson, src)); @@ -96,7 +94,7 @@ export class AccountMoveService { await this.apDeliverManagerService.deliverToFollowers(src, moveAct); // Publish meUpdated event - const iObj = await this.userEntityService.pack<true, true>(src.id, src, { detail: true, includeSecrets: true }); + const iObj = await this.userEntityService.pack(src.id, src, { schema: 'MeDetailed', includeSecrets: true }); this.globalEventService.publishMainStream(src.id, 'meUpdated', iObj); // Unfollow after 24 hours diff --git a/packages/backend/src/core/AccountUpdateService.ts b/packages/backend/src/core/AccountUpdateService.ts index 664700ea6b068e5d3eb4977264804963aa708abe..69a57b485468ef4eca2debf7bfb6abbd334407e6 100644 --- a/packages/backend/src/core/AccountUpdateService.ts +++ b/packages/backend/src/core/AccountUpdateService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/AchievementService.ts b/packages/backend/src/core/AchievementService.ts index 88fc033859430b0fe50f5df2c93b7302bdae9856..4fc1193f32aa1a390d971b75076aaacfff891e85 100644 --- a/packages/backend/src/core/AchievementService.ts +++ b/packages/backend/src/core/AchievementService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -87,6 +87,8 @@ export const ACHIEVEMENT_TYPES = [ 'brainDiver', 'smashTestNotificationButton', 'tutorialCompleted', + 'bubbleGameExplodingHead', + 'bubbleGameDoubleExplodingHead', ] as const; @Injectable() diff --git a/packages/backend/src/core/AnnouncementService.ts b/packages/backend/src/core/AnnouncementService.ts index 8c348e595dab16deb0b71a99cc56f7d7ef213d19..b298a70929839e08acf325a838b3de767f53b656 100644 --- a/packages/backend/src/core/AnnouncementService.ts +++ b/packages/backend/src/core/AnnouncementService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index 2c27a02559a03a2c6eaee33cc5c0c11faba23660..4f956a43ed7799483cf25a775c02eb4ea819220a 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -55,23 +55,29 @@ export class AntennaService implements OnApplicationShutdown { const { type, body } = obj.message as GlobalEvents['internal']['payload']; switch (type) { case 'antennaCreated': - this.antennas.push({ + this.antennas.push({ // TODO: ã“ã®ã‚ãŸã‚Šã®ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºå‡¦ç†ã¯å„modelファイル内ã«é–¢æ•°ã¨ã—ã¦exportã—ãŸã„ ...body, lastUsedAt: new Date(body.lastUsedAt), + user: null, // joinãªã‚«ãƒ©ãƒ ã¯é€šå¸¸å–ã£ã¦ã“ãªã„ã®ã§ + userList: null, // joinãªã‚«ãƒ©ãƒ ã¯é€šå¸¸å–ã£ã¦ã“ãªã„ã®ã§ }); break; case 'antennaUpdated': { const idx = this.antennas.findIndex(a => a.id === body.id); if (idx >= 0) { - this.antennas[idx] = { + this.antennas[idx] = { // TODO: ã“ã®ã‚ãŸã‚Šã®ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºå‡¦ç†ã¯å„modelファイル内ã«é–¢æ•°ã¨ã—ã¦exportã—ãŸã„ ...body, lastUsedAt: new Date(body.lastUsedAt), + user: null, // joinãªã‚«ãƒ©ãƒ ã¯é€šå¸¸å–ã£ã¦ã“ãªã„ã®ã§ + userList: null, // joinãªã‚«ãƒ©ãƒ ã¯é€šå¸¸å–ã£ã¦ã“ãªã„ã®ã§ }; } else { // サーãƒèµ·å‹•æ™‚ã«activeã˜ã‚ƒãªã‹ã£ãŸå ´åˆã€ãƒªã‚¹ãƒˆã«æŒã£ã¦ã„ãªã„ã®ã§è¿½åŠ ã™ã‚‹å¿…è¦ã‚ã‚Š - this.antennas.push({ + this.antennas.push({ // TODO: ã“ã®ã‚ãŸã‚Šã®ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºå‡¦ç†ã¯å„modelファイル内ã«é–¢æ•°ã¨ã—ã¦exportã—ãŸã„ ...body, lastUsedAt: new Date(body.lastUsedAt), + user: null, // joinãªã‚«ãƒ©ãƒ ã¯é€šå¸¸å–ã£ã¦ã“ãªã„ã®ã§ + userList: null, // joinãªã‚«ãƒ©ãƒ ã¯é€šå¸¸å–ã£ã¦ã“ãªã„ã®ã§ }); } } diff --git a/packages/backend/src/core/AppLockService.ts b/packages/backend/src/core/AppLockService.ts index 7a1293a6de57d405b1e6d411342ab67f44253a2d..bd2749cb871e74604c60c4f60aa57ec9dc703084 100644 --- a/packages/backend/src/core/AppLockService.ts +++ b/packages/backend/src/core/AppLockService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/AvatarDecorationService.ts b/packages/backend/src/core/AvatarDecorationService.ts index e97946f9dc0e97b7ba1cf511620e66d2dbc5a66f..21e31d79a4292dc3a99fa90e71d00ba4c020ba10 100644 --- a/packages/backend/src/core/AvatarDecorationService.ts +++ b/packages/backend/src/core/AvatarDecorationService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts index e1413342b19af8fc30de1c2e8feaf118cd75db4f..d008e7ec52a3c6cd4b3fcdee28ef95d793dbb024 100644 --- a/packages/backend/src/core/CacheService.ts +++ b/packages/backend/src/core/CacheService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -16,10 +16,10 @@ import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() export class CacheService implements OnApplicationShutdown { - public userByIdCache: MemoryKVCache<MiUser, MiUser | string>; - public localUserByNativeTokenCache: MemoryKVCache<MiLocalUser | null, string | null>; + public userByIdCache: MemoryKVCache<MiUser>; + public localUserByNativeTokenCache: MemoryKVCache<MiLocalUser | null>; public localUserByIdCache: MemoryKVCache<MiLocalUser>; - public uriPersonCache: MemoryKVCache<MiUser | null, string | null>; + public uriPersonCache: MemoryKVCache<MiUser | null>; public userProfileCache: RedisKVCache<MiUserProfile>; public userMutingsCache: RedisKVCache<Set<string>>; public userBlockingCache: RedisKVCache<Set<string>>; @@ -56,41 +56,10 @@ export class CacheService implements OnApplicationShutdown { ) { //this.onMessage = this.onMessage.bind(this); - const localUserByIdCache = new MemoryKVCache<MiLocalUser>(1000 * 60 * 60 * 6 /* 6h */); - this.localUserByIdCache = localUserByIdCache; - - // ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ãªã‚‰localUserByIdCacheã«ãƒ‡ãƒ¼ã‚¿ã‚’è¿½åŠ ã—ã€ã“ã¡ã‚‰ã«ã¯id(æ–‡å—列)ã ã‘ã‚’è¿½åŠ ã™ã‚‹ - const userByIdCache = new MemoryKVCache<MiUser, MiUser | string>(1000 * 60 * 60 * 6 /* 6h */, { - toMapConverter: user => { - if (user.host === null) { - localUserByIdCache.set(user.id, user as MiLocalUser); - return user.id; - } - - return user; - }, - fromMapConverter: userOrId => typeof userOrId === 'string' ? localUserByIdCache.get(userOrId) : userOrId, - }); - this.userByIdCache = userByIdCache; - - this.localUserByNativeTokenCache = new MemoryKVCache<MiLocalUser | null, string | null>(Infinity, { - toMapConverter: user => { - if (user === null) return null; - - localUserByIdCache.set(user.id, user); - return user.id; - }, - fromMapConverter: id => id === null ? null : localUserByIdCache.get(id), - }); - this.uriPersonCache = new MemoryKVCache<MiUser | null, string | null>(Infinity, { - toMapConverter: user => { - if (user === null) return null; - - userByIdCache.set(user.id, user); - return user.id; - }, - fromMapConverter: id => id === null ? null : userByIdCache.get(id), - }); + this.userByIdCache = new MemoryKVCache<MiUser>(Infinity); + this.localUserByNativeTokenCache = new MemoryKVCache<MiLocalUser | null>(Infinity); + this.localUserByIdCache = new MemoryKVCache<MiLocalUser>(Infinity); + this.uriPersonCache = new MemoryKVCache<MiUser | null>(Infinity); this.userProfileCache = new RedisKVCache<MiUserProfile>(this.redisClient, 'userProfile', { lifetime: 1000 * 60 * 30, // 30m @@ -159,17 +128,29 @@ export class CacheService implements OnApplicationShutdown { const { type, body } = obj.message as GlobalEvents['internal']['payload']; switch (type) { case 'userChangeSuspendedState': - case 'remoteUserUpdated': { - const user = await this.usersRepository.findOneByOrFail({ id: body.id }); - this.userByIdCache.set(user.id, user); - for (const [k, v] of this.uriPersonCache.cache.entries()) { - if (v.value === user.id) { - this.uriPersonCache.set(k, user); + case 'userChangeDeletedState': + case 'remoteUserUpdated': + case 'localUserUpdated': { + const user = await this.usersRepository.findOneBy({ id: body.id }); + if (user == null) { + this.userByIdCache.delete(body.id); + this.localUserByIdCache.delete(body.id); + for (const [k, v] of this.uriPersonCache.cache.entries()) { + if (v.value?.id === body.id) { + this.uriPersonCache.delete(k); + } + } + } else { + this.userByIdCache.set(user.id, user); + for (const [k, v] of this.uriPersonCache.cache.entries()) { + if (v.value?.id === user.id) { + this.uriPersonCache.set(k, user); + } + } + if (this.userEntityService.isLocalUser(user)) { + this.localUserByNativeTokenCache.set(user.token!, user); + this.localUserByIdCache.set(user.id, user); } - } - if (this.userEntityService.isLocalUser(user)) { - this.localUserByNativeTokenCache.set(user.token!, user); - this.localUserByIdCache.set(user.id, user); } break; } diff --git a/packages/backend/src/core/CaptchaService.ts b/packages/backend/src/core/CaptchaService.ts index f64196f4fcaee47723eb89a5f11d20fcf7c70a1e..f6b7955cd20778afd69b8e7e4015a4deb2f4ff45 100644 --- a/packages/backend/src/core/CaptchaService.ts +++ b/packages/backend/src/core/CaptchaService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -73,6 +73,37 @@ export class CaptchaService { } } + // https://codeberg.org/Gusted/mCaptcha/src/branch/main/mcaptcha.go + @bindThis + public async verifyMcaptcha(secret: string, siteKey: string, instanceHost: string, response: string | null | undefined): Promise<void> { + if (response == null) { + throw new Error('mcaptcha-failed: no response provided'); + } + + const endpointUrl = new URL('/api/v1/pow/siteverify', instanceHost); + const result = await this.httpRequestService.send(endpointUrl.toString(), { + method: 'POST', + body: JSON.stringify({ + key: siteKey, + secret: secret, + token: response, + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (result.status !== 200) { + throw new Error('mcaptcha-failed: mcaptcha didn\'t return 200 OK'); + } + + const resp = (await result.json()) as { valid: boolean }; + + if (!resp.valid) { + throw new Error('mcaptcha-request-failed'); + } + } + @bindThis public async verifyTurnstile(secret: string, response: string | null | undefined): Promise<void> { if (response == null) { diff --git a/packages/backend/src/core/ClipService.ts b/packages/backend/src/core/ClipService.ts index e94f1eb531852e550d5f1cafd0f18319413b2c88..bb8be26ce6c5bda7955b6bde155096b5382d679f 100644 --- a/packages/backend/src/core/ClipService.ts +++ b/packages/backend/src/core/ClipService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index fa868ff8b0eeb7f33e3e5b5ad2f2c57fc4feffe2..48a2a2b80c1725675439adc6046e264aa843775e 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -66,6 +66,8 @@ import { FeaturedService } from './FeaturedService.js'; import { FanoutTimelineService } from './FanoutTimelineService.js'; import { ChannelFollowingService } from './ChannelFollowingService.js'; import { RegistryApiService } from './RegistryApiService.js'; +import { ReversiService } from './ReversiService.js'; + import { ChartLoggerService } from './chart/ChartLoggerService.js'; import FederationChart from './chart/charts/federation.js'; import NotesChart from './chart/charts/notes.js'; @@ -80,6 +82,7 @@ import PerUserFollowingChart from './chart/charts/per-user-following.js'; import PerUserDriveChart from './chart/charts/per-user-drive.js'; import ApRequestChart from './chart/charts/ap-request.js'; import { ChartManagementService } from './chart/ChartManagementService.js'; + import { AbuseUserReportEntityService } from './entities/AbuseUserReportEntityService.js'; import { AntennaEntityService } from './entities/AntennaEntityService.js'; import { AppEntityService } from './entities/AppEntityService.js'; @@ -112,6 +115,9 @@ import { UserListEntityService } from './entities/UserListEntityService.js'; import { FlashEntityService } from './entities/FlashEntityService.js'; import { FlashLikeEntityService } from './entities/FlashLikeEntityService.js'; import { RoleEntityService } from './entities/RoleEntityService.js'; +import { ReversiGameEntityService } from './entities/ReversiGameEntityService.js'; +import { MetaEntityService } from './entities/MetaEntityService.js'; + import { ApAudienceService } from './activitypub/ApAudienceService.js'; import { ApDbResolverService } from './activitypub/ApDbResolverService.js'; import { ApDeliverManagerService } from './activitypub/ApDeliverManagerService.js'; @@ -199,6 +205,7 @@ const $FanoutTimelineService: Provider = { provide: 'FanoutTimelineService', use const $FanoutTimelineEndpointService: Provider = { provide: 'FanoutTimelineEndpointService', useExisting: FanoutTimelineEndpointService }; const $ChannelFollowingService: Provider = { provide: 'ChannelFollowingService', useExisting: ChannelFollowingService }; const $RegistryApiService: Provider = { provide: 'RegistryApiService', useExisting: RegistryApiService }; +const $ReversiService: Provider = { provide: 'ReversiService', useExisting: ReversiService }; const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService }; const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart }; @@ -247,6 +254,8 @@ const $UserListEntityService: Provider = { provide: 'UserListEntityService', use const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisting: FlashEntityService }; const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService }; const $RoleEntityService: Provider = { provide: 'RoleEntityService', useExisting: RoleEntityService }; +const $ReversiGameEntityService: Provider = { provide: 'ReversiGameEntityService', useExisting: ReversiGameEntityService }; +const $MetaEntityService: Provider = { provide: 'MetaEntityService', useExisting: MetaEntityService }; const $ApAudienceService: Provider = { provide: 'ApAudienceService', useExisting: ApAudienceService }; const $ApDbResolverService: Provider = { provide: 'ApDbResolverService', useExisting: ApDbResolverService }; @@ -336,6 +345,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting FanoutTimelineEndpointService, ChannelFollowingService, RegistryApiService, + ReversiService, + ChartLoggerService, FederationChart, NotesChart, @@ -350,6 +361,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting PerUserDriveChart, ApRequestChart, ChartManagementService, + AbuseUserReportEntityService, AntennaEntityService, AppEntityService, @@ -382,6 +394,9 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting FlashEntityService, FlashLikeEntityService, RoleEntityService, + ReversiGameEntityService, + MetaEntityService, + ApAudienceService, ApDbResolverService, ApDeliverManagerService, @@ -466,6 +481,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $FanoutTimelineEndpointService, $ChannelFollowingService, $RegistryApiService, + $ReversiService, + $ChartLoggerService, $FederationChart, $NotesChart, @@ -480,6 +497,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $PerUserDriveChart, $ApRequestChart, $ChartManagementService, + $AbuseUserReportEntityService, $AntennaEntityService, $AppEntityService, @@ -512,6 +530,9 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $FlashEntityService, $FlashLikeEntityService, $RoleEntityService, + $ReversiGameEntityService, + $MetaEntityService, + $ApAudienceService, $ApDbResolverService, $ApDeliverManagerService, @@ -597,6 +618,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting FanoutTimelineEndpointService, ChannelFollowingService, RegistryApiService, + ReversiService, + FederationChart, NotesChart, UsersChart, @@ -610,6 +633,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting PerUserDriveChart, ApRequestChart, ChartManagementService, + AbuseUserReportEntityService, AntennaEntityService, AppEntityService, @@ -642,6 +666,9 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting FlashEntityService, FlashLikeEntityService, RoleEntityService, + ReversiGameEntityService, + MetaEntityService, + ApAudienceService, ApDbResolverService, ApDeliverManagerService, @@ -726,6 +753,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $FanoutTimelineEndpointService, $ChannelFollowingService, $RegistryApiService, + $ReversiService, + $FederationChart, $NotesChart, $UsersChart, @@ -739,6 +768,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $PerUserDriveChart, $ApRequestChart, $ChartManagementService, + $AbuseUserReportEntityService, $AntennaEntityService, $AppEntityService, @@ -771,6 +801,9 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $FlashEntityService, $FlashLikeEntityService, $RoleEntityService, + $ReversiGameEntityService, + $MetaEntityService, + $ApAudienceService, $ApDbResolverService, $ApDeliverManagerService, diff --git a/packages/backend/src/core/CreateSystemUserService.ts b/packages/backend/src/core/CreateSystemUserService.ts index 58853c8e23ec58259e0935a4a545128670551da8..14d814b0e6ca7189249db45decde6a20fc9dfdfa 100644 --- a/packages/backend/src/core/CreateSystemUserService.ts +++ b/packages/backend/src/core/CreateSystemUserService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 5a1fe3d0890335c5b7d35bf33d9d0e5605fa0dee..acc1c604e63a597e310e70ddaf830be7ca08298c 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -408,7 +408,7 @@ export class CustomEmojiService implements OnApplicationShutdown { */ @bindThis public checkDuplicate(name: string): Promise<boolean> { - return this.emojisRepository.exist({ where: { name, host: IsNull() } }); + return this.emojisRepository.exists({ where: { name, host: IsNull() } }); } @bindThis @@ -416,6 +416,11 @@ export class CustomEmojiService implements OnApplicationShutdown { return this.emojisRepository.findOneBy({ id }); } + @bindThis + public getEmojiByName(name: string): Promise<MiEmoji | null> { + return this.emojisRepository.findOneBy({ name, host: IsNull() }); + } + @bindThis public dispose(): void { this.cache.dispose(); diff --git a/packages/backend/src/core/DeleteAccountService.ts b/packages/backend/src/core/DeleteAccountService.ts index 570bd440e44888b9475a2b63ae5f09f806dcad7b..79b614edbacac4edb725eb10c0be72767fb27086 100644 --- a/packages/backend/src/core/DeleteAccountService.ts +++ b/packages/backend/src/core/DeleteAccountService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -9,6 +9,7 @@ import { QueueService } from '@/core/QueueService.js'; import { UserSuspendService } from '@/core/UserSuspendService.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; @Injectable() export class DeleteAccountService { @@ -18,6 +19,7 @@ export class DeleteAccountService { private userSuspendService: UserSuspendService, private queueService: QueueService, + private globalEventService: GlobalEventService, ) { } @@ -39,5 +41,7 @@ export class DeleteAccountService { await this.usersRepository.update(user.id, { isDeleted: true, }); + + this.globalEventService.publishInternalEvent('userChangeDeletedState', { id: user.id, isDeleted: true }); } } diff --git a/packages/backend/src/core/DownloadService.ts b/packages/backend/src/core/DownloadService.ts index 5474272b003b2c8bd900255b0f6cab839853f236..21ae798f9fca8aebf828e3fed61953dd845806a1 100644 --- a/packages/backend/src/core/DownloadService.ts +++ b/packages/backend/src/core/DownloadService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -145,7 +145,8 @@ export class DownloadService { const parsedIp = ipaddr.parse(ip); for (const net of this.config.allowedPrivateNetworks ?? []) { - if (parsedIp.match(ipaddr.parseCIDR(net))) { + const cidr = ipaddr.parseCIDR(net); + if (cidr[0].kind() === parsedIp.kind() && parsedIp.match(ipaddr.parseCIDR(net))) { return false; } } diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index 9b6187be4fff95cfa8f8e0f09319cf88eedb8cdd..f64568ee9aa51a900d551673f12ca1524cc143cf 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -7,7 +7,7 @@ import { randomUUID } from 'node:crypto'; import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; import sharp from 'sharp'; -import { sharpBmp } from 'sharp-read-bmp'; +import { sharpBmp } from '@misskey-dev/sharp-read-bmp'; import { IsNull } from 'typeorm'; import { DeleteObjectCommandInput, PutObjectCommandInput, NoSuchKey } from '@aws-sdk/client-s3'; import { DI } from '@/di-symbols.js'; @@ -634,7 +634,7 @@ export class DriveService { public async updateFile(file: MiDriveFile, values: Partial<MiDriveFile>, updater: MiUser) { const alwaysMarkNsfw = (await this.roleService.getUserPolicies(file.userId)).alwaysMarkNsfw; - if (values.name && !this.driveFileEntityService.validateFileName(file.name)) { + if (values.name != null && !this.driveFileEntityService.validateFileName(values.name)) { throw new DriveService.InvalidFileNameError(); } diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts index 7fc7800783e00241e8ded9bf4021a0225f4189ec..08f8f80a6ed9151783e743ada5a4a43a965a4869 100644 --- a/packages/backend/src/core/EmailService.ts +++ b/packages/backend/src/core/EmailService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -40,6 +40,8 @@ export class EmailService { public async sendEmail(to: string, subject: string, html: string, text: string) { const meta = await this.metaService.fetch(true); + if (!meta.enableEmail) return; + const iconUrl = `${this.config.url}/static-assets/mi-white.png`; const emailSettingUrl = `${this.config.url}/settings/email`; @@ -156,7 +158,7 @@ export class EmailService { @bindThis public async validateEmailForAccount(emailAddress: string): Promise<{ available: boolean; - reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp' | 'banned'; + reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp' | 'banned' | 'network' | 'blacklist'; }> { const meta = await this.metaService.fetch(); @@ -165,14 +167,23 @@ export class EmailService { email: emailAddress, }); + if (exist !== 0) { + return { + available: false, + reason: 'used', + }; + } + let validated: { valid: boolean, reason?: string | null, - }; + } = { valid: true, reason: null }; if (meta.enableActiveEmailValidation) { if (meta.enableVerifymailApi && meta.verifymailAuthKey != null) { validated = await this.verifyMail(emailAddress, meta.verifymailAuthKey); + } else if (meta.enableTruemailApi && meta.truemailInstance && meta.truemailAuthKey != null) { + validated = await this.trueMail(meta.truemailInstance, emailAddress, meta.truemailAuthKey); } else { validated = await validateEmail({ email: emailAddress, @@ -183,25 +194,37 @@ export class EmailService { validateSMTP: false, // 日本ã ã¨25ãƒãƒ¼ãƒˆãŒæ®†ã©ã®ãƒ—ãƒãƒã‚¤ãƒ€ãƒ¼ã§å¡žãŒã‚Œã¦ã„ã¦ã‚¿ã‚¤ãƒ アウトã«ãªã‚‹ã®ã§ }); } - } else { - validated = { valid: true, reason: null }; + } + + if (!validated.valid) { + const formatReason: Record<string, 'format' | 'disposable' | 'mx' | 'smtp' | 'network' | 'blacklist' | undefined> = { + regex: 'format', + disposable: 'disposable', + mx: 'mx', + smtp: 'smtp', + network: 'network', + blacklist: 'blacklist', + }; + + return { + available: false, + reason: validated.reason ? formatReason[validated.reason] ?? null : null, + }; } const emailDomain: string = emailAddress.split('@')[1]; const isBanned = this.utilityService.isBlockedHost(meta.bannedEmailDomains, emailDomain); - const available = exist === 0 && validated.valid && !isBanned; + if (isBanned) { + return { + available: false, + reason: 'banned', + }; + } return { - available, - reason: available ? null : - exist !== 0 ? 'used' : - isBanned ? 'banned' : - validated.reason === 'regex' ? 'format' : - validated.reason === 'disposable' ? 'disposable' : - validated.reason === 'mx' ? 'mx' : - validated.reason === 'smtp' ? 'smtp' : - null, + available: true, + reason: null, }; } @@ -218,7 +241,8 @@ export class EmailService { }, }); - const json = (await res.json()) as { + const json = (await res.json()) as Partial<{ + message: string; block: boolean; catch_all: boolean; deliverable_email: boolean; @@ -233,8 +257,15 @@ export class EmailService { mx_priority: { [key: string]: number }; privacy: boolean; related_domains: string[]; - }; + }>; + /* api error: when there is only one `message` attribute in the returned result */ + if (Object.keys(json).length === 1 && Reflect.has(json, 'message')) { + return { + valid: false, + reason: null, + }; + } if (json.email_address === undefined) { return { valid: false, @@ -265,4 +296,68 @@ export class EmailService { reason: null, }; } + + private async trueMail<T>(truemailInstance: string, emailAddress: string, truemailAuthKey: string): Promise<{ + valid: boolean; + reason: 'used' | 'format' | 'blacklist' | 'mx' | 'smtp' | 'network' | T | null; + }> { + const endpoint = truemailInstance + '?email=' + emailAddress; + try { + const res = await this.httpRequestService.send(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + Authorization: truemailAuthKey, + }, + }); + + const json = (await res.json()) as { + email: string; + success: boolean; + error?: string; + errors?: { + list_match?: string; + regex?: string; + mx?: string; + smtp?: string; + } | null; + }; + + if (json.email === undefined || json.errors?.regex) { + return { + valid: false, + reason: 'format', + }; + } + if (json.errors?.smtp) { + return { + valid: false, + reason: 'smtp', + }; + } + if (json.errors?.mx) { + return { + valid: false, + reason: 'mx', + }; + } + if (!json.success) { + return { + valid: false, + reason: json.errors?.list_match as T || 'blacklist', + }; + } + + return { + valid: true, + reason: null, + }; + } catch (error) { + return { + valid: false, + reason: 'network', + }; + } + } } diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts index 6d857d189ed8f339be3d40691dc53c2a6e397dc4..6aa63d7d55e5cfe1847fa4fcaf6ab7b6c9477b3e 100644 --- a/packages/backend/src/core/FanoutTimelineEndpointService.ts +++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -122,6 +122,8 @@ export class FanoutTimelineEndpointService { filter = (note) => { if (isUserRelated(note, userIdsWhoBlockingMe, ps.ignoreAuthorFromBlock)) return false; if (isUserRelated(note, userIdsWhoMeMuting, ps.ignoreAuthorFromMute)) return false; + if (note.mentions.some(mention => userIdsWhoMeMuting.has(mention))) return false; + if (isPureRenote(note) && note.renote && note.renote.mentions.some(mention => userIdsWhoMeMuting.has(mention))) return false; if (isPureRenote(note) && isUserRelated(note, userIdsWhoMeMutingRenotes, ps.ignoreAuthorFromMute)) return false; if (isInstanceMuted(note, userMutedInstances)) return false; diff --git a/packages/backend/src/core/FanoutTimelineService.ts b/packages/backend/src/core/FanoutTimelineService.ts index 9b2678fbcda6cf5fb15648ac0deebe3c6142b24a..f6dabfadcd6d13c70635ff57687751ba0ce3955b 100644 --- a/packages/backend/src/core/FanoutTimelineService.ts +++ b/packages/backend/src/core/FanoutTimelineService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/FeaturedService.ts b/packages/backend/src/core/FeaturedService.ts index 595383c82c9084a0f704ca6e893959efd903ea2d..b3335e38da475457fc2f38542fa3fc39608e000a 100644 --- a/packages/backend/src/core/FeaturedService.ts +++ b/packages/backend/src/core/FeaturedService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/FederatedInstanceService.ts b/packages/backend/src/core/FederatedInstanceService.ts index e41f010e48f2d536a60bed9533790f9a1c44ae6c..66db2067d9f8ce1f391956267d3a07bdf1f236ad 100644 --- a/packages/backend/src/core/FederatedInstanceService.ts +++ b/packages/backend/src/core/FederatedInstanceService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index 682acef15b967d4f0474988106e20665f40c2a63..bc270bd28fd3cddd4fabecd788aa3ae9cb5bb687 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/FileInfoService.ts b/packages/backend/src/core/FileInfoService.ts index 803f6908deba407d67132b09129dd7a5391debaa..2d3312f2474de9eebbbd1cfd59ceff300256afcd 100644 --- a/packages/backend/src/core/FileInfoService.ts +++ b/packages/backend/src/core/FileInfoService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -11,6 +11,7 @@ import * as fileType from 'file-type'; import isSvg from 'is-svg'; import probeImageSize from 'probe-image-size'; import sharp from 'sharp'; +import { sharpBmp } from '@misskey-dev/sharp-read-bmp'; import { encode } from 'blurhash'; import { bindThis } from '@/decorators.js'; @@ -110,7 +111,7 @@ export class FileInfoService { 'image/avif', 'image/svg+xml', ].includes(type.mime)) { - blurhash = await this.getBlurhash(path).catch(e => { + blurhash = await this.getBlurhash(path, type.mime).catch(e => { warnings.push(`getBlurhash failed: ${e}`); return undefined; }); @@ -237,9 +238,9 @@ export class FileInfoService { * Calculate average color of image */ @bindThis - private getBlurhash(path: string): Promise<string> { - return new Promise((resolve, reject) => { - sharp(path) + private getBlurhash(path: string, type: string): Promise<string> { + return new Promise(async (resolve, reject) => { + (await sharpBmp(path, type)) .raw() .ensureAlpha() .resize(64, 64, { fit: 'inside' }) diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 95a4eba7428b0d12736fb7f81e94c8f9623973f4..22871adb16bc4bc9529f5543b093a65ab54e43c1 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -1,10 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; +import * as Reversi from 'misskey-reversi'; import type { MiChannel } from '@/models/Channel.js'; import type { MiUser } from '@/models/User.js'; import type { MiUserProfile } from '@/models/UserProfile.js'; @@ -18,7 +19,7 @@ import type { MiSignin } from '@/models/Signin.js'; import type { MiPage } from '@/models/Page.js'; import type { MiWebhook } from '@/models/Webhook.js'; import type { MiMeta } from '@/models/Meta.js'; -import { MiAvatarDecoration, MiRole, MiRoleAssignment } from '@/models/_.js'; +import { MiAvatarDecoration, MiReversiGame, MiRole, MiRoleAssignment } from '@/models/_.js'; import type { Packed } from '@/misc/json-schema.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; @@ -53,21 +54,22 @@ export interface MainEventTypes { reply: Packed<'Note'>; renote: Packed<'Note'>; follow: Packed<'UserDetailedNotMe'>; - followed: Packed<'User'>; - unfollow: Packed<'User'>; - meUpdated: Packed<'User'>; + followed: Packed<'UserLite'>; + unfollow: Packed<'UserDetailedNotMe'>; + meUpdated: Packed<'MeDetailed'>; pageEvent: { pageId: MiPage['id']; event: string; var: any; userId: MiUser['id']; - user: Packed<'User'>; + user: Packed<'UserDetailed'>; }; urlUploadFinished: { marker?: string | null; file: Packed<'DriveFile'>; }; readAllNotifications: undefined; + notificationFlushed: undefined; unreadNotification: Packed<'Notification'>; unreadMention: MiNote['id']; readAllUnreadMentions: undefined; @@ -91,10 +93,11 @@ export interface MainEventTypes { }; driveFileCreated: Packed<'DriveFile'>; readAntenna: MiAntenna; - receiveFollowRequest: Packed<'User'>; + receiveFollowRequest: Packed<'UserLite'>; announcementCreated: { announcement: Packed<'Announcement'>; }; + edited: Packed<'Note'>; } export interface DriveEventTypes { @@ -142,8 +145,8 @@ type NoteStreamEventTypes = { }; export interface UserListEventTypes { - userAdded: Packed<'User'>; - userRemoved: Packed<'User'>; + userAdded: Packed<'UserLite'>; + userRemoved: Packed<'UserLite'>; } export interface AntennaEventTypes { @@ -162,6 +165,38 @@ export interface AdminEventTypes { comment: string; }; } + +export interface ReversiEventTypes { + matched: { + game: Packed<'ReversiGameDetailed'>; + }; + invited: { + user: Packed<'User'>; + }; +} + +export interface ReversiGameEventTypes { + changeReadyStates: { + user1: boolean; + user2: boolean; + }; + updateSettings: { + userId: MiUser['id']; + key: string; + value: any; + }; + log: Reversi.Serializer.Log & { id: string | null }; + started: { + game: Packed<'ReversiGameDetailed'>; + }; + ended: { + winnerId: MiUser['id'] | null; + game: Packed<'ReversiGameDetailed'>; + }; + canceled: { + userId: MiUser['id']; + }; +} //#endregion // 辞書(interface or type)ã‹ã‚‰{ type, body }ユニオンを定義 @@ -179,8 +214,10 @@ type SerializedAll<T> = { export interface InternalEventTypes { userChangeSuspendedState: { id: MiUser['id']; isSuspended: MiUser['isSuspended']; }; + userChangeDeletedState: { id: MiUser['id']; isDeleted: MiUser['isDeleted']; }; userTokenRegenerated: { id: MiUser['id']; oldToken: string; newToken: string; }; remoteUserUpdated: { id: MiUser['id']; }; + localUserUpdated: { id: MiUser['id']; }; follow: { followerId: MiUser['id']; followeeId: MiUser['id']; }; unfollow: { followerId: MiUser['id']; followeeId: MiUser['id']; }; blockingCreated: { blockerId: MiUser['id']; blockeeId: MiUser['id']; }; @@ -252,6 +289,14 @@ export type GlobalEvents = { name: 'notesStream'; payload: Serialized<Packed<'Note'>>; }; + reversi: { + name: `reversiStream:${MiUser['id']}`; + payload: EventUnionFromDictionary<SerializedAll<ReversiEventTypes>>; + }; + reversiGame: { + name: `reversiGameStream:${MiReversiGame['id']}`; + payload: EventUnionFromDictionary<SerializedAll<ReversiGameEventTypes>>; + }; }; // API event definitions @@ -341,4 +386,14 @@ export class GlobalEventService { public publishAdminStream<K extends keyof AdminEventTypes>(userId: MiUser['id'], type: K, value?: AdminEventTypes[K]): void { this.publish(`adminStream:${userId}`, type, typeof value === 'undefined' ? null : value); } + + @bindThis + public publishReversiStream<K extends keyof ReversiEventTypes>(userId: MiUser['id'], type: K, value?: ReversiEventTypes[K]): void { + this.publish(`reversiStream:${userId}`, type, typeof value === 'undefined' ? null : value); + } + + @bindThis + public publishReversiGameStream<K extends keyof ReversiGameEventTypes>(gameId: MiReversiGame['id'], type: K, value?: ReversiGameEventTypes[K]): void { + this.publish(`reversiGameStream:${gameId}`, type, typeof value === 'undefined' ? null : value); + } } diff --git a/packages/backend/src/core/HashtagService.ts b/packages/backend/src/core/HashtagService.ts index 5a2417c9cdb4b8fa822cf2cbad3d7710314d870e..eb192ee6dafca28d2ce0945975daf520c1657778 100644 --- a/packages/backend/src/core/HashtagService.ts +++ b/packages/backend/src/core/HashtagService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -163,7 +163,7 @@ export class HashtagService { const instance = await this.metaService.fetch(); const hiddenTags = instance.hiddenTags.map(t => normalizeForSearch(t)); if (hiddenTags.includes(hashtag)) return; - if (this.utilityService.isSensitiveWordIncluded(hashtag, instance.sensitiveWords)) return; + if (this.utilityService.isKeyWordIncluded(hashtag, instance.sensitiveWords)) return; // YYYYMMDDHHmm (10分間隔) const now = new Date(); diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts index 1352e137cead719e8df5d673ec79a49aa86e0318..7f3cac7c58078b5a1abd4db0b4066ed16c312032 100644 --- a/packages/backend/src/core/HttpRequestService.ts +++ b/packages/backend/src/core/HttpRequestService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/IdService.ts b/packages/backend/src/core/IdService.ts index 43e72d2d7bdacfbeb5e2a645992fc662ee4afdb0..10df6ef266e70ac8c42bcbca9a400b1fe28ac019 100644 --- a/packages/backend/src/core/IdService.ts +++ b/packages/backend/src/core/IdService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/ImageProcessingService.ts b/packages/backend/src/core/ImageProcessingService.ts index 8e800eb8f518f45adb86a4d8ea136d0e66a93406..6f978b34c8b76f0aa66f29abd37222022083db2e 100644 --- a/packages/backend/src/core/ImageProcessingService.ts +++ b/packages/backend/src/core/ImageProcessingService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/InstanceActorService.ts b/packages/backend/src/core/InstanceActorService.ts index b40fd46291ecdbc2f1dc4102a84d7dba4e9ff351..22c47297a34d93d0ef917f6c4ef69c01fbda2e10 100644 --- a/packages/backend/src/core/InstanceActorService.ts +++ b/packages/backend/src/core/InstanceActorService.ts @@ -1,10 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { Inject, Injectable } from '@nestjs/common'; -import { IsNull } from 'typeorm'; +import { IsNull, Not } from 'typeorm'; import type { MiLocalUser } from '@/models/User.js'; import type { UsersRepository } from '@/models/_.js'; import { MemorySingleCache } from '@/misc/cache.js'; @@ -27,6 +27,14 @@ export class InstanceActorService { this.cache = new MemorySingleCache<MiLocalUser>(Infinity); } + @bindThis + public async realLocalUsersPresent(): Promise<boolean> { + return await this.usersRepository.existsBy({ + host: IsNull(), + username: Not(ACTOR_USERNAME), + }); + } + @bindThis public async getInstanceActor(): Promise<MiLocalUser> { const cached = this.cache.get(); diff --git a/packages/backend/src/core/InternalStorageService.ts b/packages/backend/src/core/InternalStorageService.ts index 22129bb3483990fc76dca55bbd693f1a9e864479..4fb8a93e4916400bdf153493c69b0c1505562fd8 100644 --- a/packages/backend/src/core/InternalStorageService.ts +++ b/packages/backend/src/core/InternalStorageService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/LoggerService.ts b/packages/backend/src/core/LoggerService.ts index 46b000ee63a6754759e331fa5c7b4de5246db9c9..96d9b099927b5ad58f28aab81ed0be996b11b9c9 100644 --- a/packages/backend/src/core/LoggerService.ts +++ b/packages/backend/src/core/LoggerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/MetaService.ts b/packages/backend/src/core/MetaService.ts index 80e8020961388e74110414d6ab10eabd26fc2dc1..ec630f804e7cf3406fde4e9a314560f32745ad95 100644 --- a/packages/backend/src/core/MetaService.ts +++ b/packages/backend/src/core/MetaService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -51,7 +51,10 @@ export class MetaService implements OnApplicationShutdown { const { type, body } = obj.message as GlobalEvents['internal']['payload']; switch (type) { case 'metaUpdated': { - this.cache = body; + this.cache = { // TODO: ã“ã®ã‚ãŸã‚Šã®ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºå‡¦ç†ã¯å„modelファイル内ã«é–¢æ•°ã¨ã—ã¦exportã—ãŸã„ + ...body, + proxyAccount: null, // joinãªã‚«ãƒ©ãƒ ã¯é€šå¸¸å–ã£ã¦ã“ãªã„ã®ã§ + }; break; } default: diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index 60bf8b3c2a0017eb3f651ac717b88e46ca9a3118..b7c9064cef1b4731d51e516918b5024c86cce3ba 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -13,7 +13,7 @@ import { intersperse } from '@/misc/prelude/array.js'; import type { IMentionedRemoteUsers } from '@/models/Note.js'; import { bindThis } from '@/decorators.js'; import * as TreeAdapter from '../../node_modules/parse5/dist/tree-adapters/default.js'; -import type * as mfm from '@sharkey/sfm-js'; +import type * as mfm from '@transfem-org/sfm-js'; const treeAdapter = TreeAdapter.defaultTreeAdapter; @@ -419,6 +419,10 @@ export class MfmService { }, text: (node) => { + if (!node.props.text.match(/[\r\n]/)) { + return doc.createTextNode(node.props.text); + } + const el = doc.createElement('span'); const nodes = node.props.text.split(/\r\n|\r|\n/).map(x => doc.createTextNode(x)); @@ -475,7 +479,7 @@ export class MfmService { const handlers: { [K in mfm.MfmNode['type']]: (node: mfm.NodeType<K>) => any; - } = { + } = { async bold(node) { const el = doc.createElement('span'); el.textContent = '**'; @@ -643,8 +647,8 @@ export class MfmService { await appendChildren(node.children, el); return el; }, - }; - + }; + await appendChildren(nodes, doc.body); if (quoteUri !== null) { diff --git a/packages/backend/src/core/ModerationLogService.ts b/packages/backend/src/core/ModerationLogService.ts index 8b78d020472ab5ddea5078ef5aa74cd716921d08..6c155c9a627c145c784442ec46d490d484c445bf 100644 --- a/packages/backend/src/core/ModerationLogService.ts +++ b/packages/backend/src/core/ModerationLogService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 3bc4a29b99b1ca778c005d881989e13f10569d26..b985846f1c49e138d9edb2d073e154137e85172f 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -1,10 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { setImmediate } from 'node:timers/promises'; -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; import { In, DataSource, IsNull, LessThan } from 'typeorm'; import * as Redis from 'ioredis'; import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; @@ -57,7 +57,12 @@ import { FeaturedService } from '@/core/FeaturedService.js'; import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; +import { CacheService } from '@/core/CacheService.js'; import { isReply } from '@/misc/is-reply.js'; +import { trackPromise } from '@/misc/promise-tracker.js'; +import { isUserRelated } from '@/misc/is-user-related.js'; +import { isNotNull } from '@/misc/is-not-null.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; @@ -216,6 +221,7 @@ export class NoteCreateService implements OnApplicationShutdown { private instanceChart: InstanceChart, private utilityService: UtilityService, private userBlockingService: UserBlockingService, + private cacheService: CacheService, ) { } @bindThis @@ -253,7 +259,7 @@ export class NoteCreateService implements OnApplicationShutdown { if (data.visibility === 'public' && data.channel == null) { const sensitiveWords = meta.sensitiveWords; - if (this.utilityService.isSensitiveWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) { + if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) { data.visibility = 'home'; } else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) { data.visibility = 'home'; @@ -324,6 +330,9 @@ export class NoteCreateService implements OnApplicationShutdown { data.text = data.text.slice(0, DB_MAX_NOTE_TEXT_LENGTH); } data.text = data.text.trim(); + if (data.text === '') { + data.text = null; + } } else { data.text = null; } @@ -377,6 +386,10 @@ export class NoteCreateService implements OnApplicationShutdown { }); } + if (mentionedUsers.length > 0 && mentionedUsers.length > (await this.roleService.getUserPolicies(user.id)).mentionLimit) { + throw new IdentifiableError('9f466dab-c856-48cd-9e65-ff90ff750580', 'Note contains too many mentions'); + } + const note = await this.insertNote(user, data, tags, emojis, mentionedUsers); setImmediate('post created', { signal: this.#shutdownController.signal }).then( @@ -422,13 +435,23 @@ export class NoteCreateService implements OnApplicationShutdown { if (data.visibility === 'public' && data.channel == null) { const sensitiveWords = meta.sensitiveWords; - if (this.utilityService.isSensitiveWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) { + if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) { data.visibility = 'home'; } else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) { data.visibility = 'home'; } } + const hasProhibitedWords = await this.checkProhibitedWordsContain({ + cw: data.cw, + text: data.text, + pollChoices: data.poll?.choices, + }, meta.prohibitedWords); + + if (hasProhibitedWords) { + throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words'); + } + const inSilencedInstance = this.utilityService.isSilencedHost(meta.silencedHosts, user.host); if (data.visibility === 'public' && inSilencedInstance && user.host !== null) { @@ -493,6 +516,9 @@ export class NoteCreateService implements OnApplicationShutdown { data.text = data.text.slice(0, DB_MAX_NOTE_TEXT_LENGTH); } data.text = data.text.trim(); + if (data.text === '') { + data.text = null; + } } else { data.text = null; } @@ -538,6 +564,10 @@ export class NoteCreateService implements OnApplicationShutdown { } } + if (mentionedUsers.length > 0 && mentionedUsers.length > (await this.roleService.getUserPolicies(user.id)).mentionLimit) { + throw new IdentifiableError('9f466dab-c856-48cd-9e65-ff90ff750580', 'Note contains too many mentions'); + } + const note = await this.insertNote(user, data, tags, emojis, mentionedUsers); setImmediate('post created', { signal: this.#shutdownController.signal }).then( @@ -785,14 +815,22 @@ export class NoteCreateService implements OnApplicationShutdown { }); // 通知 if (data.reply.userHost === null) { - const isThreadMuted = await this.noteThreadMutingsRepository.exist({ + const isThreadMuted = await this.noteThreadMutingsRepository.exists({ where: { userId: data.reply.userId, threadId: data.reply.threadId ?? data.reply.id, }, }); - if (!isThreadMuted) { + const [ + userIdsWhoMeMuting, + ] = data.reply.userId ? await Promise.all([ + this.cacheService.userMutingsCache.fetch(data.reply.userId), + ]) : [new Set<string>()]; + + const muted = isUserRelated(note, userIdsWhoMeMuting); + + if (!isThreadMuted && !muted) { nm.push(data.reply.userId, 'reply'); this.globalEventService.publishMainStream(data.reply.userId, 'reply', noteObj); @@ -812,7 +850,24 @@ export class NoteCreateService implements OnApplicationShutdown { // Notify if (data.renote.userHost === null) { - nm.push(data.renote.userId, type); + const isThreadMuted = await this.noteThreadMutingsRepository.exists({ + where: { + userId: data.renote.userId, + threadId: data.renote.threadId ?? data.renote.id, + }, + }); + + const [ + userIdsWhoMeMuting, + ] = data.renote.userId ? await Promise.all([ + this.cacheService.userMutingsCache.fetch(data.renote.userId), + ]) : [new Set<string>()]; + + const muted = isUserRelated(note, userIdsWhoMeMuting); + + if (!isThreadMuted && !muted) { + nm.push(data.renote.userId, type); + } } // Publish event @@ -862,7 +917,7 @@ export class NoteCreateService implements OnApplicationShutdown { this.relayService.deliverToRelays(user, noteActivity); } - dm.execute(); + trackPromise(dm.execute()); })(); } //#endregion @@ -1022,14 +1077,22 @@ export class NoteCreateService implements OnApplicationShutdown { @bindThis private async createMentionedEvents(mentionedUsers: MinimumUser[], note: MiNote, nm: NotificationManager) { for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) { - const isThreadMuted = await this.noteThreadMutingsRepository.exist({ + const isThreadMuted = await this.noteThreadMutingsRepository.exists({ where: { userId: u.id, threadId: note.threadId ?? note.id, }, }); - if (isThreadMuted) { + const [ + userIdsWhoMeMuting, + ] = u.id ? await Promise.all([ + this.cacheService.userMutingsCache.fetch(u.id), + ]) : [new Set<string>()]; + + const muted = isUserRelated(note, userIdsWhoMeMuting); + + if (isThreadMuted || muted) { continue; } @@ -1092,7 +1155,7 @@ export class NoteCreateService implements OnApplicationShutdown { const mentions = extractMentions(tokens); let mentionedUsers = (await Promise.all(mentions.map(m => this.remoteUserResolveService.resolveUser(m.username, m.host ?? user.host).catch(() => null), - ))).filter(x => x != null) as MiUser[]; + ))).filter(isNotNull); // Drop duplicate users mentionedUsers = mentionedUsers.filter((u, i, self) => @@ -1266,6 +1329,23 @@ export class NoteCreateService implements OnApplicationShutdown { } } + public async checkProhibitedWordsContain(content: Parameters<UtilityService['concatNoteContentsForKeyWordCheck']>[0], prohibitedWords?: string[]) { + if (prohibitedWords == null) { + prohibitedWords = (await this.metaService.fetch()).prohibitedWords; + } + + if ( + this.utilityService.isKeyWordIncluded( + this.utilityService.concatNoteContentsForKeyWordCheck(content), + prohibitedWords, + ) + ) { + return true; + } + + return false; + } + @bindThis public dispose(): void { this.#shutdownController.abort(); diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index 99d9d9db7e685f561382a14c61d06d779ec8ab5b..471ade92c7251cbf6c607afc63219b3f26ec5d42 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/NoteEditService.ts b/packages/backend/src/core/NoteEditService.ts index dae3f485ffb335f355b1586de919cbd1d822039f..72fc01ae3b41721cb6384cdb1fad9d911bec8a9a 100644 --- a/packages/backend/src/core/NoteEditService.ts +++ b/packages/backend/src/core/NoteEditService.ts @@ -4,7 +4,7 @@ */ import { setImmediate } from 'node:timers/promises'; -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; import { DataSource, In, IsNull, LessThan } from 'typeorm'; import * as Redis from 'ioredis'; import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; @@ -46,8 +46,15 @@ import { MetaService } from '@/core/MetaService.js'; import { SearchService } from '@/core/SearchService.js'; import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import { UtilityService } from '@/core/UtilityService.js'; +import { UserBlockingService } from '@/core/UserBlockingService.js'; +import { CacheService } from '@/core/CacheService.js'; +import { isReply } from '@/misc/is-reply.js'; +import { trackPromise } from '@/misc/promise-tracker.js'; +import { isUserRelated } from '@/misc/is-user-related.js'; +import { isNotNull } from '@/misc/is-not-null.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; -type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; +type NotificationType = 'reply' | 'renote' | 'quote' | 'mention' | 'edited'; class NotificationManager { private notifier: { id: MiUser['id']; }; @@ -206,6 +213,8 @@ export class NoteEditService implements OnApplicationShutdown { private activeUsersChart: ActiveUsersChart, private instanceChart: InstanceChart, private utilityService: UtilityService, + private userBlockingService: UserBlockingService, + private cacheService: CacheService, ) { } @bindThis @@ -222,7 +231,7 @@ export class NoteEditService implements OnApplicationShutdown { const oldnote = await this.notesRepository.findOneBy({ id: editid, - }); + }); if (oldnote == null) { throw new Error('no such note'); @@ -232,6 +241,13 @@ export class NoteEditService implements OnApplicationShutdown { throw new Error('not the author'); } + // we never want to change the replyId, so fetch the original "parent" + if (oldnote.replyId) { + data.reply = await this.notesRepository.findOneBy({ id: oldnote.replyId }); + } else { + data.reply = undefined; + } + // ãƒãƒ£ãƒ³ãƒãƒ«å¤–ã«ãƒªãƒ—ライã—ãŸã‚‰å¯¾è±¡ã®ã‚¹ã‚³ãƒ¼ãƒ—ã«åˆã‚ã›ã‚‹ // (クライアントサイドã§ã‚„ã£ã¦ã‚‚良ã„処ç†ã ã¨æ€ã†ã‘ã©ã¨ã‚Šã‚ãˆãšã‚µãƒ¼ãƒãƒ¼ã‚µã‚¤ãƒ‰ã§) if (data.reply && data.channel && data.reply.channelId !== data.channel.id) { @@ -255,16 +271,28 @@ export class NoteEditService implements OnApplicationShutdown { if (data.channel != null) data.localOnly = true; if (data.updatedAt == null) data.updatedAt = new Date(); + const meta = await this.metaService.fetch(); + if (data.visibility === 'public' && data.channel == null) { - const sensitiveWords = (await this.metaService.fetch()).sensitiveWords; - if (this.isSensitive(data, sensitiveWords)) { + const sensitiveWords = meta.sensitiveWords; + if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) { data.visibility = 'home'; } else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) { data.visibility = 'home'; } } - const inSilencedInstance = this.utilityService.isSilencedHost((await this.metaService.fetch()).silencedHosts, user.host); + const hasProhibitedWords = await this.checkProhibitedWordsContain({ + cw: data.cw, + text: data.text, + pollChoices: data.poll?.choices, + }, meta.prohibitedWords); + + if (hasProhibitedWords) { + throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words'); + } + + const inSilencedInstance = this.utilityService.isSilencedHost((meta).silencedHosts, user.host); if (data.visibility === 'public' && inSilencedInstance && user.host !== null) { data.visibility = 'home'; @@ -296,6 +324,18 @@ export class NoteEditService implements OnApplicationShutdown { } } + // Check blocking + if (data.renote && !this.isQuote(data)) { + if (data.renote.userHost === null) { + if (data.renote.userId !== user.id) { + const blocked = await this.userBlockingService.checkBlocked(data.renote.userId, user.id); + if (blocked) { + throw new Error('blocked'); + } + } + } + } + // 返信対象ãŒpublicã§ã¯ãªã„ãªã‚‰homeã«ã™ã‚‹ if (data.reply && data.reply.visibility !== 'public' && data.visibility === 'public') { data.visibility = 'home'; @@ -316,6 +356,9 @@ export class NoteEditService implements OnApplicationShutdown { data.text = data.text.slice(0, DB_MAX_NOTE_TEXT_LENGTH); } data.text = data.text.trim(); + if (data.text === '') { + data.text = null; + } } else { data.text = null; } @@ -361,6 +404,18 @@ export class NoteEditService implements OnApplicationShutdown { } } + if (user.host && !data.cw) { + await this.federatedInstanceService.fetch(user.host).then(async i => { + if (i.isNSFW) { + data.cw = 'Instance is marked as NSFW'; + } + }); + } + + if (mentionedUsers.length > 0 && mentionedUsers.length > (await this.roleService.getUserPolicies(user.id)).mentionLimit) { + throw new IdentifiableError('9f466dab-c856-48cd-9e65-ff90ff750580', 'Note contains too many mentions'); + } + const update: Partial<MiNote> = {}; if (data.text !== oldnote.text) { update.text = data.text; @@ -397,7 +452,7 @@ export class NoteEditService implements OnApplicationShutdown { id: oldnote.id, updatedAt: data.updatedAt ? data.updatedAt : new Date(), fileIds: data.files ? data.files.map(file => file.id) : [], - replyId: data.reply ? data.reply.id : null, + replyId: oldnote.replyId, renoteId: data.renote ? data.renote.id : null, channelId: data.channel ? data.channel.id : null, threadId: data.reply @@ -548,7 +603,7 @@ export class NoteEditService implements OnApplicationShutdown { } // Pack the note - const noteObj = await this.noteEntityService.pack(note, null, { skipHide: true }); + const noteObj = await this.noteEntityService.pack(note, null, { skipHide: true, withReactionAndUserPairCache: true }); if (data.poll != null) { this.globalEventService.publishNoteStream(note.id, 'updated', { cw: note.cw, @@ -557,7 +612,7 @@ export class NoteEditService implements OnApplicationShutdown { } else { this.globalEventService.publishNoteStream(note.id, 'updated', { cw: note.cw, - text: note.text! + text: note.text!, }); } @@ -574,26 +629,34 @@ export class NoteEditService implements OnApplicationShutdown { const nm = new NotificationManager(this.mutingsRepository, this.notificationService, user, note); - await this.createMentionedEvents(mentionedUsers, note, nm); + //await this.createMentionedEvents(mentionedUsers, note, nm); // If has in reply to note if (data.reply) { // 通知 if (data.reply.userHost === null) { - const isThreadMuted = await this.noteThreadMutingsRepository.exist({ + const isThreadMuted = await this.noteThreadMutingsRepository.exists({ where: { userId: data.reply.userId, threadId: data.reply.threadId ?? data.reply.id, }, }); - if (!isThreadMuted) { - nm.push(data.reply.userId, 'reply'); - this.globalEventService.publishMainStream(data.reply.userId, 'reply', noteObj); + const [ + userIdsWhoMeMuting, + ] = data.reply.userId ? await Promise.all([ + this.cacheService.userMutingsCache.fetch(data.reply.userId), + ]) : [new Set<string>()]; + + const muted = isUserRelated(note, userIdsWhoMeMuting); + + if (!isThreadMuted && !muted) { + nm.push(data.reply.userId, 'edited'); + this.globalEventService.publishMainStream(data.reply.userId, 'edited', noteObj); - const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === data.reply!.userId && x.on.includes('reply')); + const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === data.reply!.userId && x.on.includes('edited')); for (const webhook of webhooks) { - this.queueService.webhookDeliver(webhook, 'reply', { + this.queueService.webhookDeliver(webhook, 'edited', { note: noteObj, }); } @@ -601,28 +664,6 @@ export class NoteEditService implements OnApplicationShutdown { } } - // If it is renote - if (data.renote) { - const type = data.text ? 'quote' : 'renote'; - - // Notify - if (data.renote.userHost === null) { - nm.push(data.renote.userId, type); - } - - // Publish event - if ((user.id !== data.renote.userId) && data.renote.userHost === null) { - this.globalEventService.publishMainStream(data.renote.userId, 'renote', noteObj); - - const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === data.renote!.userId && x.on.includes('renote')); - for (const webhook of webhooks) { - this.queueService.webhookDeliver(webhook, 'renote', { - note: noteObj, - }); - } - } - } - nm.notify(); //#region AP deliver @@ -657,7 +698,7 @@ export class NoteEditService implements OnApplicationShutdown { this.relayService.deliverToRelays(user, noteActivity); } - dm.execute(); + trackPromise(dm.execute()); })(); } //#endregion @@ -686,41 +727,30 @@ export class NoteEditService implements OnApplicationShutdown { } @bindThis - private isSensitive(note: Option, sensitiveWord: string[]): boolean { - if (sensitiveWord.length > 0) { - const text = note.cw ?? note.text ?? ''; - if (text === '') return false; - const matched = sensitiveWord.some(filter => { - // represents RegExp - const regexp = filter.match(/^\/(.+)\/(.*)$/); - // This should never happen due to input sanitisation. - if (!regexp) { - const words = filter.split(' '); - return words.every(keyword => text.includes(keyword)); - } - try { - return new RE2(regexp[1], regexp[2]).test(text); - } catch (err) { - // This should never happen due to input sanitisation. - return false; - } - }); - if (matched) return true; - } - return false; + private isQuote(note: Option): note is Option & { renote: MiNote } { + // sync with misc/is-quote.ts + return !!note.renote && (!!note.text || !!note.cw || (!!note.files && !!note.files.length) || !!note.poll); } @bindThis private async createMentionedEvents(mentionedUsers: MinimumUser[], note: MiNote, nm: NotificationManager) { for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) { - const isThreadMuted = await this.noteThreadMutingsRepository.exist({ + const isThreadMuted = await this.noteThreadMutingsRepository.exists({ where: { userId: u.id, threadId: note.threadId ?? note.id, }, }); - if (isThreadMuted) { + const [ + userIdsWhoMeMuting, + ] = u.id ? await Promise.all([ + this.cacheService.userMutingsCache.fetch(u.id), + ]) : [new Set<string>()]; + + const muted = isUserRelated(note, userIdsWhoMeMuting); + + if (isThreadMuted || muted) { continue; } @@ -728,17 +758,17 @@ export class NoteEditService implements OnApplicationShutdown { detail: true, }); - this.globalEventService.publishMainStream(u.id, 'mention', detailPackedNote); + this.globalEventService.publishMainStream(u.id, 'edited', detailPackedNote); - const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === u.id && x.on.includes('mention')); + const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === u.id && x.on.includes('edited')); for (const webhook of webhooks) { - this.queueService.webhookDeliver(webhook, 'mention', { + this.queueService.webhookDeliver(webhook, 'edited', { note: detailPackedNote, }); } // Create notification - nm.push(u.id, 'mention'); + nm.push(u.id, 'edited'); } } @@ -748,7 +778,7 @@ export class NoteEditService implements OnApplicationShutdown { const user = await this.usersRepository.findOneBy({ id: note.userId }); if (user == null) throw new Error('user not found'); - const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0) + const content = data.renote && !this.isQuote(data) ? this.apRendererService.renderAnnounce(data.renote.uri ? data.renote.uri : `${this.config.url}/notes/${data.renote.id}`, note) : this.apRendererService.renderUpdate(await this.apRendererService.renderUpNote(note, false), user); @@ -769,7 +799,7 @@ export class NoteEditService implements OnApplicationShutdown { const mentions = extractMentions(tokens); let mentionedUsers = (await Promise.all(mentions.map(m => this.remoteUserResolveService.resolveUser(m.username, m.host ?? user.host).catch(() => null), - ))).filter(x => x != null) as MiUser[]; + ))).filter(isNotNull) as MiUser[]; // Drop duplicate users mentionedUsers = mentionedUsers.filter((u, i, self) => @@ -782,6 +812,7 @@ export class NoteEditService implements OnApplicationShutdown { @bindThis private async pushToTl(note: MiNote, user: { id: MiUser['id']; host: MiUser['host']; }) { const meta = await this.metaService.fetch(); + if (!meta.enableFanoutTimeline) return; const r = this.redisForTimelines.pipeline(); @@ -825,7 +856,7 @@ export class NoteEditService implements OnApplicationShutdown { if (note.visibility === 'followers') { // TODO: é‡ãã†ã ã‹ã‚‰ä½•ã¨ã‹ã—ãŸã„ Set 使ã†ï¼Ÿ - userListMemberships = userListMemberships.filter(x => followings.some(f => f.followerId === x.userListUserId)); + userListMemberships = userListMemberships.filter(x => x.userListUserId === user.id || followings.some(f => f.followerId === x.userListUserId)); } // TODO: ã‚ã¾ã‚Šã«ã‚‚æ•°ãŒå¤šã„㨠redisPipeline.exec ã«å¤±æ•—ã™ã‚‹(ç†ç”±ã¯ä¸æ˜Ž)ãŸã‚ã€3万件程度を目安ã«åˆ†å‰²ã—ã¦å®Ÿè¡Œã™ã‚‹ã‚ˆã†ã«ã™ã‚‹ @@ -834,7 +865,7 @@ export class NoteEditService implements OnApplicationShutdown { if (note.visibility === 'specified' && !note.visibleUserIds.some(v => v === following.followerId)) continue; // 「自分自身ã¸ã®è¿”ä¿¡ or ãã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã¸ã®è¿”ä¿¡ã€ã®ã©ã¡ã‚‰ã§ã‚‚ãªã„å ´åˆ - if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === following.followerId)) { + if (isReply(note, following.followerId)) { if (!following.withReplies) continue; } @@ -848,11 +879,12 @@ export class NoteEditService implements OnApplicationShutdown { // ダイレクトã®ã¨ãã€ãã®ãƒªã‚¹ãƒˆãŒå¯¾è±¡å¤–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®å ´åˆ if ( note.visibility === 'specified' && + note.userId !== userListMembership.userListUserId && !note.visibleUserIds.some(v => v === userListMembership.userListUserId) ) continue; // 「自分自身ã¸ã®è¿”ä¿¡ or ãã®ãƒªã‚¹ãƒˆã®ä½œæˆè€…ã¸ã®è¿”ä¿¡ã€ã®ã©ã¡ã‚‰ã§ã‚‚ãªã„å ´åˆ - if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === userListMembership.userListUserId)) { + if (isReply(note, userListMembership.userListUserId)) { if (!userListMembership.withReplies) continue; } @@ -870,11 +902,14 @@ export class NoteEditService implements OnApplicationShutdown { } // 自分自身以外ã¸ã®è¿”ä¿¡ - if (note.replyId && note.replyUserId !== note.userId) { + if (isReply(note)) { this.fanoutTimelineService.push(`userTimelineWithReplies:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); if (note.visibility === 'public' && note.userHost == null) { this.fanoutTimelineService.push('localTimelineWithReplies', note.id, 300, r); + if (note.replyUserHost == null) { + this.fanoutTimelineService.push(`localTimelineWithReplyTo:${note.replyUserId}`, note.id, 300 / 10, r); + } } } else { this.fanoutTimelineService.push(`userTimeline:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); @@ -938,6 +973,23 @@ export class NoteEditService implements OnApplicationShutdown { } } + public async checkProhibitedWordsContain(content: Parameters<UtilityService['concatNoteContentsForKeyWordCheck']>[0], prohibitedWords?: string[]) { + if (prohibitedWords == null) { + prohibitedWords = (await this.metaService.fetch()).prohibitedWords; + } + + if ( + this.utilityService.isKeyWordIncluded( + this.utilityService.concatNoteContentsForKeyWordCheck(content), + prohibitedWords, + ) + ) { + return true; + } + + return false; + } + @bindThis public dispose(): void { this.#shutdownController.abort(); diff --git a/packages/backend/src/core/NotePiningService.ts b/packages/backend/src/core/NotePiningService.ts index 74e53c5c46f436a0136c720b06cf37c2a681a2d3..d38b48b65d45453fcd3b3fb55a3427b168ffd065 100644 --- a/packages/backend/src/core/NotePiningService.ts +++ b/packages/backend/src/core/NotePiningService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/NoteReadService.ts b/packages/backend/src/core/NoteReadService.ts index 03c1735e04f004ab7fb183fe37a1eb9344d8c962..320b23cc1a0c2330d0879ffae31b968c7181e65b 100644 --- a/packages/backend/src/core/NoteReadService.ts +++ b/packages/backend/src/core/NoteReadService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -14,6 +14,7 @@ import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { NoteUnreadsRepository, MutingsRepository, NoteThreadMutingsRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; +import { trackPromise } from '@/misc/promise-tracker.js'; @Injectable() export class NoteReadService implements OnApplicationShutdown { @@ -48,7 +49,7 @@ export class NoteReadService implements OnApplicationShutdown { //#endregion // スレッドミュート - const isThreadMuted = await this.noteThreadMutingsRepository.exist({ + const isThreadMuted = await this.noteThreadMutingsRepository.exists({ where: { userId: userId, threadId: note.threadId ?? note.id, @@ -65,11 +66,15 @@ export class NoteReadService implements OnApplicationShutdown { noteUserId: note.userId, }; - await this.noteUnreadsRepository.insert(unread); + /* we may be called from NoteEditService, for a note that's + already present in the `note_unread` table: `upsert` makes sure + we don't throw a "duplicate key" error, while still updating + the other columns if they've changed */ + await this.noteUnreadsRepository.upsert(unread, ['userId', 'noteId']); // 2秒経ã£ã¦ã‚‚æ—¢èªã«ãªã‚‰ãªã‹ã£ãŸã‚‰ã€Œæœªèªã®æŠ•ç¨¿ãŒã‚ã‚Šã¾ã™ã‚ˆã€ã‚¤ãƒ™ãƒ³ãƒˆã‚’発行ã™ã‚‹ setTimeout(2000, 'unread note', { signal: this.#shutdownController.signal }).then(async () => { - const exist = await this.noteUnreadsRepository.exist({ where: { id: unread.id } }); + const exist = await this.noteUnreadsRepository.exists({ where: { id: unread.id } }); if (!exist) return; @@ -87,46 +92,47 @@ export class NoteReadService implements OnApplicationShutdown { userId: MiUser['id'], notes: (MiNote | Packed<'Note'>)[], ): Promise<void> { - const readMentions: (MiNote | Packed<'Note'>)[] = []; - const readSpecifiedNotes: (MiNote | Packed<'Note'>)[] = []; + if (notes.length === 0) return; + + const noteIds = new Set<MiNote['id']>(); for (const note of notes) { if (note.mentions && note.mentions.includes(userId)) { - readMentions.push(note); + noteIds.add(note.id); } else if (note.visibleUserIds && note.visibleUserIds.includes(userId)) { - readSpecifiedNotes.push(note); + noteIds.add(note.id); } } - if ((readMentions.length > 0) || (readSpecifiedNotes.length > 0)) { - // Remove the record - await this.noteUnreadsRepository.delete({ - userId: userId, - noteId: In([...readMentions.map(n => n.id), ...readSpecifiedNotes.map(n => n.id)]), - }); + if (noteIds.size === 0) return; - // TODO: ↓ã¾ã¨ã‚ã¦ã‚¯ã‚¨ãƒªã—ãŸã„ + // Remove the record + await this.noteUnreadsRepository.delete({ + userId: userId, + noteId: In(Array.from(noteIds)), + }); - this.noteUnreadsRepository.countBy({ - userId: userId, - isMentioned: true, - }).then(mentionsCount => { - if (mentionsCount === 0) { - // å…¨ã¦æ—¢èªã«ãªã£ãŸã‚¤ãƒ™ãƒ³ãƒˆã‚’発行 - this.globalEventService.publishMainStream(userId, 'readAllUnreadMentions'); - } - }); - - this.noteUnreadsRepository.countBy({ - userId: userId, - isSpecified: true, - }).then(specifiedCount => { - if (specifiedCount === 0) { - // å…¨ã¦æ—¢èªã«ãªã£ãŸã‚¤ãƒ™ãƒ³ãƒˆã‚’発行 - this.globalEventService.publishMainStream(userId, 'readAllUnreadSpecifiedNotes'); - } - }); - } + // TODO: ↓ã¾ã¨ã‚ã¦ã‚¯ã‚¨ãƒªã—ãŸã„ + + trackPromise(this.noteUnreadsRepository.countBy({ + userId: userId, + isMentioned: true, + }).then(mentionsCount => { + if (mentionsCount === 0) { + // å…¨ã¦æ—¢èªã«ãªã£ãŸã‚¤ãƒ™ãƒ³ãƒˆã‚’発行 + this.globalEventService.publishMainStream(userId, 'readAllUnreadMentions'); + } + })); + + trackPromise(this.noteUnreadsRepository.countBy({ + userId: userId, + isSpecified: true, + }).then(specifiedCount => { + if (specifiedCount === 0) { + // å…¨ã¦æ—¢èªã«ãªã£ãŸã‚¤ãƒ™ãƒ³ãƒˆã‚’発行 + this.globalEventService.publishMainStream(userId, 'readAllUnreadSpecifiedNotes'); + } + })); } @bindThis diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index ad7be83e5b40a7240f9916f7d4f01dfe30b89f31..68ad92f3969053ca69ea25733f409bb0077527ad 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -20,6 +20,7 @@ import { CacheService } from '@/core/CacheService.js'; import type { Config } from '@/config.js'; import { UserListService } from '@/core/UserListService.js'; import type { FilterUnionByProperty } from '@/types.js'; +import { trackPromise } from '@/misc/promise-tracker.js'; @Injectable() export class NotificationService implements OnApplicationShutdown { @@ -74,7 +75,18 @@ export class NotificationService implements OnApplicationShutdown { } @bindThis - public async createNotification<T extends MiNotification['type']>( + public createNotification<T extends MiNotification['type']>( + notifieeId: MiUser['id'], + type: T, + data: Omit<FilterUnionByProperty<MiNotification, 'type', T>, 'type' | 'id' | 'createdAt' | 'notifierId'>, + notifierId?: MiUser['id'] | null, + ) { + trackPromise( + this.#createNotificationInternal(notifieeId, type, data, notifierId), + ); + } + + async #createNotificationInternal<T extends MiNotification['type']>( notifieeId: MiUser['id'], type: T, data: Omit<FilterUnionByProperty<MiNotification, 'type', T>, 'type' | 'id' | 'createdAt' | 'notifierId'>, @@ -110,6 +122,14 @@ export class NotificationService implements OnApplicationShutdown { return null; } } else if (recieveConfig?.type === 'mutualFollow') { + const [isFollowing, isFollower] = await Promise.all([ + this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => Object.hasOwn(followings, notifierId)), + this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => Object.hasOwn(followings, notifieeId)), + ]); + if (!(isFollowing && isFollower)) { + return null; + } + } else if (recieveConfig?.type === 'followingOrFollower') { const [isFollowing, isFollower] = await Promise.all([ this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => Object.hasOwn(followings, notifierId)), this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => Object.hasOwn(followings, notifieeId)), @@ -143,6 +163,8 @@ export class NotificationService implements OnApplicationShutdown { const packed = await this.notificationEntityService.pack(notification, notifieeId, {}); + if (packed == null) return null; + // Publish notification event this.globalEventService.publishMainStream(notifieeId, 'notification', packed); @@ -192,6 +214,15 @@ export class NotificationService implements OnApplicationShutdown { */ } + @bindThis + public async flushAllNotifications(userId: MiUser['id']) { + await Promise.all([ + this.redisClient.del(`notificationTimeline:${userId}`), + this.redisClient.del(`latestReadNotification:${userId}`), + ]); + this.globalEventService.publishMainStream(userId, 'notificationFlushed'); + } + @bindThis public dispose(): void { this.#shutdownController.abort(); diff --git a/packages/backend/src/core/PollService.ts b/packages/backend/src/core/PollService.ts index 9e1b5ca78acce07a8d1780a263306b9ce935fcd0..6c96ab16cff0e7e8ec2564450d520572993d285a 100644 --- a/packages/backend/src/core/PollService.ts +++ b/packages/backend/src/core/PollService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/ProxyAccountService.ts b/packages/backend/src/core/ProxyAccountService.ts index b1bc60701ba03899609d3ae1f24f574f60ee9f10..71d663bf90607b225166505636ee61b8206cea52 100644 --- a/packages/backend/src/core/ProxyAccountService.ts +++ b/packages/backend/src/core/ProxyAccountService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts index 40d1deceebb0c02894084622f9425199b381b1ea..3b706d985433bc6d35a89135cf40e2c850a347fe 100644 --- a/packages/backend/src/core/PushNotificationService.ts +++ b/packages/backend/src/core/PushNotificationService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -115,12 +115,19 @@ export class PushNotificationService implements OnApplicationShutdown { endpoint: subscription.endpoint, auth: subscription.auth, publickey: subscription.publickey, + }).then(() => { + this.refreshCache(userId); }); } }); } } + @bindThis + public refreshCache(userId: string): void { + this.subscriptionsCache.refresh(userId); + } + @bindThis public dispose(): void { this.subscriptionsCache.dispose(); diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index f006ed494420baaa6134bfbc53dfab26e2e05872..c4feeaf9717e7ddd56e7e64a36130c1b2e35ca5d 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -212,8 +212,8 @@ export class QueryService { // ã¾ãŸã¯ 自分自身 .orWhere('note.userId = :meId') // ã¾ãŸã¯ 自分宛㦠- .orWhere(':meId = ANY(note.visibleUserIds)') - .orWhere(':meId = ANY(note.mentions)') + .orWhere(':meIdAsList <@ note.visibleUserIds') + .orWhere(':meIdAsList <@ note.mentions') .orWhere(new Brackets(qb => { qb // ã¾ãŸã¯ フォãƒãƒ¯ãƒ¼å®›ã¦ã®æŠ•ç¨¿ã§ã‚り〠@@ -228,7 +228,7 @@ export class QueryService { })); })); - q.setParameters({ meId: me.id }); + q.setParameters({ meId: me.id, meIdAsList: [me.id] }); } } diff --git a/packages/backend/src/core/QueueModule.ts b/packages/backend/src/core/QueueModule.ts index 4444dc978790f9f11763b9e68c28a510abf2ad35..216734e9e5e414cbcb6204c153e9e8f486426cc5 100644 --- a/packages/backend/src/core/QueueModule.ts +++ b/packages/backend/src/core/QueueModule.ts @@ -1,14 +1,14 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import { setTimeout } from 'node:timers/promises'; import { Inject, Module, OnApplicationShutdown } from '@nestjs/common'; import * as Bull from 'bullmq'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { QUEUE, baseQueueOptions } from '@/queue/const.js'; +import { allSettled } from '@/misc/promise-tracker.js'; import type { Provider } from '@nestjs/common'; import type { DeliverJobData, InboxJobData, EndedPollNotificationJobData, WebhookDeliverJobData, RelationshipJobData } from '../queue/types.js'; @@ -106,14 +106,9 @@ export class QueueModule implements OnApplicationShutdown { ) {} public async dispose(): Promise<void> { - if (process.env.NODE_ENV === 'test') { - // XXX: - // Shutting down the existing connections causes errors on Jest as - // Misskey has asynchronous postgres/redis connections that are not - // awaited. - // Let's wait for some random time for them to finish. - await setTimeout(5000); - } + // Wait for all potential queue jobs + await allSettled(); + // And then close all queues await Promise.all([ this.systemQueue.close(), this.endedPollNotificationQueue.close(), diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts index 2ee61eb5497f44927de388ce3244d6c99ec70cb4..103813acf293b962968c3ec24ab0aebf2c4a0772 100644 --- a/packages/backend/src/core/QueueService.ts +++ b/packages/backend/src/core/QueueService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -17,6 +17,7 @@ import type { DbJobData, DeliverJobData, RelationshipJobData, ThinUser } from '. import type httpSignature from '@peertube/http-signature'; import type * as Bull from 'bullmq'; import { MiNote } from '@/models/Note.js'; +import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js'; @Injectable() export class QueueService { @@ -75,11 +76,15 @@ export class QueueService { if (content == null) return null; if (to == null) return null; + const contentBody = JSON.stringify(content); + const digest = ApRequestCreator.createDigest(contentBody); + const data: DeliverJobData = { user: { id: user.id, }, - content, + content: contentBody, + digest, to, isSharedInbox, }; @@ -104,6 +109,8 @@ export class QueueService { @bindThis public async deliverMany(user: ThinUser, content: IActivity | null, inboxes: Map<string, boolean>) { if (content == null) return null; + const contentBody = JSON.stringify(content); + const digest = ApRequestCreator.createDigest(contentBody); const opts = { attempts: this.config.deliverJobMaxAttempts ?? 12, @@ -118,7 +125,8 @@ export class QueueService { name: d[0], data: { user, - content, + content: contentBody, + digest, to: d[0], isSharedInbox: d[1], } as DeliverJobData, @@ -185,6 +193,16 @@ export class QueueService { }); } + @bindThis + public createExportClipsJob(user: ThinUser) { + return this.dbQueue.add('exportClips', { + user: { id: user.id }, + }, { + removeOnComplete: true, + removeOnFail: true, + }); + } + @bindThis public createExportFavoritesJob(user: ThinUser) { return this.dbQueue.add('exportFavorites', { diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index 0daee34ce5f180bd55bd6af066a366d6791aa12e..90586b500e7135e4b4cc472cff53a299ca30eb80 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -28,13 +28,14 @@ import { UserBlockingService } from '@/core/UserBlockingService.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { RoleService } from '@/core/RoleService.js'; import { FeaturedService } from '@/core/FeaturedService.js'; +import { trackPromise } from '@/misc/promise-tracker.js'; -const FALLBACK = 'â¤'; +const FALLBACK = '\u2764'; const PER_NOTE_REACTION_USER_PAIR_CACHE_MAX = 16; const legacies: Record<string, string> = { 'like': 'ðŸ‘', - 'love': 'â¤', // ã“ã“ã«è¨˜è¿°ã™ã‚‹å ´åˆã¯ç•°ä½“å—セレクタを入れãªã„ + 'love': '\u2764', // ãƒãƒ¼ãƒˆã€ç•°ä½“å—セレクタを入れãªã„ 'laugh': '😆', 'hmm': '🤔', 'surprise': '😮', @@ -122,7 +123,7 @@ export class ReactionService { let reaction = _reaction ?? FALLBACK; if (note.reactionAcceptance === 'likeOnly' || ((note.reactionAcceptance === 'likeOnlyForRemote' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote') && (user.host != null))) { - reaction = 'â¤ï¸'; + reaction = '\u2764'; } else if (_reaction) { const custom = reaction.match(isCustomEmojiRegexp); if (custom) { @@ -247,7 +248,7 @@ export class ReactionService { // リアクションã•ã‚ŒãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ãªã‚‰é€šçŸ¥ã‚’ä½œæˆ if (note.userHost === null) { - const isThreadMuted = await this.noteThreadMutingsRepository.exist({ + const isThreadMuted = await this.noteThreadMutingsRepository.exists({ where: { userId: note.userId, threadId: note.threadId ?? note.id, @@ -280,7 +281,7 @@ export class ReactionService { } } - dm.execute(); + trackPromise(dm.execute()); } //#endregion } @@ -328,40 +329,41 @@ export class ReactionService { dm.addDirectRecipe(reactee as MiRemoteUser); } dm.addFollowersRecipe(); - dm.execute(); + trackPromise(dm.execute()); } //#endregion } + /** + * æ–‡å—列タイプã®ãƒ¬ã‚¬ã‚·ãƒ¼ãªå½¢å¼ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’ç¾åœ¨ã®å½¢å¼ã«å¤‰æ›ã—ã¤ã¤ã€ + * データベース上ã«ã¯å˜åœ¨ã™ã‚‹ã€Œ0個ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ãŒã¤ã„ã¦ã„ã‚‹ã€ã¨ã„ã†æƒ…å ±ã‚’å‰Šé™¤ã™ã‚‹ã€‚ + */ @bindThis - public convertLegacyReactions(reactions: Record<string, number>) { - const _reactions = {} as Record<string, number>; + public convertLegacyReactions(reactions: MiNote['reactions']): MiNote['reactions'] { + return Object.entries(reactions) + .filter(([, count]) => { + // `ReactionService.prototype.delete`ã§ã¯ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³å‰Šé™¤æ™‚ã«ã€ + // `MiNote['reactions']`ã®ã‚¨ãƒ³ãƒˆãƒªã®å€¤ã‚’デクリメントã—ã¦ã„ã‚‹ãŒã€ + // デクリメントã—ã¦ã„ã‚‹ã ã‘ãªã®ã§ã‚¨ãƒ³ãƒˆãƒªè‡ªä½“ã¯0を値ã¨ã—ã¦æŒã¤å½¢ã§æ®‹ã‚Šç¶šã‘る。 + // ãã®ãŸã‚ã€ã“ã®å‡¦ç†ãŒãªã‘ã‚Œã°ã€ã€Œ0個ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ãŒã¤ã„ã¦ã„ã‚‹ã€ã¨ã„ã†ã“ã¨ã«ãªã£ã¦ã—ã¾ã†ã€‚ + return count > 0; + }) + .map(([reaction, count]) => { + // unchecked indexed access + const convertedReaction = legacies[reaction] as string | undefined; - for (const reaction of Object.keys(reactions)) { - if (reactions[reaction] <= 0) continue; + const key = this.decodeReaction(convertedReaction ?? reaction).reaction; - if (Object.keys(legacies).includes(reaction)) { - if (_reactions[legacies[reaction]]) { - _reactions[legacies[reaction]] += reactions[reaction]; - } else { - _reactions[legacies[reaction]] = reactions[reaction]; - } - } else { - if (_reactions[reaction]) { - _reactions[reaction] += reactions[reaction]; - } else { - _reactions[reaction] = reactions[reaction]; - } - } - } - - const _reactions2 = {} as Record<string, number>; + return [key, count] as const; + }) + .reduce<MiNote['reactions']>((acc, [key, count]) => { + // unchecked indexed access + const prevCount = acc[key] as number | undefined; - for (const reaction of Object.keys(_reactions)) { - _reactions2[this.decodeReaction(reaction).reaction] = _reactions[reaction]; - } + acc[key] = (prevCount ?? 0) + count; - return _reactions2; + return acc; + }, {}); } @bindThis diff --git a/packages/backend/src/core/RegistryApiService.ts b/packages/backend/src/core/RegistryApiService.ts index d340c5e480adf9b84f5bd4cbf1a1723077e5931c..2c8877d8a8d9bd31b1669fb5f2e79970c4a0173e 100644 --- a/packages/backend/src/core/RegistryApiService.ts +++ b/packages/backend/src/core/RegistryApiService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts index d40cd080c725a1e0f14fda2d6cc3321297b541e8..e9dc9b57afef6415d7eda5ccdac9a974c7d40fb0 100644 --- a/packages/backend/src/core/RelayService.ts +++ b/packages/backend/src/core/RelayService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/RemoteLoggerService.ts b/packages/backend/src/core/RemoteLoggerService.ts index 5d13988ed7d6d0d55024f38b5cf12be04a947c62..413b03bb56d9a742cb2f9f140c1e2e8778e8ce9d 100644 --- a/packages/backend/src/core/RemoteLoggerService.ts +++ b/packages/backend/src/core/RemoteLoggerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/RemoteUserResolveService.ts b/packages/backend/src/core/RemoteUserResolveService.ts index 75c5f14aa41380473739d59fab473df568eb3a46..f5a55eb8bcd4bf8ea8eeb505d6896b122fa77f30 100644 --- a/packages/backend/src/core/RemoteUserResolveService.ts +++ b/packages/backend/src/core/RemoteUserResolveService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/ReversiService.ts b/packages/backend/src/core/ReversiService.ts new file mode 100644 index 0000000000000000000000000000000000000000..53a7234823cf597fa04638f409a811012795dcfa --- /dev/null +++ b/packages/backend/src/core/ReversiService.ts @@ -0,0 +1,615 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import * as Redis from 'ioredis'; +import { ModuleRef } from '@nestjs/core'; +import * as Reversi from 'misskey-reversi'; +import { IsNull, LessThan, MoreThan } from 'typeorm'; +import type { + MiReversiGame, + ReversiGamesRepository, +} from '@/models/_.js'; +import type { MiUser } from '@/models/User.js'; +import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; +import { CacheService } from '@/core/CacheService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { IdService } from '@/core/IdService.js'; +import { NotificationService } from '@/core/NotificationService.js'; +import { Serialized } from '@/types.js'; +import { ReversiGameEntityService } from './entities/ReversiGameEntityService.js'; +import type { OnApplicationShutdown, OnModuleInit } from '@nestjs/common'; + +const INVITATION_TIMEOUT_MS = 1000 * 20; // 20sec + +@Injectable() +export class ReversiService implements OnApplicationShutdown, OnModuleInit { + private notificationService: NotificationService; + + constructor( + private moduleRef: ModuleRef, + + @Inject(DI.redis) + private redisClient: Redis.Redis, + + @Inject(DI.reversiGamesRepository) + private reversiGamesRepository: ReversiGamesRepository, + + private cacheService: CacheService, + private userEntityService: UserEntityService, + private globalEventService: GlobalEventService, + private reversiGameEntityService: ReversiGameEntityService, + private idService: IdService, + ) { + } + + async onModuleInit() { + this.notificationService = this.moduleRef.get(NotificationService.name); + } + + @bindThis + private async cacheGame(game: MiReversiGame) { + await this.redisClient.setex(`reversi:game:cache:${game.id}`, 60 * 60, JSON.stringify(game)); + } + + @bindThis + private async deleteGameCache(gameId: MiReversiGame['id']) { + await this.redisClient.del(`reversi:game:cache:${gameId}`); + } + + @bindThis + private getBakeProps(game: MiReversiGame) { + return { + startedAt: game.startedAt, + endedAt: game.endedAt, + // ゲームã®é€”ä¸ã‹ã‚‰ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒå¤‰ã‚ã‚‹ã“ã¨ã¯ç„¡ã„ã®ã§ + //user1Id: game.user1Id, + //user2Id: game.user2Id, + user1Ready: game.user1Ready, + user2Ready: game.user2Ready, + black: game.black, + isStarted: game.isStarted, + isEnded: game.isEnded, + winnerId: game.winnerId, + surrenderedUserId: game.surrenderedUserId, + timeoutUserId: game.timeoutUserId, + isLlotheo: game.isLlotheo, + canPutEverywhere: game.canPutEverywhere, + loopedBoard: game.loopedBoard, + timeLimitForEachTurn: game.timeLimitForEachTurn, + logs: game.logs, + map: game.map, + bw: game.bw, + crc32: game.crc32, + noIrregularRules: game.noIrregularRules, + } satisfies Partial<MiReversiGame>; + } + + @bindThis + public async matchSpecificUser(me: MiUser, targetUser: MiUser, multiple = false): Promise<MiReversiGame | null> { + if (targetUser.id === me.id) { + throw new Error('You cannot match yourself.'); + } + + if (!multiple) { + // æ—¢ã«ãƒžãƒƒãƒã—ã¦ã„る対局ãŒç„¡ã„ã‹æŽ¢ã™(3分以内) + const games = await this.reversiGamesRepository.find({ + where: [ + { id: MoreThan(this.idService.gen(Date.now() - 1000 * 60 * 3)), user1Id: me.id, user2Id: targetUser.id, isStarted: false }, + { id: MoreThan(this.idService.gen(Date.now() - 1000 * 60 * 3)), user1Id: targetUser.id, user2Id: me.id, isStarted: false }, + ], + relations: ['user1', 'user2'], + order: { id: 'DESC' }, + }); + if (games.length > 0) { + return games[0]; + } + } + + //#region 相手ã‹ã‚‰æ—¢ã«æ‹›å¾…ã•ã‚Œã¦ãªã„ã‹ç¢ºèª + const invitations = await this.redisClient.zrange( + `reversi:matchSpecific:${me.id}`, + Date.now() - INVITATION_TIMEOUT_MS, + '+inf', + 'BYSCORE'); + + if (invitations.includes(targetUser.id)) { + await this.redisClient.zrem(`reversi:matchSpecific:${me.id}`, targetUser.id); + + const game = await this.matched(targetUser.id, me.id, { + noIrregularRules: false, + }); + + return game; + } + //#endregion + + const redisPipeline = this.redisClient.pipeline(); + redisPipeline.zadd(`reversi:matchSpecific:${targetUser.id}`, Date.now(), me.id); + redisPipeline.expire(`reversi:matchSpecific:${targetUser.id}`, 120, 'NX'); + await redisPipeline.exec(); + + this.globalEventService.publishReversiStream(targetUser.id, 'invited', { + user: await this.userEntityService.pack(me, targetUser), + }); + + return null; + } + + @bindThis + public async matchAnyUser(me: MiUser, options: { noIrregularRules: boolean }, multiple = false): Promise<MiReversiGame | null> { + if (!multiple) { + // æ—¢ã«ãƒžãƒƒãƒã—ã¦ã„る対局ãŒç„¡ã„ã‹æŽ¢ã™(3分以内) + const games = await this.reversiGamesRepository.find({ + where: [ + { id: MoreThan(this.idService.gen(Date.now() - 1000 * 60 * 3)), user1Id: me.id, isStarted: false }, + { id: MoreThan(this.idService.gen(Date.now() - 1000 * 60 * 3)), user2Id: me.id, isStarted: false }, + ], + relations: ['user1', 'user2'], + order: { id: 'DESC' }, + }); + if (games.length > 0) { + return games[0]; + } + } + + //#region ã¾ãšè‡ªåˆ†å®›ã¦ã®æ‹›å¾…を探㙠+ const invitations = await this.redisClient.zrange( + `reversi:matchSpecific:${me.id}`, + Date.now() - INVITATION_TIMEOUT_MS, + '+inf', + 'BYSCORE'); + + if (invitations.length > 0) { + const invitorId = invitations[Math.floor(Math.random() * invitations.length)]; + await this.redisClient.zrem(`reversi:matchSpecific:${me.id}`, invitorId); + + const game = await this.matched(invitorId, me.id, { + noIrregularRules: false, + }); + + return game; + } + //#endregion + + const matchings = await this.redisClient.zrange( + 'reversi:matchAny', + 0, + 2, // 自分自身ã®IDãŒå…¥ã£ã¦ã„ã‚‹å ´åˆã‚‚ã‚ã‚‹ã®ã§2ã¤å–å¾— + 'REV'); + + const items = matchings.filter(id => !id.startsWith(me.id)); + + if (items.length > 0) { + const [matchedUserId, option] = items[0].split(':'); + + await this.redisClient.zrem('reversi:matchAny', + me.id, + matchedUserId, + me.id + ':noIrregularRules', + matchedUserId + ':noIrregularRules'); + + const game = await this.matched(matchedUserId, me.id, { + noIrregularRules: options.noIrregularRules || option === 'noIrregularRules', + }); + + return game; + } else { + const redisPipeline = this.redisClient.pipeline(); + if (options.noIrregularRules) { + redisPipeline.zadd('reversi:matchAny', Date.now(), me.id + ':noIrregularRules'); + } else { + redisPipeline.zadd('reversi:matchAny', Date.now(), me.id); + } + redisPipeline.expire('reversi:matchAny', 15, 'NX'); + await redisPipeline.exec(); + return null; + } + } + + @bindThis + public async matchSpecificUserCancel(user: MiUser, targetUserId: MiUser['id']) { + await this.redisClient.zrem(`reversi:matchSpecific:${targetUserId}`, user.id); + } + + @bindThis + public async matchAnyUserCancel(user: MiUser) { + await this.redisClient.zrem('reversi:matchAny', user.id, user.id + ':noIrregularRules'); + } + + @bindThis + public async cleanOutdatedGames() { + await this.reversiGamesRepository.delete({ + id: LessThan(this.idService.gen(Date.now() - 1000 * 60 * 10)), + isStarted: false, + }); + } + + @bindThis + public async gameReady(gameId: MiReversiGame['id'], user: MiUser, ready: boolean) { + const game = await this.get(gameId); + if (game == null) throw new Error('game not found'); + if (game.isStarted) return; + + let isBothReady = false; + + if (game.user1Id === user.id) { + const updatedGame = { + ...game, + user1Ready: ready, + }; + this.cacheGame(updatedGame); + + this.globalEventService.publishReversiGameStream(game.id, 'changeReadyStates', { + user1: ready, + user2: updatedGame.user2Ready, + }); + + if (ready && updatedGame.user2Ready) isBothReady = true; + } else if (game.user2Id === user.id) { + const updatedGame = { + ...game, + user2Ready: ready, + }; + this.cacheGame(updatedGame); + + this.globalEventService.publishReversiGameStream(game.id, 'changeReadyStates', { + user1: updatedGame.user1Ready, + user2: ready, + }); + + if (ready && updatedGame.user1Ready) isBothReady = true; + } else { + return; + } + + if (isBothReady) { + // 3秒後ã€ä¸¡è€…readyãªã‚‰ã‚²ãƒ¼ãƒ 開始 + setTimeout(async () => { + const freshGame = await this.get(game.id); + if (freshGame == null || freshGame.isStarted || freshGame.isEnded) return; + if (!freshGame.user1Ready || !freshGame.user2Ready) return; + + this.startGame(freshGame); + }, 3000); + } + } + + @bindThis + private async matched(parentId: MiUser['id'], childId: MiUser['id'], options: { noIrregularRules: boolean; }): Promise<MiReversiGame> { + const game = await this.reversiGamesRepository.insert({ + id: this.idService.gen(), + user1Id: parentId, + user2Id: childId, + user1Ready: false, + user2Ready: false, + isStarted: false, + isEnded: false, + logs: [], + map: Reversi.maps.eighteight.data, + bw: 'random', + isLlotheo: false, + noIrregularRules: options.noIrregularRules, + }).then(x => this.reversiGamesRepository.findOneOrFail({ + where: { id: x.identifiers[0].id }, + relations: ['user1', 'user2'], + })); + this.cacheGame(game); + + const packed = await this.reversiGameEntityService.packDetail(game); + this.globalEventService.publishReversiStream(parentId, 'matched', { game: packed }); + + return game; + } + + @bindThis + private async startGame(game: MiReversiGame) { + let bw: number; + if (game.bw === 'random') { + bw = Math.random() > 0.5 ? 1 : 2; + } else { + bw = parseInt(game.bw, 10); + } + + const engine = new Reversi.Game(game.map, { + isLlotheo: game.isLlotheo, + canPutEverywhere: game.canPutEverywhere, + loopedBoard: game.loopedBoard, + }); + + const crc32 = engine.calcCrc32().toString(); + + const updatedGame = await this.reversiGamesRepository.createQueryBuilder().update() + .set({ + ...this.getBakeProps(game), + startedAt: new Date(), + isStarted: true, + black: bw, + map: game.map, + crc32, + }) + .where('id = :id', { id: game.id }) + .returning('*') + .execute() + .then((response) => response.raw[0]); + // ã‚ャッシュ効率化ã®ãŸã‚ã«ãƒ¦ãƒ¼ã‚¶ãƒ¼æƒ…å ±ã¯å†åˆ©ç”¨ + updatedGame.user1 = game.user1; + updatedGame.user2 = game.user2; + this.cacheGame(updatedGame); + + //#region 盤é¢ã«æœ€åˆã‹ã‚‰çŸ³ãŒãªã„ãªã©ã—ã¦å§‹ã¾ã£ãŸçž¬é–“ã«å‹æ•—ãŒæ±ºå®šã™ã‚‹å ´åˆãŒã‚ã‚‹ã®ã§ãã®å‡¦ç† + if (engine.isEnded) { + let winnerId; + if (engine.winner === true) { + winnerId = bw === 1 ? updatedGame.user1Id : updatedGame.user2Id; + } else if (engine.winner === false) { + winnerId = bw === 1 ? updatedGame.user2Id : updatedGame.user1Id; + } else { + winnerId = null; + } + + await this.endGame(updatedGame, winnerId, null); + + return; + } + //#endregion + + this.redisClient.setex(`reversi:game:turnTimer:${game.id}:1`, updatedGame.timeLimitForEachTurn, ''); + + this.globalEventService.publishReversiGameStream(game.id, 'started', { + game: await this.reversiGameEntityService.packDetail(updatedGame), + }); + } + + @bindThis + private async endGame(game: MiReversiGame, winnerId: MiUser['id'] | null, reason: 'surrender' | 'timeout' | null) { + const updatedGame = await this.reversiGamesRepository.createQueryBuilder().update() + .set({ + ...this.getBakeProps(game), + isEnded: true, + endedAt: new Date(), + winnerId: winnerId, + surrenderedUserId: reason === 'surrender' ? (winnerId === game.user1Id ? game.user2Id : game.user1Id) : null, + timeoutUserId: reason === 'timeout' ? (winnerId === game.user1Id ? game.user2Id : game.user1Id) : null, + }) + .where('id = :id', { id: game.id }) + .returning('*') + .execute() + .then((response) => response.raw[0]); + // ã‚ャッシュ効率化ã®ãŸã‚ã«ãƒ¦ãƒ¼ã‚¶ãƒ¼æƒ…å ±ã¯å†åˆ©ç”¨ + updatedGame.user1 = game.user1; + updatedGame.user2 = game.user2; + this.cacheGame(updatedGame); + + this.globalEventService.publishReversiGameStream(game.id, 'ended', { + winnerId: winnerId, + game: await this.reversiGameEntityService.packDetail(updatedGame), + }); + } + + @bindThis + public async getInvitations(user: MiUser): Promise<MiUser['id'][]> { + const invitations = await this.redisClient.zrange( + `reversi:matchSpecific:${user.id}`, + Date.now() - INVITATION_TIMEOUT_MS, + '+inf', + 'BYSCORE'); + return invitations; + } + + @bindThis + public async updateSettings(gameId: MiReversiGame['id'], user: MiUser, key: string, value: any) { + const game = await this.get(gameId); + if (game == null) throw new Error('game not found'); + if (game.isStarted) return; + if ((game.user1Id !== user.id) && (game.user2Id !== user.id)) return; + if ((game.user1Id === user.id) && game.user1Ready) return; + if ((game.user2Id === user.id) && game.user2Ready) return; + + if (!['map', 'bw', 'isLlotheo', 'canPutEverywhere', 'loopedBoard', 'timeLimitForEachTurn'].includes(key)) return; + + // TODO: ã‚ˆã‚ŠåŽ³æ ¼ãªãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ + + const updatedGame = { + ...game, + [key]: value, + }; + this.cacheGame(updatedGame); + + this.globalEventService.publishReversiGameStream(game.id, 'updateSettings', { + userId: user.id, + key: key, + value: value, + }); + } + + @bindThis + public async putStoneToGame(gameId: MiReversiGame['id'], user: MiUser, pos: number, id?: string | null) { + const game = await this.get(gameId); + if (game == null) throw new Error('game not found'); + if (!game.isStarted) return; + if (game.isEnded) return; + if ((game.user1Id !== user.id) && (game.user2Id !== user.id)) return; + + const myColor = + ((game.user1Id === user.id) && game.black === 1) || ((game.user2Id === user.id) && game.black === 2) + ? true + : false; + + const engine = Reversi.Serializer.restoreGame({ + map: game.map, + isLlotheo: game.isLlotheo, + canPutEverywhere: game.canPutEverywhere, + loopedBoard: game.loopedBoard, + logs: game.logs, + }); + + if (engine.turn !== myColor) return; + if (!engine.canPut(myColor, pos)) return; + + engine.putStone(pos); + + const logs = Reversi.Serializer.deserializeLogs(game.logs); + + const log = { + time: Date.now(), + player: myColor, + operation: 'put', + pos, + } as const; + + logs.push(log); + + const serializeLogs = Reversi.Serializer.serializeLogs(logs); + + const crc32 = engine.calcCrc32().toString(); + + const updatedGame = { + ...game, + crc32, + logs: serializeLogs, + }; + this.cacheGame(updatedGame); + + this.globalEventService.publishReversiGameStream(game.id, 'log', { + ...log, + id: id ?? null, + }); + + if (engine.isEnded) { + let winnerId; + if (engine.winner === true) { + winnerId = game.black === 1 ? game.user1Id : game.user2Id; + } else if (engine.winner === false) { + winnerId = game.black === 1 ? game.user2Id : game.user1Id; + } else { + winnerId = null; + } + + await this.endGame(updatedGame, winnerId, null); + } else { + this.redisClient.setex(`reversi:game:turnTimer:${game.id}:${engine.turn ? '1' : '0'}`, updatedGame.timeLimitForEachTurn, ''); + } + } + + @bindThis + public async surrender(gameId: MiReversiGame['id'], user: MiUser) { + const game = await this.get(gameId); + if (game == null) throw new Error('game not found'); + if (game.isEnded) return; + if ((game.user1Id !== user.id) && (game.user2Id !== user.id)) return; + + const winnerId = game.user1Id === user.id ? game.user2Id : game.user1Id; + + await this.endGame(game, winnerId, 'surrender'); + } + + @bindThis + public async checkTimeout(gameId: MiReversiGame['id']) { + const game = await this.get(gameId); + if (game == null) throw new Error('game not found'); + if (game.isEnded) return; + + const engine = Reversi.Serializer.restoreGame({ + map: game.map, + isLlotheo: game.isLlotheo, + canPutEverywhere: game.canPutEverywhere, + loopedBoard: game.loopedBoard, + logs: game.logs, + }); + + if (engine.turn == null) return; + + const timer = await this.redisClient.exists(`reversi:game:turnTimer:${game.id}:${engine.turn ? '1' : '0'}`); + + if (timer === 0) { + const winnerId = engine.turn ? (game.black === 1 ? game.user2Id : game.user1Id) : (game.black === 1 ? game.user1Id : game.user2Id); + + await this.endGame(game, winnerId, 'timeout'); + } + } + + @bindThis + public async cancelGame(gameId: MiReversiGame['id'], user: MiUser) { + const game = await this.get(gameId); + if (game == null) throw new Error('game not found'); + if (game.isStarted) return; + if ((game.user1Id !== user.id) && (game.user2Id !== user.id)) return; + + await this.reversiGamesRepository.delete(game.id); + this.deleteGameCache(game.id); + + this.globalEventService.publishReversiGameStream(game.id, 'canceled', { + userId: user.id, + }); + } + + @bindThis + public async get(id: MiReversiGame['id']): Promise<MiReversiGame | null> { + const cached = await this.redisClient.get(`reversi:game:cache:${id}`); + if (cached != null) { + // TODO: ã“ã®è¾ºã‚Šã®ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºå‡¦ç†ã‚’ã©ã“ã‹åˆ¥ã®ã‚µãƒ¼ãƒ“スã«åˆ‡ã‚Šå‡ºã—ãŸã„ + const parsed = JSON.parse(cached) as Serialized<MiReversiGame>; + return { + ...parsed, + startedAt: parsed.startedAt != null ? new Date(parsed.startedAt) : null, + endedAt: parsed.endedAt != null ? new Date(parsed.endedAt) : null, + user1: parsed.user1 != null ? { + ...parsed.user1, + avatar: null, + banner: null, + background: null, + updatedAt: parsed.user1.updatedAt != null ? new Date(parsed.user1.updatedAt) : null, + lastActiveDate: parsed.user1.lastActiveDate != null ? new Date(parsed.user1.lastActiveDate) : null, + lastFetchedAt: parsed.user1.lastFetchedAt != null ? new Date(parsed.user1.lastFetchedAt) : null, + movedAt: parsed.user1.movedAt != null ? new Date(parsed.user1.movedAt) : null, + } : null, + user2: parsed.user2 != null ? { + ...parsed.user2, + avatar: null, + banner: null, + background: null, + updatedAt: parsed.user2.updatedAt != null ? new Date(parsed.user2.updatedAt) : null, + lastActiveDate: parsed.user2.lastActiveDate != null ? new Date(parsed.user2.lastActiveDate) : null, + lastFetchedAt: parsed.user2.lastFetchedAt != null ? new Date(parsed.user2.lastFetchedAt) : null, + movedAt: parsed.user2.movedAt != null ? new Date(parsed.user2.movedAt) : null, + } : null, + }; + } else { + const game = await this.reversiGamesRepository.findOne({ + where: { id }, + relations: ['user1', 'user2'], + }); + if (game == null) return null; + + this.cacheGame(game); + + return game; + } + } + + @bindThis + public async checkCrc(gameId: MiReversiGame['id'], crc32: string | number) { + const game = await this.get(gameId); + if (game == null) throw new Error('game not found'); + + if (crc32.toString() !== game.crc32) { + return game; + } else { + return null; + } + } + + @bindThis + public dispose(): void { + } + + @bindThis + public onApplicationShutdown(signal?: string | undefined): void { + this.dispose(); + } +} diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index dcd9d7399f2a266f52103a0fe523aab209aaa5a1..2059a6e784e33a8b765ac2f278682a4939032a86 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -36,6 +36,7 @@ export type RolePolicies = { ltlAvailable: boolean; btlAvailable: boolean; canPublicNote: boolean; + mentionLimit: number; canInvite: boolean; inviteLimit: number; inviteLimitCycle: number; @@ -65,6 +66,7 @@ export const DEFAULT_POLICIES: RolePolicies = { ltlAvailable: true, btlAvailable: false, canPublicNote: true, + mentionLimit: 20, canInvite: false, inviteLimit: 0, inviteLimitCycle: 60 * 24 * 7, @@ -181,9 +183,11 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { case 'userRoleAssigned': { const cached = this.roleAssignmentByUserIdCache.get(body.userId); if (cached) { - cached.push({ + cached.push({ // TODO: ã“ã®ã‚ãŸã‚Šã®ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºå‡¦ç†ã¯å„modelファイル内ã«é–¢æ•°ã¨ã—ã¦exportã—ãŸã„ ...body, expiresAt: body.expiresAt ? new Date(body.expiresAt) : null, + user: null, // joinãªã‚«ãƒ©ãƒ ã¯é€šå¸¸å–ã£ã¦ã“ãªã„ã®ã§ + role: null, // joinãªã‚«ãƒ©ãƒ ã¯é€šå¸¸å–ã£ã¦ã“ãªã„ã®ã§ }); } break; @@ -202,17 +206,20 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { } @bindThis - private evalCond(user: MiUser, value: RoleCondFormulaValue): boolean { + private evalCond(user: MiUser, roles: MiRole[], value: RoleCondFormulaValue): boolean { try { switch (value.type) { case 'and': { - return value.values.every(v => this.evalCond(user, v)); + return value.values.every(v => this.evalCond(user, roles, v)); } case 'or': { - return value.values.some(v => this.evalCond(user, v)); + return value.values.some(v => this.evalCond(user, roles, v)); } case 'not': { - return !this.evalCond(user, value.value); + return !this.evalCond(user, roles, value.value); + } + case 'roleAssignedTo': { + return roles.some(r => r.id === value.roleId); } case 'isLocal': { return this.userEntityService.isLocalUser(user); @@ -274,7 +281,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { const assigns = await this.getUserAssigns(userId); const assignedRoles = roles.filter(r => assigns.map(x => x.roleId).includes(r.id)); const user = roles.some(r => r.target === 'conditional') ? await this.cacheService.findUserById(userId) : null; - const matchedCondRoles = roles.filter(r => r.target === 'conditional' && this.evalCond(user!, r.condFormula)); + const matchedCondRoles = roles.filter(r => r.target === 'conditional' && this.evalCond(user!, assignedRoles, r.condFormula)); return [...assignedRoles, ...matchedCondRoles]; } @@ -287,13 +294,13 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId })); // 期é™åˆ‡ã‚Œã®ãƒãƒ¼ãƒ«ã‚’除外 assigns = assigns.filter(a => a.expiresAt == null || (a.expiresAt.getTime() > now)); - const assignedRoleIds = assigns.map(x => x.roleId); const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({})); - const assignedBadgeRoles = roles.filter(r => r.asBadge && assignedRoleIds.includes(r.id)); + const assignedRoles = roles.filter(r => assigns.map(x => x.roleId).includes(r.id)); + const assignedBadgeRoles = assignedRoles.filter(r => r.asBadge); const badgeCondRoles = roles.filter(r => r.asBadge && (r.target === 'conditional')); if (badgeCondRoles.length > 0) { const user = roles.some(r => r.target === 'conditional') ? await this.cacheService.findUserById(userId) : null; - const matchedBadgeCondRoles = badgeCondRoles.filter(r => this.evalCond(user!, r.condFormula)); + const matchedBadgeCondRoles = badgeCondRoles.filter(r => this.evalCond(user!, assignedRoles, r.condFormula)); return [...assignedBadgeRoles, ...matchedBadgeCondRoles]; } else { return assignedBadgeRoles; @@ -328,6 +335,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { btlAvailable: calc('btlAvailable', vs => vs.some(v => v === true)), ltlAvailable: calc('ltlAvailable', vs => vs.some(v => v === true)), canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)), + mentionLimit: calc('mentionLimit', vs => Math.max(...vs)), canInvite: calc('canInvite', vs => vs.some(v => v === true)), inviteLimit: calc('inviteLimit', vs => Math.max(...vs)), inviteLimitCycle: calc('inviteLimitCycle', vs => Math.max(...vs)), diff --git a/packages/backend/src/core/S3Service.ts b/packages/backend/src/core/S3Service.ts index df0991539d5a99d3d1cf8a4fd73d1925ea9424e0..bb2a463354a7bf92e9ed150a075961d5f1afc73f 100644 --- a/packages/backend/src/core/S3Service.ts +++ b/packages/backend/src/core/S3Service.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts index 57c7e4baba52ada90e713804127d125b145aee71..6dc3e85fc824351ee8ddbcfe749d2ea47c1c7bf4 100644 --- a/packages/backend/src/core/SearchService.ts +++ b/packages/backend/src/core/SearchService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts index 32e3dee937c58850d3ebdc0e8bf6319f5c7af9b7..e3d69e5e94fe70006d20c46c3bf10b74bacf1faa 100644 --- a/packages/backend/src/core/SignupService.ts +++ b/packages/backend/src/core/SignupService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -17,6 +17,7 @@ import { MiUserKeypair } from '@/models/UserKeypair.js'; import { MiUsedUsername } from '@/models/UsedUsername.js'; import generateUserToken from '@/misc/generate-native-user-token.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { InstanceActorService } from '@/core/InstanceActorService.js'; import { bindThis } from '@/decorators.js'; import UsersChart from '@/core/chart/charts/users.js'; import { UtilityService } from '@/core/UtilityService.js'; @@ -38,6 +39,7 @@ export class SignupService { private userEntityService: UserEntityService, private idService: IdService, private metaService: MetaService, + private instanceActorService: InstanceActorService, private usersChart: UsersChart, ) { } @@ -75,16 +77,16 @@ export class SignupService { const secret = generateUserToken(); // Check username duplication - if (await this.usersRepository.exist({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) { + if (await this.usersRepository.exists({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) { throw new Error('DUPLICATED_USERNAME'); } // Check deleted username duplication - if (await this.usedUsernamesRepository.exist({ where: { username: username.toLowerCase() } })) { + if (await this.usedUsernamesRepository.exists({ where: { username: username.toLowerCase() } })) { throw new Error('USED_USERNAME'); } - const isTheFirstUser = (await this.usersRepository.countBy({ host: IsNull() })) === 0; + const isTheFirstUser = !await this.instanceActorService.realLocalUsersPresent(); if (!opts.ignorePreservedUsernames && !isTheFirstUser) { const isPreserved = instance.preservedUsernames.map(x => x.toLowerCase()).includes(username.toLowerCase()); diff --git a/packages/backend/src/core/UserAuthService.ts b/packages/backend/src/core/UserAuthService.ts index ccf4dfc6bdfb89ee0b56ccbb72631e61f2cb06a1..bdc27cbe8e500401d34586c0f0fba2b3b296eaf4 100644 --- a/packages/backend/src/core/UserAuthService.ts +++ b/packages/backend/src/core/UserAuthService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/UserBlockingService.ts b/packages/backend/src/core/UserBlockingService.ts index 39b19325c351fca8dbed7599b07385bec62c43db..96f389b54c558779da91a77b494857f1753b4364 100644 --- a/packages/backend/src/core/UserBlockingService.ts +++ b/packages/backend/src/core/UserBlockingService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -109,13 +109,13 @@ export class UserBlockingService implements OnModuleInit { if (this.userEntityService.isLocalUser(followee)) { this.userEntityService.pack(followee, followee, { - detail: true, + schema: 'MeDetailed', }).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed)); } if (this.userEntityService.isLocalUser(follower) && !silent) { this.userEntityService.pack(followee, follower, { - detail: true, + schema: 'UserDetailedNotMe', }).then(async packed => { this.globalEventService.publishMainStream(follower.id, 'unfollow', packed); diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index d600ffb9d9d76c1d783c701616b50b49cc7843d8..deeecdeb1f1290085076246567bc9bf4fea4ccb3 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -30,6 +30,7 @@ import type { Config } from '@/config.js'; import { AccountMoveService } from '@/core/AccountMoveService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; +import type { ThinUser } from '@/queue/types.js'; import Logger from '../logger.js'; const logger = new Logger('following/create'); @@ -94,21 +95,35 @@ export class UserFollowingService implements OnModuleInit { this.userBlockingService = this.moduleRef.get('UserBlockingService'); } + @bindThis + public async deliverAccept(follower: MiRemoteUser, followee: MiPartialLocalUser, requestId?: string) { + const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee)); + this.queueService.deliver(followee, content, follower.inbox, false); + } + @bindThis public async follow( - _follower: { id: MiUser['id'] }, - _followee: { id: MiUser['id'] }, + _follower: ThinUser, + _followee: ThinUser, { requestId, silent = false, withReplies }: { requestId?: string, silent?: boolean, withReplies?: boolean, } = {}, ): Promise<void> { + /** + * å¿…ãšæœ€æ–°ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼æƒ…å ±ã‚’å–å¾—ã™ã‚‹ + */ const [follower, followee] = await Promise.all([ this.usersRepository.findOneByOrFail({ id: _follower.id }), this.usersRepository.findOneByOrFail({ id: _followee.id }), ]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser]; + if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isRemoteUser(followee)) { + // What? + throw new Error('Remote user cannot follow remote user.'); + } + // check blocking const [blocking, blocked] = await Promise.all([ this.userBlockingService.checkBlocked(follower.id, followee.id), @@ -129,6 +144,24 @@ export class UserFollowingService implements OnModuleInit { if (blocked) throw new IdentifiableError('3338392a-f764-498d-8855-db939dcf8c48', 'blocked'); } + if (await this.followingsRepository.exists({ + where: { + followerId: follower.id, + followeeId: followee.id, + }, + })) { + // ã™ã§ã«ãƒ•ã‚©ãƒãƒ¼é–¢ä¿‚ãŒå˜åœ¨ã—ã¦ã„ã‚‹å ´åˆ + if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { + // リモート → ãƒãƒ¼ã‚«ãƒ«: acceptã‚’é€ã‚Šè¿”ã—ã¦ãŠã—ã¾ã„ + this.deliverAccept(follower, followee, requestId); + return; + } + if (this.userEntityService.isLocalUser(follower)) { + // ãƒãƒ¼ã‚«ãƒ« → リモート/ãƒãƒ¼ã‚«ãƒ«: 例外 + throw new IdentifiableError('ec3f65c0-a9d1-47d9-8791-b2e7b9dcdced', 'already following'); + } + } + const followeeProfile = await this.userProfilesRepository.findOneByOrFail({ userId: followee.id }); // フォãƒãƒ¼å¯¾è±¡ãŒéµã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã‚ã‚‹ or // フォãƒãƒ¯ãƒ¼ãŒBotã§ã‚ã‚Šã€ãƒ•ã‚©ãƒãƒ¼å¯¾è±¡ãŒBotã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒ¼ã«æ…Žé‡ã§ã‚ã‚‹ or @@ -144,7 +177,7 @@ export class UserFollowingService implements OnModuleInit { let autoAccept = false; // éµã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã‚ã£ã¦ã‚‚ã€æ—¢ã«ãƒ•ã‚©ãƒãƒ¼ã•ã‚Œã¦ã„ãŸå ´åˆã¯ã‚¹ãƒ«ãƒ¼ - const isFollowing = await this.followingsRepository.exist({ + const isFollowing = await this.followingsRepository.exists({ where: { followerId: follower.id, followeeId: followee.id, @@ -156,7 +189,7 @@ export class UserFollowingService implements OnModuleInit { // フォãƒãƒ¼ã—ã¦ã„るユーザーã¯è‡ªå‹•æ‰¿èªã‚ªãƒ—ション if (!autoAccept && (this.userEntityService.isLocalUser(followee) && followeeProfile.autoAcceptFollowed)) { - const isFollowed = await this.followingsRepository.exist({ + const isFollowed = await this.followingsRepository.exists({ where: { followerId: followee.id, followeeId: follower.id, @@ -170,7 +203,7 @@ export class UserFollowingService implements OnModuleInit { if (followee.isLocked && !autoAccept) { autoAccept = !!(await this.accountMoveService.validateAlsoKnownAs( follower, - (oldSrc, newSrc) => this.followingsRepository.exist({ + (oldSrc, newSrc) => this.followingsRepository.exists({ where: { followeeId: followee.id, followerId: newSrc.id, @@ -189,8 +222,7 @@ export class UserFollowingService implements OnModuleInit { await this.insertFollowingDoc(followee, follower, silent, withReplies); if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { - const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee)); - this.queueService.deliver(followee, content, follower.inbox, false); + this.deliverAccept(follower, followee, requestId); } } @@ -233,7 +265,7 @@ export class UserFollowingService implements OnModuleInit { this.cacheService.userFollowingsCache.refresh(follower.id); - const requestExist = await this.followRequestsRepository.exist({ + const requestExist = await this.followRequestsRepository.exists({ where: { followeeId: followee.id, followerId: follower.id, @@ -293,9 +325,9 @@ export class UserFollowingService implements OnModuleInit { if (this.userEntityService.isLocalUser(follower) && !silent) { // Publish follow event this.userEntityService.pack(followee.id, follower, { - detail: true, + schema: 'UserDetailedNotMe', }).then(async packed => { - this.globalEventService.publishMainStream(follower.id, 'follow', packed as Packed<'UserDetailedNotMe'>); + this.globalEventService.publishMainStream(follower.id, 'follow', packed); const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('follow')); for (const webhook of webhooks) { @@ -360,7 +392,7 @@ export class UserFollowingService implements OnModuleInit { if (!silent && this.userEntityService.isLocalUser(follower)) { // Publish unfollow event this.userEntityService.pack(followee.id, follower, { - detail: true, + schema: 'UserDetailedNotMe', }).then(async packed => { this.globalEventService.publishMainStream(follower.id, 'unfollow', packed); @@ -479,6 +511,12 @@ export class UserFollowingService implements OnModuleInit { if (blocking) throw new Error('blocking'); if (blocked) throw new Error('blocked'); + // Remove old follow requests before creating a new one. + await this.followRequestsRepository.delete({ + followeeId: followee.id, + followerId: follower.id, + }); + const followRequest = await this.followRequestsRepository.insert({ id: this.idService.gen(), followerId: follower.id, @@ -500,7 +538,7 @@ export class UserFollowingService implements OnModuleInit { this.userEntityService.pack(follower.id, followee).then(packed => this.globalEventService.publishMainStream(followee.id, 'receiveFollowRequest', packed)); this.userEntityService.pack(followee.id, followee, { - detail: true, + schema: 'MeDetailed', }).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed)); // é€šçŸ¥ã‚’ä½œæˆ @@ -531,7 +569,7 @@ export class UserFollowingService implements OnModuleInit { } } - const requestExist = await this.followRequestsRepository.exist({ + const requestExist = await this.followRequestsRepository.exists({ where: { followeeId: followee.id, followerId: follower.id, @@ -548,7 +586,7 @@ export class UserFollowingService implements OnModuleInit { }); this.userEntityService.pack(followee.id, followee, { - detail: true, + schema: 'MeDetailed', }).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed)); } @@ -571,12 +609,11 @@ export class UserFollowingService implements OnModuleInit { await this.insertFollowingDoc(followee, follower, false, request.withReplies); if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { - const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee as MiPartialLocalUser, request.requestId!), followee)); - this.queueService.deliver(followee, content, follower.inbox, false); + this.deliverAccept(follower, followee as MiPartialLocalUser, request.requestId ?? undefined); } this.userEntityService.pack(followee.id, followee, { - detail: true, + schema: 'MeDetailed', }).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed)); } @@ -696,7 +733,7 @@ export class UserFollowingService implements OnModuleInit { @bindThis private async publishUnfollow(followee: Both, follower: Local): Promise<void> { const packedFollowee = await this.userEntityService.pack(followee.id, follower, { - detail: true, + schema: 'UserDetailedNotMe', }); this.globalEventService.publishMainStream(follower.id, 'unfollow', packedFollowee); diff --git a/packages/backend/src/core/UserKeypairService.ts b/packages/backend/src/core/UserKeypairService.ts index 425a97f3f12829833405fc22fa1c054a5f85d694..51ac99179a6a8828309d5b76c2b03dfccfc0c464 100644 --- a/packages/backend/src/core/UserKeypairService.ts +++ b/packages/backend/src/core/UserKeypairService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts index b6e4e1e8843252ef41f23653f9ad52c1747c284c..bbdcfed73836a43c2847529bdb81c438091f09d9 100644 --- a/packages/backend/src/core/UserListService.ts +++ b/packages/backend/src/core/UserListService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/UserMutingService.ts b/packages/backend/src/core/UserMutingService.ts index 397e6bdd5d90cba1717f910fc1cf912632d054eb..06643be5fb3669d85a95a7dea789d745d910c59d 100644 --- a/packages/backend/src/core/UserMutingService.ts +++ b/packages/backend/src/core/UserMutingService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/UserService.ts b/packages/backend/src/core/UserService.ts index d16e1be61513e0e77b774a96bbe790a0481d3772..72fa4d928d915583f9539e2bbcc1dda64b0fc384 100644 --- a/packages/backend/src/core/UserService.ts +++ b/packages/backend/src/core/UserService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index 8940a142d10f7b7b87fbd9ddd94275dcbb1268e2..d594a223f4e2d27d0fbbc2ac65eca1e12f8048ad 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts index 5dec36c89e31664ffcb332e2412fcca48031e81a..652e8f74499076bdffdb84f4f05f105007a69936 100644 --- a/packages/backend/src/core/UtilityService.ts +++ b/packages/backend/src/core/UtilityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -43,13 +43,27 @@ export class UtilityService { } @bindThis - public isSensitiveWordIncluded(text: string, sensitiveWords: string[]): boolean { - if (sensitiveWords.length === 0) return false; + public concatNoteContentsForKeyWordCheck(content: { + cw?: string | null; + text?: string | null; + pollChoices?: string[] | null; + others?: string[] | null; + }): string { + /** + * ノートã®å†…容をçµåˆã—ã¦ã‚ーワードãƒã‚§ãƒƒã‚¯ç”¨ã®æ–‡å—列を生æˆã™ã‚‹ + * cwã¨textã¯å†…容ãŒç¹‹ãŒã£ã¦ã„ã‚‹ã‹ã‚‚ã—ã‚Œãªã„ã®ã§é–“ã«ä½•ã‚‚入れãšã«ãƒã‚§ãƒƒã‚¯ã™ã‚‹ + */ + return `${content.cw ?? ''}${content.text ?? ''}\n${(content.pollChoices ?? []).join('\n')}\n${(content.others ?? []).join('\n')}`; + } + + @bindThis + public isKeyWordIncluded(text: string, keyWords: string[]): boolean { + if (keyWords.length === 0) return false; if (text === '') return false; const regexpregexp = /^\/(.+)\/(.*)$/; - const matched = sensitiveWords.some(filter => { + const matched = keyWords.some(filter => { // represents RegExp const regexp = filter.match(regexpregexp); // This should never happen due to input sanitisation. diff --git a/packages/backend/src/core/VideoProcessingService.ts b/packages/backend/src/core/VideoProcessingService.ts index ffb7573358b5e0f7322d51e9b9edf2a95b6481d5..747fe4fc7ee7edcbd909e3300f09844667c06100 100644 --- a/packages/backend/src/core/VideoProcessingService.ts +++ b/packages/backend/src/core/VideoProcessingService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/WebAuthnService.ts b/packages/backend/src/core/WebAuthnService.ts index 5945dc29197a166c907ab4eb21e94e49af7b5862..42fbed21107aefab096099b505eeff4f20a89ae7 100644 --- a/packages/backend/src/core/WebAuthnService.ts +++ b/packages/backend/src/core/WebAuthnService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -26,7 +26,7 @@ import type { PublicKeyCredentialDescriptorFuture, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON, -} from '@simplewebauthn/typescript-types'; +} from '@simplewebauthn/types'; @Injectable() export class WebAuthnService { @@ -191,7 +191,7 @@ export class WebAuthnService { if (cert[0] === 0x04) { // å‰ã®å®Ÿè£…ã§ã¯ã„ã¤ã‚‚ 0x04 ã§å§‹ã¾ã£ã¦ã„㟠const halfLength = (cert.length - 1) / 2; - const cborMap = new Map<number, number | ArrayBufferLike>(); + const cborMap = new Map<number, number | Uint8Array>(); cborMap.set(1, 2); // kty, EC2 cborMap.set(3, -7); // alg, ES256 cborMap.set(-1, 1); // crv, P256 diff --git a/packages/backend/src/core/WebfingerService.ts b/packages/backend/src/core/WebfingerService.ts index 3d5747aebdaf8458f7c3dad9aa9fda9e2c4cfd50..374536a7410113a0073d3ed95271d2a7f33dd412 100644 --- a/packages/backend/src/core/WebfingerService.ts +++ b/packages/backend/src/core/WebfingerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/WebhookService.ts b/packages/backend/src/core/WebhookService.ts index 930e6ef64a109a4612c3d9c62851d38befe09d1c..6be34977b064d24180e1efeec66df4b7ed316e8c 100644 --- a/packages/backend/src/core/WebhookService.ts +++ b/packages/backend/src/core/WebhookService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -49,9 +49,10 @@ export class WebhookService implements OnApplicationShutdown { switch (type) { case 'webhookCreated': if (body.active) { - this.webhooks.push({ + this.webhooks.push({ // TODO: ã“ã®ã‚ãŸã‚Šã®ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºå‡¦ç†ã¯å„modelファイル内ã«é–¢æ•°ã¨ã—ã¦exportã—ãŸã„ ...body, latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null, + user: null, // joinãªã‚«ãƒ©ãƒ ã¯é€šå¸¸å–ã£ã¦ã“ãªã„ã®ã§ }); } break; @@ -59,14 +60,16 @@ export class WebhookService implements OnApplicationShutdown { if (body.active) { const i = this.webhooks.findIndex(a => a.id === body.id); if (i > -1) { - this.webhooks[i] = { + this.webhooks[i] = { // TODO: ã“ã®ã‚ãŸã‚Šã®ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºå‡¦ç†ã¯å„modelファイル内ã«é–¢æ•°ã¨ã—ã¦exportã—ãŸã„ ...body, latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null, + user: null, // joinãªã‚«ãƒ©ãƒ ã¯é€šå¸¸å–ã£ã¦ã“ãªã„ã®ã§ }; } else { - this.webhooks.push({ + this.webhooks.push({ // TODO: ã“ã®ã‚ãŸã‚Šã®ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºå‡¦ç†ã¯å„modelファイル内ã«é–¢æ•°ã¨ã—ã¦exportã—ãŸã„ ...body, latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null, + user: null, // joinãªã‚«ãƒ©ãƒ ã¯é€šå¸¸å–ã£ã¦ã“ãªã„ã®ã§ }); } } else { diff --git a/packages/backend/src/core/activitypub/ApAudienceService.ts b/packages/backend/src/core/activitypub/ApAudienceService.ts index 440852bdf370888c6c55cbcca7ff71ef22302c2e..0fccc7b95086147a0776584b1f029e4cbfe7520b 100644 --- a/packages/backend/src/core/activitypub/ApAudienceService.ts +++ b/packages/backend/src/core/activitypub/ApAudienceService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -8,6 +8,7 @@ import promiseLimit from 'promise-limit'; import type { MiRemoteUser, MiUser } from '@/models/User.js'; import { concat, unique } from '@/misc/prelude/array.js'; import { bindThis } from '@/decorators.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { getApIds } from './type.js'; import { ApPersonService } from './models/ApPersonService.js'; import type { ApObject } from './type.js'; @@ -40,7 +41,7 @@ export class ApAudienceService { const limit = promiseLimit<MiUser | null>(2); const mentionedUsers = (await Promise.all( others.map(id => limit(() => this.apPersonService.resolvePerson(id, resolver).catch(() => null))), - )).filter((x): x is MiUser => x != null); + )).filter(isNotNull); if (toGroups.public.length > 0) { return { @@ -58,7 +59,7 @@ export class ApAudienceService { }; } - if (toGroups.followers.length > 0) { + if (toGroups.followers.length > 0 || ccGroups.followers.length > 0) { return { visibility: 'followers', mentionedUsers, diff --git a/packages/backend/src/core/activitypub/ApDbResolverService.ts b/packages/backend/src/core/activitypub/ApDbResolverService.ts index dd1687edeb038542b7a86c166b73438bcff2fb5e..44680a2ed5d5d4e009d2032a2ed3ce1d204d0e22 100644 --- a/packages/backend/src/core/activitypub/ApDbResolverService.ts +++ b/packages/backend/src/core/activitypub/ApDbResolverService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -106,12 +106,12 @@ export class ApDbResolverService implements OnApplicationShutdown { return await this.cacheService.userByIdCache.fetchMaybe( parsed.id, - () => this.usersRepository.findOneBy({ id: parsed.id }).then(x => x ?? undefined), + () => this.usersRepository.findOneBy({ id: parsed.id, isDeleted: false }).then(x => x ?? undefined), ) as MiLocalUser | undefined ?? null; } else { return await this.cacheService.uriPersonCache.fetch( parsed.uri, - () => this.usersRepository.findOneBy({ uri: parsed.uri }), + () => this.usersRepository.findOneBy({ uri: parsed.uri, isDeleted: false }), ) as MiRemoteUser | null; } } @@ -136,8 +136,12 @@ export class ApDbResolverService implements OnApplicationShutdown { if (key == null) return null; + const user = await this.cacheService.findUserById(key.userId).catch(() => null) as MiRemoteUser | null; + if (user == null) return null; + if (user.isDeleted) return null; + return { - user: await this.cacheService.findUserById(key.userId) as MiRemoteUser, + user, key, }; } @@ -151,6 +155,7 @@ export class ApDbResolverService implements OnApplicationShutdown { key: MiUserPublickey | null; } | null> { const user = await this.apPersonService.resolvePerson(uri) as MiRemoteUser; + if (user.isDeleted) return null; const key = await this.publicKeyByUserIdCache.fetch( user.id, diff --git a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts index 81003bcf1c795f3eb1b7e7e116377052c225f0ad..5d07cd8e8f637a6c14ccd71f0ab9a96e226147ea 100644 --- a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -144,7 +144,7 @@ class DeliverManager { } // deliver - this.queueService.deliverMany(this.actor, this.activity, inboxes); + await this.queueService.deliverMany(this.actor, this.activity, inboxes); } } diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts index d8616d293dd3c1bbf6504a36b70a035dba1f96db..6ff03b22e1c4202f92a0677a51548ca7a7c1c272 100644 --- a/packages/backend/src/core/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -27,6 +27,7 @@ import { QueueService } from '@/core/QueueService.js'; import type { UsersRepository, NotesRepository, FollowingsRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import type { MiRemoteUser } from '@/models/User.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { getApHrefNullable, getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; import { ApNoteService } from './models/ApNoteService.js'; import { ApLoggerService } from './ApLoggerService.js'; @@ -35,6 +36,7 @@ import { ApResolverService } from './ApResolverService.js'; import { ApAudienceService } from './ApAudienceService.js'; import { ApPersonService } from './models/ApPersonService.js'; import { ApQuestionService } from './models/ApQuestionService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { Resolver } from './ApResolverService.js'; import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IReject, IRemove, IUndo, IUpdate, IMove } from './type.js'; @@ -82,6 +84,7 @@ export class ApInboxService { private apPersonService: ApPersonService, private apQuestionService: ApQuestionService, private queueService: QueueService, + private globalEventService: GlobalEventService, ) { this.logger = this.apLoggerService.logger; } @@ -97,6 +100,8 @@ export class ApInboxService { } catch (err) { if (err instanceof Error || typeof err === 'string') { this.logger.error(err); + } else { + throw err; } } } @@ -256,7 +261,7 @@ export class ApInboxService { const targetUri = getApId(activity.object); - this.announceNote(actor, activity, targetUri); + await this.announceNote(actor, activity, targetUri); } @bindThis @@ -288,7 +293,7 @@ export class ApInboxService { } catch (err) { // 対象ãŒ4xxãªã‚‰ã‚¹ã‚ップ if (err instanceof StatusError) { - if (err.isClientError) { + if (!err.isRetryable) { this.logger.warn(`Ignored announce target ${targetUri} - ${err.statusCode}`); return; } @@ -373,7 +378,7 @@ export class ApInboxService { }); if (isPost(object)) { - this.createNote(resolver, actor, object, false, activity); + await this.createNote(resolver, actor, object, false, activity); } else { this.logger.warn(`Unknown type: ${getApType(object)}`); } @@ -404,7 +409,7 @@ export class ApInboxService { await this.apNoteService.createNote(note, resolver, silent); return 'ok'; } catch (err) { - if (err instanceof StatusError && err.isClientError) { + if (err instanceof StatusError && !err.isRetryable) { return `skip ${err.statusCode}`; } else { throw err; @@ -477,6 +482,8 @@ export class ApInboxService { isDeleted: true, }); + this.globalEventService.publishInternalEvent('remoteUserUpdated', { id: actor.id }); + return `ok: queued ${job.name} ${job.id}`; } @@ -513,7 +520,7 @@ export class ApInboxService { const userIds = uris .filter(uri => uri.startsWith(this.config.url + '/users/')) .map(uri => uri.split('/').at(-1)) - .filter((userId): userId is string => userId !== undefined); + .filter(isNotNull); const users = await this.usersRepository.findBy({ id: In(userIds), }); @@ -627,7 +634,7 @@ export class ApInboxService { return 'skip: follower not found'; } - const isFollowing = await this.followingsRepository.exist({ + const isFollowing = await this.followingsRepository.exists({ where: { followerId: follower.id, followeeId: actor.id, @@ -684,14 +691,14 @@ export class ApInboxService { return 'skip: フォãƒãƒ¼è§£é™¤ã—よã†ã¨ã—ã¦ã„るユーザーã¯ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã§ã¯ã‚ã‚Šã¾ã›ã‚“'; } - const requestExist = await this.followRequestsRepository.exist({ + const requestExist = await this.followRequestsRepository.exists({ where: { followerId: actor.id, followeeId: followee.id, }, }); - const isFollowing = await this.followingsRepository.exist({ + const isFollowing = await this.followingsRepository.exists({ where: { followerId: actor.id, followeeId: followee.id, diff --git a/packages/backend/src/core/activitypub/ApLoggerService.ts b/packages/backend/src/core/activitypub/ApLoggerService.ts index cd9597e423cdb82c5ce47a402e475a92a802bc98..428d8061cea00f6a3bce11ec2748fa8571b72207 100644 --- a/packages/backend/src/core/activitypub/ApLoggerService.ts +++ b/packages/backend/src/core/activitypub/ApLoggerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/activitypub/ApMfmService.ts b/packages/backend/src/core/activitypub/ApMfmService.ts index c19eb310d219721d4d2473601d7eae426cd14b77..6d53ce5147ddbfd35317465d08ebf318f12a1992 100644 --- a/packages/backend/src/core/activitypub/ApMfmService.ts +++ b/packages/backend/src/core/activitypub/ApMfmService.ts @@ -1,10 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { Injectable } from '@nestjs/common'; -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; import { MfmService } from '@/core/MfmService.js'; import type { MiNote } from '@/models/Note.js'; import { bindThis } from '@/decorators.js'; @@ -25,8 +25,21 @@ export class ApMfmService { } @bindThis - public getNoteHtml(note: MiNote): string | null { - if (!note.text) return ''; - return this.mfmService.toHtml(mfm.parse(note.text), note.mentionedRemoteUsers ? JSON.parse(note.mentionedRemoteUsers) : []); + public getNoteHtml(note: MiNote, apAppend?: string) { + let noMisskeyContent = false; + const srcMfm = (note.text ?? '') + (apAppend ?? ''); + + const parsed = mfm.parse(srcMfm); + + if (!apAppend && parsed?.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) { + noMisskeyContent = true; + } + + const content = this.mfmService.toHtml(parsed, note.mentionedRemoteUsers ? JSON.parse(note.mentionedRemoteUsers) : []); + + return { + content, + noMisskeyContent, + }; } } diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index f4d39d240889e4abc0f881dbed30a1454d2ef314..a84feffac62f3acb81d3189381ea943e5905acfb 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -1,12 +1,12 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { createPublicKey, randomUUID } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type { MiPartialLocalUser, MiLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js'; @@ -28,10 +28,10 @@ import { bindThis } from '@/decorators.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { isNotNull } from '@/misc/is-not-null.js'; import { IdService } from '@/core/IdService.js'; +import { MetaService } from '../MetaService.js'; import { LdSignatureService } from './LdSignatureService.js'; import { ApMfmService } from './ApMfmService.js'; import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js'; -import { MetaService } from '../MetaService.js'; @Injectable() export class ApRendererService { @@ -335,7 +335,7 @@ export class ApRendererService { const getPromisedFiles = async (ids: string[]): Promise<MiDriveFile[]> => { if (ids.length === 0) return []; const items = await this.driveFilesRepository.findBy({ id: In(ids) }); - return ids.map(id => items.find(item => item.id === id)).filter((item): item is MiDriveFile => item != null); + return ids.map(id => items.find(item => item.id === id)).filter(isNotNull); }; let inReplyTo; @@ -345,7 +345,7 @@ export class ApRendererService { inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId }); if (inReplyToNote != null) { - const inReplyToUserExist = await this.usersRepository.exist({ where: { id: inReplyToNote.userId } }); + const inReplyToUserExist = await this.usersRepository.exists({ where: { id: inReplyToNote.userId } }); if (inReplyToUserExist) { if (inReplyToNote.uri) { @@ -409,17 +409,15 @@ export class ApRendererService { poll = await this.pollsRepository.findOneBy({ noteId: note.id }); } - let apText = text; + let apAppend = ''; if (quote) { - apText += `\n\nRE: ${quote}`; + apAppend += `\n\nRE: ${quote}`; } const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw; - const content = this.apMfmService.getNoteHtml(Object.assign({}, note, { - text: apText, - })); + const { content, noMisskeyContent } = this.apMfmService.getNoteHtml(note, apAppend); const emojis = await this.getEmojis(note.emojis); const apemojis = emojis.filter(emoji => !emoji.localOnly).map(emoji => this.renderEmoji(emoji)); @@ -432,9 +430,6 @@ export class ApRendererService { const asPoll = poll ? { type: 'Question', - content: this.apMfmService.getNoteHtml(Object.assign({}, note, { - text: text, - })), [poll.expiresAt && poll.expiresAt < new Date() ? 'closed' : 'endTime']: poll.expiresAt, [poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({ type: 'Note', @@ -452,11 +447,13 @@ export class ApRendererService { attributedTo, summary: summary ?? undefined, content: content ?? undefined, - _misskey_content: text, - source: { - content: text, - mediaType: 'text/x.misskeymarkdown', - }, + ...(noMisskeyContent ? {} : { + _misskey_content: text, + source: { + content: text, + mediaType: 'text/x.misskeymarkdown', + }, + }), _misskey_quote: quote, quoteUrl: quote, quoteUri: quote, @@ -639,7 +636,7 @@ export class ApRendererService { inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId }); if (inReplyToNote != null) { - const inReplyToUserExist = await this.usersRepository.exist({ where: { id: inReplyToNote.userId } }); + const inReplyToUserExist = await this.usersRepository.exists({ where: { id: inReplyToNote.userId } }); if (inReplyToUserExist) { if (inReplyToNote.uri) { @@ -703,17 +700,15 @@ export class ApRendererService { poll = await this.pollsRepository.findOneBy({ noteId: note.id }); } - let apText = text; + let apAppend = ''; if (quote) { - apText += `\n\nRE: ${quote}`; + apAppend += `\n\nRE: ${quote}`; } const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw; - const content = this.apMfmService.getNoteHtml(Object.assign({}, note, { - text: apText, - })); + const { content, noMisskeyContent } = this.apMfmService.getNoteHtml(note, apAppend); const emojis = await this.getEmojis(note.emojis); const apemojis = emojis.filter(emoji => !emoji.localOnly).map(emoji => this.renderEmoji(emoji)); @@ -726,9 +721,6 @@ export class ApRendererService { const asPoll = poll ? { type: 'Question', - content: this.apMfmService.getNoteHtml(Object.assign({}, note, { - text: text, - })), [poll.expiresAt && poll.expiresAt < new Date() ? 'closed' : 'endTime']: poll.expiresAt, [poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({ type: 'Note', @@ -747,11 +739,13 @@ export class ApRendererService { summary: summary ?? undefined, content: content ?? undefined, updated: note.updatedAt?.toISOString(), - _misskey_content: text, - source: { - content: text, - mediaType: 'text/x.misskeymarkdown', - }, + ...(noMisskeyContent ? {} : { + _misskey_content: text, + source: { + content: text, + mediaType: 'text/x.misskeymarkdown', + }, + }), _misskey_quote: quote, quoteUrl: quote, quoteUri: quote, @@ -796,6 +790,7 @@ export class ApRendererService { 'https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1', { + Key: 'sec:Key', // as non-standards manuallyApprovesFollowers: 'as:manuallyApprovesFollowers', sensitive: 'as:sensitive', @@ -821,12 +816,12 @@ export class ApRendererService { '_misskey_summary': 'misskey:_misskey_summary', 'isCat': 'misskey:isCat', // Firefish - firefish: "https://joinfirefish.org/ns#", - speakAsCat: "firefish:speakAsCat", + firefish: 'https://joinfirefish.org/ns#', + speakAsCat: 'firefish:speakAsCat', // Sharkey - sharkey: "https://joinsharkey.org/ns#", - backgroundUrl: "sharkey:backgroundUrl", - listenbrainz: "sharkey:listenbrainz", + sharkey: 'https://joinsharkey.org/ns#', + backgroundUrl: 'sharkey:backgroundUrl', + listenbrainz: 'sharkey:listenbrainz', // vcard vcard: 'http://www.w3.org/2006/vcard/ns#', }, diff --git a/packages/backend/src/core/activitypub/ApRequestService.ts b/packages/backend/src/core/activitypub/ApRequestService.ts index bd7b9bdf09e6e32a1b0cd325855674e1480de787..93ac8ce9a74bdb9b680ea37aeb8db59df7c107b5 100644 --- a/packages/backend/src/core/activitypub/ApRequestService.ts +++ b/packages/backend/src/core/activitypub/ApRequestService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -35,9 +35,9 @@ type PrivateKey = { }; export class ApRequestCreator { - static createSignedPost(args: { key: PrivateKey, url: string, body: string, additionalHeaders: Record<string, string> }): Signed { + static createSignedPost(args: { key: PrivateKey, url: string, body: string, digest?: string, additionalHeaders: Record<string, string> }): Signed { const u = new URL(args.url); - const digestHeader = `SHA-256=${crypto.createHash('sha256').update(args.body).digest('base64')}`; + const digestHeader = args.digest ?? this.createDigest(args.body); const request: Request = { url: u.href, @@ -60,6 +60,10 @@ export class ApRequestCreator { }; } + static createDigest(body: string) { + return `SHA-256=${crypto.createHash('sha256').update(body).digest('base64')}`; + } + static createSignedGet(args: { key: PrivateKey, url: string, additionalHeaders: Record<string, string> }): Signed { const u = new URL(args.url); @@ -146,8 +150,8 @@ export class ApRequestService { } @bindThis - public async signedPost(user: { id: MiUser['id'] }, url: string, object: unknown): Promise<void> { - const body = JSON.stringify(object); + public async signedPost(user: { id: MiUser['id'] }, url: string, object: unknown, digest?: string): Promise<void> { + const body = typeof object === 'string' ? object : JSON.stringify(object); const keypair = await this.userKeypairService.getUserKeypair(user.id); @@ -158,6 +162,7 @@ export class ApRequestService { }, url, body, + digest, additionalHeaders: { }, }); diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts index 870cf6372a060589984355e98454e26a6759a84e..bb3c40f0939b6a65a486123d1e0b4bdd218052c4 100644 --- a/packages/backend/src/core/activitypub/ApResolverService.ts +++ b/packages/backend/src/core/activitypub/ApResolverService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/activitypub/LdSignatureService.ts b/packages/backend/src/core/activitypub/LdSignatureService.ts index d8464b383901a5f66cc6cf5d1c460c75aa82dc9d..9de184336f0f20e86cbd0cf98c6947899f986161 100644 --- a/packages/backend/src/core/activitypub/LdSignatureService.ts +++ b/packages/backend/src/core/activitypub/LdSignatureService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/activitypub/misc/contexts.ts b/packages/backend/src/core/activitypub/misc/contexts.ts index 71c440e5cc5aef3bdb5989643d9661fb38ad7548..88afdefcd3d757c5edce405a85b36f8797d86a79 100644 --- a/packages/backend/src/core/activitypub/misc/contexts.ts +++ b/packages/backend/src/core/activitypub/misc/contexts.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/activitypub/models/ApImageService.ts b/packages/backend/src/core/activitypub/models/ApImageService.ts index 2eff7c64e07f02ae66e8c40a6ae902ef569eb5fb..1017a5018a2e0f12edcb9c97ad18ef92f41a8e40 100644 --- a/packages/backend/src/core/activitypub/models/ApImageService.ts +++ b/packages/backend/src/core/activitypub/models/ApImageService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/activitypub/models/ApMentionService.ts b/packages/backend/src/core/activitypub/models/ApMentionService.ts index 9aa8ba5ede93771b79e13e83e021c3772edc60ce..0ced7e88aff25b0a27c11c4b7d5220a554cba788 100644 --- a/packages/backend/src/core/activitypub/models/ApMentionService.ts +++ b/packages/backend/src/core/activitypub/models/ApMentionService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -8,6 +8,7 @@ import promiseLimit from 'promise-limit'; import type { MiUser } from '@/models/_.js'; import { toArray, unique } from '@/misc/prelude/array.js'; import { bindThis } from '@/decorators.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { isMention } from '../type.js'; import { Resolver } from '../ApResolverService.js'; import { ApPersonService } from './ApPersonService.js'; @@ -27,7 +28,7 @@ export class ApMentionService { const limit = promiseLimit<MiUser | null>(2); const mentionedUsers = (await Promise.all( hrefs.map(x => limit(() => this.apPersonService.resolvePerson(x, resolver).catch(() => null))), - )).filter((x): x is MiUser => x != null); + )).filter(isNotNull); return mentionedUsers; } diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 2595783e0421746b6dca4a1cfead1f1a5668ed11..6d9dc86c1662b0c65f32af8037b3c4f446c7ea80 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -25,6 +25,8 @@ import { StatusError } from '@/misc/status-error.js'; import { UtilityService } from '@/core/UtilityService.js'; import { bindThis } from '@/decorators.js'; import { checkHttps } from '@/misc/check-https.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js'; import { ApLoggerService } from '../ApLoggerService.js'; import { ApMfmService } from '../ApMfmService.js'; @@ -156,11 +158,47 @@ export class ApNoteService { throw new Error('invalid note.attributedTo: ' + note.attributedTo); } - const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as MiRemoteUser; + const uri = getOneApId(note.attributedTo); - // 投稿者ãŒå‡çµã•ã‚Œã¦ã„ãŸã‚‰ã‚¹ã‚ップ + // ãƒãƒ¼ã‚«ãƒ«ã§æŠ•ç¨¿è€…を検索ã—ã€ã‚‚ã—å‡çµã•ã‚Œã¦ã„ãŸã‚‰ã‚¹ã‚ップ + const cachedActor = await this.apPersonService.fetchPerson(uri) as MiRemoteUser; + if (cachedActor && cachedActor.isSuspended) { + throw new IdentifiableError('85ab9bd7-3a41-4530-959d-f07073900109', 'actor has been suspended'); + } + + const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver); + const apHashtags = extractApHashtags(note.tag); + + const cw = note.summary === '' ? null : note.summary; + + // テã‚ストã®ãƒ‘ース + let text: string | null = null; + if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') { + text = note.source.content; + } else if (typeof note._misskey_content !== 'undefined') { + text = note._misskey_content; + } else if (typeof note.content === 'string') { + text = this.apMfmService.htmlToMfm(note.content, note.tag); + } + + const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined); + + //#region Contents Check + // 添付ファイルã¨ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’ã“ã®ã‚µãƒ¼ãƒãƒ¼ã§ç™»éŒ²ã™ã‚‹å‰ã«å†…容をãƒã‚§ãƒƒã‚¯ã™ã‚‹ + /** + * ç¦æ¢ãƒ¯ãƒ¼ãƒ‰ãƒã‚§ãƒƒã‚¯ + */ + const hasProhibitedWords = await this.noteCreateService.checkProhibitedWordsContain({ cw, text, pollChoices: poll?.choices }); + if (hasProhibitedWords) { + throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words'); + } + //#endregion + + const actor = cachedActor ?? await this.apPersonService.resolvePerson(uri, resolver) as MiRemoteUser; + + // 解決ã—ãŸæŠ•ç¨¿è€…ãŒå‡çµã•ã‚Œã¦ã„ãŸã‚‰ã‚¹ã‚ップ if (actor.isSuspended) { - throw new Error('actor has been suspended'); + throw new IdentifiableError('85ab9bd7-3a41-4530-959d-f07073900109', 'actor has been suspended'); } const noteAudience = await this.apAudienceService.parseAudience(actor, note.to, note.cc, resolver); @@ -175,9 +213,6 @@ export class ApNoteService { } } - const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver); - const apHashtags = extractApHashtags(note.tag); - // 添付ファイル // TODO: attachmentã¯å¿…ãšã—ã‚‚Imageã§ã¯ãªã„ // TODO: attachmentã¯å¿…ãšã—ã‚‚é…列ã§ã¯ãªã„ @@ -221,12 +256,12 @@ export class ApNoteService { return { status: 'ok', res }; } catch (e) { return { - status: (e instanceof StatusError && e.isClientError) ? 'permerror' : 'temperror', + status: (e instanceof StatusError && !e.isRetryable) ? 'permerror' : 'temperror', }; } }; - const uris = unique([note._misskey_quote, note.quoteUrl, note.quoteUri].filter((x): x is string => typeof x === 'string')); + const uris = unique([note._misskey_quote, note.quoteUrl, note.quoteUri].filter(isNotNull)); const results = await Promise.all(uris.map(tryResolveNote)); quote = results.filter((x): x is { status: 'ok', res: MiNote } => x.status === 'ok').map(x => x.res).at(0); @@ -237,18 +272,6 @@ export class ApNoteService { } } - const cw = note.summary === '' ? null : note.summary; - - // テã‚ストã®ãƒ‘ース - let text: string | null = null; - if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') { - text = note.source.content; - } else if (typeof note._misskey_content !== 'undefined') { - text = note._misskey_content; - } else if (typeof note.content === 'string') { - text = this.apMfmService.htmlToMfm(note.content, note.tag); - } - // vote if (reply && reply.hasPoll) { const poll = await this.pollsRepository.findOneByOrFail({ noteId: reply.id }); @@ -278,8 +301,6 @@ export class ApNoteService { const apEmojis = emojis.map(emoji => emoji.name); - const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined); - try { return await this.noteCreateService.create(actor, { createdAt: note.published ? new Date(note.published) : null, @@ -317,14 +338,14 @@ export class ApNoteService { */ @bindThis public async updateNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<MiNote | null> { - const uri = typeof value === 'string' ? value : value.id; - if (uri == null) throw new Error('uri is null'); + const noteUri = typeof value === 'string' ? value : value.id; + if (noteUri == null) throw new Error('uri is null'); // URIãŒã“ã®ã‚µãƒ¼ãƒãƒ¼ã‚’指ã—ã¦ã„ã‚‹ãªã‚‰ã‚¹ã‚ップ - if (uri.startsWith(this.config.url + '/')) throw new Error('uri points local'); + if (noteUri.startsWith(this.config.url + '/')) throw new Error('uri points local'); //#region ã“ã®ã‚µãƒ¼ãƒãƒ¼ã«æ—¢ã«ç™»éŒ²ã•ã‚Œã¦ã„ã‚‹ã‹ - const UpdatedNote = await this.notesRepository.findOneBy({ uri }); + const UpdatedNote = await this.notesRepository.findOneBy({ uri: noteUri }); if (UpdatedNote == null) throw new Error('Note is not registered'); // eslint-disable-next-line no-param-reassign @@ -364,11 +385,47 @@ export class ApNoteService { throw new Error('invalid note.attributedTo: ' + note.attributedTo); } - const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as MiRemoteUser; + const uri = getOneApId(note.attributedTo); + + // ãƒãƒ¼ã‚«ãƒ«ã§æŠ•ç¨¿è€…を検索ã—ã€ã‚‚ã—å‡çµã•ã‚Œã¦ã„ãŸã‚‰ã‚¹ã‚ップ + const cachedActor = await this.apPersonService.fetchPerson(uri) as MiRemoteUser; + if (cachedActor && cachedActor.isSuspended) { + throw new IdentifiableError('85ab9bd7-3a41-4530-959d-f07073900109', 'actor has been suspended'); + } + + const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver); + const apHashtags = extractApHashtags(note.tag); + + const cw = note.summary === '' ? null : note.summary; + + // テã‚ストã®ãƒ‘ース + let text: string | null = null; + if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') { + text = note.source.content; + } else if (typeof note._misskey_content !== 'undefined') { + text = note._misskey_content; + } else if (typeof note.content === 'string') { + text = this.apMfmService.htmlToMfm(note.content, note.tag); + } + + const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined); + + //#region Contents Check + // 添付ファイルã¨ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’ã“ã®ã‚µãƒ¼ãƒãƒ¼ã§ç™»éŒ²ã™ã‚‹å‰ã«å†…容をãƒã‚§ãƒƒã‚¯ã™ã‚‹ + /** + * ç¦æ¢ãƒ¯ãƒ¼ãƒ‰ãƒã‚§ãƒƒã‚¯ + */ + const hasProhibitedWords = await this.noteCreateService.checkProhibitedWordsContain({ cw, text, pollChoices: poll?.choices }); + if (hasProhibitedWords) { + throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words'); + } + //#endregion + + const actor = cachedActor ?? await this.apPersonService.resolvePerson(uri, resolver) as MiRemoteUser; // 投稿者ãŒå‡çµã•ã‚Œã¦ã„ãŸã‚‰ã‚¹ã‚ップ if (actor.isSuspended) { - throw new Error('actor has been suspended'); + throw new IdentifiableError('85ab9bd7-3a41-4530-959d-f07073900109', 'actor has been suspended'); } const noteAudience = await this.apAudienceService.parseAudience(actor, note.to, note.cc, resolver); @@ -383,9 +440,6 @@ export class ApNoteService { } } - const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver); - const apHashtags = extractApHashtags(note.tag); - // 添付ファイル // TODO: attachmentã¯å¿…ãšã—ã‚‚Imageã§ã¯ãªã„ // TODO: attachmentã¯å¿…ãšã—ã‚‚é…列ã§ã¯ãªã„ @@ -445,24 +499,12 @@ export class ApNoteService { } } - const cw = note.summary === '' ? null : note.summary; - - // テã‚ストã®ãƒ‘ース - let text: string | null = null; - if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') { - text = note.source.content; - } else if (typeof note._misskey_content !== 'undefined') { - text = note._misskey_content; - } else if (typeof note.content === 'string') { - text = this.apMfmService.htmlToMfm(note.content, note.tag); - } - // vote if (reply && reply.hasPoll) { - const poll = await this.pollsRepository.findOneByOrFail({ noteId: reply.id }); + const replyPoll = await this.pollsRepository.findOneByOrFail({ noteId: reply.id }); const tryCreateVote = async (name: string, index: number): Promise<null> => { - if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) { + if (replyPoll.expiresAt && Date.now() > new Date(replyPoll.expiresAt).getTime()) { this.logger.warn(`vote to expired poll from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); } else if (index >= 0) { this.logger.info(`vote from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); @@ -475,7 +517,7 @@ export class ApNoteService { }; if (note.name) { - return await tryCreateVote(note.name, poll.choices.findIndex(x => x === note.name)); + return await tryCreateVote(note.name, replyPoll.choices.findIndex(x => x === note.name)); } } @@ -486,8 +528,6 @@ export class ApNoteService { const apEmojis = emojis.map(emoji => emoji.name); - const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined); - try { return await this.noteEditService.edit(actor, UpdatedNote.id, { createdAt: note.published ? new Date(note.published) : null, diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 8507c6f9496a1da03f0add40fe3d8734cfdb1b95..c489d38d90587474da633d9c266b0f906638466b 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -38,6 +38,7 @@ import { MetaService } from '@/core/MetaService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import type { AccountMoveService } from '@/core/AccountMoveService.js'; import { checkHttps } from '@/misc/check-https.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js'; import { extractApHashtags } from './tag.js'; import type { OnModuleInit } from '@nestjs/common'; @@ -225,23 +226,42 @@ export class ApPersonService implements OnModuleInit { return null; } - private async resolveAvatarAndBanner(user: MiRemoteUser, icon: any, image: any, bgimg: any): Promise<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'backgroundId' | 'avatarUrl' | 'bannerUrl' | 'backgroundUrl' | 'avatarBlurhash' | 'bannerBlurhash' | 'backgroundBlurhash'>> { + private async resolveAvatarAndBanner(user: MiRemoteUser, icon: any, image: any, bgimg: any): Promise<Partial<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'backgroundId' | 'avatarUrl' | 'bannerUrl' | 'backgroundUrl' | 'avatarBlurhash' | 'bannerBlurhash' | 'backgroundBlurhash'>>> { + if (user == null) throw new Error('failed to create user: user is null'); + const [avatar, banner, background] = await Promise.all([icon, image, bgimg].map(img => { - if (img == null) return null; - if (user == null) throw new Error('failed to create user: user is null'); + // if we have an explicitly missing image, return an + // explicitly-null set of values + if ((img == null) || (typeof img === 'object' && img.url == null)) { + return { id: null, url: null, blurhash: null }; + } + return this.apImageService.resolveImage(user, img).catch(() => null); })); + /* + we don't want to return nulls on errors! if the database fields + are already null, nothing changes; if the database has old + values, we should keep those. The exception is if the remote has + actually removed the images: in that case, the block above + returns the special {id:null}&c value, and we return those + */ return { - avatarId: avatar?.id ?? null, - bannerId: banner?.id ?? null, - backgroundId: background?.id ?? null, - avatarUrl: avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null, - bannerUrl: banner ? this.driveFileEntityService.getPublicUrl(banner) : null, - backgroundUrl: background ? this.driveFileEntityService.getPublicUrl(background) : null, - avatarBlurhash: avatar?.blurhash ?? null, - bannerBlurhash: banner?.blurhash ?? null, - backgroundBlurhash: background?.blurhash ?? null + ...( avatar ? { + avatarId: avatar.id, + avatarUrl: avatar.url ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null, + avatarBlurhash: avatar.blurhash, + } : {}), + ...( banner ? { + bannerId: banner.id, + bannerUrl: banner.url ? this.driveFileEntityService.getPublicUrl(banner) : null, + bannerBlurhash: banner.blurhash, + } : {}), + ...( background ? { + backgroundId: background.id, + backgroundUrl: background.url ? this.driveFileEntityService.getPublicUrl(background) : null, + backgroundBlurhash: background.blurhash, + } : {}), }; } @@ -640,7 +660,7 @@ export class ApPersonService implements OnModuleInit { // ã¨ã‚Šã‚ãˆãšidを別ã®æ™‚é–“ã§ç”Ÿæˆã—ã¦é †ç•ªã‚’ç¶æŒ let td = 0; - for (const note of featuredNotes.filter((note): note is MiNote => note != null)) { + for (const note of featuredNotes.filter(isNotNull)) { td -= 1000; transactionalEntityManager.insert(MiUserNotePining, { id: this.idService.gen(Date.now() + td), diff --git a/packages/backend/src/core/activitypub/models/ApQuestionService.ts b/packages/backend/src/core/activitypub/models/ApQuestionService.ts index 27bd62268b3c4d71b4dc28db911644ded335d314..d1936cfe1dff4e6ba863a6df74cb832aa780e567 100644 --- a/packages/backend/src/core/activitypub/models/ApQuestionService.ts +++ b/packages/backend/src/core/activitypub/models/ApQuestionService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -10,6 +10,7 @@ import type { Config } from '@/config.js'; import type { IPoll } from '@/models/Poll.js'; import type Logger from '@/logger.js'; import { bindThis } from '@/decorators.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { isQuestion } from '../type.js'; import { ApLoggerService } from '../ApLoggerService.js'; import { ApResolverService } from '../ApResolverService.js'; @@ -51,7 +52,7 @@ export class ApQuestionService { const choices = question[multiple ? 'anyOf' : 'oneOf'] ?.map((x) => x.name) - .filter((x): x is string => typeof x === 'string') + .filter(isNotNull) ?? []; const votes = question[multiple ? 'anyOf' : 'oneOf']?.map((x) => x.replies?.totalItems ?? x._misskey_votes ?? 0); diff --git a/packages/backend/src/core/activitypub/models/icon.ts b/packages/backend/src/core/activitypub/models/icon.ts index 9fed78020d9b78193fd064288c81fdc13f252f58..5722507a3b9f34b4c9c3106e3306ff07c2d93afa 100644 --- a/packages/backend/src/core/activitypub/models/icon.ts +++ b/packages/backend/src/core/activitypub/models/icon.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/activitypub/models/identifier.ts b/packages/backend/src/core/activitypub/models/identifier.ts index 22a7b0a76e4152f173453ba3e460b25b04ad074f..dce4f410b4c8e62e4241095c04b281f256f905e1 100644 --- a/packages/backend/src/core/activitypub/models/identifier.ts +++ b/packages/backend/src/core/activitypub/models/identifier.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/activitypub/models/tag.ts b/packages/backend/src/core/activitypub/models/tag.ts index 772ea1186415debb23c011ad77e116d915a42018..e7ceec3262a38fd9baf305dbc7a3740b2f979490 100644 --- a/packages/backend/src/core/activitypub/models/tag.ts +++ b/packages/backend/src/core/activitypub/models/tag.ts @@ -1,9 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { toArray } from '@/misc/prelude/array.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { isHashtag } from '../type.js'; import type { IObject, IApHashtag } from '../type.js'; @@ -15,7 +16,7 @@ export function extractApHashtags(tags: IObject | IObject[] | null | undefined): return hashtags.map(tag => { const m = tag.name.match(/^#(.+)/); return m ? m[1] : null; - }).filter((x): x is string => x != null); + }).filter(isNotNull); } export function extractApHashtagObjects(tags: IObject | IObject[] | null | undefined): IApHashtag[] { diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts index ce3317ef72a016c1bbc578ea35dd82881265581a..716515840c3103a2b1eca241db7e4ae26b213382 100644 --- a/packages/backend/src/core/activitypub/type.ts +++ b/packages/backend/src/core/activitypub/type.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/ChartLoggerService.ts b/packages/backend/src/core/chart/ChartLoggerService.ts index bd90efec647a97dc9a35ecebb0a252abcef4e2e6..afc728d5647854677dae54ab1b356d8f0dafaea1 100644 --- a/packages/backend/src/core/chart/ChartLoggerService.ts +++ b/packages/backend/src/core/chart/ChartLoggerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/ChartManagementService.ts b/packages/backend/src/core/chart/ChartManagementService.ts index f751a68cb41e8921b2372a67173a80da607ca7ad..79681370a17d3f04eb0065ed0aa9713197bc10b2 100644 --- a/packages/backend/src/core/chart/ChartManagementService.ts +++ b/packages/backend/src/core/chart/ChartManagementService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/active-users.ts b/packages/backend/src/core/chart/charts/active-users.ts index f0918e059c8d72c86f4aed488fa2bccfb411c376..05905f3782937853699b8ef10e113c9b2994b300 100644 --- a/packages/backend/src/core/chart/charts/active-users.ts +++ b/packages/backend/src/core/chart/charts/active-users.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/ap-request.ts b/packages/backend/src/core/chart/charts/ap-request.ts index 03c9b42be17c1f619ac8855e592137a5c2cd7d50..04e771a95b4e6dea017a04a10293d94542a89e88 100644 --- a/packages/backend/src/core/chart/charts/ap-request.ts +++ b/packages/backend/src/core/chart/charts/ap-request.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/drive.ts b/packages/backend/src/core/chart/charts/drive.ts index bbcbf1a95589777c60762c90359a87bc9ecba0be..613e074a9f1d2e102d4cca281ddd20f2a3d82b74 100644 --- a/packages/backend/src/core/chart/charts/drive.ts +++ b/packages/backend/src/core/chart/charts/drive.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/entities/active-users.ts b/packages/backend/src/core/chart/charts/entities/active-users.ts index e68022ef29927740296d8308ed297fab516cd216..fc2b88a2bb5dea45230605eb9c3b593bb2aaefc3 100644 --- a/packages/backend/src/core/chart/charts/entities/active-users.ts +++ b/packages/backend/src/core/chart/charts/entities/active-users.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/entities/ap-request.ts b/packages/backend/src/core/chart/charts/entities/ap-request.ts index a82451525537a901a5e4a216b884ca02bf807890..93e47e081b39810d43ddbcd2c62d81cb40be5956 100644 --- a/packages/backend/src/core/chart/charts/entities/ap-request.ts +++ b/packages/backend/src/core/chart/charts/entities/ap-request.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/entities/drive.ts b/packages/backend/src/core/chart/charts/entities/drive.ts index 4a56bd45c54df6b43bfe926b45b8bddf2248f275..4ea16da38c3258f24f113255ff876bd74b60c036 100644 --- a/packages/backend/src/core/chart/charts/entities/drive.ts +++ b/packages/backend/src/core/chart/charts/entities/drive.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/entities/federation.ts b/packages/backend/src/core/chart/charts/entities/federation.ts index e067c71a7f5896da34c761f2dfcc67b47471a153..5ed780434342e3a3bb26dff37fb576dce02c013f 100644 --- a/packages/backend/src/core/chart/charts/entities/federation.ts +++ b/packages/backend/src/core/chart/charts/entities/federation.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/entities/instance.ts b/packages/backend/src/core/chart/charts/entities/instance.ts index 4ea10d56d1795e714f41979273a21af47ec951b8..d0cac3e73f09a0da211b51ec65c91bdd1292f888 100644 --- a/packages/backend/src/core/chart/charts/entities/instance.ts +++ b/packages/backend/src/core/chart/charts/entities/instance.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/entities/notes.ts b/packages/backend/src/core/chart/charts/entities/notes.ts index 26e2529b174e84cd9029f33c55adb02877d9c362..325236ab3521f5366a6500ab173c0a8ff2c20d4f 100644 --- a/packages/backend/src/core/chart/charts/entities/notes.ts +++ b/packages/backend/src/core/chart/charts/entities/notes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/entities/per-user-drive.ts b/packages/backend/src/core/chart/charts/entities/per-user-drive.ts index aec3dd5140369a780cf582c780c516c0a1d3546c..25d4619dde0e7bac64925834e69b9f36c42d8f03 100644 --- a/packages/backend/src/core/chart/charts/entities/per-user-drive.ts +++ b/packages/backend/src/core/chart/charts/entities/per-user-drive.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/entities/per-user-following.ts b/packages/backend/src/core/chart/charts/entities/per-user-following.ts index afb5813058d53ac60e2c93ba303b22a70077e9e2..1618bd22f318eb3c6315fdf8d830ccaca4006237 100644 --- a/packages/backend/src/core/chart/charts/entities/per-user-following.ts +++ b/packages/backend/src/core/chart/charts/entities/per-user-following.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/entities/per-user-notes.ts b/packages/backend/src/core/chart/charts/entities/per-user-notes.ts index 60a0b01c8e0b26ffce43d8a65753afe70b9e9d81..30404b2e48c8ba328b4ae2af80e2b345f5be36b2 100644 --- a/packages/backend/src/core/chart/charts/entities/per-user-notes.ts +++ b/packages/backend/src/core/chart/charts/entities/per-user-notes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/entities/per-user-pv.ts b/packages/backend/src/core/chart/charts/entities/per-user-pv.ts index 78d4464d7e275db143b6c1d13d6f28a6721bb615..7a903afad4dd4a003dbcd198023159ab9796c3a9 100644 --- a/packages/backend/src/core/chart/charts/entities/per-user-pv.ts +++ b/packages/backend/src/core/chart/charts/entities/per-user-pv.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/entities/per-user-reactions.ts b/packages/backend/src/core/chart/charts/entities/per-user-reactions.ts index 761101d4793341b9f34ba67d40dbc755df9818fb..bb62bb238625532b768344801682cfe40875b7df 100644 --- a/packages/backend/src/core/chart/charts/entities/per-user-reactions.ts +++ b/packages/backend/src/core/chart/charts/entities/per-user-reactions.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/entities/test-grouped.ts b/packages/backend/src/core/chart/charts/entities/test-grouped.ts index 15eb1fd1f8ae794835fa5af3fcd4418cebbdb4f0..599c1dc1368d704e3065ba5453fc96bd02ac3c95 100644 --- a/packages/backend/src/core/chart/charts/entities/test-grouped.ts +++ b/packages/backend/src/core/chart/charts/entities/test-grouped.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/entities/test-intersection.ts b/packages/backend/src/core/chart/charts/entities/test-intersection.ts index 2ef63977a5f847efd9c8b5b0ff691efe7dcd4095..d29b39716c1fee16096c1b41a15c1bd5f608401d 100644 --- a/packages/backend/src/core/chart/charts/entities/test-intersection.ts +++ b/packages/backend/src/core/chart/charts/entities/test-intersection.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/entities/test-unique.ts b/packages/backend/src/core/chart/charts/entities/test-unique.ts index 56233585db3a806eeebd2f5d678923d0d20e920b..bdaa1716edd69193583e4ddfd381e820dfb23ba3 100644 --- a/packages/backend/src/core/chart/charts/entities/test-unique.ts +++ b/packages/backend/src/core/chart/charts/entities/test-unique.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/entities/test.ts b/packages/backend/src/core/chart/charts/entities/test.ts index 163db4e79f96fb614d8c7c4113ec87f5a9bf3b64..c80ff55c99dcabff1f2d0a54bb650900e7bb186e 100644 --- a/packages/backend/src/core/chart/charts/entities/test.ts +++ b/packages/backend/src/core/chart/charts/entities/test.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/entities/users.ts b/packages/backend/src/core/chart/charts/entities/users.ts index c7bffd3fd4cd7a230f364349034c8230c8863061..f94a5029d7efc2d00e2a4db3f71824414cc793ad 100644 --- a/packages/backend/src/core/chart/charts/entities/users.ts +++ b/packages/backend/src/core/chart/charts/entities/users.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/federation.ts b/packages/backend/src/core/chart/charts/federation.ts index fc474b002b22b2768f9f60ddd47dc287ffe22b69..5e4555ee96f52f4068e496e3cdf4a248dc88ba92 100644 --- a/packages/backend/src/core/chart/charts/federation.ts +++ b/packages/backend/src/core/chart/charts/federation.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/instance.ts b/packages/backend/src/core/chart/charts/instance.ts index 9df0afb02e5e2b28626b0389519147e3d236fdcc..97f3bc6f2bd1f3a8615aa45ff7f8a27fba29a7fd 100644 --- a/packages/backend/src/core/chart/charts/instance.ts +++ b/packages/backend/src/core/chart/charts/instance.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/notes.ts b/packages/backend/src/core/chart/charts/notes.ts index df3295dbac210bbdf835d1e4c440d6ce96ad7724..f763b5fffa73608d5011915a476cc49ef0a004b4 100644 --- a/packages/backend/src/core/chart/charts/notes.ts +++ b/packages/backend/src/core/chart/charts/notes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/per-user-drive.ts b/packages/backend/src/core/chart/charts/per-user-drive.ts index 18354359c83659dc09dd8d1eb1cb884d1745d423..404964d8b7c5d72a993a6e5c6d875c72a2cc5dc7 100644 --- a/packages/backend/src/core/chart/charts/per-user-drive.ts +++ b/packages/backend/src/core/chart/charts/per-user-drive.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/per-user-following.ts b/packages/backend/src/core/chart/charts/per-user-following.ts index 79bff2cb6689223f6c640daa46c3ac77ca8b8a2c..588ac638de90851b1550d4eaeee452d14adf3796 100644 --- a/packages/backend/src/core/chart/charts/per-user-following.ts +++ b/packages/backend/src/core/chart/charts/per-user-following.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/per-user-notes.ts b/packages/backend/src/core/chart/charts/per-user-notes.ts index 0db0e6f07fc9012296e0a17e786751629a80f170..e4900772bb9f19954c66483399f78f820de05b94 100644 --- a/packages/backend/src/core/chart/charts/per-user-notes.ts +++ b/packages/backend/src/core/chart/charts/per-user-notes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/per-user-pv.ts b/packages/backend/src/core/chart/charts/per-user-pv.ts index cf1b4c71f6acf1a5f5d0624d3da598edd2285ce4..31708fefa86a081bf46116759d87f6af9c13d0c3 100644 --- a/packages/backend/src/core/chart/charts/per-user-pv.ts +++ b/packages/backend/src/core/chart/charts/per-user-pv.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/per-user-reactions.ts b/packages/backend/src/core/chart/charts/per-user-reactions.ts index 9f4f6e965125e2c7d8a317b92e38696203b63610..c29c4d28709ea4928ce62021b324a666f67eb582 100644 --- a/packages/backend/src/core/chart/charts/per-user-reactions.ts +++ b/packages/backend/src/core/chart/charts/per-user-reactions.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/test-grouped.ts b/packages/backend/src/core/chart/charts/test-grouped.ts index 00fb87223761998fd65e40ec4215b02f8c058b03..7a2844f4ed41bff4da052776cd64a8c784afb365 100644 --- a/packages/backend/src/core/chart/charts/test-grouped.ts +++ b/packages/backend/src/core/chart/charts/test-grouped.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/test-intersection.ts b/packages/backend/src/core/chart/charts/test-intersection.ts index 45a7e805c557446e5dc5f644f0d414714275da4b..b8d0556c9faceb4572d83d43b44167870bc5912b 100644 --- a/packages/backend/src/core/chart/charts/test-intersection.ts +++ b/packages/backend/src/core/chart/charts/test-intersection.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/test-unique.ts b/packages/backend/src/core/chart/charts/test-unique.ts index e9d38eaf136c7a917a9d88abdf9896229225d235..f94e008059a355843430e10535492e7ad0f5116c 100644 --- a/packages/backend/src/core/chart/charts/test-unique.ts +++ b/packages/backend/src/core/chart/charts/test-unique.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/test.ts b/packages/backend/src/core/chart/charts/test.ts index 4dd6063b5bfb3178f51fd9d0d429d7b37b11500c..a90dc8f99b4f28d363724f70d6a551fc446ac361 100644 --- a/packages/backend/src/core/chart/charts/test.ts +++ b/packages/backend/src/core/chart/charts/test.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/charts/users.ts b/packages/backend/src/core/chart/charts/users.ts index c2026c2aeafbe48cf8701553e318fb348ee9b038..d148fc629b1d575b30b8c5267b2075c48765190d 100644 --- a/packages/backend/src/core/chart/charts/users.ts +++ b/packages/backend/src/core/chart/charts/users.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/chart/core.ts b/packages/backend/src/core/chart/core.ts index 8d0a89f2d6ae8cb589a1bd57fb13da21ebbea36e..aa0cb9dc2bd5693f58f00b6a95c74921fe8c2e39 100644 --- a/packages/backend/src/core/chart/core.ts +++ b/packages/backend/src/core/chart/core.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -94,6 +94,29 @@ type ToJsonSchema<S> = { }; export function getJsonSchema<S extends Schema>(schema: S): ToJsonSchema<Unflatten<ChartResult<S>>> { + const unflatten = (str: string, parent: Record<string, any>) => { + const keys = str.split('.'); + const key = keys.shift(); + const nextKey = keys[0]; + + if (key == null) return; + + if (parent.properties[key] == null) { + parent.properties[key] = nextKey ? { + type: 'object', + properties: {}, + required: [], + } : { + type: 'array', + items: { + type: 'number', + }, + }; + } + + if (nextKey) unflatten(keys.join('.'), parent.properties[key] as Record<string, any>); + }; + const jsonSchema = { type: 'object', properties: {} as Record<string, unknown>, @@ -101,10 +124,7 @@ export function getJsonSchema<S extends Schema>(schema: S): ToJsonSchema<Unflatt }; for (const k in schema) { - jsonSchema.properties[k] = { - type: 'array', - items: { type: 'number' }, - }; + unflatten(k, jsonSchema); } return jsonSchema as ToJsonSchema<Unflatten<ChartResult<S>>>; diff --git a/packages/backend/src/core/chart/entities.ts b/packages/backend/src/core/chart/entities.ts index b6a1299a2f7e06ca0a098926ffaabbc3b625e4e0..e424f2c8c5c258220234f2b65a9a81979bf94219 100644 --- a/packages/backend/src/core/chart/entities.ts +++ b/packages/backend/src/core/chart/entities.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts index 97de891ecebe135d28ea530a790fe1cc8b9b9de7..49f256d870df469b30e114720be93b357a007955 100644 --- a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -38,13 +38,13 @@ export class AbuseUserReportEntityService { targetUserId: report.targetUserId, assigneeId: report.assigneeId, reporter: this.userEntityService.pack(report.reporter ?? report.reporterId, null, { - detail: true, + schema: 'UserDetailedNotMe', }), targetUser: this.userEntityService.pack(report.targetUser ?? report.targetUserId, null, { - detail: true, + schema: 'UserDetailedNotMe', }), assignee: report.assigneeId ? this.userEntityService.pack(report.assignee ?? report.assigneeId, null, { - detail: true, + schema: 'UserDetailedNotMe', }) : null, forwarded: report.forwarded, }); diff --git a/packages/backend/src/core/entities/AntennaEntityService.ts b/packages/backend/src/core/entities/AntennaEntityService.ts index 265a61e8ad8a886cfae3e3756a362763f9e5f5ef..64d6a3c97841a70df80995554ed4554062d862c8 100644 --- a/packages/backend/src/core/entities/AntennaEntityService.ts +++ b/packages/backend/src/core/entities/AntennaEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/entities/AppEntityService.ts b/packages/backend/src/core/entities/AppEntityService.ts index 14a93cda5ba0e3c011a2e03a9b33e0cf64e5718f..785b84689a3b140d575eb5db777eb73d09db12d4 100644 --- a/packages/backend/src/core/entities/AppEntityService.ts +++ b/packages/backend/src/core/entities/AppEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/entities/AuthSessionEntityService.ts b/packages/backend/src/core/entities/AuthSessionEntityService.ts index fd356cc89d17f3ea2ac4d5361a1fcb4a18840a5d..72873680c9fe0f40420945837231a5b31ec78ed5 100644 --- a/packages/backend/src/core/entities/AuthSessionEntityService.ts +++ b/packages/backend/src/core/entities/AuthSessionEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/entities/BlockingEntityService.ts b/packages/backend/src/core/entities/BlockingEntityService.ts index b4760346b74707e0a70d9886564058efc28458e0..c8c1520ceb502a9b6d99171b1583c109704fd710 100644 --- a/packages/backend/src/core/entities/BlockingEntityService.ts +++ b/packages/backend/src/core/entities/BlockingEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -37,7 +37,7 @@ export class BlockingEntityService { createdAt: this.idService.parse(blocking.id).date.toISOString(), blockeeId: blocking.blockeeId, blockee: this.userEntityService.pack(blocking.blockeeId, me, { - detail: true, + schema: 'UserDetailedNotMe', }), }); } diff --git a/packages/backend/src/core/entities/ChannelEntityService.ts b/packages/backend/src/core/entities/ChannelEntityService.ts index 305946b8a648b3e9b3d37baa2a2690c540299394..1ba7ca8e57e7e80bca90d70184c014ab1851e54b 100644 --- a/packages/backend/src/core/entities/ChannelEntityService.ts +++ b/packages/backend/src/core/entities/ChannelEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -51,14 +51,14 @@ export class ChannelEntityService { const banner = channel.bannerId ? await this.driveFilesRepository.findOneBy({ id: channel.bannerId }) : null; - const isFollowing = meId ? await this.channelFollowingsRepository.exist({ + const isFollowing = meId ? await this.channelFollowingsRepository.exists({ where: { followerId: meId, followeeId: channel.id, }, }) : false; - const isFavorited = meId ? await this.channelFavoritesRepository.exist({ + const isFavorited = meId ? await this.channelFavoritesRepository.exists({ where: { userId: meId, channelId: channel.id, diff --git a/packages/backend/src/core/entities/ClipEntityService.ts b/packages/backend/src/core/entities/ClipEntityService.ts index 96422894fd47f4afe2369b253e994d9d41f4a2fd..26fcd6714d16dd9fb82ba0b22769ff104e635041 100644 --- a/packages/backend/src/core/entities/ClipEntityService.ts +++ b/packages/backend/src/core/entities/ClipEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -46,7 +46,7 @@ export class ClipEntityService { description: clip.description, isPublic: clip.isPublic, favoritedCount: await this.clipFavoritesRepository.countBy({ clipId: clip.id }), - isFavorited: meId ? await this.clipFavoritesRepository.exist({ where: { clipId: clip.id, userId: meId } }) : undefined, + isFavorited: meId ? await this.clipFavoritesRepository.exists({ where: { clipId: clip.id, userId: meId } }) : undefined, }); } diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index 14be00036766865132b82d04554877147b909bb0..8affe2b3bf84d3122b3891726890aa274f851f68 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -259,7 +259,7 @@ export class DriveFileEntityService { options?: PackOptions, ): Promise<Packed<'DriveFile'>[]> { const items = await Promise.all(files.map(f => this.packNullable(f, options))); - return items.filter((x): x is Packed<'DriveFile'> => x != null); + return items.filter(isNotNull); } @bindThis diff --git a/packages/backend/src/core/entities/DriveFolderEntityService.ts b/packages/backend/src/core/entities/DriveFolderEntityService.ts index 8fa78154b98c8a3e1c867159277abc3a281d36da..299f23ad38bcdf03982e78725401a26cff61039b 100644 --- a/packages/backend/src/core/entities/DriveFolderEntityService.ts +++ b/packages/backend/src/core/entities/DriveFolderEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index 5b97cfad5ee70f8684ecdd1f00f1ffefcd6ff5eb..841bd731c0cb7d19965819f941aaf8295780c5dd 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -31,6 +31,7 @@ export class EmojiEntityService { category: emoji.category, // || emoji.originalUrl ã—ã¦ã‚‹ã®ã¯å¾Œæ–¹äº’æ›æ€§ã®ãŸã‚(publicUrlã¯stringãªã®ã§??ã¯ã ã‚) url: emoji.publicUrl || emoji.originalUrl, + localOnly: emoji.localOnly ? true : undefined, isSensitive: emoji.isSensitive ? true : undefined, roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length > 0 ? emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : undefined, }; diff --git a/packages/backend/src/core/entities/FlashEntityService.ts b/packages/backend/src/core/entities/FlashEntityService.ts index dc335d9975e1841b8e62000c50b944a65a9bbe8f..db4cf6d360d6ab9c7875187d33832f2de5ad3d4a 100644 --- a/packages/backend/src/core/entities/FlashEntityService.ts +++ b/packages/backend/src/core/entities/FlashEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -42,12 +42,12 @@ export class FlashEntityService { createdAt: this.idService.parse(flash.id).date.toISOString(), updatedAt: flash.updatedAt.toISOString(), userId: flash.userId, - user: this.userEntityService.pack(flash.user ?? flash.userId, me), // { detail: true } ã™ã‚‹ã¨ç„¡é™ãƒ«ãƒ¼ãƒ—ã™ã‚‹ã®ã§æ³¨æ„ + user: this.userEntityService.pack(flash.user ?? flash.userId, me), // { schema: 'UserDetailed' } ã™ã‚‹ã¨ç„¡é™ãƒ«ãƒ¼ãƒ—ã™ã‚‹ã®ã§æ³¨æ„ title: flash.title, summary: flash.summary, script: flash.script, likedCount: flash.likedCount, - isLiked: meId ? await this.flashLikesRepository.exist({ where: { flashId: flash.id, userId: meId } }) : undefined, + isLiked: meId ? await this.flashLikesRepository.exists({ where: { flashId: flash.id, userId: meId } }) : undefined, }); } diff --git a/packages/backend/src/core/entities/FlashLikeEntityService.ts b/packages/backend/src/core/entities/FlashLikeEntityService.ts index 2eff86217a658660de8b02a0bc852bcf55190db5..6e0b9d6e11f0bd2b4b09e26ae390c65fc57de608 100644 --- a/packages/backend/src/core/entities/FlashLikeEntityService.ts +++ b/packages/backend/src/core/entities/FlashLikeEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/entities/FollowRequestEntityService.ts b/packages/backend/src/core/entities/FollowRequestEntityService.ts index 0e0fec9f46adddb8e1775b45d26d411c446fe220..763b75101faa4aa05cd8889f2311c7a160031229 100644 --- a/packages/backend/src/core/entities/FollowRequestEntityService.ts +++ b/packages/backend/src/core/entities/FollowRequestEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/entities/FollowingEntityService.ts b/packages/backend/src/core/entities/FollowingEntityService.ts index 52aa979677cdb07e8fe540a1fa9c64a1aa9a9d8b..24cd33e3f7d429baed956ae2e9cf8bb647aebadf 100644 --- a/packages/backend/src/core/entities/FollowingEntityService.ts +++ b/packages/backend/src/core/entities/FollowingEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -89,10 +89,10 @@ export class FollowingEntityService { followeeId: following.followeeId, followerId: following.followerId, followee: opts.populateFollowee ? this.userEntityService.pack(following.followee ?? following.followeeId, me, { - detail: true, + schema: 'UserDetailedNotMe', }) : undefined, follower: opts.populateFollower ? this.userEntityService.pack(following.follower ?? following.followerId, me, { - detail: true, + schema: 'UserDetailedNotMe', }) : undefined, }); } diff --git a/packages/backend/src/core/entities/GalleryLikeEntityService.ts b/packages/backend/src/core/entities/GalleryLikeEntityService.ts index e7407018880e9b5c82fef286115ab4050b0a981d..f199a81b4ddef476d089a0d826e53241a0eedd0b 100644 --- a/packages/backend/src/core/entities/GalleryLikeEntityService.ts +++ b/packages/backend/src/core/entities/GalleryLikeEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/entities/GalleryPostEntityService.ts b/packages/backend/src/core/entities/GalleryPostEntityService.ts index d7b960e0d9c0b6999cf91f8160128c7373d59224..101182a9e5aa9cf336e52990a11878620eaae8a0 100644 --- a/packages/backend/src/core/entities/GalleryPostEntityService.ts +++ b/packages/backend/src/core/entities/GalleryPostEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -53,7 +53,7 @@ export class GalleryPostEntityService { tags: post.tags.length > 0 ? post.tags : undefined, isSensitive: post.isSensitive, likedCount: post.likedCount, - isLiked: meId ? await this.galleryLikesRepository.exist({ where: { postId: post.id, userId: meId } }) : undefined, + isLiked: meId ? await this.galleryLikesRepository.exists({ where: { postId: post.id, userId: meId } }) : undefined, }); } diff --git a/packages/backend/src/core/entities/HashtagEntityService.ts b/packages/backend/src/core/entities/HashtagEntityService.ts index 006e267b12c1d3a5311de03e26ab676e3eccd44f..d798b15807c122a23d6c6c5d4c7ae6c14c9fbc44 100644 --- a/packages/backend/src/core/entities/HashtagEntityService.ts +++ b/packages/backend/src/core/entities/HashtagEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts index 515b356dee13a57a89e6e28cf0526e727b899fe6..b4a518a1c660f313c2d5ac868f3029757aefee73 100644 --- a/packages/backend/src/core/entities/InstanceEntityService.ts +++ b/packages/backend/src/core/entities/InstanceEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -8,12 +8,15 @@ import type { Packed } from '@/misc/json-schema.js'; import type { MiInstance } from '@/models/Instance.js'; import { MetaService } from '@/core/MetaService.js'; import { bindThis } from '@/decorators.js'; -import { UtilityService } from '../UtilityService.js'; +import { UtilityService } from '@/core/UtilityService.js'; +import { RoleService } from '@/core/RoleService.js'; +import { MiUser } from '@/models/User.js'; @Injectable() export class InstanceEntityService { constructor( private metaService: MetaService, + private roleService: RoleService, private utilityService: UtilityService, ) { @@ -22,8 +25,11 @@ export class InstanceEntityService { @bindThis public async pack( instance: MiInstance, + me?: { id: MiUser['id']; } | null | undefined, ): Promise<Packed<'FederationInstance'>> { const meta = await this.metaService.fetch(); + const iAmModerator = me ? await this.roleService.isModerator(me as MiUser) : false; + return { id: instance.id, firstRetrievedAt: instance.firstRetrievedAt.toISOString(), @@ -49,6 +55,7 @@ export class InstanceEntityService { infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null, latestRequestReceivedAt: instance.latestRequestReceivedAt ? instance.latestRequestReceivedAt.toISOString() : null, isNSFW: instance.isNSFW, + moderationNote: iAmModerator ? instance.moderationNote : null, }; } diff --git a/packages/backend/src/core/entities/InviteCodeEntityService.ts b/packages/backend/src/core/entities/InviteCodeEntityService.ts index 0f15fb5ab2e5a4a9e432f47b757caa9ec67709c7..891543bc0fec701138ed0cfcb02a263657878753 100644 --- a/packages/backend/src/core/entities/InviteCodeEntityService.ts +++ b/packages/backend/src/core/entities/InviteCodeEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts new file mode 100644 index 0000000000000000000000000000000000000000..7e3926c431ac4b0141b967684646ac3868d9c6e2 --- /dev/null +++ b/packages/backend/src/core/entities/MetaEntityService.ts @@ -0,0 +1,157 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Brackets } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; +import JSON5 from 'json5'; +import type { Packed } from '@/misc/json-schema.js'; +import type { MiMeta } from '@/models/Meta.js'; +import type { AdsRepository } from '@/models/_.js'; +import { MetaService } from '@/core/MetaService.js'; +import { bindThis } from '@/decorators.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { InstanceActorService } from '@/core/InstanceActorService.js'; +import type { Config } from '@/config.js'; +import { DI } from '@/di-symbols.js'; +import { DEFAULT_POLICIES } from '@/core/RoleService.js'; + +@Injectable() +export class MetaEntityService { + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.adsRepository) + private adsRepository: AdsRepository, + + private userEntityService: UserEntityService, + private metaService: MetaService, + private instanceActorService: InstanceActorService, + ) { } + + @bindThis + public async pack(meta?: MiMeta): Promise<Packed<'MetaLite'>> { + let instance = meta; + + if (!instance) { + instance = await this.metaService.fetch(); + } + + const ads = await this.adsRepository.createQueryBuilder('ads') + .where('ads.expiresAt > :now', { now: new Date() }) + .andWhere('ads.startsAt <= :now', { now: new Date() }) + .andWhere(new Brackets(qb => { + // 曜日ã®ãƒ“ットフラグを確èªã™ã‚‹ + qb.where('ads.dayOfWeek & :dayOfWeek > 0', { dayOfWeek: 1 << new Date().getDay() }) + .orWhere('ads.dayOfWeek = 0'); + })) + .getMany(); + + const packed: Packed<'MetaLite'> = { + maintainerName: instance.maintainerName, + maintainerEmail: instance.maintainerEmail, + + version: this.config.version, + providesTarball: this.config.publishTarballInsteadOfProvideRepositoryUrl, + + name: instance.name, + shortName: instance.shortName, + uri: this.config.url, + description: instance.description, + langs: instance.langs, + tosUrl: instance.termsOfServiceUrl, + repositoryUrl: instance.repositoryUrl, + feedbackUrl: instance.feedbackUrl, + impressumUrl: instance.impressumUrl, + donationUrl: instance.donationUrl, + privacyPolicyUrl: instance.privacyPolicyUrl, + disableRegistration: instance.disableRegistration, + emailRequiredForSignup: instance.emailRequiredForSignup, + approvalRequiredForSignup: instance.approvalRequiredForSignup, + enableHcaptcha: instance.enableHcaptcha, + hcaptchaSiteKey: instance.hcaptchaSiteKey, + enableMcaptcha: instance.enableMcaptcha, + mcaptchaSiteKey: instance.mcaptchaSitekey, + mcaptchaInstanceUrl: instance.mcaptchaInstanceUrl, + enableRecaptcha: instance.enableRecaptcha, + enableAchievements: instance.enableAchievements, + recaptchaSiteKey: instance.recaptchaSiteKey, + enableTurnstile: instance.enableTurnstile, + turnstileSiteKey: instance.turnstileSiteKey, + swPublickey: instance.swPublicKey, + themeColor: instance.themeColor, + mascotImageUrl: instance.mascotImageUrl ?? '/assets/ai.png', + bannerUrl: instance.bannerUrl, + infoImageUrl: instance.infoImageUrl, + serverErrorImageUrl: instance.serverErrorImageUrl, + notFoundImageUrl: instance.notFoundImageUrl, + iconUrl: instance.iconUrl, + backgroundImageUrl: instance.backgroundImageUrl, + logoImageUrl: instance.logoImageUrl, + maxNoteTextLength: this.config.maxNoteLength, + // クライアントã®æ‰‹é–“を減らã™ãŸã‚ã‚らã‹ã˜ã‚JSONã«å¤‰æ›ã—ã¦ãŠã + defaultLightTheme: instance.defaultLightTheme ? JSON.stringify(JSON5.parse(instance.defaultLightTheme)) : null, + defaultDarkTheme: instance.defaultDarkTheme ? JSON.stringify(JSON5.parse(instance.defaultDarkTheme)) : null, + defaultLike: instance.defaultLike, + ads: ads.map(ad => ({ + id: ad.id, + url: ad.url, + place: ad.place, + ratio: ad.ratio, + imageUrl: ad.imageUrl, + dayOfWeek: ad.dayOfWeek, + })), + notesPerOneAd: instance.notesPerOneAd, + enableEmail: instance.enableEmail, + enableServiceWorker: instance.enableServiceWorker, + + translatorAvailable: instance.deeplAuthKey != null || instance.deeplFreeMode && instance.deeplFreeInstance != null, + + serverRules: instance.serverRules, + + policies: { ...DEFAULT_POLICIES, ...instance.policies }, + + mediaProxy: this.config.mediaProxy, + }; + + return packed; + } + + @bindThis + public async packDetailed(meta?: MiMeta): Promise<Packed<'MetaDetailed'>> { + let instance = meta; + + if (!instance) { + instance = await this.metaService.fetch(); + } + + const packed = await this.pack(instance); + + const proxyAccount = instance.proxyAccountId ? await this.userEntityService.pack(instance.proxyAccountId).catch(() => null) : null; + + const packDetailed: Packed<'MetaDetailed'> = { + ...packed, + cacheRemoteFiles: instance.cacheRemoteFiles, + cacheRemoteSensitiveFiles: instance.cacheRemoteSensitiveFiles, + requireSetup: !await this.instanceActorService.realLocalUsersPresent(), + proxyAccountName: proxyAccount ? proxyAccount.username : null, + features: { + localTimeline: instance.policies.ltlAvailable, + globalTimeline: instance.policies.gtlAvailable, + registration: !instance.disableRegistration, + emailRequiredForSignup: instance.emailRequiredForSignup, + hcaptcha: instance.enableHcaptcha, + recaptcha: instance.enableRecaptcha, + turnstile: instance.enableTurnstile, + objectStorage: instance.useObjectStorage, + serviceWorker: instance.enableServiceWorker, + miauth: true, + }, + }; + + return packDetailed; + } +} + diff --git a/packages/backend/src/core/entities/ModerationLogEntityService.ts b/packages/backend/src/core/entities/ModerationLogEntityService.ts index 6729ca2671d95b2db37ab1521f781d9bc7c1d809..205e147bd106b6eef4b094f419c5110d8bcb8e6d 100644 --- a/packages/backend/src/core/entities/ModerationLogEntityService.ts +++ b/packages/backend/src/core/entities/ModerationLogEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -37,7 +37,7 @@ export class ModerationLogEntityService { info: log.info, userId: log.userId, user: this.userEntityService.pack(log.user ?? log.userId, null, { - detail: true, + schema: 'UserDetailedNotMe', }), }); } diff --git a/packages/backend/src/core/entities/MutingEntityService.ts b/packages/backend/src/core/entities/MutingEntityService.ts index 9d672169bace1ccc976a4ae812275dbbc37df7af..0a52f429a2b2650c12e64adffbd1bea65b35a905 100644 --- a/packages/backend/src/core/entities/MutingEntityService.ts +++ b/packages/backend/src/core/entities/MutingEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -39,7 +39,7 @@ export class MutingEntityService { expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null, muteeId: muting.muteeId, mutee: this.userEntityService.pack(muting.muteeId, me, { - detail: true, + schema: 'UserDetailedNotMe', }), }); } diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index fee96bb80d708d3f4ebc862de46c834802f20e88..86a8670f29c528f00fe82fc6a229f4d47140c7e9 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -114,7 +114,7 @@ export class NoteEntityService implements OnModuleInit { hide = false; } else { if (packedNote.renote) { - const isFollowing = await this.followingsRepository.exist({ + const isFollowing = await this.followingsRepository.exists({ where: { followeeId: packedNote.renote.userId, followerId: meId, @@ -124,7 +124,7 @@ export class NoteEntityService implements OnModuleInit { hide = !isFollowing; } else { // フォãƒãƒ¯ãƒ¼ã‹ã©ã†ã‹ - const isFollowing = await this.followingsRepository.exist({ + const isFollowing = await this.followingsRepository.exists({ where: { followeeId: packedNote.userId, followerId: meId, @@ -181,7 +181,7 @@ export class NoteEntityService implements OnModuleInit { return { multiple: poll.multiple, - expiresAt: poll.expiresAt, + expiresAt: poll.expiresAt?.toISOString() ?? null, choices, }; } @@ -342,9 +342,7 @@ export class NoteEntityService implements OnModuleInit { createdAt: this.idService.parse(note.id).date.toISOString(), updatedAt: note.updatedAt ? note.updatedAt.toISOString() : undefined, userId: note.userId, - user: this.userEntityService.pack(note.user ?? note.userId, me, { - detail: false, - }), + user: this.userEntityService.pack(note.user ?? note.userId, me), text: text, cw: note.cw, visibility: note.visibility, @@ -369,6 +367,7 @@ export class NoteEntityService implements OnModuleInit { color: channel.color, isSensitive: channel.isSensitive, allowRenoteToExternal: channel.allowRenoteToExternal, + userId: channel.userId, } : undefined, mentions: note.mentions && note.mentions.length > 0 ? note.mentions : undefined, uri: note.uri ?? undefined, diff --git a/packages/backend/src/core/entities/NoteFavoriteEntityService.ts b/packages/backend/src/core/entities/NoteFavoriteEntityService.ts index 1c9aed413f05d53326889546a7b91877f4c53277..3cdafe48ada264c11c6cccf609ce20d70281588e 100644 --- a/packages/backend/src/core/entities/NoteFavoriteEntityService.ts +++ b/packages/backend/src/core/entities/NoteFavoriteEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts index f4aba3e543e3c593700f9c88aa1fca851b367513..3f4fa3cf969dacd52406fab705d8815a24d7bdd4 100644 --- a/packages/backend/src/core/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -69,4 +69,19 @@ export class NoteReactionEntityService implements OnModuleInit { } : {}), }; } + + @bindThis + public async packMany( + reactions: MiNoteReaction[], + me?: { id: MiUser['id'] } | null | undefined, + options?: { + withNote: boolean; + }, + ): Promise<Packed<'NoteReaction'>[]> { + const opts = Object.assign({ + withNote: false, + }, options); + + return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts))); + } } diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index 704081ed0021076d02a63e8ced0794a020a752e7..18b9d148c4e7d582096e538fdadd583db180575a 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -14,14 +14,14 @@ import type { MiNote } from '@/models/Note.js'; import type { Packed } from '@/misc/json-schema.js'; import { bindThis } from '@/decorators.js'; import { isNotNull } from '@/misc/is-not-null.js'; -import { FilterUnionByProperty, notificationTypes } from '@/types.js'; +import { FilterUnionByProperty, groupedNotificationTypes } from '@/types.js'; +import { CacheService } from '@/core/CacheService.js'; import { RoleEntityService } from './RoleEntityService.js'; import type { OnModuleInit } from '@nestjs/common'; import type { UserEntityService } from './UserEntityService.js'; import type { NoteEntityService } from './NoteEntityService.js'; -const NOTE_REQUIRED_NOTIFICATION_TYPES = new Set(['note', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded'] as (typeof notificationTypes[number])[]); -const NOTE_REQUIRED_GROUPED_NOTIFICATION_TYPES = new Set(['note', 'mention', 'reply', 'renote', 'renote:grouped', 'quote', 'reaction', 'reaction:grouped', 'pollEnded']); +const NOTE_REQUIRED_NOTIFICATION_TYPES = new Set(['note', 'mention', 'reply', 'renote', 'renote:grouped', 'quote', 'reaction', 'reaction:grouped', 'pollEnded', 'edited'] as (typeof groupedNotificationTypes[number])[]); @Injectable() export class NotificationEntityService implements OnModuleInit { @@ -41,6 +41,8 @@ export class NotificationEntityService implements OnModuleInit { @Inject(DI.followRequestsRepository) private followRequestsRepository: FollowRequestsRepository, + private cacheService: CacheService, + //private userEntityService: UserEntityService, //private noteEntityService: NoteEntityService, ) { @@ -52,146 +54,61 @@ export class NotificationEntityService implements OnModuleInit { this.roleEntityService = this.moduleRef.get('RoleEntityService'); } - @bindThis - public async pack( - src: MiNotification, + /** + * 通知をパックã™ã‚‹å…±é€šå‡¦ç† + */ + async #packInternal <T extends MiNotification | MiGroupedNotification> ( + src: T, meId: MiUser['id'], // eslint-disable-next-line @typescript-eslint/ban-types options: { - + checkValidNotifier?: boolean; }, hint?: { packedNotes: Map<MiNote['id'], Packed<'Note'>>; - packedUsers: Map<MiUser['id'], Packed<'User'>>; + packedUsers: Map<MiUser['id'], Packed<'UserLite'>>; }, - ): Promise<Packed<'Notification'>> { + ): Promise<Packed<'Notification'> | null> { const notification = src; - const noteIfNeed = NOTE_REQUIRED_NOTIFICATION_TYPES.has(notification.type) && 'noteId' in notification ? ( - hint?.packedNotes != null - ? hint.packedNotes.get(notification.noteId) - : this.noteEntityService.pack(notification.noteId, { id: meId }, { - detail: true, - }) - ) : undefined; - const userIfNeed = 'notifierId' in notification ? ( - hint?.packedUsers != null - ? hint.packedUsers.get(notification.notifierId) - : this.userEntityService.pack(notification.notifierId, { id: meId }, { - detail: false, - }) - ) : undefined; - const role = notification.type === 'roleAssigned' ? await this.roleEntityService.pack(notification.roleId) : undefined; - - return await awaitAll({ - id: notification.id, - createdAt: new Date(notification.createdAt).toISOString(), - type: notification.type, - userId: 'notifierId' in notification ? notification.notifierId : undefined, - ...(userIfNeed != null ? { user: userIfNeed } : {}), - ...(noteIfNeed != null ? { note: noteIfNeed } : {}), - ...(notification.type === 'reaction' ? { - reaction: notification.reaction, - } : {}), - ...(notification.type === 'roleAssigned' ? { - role: role, - } : {}), - ...(notification.type === 'achievementEarned' ? { - achievement: notification.achievement, - } : {}), - ...(notification.type === 'app' ? { - body: notification.customBody, - header: notification.customHeader, - icon: notification.customIcon, - } : {}), - }); - } - - @bindThis - public async packMany( - notifications: MiNotification[], - meId: MiUser['id'], - ) { - if (notifications.length === 0) return []; - - let validNotifications = notifications; - - const noteIds = validNotifications.map(x => 'noteId' in x ? x.noteId : null).filter(isNotNull); - const notes = noteIds.length > 0 ? await this.notesRepository.find({ - where: { id: In(noteIds) }, - relations: ['user', 'reply', 'reply.user', 'renote', 'renote.user'], - }) : []; - const packedNotesArray = await this.noteEntityService.packMany(notes, { id: meId }, { - detail: true, - }); - const packedNotes = new Map(packedNotesArray.map(p => [p.id, p])); - validNotifications = validNotifications.filter(x => !('noteId' in x) || packedNotes.has(x.noteId)); + if (options.checkValidNotifier !== false && !(await this.#isValidNotifier(notification, meId))) return null; - const userIds = validNotifications.map(x => 'notifierId' in x ? x.notifierId : null).filter(isNotNull); - const users = userIds.length > 0 ? await this.usersRepository.find({ - where: { id: In(userIds) }, - }) : []; - const packedUsersArray = await this.userEntityService.packMany(users, { id: meId }, { - detail: false, - }); - const packedUsers = new Map(packedUsersArray.map(p => [p.id, p])); - - // æ—¢ã«è§£æ±ºã•ã‚ŒãŸãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã®é€šçŸ¥ã‚’除外 - const followRequestNotifications = validNotifications.filter((x): x is FilterUnionByProperty<MiGroupedNotification, 'type', 'receiveFollowRequest'> => x.type === 'receiveFollowRequest'); - if (followRequestNotifications.length > 0) { - const reqs = await this.followRequestsRepository.find({ - where: { followerId: In(followRequestNotifications.map(x => x.notifierId)) }, - }); - validNotifications = validNotifications.filter(x => (x.type !== 'receiveFollowRequest') || reqs.some(r => r.followerId === x.notifierId)); - } - - return await Promise.all(validNotifications.map(x => this.pack(x, meId, {}, { - packedNotes, - packedUsers, - }))); - } - - @bindThis - public async packGrouped( - src: MiGroupedNotification, - meId: MiUser['id'], - // eslint-disable-next-line @typescript-eslint/ban-types - options: { - - }, - hint?: { - packedNotes: Map<MiNote['id'], Packed<'Note'>>; - packedUsers: Map<MiUser['id'], Packed<'User'>>; - }, - ): Promise<Packed<'Notification'>> { - const notification = src; - const noteIfNeed = NOTE_REQUIRED_GROUPED_NOTIFICATION_TYPES.has(notification.type) && 'noteId' in notification ? ( + const needsNote = NOTE_REQUIRED_NOTIFICATION_TYPES.has(notification.type) && 'noteId' in notification; + const noteIfNeed = needsNote ? ( hint?.packedNotes != null ? hint.packedNotes.get(notification.noteId) : this.noteEntityService.pack(notification.noteId, { id: meId }, { detail: true, }) ) : undefined; - const userIfNeed = 'notifierId' in notification ? ( + // if the note has been deleted, don't show this notification + if (needsNote && !noteIfNeed) return null; + + const needsUser = 'notifierId' in notification; + const userIfNeed = needsUser ? ( hint?.packedUsers != null ? hint.packedUsers.get(notification.notifierId) - : this.userEntityService.pack(notification.notifierId, { id: meId }, { - detail: false, - }) + : this.userEntityService.pack(notification.notifierId, { id: meId }) ) : undefined; + // if the user has been deleted, don't show this notification + if (needsUser && !userIfNeed) return null; + // #region Grouped notifications if (notification.type === 'reaction:grouped') { - const reactions = await Promise.all(notification.reactions.map(async reaction => { + const reactions = (await Promise.all(notification.reactions.map(async reaction => { const user = hint?.packedUsers != null ? hint.packedUsers.get(reaction.userId)! - : await this.userEntityService.pack(reaction.userId, { id: meId }, { - detail: false, - }); + : await this.userEntityService.pack(reaction.userId, { id: meId }); return { user, reaction: reaction.reaction, }; - })); + }))).filter(r => isNotNull(r.user)); + // if all users have been deleted, don't show this notification + if (reactions.length === 0) { + return null; + } + return await awaitAll({ id: notification.id, createdAt: new Date(notification.createdAt).toISOString(), @@ -200,16 +117,19 @@ export class NotificationEntityService implements OnModuleInit { reactions, }); } else if (notification.type === 'renote:grouped') { - const users = await Promise.all(notification.userIds.map(userId => { + const users = (await Promise.all(notification.userIds.map(userId => { const packedUser = hint?.packedUsers != null ? hint.packedUsers.get(userId) : null; if (packedUser) { return packedUser; } - return this.userEntityService.pack(userId, { id: meId }, { - detail: false, - }); - })); + return this.userEntityService.pack(userId, { id: meId }); + }))).filter(isNotNull); + // if all users have been deleted, don't show this notification + if (users.length === 0) { + return null; + } + return await awaitAll({ id: notification.id, createdAt: new Date(notification.createdAt).toISOString(), @@ -218,8 +138,14 @@ export class NotificationEntityService implements OnModuleInit { users, }); } + // #endregion - const role = notification.type === 'roleAssigned' ? await this.roleEntityService.pack(notification.roleId) : undefined; + const needsRole = notification.type === 'roleAssigned'; + const role = needsRole ? await this.roleEntityService.pack(notification.roleId) : undefined; + // if the role has been deleted, don't show this notification + if (needsRole && !role) { + return null; + } return await awaitAll({ id: notification.id, @@ -245,15 +171,16 @@ export class NotificationEntityService implements OnModuleInit { }); } - @bindThis - public async packGroupedMany( - notifications: MiGroupedNotification[], + async #packManyInternal <T extends MiNotification | MiGroupedNotification> ( + notifications: T[], meId: MiUser['id'], - ) { + ): Promise<T[]> { if (notifications.length === 0) return []; let validNotifications = notifications; + validNotifications = await this.#filterValidNotifier(validNotifications, meId); + const noteIds = validNotifications.map(x => 'noteId' in x ? x.noteId : null).filter(isNotNull); const notes = noteIds.length > 0 ? await this.notesRepository.find({ where: { id: In(noteIds) }, @@ -275,13 +202,11 @@ export class NotificationEntityService implements OnModuleInit { const users = userIds.length > 0 ? await this.usersRepository.find({ where: { id: In(userIds) }, }) : []; - const packedUsersArray = await this.userEntityService.packMany(users, { id: meId }, { - detail: false, - }); + const packedUsersArray = await this.userEntityService.packMany(users, { id: meId }); const packedUsers = new Map(packedUsersArray.map(p => [p.id, p])); // æ—¢ã«è§£æ±ºã•ã‚ŒãŸãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã®é€šçŸ¥ã‚’除外 - const followRequestNotifications = validNotifications.filter((x): x is FilterUnionByProperty<MiGroupedNotification, 'type', 'receiveFollowRequest'> => x.type === 'receiveFollowRequest'); + const followRequestNotifications = validNotifications.filter((x): x is FilterUnionByProperty<T, 'type', 'receiveFollowRequest'> => x.type === 'receiveFollowRequest'); if (followRequestNotifications.length > 0) { const reqs = await this.followRequestsRepository.find({ where: { followerId: In(followRequestNotifications.map(x => x.notifierId)) }, @@ -289,9 +214,107 @@ export class NotificationEntityService implements OnModuleInit { validNotifications = validNotifications.filter(x => (x.type !== 'receiveFollowRequest') || reqs.some(r => r.followerId === x.notifierId)); } - return await Promise.all(validNotifications.map(x => this.packGrouped(x, meId, {}, { - packedNotes, - packedUsers, - }))); + const packPromises = validNotifications.map(x => { + return this.pack( + x, + meId, + { checkValidNotifier: false }, + { packedNotes, packedUsers }, + ); + }); + + return (await Promise.all(packPromises)).filter(isNotNull); + } + + @bindThis + public async pack( + src: MiNotification | MiGroupedNotification, + meId: MiUser['id'], + // eslint-disable-next-line @typescript-eslint/ban-types + options: { + checkValidNotifier?: boolean; + }, + hint?: { + packedNotes: Map<MiNote['id'], Packed<'Note'>>; + packedUsers: Map<MiUser['id'], Packed<'UserLite'>>; + }, + ): Promise<Packed<'Notification'> | null> { + return await this.#packInternal(src, meId, options, hint); + } + + @bindThis + public async packMany( + notifications: MiNotification[], + meId: MiUser['id'], + ): Promise<MiNotification[]> { + return await this.#packManyInternal(notifications, meId); + } + + @bindThis + public async packGroupedMany( + notifications: MiGroupedNotification[], + meId: MiUser['id'], + ): Promise<MiGroupedNotification[]> { + return await this.#packManyInternal(notifications, meId); + } + + /** + * notifierãŒå˜åœ¨ã™ã‚‹ã‹ã€ãƒŸãƒ¥ãƒ¼ãƒˆã•ã‚Œã¦ã„ãªã„ã‹ã€ã‚µã‚¹ãƒšãƒ³ãƒ‰ã•ã‚Œã¦ã„ãªã„ã‹ã‚’確èªã™ã‚‹validator + */ + #validateNotifier <T extends MiNotification | MiGroupedNotification> ( + notification: T, + userIdsWhoMeMuting: Set<MiUser['id']>, + userMutedInstances: Set<string>, + notifiers: MiUser[], + ): boolean { + if (!('notifierId' in notification)) return true; + if (userIdsWhoMeMuting.has(notification.notifierId)) return false; + + const notifier = notifiers.find(x => x.id === notification.notifierId) ?? null; + + if (notifier == null) return false; + if (notifier.host && userMutedInstances.has(notifier.host)) return false; + + if (notifier.isSuspended) return false; + + return true; + } + + /** + * notifierãŒå˜åœ¨ã™ã‚‹ã‹ã€ãƒŸãƒ¥ãƒ¼ãƒˆã•ã‚Œã¦ã„ãªã„ã‹ã€ã‚µã‚¹ãƒšãƒ³ãƒ‰ã•ã‚Œã¦ã„ãªã„ã‹ã‚’実際ã«ç¢ºèªã™ã‚‹ + */ + async #isValidNotifier( + notification: MiNotification | MiGroupedNotification, + meId: MiUser['id'], + ): Promise<boolean> { + return (await this.#filterValidNotifier([notification], meId)).length === 1; + } + + /** + * notifierãŒå˜åœ¨ã™ã‚‹ã‹ã€ãƒŸãƒ¥ãƒ¼ãƒˆã•ã‚Œã¦ã„ãªã„ã‹ã€ã‚µã‚¹ãƒšãƒ³ãƒ‰ã•ã‚Œã¦ã„ãªã„ã‹ã‚’実際ã«è¤‡æ•°ç¢ºèªã™ã‚‹ + */ + async #filterValidNotifier <T extends MiNotification | MiGroupedNotification> ( + notifications: T[], + meId: MiUser['id'], + ): Promise<T[]> { + const [ + userIdsWhoMeMuting, + userMutedInstances, + ] = await Promise.all([ + this.cacheService.userMutingsCache.fetch(meId), + this.cacheService.userProfileCache.fetch(meId).then(p => new Set(p.mutedInstances)), + ]); + + const notifierIds = notifications.map(notification => 'notifierId' in notification ? notification.notifierId : null).filter(isNotNull); + const notifiers = notifierIds.length > 0 ? await this.usersRepository.find({ + where: { id: In(notifierIds) }, + }) : []; + + const filteredNotifications = ((await Promise.all(notifications.map(async (notification) => { + const isValid = this.#validateNotifier(notification, userIdsWhoMeMuting, userMutedInstances, notifiers); + return isValid ? notification : null; + }))) as [T | null] ).filter(isNotNull); + + return filteredNotifications; } } diff --git a/packages/backend/src/core/entities/PageEntityService.ts b/packages/backend/src/core/entities/PageEntityService.ts index f39ef949db81907ce1f1190a994b700905647db5..65c69a49a7ec0f163a478b791ba0803e737054c6 100644 --- a/packages/backend/src/core/entities/PageEntityService.ts +++ b/packages/backend/src/core/entities/PageEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -14,6 +14,7 @@ import type { MiPage } from '@/models/Page.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; @@ -90,7 +91,7 @@ export class PageEntityService { createdAt: this.idService.parse(page.id).date.toISOString(), updatedAt: page.updatedAt.toISOString(), userId: page.userId, - user: this.userEntityService.pack(page.user ?? page.userId, me), // { detail: true } ã™ã‚‹ã¨ç„¡é™ãƒ«ãƒ¼ãƒ—ã™ã‚‹ã®ã§æ³¨æ„ + user: this.userEntityService.pack(page.user ?? page.userId, me), // { schema: 'UserDetailed' } ã™ã‚‹ã¨ç„¡é™ãƒ«ãƒ¼ãƒ—ã™ã‚‹ã®ã§æ³¨æ„ content: page.content, variables: page.variables, title: page.title, @@ -102,9 +103,9 @@ export class PageEntityService { script: page.script, eyeCatchingImageId: page.eyeCatchingImageId, eyeCatchingImage: page.eyeCatchingImageId ? await this.driveFileEntityService.pack(page.eyeCatchingImageId) : null, - attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter((x): x is MiDriveFile => x != null)), + attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter(isNotNull)), likedCount: page.likedCount, - isLiked: meId ? await this.pageLikesRepository.exist({ where: { pageId: page.id, userId: meId } }) : undefined, + isLiked: meId ? await this.pageLikesRepository.exists({ where: { pageId: page.id, userId: meId } }) : undefined, }); } diff --git a/packages/backend/src/core/entities/PageLikeEntityService.ts b/packages/backend/src/core/entities/PageLikeEntityService.ts index 4dc691ab931305cad606af7fd4b3a48d31a24e03..cfccbcb660324221e249460672343d7ca133ba1d 100644 --- a/packages/backend/src/core/entities/PageLikeEntityService.ts +++ b/packages/backend/src/core/entities/PageLikeEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/entities/RenoteMutingEntityService.ts b/packages/backend/src/core/entities/RenoteMutingEntityService.ts index 3f9dc9180afc4817fa6bcbeccaa7b372fc2c923b..0b05a5db809083fe518257fad277d747e0d25d53 100644 --- a/packages/backend/src/core/entities/RenoteMutingEntityService.ts +++ b/packages/backend/src/core/entities/RenoteMutingEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -38,7 +38,7 @@ export class RenoteMutingEntityService { createdAt: this.idService.parse(muting.id).date.toISOString(), muteeId: muting.muteeId, mutee: this.userEntityService.pack(muting.muteeId, me, { - detail: true, + schema: 'UserDetailedNotMe', }), }); } diff --git a/packages/backend/src/core/entities/ReversiGameEntityService.ts b/packages/backend/src/core/entities/ReversiGameEntityService.ts new file mode 100644 index 0000000000000000000000000000000000000000..32cbe631e4e2a436131569b4e97bba79e2fd7e03 --- /dev/null +++ b/packages/backend/src/core/entities/ReversiGameEntityService.ts @@ -0,0 +1,120 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import type { ReversiGamesRepository } from '@/models/_.js'; +import { awaitAll } from '@/misc/prelude/await-all.js'; +import type { Packed } from '@/misc/json-schema.js'; +import type { } from '@/models/Blocking.js'; +import type { MiReversiGame } from '@/models/ReversiGame.js'; +import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class ReversiGameEntityService { + constructor( + @Inject(DI.reversiGamesRepository) + private reversiGamesRepository: ReversiGamesRepository, + + private userEntityService: UserEntityService, + private idService: IdService, + ) { + } + + @bindThis + public async packDetail( + src: MiReversiGame['id'] | MiReversiGame, + ): Promise<Packed<'ReversiGameDetailed'>> { + const game = typeof src === 'object' ? src : await this.reversiGamesRepository.findOneByOrFail({ id: src }); + + const users = await Promise.all([ + this.userEntityService.pack(game.user1 ?? game.user1Id), + this.userEntityService.pack(game.user2 ?? game.user2Id), + ]); + + return await awaitAll({ + id: game.id, + createdAt: this.idService.parse(game.id).date.toISOString(), + startedAt: game.startedAt && game.startedAt.toISOString(), + endedAt: game.endedAt && game.endedAt.toISOString(), + isStarted: game.isStarted, + isEnded: game.isEnded, + form1: game.form1, + form2: game.form2, + user1Ready: game.user1Ready, + user2Ready: game.user2Ready, + user1Id: game.user1Id, + user2Id: game.user2Id, + user1: users[0], + user2: users[1], + winnerId: game.winnerId, + winner: game.winnerId ? users.find(u => u.id === game.winnerId)! : null, + surrenderedUserId: game.surrenderedUserId, + timeoutUserId: game.timeoutUserId, + black: game.black, + bw: game.bw, + isLlotheo: game.isLlotheo, + canPutEverywhere: game.canPutEverywhere, + loopedBoard: game.loopedBoard, + timeLimitForEachTurn: game.timeLimitForEachTurn, + noIrregularRules: game.noIrregularRules, + logs: game.logs, + map: game.map, + }); + } + + @bindThis + public packDetailMany( + xs: MiReversiGame[], + ) { + return Promise.all(xs.map(x => this.packDetail(x))); + } + + @bindThis + public async packLite( + src: MiReversiGame['id'] | MiReversiGame, + ): Promise<Packed<'ReversiGameLite'>> { + const game = typeof src === 'object' ? src : await this.reversiGamesRepository.findOneByOrFail({ id: src }); + + const users = await Promise.all([ + this.userEntityService.pack(game.user1 ?? game.user1Id), + this.userEntityService.pack(game.user2 ?? game.user2Id), + ]); + + return await awaitAll({ + id: game.id, + createdAt: this.idService.parse(game.id).date.toISOString(), + startedAt: game.startedAt && game.startedAt.toISOString(), + endedAt: game.endedAt && game.endedAt.toISOString(), + isStarted: game.isStarted, + isEnded: game.isEnded, + user1Id: game.user1Id, + user2Id: game.user2Id, + user1: users[0], + user2: users[1], + winnerId: game.winnerId, + winner: game.winnerId ? users.find(u => u.id === game.winnerId)! : null, + surrenderedUserId: game.surrenderedUserId, + timeoutUserId: game.timeoutUserId, + black: game.black, + bw: game.bw, + isLlotheo: game.isLlotheo, + canPutEverywhere: game.canPutEverywhere, + loopedBoard: game.loopedBoard, + timeLimitForEachTurn: game.timeLimitForEachTurn, + noIrregularRules: game.noIrregularRules, + }); + } + + @bindThis + public packLiteMany( + xs: MiReversiGame[], + ) { + return Promise.all(xs.map(x => this.packLite(x))); + } +} + diff --git a/packages/backend/src/core/entities/RoleEntityService.ts b/packages/backend/src/core/entities/RoleEntityService.ts index 5563f9a1ac4122909ae9b1da62d01af8a7081943..2a7dc37bce859b98e8083ae82f365086c5bce0dd 100644 --- a/packages/backend/src/core/entities/RoleEntityService.ts +++ b/packages/backend/src/core/entities/RoleEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/entities/SigninEntityService.ts b/packages/backend/src/core/entities/SigninEntityService.ts index 6bde3e589ad2e5a8a8377ca7363c5d57f261302c..00b124d594320d9e1432b066dea7f8f6e385ca19 100644 --- a/packages/backend/src/core/entities/SigninEntityService.ts +++ b/packages/backend/src/core/entities/SigninEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index adb7dfbf86113f738ab20220ffebdf7237dec3d0..8f5d986facaba73ca45dbcb0c6df0a04b6cb0c98 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -25,19 +25,12 @@ import { IdService } from '@/core/IdService.js'; import type { AnnouncementService } from '@/core/AnnouncementService.js'; import type { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { AvatarDecorationService } from '@/core/AvatarDecorationService.js'; +import { isNotNull } from '@/misc/is-not-null.js'; import type { OnModuleInit } from '@nestjs/common'; import type { NoteEntityService } from './NoteEntityService.js'; import type { DriveFileEntityService } from './DriveFileEntityService.js'; import type { PageEntityService } from './PageEntityService.js'; -type IsUserDetailed<Detailed extends boolean> = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>; -type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends boolean> = - Detailed extends true ? - ExpectsMe extends true ? Packed<'MeDetailed'> : - ExpectsMe extends false ? Packed<'UserDetailedNotMe'> : - Packed<'UserDetailed'> : - Packed<'UserLite'>; - const Ajv = _Ajv.default; const ajv = new Ajv(); @@ -161,43 +154,43 @@ export class UserEntityService implements OnModuleInit { followerId: me, followeeId: target, }), - this.followingsRepository.exist({ + this.followingsRepository.exists({ where: { followerId: target, followeeId: me, }, }), - this.followRequestsRepository.exist({ + this.followRequestsRepository.exists({ where: { followerId: me, followeeId: target, }, }), - this.followRequestsRepository.exist({ + this.followRequestsRepository.exists({ where: { followerId: target, followeeId: me, }, }), - this.blockingsRepository.exist({ + this.blockingsRepository.exists({ where: { blockerId: me, blockeeId: target, }, }), - this.blockingsRepository.exist({ + this.blockingsRepository.exists({ where: { blockerId: target, blockeeId: me, }, }), - this.mutingsRepository.exist({ + this.mutingsRepository.exists({ where: { muterId: me, muteeId: target, }, }), - this.renoteMutingsRepository.exist({ + this.renoteMutingsRepository.exists({ where: { muterId: me, muteeId: target, @@ -224,7 +217,7 @@ export class UserEntityService implements OnModuleInit { /* const myAntennas = (await this.antennaService.getAntennas()).filter(a => a.userId === userId); - const isUnread = (myAntennas.length > 0 ? await this.antennaNotesRepository.exist({ + const isUnread = (myAntennas.length > 0 ? await this.antennaNotesRepository.exists({ where: { antennaId: In(myAntennas.map(x => x.id)), read: false, @@ -304,17 +297,17 @@ export class UserEntityService implements OnModuleInit { return `${this.config.url}/users/${userId}`; } - public async pack<ExpectsMe extends boolean | null = null, D extends boolean = false>( + public async pack<S extends 'MeDetailed' | 'UserDetailedNotMe' | 'UserDetailed' | 'UserLite' = 'UserLite'>( src: MiUser['id'] | MiUser, me?: { id: MiUser['id']; } | null | undefined, options?: { - detail?: D, + schema?: S, includeSecrets?: boolean, userProfile?: MiUserProfile, }, - ): Promise<IsMeAndIsUserDetailed<ExpectsMe, D>> { + ): Promise<Packed<S>> { const opts = Object.assign({ - detail: false, + schema: 'UserLite', includeSecrets: false, }, options); @@ -346,19 +339,20 @@ export class UserEntityService implements OnModuleInit { }); } + const isDetailed = opts.schema !== 'UserLite'; const meId = me ? me.id : null; const isMe = meId === user.id; const iAmModerator = me ? await this.roleService.isModerator(me as MiUser) : false; - const relation = meId && !isMe && opts.detail ? await this.getRelation(meId, user.id) : null; - const pins = opts.detail ? await this.userNotePiningsRepository.createQueryBuilder('pin') + const relation = meId && !isMe && isDetailed ? await this.getRelation(meId, user.id) : null; + const pins = isDetailed ? await this.userNotePiningsRepository.createQueryBuilder('pin') .where('pin.userId = :userId', { userId: user.id }) .innerJoinAndSelect('pin.note', 'note') .orderBy('pin.id', 'DESC') .getMany() : []; - const profile = opts.detail ? (opts.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id })) : null; + const profile = isDetailed ? (opts.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id })) : null; - const mastoapi = !opts.detail ? opts.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id }) : null; + const mastoapi = !isDetailed ? opts.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id }) : null; const followingCount = profile == null ? null : (profile.followingVisibility === 'public') || isMe ? user.followingCount : @@ -370,17 +364,16 @@ export class UserEntityService implements OnModuleInit { (profile.followersVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount : null; - const isModerator = isMe && opts.detail ? this.roleService.isModerator(user) : null; - const isAdmin = isMe && opts.detail ? this.roleService.isAdministrator(user) : null; - const unreadAnnouncements = isMe && opts.detail ? + const isModerator = isMe && isDetailed ? this.roleService.isModerator(user) : null; + const isAdmin = isMe && isDetailed ? this.roleService.isAdministrator(user) : null; + const unreadAnnouncements = isMe && isDetailed ? (await this.announcementService.getUnreadAnnouncements(user)).map((announcement) => ({ createdAt: this.idService.parse(announcement.id).date.toISOString(), ...announcement, })) : null; const checkHost = user.host == null ? this.config.host : user.host; - - const notificationsInfo = isMe && opts.detail ? await this.getNotificationsInfo(user.id) : null; + const notificationsInfo = isMe && isDetailed ? await this.getNotificationsInfo(user.id) : null; const packed = { id: user.id, @@ -425,13 +418,13 @@ export class UserEntityService implements OnModuleInit { displayOrder: r.displayOrder, }))) : undefined, - ...(opts.detail ? { + ...(isDetailed ? { url: profile!.url, uri: user.uri, movedTo: user.movedToUri ? this.apPersonService.resolvePerson(user.movedToUri).then(user => user.id).catch(() => null) : null, alsoKnownAs: user.alsoKnownAs ? Promise.all(user.alsoKnownAs.map(uri => this.apPersonService.fetchPerson(uri).then(user => user?.id).catch(() => null))) - .then(xs => xs.length === 0 ? null : xs.filter(x => x != null) as string[]) + .then(xs => xs.length === 0 ? null : xs.filter(isNotNull)) : null, updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null, lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null, @@ -453,7 +446,7 @@ export class UserEntityService implements OnModuleInit { }), pinnedPageId: profile!.pinnedPageId, pinnedPage: profile!.pinnedPageId ? this.pageEntityService.pack(profile!.pinnedPageId, me) : null, - publicReactions: profile!.publicReactions, + publicReactions: this.isLocalUser(user) ? profile!.publicReactions : false, // https://github.com/misskey-dev/misskey/issues/12964 followersVisibility: profile!.followersVisibility, followingVisibility: profile!.followingVisibility, twoFactorEnabled: profile!.twoFactorEnabled, @@ -480,7 +473,7 @@ export class UserEntityService implements OnModuleInit { moderationNote: iAmModerator ? (profile!.moderationNote ?? '') : undefined, } : {}), - ...(opts.detail && isMe ? { + ...(isDetailed && isMe ? { avatarId: user.avatarId, bannerId: user.bannerId, backgroundId: user.backgroundId, @@ -554,19 +547,19 @@ export class UserEntityService implements OnModuleInit { notify: relation.following?.notify ?? 'none', withReplies: relation.following?.withReplies ?? false, } : {}), - } as Promiseable<Packed<'User'>> as Promiseable<IsMeAndIsUserDetailed<ExpectsMe, D>>; + } as Promiseable<Packed<S>>; return await awaitAll(packed); } - public packMany<D extends boolean = false>( + public packMany<S extends 'MeDetailed' | 'UserDetailedNotMe' | 'UserDetailed' | 'UserLite' = 'UserLite'>( users: (MiUser['id'] | MiUser)[], me?: { id: MiUser['id'] } | null | undefined, options?: { - detail?: D, + schema?: S, includeSecrets?: boolean, }, - ): Promise<IsUserDetailed<D>[]> { + ): Promise<Packed<S>[]> { return Promise.all(users.map(u => this.pack(u, me, options))); } } diff --git a/packages/backend/src/core/entities/UserListEntityService.ts b/packages/backend/src/core/entities/UserListEntityService.ts index 31ab7293da9fec7367d1ffd2a4a73e3ed5f41b1e..09cab245212e14c832d3546408edd00537e73ccc 100644 --- a/packages/backend/src/core/entities/UserListEntityService.ts +++ b/packages/backend/src/core/entities/UserListEntityService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/daemons/DaemonModule.ts b/packages/backend/src/daemons/DaemonModule.ts index 236985076cb3a13c2f9284baf45c5cf8f44fe5b4..a67907e6dde2e3a9ecd33b5b62b63d913dfe9607 100644 --- a/packages/backend/src/daemons/DaemonModule.ts +++ b/packages/backend/src/daemons/DaemonModule.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/daemons/QueueStatsService.ts b/packages/backend/src/daemons/QueueStatsService.ts index 5edc0f45ab61946dfc560826ef95876c7eeaddb0..ede104b9fe5443fabd4db39fa035962e1a69119b 100644 --- a/packages/backend/src/daemons/QueueStatsService.ts +++ b/packages/backend/src/daemons/QueueStatsService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/daemons/ServerStatsService.ts b/packages/backend/src/daemons/ServerStatsService.ts index c5ef9b2fa30f04c2eee57a1ce4f0c1a31ec2811e..2c70344c941ab62094865d630200c13618461b42 100644 --- a/packages/backend/src/daemons/ServerStatsService.ts +++ b/packages/backend/src/daemons/ServerStatsService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -37,7 +37,7 @@ export class ServerStatsService implements OnApplicationShutdown { const log = [] as any[]; ev.on('requestServerStatsLog', x => { - ev.emit(`serverStatsLog:${x.id}`, log.slice(0, x.length ?? 50)); + ev.emit(`serverStatsLog:${x.id}`, log.slice(0, x.length)); }); const tick = async () => { diff --git a/packages/backend/src/decorators.ts b/packages/backend/src/decorators.ts index 6b439978db376addae77f7697f7c487957c67b08..21777657d185ad450048f654b5df5d11459814b0 100644 --- a/packages/backend/src/decorators.ts +++ b/packages/backend/src/decorators.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index 0c5ac8f2d3db8a9f5a2ef4e659bd798783e636ab..564a8db70a813d88f29d756c2dfd47ba358ffe8e 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -79,5 +79,7 @@ export const DI = { flashLikesRepository: Symbol('flashLikesRepository'), userMemosRepository: Symbol('userMemosRepository'), noteEditRepository: Symbol('noteEditRepository'), + bubbleGameRecordsRepository: Symbol('bubbleGameRecordsRepository'), + reversiGamesRepository: Symbol('reversiGamesRepository'), //#endregion }; diff --git a/packages/backend/src/env.ts b/packages/backend/src/env.ts index af1c3bdd3c552c1be9b05f7cac145bff8be4a0e2..ba44cfa2e687166b5a24cdb7b8a86937e59e3073 100644 --- a/packages/backend/src/env.ts +++ b/packages/backend/src/env.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/global.d.ts b/packages/backend/src/global.d.ts index a9e6243cc482a37cf21d53821e84ff86f735472f..2f19e85525a0864db8be57142e9b7046274363cd 100644 --- a/packages/backend/src/global.d.ts +++ b/packages/backend/src/global.d.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/logger.ts b/packages/backend/src/logger.ts index 5c10559ec6f9f7ca414c9012935bf1ceef89e36f..d4705af60114759ded008a6c221ad84c6d78618c 100644 --- a/packages/backend/src/logger.ts +++ b/packages/backend/src/logger.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -71,8 +71,11 @@ export default class Logger { let log = `${l} ${worker}\t[${contexts.join(' ')}]\t${m}`; if (envOption.withLogTime) log = chalk.gray(time) + ' ' + log; - console.log(important ? chalk.bold(log) : log); - if (level === 'error' && data) console.log(data); + const args: unknown[] = [important ? chalk.bold(log) : log]; + if (data != null) { + args.push(data); + } + console.log(...args); } @bindThis diff --git a/packages/backend/src/misc/FileWriterStream.ts b/packages/backend/src/misc/FileWriterStream.ts new file mode 100644 index 0000000000000000000000000000000000000000..367a8eb56054408a194ce35ec843fdfb33f5796a --- /dev/null +++ b/packages/backend/src/misc/FileWriterStream.ts @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as fs from 'node:fs/promises'; +import type { PathLike } from 'node:fs'; + +/** + * `fs.createWriteStream()`相当ã®ã“ã¨ã‚’è¡Œã†`WritableStream` (Web標準) + */ +export class FileWriterStream extends WritableStream<Uint8Array> { + constructor(path: PathLike) { + let file: fs.FileHandle | null = null; + + super({ + start: async () => { + file = await fs.open(path, 'a'); + }, + write: async (chunk, controller) => { + if (file === null) { + controller.error(); + throw new Error(); + } + + await file.write(chunk); + }, + close: async () => { + await file?.close(); + }, + abort: async () => { + await file?.close(); + }, + }); + } +} diff --git a/packages/backend/src/misc/JsonArrayStream.ts b/packages/backend/src/misc/JsonArrayStream.ts new file mode 100644 index 0000000000000000000000000000000000000000..754938989d8be73bd61a8e1351a6b4c06b074eee --- /dev/null +++ b/packages/backend/src/misc/JsonArrayStream.ts @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { TransformStream } from 'node:stream/web'; + +/** + * ストリームã«æµã‚Œã¦ããŸå„データã«ã¤ã„ã¦`JSON.stringify()`ã—ãŸä¸Šã§ã€ãれらを一ã¤ã®é…列ã«ã¾ã¨ã‚ã‚‹ + */ +export class JsonArrayStream extends TransformStream<unknown, string> { + constructor() { + /** 最åˆã®è¦ç´ ã‹ã©ã†ã‹ã‚’変数ã«è¨˜éŒ² */ + let isFirst = true; + + super({ + start(controller) { + controller.enqueue('['); + }, + flush(controller) { + controller.enqueue(']'); + }, + transform(chunk, controller) { + if (isFirst) { + isFirst = false; + } else { + // 妥当ãªJSONé…列ã«ã™ã‚‹ãŸã‚ã«ã¯æœ€åˆä»¥å¤–ã®è¦ç´ ã®å‰ã«`,`を挿入ã—ãªã‘ã‚Œã°ãªã‚‰ãªã„ + controller.enqueue(',\n'); + } + + controller.enqueue(JSON.stringify(chunk)); + }, + }); + } +} diff --git a/packages/backend/src/misc/acct.ts b/packages/backend/src/misc/acct.ts index 5db72746c07e91e4e211ace2d502198bca784297..3d729b115109c0e0cb00e214411a5cc5e4f5d4dc 100644 --- a/packages/backend/src/misc/acct.ts +++ b/packages/backend/src/misc/acct.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index c235871931f92528e0377da6c50eabb2058f3567..bba64a06eff2dbb2dd28faa5bad1bb7041e08022 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -186,28 +186,18 @@ export class RedisSingleCache<T> { // TODO: メモリ節約ã®ãŸã‚ã‚ã¾ã‚Šå‚ç…§ã•ã‚Œãªã„ã‚ーを定期的ã«å‰Šé™¤ã§ãるよã†ã«ã™ã‚‹ï¼Ÿ -function nothingToDo<T, V = T>(value: T): V { - return value as unknown as V; -} - -export class MemoryKVCache<T, V = T> { - public cache: Map<string, { date: number; value: V; }>; +export class MemoryKVCache<T> { + /** + * データをæŒã¤ãƒžãƒƒãƒ— + * @deprecated ã“れを直接æ“作ã™ã‚‹ã¹ãã§ã¯ãªã„ + */ + public cache: Map<string, { date: number; value: T; }>; private lifetime: number; private gcIntervalHandle: NodeJS.Timeout; - private toMapConverter: (value: T) => V; - private fromMapConverter: (cached: V) => T | undefined; - - constructor(lifetime: MemoryKVCache<never>['lifetime'], options: { - toMapConverter: (value: T) => V; - fromMapConverter: (cached: V) => T | undefined; - } = { - toMapConverter: nothingToDo, - fromMapConverter: nothingToDo, - }) { + + constructor(lifetime: MemoryKVCache<never>['lifetime']) { this.cache = new Map(); this.lifetime = lifetime; - this.toMapConverter = options.toMapConverter; - this.fromMapConverter = options.fromMapConverter; this.gcIntervalHandle = setInterval(() => { this.gc(); @@ -215,10 +205,14 @@ export class MemoryKVCache<T, V = T> { } @bindThis + /** + * Mapã«ã‚ャッシュをセットã—ã¾ã™ + * @deprecated ã“れを直接呼ã³å‡ºã™ã¹ãã§ã¯ãªã„。InternalEventãªã©ã§å¤‰æ›´ã‚’å…¨ã¦ã®ãƒ—ãƒã‚»ã‚¹/マシンã«é€šçŸ¥ã™ã‚‹ã¹ã + */ public set(key: string, value: T): void { this.cache.set(key, { date: Date.now(), - value: this.toMapConverter(value), + value, }); } @@ -230,7 +224,7 @@ export class MemoryKVCache<T, V = T> { this.cache.delete(key); return undefined; } - return this.fromMapConverter(cached.value); + return cached.value; } @bindThis @@ -241,10 +235,9 @@ export class MemoryKVCache<T, V = T> { /** * ã‚ャッシュãŒã‚ã‚Œã°ãれを返ã—ã€ç„¡ã‘ã‚Œã°fetcherを呼ã³å‡ºã—ã¦çµæžœã‚’ã‚ャッシュ&è¿”ã—ã¾ã™ * optional: ã‚ャッシュãŒå˜åœ¨ã—ã¦ã‚‚validatorã§falseã‚’è¿”ã™ã¨ã‚ャッシュ無効扱ã„ã«ã—ã¾ã™ - * fetcherã®å¼•æ•°ã¯cacheã«ä¿å˜ã•ã‚Œã¦ã„る値ãŒã‚ã‚Œã°æ¸¡ã•ã‚Œã¾ã™ */ @bindThis - public async fetch(key: string, fetcher: (value: V | undefined) => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> { + public async fetch(key: string, fetcher: () => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> { const cachedValue = this.get(key); if (cachedValue !== undefined) { if (validator) { @@ -259,7 +252,7 @@ export class MemoryKVCache<T, V = T> { } // Cache MISS - const value = await fetcher(this.cache.get(key)?.value); + const value = await fetcher(); this.set(key, value); return value; } @@ -267,10 +260,9 @@ export class MemoryKVCache<T, V = T> { /** * ã‚ャッシュãŒã‚ã‚Œã°ãれを返ã—ã€ç„¡ã‘ã‚Œã°fetcherを呼ã³å‡ºã—ã¦çµæžœã‚’ã‚ャッシュ&è¿”ã—ã¾ã™ * optional: ã‚ャッシュãŒå˜åœ¨ã—ã¦ã‚‚validatorã§falseã‚’è¿”ã™ã¨ã‚ャッシュ無効扱ã„ã«ã—ã¾ã™ - * fetcherã®å¼•æ•°ã¯cacheã«ä¿å˜ã•ã‚Œã¦ã„る値ãŒã‚ã‚Œã°æ¸¡ã•ã‚Œã¾ã™ */ @bindThis - public async fetchMaybe(key: string, fetcher: (value: V | undefined) => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> { + public async fetchMaybe(key: string, fetcher: () => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> { const cachedValue = this.get(key); if (cachedValue !== undefined) { if (validator) { @@ -285,7 +277,7 @@ export class MemoryKVCache<T, V = T> { } // Cache MISS - const value = await fetcher(this.cache.get(key)?.value); + const value = await fetcher(); if (value !== undefined) { this.set(key, value); } diff --git a/packages/backend/src/misc/check-https.ts b/packages/backend/src/misc/check-https.ts index 0b13ccabddce9a0968ea0f5c91339c1ecefc7255..15a54f6ce7e2f6ce7809e4d12a4294f005a93166 100644 --- a/packages/backend/src/misc/check-https.ts +++ b/packages/backend/src/misc/check-https.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/check-word-mute.ts b/packages/backend/src/misc/check-word-mute.ts index cef5595451003ef9dc25ca8f359540de60654da4..c50f2b723c058a730f7a73f630e63562e4b38e78 100644 --- a/packages/backend/src/misc/check-word-mute.ts +++ b/packages/backend/src/misc/check-word-mute.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/clone.ts b/packages/backend/src/misc/clone.ts index 9d20deac3b0ced80814c2435154a98cd13fef9bd..ed0548564922658d31c7f6f9f92f8b12558e5796 100644 --- a/packages/backend/src/misc/clone.ts +++ b/packages/backend/src/misc/clone.ts @@ -1,12 +1,12 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ // structredCloneãŒé…ã„ãŸã‚ // SEE: http://var.blog.jp/archives/86038606.html -type Cloneable = string | number | boolean | null | { [key: string]: Cloneable } | Cloneable[]; +type Cloneable = string | number | boolean | null | undefined | { [key: string]: Cloneable } | Cloneable[]; export function deepClone<T extends Cloneable>(x: T): T { if (typeof x === 'object') { @@ -14,7 +14,7 @@ export function deepClone<T extends Cloneable>(x: T): T { if (Array.isArray(x)) return x.map(deepClone) as T; const obj = {} as Record<string, Cloneable>; for (const [k, v] of Object.entries(x)) { - obj[k] = deepClone(v); + obj[k] = v === undefined ? undefined : deepClone(v); } return obj as T; } else { diff --git a/packages/backend/src/misc/content-disposition.ts b/packages/backend/src/misc/content-disposition.ts index 1ac8c88d21439eb2968fcd23086cd38de9480b15..467b5057d6006041e964f604b9ec8ff93109d6cb 100644 --- a/packages/backend/src/misc/content-disposition.ts +++ b/packages/backend/src/misc/content-disposition.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/correct-filename.ts b/packages/backend/src/misc/correct-filename.ts index 9130af44c325b6c9d0e5103dbf1597b6076ba7a4..f7ee02781d92741a90c654b86d95b31d632301b1 100644 --- a/packages/backend/src/misc/correct-filename.ts +++ b/packages/backend/src/misc/correct-filename.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -21,7 +21,7 @@ const extRegExp = /\.[0-9a-zA-Z]+$/i; /** * 与ãˆã‚‰ã‚ŒãŸæ‹¡å¼µåã¨ãƒ•ã‚¡ã‚¤ãƒ«åãŒä¸€è‡´ã—ã¦ã„ã‚‹ã‹ã©ã†ã‹ã‚’確èªã—〠* 一致ã—ã¦ã„ãªã„å ´åˆã¯æ‹¡å¼µåを付与ã—ã¦è¿”ã™ - * + * * extã¯file-typeã®extを想定 */ export function correctFilename(filename: string, ext: string | null) { diff --git a/packages/backend/src/misc/create-temp.ts b/packages/backend/src/misc/create-temp.ts index 5b4943b7a252456d485b865477edd78119f9df4b..6cc896046fb78c2dae2232b41fddf15e259d1d54 100644 --- a/packages/backend/src/misc/create-temp.ts +++ b/packages/backend/src/misc/create-temp.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/dev-null.ts b/packages/backend/src/misc/dev-null.ts index f510177c0b425ea625795c1ba5a65dcbe0bcf2c1..4d9806fbe8e1142e5b4458f3102c64bdc0e90b76 100644 --- a/packages/backend/src/misc/dev-null.ts +++ b/packages/backend/src/misc/dev-null.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/emoji-regex.ts b/packages/backend/src/misc/emoji-regex.ts index 04c2f2e91349ffd9fe85727a758712847d0889d8..53e66298a67c102c342efc5e7cf33e110ea862c1 100644 --- a/packages/backend/src/misc/emoji-regex.ts +++ b/packages/backend/src/misc/emoji-regex.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts b/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts index 0e8dfd21f8ae0d48de7f81c02948c390149ff0c8..36a9b8e1f4165d33ec36af839e13179b470e00d5 100644 --- a/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts +++ b/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts @@ -1,9 +1,9 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; import { unique } from '@/misc/prelude/array.js'; export function extractCustomEmojisFromMfm(nodes: mfm.MfmNode[]): string[] { diff --git a/packages/backend/src/misc/extract-hashtags.ts b/packages/backend/src/misc/extract-hashtags.ts index 3598d90093162788eeaa1a52fc3328ce218a6195..ed7606d995f6e4eec9da0597d478773f807c01a7 100644 --- a/packages/backend/src/misc/extract-hashtags.ts +++ b/packages/backend/src/misc/extract-hashtags.ts @@ -1,9 +1,9 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; import { unique } from '@/misc/prelude/array.js'; export function extractHashtags(nodes: mfm.MfmNode[]): string[] { diff --git a/packages/backend/src/misc/extract-mentions.ts b/packages/backend/src/misc/extract-mentions.ts index b0897b05a85bf33e7fcedb67f09c23c0088dbc9a..bb21c32ffb22be638dc2dea5eecf4a9f3780a625 100644 --- a/packages/backend/src/misc/extract-mentions.ts +++ b/packages/backend/src/misc/extract-mentions.ts @@ -1,11 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ // test is located in test/extract-mentions -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; export function extractMentions(nodes: mfm.MfmNode[]): mfm.MfmMention['props'][] { // TODO: é‡è¤‡ã‚’削除 diff --git a/packages/backend/src/misc/fastify-hook-handlers.ts b/packages/backend/src/misc/fastify-hook-handlers.ts new file mode 100644 index 0000000000000000000000000000000000000000..49a48f6a6b000b79ff84c8b16ed33e87ee04e8de --- /dev/null +++ b/packages/backend/src/misc/fastify-hook-handlers.ts @@ -0,0 +1,9 @@ +import type { onRequestHookHandler } from 'fastify'; + +export const handleRequestRedirectToOmitSearch: onRequestHookHandler = (request, reply, done) => { + const index = request.url.indexOf('?'); + if (~index) { + reply.redirect(301, request.url.slice(0, index)); + } + done(); +}; diff --git a/packages/backend/src/misc/fastify-reply-error.ts b/packages/backend/src/misc/fastify-reply-error.ts index 7c889bab7a0016b843b16acf92d78c3cc8b4a5a5..e6c4e78d2f02603267721c96e4c113e452e4a08f 100644 --- a/packages/backend/src/misc/fastify-reply-error.ts +++ b/packages/backend/src/misc/fastify-reply-error.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/gen-identicon.ts b/packages/backend/src/misc/gen-identicon.ts index c36b00af63b4b46d76ae72c1334529974d78bb73..62a8ab8ace45e554a15979bace7cee7dfd6c037e 100644 --- a/packages/backend/src/misc/gen-identicon.ts +++ b/packages/backend/src/misc/gen-identicon.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/gen-key-pair.ts b/packages/backend/src/misc/gen-key-pair.ts index c0815613e7423d698f96ac73117e25f91da52612..02a303dc0a17d460bd605232a4d8eca276bdf6bf 100644 --- a/packages/backend/src/misc/gen-key-pair.ts +++ b/packages/backend/src/misc/gen-key-pair.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/generate-invite-code.ts b/packages/backend/src/misc/generate-invite-code.ts index 7c88561179eb4f98045b1bbfe4e6c240f2e8d999..006920cf0ef019f1d160fa26aca07a047c08d04f 100644 --- a/packages/backend/src/misc/generate-invite-code.ts +++ b/packages/backend/src/misc/generate-invite-code.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/generate-native-user-token.ts b/packages/backend/src/misc/generate-native-user-token.ts index 094c6251207634cd018030b52011f623c2af35be..85fb383ba25616a79eaf8fd3cb956d0b96f8c4c0 100644 --- a/packages/backend/src/misc/generate-native-user-token.ts +++ b/packages/backend/src/misc/generate-native-user-token.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/get-ip-hash.ts b/packages/backend/src/misc/get-ip-hash.ts index 3a01e4f5784681cfbe40e77f71c22da295fe356f..e132fa8f3113b018c805f19d386ea4c816b8e2a9 100644 --- a/packages/backend/src/misc/get-ip-hash.ts +++ b/packages/backend/src/misc/get-ip-hash.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/get-note-summary.ts b/packages/backend/src/misc/get-note-summary.ts index 1bda5cdcf709011b1bd7836dcf46ebb4742ce30f..1a07139a50e5a8d210450d6956f77c67657ac753 100644 --- a/packages/backend/src/misc/get-note-summary.ts +++ b/packages/backend/src/misc/get-note-summary.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/get-reaction-emoji.ts b/packages/backend/src/misc/get-reaction-emoji.ts index 80ef7ff7bc238e0617b8ad2ba7af8a9344c00739..3f975853edd224f112a168ed2abdaa5e03b9ddc8 100644 --- a/packages/backend/src/misc/get-reaction-emoji.ts +++ b/packages/backend/src/misc/get-reaction-emoji.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/i18n.ts b/packages/backend/src/misc/i18n.ts index 4c9d1a08e389206ee9c827bcac4aff210d75e877..6cbbdef74cfb9606cf0b1e7878c6b21d6fe420b6 100644 --- a/packages/backend/src/misc/i18n.ts +++ b/packages/backend/src/misc/i18n.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/id/aid.ts b/packages/backend/src/misc/id/aid.ts index de03f6793f3016d719b239cfee5bb8526b40172d..60ba788e44c5f1f4bbc2692d85b3f5e4a16c85e3 100644 --- a/packages/backend/src/misc/id/aid.ts +++ b/packages/backend/src/misc/id/aid.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/id/aidx.ts b/packages/backend/src/misc/id/aidx.ts index 9f457f6f0aa2737d3a17c760f0bd58c4d7566123..1b087e70af2f1b550f80f3fe840f39cdfddba17c 100644 --- a/packages/backend/src/misc/id/aidx.ts +++ b/packages/backend/src/misc/id/aidx.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/id/meid.ts b/packages/backend/src/misc/id/meid.ts index 7646282edb00ecefcb02c9e3f1315c9cbd360442..dfab48a36990e505bc089ffc1326143cb1648b34 100644 --- a/packages/backend/src/misc/id/meid.ts +++ b/packages/backend/src/misc/id/meid.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/id/meidg.ts b/packages/backend/src/misc/id/meidg.ts index f2a55443efb377fc401fcc3f8c8cfe52c27f2273..b9c0cc3dda3c3400a6ab59ac5ed196f936867864 100644 --- a/packages/backend/src/misc/id/meidg.ts +++ b/packages/backend/src/misc/id/meidg.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/id/object-id.ts b/packages/backend/src/misc/id/object-id.ts index f5c3619fdbd09805c0bbb1194b8bd4902aa0c6fe..243f92bbac1e20d4cab2defccb4a0b6fab8afc78 100644 --- a/packages/backend/src/misc/id/object-id.ts +++ b/packages/backend/src/misc/id/object-id.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/id/ulid.ts b/packages/backend/src/misc/id/ulid.ts index 00dd67dafe5ee6148d81fea823a9406ed8b61c21..fc3654d6d2a7e9086eb6e9890bbc8d03c7f62c8f 100644 --- a/packages/backend/src/misc/id/ulid.ts +++ b/packages/backend/src/misc/id/ulid.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/identifiable-error.ts b/packages/backend/src/misc/identifiable-error.ts index 71a4773fac71d921e54d1f7adb1920a4628d39ad..13c41f1e3bd6064059510f905c402458bb78f4d5 100644 --- a/packages/backend/src/misc/identifiable-error.ts +++ b/packages/backend/src/misc/identifiable-error.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/is-duplicate-key-value-error.ts b/packages/backend/src/misc/is-duplicate-key-value-error.ts index 91e0a6b93d5f87cadfbbce12ed034b94dadd9d7f..8da0280f60f1bbdff01203b91fb47cc0b2215401 100644 --- a/packages/backend/src/misc/is-duplicate-key-value-error.ts +++ b/packages/backend/src/misc/is-duplicate-key-value-error.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/is-instance-muted.ts b/packages/backend/src/misc/is-instance-muted.ts index 35fe11849d50cecb025c46b0569d2f36fa035959..096a8b39c72b307814c7905920428af8e8a9edfa 100644 --- a/packages/backend/src/misc/is-instance-muted.ts +++ b/packages/backend/src/misc/is-instance-muted.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/is-mime-image.ts b/packages/backend/src/misc/is-mime-image.ts index 1a5a8cf0f42ec9581739345cd50379ecde7be07a..8ffbc9923066ac0e83acc671559b9504fb75797e 100644 --- a/packages/backend/src/misc/is-mime-image.ts +++ b/packages/backend/src/misc/is-mime-image.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/is-native-token.ts b/packages/backend/src/misc/is-native-token.ts index 618e60b7d8701729630d5b531ccd91e50efedc97..300c4c05b339185e40a6e060489dfaa4d7bffbd9 100644 --- a/packages/backend/src/misc/is-native-token.ts +++ b/packages/backend/src/misc/is-native-token.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/is-not-null.ts b/packages/backend/src/misc/is-not-null.ts index 153a9e51efea9a16cf76ef7f3d92548dc1bbc6e7..8d9dc8bb396e04b004d482abeda128c431397878 100644 --- a/packages/backend/src/misc/is-not-null.ts +++ b/packages/backend/src/misc/is-not-null.ts @@ -1,10 +1,8 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -// we are using {} as "any non-nullish value" as expected -// eslint-disable-next-line @typescript-eslint/ban-types -export function isNotNull<T extends {}>(input: T | undefined | null): input is T { +export function isNotNull<T extends NonNullable<unknown>>(input: T | undefined | null): input is T { return input != null; } diff --git a/packages/backend/src/misc/is-quote.ts b/packages/backend/src/misc/is-quote.ts index db72d1d57abf5419dbfc7ac616e377f456092b7e..75b29f63f4929c973308bfbc5388c79d2a163e66 100644 --- a/packages/backend/src/misc/is-quote.ts +++ b/packages/backend/src/misc/is-quote.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/is-reply.ts b/packages/backend/src/misc/is-reply.ts index 964c2aa15393f63a2d2cd70f8f09c0e606f5f602..980eae11c91588a5d669356bce4e029b8c5659b3 100644 --- a/packages/backend/src/misc/is-reply.ts +++ b/packages/backend/src/misc/is-reply.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/is-user-related.ts b/packages/backend/src/misc/is-user-related.ts index 6efb1194d3bba974b5032635070e0442190c7162..93c9b2b814bfabcf1afaa039a6a87bb52db09fab 100644 --- a/packages/backend/src/misc/is-user-related.ts +++ b/packages/backend/src/misc/is-user-related.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index 176978d35f63c3a962bf09dca07f4ed121d864d2..46b0bb2fab67790ab9792d054c789c19e081a848 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -25,7 +25,7 @@ import { packedBlockingSchema } from '@/models/json-schema/blocking.js'; import { packedNoteReactionSchema } from '@/models/json-schema/note-reaction.js'; import { packedHashtagSchema } from '@/models/json-schema/hashtag.js'; import { packedInviteCodeSchema } from '@/models/json-schema/invite-code.js'; -import { packedPageSchema } from '@/models/json-schema/page.js'; +import { packedPageSchema, packedPageBlockSchema } from '@/models/json-schema/page.js'; import { packedNoteFavoriteSchema } from '@/models/json-schema/note-favorite.js'; import { packedChannelSchema } from '@/models/json-schema/channel.js'; import { packedAntennaSchema } from '@/models/json-schema/antenna.js'; @@ -37,8 +37,25 @@ import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/jso import { packedFlashSchema } from '@/models/json-schema/flash.js'; import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js'; import { packedSigninSchema } from '@/models/json-schema/signin.js'; -import { packedRoleLiteSchema, packedRoleSchema } from '@/models/json-schema/role.js'; +import { + packedRoleLiteSchema, + packedRoleSchema, + packedRolePoliciesSchema, + packedRoleCondFormulaLogicsSchema, + packedRoleCondFormulaValueNot, + packedRoleCondFormulaValueIsLocalOrRemoteSchema, + packedRoleCondFormulaValueAssignedRoleSchema, + packedRoleCondFormulaValueCreatedSchema, + packedRoleCondFormulaFollowersOrFollowingOrNotesSchema, + packedRoleCondFormulaValueSchema, +} from '@/models/json-schema/role.js'; import { packedAdSchema } from '@/models/json-schema/ad.js'; +import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js'; +import { + packedMetaLiteSchema, + packedMetaDetailedOnlySchema, + packedMetaDetailedSchema, +} from '@/models/json-schema/meta.js'; export const refs = { UserLite: packedUserLiteSchema, @@ -66,6 +83,7 @@ export const refs = { Hashtag: packedHashtagSchema, InviteCode: packedInviteCodeSchema, Page: packedPageSchema, + PageBlock: packedPageBlockSchema, Channel: packedChannelSchema, QueueCount: packedQueueCountSchema, Antenna: packedAntennaSchema, @@ -76,12 +94,28 @@ export const refs = { EmojiDetailed: packedEmojiDetailedSchema, Flash: packedFlashSchema, Signin: packedSigninSchema, + RoleCondFormulaLogics: packedRoleCondFormulaLogicsSchema, + RoleCondFormulaValueNot: packedRoleCondFormulaValueNot, + RoleCondFormulaValueIsLocalOrRemote: packedRoleCondFormulaValueIsLocalOrRemoteSchema, + RoleCondFormulaValueAssignedRole: packedRoleCondFormulaValueAssignedRoleSchema, + RoleCondFormulaValueCreated: packedRoleCondFormulaValueCreatedSchema, + RoleCondFormulaFollowersOrFollowingOrNotes: packedRoleCondFormulaFollowersOrFollowingOrNotesSchema, + RoleCondFormulaValue: packedRoleCondFormulaValueSchema, RoleLite: packedRoleLiteSchema, Role: packedRoleSchema, + RolePolicies: packedRolePoliciesSchema, + ReversiGameLite: packedReversiGameLiteSchema, + ReversiGameDetailed: packedReversiGameDetailedSchema, + MetaLite: packedMetaLiteSchema, + MetaDetailedOnly: packedMetaDetailedOnlySchema, + MetaDetailed: packedMetaDetailedSchema, }; export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>; +export type KeyOf<x extends keyof typeof refs> = PropertiesToUnion<typeof refs[x]>; +type PropertiesToUnion<p extends Schema> = p['properties'] extends NonNullable<Obj> ? keyof p['properties'] : never; + type TypeStringef = 'null' | 'boolean' | 'integer' | 'number' | 'string' | 'array' | 'object' | 'any'; type StringDefToType<T extends TypeStringef> = T extends 'null' ? null : @@ -111,6 +145,7 @@ export interface Schema extends OfSchema { readonly example?: any; readonly format?: string; readonly ref?: keyof typeof refs; + readonly selfRef?: boolean; readonly enum?: ReadonlyArray<string | null>; readonly default?: (this['type'] extends TypeStringef ? StringDefToType<this['type']> : any) | null; readonly maxLength?: number; diff --git a/packages/backend/src/misc/langmap.ts b/packages/backend/src/misc/langmap.ts index 9e287677dfffc4a455f6bb2e160dff52c350c5f8..5ff9338651140f65301201407e75732b0eceacb5 100644 --- a/packages/backend/src/misc/langmap.ts +++ b/packages/backend/src/misc/langmap.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/normalize-for-search.ts b/packages/backend/src/misc/normalize-for-search.ts index 9d96f4169db7a47a6b26af61675ba0773ca0950e..3f19617e1480f14167f980f2bec85fd3438fd7c1 100644 --- a/packages/backend/src/misc/normalize-for-search.ts +++ b/packages/backend/src/misc/normalize-for-search.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/prelude/array.ts b/packages/backend/src/misc/prelude/array.ts index 8438b648054f8788e521689e8c163e3bb70b59b4..bd6c8ee8e3d7d31bfa3eb9aa438182d6c4068980 100644 --- a/packages/backend/src/misc/prelude/array.ts +++ b/packages/backend/src/misc/prelude/array.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/prelude/await-all.ts b/packages/backend/src/misc/prelude/await-all.ts index 6b8a91f8a55c0fe379d30f6ea956fd89be80ae8b..48249fe1ae5373b6367e1d3e15f9d253113c60ba 100644 --- a/packages/backend/src/misc/prelude/await-all.ts +++ b/packages/backend/src/misc/prelude/await-all.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/prelude/math.ts b/packages/backend/src/misc/prelude/math.ts index 87b5017d09aad7d29f5ecefc5f6874458951664d..38556def2dcfd52daee7d4a770aa2fcdd91d008a 100644 --- a/packages/backend/src/misc/prelude/math.ts +++ b/packages/backend/src/misc/prelude/math.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/prelude/maybe.ts b/packages/backend/src/misc/prelude/maybe.ts index 17c100b80dfeea9d4540db08933d37f69f613a8f..1c58ccb9c778ddb6d518a2d9c7e9b826f0276c9d 100644 --- a/packages/backend/src/misc/prelude/maybe.ts +++ b/packages/backend/src/misc/prelude/maybe.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/prelude/relation.ts b/packages/backend/src/misc/prelude/relation.ts index 3456c1a0bc4b9db827c2563f90a1750cc1cca105..7dcd4c700a4a165b6105aab39266f20530a3b340 100644 --- a/packages/backend/src/misc/prelude/relation.ts +++ b/packages/backend/src/misc/prelude/relation.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/prelude/string.ts b/packages/backend/src/misc/prelude/string.ts index a727ab7f1d8ff72e52f1835ce626c4870a187c61..67ea5299619c19c7b7ae7da51ece0530eb6a9f72 100644 --- a/packages/backend/src/misc/prelude/string.ts +++ b/packages/backend/src/misc/prelude/string.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/prelude/symbol.ts b/packages/backend/src/misc/prelude/symbol.ts index 91c058a845d7cba01dfe16cb21ba93869a060b04..7e8d39bdb6b9777f24bac245ae3d7500cd79f81e 100644 --- a/packages/backend/src/misc/prelude/symbol.ts +++ b/packages/backend/src/misc/prelude/symbol.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/prelude/time.ts b/packages/backend/src/misc/prelude/time.ts index 4479db10818ffc0631d0e8f367f0d5d28a3af893..275b67ed00be1e68aecf1d088804fb1d9cd54b2b 100644 --- a/packages/backend/src/misc/prelude/time.ts +++ b/packages/backend/src/misc/prelude/time.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/prelude/url.ts b/packages/backend/src/misc/prelude/url.ts index 633eb982182dde49ebe093ffc6e8c73399ed45db..270a0750754eac81c651b7090bd0a7c2d7b3c798 100644 --- a/packages/backend/src/misc/prelude/url.ts +++ b/packages/backend/src/misc/prelude/url.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/prelude/xml.ts b/packages/backend/src/misc/prelude/xml.ts index bca116a7ec59f42c314071b0914727d4aa6b0fb3..61c166cee544ac655a863f026bb65fc93a82928f 100644 --- a/packages/backend/src/misc/prelude/xml.ts +++ b/packages/backend/src/misc/prelude/xml.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/promise-tracker.ts b/packages/backend/src/misc/promise-tracker.ts new file mode 100644 index 0000000000000000000000000000000000000000..8a52ca703e75c1df844eac54d760d0ed3b64c8aa --- /dev/null +++ b/packages/backend/src/misc/promise-tracker.ts @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +const promiseRefs: Set<WeakRef<Promise<unknown>>> = new Set(); + +/** + * This tracks promises that other modules decided not to wait for, + * and makes sure they are all settled before fully closing down the server. + */ +export function trackPromise(promise: Promise<unknown>) { + if (process.env.NODE_ENV !== 'test') { + return; + } + const ref = new WeakRef(promise); + promiseRefs.add(ref); + promise.finally(() => promiseRefs.delete(ref)); +} + +export async function allSettled(): Promise<void> { + await Promise.allSettled([...promiseRefs].map(r => r.deref())); +} diff --git a/packages/backend/src/misc/reset-db.ts b/packages/backend/src/misc/reset-db.ts index a571460a59a845049eeb132da19b34a3ed210c0e..75fb4c3e7bef2a81bf639f848b283c641f50398d 100644 --- a/packages/backend/src/misc/reset-db.ts +++ b/packages/backend/src/misc/reset-db.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/safe-for-sql.ts b/packages/backend/src/misc/safe-for-sql.ts index d7bdd0a81ccebec343c3c116deb474c8807e16f0..ac4b8e2e2eccbcf6d5c1909d6ae781ca54aaed2d 100644 --- a/packages/backend/src/misc/safe-for-sql.ts +++ b/packages/backend/src/misc/safe-for-sql.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/secure-rndstr.ts b/packages/backend/src/misc/secure-rndstr.ts index 01368d808afe5832e60a1d9b1fdc93e95574473f..7853100d89f4802eee14540a9e2b83bc15478c35 100644 --- a/packages/backend/src/misc/secure-rndstr.ts +++ b/packages/backend/src/misc/secure-rndstr.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/show-machine-info.ts b/packages/backend/src/misc/show-machine-info.ts index ed0fa651f1bc8e443f5491c01cef1963089d3ef0..8ddec35f239876689073743d398cbcc8274ea1d2 100644 --- a/packages/backend/src/misc/show-machine-info.ts +++ b/packages/backend/src/misc/show-machine-info.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/sql-like-escape.ts b/packages/backend/src/misc/sql-like-escape.ts index 85cc7405e1bc4ba87b8b24d60e7e28c74a421d98..0c052556744e03ff932839a50eb6c07c46081952 100644 --- a/packages/backend/src/misc/sql-like-escape.ts +++ b/packages/backend/src/misc/sql-like-escape.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/misc/status-error.ts b/packages/backend/src/misc/status-error.ts index 4285685d2421763ecc0a4b4f49aae0026c14067a..c3533db60759a0f82ca7ee82772b89899e5cc942 100644 --- a/packages/backend/src/misc/status-error.ts +++ b/packages/backend/src/misc/status-error.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -7,6 +7,7 @@ export class StatusError extends Error { public statusCode: number; public statusMessage?: string; public isClientError: boolean; + public isRetryable: boolean; constructor(message: string, statusCode: number, statusMessage?: string) { super(message); @@ -14,5 +15,6 @@ export class StatusError extends Error { this.statusCode = statusCode; this.statusMessage = statusMessage; this.isClientError = typeof this.statusCode === 'number' && this.statusCode >= 400 && this.statusCode < 500; + this.isRetryable = !this.isClientError || this.statusCode === 429; } } diff --git a/packages/backend/src/misc/truncate.ts b/packages/backend/src/misc/truncate.ts index b65202fbd4dc0c0c992f6594c6176521ceab08de..1c8a274609a122aa16521b22ac76618938ebc354 100644 --- a/packages/backend/src/misc/truncate.ts +++ b/packages/backend/src/misc/truncate.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/AbuseUserReport.ts b/packages/backend/src/models/AbuseUserReport.ts index 593c44f66b0a09ea0bc50e743d6698fd89bb24b1..0615fd7eb5e342518feb6df2aa074a28e1a139f3 100644 --- a/packages/backend/src/models/AbuseUserReport.ts +++ b/packages/backend/src/models/AbuseUserReport.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/AccessToken.ts b/packages/backend/src/models/AccessToken.ts index 452711eb8c25ed3882abc3668ebb983701da1bee..6f98c14ec1dd6186f80a695e9ae394bb6d6f1fca 100644 --- a/packages/backend/src/models/AccessToken.ts +++ b/packages/backend/src/models/AccessToken.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Ad.ts b/packages/backend/src/models/Ad.ts index b1d7d7d79ec1d68736df13954d4b0183679bfdd6..108e991c70e8b078a2a019824e758082cd719793 100644 --- a/packages/backend/src/models/Ad.ts +++ b/packages/backend/src/models/Ad.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Announcement.ts b/packages/backend/src/models/Announcement.ts index 8f8be88fed504ed93929650cf0df0f31c18aecaf..d0c59fff50ae1716eb5c17e8b74064e63706d30d 100644 --- a/packages/backend/src/models/Announcement.ts +++ b/packages/backend/src/models/Announcement.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -38,7 +38,7 @@ export class MiAnnouncement { length: 256, nullable: false, default: 'info', }) - public icon: string; + public icon: 'info' | 'warning' | 'error' | 'success'; // normal ... ãŠçŸ¥ã‚‰ã›ãƒšãƒ¼ã‚¸æŽ²è¼‰ // banner ... ãŠçŸ¥ã‚‰ã›ãƒšãƒ¼ã‚¸æŽ²è¼‰ + ãƒãƒŠãƒ¼è¡¨ç¤º @@ -47,7 +47,7 @@ export class MiAnnouncement { length: 256, nullable: false, default: 'normal', }) - public display: string; + public display: 'normal' | 'banner' | 'dialog'; @Column('boolean', { default: false, diff --git a/packages/backend/src/models/AnnouncementRead.ts b/packages/backend/src/models/AnnouncementRead.ts index db09e65f50f33b1ce328624712bd4dfef7123f9d..47de8dd1805018620cff53f442a1129be9502f90 100644 --- a/packages/backend/src/models/AnnouncementRead.ts +++ b/packages/backend/src/models/AnnouncementRead.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Antenna.ts b/packages/backend/src/models/Antenna.ts index b74c61b728d260fdf305dd5b2f97c1eed3271657..332a8997683ff63f9c26f2e2364bf383f6490fd5 100644 --- a/packages/backend/src/models/Antenna.ts +++ b/packages/backend/src/models/Antenna.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/App.ts b/packages/backend/src/models/App.ts index 5c56a224a2fdbb0b73c5dfe5b28979ac332da460..0185e2995c864096cdc6639ee9f8e7f3cce1c0ba 100644 --- a/packages/backend/src/models/App.ts +++ b/packages/backend/src/models/App.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/AuthSession.ts b/packages/backend/src/models/AuthSession.ts index 81bed212110a772e43083e7f795f421fd2c0c7e0..03050ba9551b0c6faa382ab0aad9531542253f7b 100644 --- a/packages/backend/src/models/AuthSession.ts +++ b/packages/backend/src/models/AuthSession.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/AvatarDecoration.ts b/packages/backend/src/models/AvatarDecoration.ts index 08ebbdeac1449c67d651c633d980afa956109ed1..13f0b0566740095fd99f31922abcca0ec5d37258 100644 --- a/packages/backend/src/models/AvatarDecoration.ts +++ b/packages/backend/src/models/AvatarDecoration.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Blocking.ts b/packages/backend/src/models/Blocking.ts index 9bf7a63b6e4c1edbed6b4ff174957394959e2c12..34a6efe5a6f3466935854648424aace6f000efea 100644 --- a/packages/backend/src/models/Blocking.ts +++ b/packages/backend/src/models/Blocking.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/BubbleGameRecord.ts b/packages/backend/src/models/BubbleGameRecord.ts new file mode 100644 index 0000000000000000000000000000000000000000..686e39c118c916fe6c6acb9441791cc74c06eae7 --- /dev/null +++ b/packages/backend/src/models/BubbleGameRecord.ts @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; + +@Entity('bubble_game_record') +export class MiBubbleGameRecord { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column({ + ...id(), + }) + public userId: MiUser['id']; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: MiUser | null; + + @Index() + @Column('timestamp with time zone') + public seededAt: Date; + + @Column('varchar', { + length: 1024, + }) + public seed: string; + + @Column('integer') + public gameVersion: number; + + @Column('varchar', { + length: 128, + }) + public gameMode: string; + + @Index() + @Column('integer') + public score: number; + + @Column('jsonb', { + default: [], + }) + public logs: number[][]; + + @Column('boolean', { + default: false, + }) + public isVerified: boolean; +} diff --git a/packages/backend/src/models/Channel.ts b/packages/backend/src/models/Channel.ts index a7f9e262b1127b23021d5c61b01502b61d7f1ed4..f5e9b17e3e1ec95ad1abf9cd714c3163f4ec0443 100644 --- a/packages/backend/src/models/Channel.ts +++ b/packages/backend/src/models/Channel.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/ChannelFavorite.ts b/packages/backend/src/models/ChannelFavorite.ts index fc25ffe2604f31eb1e74d9a71b5017219534b16c..167f41cf160380ffa5ce578772373de3c13632c4 100644 --- a/packages/backend/src/models/ChannelFavorite.ts +++ b/packages/backend/src/models/ChannelFavorite.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/ChannelFollowing.ts b/packages/backend/src/models/ChannelFollowing.ts index 4dd391a082f26f001577ff053d3fde6ee1d8aeb1..c7afdd05b0911ef1b1c050466189b95f4940f77f 100644 --- a/packages/backend/src/models/ChannelFollowing.ts +++ b/packages/backend/src/models/ChannelFollowing.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Clip.ts b/packages/backend/src/models/Clip.ts index 2483b0925a0bdcf201b3ba039fbf0ad5e8992350..6295a329fbeaa9c94a6de6deb35da55d2ddf9a1e 100644 --- a/packages/backend/src/models/Clip.ts +++ b/packages/backend/src/models/Clip.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/ClipFavorite.ts b/packages/backend/src/models/ClipFavorite.ts index aa949b3ea8cb5e1d375358614a96d7dd787dbbce..40bdb9f4aace6681e1cc63fb8a02635900028ef8 100644 --- a/packages/backend/src/models/ClipFavorite.ts +++ b/packages/backend/src/models/ClipFavorite.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/ClipNote.ts b/packages/backend/src/models/ClipNote.ts index b7cc5ee39bf20b40629a2e809e581dff2d3b7bc3..6e1d2bec4c3550f1e02478bfa1e1e880c1d3b79b 100644 --- a/packages/backend/src/models/ClipNote.ts +++ b/packages/backend/src/models/ClipNote.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/DriveFile.ts b/packages/backend/src/models/DriveFile.ts index ca564f6f0c0be7e36dc2cad40420b10a7d48ba77..efb639f07592b9983fba26c5e894d2b268c895b6 100644 --- a/packages/backend/src/models/DriveFile.ts +++ b/packages/backend/src/models/DriveFile.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/DriveFolder.ts b/packages/backend/src/models/DriveFolder.ts index 18f6d17709c020324edad119d1e064f6efe256cf..07046d6e113e92a315b95405f1ecf7a930b764c7 100644 --- a/packages/backend/src/models/DriveFolder.ts +++ b/packages/backend/src/models/DriveFolder.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Emoji.ts b/packages/backend/src/models/Emoji.ts index 563ac1d9d3066d3d37565bb37a45fbd0b91b8f35..d62b6e9f6f1988429c934262cecd217da410f9f3 100644 --- a/packages/backend/src/models/Emoji.ts +++ b/packages/backend/src/models/Emoji.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Flash.ts b/packages/backend/src/models/Flash.ts index ac880843b0e28dcfd71f3708427e062d2823c235..a1469a0d947809d81f5b0a3769449815865a9c36 100644 --- a/packages/backend/src/models/Flash.ts +++ b/packages/backend/src/models/Flash.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/FlashLike.ts b/packages/backend/src/models/FlashLike.ts index ad7f4966b492ddf9bc2433dbe354352cd2a7723a..a9fb48123e5f4344ed5cebe76938f99059e613d4 100644 --- a/packages/backend/src/models/FlashLike.ts +++ b/packages/backend/src/models/FlashLike.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/FollowRequest.ts b/packages/backend/src/models/FollowRequest.ts index 9899694dd61db6b94951782bc4f99cc3b7cb7cbb..3ff5e7a4780d3ed9a7043fa7ee2bbf299054fffd 100644 --- a/packages/backend/src/models/FollowRequest.ts +++ b/packages/backend/src/models/FollowRequest.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Following.ts b/packages/backend/src/models/Following.ts index e320911a1dee2cc6bb8eaaaf49625193aa561c5d..62cbc29f261b7f2a09153271ff17551b8cc4bcdb 100644 --- a/packages/backend/src/models/Following.ts +++ b/packages/backend/src/models/Following.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/GalleryLike.ts b/packages/backend/src/models/GalleryLike.ts index 84d4ce9c3eca270d8ad8b94908c28647a2b4a6b8..ed0963122d847378c03fde9a8d895f40e95646cc 100644 --- a/packages/backend/src/models/GalleryLike.ts +++ b/packages/backend/src/models/GalleryLike.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/GalleryPost.ts b/packages/backend/src/models/GalleryPost.ts index b72220caf99a0ea34eba22cab58a6b0e9ee4900c..04d8823e3741d2fc4b57291648e464d64a37a5e1 100644 --- a/packages/backend/src/models/GalleryPost.ts +++ b/packages/backend/src/models/GalleryPost.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Hashtag.ts b/packages/backend/src/models/Hashtag.ts index 14937747524107593b65faee4e1e4f17907dd128..3add06d0c382e68438d0923ad3b55403dbaaf017 100644 --- a/packages/backend/src/models/Hashtag.ts +++ b/packages/backend/src/models/Hashtag.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Instance.ts b/packages/backend/src/models/Instance.ts index 4200b1b461928beb68ad87f8850b3966f44b3966..7dd4e5b10c6da7ef3e89f8a7c2ead709faa5cd20 100644 --- a/packages/backend/src/models/Instance.ts +++ b/packages/backend/src/models/Instance.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -149,4 +149,9 @@ export class MiInstance { default: false, }) public isNSFW: boolean; + + @Column('varchar', { + length: 16384, default: '', + }) + public moderationNote: string; } diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 4bf856e619ebf609571179e19d20eef2e956e000..dd2e78cde28045f5e41d902fa1740e0031d5d686 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -76,6 +76,11 @@ export class MiMeta { }) public sensitiveWords: string[]; + @Column('varchar', { + length: 1024, array: true, default: '{}', + }) + public prohibitedWords: string[]; + @Column('varchar', { length: 1024, array: true, default: '{}', }) @@ -196,6 +201,29 @@ export class MiMeta { }) public hcaptchaSecretKey: string | null; + @Column('boolean', { + default: false, + }) + public enableMcaptcha: boolean; + + @Column('varchar', { + length: 1024, + nullable: true, + }) + public mcaptchaSitekey: string | null; + + @Column('varchar', { + length: 1024, + nullable: true, + }) + public mcaptchaSecretKey: string | null; + + @Column('varchar', { + length: 1024, + nullable: true, + }) + public mcaptchaInstanceUrl: string | null; + @Column('boolean', { default: false, }) @@ -230,6 +258,8 @@ export class MiMeta { }) public turnstileSecretKey: string | null; + // chaptchaç³»ã‚’è¿½åŠ ã—ãŸéš›ã«ã¯nodeinfoã®ãƒ¬ã‚¹ãƒãƒ³ã‚¹ã«è¿½åŠ ã™ã‚‹ã®ã‚’忘れãªã„よã†ã«ã™ã‚‹ã“㨠+ @Column('enum', { enum: ['none', 'all', 'local', 'remote'], default: 'none', @@ -330,6 +360,17 @@ export class MiMeta { }) public deeplIsPro: boolean; + @Column('boolean', { + default: false, + }) + public deeplFreeMode: boolean; + + @Column('varchar', { + length: 1024, + nullable: true, + }) + public deeplFreeInstance: string | null; + @Column('varchar', { length: 1024, nullable: true, @@ -338,14 +379,14 @@ export class MiMeta { @Column('varchar', { length: 1024, - default: 'https://github.com/misskey-dev/misskey', + default: 'https://activitypub.software/TransFem-org/Sharkey/', nullable: false, }) - public repositoryUrl: string; + public repositoryUrl: string | null; @Column('varchar', { length: 1024, - default: 'https://github.com/misskey-dev/misskey/issues/new', + default: 'https://activitypub.software/TransFem-org/Sharkey/-/issues/new', nullable: true, }) public feedbackUrl: string | null; @@ -362,6 +403,12 @@ export class MiMeta { }) public privacyPolicyUrl: string | null; + @Column('varchar', { + length: 1024, + nullable: true, + }) + public donationUrl: string | null; + @Column('varchar', { length: 8192, nullable: true, @@ -467,6 +514,23 @@ export class MiMeta { }) public verifymailAuthKey: string | null; + @Column('boolean', { + default: false, + }) + public enableTruemailApi: boolean; + + @Column('varchar', { + length: 1024, + nullable: true, + }) + public truemailInstance: string | null; + + @Column('varchar', { + length: 1024, + nullable: true, + }) + public truemailAuthKey: string | null; + @Column('boolean', { default: true, }) diff --git a/packages/backend/src/models/ModerationLog.ts b/packages/backend/src/models/ModerationLog.ts index 71b33c3e4748009f72ad040b28b58f5579338120..edde315fdf9ac742230570d477f577a9ad88e4e4 100644 --- a/packages/backend/src/models/ModerationLog.ts +++ b/packages/backend/src/models/ModerationLog.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Muting.ts b/packages/backend/src/models/Muting.ts index a528e1e7d704bb1ad24118575b33ee5d27944faf..e1240b9c4e5794e3789c659e801513c431f8b3e4 100644 --- a/packages/backend/src/models/Muting.ts +++ b/packages/backend/src/models/Muting.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Note.ts b/packages/backend/src/models/Note.ts index 2705282880247bb7c37581c0486a5fd330afbdf3..b11e2ec62b76817cbea0a7e0d90ca006642240f9 100644 --- a/packages/backend/src/models/Note.ts +++ b/packages/backend/src/models/Note.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -11,9 +11,6 @@ import { MiChannel } from './Channel.js'; import type { MiDriveFile } from './DriveFile.js'; @Entity('note') -@Index('IDX_NOTE_TAGS', { synchronize: false }) -@Index('IDX_NOTE_MENTIONS', { synchronize: false }) -@Index('IDX_NOTE_VISIBLE_USER_IDS', { synchronize: false }) export class MiNote { @PrimaryColumn(id()) public id: string; @@ -139,7 +136,7 @@ export class MiNote { }) public url: string | null; - @Index() + @Index('IDX_NOTE_FILE_IDS', { synchronize: false }) @Column({ ...id(), array: true, default: '{}', @@ -151,14 +148,14 @@ export class MiNote { }) public attachedFileTypes: string[]; - @Index() + @Index('IDX_NOTE_VISIBLE_USER_IDS', { synchronize: false }) @Column({ ...id(), array: true, default: '{}', }) public visibleUserIds: MiUser['id'][]; - @Index() + @Index('IDX_NOTE_MENTIONS', { synchronize: false }) @Column({ ...id(), array: true, default: '{}', @@ -180,7 +177,7 @@ export class MiNote { }) public emojis: string[]; - @Index() + @Index('IDX_NOTE_TAGS', { synchronize: false }) @Column('varchar', { length: 128, array: true, default: '{}', }) diff --git a/packages/backend/src/models/NoteFavorite.ts b/packages/backend/src/models/NoteFavorite.ts index 364eaabd986933a87d488eaed92ff3043862309c..cf76c767b026e25bb2277fbb6cc0dfac4a67cba3 100644 --- a/packages/backend/src/models/NoteFavorite.ts +++ b/packages/backend/src/models/NoteFavorite.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/NoteReaction.ts b/packages/backend/src/models/NoteReaction.ts index ee3a447464f909cbda93b35e35623c6d6b04cd6a..42dfcaa9ad877e28128ef199d3a24f1032a2b2c5 100644 --- a/packages/backend/src/models/NoteReaction.ts +++ b/packages/backend/src/models/NoteReaction.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/NoteThreadMuting.ts b/packages/backend/src/models/NoteThreadMuting.ts index 00311aa5707c6b196cf9fb752530b5128e68376d..e7bd39f348bb444d9a499ddd1f4d58eb12ad5dbc 100644 --- a/packages/backend/src/models/NoteThreadMuting.ts +++ b/packages/backend/src/models/NoteThreadMuting.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/NoteUnread.ts b/packages/backend/src/models/NoteUnread.ts index d86a4745538702560c5c13918f0ee020e073f777..c759181117acdc346ba299b526a24a486d7372fc 100644 --- a/packages/backend/src/models/NoteUnread.ts +++ b/packages/backend/src/models/NoteUnread.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Notification.ts b/packages/backend/src/models/Notification.ts index 3bc2edaa0dd54285fc83563f87d3fff5bfd8642d..4ed71a106cb1b2d6eaf7ee776fb837bc801c0139 100644 --- a/packages/backend/src/models/Notification.ts +++ b/packages/backend/src/models/Notification.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -107,6 +107,12 @@ export type MiNotification = { type: 'test'; id: string; createdAt: string; +} | { + type: 'edited'; + id: string; + createdAt: string; + notifierId: MiUser['id']; + noteId: MiNote['id']; }; export type MiGroupedNotification = MiNotification | { diff --git a/packages/backend/src/models/Page.ts b/packages/backend/src/models/Page.ts index 9cab875499f7d22c88cddedc8766d3133d36c836..1695bf570ed33b4128eca5fde5e48c2f50795426 100644 --- a/packages/backend/src/models/Page.ts +++ b/packages/backend/src/models/Page.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/PageLike.ts b/packages/backend/src/models/PageLike.ts index b845f58b7d6fc6ab799cec994e5c943c64e5dd2e..05ca22cf2cd86453584830519154ffe57d1134b4 100644 --- a/packages/backend/src/models/PageLike.ts +++ b/packages/backend/src/models/PageLike.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/PasswordResetRequest.ts b/packages/backend/src/models/PasswordResetRequest.ts index 5be439511f0f14a77d70ddc160d42a62f51f09c1..fdaf21056bce9c7980e31326756108b573c174fa 100644 --- a/packages/backend/src/models/PasswordResetRequest.ts +++ b/packages/backend/src/models/PasswordResetRequest.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Poll.ts b/packages/backend/src/models/Poll.ts index 5ce0b9a2fc423be955c22c033e6eac8448996b0e..c2693dbb193cd85dcd0cd5af32888932d44480a9 100644 --- a/packages/backend/src/models/Poll.ts +++ b/packages/backend/src/models/Poll.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/PollVote.ts b/packages/backend/src/models/PollVote.ts index 751be8a32b9d748c9e4ac9d14bae9e8c7e76c980..b5c780293c471e090b6af78b81436755a3887848 100644 --- a/packages/backend/src/models/PollVote.ts +++ b/packages/backend/src/models/PollVote.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/PromoNote.ts b/packages/backend/src/models/PromoNote.ts index f4425fe88b4291dfe21c6d3769015d057a39e648..ae27adec9ef70133e1426f3b567e4361a73f93f5 100644 --- a/packages/backend/src/models/PromoNote.ts +++ b/packages/backend/src/models/PromoNote.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/PromoRead.ts b/packages/backend/src/models/PromoRead.ts index d9f3075416c113e2e64bcb0f9cc2a302e7c324bd..b2a698cc7bfd93cf14269b06b5014e1f9f86450b 100644 --- a/packages/backend/src/models/PromoRead.ts +++ b/packages/backend/src/models/PromoRead.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/RegistrationTicket.ts b/packages/backend/src/models/RegistrationTicket.ts index 730cedffba4d6d491598db7707fa717f12af3a8f..0a4e4b9189e223ec6bad812eca8442d5420e15b0 100644 --- a/packages/backend/src/models/RegistrationTicket.ts +++ b/packages/backend/src/models/RegistrationTicket.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/RegistryItem.ts b/packages/backend/src/models/RegistryItem.ts index 60bdced9578c5fc691c6b046127c14fe6630de11..335e8b9eab8826c9c9528ef512b528d6dbbe6226 100644 --- a/packages/backend/src/models/RegistryItem.ts +++ b/packages/backend/src/models/RegistryItem.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Relay.ts b/packages/backend/src/models/Relay.ts index 293fccecfc9fbc5e181a7df1edff7dc13970773f..eca29160328890b5e5abc6d3588d8691b5a6671f 100644 --- a/packages/backend/src/models/Relay.ts +++ b/packages/backend/src/models/Relay.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/RenoteMuting.ts b/packages/backend/src/models/RenoteMuting.ts index 17df43ea314e41972ec4cc0158cc6705d96a3ad5..448a0b766312608fe5955ca4f6c9da310daa41a4 100644 --- a/packages/backend/src/models/RenoteMuting.ts +++ b/packages/backend/src/models/RenoteMuting.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index 0b5d3b640fec829366a756c817a34045bb650ae0..053edd6094e2b8b47b50a0a569feb96006bdba78 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -1,11 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { Module } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook, NoteEdit } from './_.js'; +import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook, NoteEdit, MiBubbleGameRecord, MiReversiGame } from './_.js'; import type { DataSource } from 'typeorm'; import type { Provider } from '@nestjs/common'; @@ -405,6 +405,18 @@ const $noteEditRepository: Provider = { inject: [DI.db], }; +const $bubbleGameRecordsRepository: Provider = { + provide: DI.bubbleGameRecordsRepository, + useFactory: (db: DataSource) => db.getRepository(MiBubbleGameRecord), + inject: [DI.db], +}; + +const $reversiGamesRepository: Provider = { + provide: DI.reversiGamesRepository, + useFactory: (db: DataSource) => db.getRepository(MiReversiGame), + inject: [DI.db], +}; + @Module({ imports: [ ], @@ -475,6 +487,8 @@ const $noteEditRepository: Provider = { $flashLikesRepository, $userMemosRepository, $noteEditRepository, + $bubbleGameRecordsRepository, + $reversiGamesRepository, ], exports: [ $usersRepository, @@ -543,6 +557,8 @@ const $noteEditRepository: Provider = { $flashLikesRepository, $userMemosRepository, $noteEditRepository, + $bubbleGameRecordsRepository, + $reversiGamesRepository, ], }) export class RepositoryModule {} diff --git a/packages/backend/src/models/RetentionAggregation.ts b/packages/backend/src/models/RetentionAggregation.ts index 9da401597ccbd2748349db6e4e636f92fc0812be..139f3e4dfdc68c3af6cfe54916fa2b9da20b5b1b 100644 --- a/packages/backend/src/models/RetentionAggregation.ts +++ b/packages/backend/src/models/RetentionAggregation.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/ReversiGame.ts b/packages/backend/src/models/ReversiGame.ts new file mode 100644 index 0000000000000000000000000000000000000000..c03335dd63056831d11613d18cdea898771bb622 --- /dev/null +++ b/packages/backend/src/models/ReversiGame.ts @@ -0,0 +1,143 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; + +@Entity('reversi_game') +export class MiReversiGame { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + nullable: true, + comment: 'The started date of the ReversiGame.', + }) + public startedAt: Date | null; + + @Column('timestamp with time zone', { + nullable: true, + comment: 'The ended date of the ReversiGame.', + }) + public endedAt: Date | null; + + @Column(id()) + public user1Id: MiUser['id']; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user1: MiUser | null; + + @Column(id()) + public user2Id: MiUser['id']; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user2: MiUser | null; + + @Column('boolean', { + default: false, + }) + public user1Ready: boolean; + + @Column('boolean', { + default: false, + }) + public user2Ready: boolean; + + /** + * ã©ã¡ã‚‰ã®ãƒ—レイヤーãŒå…ˆè¡Œ(é»’)ã‹ + * 1 ... user1 + * 2 ... user2 + */ + @Column('integer', { + nullable: true, + }) + public black: number | null; + + @Column('boolean', { + default: false, + }) + public isStarted: boolean; + + @Column('boolean', { + default: false, + }) + public isEnded: boolean; + + @Column({ + ...id(), + nullable: true, + }) + public winnerId: MiUser['id'] | null; + + @Column({ + ...id(), + nullable: true, + }) + public surrenderedUserId: MiUser['id'] | null; + + @Column({ + ...id(), + nullable: true, + }) + public timeoutUserId: MiUser['id'] | null; + + // in sec + @Column('smallint', { + default: 90, + }) + public timeLimitForEachTurn: number; + + @Column('jsonb', { + default: [], + }) + public logs: number[][]; + + @Column('varchar', { + array: true, length: 64, + }) + public map: string[]; + + @Column('varchar', { + length: 32, + }) + public bw: string; + + @Column('boolean', { + default: false, + }) + public noIrregularRules: boolean; + + @Column('boolean', { + default: false, + }) + public isLlotheo: boolean; + + @Column('boolean', { + default: false, + }) + public canPutEverywhere: boolean; + + @Column('boolean', { + default: false, + }) + public loopedBoard: boolean; + + @Column('jsonb', { + nullable: true, default: null, + }) + public form1: any | null; + + @Column('jsonb', { + nullable: true, default: null, + }) + public form2: any | null; + + @Column('varchar', { + length: 32, nullable: true, + }) + public crc32: string | null; +} diff --git a/packages/backend/src/models/Role.ts b/packages/backend/src/models/Role.ts index 6976956e13491e55b35ab80749c085a4aeabfbe9..058abe311863eb4511dc4eb42ba77d6e344b91f1 100644 --- a/packages/backend/src/models/Role.ts +++ b/packages/backend/src/models/Role.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -29,6 +29,11 @@ type CondFormulaValueIsRemote = { type: 'isRemote'; }; +type CondFormulaValueRoleAssignedTo = { + type: 'roleAssignedTo'; + roleId: string; +}; + type CondFormulaValueCreatedLessThan = { type: 'createdLessThan'; sec: number; @@ -69,12 +74,13 @@ type CondFormulaValueNotesMoreThanOrEq = { value: number; }; -export type RoleCondFormulaValue = +export type RoleCondFormulaValue = { id: string } & ( CondFormulaValueAnd | CondFormulaValueOr | CondFormulaValueNot | CondFormulaValueIsLocal | CondFormulaValueIsRemote | + CondFormulaValueRoleAssignedTo | CondFormulaValueCreatedLessThan | CondFormulaValueCreatedMoreThan | CondFormulaValueFollowersLessThanOrEq | @@ -82,7 +88,8 @@ export type RoleCondFormulaValue = CondFormulaValueFollowingLessThanOrEq | CondFormulaValueFollowingMoreThanOrEq | CondFormulaValueNotesLessThanOrEq | - CondFormulaValueNotesMoreThanOrEq; + CondFormulaValueNotesMoreThanOrEq +); @Entity('role') export class MiRole { diff --git a/packages/backend/src/models/RoleAssignment.ts b/packages/backend/src/models/RoleAssignment.ts index 30c7e19f2af08b0ef992c6b84ba2458560d9e8e8..37755d631bab111e162fb8c06cdd04fd6bd18523 100644 --- a/packages/backend/src/models/RoleAssignment.ts +++ b/packages/backend/src/models/RoleAssignment.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Signin.ts b/packages/backend/src/models/Signin.ts index 656b44dfe03fa0f856a3efaf065208b13023e59f..f8ff9c57d7807a26b14e9d185ead239e9a50fb6d 100644 --- a/packages/backend/src/models/Signin.ts +++ b/packages/backend/src/models/Signin.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/SwSubscription.ts b/packages/backend/src/models/SwSubscription.ts index f685a8ff3e1bf42f3e9fdb0be104107f99b9d319..0c531132b39932c8c02cce795a829dec152bf6c3 100644 --- a/packages/backend/src/models/SwSubscription.ts +++ b/packages/backend/src/models/SwSubscription.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/UsedUsername.ts b/packages/backend/src/models/UsedUsername.ts index c75bf424c10899c00897228e3e95cb482d7b9b3b..fbfc126763504f855584b37e1190ff9c61a7b224 100644 --- a/packages/backend/src/models/UsedUsername.ts +++ b/packages/backend/src/models/UsedUsername.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts index 3db8b398fdb324d38cad971a3ece40fa9a1ab9fe..b0910133c9bbe6e1c35904dc67c9de638bc31eba 100644 --- a/packages/backend/src/models/User.ts +++ b/packages/backend/src/models/User.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/UserIp.ts b/packages/backend/src/models/UserIp.ts index 60a7bc8b0193f549ded51fde7aaf184cf1a70d08..3e757fcf7988f1602842323b7f04de530de2502b 100644 --- a/packages/backend/src/models/UserIp.ts +++ b/packages/backend/src/models/UserIp.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/UserKeypair.ts b/packages/backend/src/models/UserKeypair.ts index a316dbaeb4b97645bf1a9959421046aa4822f4d5..f5252d126c5ed5026d3fbd222397b3dbaac52f52 100644 --- a/packages/backend/src/models/UserKeypair.ts +++ b/packages/backend/src/models/UserKeypair.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/UserList.ts b/packages/backend/src/models/UserList.ts index 7ad15419d7a717e46cb2ab99419717ec19272f9a..5fb991a87de77fb33cdc7e3ab390811e59330c6b 100644 --- a/packages/backend/src/models/UserList.ts +++ b/packages/backend/src/models/UserList.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/UserListFavorite.ts b/packages/backend/src/models/UserListFavorite.ts index a18ed9253a9181027a753d33fefd192c1fca0512..80b2d61eb7914477892ee61a105be84c392ee11c 100644 --- a/packages/backend/src/models/UserListFavorite.ts +++ b/packages/backend/src/models/UserListFavorite.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/UserListMembership.ts b/packages/backend/src/models/UserListMembership.ts index fa8287f17aeefbad58a36f3d379b65827463ffe0..af659d071d783fcf5f41ef098353d6728b6f55b6 100644 --- a/packages/backend/src/models/UserListMembership.ts +++ b/packages/backend/src/models/UserListMembership.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/UserMemo.ts b/packages/backend/src/models/UserMemo.ts index ab5e812c4498a1b623bf74d1667cc32501fdd0ab..29e28d290aefab58b211a0595a97a9f3b496c488 100644 --- a/packages/backend/src/models/UserMemo.ts +++ b/packages/backend/src/models/UserMemo.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/UserNotePining.ts b/packages/backend/src/models/UserNotePining.ts index ae5977aa56d5a8e8662f87d414834122bf6b4805..92c5cd55d0d89aa778c0367911357294a6f680fb 100644 --- a/packages/backend/src/models/UserNotePining.ts +++ b/packages/backend/src/models/UserNotePining.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/UserPending.ts b/packages/backend/src/models/UserPending.ts index 6b26bd228c55dc5edcf5ad6ee59f6d7c7784deec..961ae344f1980c893e6ad89eb5464d3699b028f8 100644 --- a/packages/backend/src/models/UserPending.ts +++ b/packages/backend/src/models/UserPending.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts index ae46fbc83c977bfc394db54b9752ff1bdc0eff71..40ea26f6107d90021e186ac95e8746039dfe86a1 100644 --- a/packages/backend/src/models/UserProfile.ts +++ b/packages/backend/src/models/UserProfile.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -256,6 +256,8 @@ export class MiUserProfile { type: 'follower'; } | { type: 'mutualFollow'; + } | { + type: 'followingOrFollower'; } | { type: 'list'; userListId: MiUserList['id']; diff --git a/packages/backend/src/models/UserPublickey.ts b/packages/backend/src/models/UserPublickey.ts index 33de73c636c2b22938680e88995a57eb2103cbd3..6bcd78530477e8046c1cc12b4c5ab8c82e1494ec 100644 --- a/packages/backend/src/models/UserPublickey.ts +++ b/packages/backend/src/models/UserPublickey.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/UserSecurityKey.ts b/packages/backend/src/models/UserSecurityKey.ts index 02c29bfbb59f44f4a217f3e95cb0e071a0e80e75..0babbe1abec0144af7c31bc497bc8643b92cd306 100644 --- a/packages/backend/src/models/UserSecurityKey.ts +++ b/packages/backend/src/models/UserSecurityKey.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/Webhook.ts b/packages/backend/src/models/Webhook.ts index ec4e13cc76e2b8b23559913ac7d24044b901f1ca..2a727f86fd93e1a764382da41332177de6f2d895 100644 --- a/packages/backend/src/models/Webhook.ts +++ b/packages/backend/src/models/Webhook.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -7,7 +7,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typ import { id } from './util/id.js'; import { MiUser } from './User.js'; -export const webhookEventTypes = ['mention', 'unfollow', 'follow', 'followed', 'note', 'reply', 'renote', 'reaction'] as const; +export const webhookEventTypes = ['mention', 'unfollow', 'follow', 'followed', 'note', 'reply', 'renote', 'reaction', 'edited'] as const; @Entity('webhook') export class MiWebhook { diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts index 2a7810235e1204dc8ac4cd68793094edb7d7a110..744a1dd4e7c6a006765da937bdde4e49ab9cd804 100644 --- a/packages/backend/src/models/_.ts +++ b/packages/backend/src/models/_.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -69,6 +69,9 @@ import { MiFlash } from '@/models/Flash.js'; import { MiFlashLike } from '@/models/FlashLike.js'; import { MiUserListFavorite } from '@/models/UserListFavorite.js'; import { NoteEdit } from '@/models/NoteEdit.js'; +import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js'; +import { MiReversiGame } from '@/models/ReversiGame.js'; + import type { Repository } from 'typeorm'; export { @@ -138,6 +141,8 @@ export { MiFlashLike, MiUserMemo, NoteEdit, + MiBubbleGameRecord, + MiReversiGame, }; export type AbuseUserReportsRepository = Repository<MiAbuseUserReport>; @@ -206,3 +211,5 @@ export type FlashsRepository = Repository<MiFlash>; export type FlashLikesRepository = Repository<MiFlashLike>; export type UserMemoRepository = Repository<MiUserMemo>; export type NoteEditRepository = Repository<NoteEdit>; +export type BubbleGameRecordsRepository = Repository<MiBubbleGameRecord>; +export type ReversiGamesRepository = Repository<MiReversiGame>; diff --git a/packages/backend/src/models/json-schema/ad.ts b/packages/backend/src/models/json-schema/ad.ts index 649ffcd4dcf04d3067bda4495b3c0c7973804ecb..b01b39a38b22ba1adebd10979e3e695b980a0fe0 100644 --- a/packages/backend/src/models/json-schema/ad.ts +++ b/packages/backend/src/models/json-schema/ad.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/json-schema/announcement.ts b/packages/backend/src/models/json-schema/announcement.ts index 78a98872b251bf69fbf86f6269534f9f02673d30..b9352bd31e61744adde56515932ccc0d80aa1d3b 100644 --- a/packages/backend/src/models/json-schema/announcement.ts +++ b/packages/backend/src/models/json-schema/announcement.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -37,10 +37,12 @@ export const packedAnnouncementSchema = { icon: { type: 'string', optional: false, nullable: false, + enum: ['info', 'warning', 'error', 'success'], }, display: { type: 'string', optional: false, nullable: false, + enum: ['dialog', 'normal', 'banner'], }, needConfirmationToRead: { type: 'boolean', diff --git a/packages/backend/src/models/json-schema/antenna.ts b/packages/backend/src/models/json-schema/antenna.ts index 4a9f0ed35546f8564820a30614a8cadb17555105..74622b619398012317a41ea02c9a465c255ca8f5 100644 --- a/packages/backend/src/models/json-schema/antenna.ts +++ b/packages/backend/src/models/json-schema/antenna.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/json-schema/app.ts b/packages/backend/src/models/json-schema/app.ts index 9e0916299cb0cca7037875bf45793d148035f8a2..6148232224ddffebd9a5641769d1daf399681892 100644 --- a/packages/backend/src/models/json-schema/app.ts +++ b/packages/backend/src/models/json-schema/app.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/json-schema/blocking.ts b/packages/backend/src/models/json-schema/blocking.ts index 0b58f1f8d75ab6a4e594e349b02950a979ae981c..2d02ba6a70703434185c12a5d38b996bdcc2addc 100644 --- a/packages/backend/src/models/json-schema/blocking.ts +++ b/packages/backend/src/models/json-schema/blocking.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -25,7 +25,7 @@ export const packedBlockingSchema = { blockee: { type: 'object', optional: false, nullable: false, - ref: 'UserDetailed', + ref: 'UserDetailedNotMe', }, }, } as const; diff --git a/packages/backend/src/models/json-schema/channel.ts b/packages/backend/src/models/json-schema/channel.ts index 5b0fa0f15dd70787533c37cd5ddf90235bcae34e..d233f7858d9a326ceaf7c11ea15b81c9812ce98f 100644 --- a/packages/backend/src/models/json-schema/channel.ts +++ b/packages/backend/src/models/json-schema/channel.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/json-schema/clip.ts b/packages/backend/src/models/json-schema/clip.ts index 1ab96c2b3bfbe5682562c95d2b07918f766c9e92..ca4886c97837cf2dcc5ac57e007d905d90df5f04 100644 --- a/packages/backend/src/models/json-schema/clip.ts +++ b/packages/backend/src/models/json-schema/clip.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/json-schema/drive-file.ts b/packages/backend/src/models/json-schema/drive-file.ts index 79f242a7111e7fed13230ed72e5394dc340bfe2f..ca88cc0e397520521c240ead8d54826de57e5198 100644 --- a/packages/backend/src/models/json-schema/drive-file.ts +++ b/packages/backend/src/models/json-schema/drive-file.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/json-schema/drive-folder.ts b/packages/backend/src/models/json-schema/drive-folder.ts index aaad301303b1a01505c2aee51ce306572971bb1c..12012a7e128e6ca51545df9ee2cde0f8a34f87d0 100644 --- a/packages/backend/src/models/json-schema/drive-folder.ts +++ b/packages/backend/src/models/json-schema/drive-folder.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/json-schema/emoji.ts b/packages/backend/src/models/json-schema/emoji.ts index 99a58f8773e320f76656aeee7071bb0e0e8d9679..62686ad5ae62eb9aeb9a90550cb0dd7ca185a570 100644 --- a/packages/backend/src/models/json-schema/emoji.ts +++ b/packages/backend/src/models/json-schema/emoji.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -27,6 +27,10 @@ export const packedEmojiSimpleSchema = { type: 'string', optional: false, nullable: false, }, + localOnly: { + type: 'boolean', + optional: true, nullable: false, + }, isSensitive: { type: 'boolean', optional: true, nullable: false, diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts index 94873716bfa1e25b49e0c13c0e67fb9319b02f63..7b8ab2283144a287fa1898f94c779fba7cb04c54 100644 --- a/packages/backend/src/models/json-schema/federation-instance.ts +++ b/packages/backend/src/models/json-schema/federation-instance.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -112,5 +112,9 @@ export const packedFederationInstanceSchema = { optional: false, nullable: false, }, + moderationNote: { + type: 'string', + optional: true, nullable: true, + }, }, } as const; diff --git a/packages/backend/src/models/json-schema/flash.ts b/packages/backend/src/models/json-schema/flash.ts index f08fa7a2795e4346b532c51f95d186bcc326a1d5..952df649adad8feae9883f6151cc823df31fd264 100644 --- a/packages/backend/src/models/json-schema/flash.ts +++ b/packages/backend/src/models/json-schema/flash.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/json-schema/following.ts b/packages/backend/src/models/json-schema/following.ts index e92cff20a10c4dadbf75b172695e12c61edb20fb..c5295a5128ac03366e7614d356967eda1f1f41a3 100644 --- a/packages/backend/src/models/json-schema/following.ts +++ b/packages/backend/src/models/json-schema/following.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -30,12 +30,12 @@ export const packedFollowingSchema = { followee: { type: 'object', optional: true, nullable: false, - ref: 'UserDetailed', + ref: 'UserDetailedNotMe', }, follower: { type: 'object', optional: true, nullable: false, - ref: 'UserDetailed', + ref: 'UserDetailedNotMe', }, }, } as const; diff --git a/packages/backend/src/models/json-schema/gallery-post.ts b/packages/backend/src/models/json-schema/gallery-post.ts index df7038950c24af5920652a34cf7dd56e03bdb3bd..a46d5115c2c39c99a070375d29f699745dbc6c0d 100644 --- a/packages/backend/src/models/json-schema/gallery-post.ts +++ b/packages/backend/src/models/json-schema/gallery-post.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/json-schema/hashtag.ts b/packages/backend/src/models/json-schema/hashtag.ts index a48e972a5d8af1b904bc5b1d7bca7ad1c37eeefa..4fd136afed8496fa50de1cab14991365c19380f1 100644 --- a/packages/backend/src/models/json-schema/hashtag.ts +++ b/packages/backend/src/models/json-schema/hashtag.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/json-schema/invite-code.ts b/packages/backend/src/models/json-schema/invite-code.ts index cd8bf98d90e3a5178d289762bc90b83d2adbf2d0..08d1b8fd0c5d314d095663095f19b8589cc2971a 100644 --- a/packages/backend/src/models/json-schema/invite-code.ts +++ b/packages/backend/src/models/json-schema/invite-code.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/json-schema/meta.ts b/packages/backend/src/models/json-schema/meta.ts new file mode 100644 index 0000000000000000000000000000000000000000..9db3f7f809e760894be968285ae2651671f55dad --- /dev/null +++ b/packages/backend/src/models/json-schema/meta.ts @@ -0,0 +1,344 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export const packedMetaLiteSchema = { + type: 'object', + optional: false, nullable: false, + properties: { + maintainerName: { + type: 'string', + optional: false, nullable: true, + }, + maintainerEmail: { + type: 'string', + optional: false, nullable: true, + }, + version: { + type: 'string', + optional: false, nullable: false, + }, + providesTarball: { + type: 'boolean', + optional: false, nullable: false, + }, + name: { + type: 'string', + optional: false, nullable: true, + }, + shortName: { + type: 'string', + optional: false, nullable: true, + }, + uri: { + type: 'string', + optional: false, nullable: false, + format: 'url', + example: 'https://misskey.example.com', + }, + description: { + type: 'string', + optional: false, nullable: true, + }, + langs: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + }, + }, + tosUrl: { + type: 'string', + optional: false, nullable: true, + }, + repositoryUrl: { + type: 'string', + optional: false, nullable: true, + default: 'https://github.com/misskey-dev/misskey', + }, + feedbackUrl: { + type: 'string', + optional: false, nullable: true, + default: 'https://github.com/misskey-dev/misskey/issues/new', + }, + donationUrl: { + type: 'string', + optional: false, nullable: true, + }, + defaultDarkTheme: { + type: 'string', + optional: false, nullable: true, + }, + defaultLightTheme: { + type: 'string', + optional: false, nullable: true, + }, + defaultLike: { + type: 'string', + optional: false, nullable: true, + }, + disableRegistration: { + type: 'boolean', + optional: false, nullable: false, + }, + emailRequiredForSignup: { + type: 'boolean', + optional: false, nullable: false, + }, + approvalRequiredForSignup: { + type: 'boolean', + optional: false, nullable: false, + }, + enableHcaptcha: { + type: 'boolean', + optional: false, nullable: false, + }, + hcaptchaSiteKey: { + type: 'string', + optional: false, nullable: true, + }, + enableMcaptcha: { + type: 'boolean', + optional: false, nullable: false, + }, + mcaptchaSiteKey: { + type: 'string', + optional: false, nullable: true, + }, + mcaptchaInstanceUrl: { + type: 'string', + optional: false, nullable: true, + }, + enableRecaptcha: { + type: 'boolean', + optional: false, nullable: false, + }, + recaptchaSiteKey: { + type: 'string', + optional: false, nullable: true, + }, + enableTurnstile: { + type: 'boolean', + optional: false, nullable: false, + }, + turnstileSiteKey: { + type: 'string', + optional: false, nullable: true, + }, + enableAchievements: { + type: 'boolean', + optional: false, nullable: true, + }, + swPublickey: { + type: 'string', + optional: false, nullable: true, + }, + mascotImageUrl: { + type: 'string', + optional: false, nullable: false, + default: '/assets/ai.png', + }, + bannerUrl: { + type: 'string', + optional: false, nullable: true, + }, + serverErrorImageUrl: { + type: 'string', + optional: false, nullable: true, + }, + infoImageUrl: { + type: 'string', + optional: false, nullable: true, + }, + notFoundImageUrl: { + type: 'string', + optional: false, nullable: true, + }, + iconUrl: { + type: 'string', + optional: false, nullable: true, + }, + maxNoteTextLength: { + type: 'number', + optional: false, nullable: false, + }, + ads: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + example: 'xxxxxxxxxx', + }, + url: { + type: 'string', + optional: false, nullable: false, + format: 'url', + }, + place: { + type: 'string', + optional: false, nullable: false, + }, + ratio: { + type: 'number', + optional: false, nullable: false, + }, + imageUrl: { + type: 'string', + optional: false, nullable: false, + format: 'url', + }, + dayOfWeek: { + type: 'integer', + optional: false, nullable: false, + }, + }, + }, + }, + notesPerOneAd: { + type: 'number', + optional: false, nullable: false, + default: 0, + }, + enableEmail: { + type: 'boolean', + optional: false, nullable: false, + }, + enableServiceWorker: { + type: 'boolean', + optional: false, nullable: false, + }, + translatorAvailable: { + type: 'boolean', + optional: false, nullable: false, + }, + mediaProxy: { + type: 'string', + optional: false, nullable: false, + }, + backgroundImageUrl: { + type: 'string', + optional: false, nullable: true, + }, + impressumUrl: { + type: 'string', + optional: false, nullable: true, + }, + logoImageUrl: { + type: 'string', + optional: false, nullable: true, + }, + privacyPolicyUrl: { + type: 'string', + optional: false, nullable: true, + }, + serverRules: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + }, + }, + themeColor: { + type: 'string', + optional: false, nullable: true, + }, + policies: { + type: 'object', + optional: false, nullable: false, + ref: 'RolePolicies', + }, + }, +} as const; + +export const packedMetaDetailedOnlySchema = { + type: 'object', + optional: false, nullable: false, + properties: { + features: { + type: 'object', + optional: true, nullable: false, + properties: { + registration: { + type: 'boolean', + optional: false, nullable: false, + }, + emailRequiredForSignup: { + type: 'boolean', + optional: false, nullable: false, + }, + localTimeline: { + type: 'boolean', + optional: false, nullable: false, + }, + globalTimeline: { + type: 'boolean', + optional: false, nullable: false, + }, + hcaptcha: { + type: 'boolean', + optional: false, nullable: false, + }, + turnstile: { + type: 'boolean', + optional: false, nullable: false, + }, + recaptcha: { + type: 'boolean', + optional: false, nullable: false, + }, + objectStorage: { + type: 'boolean', + optional: false, nullable: false, + }, + serviceWorker: { + type: 'boolean', + optional: false, nullable: false, + }, + miauth: { + type: 'boolean', + optional: true, nullable: false, + default: true, + }, + }, + }, + proxyAccountName: { + type: 'string', + optional: false, nullable: true, + }, + requireSetup: { + type: 'boolean', + optional: false, nullable: false, + example: false, + }, + cacheRemoteFiles: { + type: 'boolean', + optional: false, nullable: false, + }, + cacheRemoteSensitiveFiles: { + type: 'boolean', + optional: false, nullable: false, + }, + }, +} as const; + +export const packedMetaDetailedSchema = { + type: 'object', + allOf: [ + { + type: 'object', + ref: 'MetaLite', + }, + { + type: 'object', + ref: 'MetaDetailedOnly', + }, + ], +} as const; diff --git a/packages/backend/src/models/json-schema/muting.ts b/packages/backend/src/models/json-schema/muting.ts index dde9dc02882bc255e3009008bc0d732720635c00..b5fab013ef15635fab8a33b299543e684ccddb2e 100644 --- a/packages/backend/src/models/json-schema/muting.ts +++ b/packages/backend/src/models/json-schema/muting.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -30,7 +30,7 @@ export const packedMutingSchema = { mutee: { type: 'object', optional: false, nullable: false, - ref: 'UserDetailed', + ref: 'UserDetailedNotMe', }, }, } as const; diff --git a/packages/backend/src/models/json-schema/note-favorite.ts b/packages/backend/src/models/json-schema/note-favorite.ts index 3f0007d917e853ac132c6c1f20148d1ee347c2c0..d2a3745f4b76c7c4cf6a7f6088ceb8a44318dc2f 100644 --- a/packages/backend/src/models/json-schema/note-favorite.ts +++ b/packages/backend/src/models/json-schema/note-favorite.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/json-schema/note-reaction.ts b/packages/backend/src/models/json-schema/note-reaction.ts index e3335f426ec628f8aface58a57b37464355d24fd..95658ace1fca027b9ccf1becc04859acec557413 100644 --- a/packages/backend/src/models/json-schema/note-reaction.ts +++ b/packages/backend/src/models/json-schema/note-reaction.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts index aa749943f0999a97481710282cd5c7007498ca1a..bb4ccc7ee4db1382eb21eda64965e7b39b16c7a7 100644 --- a/packages/backend/src/models/json-schema/note.ts +++ b/packages/backend/src/models/json-schema/note.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -69,6 +69,7 @@ export const packedNoteSchema = { visibility: { type: 'string', optional: false, nullable: false, + enum: ['public', 'home', 'followers', 'specified'], }, mentions: { type: 'array', @@ -117,6 +118,48 @@ export const packedNoteSchema = { poll: { type: 'object', optional: true, nullable: true, + properties: { + expiresAt: { + type: 'string', + optional: true, nullable: true, + format: 'date-time', + }, + multiple: { + type: 'boolean', + optional: false, nullable: false, + }, + choices: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + properties: { + isVoted: { + type: 'boolean', + optional: false, nullable: false, + }, + text: { + type: 'string', + optional: false, nullable: false, + }, + votes: { + type: 'number', + optional: false, nullable: false, + }, + }, + }, + }, + }, + }, + emojis: { + type: 'object', + optional: true, nullable: false, + additionalProperties: { + anyOf: [{ + type: 'string', + }], + }, }, channelId: { type: 'string', @@ -148,6 +191,10 @@ export const packedNoteSchema = { type: 'boolean', optional: false, nullable: false, }, + userId: { + type: 'string', + optional: false, nullable: true, + }, }, }, localOnly: { @@ -158,9 +205,23 @@ export const packedNoteSchema = { type: 'string', optional: false, nullable: true, }, + reactionEmojis: { + type: 'object', + optional: false, nullable: false, + additionalProperties: { + anyOf: [{ + type: 'string', + }], + }, + }, reactions: { type: 'object', optional: false, nullable: false, + additionalProperties: { + anyOf: [{ + type: 'number', + }], + }, }, renoteCount: { type: 'number', @@ -192,7 +253,7 @@ export const packedNoteSchema = { }, myReaction: { - type: 'object', + type: 'string', optional: true, nullable: true, }, }, diff --git a/packages/backend/src/models/json-schema/notification.ts b/packages/backend/src/models/json-schema/notification.ts index c6d6e8431731df8dd79f21bcc928a58187689242..3f31cc47ee13120ad734ab1108e9b73f1b59bd28 100644 --- a/packages/backend/src/models/json-schema/notification.ts +++ b/packages/backend/src/models/json-schema/notification.ts @@ -1,11 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { notificationTypes } from '@/types.js'; -export const packedNotificationSchema = { +const baseSchema = { type: 'object', properties: { id: { @@ -23,68 +23,393 @@ export const packedNotificationSchema = { optional: false, nullable: false, enum: [...notificationTypes, 'reaction:grouped', 'renote:grouped'], }, - user: { - type: 'object', - ref: 'UserLite', - optional: true, nullable: true, + }, +} as const; + +export const packedNotificationSchema = { + type: 'object', + oneOf: [{ + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['note'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, }, - userId: { - type: 'string', - optional: true, nullable: true, - format: 'id', + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['mention'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, }, - note: { - type: 'object', - ref: 'Note', - optional: true, nullable: true, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['reply'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, }, - reaction: { - type: 'string', - optional: true, nullable: true, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['renote'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, }, - achievement: { - type: 'string', - optional: true, nullable: false, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['quote'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, }, - body: { - type: 'string', - optional: true, nullable: true, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['reaction'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + reaction: { + type: 'string', + optional: false, nullable: false, + }, }, - header: { - type: 'string', - optional: true, nullable: true, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['pollEnded'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, }, - icon: { - type: 'string', - optional: true, nullable: true, - }, - reactions: { - type: 'array', - optional: true, nullable: true, - items: { - type: 'object', - properties: { - user: { - type: 'object', - ref: 'UserLite', - optional: false, nullable: false, - }, - reaction: { - type: 'string', - optional: false, nullable: false, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['follow'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['receiveFollowRequest'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['followRequestAccepted'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['roleAssigned'], + }, + role: { + type: 'object', + ref: 'Role', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['achievementEarned'], + }, + achievement: { + type: 'string', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['app'], + }, + body: { + type: 'string', + optional: false, nullable: false, + }, + header: { + type: 'string', + optional: false, nullable: false, + }, + icon: { + type: 'string', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['edited'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['reaction:grouped'], + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + reactions: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + properties: { + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + reaction: { + type: 'string', + optional: false, nullable: false, + }, }, + required: ['user', 'reaction'], }, - required: ['user', 'reaction'], }, }, - users: { - type: 'array', - optional: true, nullable: true, - items: { + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['renote:grouped'], + }, + note: { type: 'object', - ref: 'UserLite', + ref: 'Note', + optional: false, nullable: false, + }, + users: { + type: 'array', optional: false, nullable: false, + items: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, }, }, - }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['test'], + }, + }, + }], } as const; diff --git a/packages/backend/src/models/json-schema/page.ts b/packages/backend/src/models/json-schema/page.ts index 9baacd6884d4b42e5e99f0234a5fff5e97354ca6..748d6f1245a344c9aca10f96becaafce9881d080 100644 --- a/packages/backend/src/models/json-schema/page.ts +++ b/packages/backend/src/models/json-schema/page.ts @@ -1,8 +1,110 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ +const blockBaseSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + }, + type: { + type: 'string', + optional: false, nullable: false, + }, + }, +} as const; + +const textBlockSchema = { + type: 'object', + properties: { + ...blockBaseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['text'], + }, + text: { + type: 'string', + optional: false, nullable: false, + }, + }, +} as const; + +const sectionBlockSchema = { + type: 'object', + properties: { + ...blockBaseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['section'], + }, + title: { + type: 'string', + optional: false, nullable: false, + }, + children: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'PageBlock', + selfRef: true, + }, + }, + }, +} as const; + +const imageBlockSchema = { + type: 'object', + properties: { + ...blockBaseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['image'], + }, + fileId: { + type: 'string', + optional: false, nullable: true, + }, + }, +} as const; + +const noteBlockSchema = { + type: 'object', + properties: { + ...blockBaseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['note'], + }, + detailed: { + type: 'boolean', + optional: false, nullable: false, + }, + note: { + type: 'string', + optional: false, nullable: true, + }, + }, +} as const; + +export const packedPageBlockSchema = { + type: 'object', + oneOf: [ + textBlockSchema, + sectionBlockSchema, + imageBlockSchema, + noteBlockSchema, + ], +} as const; + export const packedPageSchema = { type: 'object', properties: { @@ -38,6 +140,7 @@ export const packedPageSchema = { items: { type: 'object', optional: false, nullable: false, + ref: 'PageBlock', }, }, variables: { diff --git a/packages/backend/src/models/json-schema/queue.ts b/packages/backend/src/models/json-schema/queue.ts index 43da6e605d4d5ee41123aa4f311dbbbe154dd410..2ecf5c831f8981c1cc664fb3855149cac96560d6 100644 --- a/packages/backend/src/models/json-schema/queue.ts +++ b/packages/backend/src/models/json-schema/queue.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/json-schema/renote-muting.ts b/packages/backend/src/models/json-schema/renote-muting.ts index feed1ceb09b068fd90254aeeb37af1950ecf7839..344d6c7c005b31f87274e3c25b6cb34538d722aa 100644 --- a/packages/backend/src/models/json-schema/renote-muting.ts +++ b/packages/backend/src/models/json-schema/renote-muting.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -25,7 +25,7 @@ export const packedRenoteMutingSchema = { mutee: { type: 'object', optional: false, nullable: false, - ref: 'UserDetailed', + ref: 'UserDetailedNotMe', }, }, } as const; diff --git a/packages/backend/src/models/json-schema/reversi-game.ts b/packages/backend/src/models/json-schema/reversi-game.ts new file mode 100644 index 0000000000000000000000000000000000000000..cb37200384ddf7f78855064d5864af97c8bcd233 --- /dev/null +++ b/packages/backend/src/models/json-schema/reversi-game.ts @@ -0,0 +1,243 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export const packedReversiGameLiteSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + createdAt: { + type: 'string', + optional: false, nullable: false, + format: 'date-time', + }, + startedAt: { + type: 'string', + optional: false, nullable: true, + format: 'date-time', + }, + endedAt: { + type: 'string', + optional: false, nullable: true, + format: 'date-time', + }, + isStarted: { + type: 'boolean', + optional: false, nullable: false, + }, + isEnded: { + type: 'boolean', + optional: false, nullable: false, + }, + user1Id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + user2Id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + user1: { + type: 'object', + optional: false, nullable: false, + ref: 'UserLite', + }, + user2: { + type: 'object', + optional: false, nullable: false, + ref: 'UserLite', + }, + winnerId: { + type: 'string', + optional: false, nullable: true, + format: 'id', + }, + winner: { + type: 'object', + optional: false, nullable: true, + ref: 'UserLite', + }, + surrenderedUserId: { + type: 'string', + optional: false, nullable: true, + format: 'id', + }, + timeoutUserId: { + type: 'string', + optional: false, nullable: true, + format: 'id', + }, + black: { + type: 'number', + optional: false, nullable: true, + }, + bw: { + type: 'string', + optional: false, nullable: false, + }, + noIrregularRules: { + type: 'boolean', + optional: false, nullable: false, + }, + isLlotheo: { + type: 'boolean', + optional: false, nullable: false, + }, + canPutEverywhere: { + type: 'boolean', + optional: false, nullable: false, + }, + loopedBoard: { + type: 'boolean', + optional: false, nullable: false, + }, + timeLimitForEachTurn: { + type: 'number', + optional: false, nullable: false, + }, + }, +} as const; + +export const packedReversiGameDetailedSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + createdAt: { + type: 'string', + optional: false, nullable: false, + format: 'date-time', + }, + startedAt: { + type: 'string', + optional: false, nullable: true, + format: 'date-time', + }, + endedAt: { + type: 'string', + optional: false, nullable: true, + format: 'date-time', + }, + isStarted: { + type: 'boolean', + optional: false, nullable: false, + }, + isEnded: { + type: 'boolean', + optional: false, nullable: false, + }, + form1: { + type: 'object', + optional: false, nullable: true, + }, + form2: { + type: 'object', + optional: false, nullable: true, + }, + user1Ready: { + type: 'boolean', + optional: false, nullable: false, + }, + user2Ready: { + type: 'boolean', + optional: false, nullable: false, + }, + user1Id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + user2Id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + user1: { + type: 'object', + optional: false, nullable: false, + ref: 'UserLite', + }, + user2: { + type: 'object', + optional: false, nullable: false, + ref: 'UserLite', + }, + winnerId: { + type: 'string', + optional: false, nullable: true, + format: 'id', + }, + winner: { + type: 'object', + optional: false, nullable: true, + ref: 'UserLite', + }, + surrenderedUserId: { + type: 'string', + optional: false, nullable: true, + format: 'id', + }, + timeoutUserId: { + type: 'string', + optional: false, nullable: true, + format: 'id', + }, + black: { + type: 'number', + optional: false, nullable: true, + }, + bw: { + type: 'string', + optional: false, nullable: false, + }, + noIrregularRules: { + type: 'boolean', + optional: false, nullable: false, + }, + isLlotheo: { + type: 'boolean', + optional: false, nullable: false, + }, + canPutEverywhere: { + type: 'boolean', + optional: false, nullable: false, + }, + loopedBoard: { + type: 'boolean', + optional: false, nullable: false, + }, + timeLimitForEachTurn: { + type: 'number', + optional: false, nullable: false, + }, + logs: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'number', + }, + }, + }, + map: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + }, + }, + }, +} as const; diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts index b0c6804bb8f5021be0a3cad4c73b9581b2477728..7eba1d54430f5a8a1605e0ab7ac82c05aead1d0f 100644 --- a/packages/backend/src/models/json-schema/role.ts +++ b/packages/backend/src/models/json-schema/role.ts @@ -1,26 +1,260 @@ -const rolePolicyValue = { +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export const packedRoleCondFormulaLogicsSchema = { + type: 'object', + properties: { + id: { + type: 'string', optional: false, + }, + type: { + type: 'string', + nullable: false, optional: false, + enum: ['and', 'or'], + }, + values: { + type: 'array', + nullable: false, optional: false, + items: { + ref: 'RoleCondFormulaValue', + }, + }, + }, +} as const; + +export const packedRoleCondFormulaValueNot = { type: 'object', properties: { + id: { + type: 'string', optional: false, + }, + type: { + type: 'string', + nullable: false, optional: false, + enum: ['not'], + }, value: { - oneOf: [ - { - type: 'integer', - optional: false, nullable: false, - }, - { - type: 'boolean', - optional: false, nullable: false, - }, + type: 'object', + optional: false, + ref: 'RoleCondFormulaValue', + }, + }, +} as const; + +export const packedRoleCondFormulaValueIsLocalOrRemoteSchema = { + type: 'object', + properties: { + id: { + type: 'string', optional: false, + }, + type: { + type: 'string', + nullable: false, optional: false, + enum: ['isLocal', 'isRemote'], + }, + }, +} as const; + +export const packedRoleCondFormulaValueAssignedRoleSchema = { + type: 'object', + properties: { + id: { + type: 'string', optional: false, + }, + type: { + type: 'string', + nullable: false, optional: false, + enum: ['roleAssignedTo'], + }, + roleId: { + type: 'string', + nullable: false, optional: false, + format: 'id', + example: 'xxxxxxxxxx', + }, + }, +} as const; + +export const packedRoleCondFormulaValueCreatedSchema = { + type: 'object', + properties: { + id: { + type: 'string', optional: false, + }, + type: { + type: 'string', + nullable: false, optional: false, + enum: [ + 'createdLessThan', + 'createdMoreThan', + ], + }, + sec: { + type: 'number', + nullable: false, optional: false, + }, + }, +} as const; + +export const packedRoleCondFormulaFollowersOrFollowingOrNotesSchema = { + type: 'object', + properties: { + id: { + type: 'string', optional: false, + }, + type: { + type: 'string', + nullable: false, optional: false, + enum: [ + 'followersLessThanOrEq', + 'followersMoreThanOrEq', + 'followingLessThanOrEq', + 'followingMoreThanOrEq', + 'notesLessThanOrEq', + 'notesMoreThanOrEq', ], }, - priority: { + value: { + type: 'number', + nullable: false, optional: false, + }, + }, +} as const; + +export const packedRoleCondFormulaValueSchema = { + type: 'object', + oneOf: [ + { + ref: 'RoleCondFormulaLogics', + }, + { + ref: 'RoleCondFormulaValueNot', + }, + { + ref: 'RoleCondFormulaValueIsLocalOrRemote', + }, + { + ref: 'RoleCondFormulaValueAssignedRole', + }, + { + ref: 'RoleCondFormulaValueCreated', + }, + { + ref: 'RoleCondFormulaFollowersOrFollowingOrNotes', + }, + ], +} as const; + +export const packedRolePoliciesSchema = { + type: 'object', + optional: false, nullable: false, + properties: { + gtlAvailable: { + type: 'boolean', + optional: false, nullable: false, + }, + ltlAvailable: { + type: 'boolean', + optional: false, nullable: false, + }, + btlAvailable: { + type: 'boolean', + optional: false, nullable: false, + }, + canPublicNote: { + type: 'boolean', + optional: false, nullable: false, + }, + mentionLimit: { + type: 'integer', + optional: false, nullable: false, + }, + canInvite: { + type: 'boolean', + optional: false, nullable: false, + }, + inviteLimit: { + type: 'integer', + optional: false, nullable: false, + }, + inviteLimitCycle: { + type: 'integer', + optional: false, nullable: false, + }, + inviteExpirationTime: { + type: 'integer', + optional: false, nullable: false, + }, + canManageCustomEmojis: { + type: 'boolean', + optional: false, nullable: false, + }, + canManageAvatarDecorations: { + type: 'boolean', + optional: false, nullable: false, + }, + canSearchNotes: { + type: 'boolean', + optional: false, nullable: false, + }, + canUseTranslator: { + type: 'boolean', + optional: false, nullable: false, + }, + canHideAds: { + type: 'boolean', + optional: false, nullable: false, + }, + driveCapacityMb: { type: 'integer', optional: false, nullable: false, }, - useDefault: { + alwaysMarkNsfw: { type: 'boolean', optional: false, nullable: false, }, + pinLimit: { + type: 'integer', + optional: false, nullable: false, + }, + antennaLimit: { + type: 'integer', + optional: false, nullable: false, + }, + wordMuteLimit: { + type: 'integer', + optional: false, nullable: false, + }, + webhookLimit: { + type: 'integer', + optional: false, nullable: false, + }, + clipLimit: { + type: 'integer', + optional: false, nullable: false, + }, + noteEachClipsLimit: { + type: 'integer', + optional: false, nullable: false, + }, + userListLimit: { + type: 'integer', + optional: false, nullable: false, + }, + userEachUserListsLimit: { + type: 'integer', + optional: false, nullable: false, + }, + rateLimitFactor: { + type: 'integer', + optional: false, nullable: false, + }, + avatarDecorationLimit: { + type: 'integer', + optional: false, nullable: false, + }, }, } as const; @@ -97,6 +331,7 @@ export const packedRoleSchema = { condFormula: { type: 'object', optional: false, nullable: false, + ref: 'RoleCondFormulaValue', }, isPublic: { type: 'boolean', @@ -121,31 +356,28 @@ export const packedRoleSchema = { policies: { type: 'object', optional: false, nullable: false, - properties: { - pinLimit: rolePolicyValue, - canInvite: rolePolicyValue, - clipLimit: rolePolicyValue, - canHideAds: rolePolicyValue, - inviteLimit: rolePolicyValue, - antennaLimit: rolePolicyValue, - gtlAvailable: rolePolicyValue, - ltlAvailable: rolePolicyValue, - webhookLimit: rolePolicyValue, - canPublicNote: rolePolicyValue, - userListLimit: rolePolicyValue, - wordMuteLimit: rolePolicyValue, - alwaysMarkNsfw: rolePolicyValue, - canSearchNotes: rolePolicyValue, - driveCapacityMb: rolePolicyValue, - rateLimitFactor: rolePolicyValue, - inviteLimitCycle: rolePolicyValue, - noteEachClipsLimit: rolePolicyValue, - inviteExpirationTime: rolePolicyValue, - canManageCustomEmojis: rolePolicyValue, - userEachUserListsLimit: rolePolicyValue, - canManageAvatarDecorations: rolePolicyValue, - canUseTranslator: rolePolicyValue, - avatarDecorationLimit: rolePolicyValue, + additionalProperties: { + anyOf: [{ + type: 'object', + properties: { + value: { + oneOf: [ + { + type: 'integer', + }, + { + type: 'boolean', + }, + ], + }, + priority: { + type: 'integer', + }, + useDefault: { + type: 'boolean', + }, + }, + }], }, }, usersCount: { diff --git a/packages/backend/src/models/json-schema/user-list.ts b/packages/backend/src/models/json-schema/user-list.ts index e257d9984c34e48d0c34a16fc34f6d134535338d..dc9af25602a2488b2709adf05c42db7633d4f0c1 100644 --- a/packages/backend/src/models/json-schema/user-list.ts +++ b/packages/backend/src/models/json-schema/user-list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index af67e62afa5a500257c2990e967275c51540dabd..33a3efd453016a0488dbcfdd0643304b30932db4 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -1,18 +1,40 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -const notificationRecieveConfig = { +export const notificationRecieveConfig = { type: 'object', - nullable: false, optional: true, - properties: { - type: { - type: 'string', - nullable: false, optional: false, - enum: ['all', 'following', 'follower', 'mutualFollow', 'list', 'never'], + oneOf: [ + { + type: 'object', + nullable: false, + properties: { + type: { + type: 'string', + nullable: false, + enum: ['all', 'following', 'follower', 'mutualFollow', 'followingOrFollower', 'never'], + }, + }, + required: ['type'], }, - }, + { + type: 'object', + nullable: false, + properties: { + type: { + type: 'string', + nullable: false, + enum: ['list'], + }, + userListId: { + type: 'string', + format: 'misskey:id', + }, + }, + required: ['type', 'userListId'], + }, + ], } as const; export const packedUserLiteSchema = { @@ -148,6 +170,9 @@ export const packedUserLiteSchema = { emojis: { type: 'object', nullable: false, optional: false, + additionalProperties: { + type: 'string', + }, }, onlineStatus: { type: 'string', @@ -584,15 +609,20 @@ export const packedMeDetailedOnlySchema = { type: 'object', nullable: false, optional: false, properties: { - app: notificationRecieveConfig, - quote: notificationRecieveConfig, - reply: notificationRecieveConfig, - follow: notificationRecieveConfig, - renote: notificationRecieveConfig, - mention: notificationRecieveConfig, - reaction: notificationRecieveConfig, - pollEnded: notificationRecieveConfig, - receiveFollowRequest: notificationRecieveConfig, + note: { optional: true, ...notificationRecieveConfig }, + follow: { optional: true, ...notificationRecieveConfig }, + mention: { optional: true, ...notificationRecieveConfig }, + reply: { optional: true, ...notificationRecieveConfig }, + renote: { optional: true, ...notificationRecieveConfig }, + quote: { optional: true, ...notificationRecieveConfig }, + reaction: { optional: true, ...notificationRecieveConfig }, + pollEnded: { optional: true, ...notificationRecieveConfig }, + receiveFollowRequest: { optional: true, ...notificationRecieveConfig }, + followRequestAccepted: { optional: true, ...notificationRecieveConfig }, + roleAssigned: { optional: true, ...notificationRecieveConfig }, + achievementEarned: { optional: true, ...notificationRecieveConfig }, + app: { optional: true, ...notificationRecieveConfig }, + test: { optional: true, ...notificationRecieveConfig }, }, }, emailNotificationTypes: { @@ -628,104 +658,7 @@ export const packedMeDetailedOnlySchema = { policies: { type: 'object', nullable: false, optional: false, - properties: { - gtlAvailable: { - type: 'boolean', - nullable: false, optional: false, - }, - ltlAvailable: { - type: 'boolean', - nullable: false, optional: false, - }, - canPublicNote: { - type: 'boolean', - nullable: false, optional: false, - }, - canInvite: { - type: 'boolean', - nullable: false, optional: false, - }, - inviteLimit: { - type: 'number', - nullable: false, optional: false, - }, - inviteLimitCycle: { - type: 'number', - nullable: false, optional: false, - }, - inviteExpirationTime: { - type: 'number', - nullable: false, optional: false, - }, - canManageCustomEmojis: { - type: 'boolean', - nullable: false, optional: false, - }, - canManageAvatarDecorations: { - type: 'boolean', - nullable: false, optional: false, - }, - canSearchNotes: { - type: 'boolean', - nullable: false, optional: false, - }, - canUseTranslator: { - type: 'boolean', - nullable: false, optional: false, - }, - canHideAds: { - type: 'boolean', - nullable: false, optional: false, - }, - driveCapacityMb: { - type: 'number', - nullable: false, optional: false, - }, - alwaysMarkNsfw: { - type: 'boolean', - nullable: false, optional: false, - }, - pinLimit: { - type: 'number', - nullable: false, optional: false, - }, - antennaLimit: { - type: 'number', - nullable: false, optional: false, - }, - wordMuteLimit: { - type: 'number', - nullable: false, optional: false, - }, - webhookLimit: { - type: 'number', - nullable: false, optional: false, - }, - clipLimit: { - type: 'number', - nullable: false, optional: false, - }, - noteEachClipsLimit: { - type: 'number', - nullable: false, optional: false, - }, - userListLimit: { - type: 'number', - nullable: false, optional: false, - }, - userEachUserListsLimit: { - type: 'number', - nullable: false, optional: false, - }, - rateLimitFactor: { - type: 'number', - nullable: false, optional: false, - }, - avatarDecorationLimit: { - type: 'number', - nullable: false, optional: false, - }, - }, + ref: 'RolePolicies', }, //#region secrets email: { @@ -820,13 +753,5 @@ export const packedUserSchema = { type: 'object', ref: 'UserDetailed', }, - { - type: 'object', - ref: 'UserDetailedNotMe', - }, - { - type: 'object', - ref: 'MeDetailed', - }, ], } as const; diff --git a/packages/backend/src/models/util/id.ts b/packages/backend/src/models/util/id.ts index 81e83b8db92a04ccab006c62db65f6a3a07eceab..2d742702c7ac1bea99b6ecf5e7a44b34a3da8bdf 100644 --- a/packages/backend/src/models/util/id.ts +++ b/packages/backend/src/models/util/id.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts index 18773a1b6654a8818eef2e223c3fde77ae41209d..4a1b42383f19ef5ce29b2c4a16d507126483c167 100644 --- a/packages/backend/src/postgres.ts +++ b/packages/backend/src/postgres.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -77,6 +77,8 @@ import { MiFlash } from '@/models/Flash.js'; import { MiFlashLike } from '@/models/FlashLike.js'; import { MiUserMemo } from '@/models/UserMemo.js'; import { NoteEdit } from '@/models/NoteEdit.js'; +import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js'; +import { MiReversiGame } from '@/models/ReversiGame.js'; import { Config } from '@/config.js'; import MisskeyLogger from '@/logger.js'; @@ -192,6 +194,8 @@ export const entities = [ MiFlashLike, MiUserMemo, NoteEdit, + MiBubbleGameRecord, + MiReversiGame, ...charts, ]; @@ -209,22 +213,24 @@ export function createPostgresDataSource(config: Config) { statement_timeout: 1000 * 10, ...config.db.extra, }, - replication: config.dbReplications ? { - master: { - host: config.db.host, - port: config.db.port, - username: config.db.user, - password: config.db.pass, - database: config.db.db, + ...(config.dbReplications ? { + replication: { + master: { + host: config.db.host, + port: config.db.port, + username: config.db.user, + password: config.db.pass, + database: config.db.db, + }, + slaves: config.dbSlaves!.map(rep => ({ + host: rep.host, + port: rep.port, + username: rep.user, + password: rep.pass, + database: rep.db, + })), }, - slaves: config.dbSlaves!.map(rep => ({ - host: rep.host, - port: rep.port, - username: rep.user, - password: rep.pass, - database: rep.db, - })), - } : undefined, + } : {}), synchronize: process.env.NODE_ENV === 'test', dropSchema: process.env.NODE_ENV === 'test', cache: !config.db.disableCache && process.env.NODE_ENV !== 'test' ? { // dbã‚’closeã—ã¦ã‚‚何故ã‹redisã®ã‚³ãƒã‚¯ã‚·ãƒ§ãƒ³ãŒå†…部的ã«æ®‹ã‚Šç¶šã‘るよã†ã§ã€ãƒ†ã‚¹ãƒˆã®éš›ã«æ”¯éšœãŒå‡ºã‚‹ãŸã‚無効ã«ã™ã‚‹(ã‚ャッシュもå«ã‚ã¦ãƒ†ã‚¹ãƒˆã—ãŸã„ãŸã‚本当ã¯æœ‰åŠ¹ã«ã—ãŸã„ãŒ...) diff --git a/packages/backend/src/queue/QueueLoggerService.ts b/packages/backend/src/queue/QueueLoggerService.ts index 618d1d5c2f6d1d7a4fac6d8f5f625370c27eabad..65869afd461ca5e67a4023d6894b82d74bb69dbc 100644 --- a/packages/backend/src/queue/QueueLoggerService.ts +++ b/packages/backend/src/queue/QueueLoggerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/QueueProcessorModule.ts b/packages/backend/src/queue/QueueProcessorModule.ts index 29dc78605bd7290dd5132ad37ca36f565f5fdfe7..d7316e19e3a450cea1948aa0ac8ad2537413119c 100644 --- a/packages/backend/src/queue/QueueProcessorModule.ts +++ b/packages/backend/src/queue/QueueProcessorModule.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -25,6 +25,7 @@ import { ExportCustomEmojisProcessorService } from './processors/ExportCustomEmo import { ExportFollowingProcessorService } from './processors/ExportFollowingProcessorService.js'; import { ExportMutingProcessorService } from './processors/ExportMutingProcessorService.js'; import { ExportNotesProcessorService } from './processors/ExportNotesProcessorService.js'; +import { ExportClipsProcessorService } from './processors/ExportClipsProcessorService.js'; import { ExportUserListsProcessorService } from './processors/ExportUserListsProcessorService.js'; import { ExportAntennasProcessorService } from './processors/ExportAntennasProcessorService.js'; import { ImportBlockingProcessorService } from './processors/ImportBlockingProcessorService.js'; @@ -56,6 +57,7 @@ import { RelationshipProcessorService } from './processors/RelationshipProcessor ExportAccountDataProcessorService, ExportCustomEmojisProcessorService, ExportNotesProcessorService, + ExportClipsProcessorService, ExportFavoritesProcessorService, ExportFollowingProcessorService, ExportMutingProcessorService, diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index ea3ecd4dedf4bc2688decf1cf890d82b9ace2ace..76b6d7fb05a3acc8958b777ccd8dacc5acb9edb5 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -17,6 +17,7 @@ import { DeleteDriveFilesProcessorService } from './processors/DeleteDriveFilesP import { ExportAccountDataProcessorService } from './processors/ExportAccountDataProcessorService.js'; import { ExportCustomEmojisProcessorService } from './processors/ExportCustomEmojisProcessorService.js'; import { ExportNotesProcessorService } from './processors/ExportNotesProcessorService.js'; +import { ExportClipsProcessorService } from './processors/ExportClipsProcessorService.js'; import { ExportFollowingProcessorService } from './processors/ExportFollowingProcessorService.js'; import { ExportMutingProcessorService } from './processors/ExportMutingProcessorService.js'; import { ExportBlockingProcessorService } from './processors/ExportBlockingProcessorService.js'; @@ -94,6 +95,7 @@ export class QueueProcessorService implements OnApplicationShutdown { private exportAccountDataProcessorService: ExportAccountDataProcessorService, private exportCustomEmojisProcessorService: ExportCustomEmojisProcessorService, private exportNotesProcessorService: ExportNotesProcessorService, + private exportClipsProcessorService: ExportClipsProcessorService, private exportFavoritesProcessorService: ExportFavoritesProcessorService, private exportFollowingProcessorService: ExportFollowingProcessorService, private exportMutingProcessorService: ExportMutingProcessorService, @@ -169,6 +171,7 @@ export class QueueProcessorService implements OnApplicationShutdown { case 'exportAccountData': return this.exportAccountDataProcessorService.process(job); case 'exportCustomEmojis': return this.exportCustomEmojisProcessorService.process(job); case 'exportNotes': return this.exportNotesProcessorService.process(job); + case 'exportClips': return this.exportClipsProcessorService.process(job); case 'exportFavorites': return this.exportFavoritesProcessorService.process(job); case 'exportFollowing': return this.exportFollowingProcessorService.process(job); case 'exportMuting': return this.exportMutingProcessorService.process(job); @@ -292,9 +295,9 @@ export class QueueProcessorService implements OnApplicationShutdown { }, { ...baseQueueOptions(this.config, QUEUE.RELATIONSHIP), autorun: false, - concurrency: this.config.relashionshipJobConcurrency ?? 16, + concurrency: this.config.relationshipJobConcurrency ?? 16, limiter: { - max: this.config.relashionshipJobPerSec ?? 64, + max: this.config.relationshipJobPerSec ?? 64, duration: 1000, }, }); diff --git a/packages/backend/src/queue/const.ts b/packages/backend/src/queue/const.ts index 87d075304dc668b222dfe0a60de02554c79a4301..132e9166121c60dd15d49165283a72be57504c36 100644 --- a/packages/backend/src/queue/const.ts +++ b/packages/backend/src/queue/const.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts b/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts index 9f49d85c7f212cf7f0596124c1d31f70654ff144..4769cccabf43c3507b29bf474a044b936251780d 100644 --- a/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts +++ b/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts index 9b07389dc3699e9b1351746978abfa21baa652a7..448fc9c7633d5f3efa7f111d173b9cf7cc8afb13 100644 --- a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts +++ b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts index 55c444eee6977fee252d0f04253fc41f5b3ed985..110468801ce2d5b5ec704fda1f64f4ee5ac2ab75 100644 --- a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/CleanProcessorService.ts b/packages/backend/src/queue/processors/CleanProcessorService.ts index e252c5d8a1444142d274558b00eb983831f86ed2..a26b69cd2b47645b189ed3a1f72aaa68009483ff 100644 --- a/packages/backend/src/queue/processors/CleanProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -11,6 +11,7 @@ import type Logger from '@/logger.js'; import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; import type { Config } from '@/config.js'; +import { ReversiService } from '@/core/ReversiService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type * as Bull from 'bullmq'; @@ -32,6 +33,7 @@ export class CleanProcessorService { private roleAssignmentsRepository: RoleAssignmentsRepository, private queueLoggerService: QueueLoggerService, + private reversiService: ReversiService, private idService: IdService, ) { this.logger = this.queueLoggerService.logger.createSubLogger('clean'); @@ -65,6 +67,8 @@ export class CleanProcessorService { }); } + this.reversiService.cleanOutdatedGames(); + this.logger.succ('Cleaned.'); } } diff --git a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts index b62cc8a8fdfe0ea65c2f091caccef2105ac65c00..917de8b72c13958e8e3b74f0d6cf6dab259325cf 100644 --- a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts index 56369f3a7a2290e53282294b7c79462b6b828907..0e604a0501f392b0f8a3f7c183a61c4956840f35 100644 --- a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts index 6d0a45bcc072ebb0241ed41e1ea6c87d498804ff..291fa4a6d87218d556ee0d2a1b51a49aa80de6fa 100644 --- a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts index a4638bfaaf8d3e24d54d9f2dcf7e57a5f88f00fc..fc1dd93ce72a4a1262f1896c9dfced2b7e30616c 100644 --- a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts index 4a1d9f28b4fd9b4f989d0075d938e95b493a1637..5fed070929e8135beb013df83eb97cd7489a577b 100644 --- a/packages/backend/src/queue/processors/DeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -72,7 +72,7 @@ export class DeliverProcessorService { } try { - await this.apRequestService.signedPost(job.data.user, job.data.to, job.data.content); + await this.apRequestService.signedPost(job.data.user, job.data.to, job.data.content, job.data.digest); // Update stats this.federatedInstanceService.fetch(host).then(i => { @@ -111,7 +111,7 @@ export class DeliverProcessorService { if (res instanceof StatusError) { // 4xx - if (res.isClientError) { + if (!res.isRetryable) { // 相手ãŒé–‰éŽ–ã—ã¦ã„ã‚‹ã“ã¨ã‚’明示ã—ã¦ã„ã‚‹ãŸã‚ã€é…é€åœæ¢ã™ã‚‹ if (job.data.isSharedInbox && res.statusCode === 410) { this.federatedInstanceService.fetch(host).then(i => { diff --git a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts index 4a480844368f9f218825a1c31e12d628b558a016..29c1f27bb1179c67fc89ef6762e7edc8eb70c2d9 100644 --- a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts +++ b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts index d0968d29236754cfd3be01d9e66efe97609944c1..af48bad4177e5a691c2c4431cefb04a39b069365 100644 --- a/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts index 0a37e3ca1e8f302d2b77e69a1989106d2437dbb0..6ec3c18786fee8e5d6d51947e7932b62f7d76f80 100644 --- a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/ExportClipsProcessorService.ts b/packages/backend/src/queue/processors/ExportClipsProcessorService.ts new file mode 100644 index 0000000000000000000000000000000000000000..01eab26e96abc55ee65242262750efacc275a6ce --- /dev/null +++ b/packages/backend/src/queue/processors/ExportClipsProcessorService.ts @@ -0,0 +1,206 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as fs from 'node:fs'; +import { Writable } from 'node:stream'; +import { Inject, Injectable, StreamableFile } from '@nestjs/common'; +import { MoreThan } from 'typeorm'; +import { format as dateFormat } from 'date-fns'; +import { DI } from '@/di-symbols.js'; +import type { ClipNotesRepository, ClipsRepository, MiClip, MiClipNote, MiUser, NotesRepository, PollsRepository, UsersRepository } from '@/models/_.js'; +import type Logger from '@/logger.js'; +import { DriveService } from '@/core/DriveService.js'; +import { createTemp } from '@/misc/create-temp.js'; +import type { MiPoll } from '@/models/Poll.js'; +import type { MiNote } from '@/models/Note.js'; +import { bindThis } from '@/decorators.js'; +import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; +import { Packed } from '@/misc/json-schema.js'; +import { IdService } from '@/core/IdService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; +import type * as Bull from 'bullmq'; +import type { DbJobDataWithUser } from '../types.js'; + +@Injectable() +export class ExportClipsProcessorService { + private logger: Logger; + + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.pollsRepository) + private pollsRepository: PollsRepository, + + @Inject(DI.clipsRepository) + private clipsRepository: ClipsRepository, + + @Inject(DI.clipNotesRepository) + private clipNotesRepository: ClipNotesRepository, + + private driveService: DriveService, + private queueLoggerService: QueueLoggerService, + private idService: IdService, + ) { + this.logger = this.queueLoggerService.logger.createSubLogger('export-clips'); + } + + @bindThis + public async process(job: Bull.Job<DbJobDataWithUser>): Promise<void> { + this.logger.info(`Exporting clips of ${job.data.user.id} ...`); + + const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); + if (user == null) { + return; + } + + // Create temp file + const [path, cleanup] = await createTemp(); + + this.logger.info(`Temp file is ${path}`); + + try { + const stream = Writable.toWeb(fs.createWriteStream(path, { flags: 'a' })); + const writer = stream.getWriter(); + writer.closed.catch(this.logger.error); + + await writer.write('['); + + await this.processClips(writer, user, job); + + await writer.write(']'); + await writer.close(); + + this.logger.succ(`Exported to: ${path}`); + + const fileName = 'clips-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json'; + const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'json' }); + + this.logger.succ(`Exported to: ${driveFile.id}`); + } finally { + cleanup(); + } + } + + async processClips(writer: WritableStreamDefaultWriter, user: MiUser, job: Bull.Job<DbJobDataWithUser>) { + let exportedClipsCount = 0; + let cursor: MiClip['id'] | null = null; + + while (true) { + const clips = await this.clipsRepository.find({ + where: { + userId: user.id, + ...(cursor ? { id: MoreThan(cursor) } : {}), + }, + take: 100, + order: { + id: 1, + }, + }); + + if (clips.length === 0) { + job.updateProgress(100); + break; + } + + cursor = clips.at(-1)?.id ?? null; + + for (const clip of clips) { + // Stringify but remove the last `]}` + const content = JSON.stringify(this.serializeClip(clip)).slice(0, -2); + const isFirst = exportedClipsCount === 0; + await writer.write(isFirst ? content : ',\n' + content); + + await this.processClipNotes(writer, clip.id); + + await writer.write(']}'); + exportedClipsCount++; + } + + const total = await this.clipsRepository.countBy({ + userId: user.id, + }); + + job.updateProgress(exportedClipsCount / total); + } + } + + async processClipNotes(writer: WritableStreamDefaultWriter, clipId: string): Promise<void> { + let exportedClipNotesCount = 0; + let cursor: MiClipNote['id'] | null = null; + + while (true) { + const clipNotes = await this.clipNotesRepository.find({ + where: { + clipId, + ...(cursor ? { id: MoreThan(cursor) } : {}), + }, + take: 100, + order: { + id: 1, + }, + relations: ['note', 'note.user'], + }) as (MiClipNote & { note: MiNote & { user: MiUser } })[]; + + if (clipNotes.length === 0) { + break; + } + + cursor = clipNotes.at(-1)?.id ?? null; + + for (const clipNote of clipNotes) { + let poll: MiPoll | undefined; + if (clipNote.note.hasPoll) { + poll = await this.pollsRepository.findOneByOrFail({ noteId: clipNote.note.id }); + } + const content = JSON.stringify(this.serializeClipNote(clipNote, poll)); + const isFirst = exportedClipNotesCount === 0; + await writer.write(isFirst ? content : ',\n' + content); + + exportedClipNotesCount++; + } + } + } + + private serializeClip(clip: MiClip): Record<string, unknown> { + return { + id: clip.id, + name: clip.name, + description: clip.description, + lastClippedAt: clip.lastClippedAt?.toISOString(), + clipNotes: [], + }; + } + + private serializeClipNote(clip: MiClipNote & { note: MiNote & { user: MiUser } }, poll: MiPoll | undefined): Record<string, unknown> { + return { + id: clip.id, + createdAt: this.idService.parse(clip.id).date.toISOString(), + note: { + id: clip.note.id, + text: clip.note.text, + createdAt: this.idService.parse(clip.note.id).date.toISOString(), + fileIds: clip.note.fileIds, + replyId: clip.note.replyId, + renoteId: clip.note.renoteId, + poll: poll, + cw: clip.note.cw, + visibility: clip.note.visibility, + visibleUserIds: clip.note.visibleUserIds, + localOnly: clip.note.localOnly, + reactionAcceptance: clip.note.reactionAcceptance, + uri: clip.note.uri, + url: clip.note.url, + user: { + id: clip.note.user.id, + name: clip.note.user.name, + username: clip.note.user.username, + host: clip.note.user.host, + uri: clip.note.user.uri, + }, + }, + }; + } +} diff --git a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts index d5387fe42e7597560d6a23205a1874780cf3d7b1..e4eb4791bd7f0133175255f5fdcab2811de916a7 100644 --- a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts b/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts index af2a3434a9488125ed73b2238c8e75c5b20bc616..7bb626dd31bf43cce4cce1867bdf4331bd4f263a 100644 --- a/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts index c9739eb1cbfaffde2e4678a2868c74a062ae5b47..1cc80e66d7189f552431ffd30dabb958bc1c0185 100644 --- a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts index c8425c1f2d09f9c840b3ac0638491e09a3950b39..243b74f2c2be99c63736800f058df92ff6f4f158 100644 --- a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index cd4ccb0b074a64fa60a03630a850d18493a43ad4..c7611012d74b8aacc838bd5a0004d769a1a04b9f 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -1,9 +1,9 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import * as fs from 'node:fs'; +import { ReadableStream, TextEncoderStream } from 'node:stream/web'; import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; @@ -18,10 +18,82 @@ import { bindThis } from '@/decorators.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { Packed } from '@/misc/json-schema.js'; import { IdService } from '@/core/IdService.js'; +import { JsonArrayStream } from '@/misc/JsonArrayStream.js'; +import { FileWriterStream } from '@/misc/FileWriterStream.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type * as Bull from 'bullmq'; import type { DbJobDataWithUser } from '../types.js'; +class NoteStream extends ReadableStream<Record<string, unknown>> { + constructor( + job: Bull.Job, + notesRepository: NotesRepository, + pollsRepository: PollsRepository, + driveFileEntityService: DriveFileEntityService, + idService: IdService, + userId: string, + ) { + let exportedNotesCount = 0; + let cursor: MiNote['id'] | null = null; + + const serialize = ( + note: MiNote, + poll: MiPoll | null, + files: Packed<'DriveFile'>[], + ): Record<string, unknown> => { + return { + id: note.id, + text: note.text, + createdAt: idService.parse(note.id).date.toISOString(), + fileIds: note.fileIds, + files: files, + replyId: note.replyId, + renoteId: note.renoteId, + poll: poll, + cw: note.cw, + visibility: note.visibility, + visibleUserIds: note.visibleUserIds, + localOnly: note.localOnly, + reactionAcceptance: note.reactionAcceptance, + }; + }; + + super({ + async pull(controller): Promise<void> { + const notes = await notesRepository.find({ + where: { + userId, + ...(cursor !== null ? { id: MoreThan(cursor) } : {}), + }, + take: 100, // 100件ãšã¤å–å¾— + order: { id: 1 }, + }); + + if (notes.length === 0) { + job.updateProgress(100); + controller.close(); + } + + cursor = notes.at(-1)?.id ?? null; + + for (const note of notes) { + const poll = note.hasPoll + ? await pollsRepository.findOneByOrFail({ noteId: note.id }) // N+1 + : null; + const files = await driveFileEntityService.packManyByIds(note.fileIds); // N+1 + const content = serialize(note, poll, files); + + controller.enqueue(content); + exportedNotesCount++; + } + + const total = await notesRepository.countBy({ userId }); + job.updateProgress(exportedNotesCount / total); + }, + }); + } +} + @Injectable() export class ExportNotesProcessorService { private logger: Logger; @@ -59,67 +131,19 @@ export class ExportNotesProcessorService { this.logger.info(`Temp file is ${path}`); try { - const stream = fs.createWriteStream(path, { flags: 'a' }); - - const write = (text: string): Promise<void> => { - return new Promise<void>((res, rej) => { - stream.write(text, err => { - if (err) { - this.logger.error(err); - rej(err); - } else { - res(); - } - }); - }); - }; - - await write('['); - - let exportedNotesCount = 0; - let cursor: MiNote['id'] | null = null; - - while (true) { - const notes = await this.notesRepository.find({ - where: { - userId: user.id, - ...(cursor ? { id: MoreThan(cursor) } : {}), - }, - take: 100, - order: { - id: 1, - }, - }) as MiNote[]; - - if (notes.length === 0) { - job.updateProgress(100); - break; - } - - cursor = notes.at(-1)?.id ?? null; + // メモリãŒè¶³ã‚Šãªããªã‚‰ãªã„よã†ã«ã‚¹ãƒˆãƒªãƒ¼ãƒ ã§å‡¦ç†ã™ã‚‹ + await new NoteStream( + job, + this.notesRepository, + this.pollsRepository, + this.driveFileEntityService, + this.idService, + user.id, + ) + .pipeThrough(new JsonArrayStream()) + .pipeThrough(new TextEncoderStream()) + .pipeTo(new FileWriterStream(path)); - for (const note of notes) { - let poll: MiPoll | undefined; - if (note.hasPoll) { - poll = await this.pollsRepository.findOneByOrFail({ noteId: note.id }); - } - const files = await this.driveFileEntityService.packManyByIds(note.fileIds); - const content = JSON.stringify(this.serialize(note, poll, files)); - const isFirst = exportedNotesCount === 0; - await write(isFirst ? content : ',\n' + content); - exportedNotesCount++; - } - - const total = await this.notesRepository.countBy({ - userId: user.id, - }); - - job.updateProgress(exportedNotesCount / total); - } - - await write(']'); - - stream.end(); this.logger.succ(`Exported to: ${path}`); const fileName = 'notes-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json'; @@ -130,22 +154,4 @@ export class ExportNotesProcessorService { cleanup(); } } - - private serialize(note: MiNote, poll: MiPoll | null = null, files: Packed<'DriveFile'>[]): Record<string, unknown> { - return { - id: note.id, - text: note.text, - createdAt: this.idService.parse(note.id).date.toISOString(), - fileIds: note.fileIds, - files: files, - replyId: note.replyId, - renoteId: note.renoteId, - poll: poll, - cw: note.cw, - visibility: note.visibility, - visibleUserIds: note.visibleUserIds, - localOnly: note.localOnly, - reactionAcceptance: note.reactionAcceptance, - }; - } } diff --git a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts index a3f9441dc28247fc7ee649c1506d6dda615e89fd..ee87cff5d360e37aa945dd113e04761a17c33417 100644 --- a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts index 291ea14b67fd22d4c7ab65a8fecaee26bc3df095..951b5605978782bd572321ed08400fd5b5a3dcd3 100644 --- a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts index 64520b770b36a4e31b6a72fdb19bdeb3a24e8e27..b78229c648e35333f308af394451afcf4759a0d7 100644 --- a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index a52af54a3966c25bf3ff2e36a69533dd3c4c5914..171809d25c3a8fc1b323715c35f4353e35916e3f 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts index e75499a56f84d0cdeb342c554ab2e90598b0f5cb..70c9f3a09679fca5f85f87f36738b88ac4a656c8 100644 --- a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts index 9db4e5d8e0e9cd41a1ace3e9d18e79f79db6eead..ec9d2b6c4ce567594d0ce2bc0abaa8a4345a104b 100644 --- a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts index d64a861b030a1bdfd9e0577cf54bb2a76e661d70..10cd90c4ad695acc48ee2281416dc69c639df523 100644 --- a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts @@ -1,5 +1,5 @@ import * as fs from 'node:fs'; -import * as vm from 'node:vm'; +import * as fsp from 'node:fs/promises'; import * as crypto from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import { ZipReader } from 'slacc'; @@ -51,7 +51,7 @@ export class ImportNotesProcessorService { @bindThis private async uploadFiles(dir: string, user: MiUser, folder?: MiDriveFolder['id']) { - const fileList = fs.readdirSync(dir); + const fileList = await fsp.readdir(dir); for await (const file of fileList) { const name = `${dir}/${file}`; if (fs.statSync(name).isDirectory()) { @@ -132,7 +132,7 @@ export class ImportNotesProcessorService { private parseTwitterFile(str : string) : null | [{ tweet: any }] { const removed = str.replace(new RegExp('window\\.YTD\\.tweets\\.part0 = ', 'g'), ''); - + try { return JSON.parse(removed); } catch (error) { @@ -141,6 +141,19 @@ export class ImportNotesProcessorService { } } + @bindThis + private parseTwitterFile(str : string) : { tweet: object }[] { + const jsonStr = str.replace(/^\s*window\.YTD\.tweets\.part0\s*=\s*/, ''); + + try { + return JSON.parse(jsonStr); + } catch (error) { + //The format is not what we expected. Either this file was tampered with or twitters exports changed + this.logger.warn('Failed to import twitter notes due to malformed file'); + throw error; + } + } + @bindThis public async process(job: Bull.Job<DbNoteImportJobData>): Promise<void> { this.logger.info(`Starting note import of ${job.data.user.id} ...`); @@ -173,7 +186,7 @@ export class ImportNotesProcessorService { const destPath = path + '/twitter.zip'; try { - fs.writeFileSync(destPath, '', 'binary'); + await fsp.writeFile(destPath, '', 'binary'); await this.downloadService.downloadUrl(file.url, destPath); } catch (e) { // TODO: 何度ã‹å†è©¦è¡Œ if (e instanceof Error || typeof e === 'string') { @@ -185,21 +198,13 @@ export class ImportNotesProcessorService { const outputPath = path + '/twitter'; try { this.logger.succ(`Unzipping to ${outputPath}`); - ZipReader.withDestinationPath(outputPath).viaBuffer(await fs.promises.readFile(destPath)); + ZipReader.withDestinationPath(outputPath).viaBuffer(await fsp.readFile(destPath)); - const unprocessedTweetJson = this.parseTwitterFile(fs.readFileSync(outputPath + '/data/tweets.js', 'utf-8')); + const unprocessedTweets = this.parseTwitterFile(await fsp.readFile(outputPath + '/data/tweets.js', 'utf-8')); - //Make sure that it isnt null (because if something went wrong in parseTwitterFile it returns null) - if (unprocessedTweetJson) { - const tweets = Object.keys(unprocessedTweetJson).reduce((m, key, i, obj) => { - return m.concat(unprocessedTweetJson[i].tweet); - }, []); - - const processedTweets = await this.recreateChain(['id_str'], ['in_reply_to_status_id_str'], tweets, false); - this.queueService.createImportTweetsToDbJob(job.data.user, processedTweets, null); - } else { - this.logger.warn('Failed to import twitter notes due to malformed file'); - } + const tweets = unprocessedTweets.map(e => e.tweet); + const processedTweets = await this.recreateChain(['id_str'], ['in_reply_to_status_id_str'], tweets, false); + this.queueService.createImportTweetsToDbJob(job.data.user, processedTweets, null); } finally { cleanup(); } @@ -211,7 +216,7 @@ export class ImportNotesProcessorService { const destPath = path + '/facebook.zip'; try { - fs.writeFileSync(destPath, '', 'binary'); + await fsp.writeFile(destPath, '', 'binary'); await this.downloadService.downloadUrl(file.url, destPath); } catch (e) { // TODO: 何度ã‹å†è©¦è¡Œ if (e instanceof Error || typeof e === 'string') { @@ -223,8 +228,8 @@ export class ImportNotesProcessorService { const outputPath = path + '/facebook'; try { this.logger.succ(`Unzipping to ${outputPath}`); - ZipReader.withDestinationPath(outputPath).viaBuffer(await fs.promises.readFile(destPath)); - const postsJson = fs.readFileSync(outputPath + '/your_activity_across_facebook/posts/your_posts__check_ins__photos_and_videos_1.json', 'utf-8'); + ZipReader.withDestinationPath(outputPath).viaBuffer(await fsp.readFile(destPath)); + const postsJson = await fsp.readFile(outputPath + '/your_activity_across_facebook/posts/your_posts__check_ins__photos_and_videos_1.json', 'utf-8'); const posts = JSON.parse(postsJson); const facebookFolder = await this.driveFoldersRepository.findOneBy({ name: 'Facebook', userId: job.data.user.id, parentId: folder?.id }); if (facebookFolder == null && folder) { @@ -244,7 +249,7 @@ export class ImportNotesProcessorService { const destPath = path + '/unknown.zip'; try { - fs.writeFileSync(destPath, '', 'binary'); + await fsp.writeFile(destPath, '', 'binary'); await this.downloadService.downloadUrl(file.url, destPath); } catch (e) { // TODO: 何度ã‹å†è©¦è¡Œ if (e instanceof Error || typeof e === 'string') { @@ -256,11 +261,11 @@ export class ImportNotesProcessorService { const outputPath = path + '/unknown'; try { this.logger.succ(`Unzipping to ${outputPath}`); - ZipReader.withDestinationPath(outputPath).viaBuffer(await fs.promises.readFile(destPath)); + ZipReader.withDestinationPath(outputPath).viaBuffer(await fsp.readFile(destPath)); const isInstagram = type === 'Instagram' || fs.existsSync(outputPath + '/instagram_live') || fs.existsSync(outputPath + '/instagram_ads_and_businesses'); const isOutbox = type === 'Mastodon' || fs.existsSync(outputPath + '/outbox.json'); if (isInstagram) { - const postsJson = fs.readFileSync(outputPath + '/content/posts_1.json', 'utf-8'); + const postsJson = await fsp.readFile(outputPath + '/content/posts_1.json', 'utf-8'); const posts = JSON.parse(postsJson); const igFolder = await this.driveFoldersRepository.findOneBy({ name: 'Instagram', userId: job.data.user.id, parentId: folder?.id }); if (igFolder == null && folder) { @@ -270,16 +275,16 @@ export class ImportNotesProcessorService { } this.queueService.createImportIGToDbJob(job.data.user, posts); } else if (isOutbox) { - const actorJson = fs.readFileSync(outputPath + '/actor.json', 'utf-8'); + const actorJson = await fsp.readFile(outputPath + '/actor.json', 'utf-8'); const actor = JSON.parse(actorJson); const isPleroma = actor['@context'].some((v: any) => typeof v === 'string' && v.match(/litepub(.*)/)); if (isPleroma) { - const outboxJson = fs.readFileSync(outputPath + '/outbox.json', 'utf-8'); + const outboxJson = await fsp.readFile(outputPath + '/outbox.json', 'utf-8'); const outbox = JSON.parse(outboxJson); const processedToots = await this.recreateChain(['object', 'id'], ['object', 'inReplyTo'], outbox.orderedItems.filter((x: any) => x.type === 'Create' && x.object.type === 'Note'), true); this.queueService.createImportPleroToDbJob(job.data.user, processedToots, null); } else { - const outboxJson = fs.readFileSync(outputPath + '/outbox.json', 'utf-8'); + const outboxJson = await fsp.readFile(outputPath + '/outbox.json', 'utf-8'); const outbox = JSON.parse(outboxJson); let mastoFolder = await this.driveFoldersRepository.findOneBy({ name: 'Mastodon', userId: job.data.user.id, parentId: folder?.id }); if (mastoFolder == null && folder) { @@ -302,7 +307,7 @@ export class ImportNotesProcessorService { this.logger.info(`Temp dir is ${path}`); try { - fs.writeFileSync(path, '', 'utf-8'); + await fsp.writeFile(path, '', 'utf-8'); await this.downloadService.downloadUrl(file.url, path); } catch (e) { // TODO: 何度ã‹å†è©¦è¡Œ if (e instanceof Error || typeof e === 'string') { @@ -311,7 +316,7 @@ export class ImportNotesProcessorService { throw e; } - const notesJson = fs.readFileSync(path, 'utf-8'); + const notesJson = await fsp.readFile(path, 'utf-8'); const notes = JSON.parse(notesJson); const processedNotes = await this.recreateChain(['id'], ['replyId'], notes, false); this.queueService.createImportKeyNotesToDbJob(job.data.user, processedNotes, null); @@ -424,6 +429,10 @@ export class ImportNotesProcessorService { const name = file.url.substring(slashdex + 1); const exists = await this.driveFilesRepository.findOneBy({ name: name, userId: user.id }); if (exists) { + if (file.name) { + this.driveService.updateFile(exists, { comment: file.name }, user); + } + files.push(exists); } } @@ -583,14 +592,15 @@ export class ImportNotesProcessorService { async function replaceTwitterMentions(full_text: string, mentions: any) { let full_textedit = full_text; mentions.forEach((mention: any) => { - full_textedit = full_textedit.replaceAll(`@${mention.screen_name}`, `[@${mention.screen_name}](https://nitter.net/${mention.screen_name})`); + full_textedit = full_textedit.replaceAll(`@${mention.screen_name}`, `[@${mention.screen_name}](https://twitter.com/${mention.screen_name})`); }); return full_textedit; } try { const date = new Date(tweet.created_at); - const textReplaceURLs = tweet.entities.urls && tweet.entities.urls.length > 0 ? await replaceTwitterUrls(tweet.full_text, tweet.entities.urls) : tweet.full_text; + const decodedText = tweet.full_text.replaceAll('>', '>').replaceAll('<', '<').replaceAll('&', '&'); + const textReplaceURLs = tweet.entities.urls && tweet.entities.urls.length > 0 ? await replaceTwitterUrls(decodedText, tweet.entities.urls) : decodedText; const text = tweet.entities.user_mentions && tweet.entities.user_mentions.length > 0 ? await replaceTwitterMentions(textReplaceURLs, tweet.entities.user_mentions) : textReplaceURLs; const files: MiDriveFile[] = []; diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts index 5dd3fbe887d3f8e4627929bbff86474358d044c7..a5992c28c84674c22d961134c2244a5738651cb7 100644 --- a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index f69634968df9e52dd4799b3f07099547f0592c3b..ad1d9799a70ad9fe6273d4298f30d6fe1b3dcbf7 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -24,6 +24,7 @@ import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; import { LdSignatureService } from '@/core/activitypub/LdSignatureService.js'; import { ApInboxService } from '@/core/activitypub/ApInboxService.js'; import { bindThis } from '@/decorators.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type { InboxJobData } from '../types.js'; @@ -85,7 +86,7 @@ export class InboxProcessorService { } catch (err) { // 対象ãŒ4xxãªã‚‰ã‚¹ã‚ップ if (err instanceof StatusError) { - if (err.isClientError) { + if (!err.isRetryable) { throw new Bull.UnrecoverableError(`skip: Ignored deleted actors on both ends ${activity.actor} - ${err.statusCode}`); } throw new Error(`Error in actor ${activity.actor} - ${err.statusCode}`); @@ -191,7 +192,17 @@ export class InboxProcessorService { }); // ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ãƒ†ã‚£ã‚’å‡¦ç† - await this.apInboxService.performActivity(authUser.user, activity); + try { + await this.apInboxService.performActivity(authUser.user, activity); + } catch (e) { + if (e instanceof IdentifiableError) { + if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') { + return 'blocked notes with prohibited words'; + } + if (e.id === '85ab9bd7-3a41-4530-959d-f07073900109') return 'actor has been suspended'; + } + throw e; + } return 'ok'; } } diff --git a/packages/backend/src/queue/processors/RelationshipProcessorService.ts b/packages/backend/src/queue/processors/RelationshipProcessorService.ts index b2d8e3631f3b03d5a68479fdf3547b4a48b17742..408b02fb380cebc75c4e59ae4c7c0c8aef4aa2ff 100644 --- a/packages/backend/src/queue/processors/RelationshipProcessorService.ts +++ b/packages/backend/src/queue/processors/RelationshipProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts index b3b055ef8cce6c3bcd5a4362e8582bf8c7842f20..570cdf9a7504f56e7cb3a06d039f49ac94184220 100644 --- a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/TickChartsProcessorService.ts b/packages/backend/src/queue/processors/TickChartsProcessorService.ts index 7b1efb71e02ce206a0845826a2f3aee8fe2ef263..93ec34162db4e655596f78295b0b260a921fcf4f 100644 --- a/packages/backend/src/queue/processors/TickChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/TickChartsProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts index a41f5565c8ed4e29c10e5242515af9d7258164d6..8c260c0137ce33e02ef50215239f75b3c29db6e2 100644 --- a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -71,7 +71,7 @@ export class WebhookDeliverProcessorService { if (res instanceof StatusError) { // 4xx - if (res.isClientError) { + if (!res.isRetryable) { throw new Bull.UnrecoverableError(`${res.statusCode} ${res.statusMessage}`); } diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts index 432b3d364fc6e5ba0f3580f9848e0d1deefbbfbd..91718898b20d53fbec7cce148b605814b4e04816 100644 --- a/packages/backend/src/queue/types.ts +++ b/packages/backend/src/queue/types.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -15,7 +15,9 @@ export type DeliverJobData = { /** Actor */ user: ThinUser; /** Activity */ - content: unknown; + content: string; + /** Digest header */ + digest: string; /** inbox URL to deliver */ to: string; /** whether it is sharedInbox */ diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 8fa8320c8ca3085fb0eb28fe797bec06d64290b2..64bce07a98109bba0fbdc4d1f54cbb7a92a4064c 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -162,23 +162,25 @@ export class ActivityPubServerService { return true; } + const keyId = new URL(signature.keyId); + const keyHost = this.utilityService.toPuny(keyId.hostname); + + const logPrefix = `${request.id} ${request.url} (by ${request.headers['user-agent']}) apparently from ${keyHost}:`; + if (signature.params.headers.indexOf('host') === -1 || request.headers.host !== this.config.host) { // no destination host, or not us: refuse - this.authlogger.warn(`${request.id} ${request.url} no destination host, or not us: refuse`); + this.authlogger.warn(`${logPrefix} no destination host, or not us: refuse`); reply.code(401); return true; } - const keyId = new URL(signature.keyId); - const keyHost = this.utilityService.toPuny(keyId.hostname); - const meta = await this.metaService.fetch(); if (this.utilityService.isBlockedHost(meta.blockedHosts, keyHost)) { /* blocked instance: refuse (we don't care if the signature is good, if they even pretend to be from a blocked instance, they're out) */ - this.authlogger.warn(`${request.id} ${request.url} instance ${keyHost} is blocked: refuse`); + this.authlogger.warn(`${logPrefix} instance is blocked: refuse`); reply.code(401); return true; } @@ -193,13 +195,13 @@ export class ActivityPubServerService { /* keyId is often in the shape `${user.uri}#${keyname}`, try fetching information about the remote user */ const candidate = formatURL(keyId, { fragment: false }); - this.authlogger.info(`${request.id} ${request.url} we don't know the user for keyId ${keyId}, trying to fetch via ${candidate}`); + this.authlogger.info(`${logPrefix} we don't know the user for keyId ${keyId}, trying to fetch via ${candidate}`); authUser = await this.apDbResolverService.getAuthUserFromApId(candidate); } if (authUser?.key == null) { // we can't figure out who the signer is, or we can't get their key: refuse - this.authlogger.warn(`${request.id} ${request.url} we can't figure out who the signer is, or we can't get their key: refuse`); + this.authlogger.warn(`${logPrefix} we can't figure out who the signer is, or we can't get their key: refuse`); reply.code(401); return true; } @@ -207,20 +209,20 @@ export class ActivityPubServerService { let httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem); if (!httpSignatureValidated) { - this.authlogger.info(`${request.id} ${request.url} failed to validate signature, re-fetching the key for ${authUser.user.uri}`); + this.authlogger.info(`${logPrefix} failed to validate signature, re-fetching the key for ${authUser.user.uri}`); // maybe they changed their key? refetch it authUser.key = await this.apDbResolverService.refetchPublicKeyForApId(authUser.user); if (authUser.key != null) { httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem); } else { - this.authlogger.warn(`${request.id} ${request.url} failed to re-fetch key for ${authUser.user}`); + this.authlogger.warn(`${logPrefix} failed to re-fetch key for ${authUser.user}`); } } if (!httpSignatureValidated) { // bad signature: refuse - this.authlogger.info(`${request.id} ${request.url} failed to validate signature: refuse`); + this.authlogger.info(`${logPrefix} failed to validate signature: refuse`); reply.code(401); return true; } @@ -793,6 +795,8 @@ export class ActivityPubServerService { fastify.get<{ Params: { user: string; } }>('/users/:user', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => { if (await this.shouldRefuseGetRequest(request, reply, request.params.user)) return; + + vary(reply.raw, 'Accept'); const userId = request.params.user; @@ -807,6 +811,8 @@ export class ActivityPubServerService { fastify.get<{ Params: { user: string; } }>('/@:user', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => { if (await this.shouldRefuseGetRequest(request, reply, request.params.user)) return; + + vary(reply.raw, 'Accept'); const user = await this.usersRepository.findOneBy({ usernameLower: request.params.user.toLowerCase(), diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index e82ef64dc4a7fd1e718d7c226962bbef81e95571..6d24898accac994c0183a6c907aa4d5d5e2bfa61 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -9,7 +9,7 @@ import { dirname } from 'node:path'; import { Inject, Injectable } from '@nestjs/common'; import rename from 'rename'; import sharp from 'sharp'; -import { sharpBmp } from 'sharp-read-bmp'; +import { sharpBmp } from '@misskey-dev/sharp-read-bmp'; import type { Config } from '@/config.js'; import type { MiDriveFile, DriveFilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; @@ -27,6 +27,7 @@ import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import { isMimeImage } from '@/misc/is-mime-image.js'; import { correctFilename } from '@/misc/correct-filename.js'; +import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js'; import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify'; const _filename = fileURLToPath(import.meta.url); @@ -65,20 +66,23 @@ export class FileServerService { done(); }); - fastify.get('/files/app-default.jpg', (request, reply) => { - const file = fs.createReadStream(`${_dirname}/assets/dummy.png`); - reply.header('Content-Type', 'image/jpeg'); - reply.header('Cache-Control', 'max-age=31536000, immutable'); - return reply.send(file); - }); - - fastify.get<{ Params: { key: string; } }>('/files/:key', async (request, reply) => { - return await this.sendDriveFile(request, reply) - .catch(err => this.errorHandler(request, reply, err)); - }); - fastify.get<{ Params: { key: string; } }>('/files/:key/*', async (request, reply) => { - return await this.sendDriveFile(request, reply) - .catch(err => this.errorHandler(request, reply, err)); + fastify.register((fastify, options, done) => { + fastify.addHook('onRequest', handleRequestRedirectToOmitSearch); + fastify.get('/files/app-default.jpg', (request, reply) => { + const file = fs.createReadStream(`${_dirname}/assets/dummy.png`); + reply.header('Content-Type', 'image/jpeg'); + reply.header('Cache-Control', 'max-age=31536000, immutable'); + return reply.send(file); + }); + + fastify.get<{ Params: { key: string; } }>('/files/:key', async (request, reply) => { + return await this.sendDriveFile(request, reply) + .catch(err => this.errorHandler(request, reply, err)); + }); + fastify.get<{ Params: { key: string; } }>('/files/:key/*', async (request, reply) => { + return await reply.redirect(301, `${this.config.url}/files/${request.params.key}`); + }); + done(); }); fastify.get<{ @@ -166,11 +170,35 @@ export class FileServerService { } if (!image) { - image = { - data: fs.createReadStream(file.path), - ext: file.ext, - type: file.mime, - }; + if (request.headers.range && file.file.size > 0) { + const range = request.headers.range as string; + const parts = range.replace(/bytes=/, '').split('-'); + const start = parseInt(parts[0], 10); + let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1; + if (end > file.file.size) { + end = file.file.size - 1; + } + const chunksize = end - start + 1; + + image = { + data: fs.createReadStream(file.path, { + start, + end, + }), + ext: file.ext, + type: file.mime, + }; + + reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`); + reply.header('Accept-Ranges', 'bytes'); + reply.header('Content-Length', chunksize); + } else { + image = { + data: fs.createReadStream(file.path), + ext: file.ext, + type: file.mime, + }; + } } if ('pipe' in image.data && typeof image.data.pipe === 'function') { @@ -201,11 +229,54 @@ export class FileServerService { reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(file.mime) ? file.mime : 'application/octet-stream'); reply.header('Cache-Control', 'max-age=31536000, immutable'); reply.header('Content-Disposition', contentDisposition('inline', filename)); + + if (request.headers.range && file.file.size > 0) { + const range = request.headers.range as string; + const parts = range.replace(/bytes=/, '').split('-'); + const start = parseInt(parts[0], 10); + let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1; + if (end > file.file.size) { + end = file.file.size - 1; + } + const chunksize = end - start + 1; + const fileStream = fs.createReadStream(file.path, { + start, + end, + }); + reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`); + reply.header('Accept-Ranges', 'bytes'); + reply.header('Content-Length', chunksize); + reply.code(206); + return fileStream; + } + return fs.createReadStream(file.path); } else { reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(file.file.type) ? file.file.type : 'application/octet-stream'); reply.header('Cache-Control', 'max-age=31536000, immutable'); reply.header('Content-Disposition', contentDisposition('inline', file.filename)); + + if (request.headers.range && file.file.size > 0) { + const range = request.headers.range as string; + const parts = range.replace(/bytes=/, '').split('-'); + const start = parseInt(parts[0], 10); + let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1; + console.log(end); + if (end > file.file.size) { + end = file.file.size - 1; + } + const chunksize = end - start + 1; + const fileStream = fs.createReadStream(file.path, { + start, + end, + }); + reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`); + reply.header('Accept-Ranges', 'bytes'); + reply.header('Content-Length', chunksize); + reply.code(206); + return fileStream; + } + return fs.createReadStream(file.path); } } catch (e) { @@ -338,11 +409,35 @@ export class FileServerService { } if (!image) { - image = { - data: fs.createReadStream(file.path), - ext: file.ext, - type: file.mime, - }; + if (request.headers.range && file.file && file.file.size > 0) { + const range = request.headers.range as string; + const parts = range.replace(/bytes=/, '').split('-'); + const start = parseInt(parts[0], 10); + let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1; + if (end > file.file.size) { + end = file.file.size - 1; + } + const chunksize = end - start + 1; + + image = { + data: fs.createReadStream(file.path, { + start, + end, + }), + ext: file.ext, + type: file.mime, + }; + + reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`); + reply.header('Accept-Ranges', 'bytes'); + reply.header('Content-Length', chunksize); + } else { + image = { + data: fs.createReadStream(file.path), + ext: file.ext, + type: file.mime, + }; + } } if ('cleanup' in file) { diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 31479269b96c9dea02339f97aaf9ddb7ffd63991..d4f4c8b752b3cb26eeb16b7d0a2c138ce3aec23e 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -108,6 +108,7 @@ export class NodeinfoServerService { tosUrl: meta.termsOfServiceUrl, privacyPolicyUrl: meta.privacyPolicyUrl, impressumUrl: meta.impressumUrl, + donationUrl: meta.donationUrl, repositoryUrl: meta.repositoryUrl, feedbackUrl: meta.feedbackUrl, disableRegistration: meta.disableRegistration, @@ -117,6 +118,8 @@ export class NodeinfoServerService { emailRequiredForSignup: meta.emailRequiredForSignup, enableHcaptcha: meta.enableHcaptcha, enableRecaptcha: meta.enableRecaptcha, + enableMcaptcha: meta.enableMcaptcha, + enableTurnstile: meta.enableTurnstile, maxNoteTextLength: this.config.maxNoteLength, enableEmail: meta.enableEmail, enableServiceWorker: meta.enableServiceWorker, diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index 52070b515744ffbc4d37c0be5a4252e5ac6cba29..e0c4768ffc4b4934fce468af971d0529f14a5c60 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -22,10 +22,14 @@ import { SigninApiService } from './api/SigninApiService.js'; import { SigninService } from './api/SigninService.js'; import { SignupApiService } from './api/SignupApiService.js'; import { StreamingApiServerService } from './api/StreamingApiServerService.js'; +import { OpenApiServerService } from './api/openapi/OpenApiServerService.js'; import { ClientServerService } from './web/ClientServerService.js'; import { MastoConverters } from './api/mastodon/converters.js'; import { FeedService } from './web/FeedService.js'; import { UrlPreviewService } from './web/UrlPreviewService.js'; +import { ClientLoggerService } from './web/ClientLoggerService.js'; +import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js'; + import { MainChannelService } from './api/stream/channels/main.js'; import { AdminChannelService } from './api/stream/channels/admin.js'; import { AntennaChannelService } from './api/stream/channels/antenna.js'; @@ -40,11 +44,10 @@ import { LocalTimelineChannelService } from './api/stream/channels/local-timelin import { QueueStatsChannelService } from './api/stream/channels/queue-stats.js'; import { ServerStatsChannelService } from './api/stream/channels/server-stats.js'; import { UserListChannelService } from './api/stream/channels/user-list.js'; -import { OpenApiServerService } from './api/openapi/OpenApiServerService.js'; import { MastodonApiServerService } from './api/mastodon/MastodonApiServerService.js'; -import { ClientLoggerService } from './web/ClientLoggerService.js'; import { RoleTimelineChannelService } from './api/stream/channels/role-timeline.js'; -import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js'; +import { ReversiChannelService } from './api/stream/channels/reversi.js'; +import { ReversiGameChannelService } from './api/stream/channels/reversi-game.js'; @Module({ imports: [ @@ -81,6 +84,8 @@ import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js'; BubbleTimelineChannelService, HashtagChannelService, RoleTimelineChannelService, + ReversiChannelService, + ReversiGameChannelService, HomeTimelineChannelService, HybridTimelineChannelService, LocalTimelineChannelService, diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 3b43b931ae922a2e3358ba4cc659ec4634a3bfaa..5a456e09ad80315cc91586e69b203a1013688868 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -206,7 +206,7 @@ export class ServerService implements OnApplicationShutdown { }); this.globalEventService.publishMainStream(profile.userId, 'meUpdated', await this.userEntityService.pack(profile.userId, { id: profile.userId }, { - detail: true, + schema: 'MeDetailed', includeSecrets: true, })); diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts index c3eaf53a142e960507c4f53e01219129b9a2f5e2..8e326da89a6ae64000c765ba0b8279542b9083c0 100644 --- a/packages/backend/src/server/WellKnownServerService.ts +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index 56f804dee8ce60442aa1f201d11a552f7f65a22e..9836689872aede822f8d70112b905a8272d8fa46 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/ApiLoggerService.ts b/packages/backend/src/server/api/ApiLoggerService.ts index 2339366a5d3c5060e82aab5551a7d64744ac7292..72b71c0b5c7c3bc07be0a176dc1243adc1c57e82 100644 --- a/packages/backend/src/server/api/ApiLoggerService.ts +++ b/packages/backend/src/server/api/ApiLoggerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index 1758c03acab83257fb693c154242924a79ea915d..e99244cdd02da4d02aad3c23fbbb415d883e06e7 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -157,7 +157,7 @@ export class ApiServerService { return { ok: true, token: token.token, - user: await this.userEntityService.pack(token.userId, null, { detail: true }), + user: await this.userEntityService.pack(token.userId, null, { schema: 'UserDetailedNotMe' }), }; } else { return { diff --git a/packages/backend/src/server/api/AuthenticateService.ts b/packages/backend/src/server/api/AuthenticateService.ts index f07568819406607362e281fb4b193ee10358dc0d..ddef8db9879ac82b5ebba604ac4b00cbe8428c5f 100644 --- a/packages/backend/src/server/api/AuthenticateService.ts +++ b/packages/backend/src/server/api/AuthenticateService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index ed1b2d4377a3f95c2b386a7366d8ba5292c92e7d..f2945f477ca1872f09c629af56660f8a118bdb60 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -214,6 +214,7 @@ import * as ep___i_exportBlocking from './endpoints/i/export-blocking.js'; import * as ep___i_exportFollowing from './endpoints/i/export-following.js'; import * as ep___i_exportMute from './endpoints/i/export-mute.js'; import * as ep___i_exportNotes from './endpoints/i/export-notes.js'; +import * as ep___i_exportClips from './endpoints/i/export-clips.js'; import * as ep___i_exportFavorites from './endpoints/i/export-favorites.js'; import * as ep___i_exportUserLists from './endpoints/i/export-user-lists.js'; import * as ep___i_exportAntennas from './endpoints/i/export-antennas.js'; @@ -304,6 +305,7 @@ import * as ep___notes_translate from './endpoints/notes/translate.js'; import * as ep___notes_unrenote from './endpoints/notes/unrenote.js'; import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js'; import * as ep___notifications_create from './endpoints/notifications/create.js'; +import * as ep___notifications_flush from './endpoints/notifications/flush.js'; import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js'; import * as ep___notifications_testNotification from './endpoints/notifications/test-notification.js'; import * as ep___pagePush from './endpoints/page-push.js'; @@ -376,6 +378,15 @@ import * as ep___fetchRss from './endpoints/fetch-rss.js'; import * as ep___fetchExternalResources from './endpoints/fetch-external-resources.js'; import * as ep___retention from './endpoints/retention.js'; import * as ep___sponsors from './endpoints/sponsors.js'; +import * as ep___bubbleGame_register from './endpoints/bubble-game/register.js'; +import * as ep___bubbleGame_ranking from './endpoints/bubble-game/ranking.js'; +import * as ep___reversi_cancelMatch from './endpoints/reversi/cancel-match.js'; +import * as ep___reversi_games from './endpoints/reversi/games.js'; +import * as ep___reversi_match from './endpoints/reversi/match.js'; +import * as ep___reversi_invitations from './endpoints/reversi/invitations.js'; +import * as ep___reversi_showGame from './endpoints/reversi/show-game.js'; +import * as ep___reversi_surrender from './endpoints/reversi/surrender.js'; +import * as ep___reversi_verify from './endpoints/reversi/verify.js'; import { GetterService } from './GetterService.js'; import { ApiLoggerService } from './ApiLoggerService.js'; import type { Provider } from '@nestjs/common'; @@ -588,6 +599,7 @@ const $i_exportBlocking: Provider = { provide: 'ep:i/export-blocking', useClass: const $i_exportFollowing: Provider = { provide: 'ep:i/export-following', useClass: ep___i_exportFollowing.default }; const $i_exportMute: Provider = { provide: 'ep:i/export-mute', useClass: ep___i_exportMute.default }; const $i_exportNotes: Provider = { provide: 'ep:i/export-notes', useClass: ep___i_exportNotes.default }; +const $i_exportClips: Provider = { provide: 'ep:i/export-clips', useClass: ep___i_exportClips.default }; const $i_exportFavorites: Provider = { provide: 'ep:i/export-favorites', useClass: ep___i_exportFavorites.default }; const $i_exportUserLists: Provider = { provide: 'ep:i/export-user-lists', useClass: ep___i_exportUserLists.default }; const $i_exportAntennas: Provider = { provide: 'ep:i/export-antennas', useClass: ep___i_exportAntennas.default }; @@ -678,6 +690,7 @@ const $notes_userListTimeline: Provider = { provide: 'ep:notes/user-list-timelin const $notes_edit: Provider = { provide: 'ep:notes/edit', useClass: ep___notes_edit.default }; const $notes_versions: Provider = { provide: 'ep:notes/versions', useClass: ep___notes_versions.default }; const $notifications_create: Provider = { provide: 'ep:notifications/create', useClass: ep___notifications_create.default }; +const $notifications_flush: Provider = { provide: 'ep:notifications/flush', useClass: ep___notifications_flush.default }; const $notifications_markAllAsRead: Provider = { provide: 'ep:notifications/mark-all-as-read', useClass: ep___notifications_markAllAsRead.default }; const $notifications_testNotification: Provider = { provide: 'ep:notifications/test-notification', useClass: ep___notifications_testNotification.default }; const $pagePush: Provider = { provide: 'ep:page-push', useClass: ep___pagePush.default }; @@ -750,6 +763,15 @@ const $fetchRss: Provider = { provide: 'ep:fetch-rss', useClass: ep___fetchRss.d const $fetchExternalResources: Provider = { provide: 'ep:fetch-external-resources', useClass: ep___fetchExternalResources.default }; const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention.default }; const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.default }; +const $bubbleGame_register: Provider = { provide: 'ep:bubble-game/register', useClass: ep___bubbleGame_register.default }; +const $bubbleGame_ranking: Provider = { provide: 'ep:bubble-game/ranking', useClass: ep___bubbleGame_ranking.default }; +const $reversi_cancelMatch: Provider = { provide: 'ep:reversi/cancel-match', useClass: ep___reversi_cancelMatch.default }; +const $reversi_games: Provider = { provide: 'ep:reversi/games', useClass: ep___reversi_games.default }; +const $reversi_match: Provider = { provide: 'ep:reversi/match', useClass: ep___reversi_match.default }; +const $reversi_invitations: Provider = { provide: 'ep:reversi/invitations', useClass: ep___reversi_invitations.default }; +const $reversi_showGame: Provider = { provide: 'ep:reversi/show-game', useClass: ep___reversi_showGame.default }; +const $reversi_surrender: Provider = { provide: 'ep:reversi/surrender', useClass: ep___reversi_surrender.default }; +const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep___reversi_verify.default }; @Module({ imports: [ @@ -966,6 +988,7 @@ const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.de $i_exportFollowing, $i_exportMute, $i_exportNotes, + $i_exportClips, $i_exportFavorites, $i_exportUserLists, $i_exportAntennas, @@ -1056,6 +1079,7 @@ const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.de $notes_edit, $notes_versions, $notifications_create, + $notifications_flush, $notifications_markAllAsRead, $notifications_testNotification, $pagePush, @@ -1128,6 +1152,15 @@ const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.de $fetchExternalResources, $retention, $sponsors, + $bubbleGame_register, + $bubbleGame_ranking, + $reversi_cancelMatch, + $reversi_games, + $reversi_match, + $reversi_invitations, + $reversi_showGame, + $reversi_surrender, + $reversi_verify, ], exports: [ $admin_meta, @@ -1338,6 +1371,7 @@ const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.de $i_exportFollowing, $i_exportMute, $i_exportNotes, + $i_exportClips, $i_exportFavorites, $i_exportUserLists, $i_exportAntennas, @@ -1428,7 +1462,9 @@ const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.de $notes_edit, $notes_versions, $notifications_create, + $notifications_flush, $notifications_markAllAsRead, + $notifications_testNotification, $pagePush, $pages_create, $pages_delete, @@ -1497,6 +1533,15 @@ const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.de $fetchExternalResources, $retention, $sponsors, + $bubbleGame_register, + $bubbleGame_ranking, + $reversi_cancelMatch, + $reversi_games, + $reversi_match, + $reversi_invitations, + $reversi_showGame, + $reversi_surrender, + $reversi_verify, ], }) export class EndpointsModule {} diff --git a/packages/backend/src/server/api/GetterService.ts b/packages/backend/src/server/api/GetterService.ts index 2616cbb7610e0ec1e8c1e819a0579c95993a2e71..8643be0f30ca7d6c9b71100f6eaebbe9c6089d6b 100644 --- a/packages/backend/src/server/api/GetterService.ts +++ b/packages/backend/src/server/api/GetterService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/RateLimiterService.ts b/packages/backend/src/server/api/RateLimiterService.ts index 0e644aa091ab9612dde68d0c02a882693f37c9ef..0439cdfe5e033b91ae56bc50f49ad9703f3f2118 100644 --- a/packages/backend/src/server/api/RateLimiterService.ts +++ b/packages/backend/src/server/api/RateLimiterService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index fd247df22a86c89897998e673cccf55254158391..6fbcacbc1172f7ecf683b92b12f8b463e9250934 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -24,7 +24,7 @@ import { UserAuthService } from '@/core/UserAuthService.js'; import { MetaService } from '@/core/MetaService.js'; import { RateLimiterService } from './RateLimiterService.js'; import { SigninService } from './SigninService.js'; -import type { AuthenticationResponseJSON } from '@simplewebauthn/typescript-types'; +import type { AuthenticationResponseJSON } from '@simplewebauthn/types'; import type { FastifyReply, FastifyRequest } from 'fastify'; @Injectable() diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts index 98e902700680fe2748d43c93fa1698239b95426a..714e56e8c3fad6d2138cfcbe575d7b3f568fe823 100644 --- a/packages/backend/src/server/api/SigninService.ts +++ b/packages/backend/src/server/api/SigninService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index 63379c88787bfce1a4bc8664d3b84b0e13a00a18..9c221314ac0885bc5013737e3ad648b501238b79 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -70,6 +70,7 @@ export class SignupApiService { 'hcaptcha-response'?: string; 'g-recaptcha-response'?: string; 'turnstile-response'?: string; + 'm-captcha-response'?: string; } }>, reply: FastifyReply, @@ -87,6 +88,12 @@ export class SignupApiService { }); } + if (instance.enableMcaptcha && instance.mcaptchaSecretKey && instance.mcaptchaSitekey && instance.mcaptchaInstanceUrl) { + await this.captchaService.verifyMcaptcha(instance.mcaptchaSecretKey, instance.mcaptchaSitekey, instance.mcaptchaInstanceUrl, body['m-captcha-response']).catch(err => { + throw new FastifyReplyError(400, err); + }); + } + if (instance.enableRecaptcha && instance.recaptchaSecretKey) { await this.captchaService.verifyRecaptcha(instance.recaptchaSecretKey, body['g-recaptcha-response']).catch(err => { throw new FastifyReplyError(400, err); @@ -169,12 +176,12 @@ export class SignupApiService { } if (instance.emailRequiredForSignup) { - if (await this.usersRepository.exist({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) { + if (await this.usersRepository.exists({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) { throw new FastifyReplyError(400, 'DUPLICATED_USERNAME'); } // Check deleted username duplication - if (await this.usedUsernamesRepository.exist({ where: { username: username.toLowerCase() } })) { + if (await this.usedUsernamesRepository.exists({ where: { username: username.toLowerCase() } })) { throw new FastifyReplyError(400, 'USED_USERNAME'); } @@ -253,7 +260,7 @@ export class SignupApiService { }); const res = await this.userEntityService.pack(account, account, { - detail: true, + schema: 'MeDetailed', includeSecrets: true, }); diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index 3b387d92ca1c7bf21e384a63307e176a8f7b39c6..b8f448477b06ba0d44fb0cfe425aef282594b406 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoint-base.ts b/packages/backend/src/server/api/endpoint-base.ts index d5279faa1c327c4db51636a165eaedbace8d5205..e061aa3a8e38d3361963fa1a9b232cf66520a772 100644 --- a/packages/backend/src/server/api/endpoint-base.ts +++ b/packages/backend/src/server/api/endpoint-base.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index f82bf257fcd41f4633587ef6483c8e44eb8cc362..f83d2cacff910369ac3c3a380a72bef4b3dfa69b 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -1,11 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import type { Schema } from '@/misc/json-schema.js'; import { permissions } from 'misskey-js'; -import { RolePolicies } from '@/core/RoleService.js'; +import type { KeyOf, Schema } from '@/misc/json-schema.js'; import * as ep___admin_meta from './endpoints/admin/meta.js'; import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js'; @@ -215,6 +214,7 @@ import * as ep___i_exportBlocking from './endpoints/i/export-blocking.js'; import * as ep___i_exportFollowing from './endpoints/i/export-following.js'; import * as ep___i_exportMute from './endpoints/i/export-mute.js'; import * as ep___i_exportNotes from './endpoints/i/export-notes.js'; +import * as ep___i_exportClips from './endpoints/i/export-clips.js'; import * as ep___i_exportFavorites from './endpoints/i/export-favorites.js'; import * as ep___i_exportUserLists from './endpoints/i/export-user-lists.js'; import * as ep___i_exportAntennas from './endpoints/i/export-antennas.js'; @@ -305,6 +305,7 @@ import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeli import * as ep___notes_edit from './endpoints/notes/edit.js'; import * as ep___notes_versions from './endpoints/notes/versions.js'; import * as ep___notifications_create from './endpoints/notifications/create.js'; +import * as ep___notifications_flush from './endpoints/notifications/flush.js'; import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js'; import * as ep___notifications_testNotification from './endpoints/notifications/test-notification.js'; import * as ep___pagePush from './endpoints/page-push.js'; @@ -377,6 +378,15 @@ import * as ep___fetchRss from './endpoints/fetch-rss.js'; import * as ep___fetchExternalResources from './endpoints/fetch-external-resources.js'; import * as ep___retention from './endpoints/retention.js'; import * as ep___sponsors from './endpoints/sponsors.js'; +import * as ep___bubbleGame_register from './endpoints/bubble-game/register.js'; +import * as ep___bubbleGame_ranking from './endpoints/bubble-game/ranking.js'; +import * as ep___reversi_cancelMatch from './endpoints/reversi/cancel-match.js'; +import * as ep___reversi_games from './endpoints/reversi/games.js'; +import * as ep___reversi_match from './endpoints/reversi/match.js'; +import * as ep___reversi_invitations from './endpoints/reversi/invitations.js'; +import * as ep___reversi_showGame from './endpoints/reversi/show-game.js'; +import * as ep___reversi_surrender from './endpoints/reversi/surrender.js'; +import * as ep___reversi_verify from './endpoints/reversi/verify.js'; const eps = [ ['admin/meta', ep___admin_meta], @@ -587,6 +597,7 @@ const eps = [ ['i/export-following', ep___i_exportFollowing], ['i/export-mute', ep___i_exportMute], ['i/export-notes', ep___i_exportNotes], + ['i/export-clips', ep___i_exportClips], ['i/export-favorites', ep___i_exportFavorites], ['i/export-user-lists', ep___i_exportUserLists], ['i/export-antennas', ep___i_exportAntennas], @@ -677,6 +688,7 @@ const eps = [ ['notes/edit', ep___notes_edit], ['notes/versions', ep___notes_versions], ['notifications/create', ep___notifications_create], + ['notifications/flush', ep___notifications_flush], ['notifications/mark-all-as-read', ep___notifications_markAllAsRead], ['notifications/test-notification', ep___notifications_testNotification], ['page-push', ep___pagePush], @@ -749,6 +761,15 @@ const eps = [ ['fetch-external-resources', ep___fetchExternalResources], ['retention', ep___retention], ['sponsors', ep___sponsors], + ['bubble-game/register', ep___bubbleGame_register], + ['bubble-game/ranking', ep___bubbleGame_ranking], + ['reversi/cancel-match', ep___reversi_cancelMatch], + ['reversi/games', ep___reversi_games], + ['reversi/match', ep___reversi_match], + ['reversi/invitations', ep___reversi_invitations], + ['reversi/show-game', ep___reversi_showGame], + ['reversi/surrender', ep___reversi_surrender], + ['reversi/verify', ep___reversi_verify], ]; interface IEndpointMetaBase { @@ -782,7 +803,7 @@ interface IEndpointMetaBase { */ readonly requireAdmin?: boolean; - readonly requireRolePolicy?: keyof RolePolicies; + readonly requireRolePolicy?: KeyOf<'RolePolicies'>; /** * 引ã£è¶Šã—済ã¿ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã‚ˆã‚‹ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’ç¦æ¢ã™ã‚‹ã‹ diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts index 3484d6707a3650de98066cc86954b064bcff97e5..cf3f257ca692b83f786bc3187b5e63e50cb7b7d8 100644 --- a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts +++ b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -62,17 +62,17 @@ export const meta = { reporter: { type: 'object', nullable: false, optional: false, - ref: 'User', + ref: 'UserDetailedNotMe', }, targetUser: { type: 'object', nullable: false, optional: false, - ref: 'User', + ref: 'UserDetailedNotMe', }, assignee: { type: 'object', nullable: true, optional: true, - ref: 'User', + ref: 'UserDetailedNotMe', }, }, }, diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts index f54d567fff9ccad03d2ae3c096bb9a1c88398ffa..a7e8a3b018cd30cd45dece1d17abcb407a5a68e7 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -9,8 +9,10 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UsersRepository } from '@/models/_.js'; import { SignupService } from '@/core/SignupService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { InstanceActorService } from '@/core/InstanceActorService.js'; import { localUsernameSchema, passwordSchema } from '@/models/User.js'; import { DI } from '@/di-symbols.js'; +import { Packed } from '@/misc/json-schema.js'; export const meta = { tags: ['admin'], @@ -18,7 +20,7 @@ export const meta = { res: { type: 'object', optional: false, nullable: false, - ref: 'User', + ref: 'MeDetailed', properties: { token: { type: 'string', @@ -45,13 +47,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private userEntityService: UserEntityService, private signupService: SignupService, + private instanceActorService: InstanceActorService, ) { super(meta, paramDef, async (ps, _me, token) => { const me = _me ? await this.usersRepository.findOneByOrFail({ id: _me.id }) : null; - const noUsers = (await this.usersRepository.countBy({ - host: IsNull(), - })) === 0; - if ((!noUsers && !me?.isRoot) || token !== null) throw new Error('access denied'); + const realUsers = await this.instanceActorService.realLocalUsersPresent(); + if ((realUsers && !me?.isRoot) || token !== null) throw new Error('access denied'); const { account, secret } = await this.signupService.signup({ username: ps.username, @@ -60,11 +61,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }); const res = await this.userEntityService.pack(account, account, { - detail: true, + schema: 'MeDetailed', includeSecrets: true, - }); + }) as Packed<'MeDetailed'> & { token: string }; - (res as any).token = secret; + res.token = secret; return res; }); diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts index 52d8c8ce18f409bcbaa8ae3987d228ab932c0075..4074e416b8a34072843f0e302c2f0af7438b05fa 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts b/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts index 93673453d620ba35ab6836f9d4e7ff283c0bb19b..12cd5cf29550284da6a7f4301c4fa39c67dcf549 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -27,7 +27,7 @@ export const meta = { res: { type: 'object', optional: false, nullable: false, - ref: 'User', + ref: 'UserDetailedNotMe', }, } as const; @@ -58,7 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } const res = await this.userEntityService.pack(profile.user!, null, { - detail: true, + schema: 'UserDetailedNotMe', }); return res; diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts index 041b10f9f79cba13dc6297dd66b64bb8352a8594..1e7a9fb3ecf33d80cf4580316cef18daa732cc75 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts index 5b18b347d3306a3d6cbb04db33f21b6c6f666519..501e13c6a7175e04fad2731b00fa990b42a263b7 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts index 586c1f44db4ce016c074d5d7b7a673947ec5802b..6406709cdab3a009326a1ac8b90db69af5ec6a87 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/ad/update.ts b/packages/backend/src/server/api/endpoints/admin/ad/update.ts index bf96e44b0c74b6f98cab8836877c5fd25ce6e0cc..62358457ff769626ab1b0b944a1763c9508b6227 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts index c9df70c76b7c32fb6ca4cc298e3002fdbbb35a09..2dae1df87d46572a5632462e5023a873cafc7d8f 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts index 939333345ea47e0508b90e70cdb0804045ef25bd..6d1e1b0a10e84a99bc03bff27d9ca97fa0df5825 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts index 429b1385991a8037fd16121bf74f7fb6f1a28c3d..87eaad31a365ac676bfb2693d9ca9a1235645500 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts index db6db8356df43a8750d410b889cead27583c3af6..6fce6e4e0a47412e41a98964c82a6b20061c1988 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts index 4ac74253ccfeb37ebcd532b01829c0a726112c99..fd213098188d290201b303659c91c061a11346bb 100644 --- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/delete.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/delete.ts index 88977f801afe3cf5d7e7d076d2f7ecf6dac79b1f..3a5673d99d5b8a1bf2643af246014c14b229aa3e 100644 --- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts index 33122c3eef01ca7116b699846791183338eec102..aee90023e14512ec2c5f2ccfcbc7e23732a2fe6a 100644 --- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts index 6211345f963541c01e8750257eae1975e9f57aa9..34b3b5a11f50aaaecd8129ec3122ccfa5a9cf23a 100644 --- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/delete-account.ts b/packages/backend/src/server/api/endpoints/admin/delete-account.ts index 2c82c2879d58eee0faf699018aed9c3afe83209c..b6f0f22d607c11b1f357dcafdf3e22c8d7aa655d 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-account.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -15,9 +15,6 @@ export const meta = { requireCredential: true, requireAdmin: true, kind: 'write:admin:delete-account', - - res: { - }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts index 7d33065f2e5d073e616709fe020b233779c87609..d8341b3ad78cfaab6dc0272fe4439a8fb329a416 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts index af2bb6b1caef8055fcfed8a2755b2b8c50cee1bf..d420a929bd5544fb65a3c7d6fe8eec08d96cd44e 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts index a3b221284b7aa03e99a85b20d7a42d81040ebb25..d612572e2ee4525f0ed48fe7c4426f11bee08720 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts index 37fa439bcf0a8a2a9a070f5aa1bdfa6657e2aabb..915d777e773fc20ab043e8c015f8f4e5b4dd19d2 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts index 3aeb3e45e3369a9c29f592b617e65fba30d36f9e..459d8880fad372928a40967994bd5a72fee687b0 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -84,6 +84,24 @@ export const meta = { properties: { type: 'object', optional: false, nullable: false, + properties: { + width: { + type: 'number', + optional: true, nullable: false, + }, + height: { + type: 'number', + optional: true, nullable: false, + }, + orientation: { + type: 'number', + optional: true, nullable: false, + }, + avgColor: { + type: 'string', + optional: true, nullable: false, + }, + }, }, storedInternal: { type: 'boolean', diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts index 1cd8125c5262aacae3152be4e288f265ddb852da..a30a080e591c1630469cd639fb8208d7f09effd7 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 0868e249482c5e5d4bf29ad49efd1980f4397807..767e517b803737dbdc8cdaf5877f99c0b216550f 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -31,7 +31,10 @@ export const meta = { }, }, - ref: 'EmojiDetailed', + res: { + type: 'object', + ref: 'EmojiDetailed', + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 611b64be07c2be1b1c0c5f8004a5c92998e5f7a3..29af7598ed739cda9e7689b1f086f10e7132e074 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts index 450695984a7b52a39789d623652f214dc8ba9835..cec9f700c3afadebf0bbfbf222a78dd392e00ab4 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index e1e6e7c2c4a7e59dd282c817199609d23938436b..50c45b6ac5d98b9508c402899f8a2c89f695868d 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts index 208616c0ac35d828019c125673bea1d2ee3a7410..8e5f69c89429e183d2080a3614f64e03a082a8e7 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts index f3e0c1ef1f27738eec22e2c28d8a9c9f7b10f885..e423f440d0156ca98626604cf764d5a68262ed87 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -98,11 +98,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } if (ps.query) { - q.andWhere('emoji.name like :query', { query: '%' + sqlLikeEscape(ps.query) + '%' }); + q.andWhere('emoji.name like :query', { query: '%' + sqlLikeEscape(ps.query) + '%' }) + .orderBy('length(emoji.name)', 'ASC'); } const emojis = await q - .orderBy('emoji.id', 'DESC') + .addOrderBy('emoji.id', 'DESC') .limit(ps.limit) .getMany(); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index 59e87253f6fee3c811fbc75ac1b405924ea7e96c..53810d1d16e1db4469ceffa3312bb073b54dc6d0 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -91,7 +91,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- //q.andWhere('emoji.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }); //const emojis = await q.limit(ps.limit).getMany(); - emojis = await q.getMany(); + emojis = await q.orderBy('length(emoji.name)', 'ASC').getMany(); const queryarry = ps.query.match(/\:([a-z0-9_]*)\:/g); if (queryarry) { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts index 26dd43e926296e47ff83715a2bd960ae27cb74d2..0fa119eabeab8d3240354dd7c3da68a6295b8e20 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts index 18961976f9c6b3a310938789f42538947d5044a3..d9ee18699c429c1566c4d78692a64ff823ac0cfd 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts index c680f2e2d4937601b348231194a213f142d12f9f..dc25df276780854e6feba04aa6b1de71128579c7 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts index 47c692b6137fe015f4492bb2118e62b258c1dd29..4ba99faab76d431769abd227f16c3811b6b48d9b 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index 550bb0052bbaae641403c1b6382f9fb369a8a51f..22609a16a39a6d351c52f48b580988912e4777e8 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -57,7 +57,10 @@ export const paramDef = { type: 'string', } }, }, - required: ['id', 'name', 'aliases'], + anyOf: [ + { required: ['id'] }, + { required: ['name'] }, + ], } as const; @Injectable() @@ -70,27 +73,33 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- ) { super(meta, paramDef, async (ps, me) => { let driveFile; - if (ps.fileId) { driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); } - const emoji = await this.customEmojiService.getEmojiById(ps.id); - if (emoji != null) { - if (ps.name !== emoji.name) { + + let emojiId; + if (ps.id) { + emojiId = ps.id; + const emoji = await this.customEmojiService.getEmojiById(ps.id); + if (!emoji) throw new ApiError(meta.errors.noSuchEmoji); + if (ps.name && (ps.name !== emoji.name)) { const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name); if (isDuplicate) throw new ApiError(meta.errors.sameNameEmojiExists); } } else { - throw new ApiError(meta.errors.noSuchEmoji); + if (!ps.name) throw new Error('Invalid Params unexpectedly passed. This is a BUG. Please report it to the development team.'); + const emoji = await this.customEmojiService.getEmojiByName(ps.name); + if (!emoji) throw new ApiError(meta.errors.noSuchEmoji); + emojiId = emoji.id; } - await this.customEmojiService.update(ps.id, { + await this.customEmojiService.update(emojiId, { driveFile, name: ps.name, - category: ps.category ?? null, + category: ps.category, aliases: ps.aliases, - license: ps.license ?? null, + license: ps.license, isSensitive: ps.isSensitive, localOnly: ps.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction, diff --git a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts index 57612850b4da843e568669be3a8259ca6e238edd..4a54c26009f6445eef7717b2c7cbfc0c233c9660 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts index 0d061c685f740fccfeaf138d18c1e49d589440a3..556e291025d9dba04bc6718e9ea3c0bd10410de5 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts index c15fb83454e755ace7575bb17817ff7dfa47dc70..9e93310746c95900fdc1f7640dd603a726041963 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts index 188ab69532cb6f0057407c93c3660f69dffeeb5a..4ababae9f2761253afbee7510063b0d782508835 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -25,6 +25,7 @@ export const paramDef = { host: { type: 'string' }, isSuspended: { type: 'boolean' }, isNSFW: { type: 'boolean' }, + moderationNote: { type: 'string' }, }, required: ['host'], } as const; @@ -46,29 +47,32 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw new Error('instance not found'); } - if (ps.isSuspended != null) { - await this.federatedInstanceService.update(instance.id, { - isSuspended: ps.isSuspended, - }); + await this.federatedInstanceService.update(instance.id, { + isSuspended: ps.isSuspended, + isNSFW: ps.isNSFW, + moderationNote: ps.moderationNote, + }); - if (instance.isSuspended !== ps.isSuspended) { - if (ps.isSuspended) { - this.moderationLogService.log(me, 'suspendRemoteInstance', { - id: instance.id, - host: instance.host, - }); - } else { - this.moderationLogService.log(me, 'unsuspendRemoteInstance', { - id: instance.id, - host: instance.host, - }); - } + if (ps.isSuspended != null && instance.isSuspended !== ps.isSuspended) { + if (ps.isSuspended) { + this.moderationLogService.log(me, 'suspendRemoteInstance', { + id: instance.id, + host: instance.host, + }); + } else { + this.moderationLogService.log(me, 'unsuspendRemoteInstance', { + id: instance.id, + host: instance.host, + }); } } - if (ps.isNSFW != null) { - await this.federatedInstanceService.update(instance.id, { - isNSFW: ps.isNSFW, + if (ps.moderationNote != null && instance.moderationNote !== ps.moderationNote) { + this.moderationLogService.log(me, 'updateRemoteInstanceNote', { + id: instance.id, + host: instance.host, + before: instance.moderationNote, + after: ps.moderationNote, }); } }); diff --git a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts index 0b50212119d7a9dcbeb2d7aa8dbffc79ffabba4e..90a3fa02003ab29e79d2aef54a5ce97501b9e737 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts index 0d44b288cb82fefd217a2b40f78dc0651953c3bf..eb85fca1790587501c44cb8587fa3429fbe36429 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -18,6 +18,18 @@ export const meta = { res: { type: 'object', optional: false, nullable: false, + additionalProperties: { + type: 'object', + properties: { + count: { + type: 'number', + }, + size: { + type: 'number', + }, + }, + required: ['count', 'size'], + }, example: { migrations: { count: 66, diff --git a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts index 1b437f718baafa1205a4e280babe92c0ff83492a..b7781b8c9987fe4ecd975d19be8158f2f6ef2dc9 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/invite/create.ts b/packages/backend/src/server/api/endpoints/admin/invite/create.ts index 396b84623f2166eba2fc933f7964c4b88d6b9270..0f551e1ba2c4f12b77af4fda2e003e28af7588f7 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/invite/list.ts b/packages/backend/src/server/api/endpoints/admin/invite/list.ts index d293dcadc6215684585e3bafd79ba940e52da79f..e33a9a1aecbe5e002c4a4d4846d8ef80d798be19 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 4fd2a568ad817cfb10d4278c6f3ee364bab116aa..34454c276e2898cb0765b20290ac80ea0a616521 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -45,6 +45,18 @@ export const meta = { type: 'string', optional: false, nullable: true, }, + enableMcaptcha: { + type: 'boolean', + optional: false, nullable: false, + }, + mcaptchaSiteKey: { + type: 'string', + optional: false, nullable: true, + }, + mcaptchaInstanceUrl: { + type: 'string', + optional: false, nullable: true, + }, enableRecaptcha: { type: 'boolean', optional: false, nullable: false, @@ -148,6 +160,13 @@ export const meta = { type: 'string', }, }, + prohibitedWords: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + }, + }, bannedEmailDomains: { type: 'array', optional: true, nullable: false, @@ -174,6 +193,10 @@ export const meta = { type: 'string', optional: false, nullable: true, }, + mcaptchaSecretKey: { + type: 'string', + optional: false, nullable: true, + }, recaptchaSecretKey: { type: 'string', optional: false, nullable: true, @@ -299,6 +322,18 @@ export const meta = { type: 'string', optional: false, nullable: true, }, + enableTruemailApi: { + type: 'boolean', + optional: false, nullable: false, + }, + truemailInstance: { + type: 'string', + optional: false, nullable: true, + }, + truemailAuthKey: { + type: 'string', + optional: false, nullable: true, + }, enableChartsForRemoteUser: { type: 'boolean', optional: false, nullable: false, @@ -367,6 +402,14 @@ export const meta = { type: 'boolean', optional: false, nullable: false, }, + deeplFreeMode: { + type: 'boolean', + optional: false, nullable: false, + }, + deeplFreeInstance: { + type: 'string', + optional: false, nullable: true, + }, defaultDarkTheme: { type: 'string', optional: false, nullable: true, @@ -387,6 +430,10 @@ export const meta = { type: 'string', optional: false, nullable: true, }, + donationUrl: { + type: 'string', + optional: false, nullable: true, + }, maintainerEmail: { type: 'string', optional: false, nullable: true, @@ -413,7 +460,7 @@ export const meta = { }, repositoryUrl: { type: 'string', - optional: false, nullable: false, + optional: false, nullable: true, }, summalyProxy: { type: 'string', @@ -470,12 +517,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- repositoryUrl: instance.repositoryUrl, feedbackUrl: instance.feedbackUrl, impressumUrl: instance.impressumUrl, + donationUrl: instance.donationUrl, privacyPolicyUrl: instance.privacyPolicyUrl, disableRegistration: instance.disableRegistration, emailRequiredForSignup: instance.emailRequiredForSignup, approvalRequiredForSignup: instance.approvalRequiredForSignup, enableHcaptcha: instance.enableHcaptcha, hcaptchaSiteKey: instance.hcaptchaSiteKey, + enableMcaptcha: instance.enableMcaptcha, + mcaptchaSiteKey: instance.mcaptchaSitekey, + mcaptchaInstanceUrl: instance.mcaptchaInstanceUrl, enableRecaptcha: instance.enableRecaptcha, recaptchaSiteKey: instance.recaptchaSiteKey, enableTurnstile: instance.enableTurnstile, @@ -505,9 +556,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- blockedHosts: instance.blockedHosts, silencedHosts: instance.silencedHosts, sensitiveWords: instance.sensitiveWords, + prohibitedWords: instance.prohibitedWords, preservedUsernames: instance.preservedUsernames, bubbleInstances: instance.bubbleInstances, hcaptchaSecretKey: instance.hcaptchaSecretKey, + mcaptchaSecretKey: instance.mcaptchaSecretKey, recaptchaSecretKey: instance.recaptchaSecretKey, turnstileSecretKey: instance.turnstileSecretKey, sensitiveMediaDetection: instance.sensitiveMediaDetection, @@ -539,10 +592,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- objectStorageS3ForcePathStyle: instance.objectStorageS3ForcePathStyle, deeplAuthKey: instance.deeplAuthKey, deeplIsPro: instance.deeplIsPro, + deeplFreeMode: instance.deeplFreeMode, + deeplFreeInstance: instance.deeplFreeInstance, enableIpLogging: instance.enableIpLogging, enableActiveEmailValidation: instance.enableActiveEmailValidation, enableVerifymailApi: instance.enableVerifymailApi, verifymailAuthKey: instance.verifymailAuthKey, + enableTruemailApi: instance.enableTruemailApi, + truemailInstance: instance.truemailInstance, + truemailAuthKey: instance.truemailAuthKey, enableChartsForRemoteUser: instance.enableChartsForRemoteUser, enableChartsForFederatedInstances: instance.enableChartsForFederatedInstances, enableServerMachineStats: instance.enableServerMachineStats, diff --git a/packages/backend/src/server/api/endpoints/admin/promo/create.ts b/packages/backend/src/server/api/endpoints/admin/promo/create.ts index ab69dfba9635493927eac23672a7c3021d569e49..1d32c6cc0074f52e8956b48e2397b942c34f1182 100644 --- a/packages/backend/src/server/api/endpoints/admin/promo/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/promo/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -55,7 +55,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw e; }); - const exist = await this.promoNotesRepository.exist({ where: { noteId: note.id } }); + const exist = await this.promoNotesRepository.exists({ where: { noteId: note.id } }); if (exist) { throw new ApiError(meta.errors.alreadyPromoted); diff --git a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts index 9912043c8b9fd365ea43144674e5b82984d50c34..3f7df0e63d385a20ef50cb4a6906b7969dee8e45 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts index 84739091038f8c59f692a2ba0520aba2264dd9e0..7a3410ffa7519ca0af75458abe896da12c1a091f 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts index 19f7cb85c05835b16f90568328f82d37d0ad02c7..305ae1af1da5d404ca9b745b6291e645c274806e 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/queue/promote.ts b/packages/backend/src/server/api/endpoints/admin/queue/promote.ts index d06780e044255e6f34c9663337361734bf2cd0a8..7502d4e1f7560b28f239d707d2c3d4e7a2f19ca5 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/promote.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/promote.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts index 189690b70356c17998436ec17e7c26b17656c75b..9694b3fa40a3efccc6e8979051df1ae9caa017e0 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/relays/add.ts b/packages/backend/src/server/api/endpoints/admin/relays/add.ts index d55dff7b0c0c7a45cc8b252dacade0c362508ce5..3d7bc4567e7b342ce464712523fc041f9b19843a 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/add.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/relays/list.ts b/packages/backend/src/server/api/endpoints/admin/relays/list.ts index 61ea287bff27181745fae77466406130c12a2822..587d5c3b03f6b052ec18ac939c9efabe3bb1576e 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts index 8a6dd4e1529f2deea1a54ca713157a66e74ef646..1f6e773cd4a5e5ae0e820794f9ab5bafdc5d86e5 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/reset-password.ts b/packages/backend/src/server/api/endpoints/admin/reset-password.ts index 506cd609ae5577decbfad402bb6b49c2ddc71c1d..828dbae7126d1a72b17ba38a3624803cc3356045 100644 --- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts index 26c4038b980246320aa9ffdc341b2f746a60545a..8b0456068b8939da1ecb9ad663869824d9fb1d98 100644 --- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts +++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/roles/assign.ts b/packages/backend/src/server/api/endpoints/admin/roles/assign.ts index 8eb3d2bf59ba18026071adcc7b19b66763e56ee7..b6c79537811f26c0985bc56c2cecc1779b2a793b 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/assign.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/assign.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/roles/create.ts b/packages/backend/src/server/api/endpoints/admin/roles/create.ts index de23d2fb11699d00ac0d2456b1bf0a958a7e2248..e0c02f7a5d5b2dbb3d6b5b31978144f8b986ddf8 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/roles/delete.ts b/packages/backend/src/server/api/endpoints/admin/roles/delete.ts index 9e2968e317eff869c181ac5dbb0c31e14abd6b19..638e2b15b92c7898a23177917f0a143c07ece5a2 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/roles/list.ts b/packages/backend/src/server/api/endpoints/admin/roles/list.ts index d3d1a10a6933a11221f21ccff23a770e62fa1213..333fac6aa6994da6c5b6c81c6420b3199c9264bd 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/roles/show.ts b/packages/backend/src/server/api/endpoints/admin/roles/show.ts index ad4345e5a53016f5ca822aeaf08fc6ab48962de6..13e5cbb995c81e20c022f5a9c271edef1122327a 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/show.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts b/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts index c11265252c02f1920695ff544f939745e763254f..e7da3384b16c5214d8f1ea93bd89ccf031c8dd64 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts index 203f749a6e7ba8b1d910b29dd83331d2095dab4a..d7209965dba1d91d356be095292b74e254887146 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update.ts b/packages/backend/src/server/api/endpoints/admin/roles/update.ts index 74d5aae5d83cbfe346a0e87c97af85c964cbbb05..5242e0be2f96154d18010caa2f388824f773bfc9 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts index 66f4d9d26b45bdb80c32acab39cd57da54a937ca..45758d4f50f42bb53946257f4424a4530fbca74e 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/users.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -17,7 +17,7 @@ export const meta = { tags: ['admin', 'role', 'users'], requireCredential: false, - requireAdmin: true, + requireModerator: true, kind: 'read:admin:roles', errors: { @@ -40,7 +40,7 @@ export const meta = { }, required: ['id', 'createdAt', 'user'], }, - } + }, } as const; export const paramDef = { @@ -92,7 +92,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- return await Promise.all(assigns.map(async assign => ({ id: assign.id, createdAt: this.idService.parse(assign.id).date.toISOString(), - user: await this.userEntityService.pack(assign.user!, me, { detail: true }), + user: await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }), expiresAt: assign.expiresAt?.toISOString() ?? null, }))); }); diff --git a/packages/backend/src/server/api/endpoints/admin/send-email.ts b/packages/backend/src/server/api/endpoints/admin/send-email.ts index d20aee656ca2239f8c4ab8ca624cb27d02fba4fd..f01a7778a862bea32f92d80ebe68dd895a8e6fd4 100644 --- a/packages/backend/src/server/api/endpoints/admin/send-email.ts +++ b/packages/backend/src/server/api/endpoints/admin/send-email.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/server-info.ts b/packages/backend/src/server/api/endpoints/admin/server-info.ts index 374712f57d168b4cb3ded83f42180d70c595867e..80b6a4d32e014d3bddf8166d6194d29804293751 100644 --- a/packages/backend/src/server/api/endpoints/admin/server-info.ts +++ b/packages/backend/src/server/api/endpoints/admin/server-info.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts index f3601be9bbbd2f3196c86357b1bde74881973863..58c5f1f60a5a70c062d301065eba461a1e540774 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -50,7 +50,7 @@ export const meta = { user: { type: 'object', optional: false, nullable: false, - ref: 'UserDetailed', + ref: 'UserDetailedNotMe', }, }, }, diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index ea22f9eeb90bb1bd318dda4727e9dbee8164c442..a7ca7f9547fa8665d82466e2a54cc827763aa9e3 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -10,6 +10,7 @@ import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; import { IdService } from '@/core/IdService.js'; +import { notificationRecieveConfig } from '@/models/json-schema/user.js'; export const meta = { tags: ['admin'], @@ -21,6 +22,157 @@ export const meta = { res: { type: 'object', nullable: false, optional: false, + properties: { + email: { + type: 'string', + optional: false, nullable: true, + }, + emailVerified: { + type: 'boolean', + optional: false, nullable: false, + }, + autoAcceptFollowed: { + type: 'boolean', + optional: false, nullable: false, + }, + noCrawle: { + type: 'boolean', + optional: false, nullable: false, + }, + preventAiLearning: { + type: 'boolean', + optional: false, nullable: false, + }, + alwaysMarkNsfw: { + type: 'boolean', + optional: false, nullable: false, + }, + autoSensitive: { + type: 'boolean', + optional: false, nullable: false, + }, + carefulBot: { + type: 'boolean', + optional: false, nullable: false, + }, + injectFeaturedNote: { + type: 'boolean', + optional: false, nullable: false, + }, + receiveAnnouncementEmail: { + type: 'boolean', + optional: false, nullable: false, + }, + mutedWords: { + type: 'array', + optional: false, nullable: false, + items: { + anyOf: [ + { + type: 'string', + }, + { + type: 'array', + items: { + type: 'string', + }, + }, + ], + }, + }, + mutedInstances: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + }, + }, + notificationRecieveConfig: { + type: 'object', + optional: false, nullable: false, + properties: { + note: { optional: true, ...notificationRecieveConfig }, + follow: { optional: true, ...notificationRecieveConfig }, + mention: { optional: true, ...notificationRecieveConfig }, + reply: { optional: true, ...notificationRecieveConfig }, + renote: { optional: true, ...notificationRecieveConfig }, + quote: { optional: true, ...notificationRecieveConfig }, + reaction: { optional: true, ...notificationRecieveConfig }, + pollEnded: { optional: true, ...notificationRecieveConfig }, + receiveFollowRequest: { optional: true, ...notificationRecieveConfig }, + followRequestAccepted: { optional: true, ...notificationRecieveConfig }, + roleAssigned: { optional: true, ...notificationRecieveConfig }, + achievementEarned: { optional: true, ...notificationRecieveConfig }, + app: { optional: true, ...notificationRecieveConfig }, + test: { optional: true, ...notificationRecieveConfig }, + }, + }, + isModerator: { + type: 'boolean', + optional: false, nullable: false, + }, + isSilenced: { + type: 'boolean', + optional: false, nullable: false, + }, + isSuspended: { + type: 'boolean', + optional: false, nullable: false, + }, + isHibernated: { + type: 'boolean', + optional: false, nullable: false, + }, + lastActiveDate: { + type: 'string', + optional: false, nullable: true, + }, + moderationNote: { + type: 'string', + optional: false, nullable: false, + }, + signins: { + type: 'array', + optional: false, nullable: false, + items: { + ref: 'Signin', + }, + }, + policies: { + type: 'object', + optional: false, nullable: false, + ref: 'RolePolicies', + }, + roles: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + ref: 'Role', + }, + }, + roleAssigns: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + properties: { + createdAt: { + type: 'string', + optional: false, nullable: false, + }, + expiresAt: { + type: 'string', + optional: false, nullable: true, + }, + roleId: { + type: 'string', + optional: false, nullable: false, + }, + }, + }, + }, + }, }, } as const; @@ -91,7 +243,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- isSilenced: isSilenced, isSuspended: user.isSuspended, isHibernated: user.isHibernated, - lastActiveDate: user.lastActiveDate, + lastActiveDate: user.lastActiveDate ? user.lastActiveDate.toISOString() : null, moderationNote: profile.moderationNote ?? '', signins, policies: await this.roleService.getUserPolicies(user.id), diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts index 6267fb97b2cb750ee475cf51204e9dd69f6102bf..685da928e3566fa2094df23e97151531e6b4d358 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -115,7 +115,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const users = await query.getMany(); - return await this.userEntityService.packMany(users, me, { detail: true }); + return await this.userEntityService.packMany(users, me, { schema: 'UserDetailed' }); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts index a26fa81c1329d33cbe2c1109bd337feaf399b98b..8a946405cc0c299f495a2b6af09ed194ab797a1e 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/unset-user-avatar.ts b/packages/backend/src/server/api/endpoints/admin/unset-user-avatar.ts index 8b22fad1d474aa5004142b5e978ec8667850525b..ddab6f3a9d567ed2df7706943ef684c385d3adca 100644 --- a/packages/backend/src/server/api/endpoints/admin/unset-user-avatar.ts +++ b/packages/backend/src/server/api/endpoints/admin/unset-user-avatar.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/unset-user-banner.ts b/packages/backend/src/server/api/endpoints/admin/unset-user-banner.ts index 5ec359c0ef931ea58962dedd60c632d50325eb9c..e16dad719ce13a9e3b9bb1d10ea203e396ce5d38 100644 --- a/packages/backend/src/server/api/endpoints/admin/unset-user-banner.ts +++ b/packages/backend/src/server/api/endpoints/admin/unset-user-banner.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts index 9c896f0e64785261eee611ecdb68612198c980ad..2c2b1bf6f55acf5303ee836135955f2666912bce 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 5c916fe340501254e311f182b1b478c9a3ecd37a..7fea7d969e86a7fde6d1c92c8e68bbdf1ac7bf09 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -41,6 +41,11 @@ export const paramDef = { type: 'string', }, }, + prohibitedWords: { + type: 'array', nullable: true, items: { + type: 'string', + }, + }, themeColor: { type: 'string', nullable: true, pattern: '^#[0-9a-fA-F]{6}$' }, mascotImageUrl: { type: 'string', nullable: true }, bannerUrl: { type: 'string', nullable: true }, @@ -65,6 +70,10 @@ export const paramDef = { enableHcaptcha: { type: 'boolean' }, hcaptchaSiteKey: { type: 'string', nullable: true }, hcaptchaSecretKey: { type: 'string', nullable: true }, + enableMcaptcha: { type: 'boolean' }, + mcaptchaSiteKey: { type: 'string', nullable: true }, + mcaptchaInstanceUrl: { type: 'string', nullable: true }, + mcaptchaSecretKey: { type: 'string', nullable: true }, enableRecaptcha: { type: 'boolean' }, recaptchaSiteKey: { type: 'string', nullable: true }, recaptchaSecretKey: { type: 'string', nullable: true }, @@ -87,6 +96,8 @@ export const paramDef = { summalyProxy: { type: 'string', nullable: true }, deeplAuthKey: { type: 'string', nullable: true }, deeplIsPro: { type: 'boolean' }, + deeplFreeMode: { type: 'boolean' }, + deeplFreeInstance: { type: 'string', nullable: true }, enableEmail: { type: 'boolean' }, email: { type: 'string', nullable: true }, smtpSecure: { type: 'boolean' }, @@ -98,9 +109,10 @@ export const paramDef = { swPublicKey: { type: 'string', nullable: true }, swPrivateKey: { type: 'string', nullable: true }, tosUrl: { type: 'string', nullable: true }, - repositoryUrl: { type: 'string' }, - feedbackUrl: { type: 'string' }, + repositoryUrl: { type: 'string', nullable: true }, + feedbackUrl: { type: 'string', nullable: true }, impressumUrl: { type: 'string', nullable: true }, + donationUrl: { type: 'string', nullable: true }, privacyPolicyUrl: { type: 'string', nullable: true }, useObjectStorage: { type: 'boolean' }, objectStorageBaseUrl: { type: 'string', nullable: true }, @@ -119,6 +131,9 @@ export const paramDef = { enableActiveEmailValidation: { type: 'boolean' }, enableVerifymailApi: { type: 'boolean' }, verifymailAuthKey: { type: 'string', nullable: true }, + enableTruemailApi: { type: 'boolean' }, + truemailInstance: { type: 'string', nullable: true }, + truemailAuthKey: { type: 'string', nullable: true }, enableChartsForRemoteUser: { type: 'boolean' }, enableChartsForFederatedInstances: { type: 'boolean' }, enableServerMachineStats: { type: 'boolean' }, @@ -175,6 +190,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (Array.isArray(ps.sensitiveWords)) { set.sensitiveWords = ps.sensitiveWords.filter(Boolean); } + if (Array.isArray(ps.prohibitedWords)) { + set.prohibitedWords = ps.prohibitedWords.filter(Boolean); + } if (Array.isArray(ps.silencedHosts)) { let lastValue = ''; set.silencedHosts = ps.silencedHosts.sort().filter((h) => { @@ -279,6 +297,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- set.hcaptchaSecretKey = ps.hcaptchaSecretKey; } + if (ps.enableMcaptcha !== undefined) { + set.enableMcaptcha = ps.enableMcaptcha; + } + + if (ps.mcaptchaSiteKey !== undefined) { + set.mcaptchaSitekey = ps.mcaptchaSiteKey; + } + + if (ps.mcaptchaInstanceUrl !== undefined) { + set.mcaptchaInstanceUrl = ps.mcaptchaInstanceUrl; + } + + if (ps.mcaptchaSecretKey !== undefined) { + set.mcaptchaSecretKey = ps.mcaptchaSecretKey; + } + if (ps.enableRecaptcha !== undefined) { set.enableRecaptcha = ps.enableRecaptcha; } @@ -372,7 +406,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } if (ps.repositoryUrl !== undefined) { - set.repositoryUrl = ps.repositoryUrl; + set.repositoryUrl = URL.canParse(ps.repositoryUrl!) ? ps.repositoryUrl : null; } if (ps.feedbackUrl !== undefined) { @@ -383,6 +417,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- set.impressumUrl = ps.impressumUrl; } + if (ps.donationUrl !== undefined) { + set.donationUrl = ps.donationUrl; + } + if (ps.privacyPolicyUrl !== undefined) { set.privacyPolicyUrl = ps.privacyPolicyUrl; } @@ -451,6 +489,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- set.deeplIsPro = ps.deeplIsPro; } + if (ps.deeplFreeMode !== undefined) { + set.deeplFreeMode = ps.deeplFreeMode; + } + + if (ps.deeplFreeInstance !== undefined) { + if (ps.deeplFreeInstance === '') { + set.deeplFreeInstance = null; + } else { + set.deeplFreeInstance = ps.deeplFreeInstance; + } + } + if (ps.enableIpLogging !== undefined) { set.enableIpLogging = ps.enableIpLogging; } @@ -471,6 +521,26 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } } + if (ps.enableTruemailApi !== undefined) { + set.enableTruemailApi = ps.enableTruemailApi; + } + + if (ps.truemailInstance !== undefined) { + if (ps.truemailInstance === '') { + set.truemailInstance = null; + } else { + set.truemailInstance = ps.truemailInstance; + } + } + + if (ps.truemailAuthKey !== undefined) { + if (ps.truemailAuthKey === '') { + set.truemailAuthKey = null; + } else { + set.truemailAuthKey = ps.truemailAuthKey; + } + } + if (ps.enableChartsForRemoteUser !== undefined) { set.enableChartsForRemoteUser = ps.enableChartsForRemoteUser; } diff --git a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts index e582147e721f13656bdbf1c3e8c34f723dfda30a..e9930422c09cd0f70382795d5914b474c1b5117f 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/announcements.ts b/packages/backend/src/server/api/endpoints/announcements.ts index 7c242dbcd54417ecbbaea5e2783ffabdd22e074b..3b12f5b62ccba3f06355e255c7a55d982c3ebb62 100644 --- a/packages/backend/src/server/api/endpoints/announcements.ts +++ b/packages/backend/src/server/api/endpoints/announcements.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index b029493d3a362446a8bfed61c30118b49a21e1f7..191de8f833e6fe59ac137a3f1d6f9b1cb2746e5a 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/antennas/delete.ts b/packages/backend/src/server/api/endpoints/antennas/delete.ts index e6240aec65e668a583e6bef95f0516f75026bc33..2258954b56b515aa38abf578d8e12a19f33b06de 100644 --- a/packages/backend/src/server/api/endpoints/antennas/delete.ts +++ b/packages/backend/src/server/api/endpoints/antennas/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/antennas/list.ts b/packages/backend/src/server/api/endpoints/antennas/list.ts index 3a9f969d241aceee04033cf4e3768b5fcb591186..83d29f9c8c80902d36eca33fac5bfb278d711e0e 100644 --- a/packages/backend/src/server/api/endpoints/antennas/list.ts +++ b/packages/backend/src/server/api/endpoints/antennas/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 0bf2688b4a7e2c5282e135fa7c3c2946585b7798..f4dfe1ecc4d7368e3e1e84bba42487178016593c 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -14,6 +14,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { IdService } from '@/core/IdService.js'; import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { trackPromise } from '@/misc/promise-tracker.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -92,7 +93,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- antenna.isActive = true; antenna.lastUsedAt = new Date(); - this.antennasRepository.update(antenna.id, antenna); + trackPromise(this.antennasRepository.update(antenna.id, antenna)); if (needPublishEvent) { this.globalEventService.publishInternalEvent('antennaUpdated', antenna); @@ -123,9 +124,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- notes.sort((a, b) => a.id > b.id ? -1 : 1); } - if (notes.length > 0) { - this.noteReadService.read(me.id, notes); - } + this.noteReadService.read(me.id, notes); return await this.noteEntityService.packMany(notes, me); }); diff --git a/packages/backend/src/server/api/endpoints/antennas/show.ts b/packages/backend/src/server/api/endpoints/antennas/show.ts index 77c9b31763aefd5ec9d236e640407807ab2a6b27..a40f187d0bc9ea07d5c586b7fda225fc026bc0b8 100644 --- a/packages/backend/src/server/api/endpoints/antennas/show.ts +++ b/packages/backend/src/server/api/endpoints/antennas/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts index 3457bb6f663e1c551668e18179afcff86c4e2ae5..459729f61f2e5db72b592bf191b8979c06b34ab8 100644 --- a/packages/backend/src/server/api/endpoints/antennas/update.ts +++ b/packages/backend/src/server/api/endpoints/antennas/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/ap/get.ts b/packages/backend/src/server/api/endpoints/ap/get.ts index e0ef5d413a99be29ee11173004ce8f064259772c..d8c55de7ec6ba87cafaeb11da180cdccbf6c1ed3 100644 --- a/packages/backend/src/server/api/endpoints/ap/get.ts +++ b/packages/backend/src/server/api/endpoints/ap/get.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index 8ab16880fa1768f17b54fb585047a689ec4ac6cd..364a4826e35efec54c704ab0d68329eb8b0adfa0 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -148,7 +148,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (user != null) { return { type: 'User', - object: await this.userEntityService.pack(user, me, { detail: true }), + object: await this.userEntityService.pack(user, me, { schema: 'UserDetailedNotMe' }), }; } else if (note != null) { try { diff --git a/packages/backend/src/server/api/endpoints/app/create.ts b/packages/backend/src/server/api/endpoints/app/create.ts index f89d9823ba68e24097e33566d8ca8c0ddab0dc7f..492705d6f9215000469d548aac20dbdabca32b79 100644 --- a/packages/backend/src/server/api/endpoints/app/create.ts +++ b/packages/backend/src/server/api/endpoints/app/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/app/show.ts b/packages/backend/src/server/api/endpoints/app/show.ts index cb968a1c650b4b9c816e9ffd04c4ad9180741c7a..3db9a0d0d4e5bd5383f3988f7099127467b074ef 100644 --- a/packages/backend/src/server/api/endpoints/app/show.ts +++ b/packages/backend/src/server/api/endpoints/app/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/auth/accept.ts b/packages/backend/src/server/api/endpoints/auth/accept.ts index e0baeb35656ef48798d09198348471c2bb04c12a..2e62f04df061393e730c11fbe8461813114e7dc6 100644 --- a/packages/backend/src/server/api/endpoints/auth/accept.ts +++ b/packages/backend/src/server/api/endpoints/auth/accept.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -62,7 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const accessToken = secureRndstr(32); // Fetch exist access token - const exist = await this.accessTokensRepository.exist({ + const exist = await this.accessTokensRepository.exists({ where: { appId: session.appId, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/auth/session/generate.ts b/packages/backend/src/server/api/endpoints/auth/session/generate.ts index 6e474c59e06205e720a6ac00cb8a21dd24fc6ae0..26dd8931385e4651744159df72f0ed01cca4ba96 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/generate.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/generate.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/auth/session/show.ts b/packages/backend/src/server/api/endpoints/auth/session/show.ts index 0f5da0f252ee7e46e28989df7a45328cb94ac7be..13e02a25419a89a228de03f4903da9f2e7a798cf 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/show.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts index ffddda090b0c897b0beb5f764340cad25ea37574..b490c5832d7efcd6c66073b0735942b5997335d4 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -112,7 +112,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- return { accessToken: accessToken.token, user: await this.userEntityService.pack(session.userId, null, { - detail: true, + schema: 'UserDetailedNotMe', }), }; }); diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index 3c7d7ac8cde38dcd2c51204fe8c7368e2963916f..506621574969dc3d0e3952b481562de641490d88 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -88,7 +88,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }); // Check if already blocking - const exist = await this.blockingsRepository.exist({ + const exist = await this.blockingsRepository.exists({ where: { blockerId: blocker.id, blockeeId: blockee.id, @@ -102,7 +102,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- await this.userBlockingService.block(blocker, blockee); return await this.userEntityService.pack(blockee.id, blocker, { - detail: true, + schema: 'UserDetailedNotMe', }); }); } diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index 0ce334d5591b2b074dc669ede0122fd0fb6863fd..cebb30733848381df97a87eea837bf58856dd9a4 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -88,7 +88,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }); // Check not blocking - const exist = await this.blockingsRepository.exist({ + const exist = await this.blockingsRepository.exists({ where: { blockerId: blocker.id, blockeeId: blockee.id, @@ -103,7 +103,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- await this.userBlockingService.unblock(blocker, blockee); return await this.userEntityService.pack(blockee.id, blocker, { - detail: true, + schema: 'UserDetailedNotMe', }); }); } diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index 58d24540d1aa4b18269778c07858266e7be69390..8431fa6b34a34528fc2b10138d8d68e8eaa24bd7 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts b/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts new file mode 100644 index 0000000000000000000000000000000000000000..ab877bbe2051b5106d42289d61753d8bd85e8687 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts @@ -0,0 +1,83 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { MoreThan } from 'typeorm'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { BubbleGameRecordsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; + +export const meta = { + allowGet: true, + cacheSec: 60, + + errors: { + }, + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + properties: { + id: { + type: 'string', format: 'misskey:id', + optional: false, nullable: false, + }, + score: { + type: 'integer', + optional: false, nullable: false, + }, + user: { + type: 'object', + optional: true, nullable: false, + ref: 'UserLite', + }, + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + gameMode: { type: 'string' }, + }, + required: ['gameMode'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.bubbleGameRecordsRepository) + private bubbleGameRecordsRepository: BubbleGameRecordsRepository, + + private userEntityService: UserEntityService, + ) { + super(meta, paramDef, async (ps) => { + const records = await this.bubbleGameRecordsRepository.find({ + where: { + gameMode: ps.gameMode, + seededAt: MoreThan(new Date(Date.now() - 1000 * 60 * 60 * 24 * 7)), + }, + order: { + score: 'DESC', + }, + take: 10, + relations: ['user'], + }); + + const users = await this.userEntityService.packMany(records.map(r => r.user!), null); + + return records.map(r => ({ + id: r.id, + score: r.score, + user: users.find(u => u.id === r.user!.id), + })); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/bubble-game/register.ts b/packages/backend/src/server/api/endpoints/bubble-game/register.ts new file mode 100644 index 0000000000000000000000000000000000000000..0a999e42cd9cf6058529713a1a8d209dab122338 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/bubble-game/register.ts @@ -0,0 +1,89 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { IdService } from '@/core/IdService.js'; +import type { BubbleGameRecordsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + requireCredential: true, + + kind: 'write:account', + + limit: { + duration: ms('1hour'), + max: 120, + minInterval: ms('30sec'), + }, + + errors: { + invalidSeed: { + message: 'Provided seed is invalid.', + code: 'INVALID_SEED', + id: 'eb627bc7-574b-4a52-a860-3c3eae772b88', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + score: { type: 'integer', minimum: 0 }, + seed: { type: 'string', minLength: 1, maxLength: 1024 }, + logs: { + type: 'array', + items: { + type: 'array', + items: { + type: 'number', + }, + }, + }, + gameMode: { type: 'string' }, + gameVersion: { type: 'integer' }, + }, + required: ['score', 'seed', 'logs', 'gameMode', 'gameVersion'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.bubbleGameRecordsRepository) + private bubbleGameRecordsRepository: BubbleGameRecordsRepository, + + private idService: IdService, + ) { + super(meta, paramDef, async (ps, me) => { + const seedDate = new Date(parseInt(ps.seed, 10)); + const now = new Date(); + + // シードãŒæœªæ¥ãªã®ã¯é€šå¸¸ã®ãƒ—レイã§ã¯ã‚ã‚Šãˆãªã„ã®ã§å¼¾ã + if (seedDate.getTime() > now.getTime()) { + throw new ApiError(meta.errors.invalidSeed); + } + + // シードãŒå¤ã™ãŽã‚‹(5時間以上å‰)ã®ã‚‚å¼¾ã + if (seedDate.getTime() < now.getTime() - 1000 * 60 * 60 * 5) { + throw new ApiError(meta.errors.invalidSeed); + } + + await this.bubbleGameRecordsRepository.insert({ + id: this.idService.gen(now.getTime()), + seed: ps.seed, + seededAt: seedDate, + userId: me.id, + score: ps.score, + logs: ps.logs, + gameMode: ps.gameMode, + gameVersion: ps.gameVersion, + isVerified: false, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts index 3dd1eddd0120af11eea999a2e4992a73d769cb9e..2866db54240c2ab9dd52f33e528c5539360c4ec9 100644 --- a/packages/backend/src/server/api/endpoints/channels/create.ts +++ b/packages/backend/src/server/api/endpoints/channels/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/channels/favorite.ts b/packages/backend/src/server/api/endpoints/channels/favorite.ts index c175718919877a0b46b58b24d12c019fd91626b9..a1ae9b80a76a7e4fa8222409de9b2c907024be22 100644 --- a/packages/backend/src/server/api/endpoints/channels/favorite.ts +++ b/packages/backend/src/server/api/endpoints/channels/favorite.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/channels/featured.ts b/packages/backend/src/server/api/endpoints/channels/featured.ts index 412ea1bb16360faa2977913156dae3bff0e1d73e..a9a79ba8fc63383311e68e15cbed9f3ef8964e53 100644 --- a/packages/backend/src/server/api/endpoints/channels/featured.ts +++ b/packages/backend/src/server/api/endpoints/channels/featured.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/channels/follow.ts b/packages/backend/src/server/api/endpoints/channels/follow.ts index bb5a477eb882804edb73ad05c404dde36c06e9e7..1812820ba298b3144b5134319d2ec0367cbef161 100644 --- a/packages/backend/src/server/api/endpoints/channels/follow.ts +++ b/packages/backend/src/server/api/endpoints/channels/follow.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/channels/followed.ts b/packages/backend/src/server/api/endpoints/channels/followed.ts index 6514f1ea3cd6fc406054bb67fa839b388b385f81..d2f36f251ecc7fddebc2e0de4470bf8b9b1e12aa 100644 --- a/packages/backend/src/server/api/endpoints/channels/followed.ts +++ b/packages/backend/src/server/api/endpoints/channels/followed.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/channels/my-favorites.ts b/packages/backend/src/server/api/endpoints/channels/my-favorites.ts index 057a438ac912dfe2fcbac327ac264c2d93f05fca..d96e6c3ad2279d9bac467925b97ab0a7e542312c 100644 --- a/packages/backend/src/server/api/endpoints/channels/my-favorites.ts +++ b/packages/backend/src/server/api/endpoints/channels/my-favorites.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/channels/owned.ts b/packages/backend/src/server/api/endpoints/channels/owned.ts index b1dd6935371f0bf64c5c8c4243c2f7ba184b8617..daab685f1bd7a1a60f87f722c1d53eecc5547c77 100644 --- a/packages/backend/src/server/api/endpoints/channels/owned.ts +++ b/packages/backend/src/server/api/endpoints/channels/owned.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/channels/search.ts b/packages/backend/src/server/api/endpoints/channels/search.ts index 9c78a948443f00e5a52b29c93fc226877cae5b1b..ae32203603609952372ef47d8dc86f6eb653dbce 100644 --- a/packages/backend/src/server/api/endpoints/channels/search.ts +++ b/packages/backend/src/server/api/endpoints/channels/search.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/channels/show.ts b/packages/backend/src/server/api/endpoints/channels/show.ts index 3eaa83c7e82ad256607ef1140ea4fce6bd0a8143..332ce2c9dcc022f42b8069ad1ae7d7bce280a495 100644 --- a/packages/backend/src/server/api/endpoints/channels/show.ts +++ b/packages/backend/src/server/api/endpoints/channels/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 006228ceee619395c1a11e07741f50fe4b9c4e40..8c5567359088309e16666567174b732dc71e9941 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/channels/unfavorite.ts b/packages/backend/src/server/api/endpoints/channels/unfavorite.ts index b4c7af8154ab19f01a1e9c765865eafb38d8a7f3..fc6b75e29537342d254fbc1be65d1f3d0d7f9875 100644 --- a/packages/backend/src/server/api/endpoints/channels/unfavorite.ts +++ b/packages/backend/src/server/api/endpoints/channels/unfavorite.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/channels/unfollow.ts b/packages/backend/src/server/api/endpoints/channels/unfollow.ts index c95332c7f8e671fd6b2af91501d1eb3193144933..48c5261135fa55dc71b3f8907ec68c6507e417f0 100644 --- a/packages/backend/src/server/api/endpoints/channels/unfollow.ts +++ b/packages/backend/src/server/api/endpoints/channels/unfollow.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/channels/update.ts b/packages/backend/src/server/api/endpoints/channels/update.ts index 93d02e4a12749203fefc6bed7fff4fc9f63de696..dba2938b3993de7ea60760e23d12a2630af169db 100644 --- a/packages/backend/src/server/api/endpoints/channels/update.ts +++ b/packages/backend/src/server/api/endpoints/channels/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/charts/active-users.ts b/packages/backend/src/server/api/endpoints/charts/active-users.ts index e768923ce10c15dd22e17b993dcb0b6f5c03656a..fd21e3d9feb077fe2bd1bb7a807c36d533fc5716 100644 --- a/packages/backend/src/server/api/endpoints/charts/active-users.ts +++ b/packages/backend/src/server/api/endpoints/charts/active-users.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/charts/ap-request.ts b/packages/backend/src/server/api/endpoints/charts/ap-request.ts index f518ae41caaa33e4c150172c13b6e66d9d8580d4..cbe792376b7cf15778d85b141e12b76e8366a663 100644 --- a/packages/backend/src/server/api/endpoints/charts/ap-request.ts +++ b/packages/backend/src/server/api/endpoints/charts/ap-request.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/charts/drive.ts b/packages/backend/src/server/api/endpoints/charts/drive.ts index 94afab113e150c7230e5baeaff24d5d85fa45715..d32bc765a447dea6696de2f52b51bdc5d16f35fb 100644 --- a/packages/backend/src/server/api/endpoints/charts/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/drive.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/charts/federation.ts b/packages/backend/src/server/api/endpoints/charts/federation.ts index bc33930ca425cc0a8826ec0207e5f3395e2eb4d9..dad21e9e8e9fe572abe3507030c423a26c0b766c 100644 --- a/packages/backend/src/server/api/endpoints/charts/federation.ts +++ b/packages/backend/src/server/api/endpoints/charts/federation.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/charts/instance.ts b/packages/backend/src/server/api/endpoints/charts/instance.ts index a432845b385425475746e227a8cb58e7813a5ab9..68aa12ac0ee3bc07d5f19b9731d71693c0aee738 100644 --- a/packages/backend/src/server/api/endpoints/charts/instance.ts +++ b/packages/backend/src/server/api/endpoints/charts/instance.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/charts/notes.ts b/packages/backend/src/server/api/endpoints/charts/notes.ts index e1e9d0631148845022fff6626b35293acbaabd54..e1979cfe8bbfda0889c55dfdc5f635dea321f565 100644 --- a/packages/backend/src/server/api/endpoints/charts/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/notes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/charts/user/drive.ts b/packages/backend/src/server/api/endpoints/charts/user/drive.ts index b4a58c98724edd38ae5fbc22256d07be321b877b..dcb72084b7be34b27d5db3dc87ed59af6035e1b2 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/drive.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/charts/user/following.ts b/packages/backend/src/server/api/endpoints/charts/user/following.ts index c609c5a7fe016630ebb2237d85028ea97f16e9fe..0a019ce4fbc0fc8d99b43662a9c2f1dfb8f92933 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/following.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/following.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/charts/user/notes.ts b/packages/backend/src/server/api/endpoints/charts/user/notes.ts index ad6a342fb7997ddef800df97ce64faa6a33e2ff7..06b15bca18e389a7c4091ba1eb7d492e61684621 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/notes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/charts/user/pv.ts b/packages/backend/src/server/api/endpoints/charts/user/pv.ts index 635a403d1269956e594daeb62ef21fbb4fdb0781..d359b491e2c17ce81e81188e46c312d918b4edff 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/pv.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/pv.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts index 92bc7028adea99d5a74691138925598a9ebb729d..4355aa53481a5ebfdc4af88f50617ddc01e4a5e1 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/charts/users.ts b/packages/backend/src/server/api/endpoints/charts/users.ts index 3be3721e3aec40b4e391e0021de298a5683e2620..1f5f5fea54dad0d3165f5220055d3ff50034a5d1 100644 --- a/packages/backend/src/server/api/endpoints/charts/users.ts +++ b/packages/backend/src/server/api/endpoints/charts/users.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts index 749593aa656da16bcd6a38bb6d554aed46e8a1fe..d7c9ea3964bcc92ccff25266a81899fc66736285 100644 --- a/packages/backend/src/server/api/endpoints/clips/add-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts index b4c7b52e727d9de4602e9fc3dd4c20d6d37a075b..ceebc8ba5e2d58017ee2cfae0686b11e20c1d242 100644 --- a/packages/backend/src/server/api/endpoints/clips/create.ts +++ b/packages/backend/src/server/api/endpoints/clips/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/clips/delete.ts b/packages/backend/src/server/api/endpoints/clips/delete.ts index 239945e8a4a04854bd50dca06c09fc3a3fa3311c..ca8ff2e1f1c668f82e0f14f71eea18f1b1e59002 100644 --- a/packages/backend/src/server/api/endpoints/clips/delete.ts +++ b/packages/backend/src/server/api/endpoints/clips/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/clips/favorite.ts b/packages/backend/src/server/api/endpoints/clips/favorite.ts index 015b2cfa859754a7673cb549d0bb90b3c65042b7..11f8ec3e927bd4a920837bfe71079311fb85c1bf 100644 --- a/packages/backend/src/server/api/endpoints/clips/favorite.ts +++ b/packages/backend/src/server/api/endpoints/clips/favorite.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -62,7 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw new ApiError(meta.errors.noSuchClip); } - const exist = await this.clipFavoritesRepository.exist({ + const exist = await this.clipFavoritesRepository.exists({ where: { clipId: clip.id, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/clips/list.ts b/packages/backend/src/server/api/endpoints/clips/list.ts index c124762e3302361250a98af7837d57e7c15c2338..2e4a3ff82038b6bd5702bcc8f544cf6a5337eb69 100644 --- a/packages/backend/src/server/api/endpoints/clips/list.ts +++ b/packages/backend/src/server/api/endpoints/clips/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/clips/my-favorites.ts b/packages/backend/src/server/api/endpoints/clips/my-favorites.ts index c58c16e25f6654febbd09ea37814b583a6731c5c..44719592d1173a25778b272c692a60e8dba50fbd 100644 --- a/packages/backend/src/server/api/endpoints/clips/my-favorites.ts +++ b/packages/backend/src/server/api/endpoints/clips/my-favorites.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index 1427d8d0a7559879239ec29beed96eb7b64689ec..943c31c894b1dff4431b133a4e4a3d7e52f35391 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/clips/remove-note.ts b/packages/backend/src/server/api/endpoints/clips/remove-note.ts index 7b153cb5556207ab81c6a6fa2ec9b5cf343b2392..33f9ecd25bf5b8a7252f98c66d0ff86581736947 100644 --- a/packages/backend/src/server/api/endpoints/clips/remove-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/remove-note.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/clips/show.ts b/packages/backend/src/server/api/endpoints/clips/show.ts index 03b1e09dfb9ee9591ec1dc49f3fdcf2d3b2b063f..1078a1b176aeeb24457ddd835f3ae6a5b999797e 100644 --- a/packages/backend/src/server/api/endpoints/clips/show.ts +++ b/packages/backend/src/server/api/endpoints/clips/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/clips/unfavorite.ts b/packages/backend/src/server/api/endpoints/clips/unfavorite.ts index d1007f7a192483be1523162cdb39491a933be611..a458fda4a08c97ff8d4f2c42af2ed04f402ba01b 100644 --- a/packages/backend/src/server/api/endpoints/clips/unfavorite.ts +++ b/packages/backend/src/server/api/endpoints/clips/unfavorite.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/clips/update.ts b/packages/backend/src/server/api/endpoints/clips/update.ts index 0b9878578cd8a44ebdc149680678f16080e50aef..3b44ba81b3e66f63f0fa6a3a3f2c631ce3a0fb2c 100644 --- a/packages/backend/src/server/api/endpoints/clips/update.ts +++ b/packages/backend/src/server/api/endpoints/clips/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/drive.ts b/packages/backend/src/server/api/endpoints/drive.ts index 71d3ca5f146d3e130e09fd3c0dcec6ef8d6ff42d..7e9b0fa0e1e430f3831bbd0d691c128ebb0026a6 100644 --- a/packages/backend/src/server/api/endpoints/drive.ts +++ b/packages/backend/src/server/api/endpoints/drive.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts index b7e9d12e94e141668bef20923fd2fa2356115cf7..10c521332d1adb3935ee0d69d2fcb08b14867297 100644 --- a/packages/backend/src/server/api/endpoints/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/drive/files.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -36,7 +36,7 @@ export const paramDef = { untilId: { type: 'string', format: 'misskey:id' }, folderId: { type: 'string', format: 'misskey:id', nullable: true, default: null }, type: { type: 'string', nullable: true, pattern: /^[a-zA-Z\/\-*]+$/.toString().slice(1, -1) }, - sort: { type: 'string', nullable: true, enum: ['+createdAt', '-createdAt', '+name', '-name', '+size', '-size'] }, + sort: { type: 'string', nullable: true, enum: ['+createdAt', '-createdAt', '+name', '-name', '+size', '-size', null] }, }, required: [], } as const; diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts index 14a13b09c926714b3d6978c4c983ec890a3eed61..4670392025242aec0958949c870715bfa969ac28 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -74,7 +74,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId); - query.andWhere(':file = ANY(note.fileIds)', { file: file.id }); + query.andWhere(':file <@ note.fileIds', { file: [file.id] }); const notes = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts index 85e6312b6abef733ca6055b68da6aa336c01e945..cc7920505f97c4f303ba24d44d6adadea86511b8 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -38,7 +38,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private driveFilesRepository: DriveFilesRepository, ) { super(meta, paramDef, async (ps, me) => { - const exist = await this.driveFilesRepository.exist({ + const exist = await this.driveFilesRepository.exists({ where: { md5: ps.md5, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/drive/files/create.ts b/packages/backend/src/server/api/endpoints/drive/files/create.ts index 5e97588c9996e1f7fc409b7b477f55228c8dbc75..9c17f93ab26bfbf280a97b89f25fd22752bab9a4 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/drive/files/delete.ts b/packages/backend/src/server/api/endpoints/drive/files/delete.ts index f46bf499652aa086c419f0131d5d3389bb72b37f..fa6e11da49e28edcac0565391fa9db30c292fc12 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts index 7b784f253e9aee53f241c99b4f0e93c95ffd1037..090cff6875ffe5d2dc7e300a29bb260577f7e7e3 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/drive/files/find.ts b/packages/backend/src/server/api/endpoints/drive/files/find.ts index 0ceb31e58dce0bf3e786168a00ecaf0ddb8af248..595a6957b2f33ac85ec917eca0cb23acc982284f 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/drive/files/show.ts b/packages/backend/src/server/api/endpoints/drive/files/show.ts index 474c7f02d30c9b2e051baf919cff0f5f99085d4c..e8f4539d6149ac80fb6513c1afd73a04c161ad32 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts index c96e1a1aac3b844d775ab9cdb0d67ad196d9afbe..554101812694c8016bf5c67017c3916364b89fdd 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts index 1e66035b5c2f374bdbe2280fdea3b723712f2bf7..49d2e78d08bc24a36635ac35d00ed27aaf36dc5e 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/drive/folders.ts b/packages/backend/src/server/api/endpoints/drive/folders.ts index 3a092665914417e8a5992898e3204f6d85048953..8c4848f8e15e4159c80713007fe673da0a1aad9d 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/drive/folders/create.ts b/packages/backend/src/server/api/endpoints/drive/folders/create.ts index d18199f19b2396850b1a9d6b5fde7d172a5d1935..c94070d9ff099f96a223224723b2e30da26bfef4 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts index 46a00ca3dce88357db3176591aec92f4b45c9b70..85d63873a4ca9d23ea49c6cfdc81d3f20e751542 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/drive/folders/find.ts b/packages/backend/src/server/api/endpoints/drive/folders/find.ts index 2f5cdcc648e64c93baf0d12cd10e02f8aec45ef5..eb45a30bc0c60bafe096228cf07a91dd6cee3de2 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/find.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/drive/folders/show.ts b/packages/backend/src/server/api/endpoints/drive/folders/show.ts index dd44fc46c9471ecb779c01fa2055dd4ea7e15c3e..a1c0df6697dfbf30fa0c8f4788e7666de8e96c8b 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/drive/folders/update.ts b/packages/backend/src/server/api/endpoints/drive/folders/update.ts index f8683132b29361f1bdb280a88045906d58fdac08..52b8b335b50118adc7bf9caebecfea6815052640 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/drive/stream.ts b/packages/backend/src/server/api/endpoints/drive/stream.ts index 27e1656f825371b5d566099cb0e0b272a297c51b..f7c1ed39b5857643c044735eec8033e4a4535438 100644 --- a/packages/backend/src/server/api/endpoints/drive/stream.ts +++ b/packages/backend/src/server/api/endpoints/drive/stream.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/email-address/available.ts b/packages/backend/src/server/api/endpoints/email-address/available.ts index 787009f13c7df0fe6f915e0ff07dc44ee723d63a..1d7dacd60eeaabc739c8ba2ceb7f9816534cb8d3 100644 --- a/packages/backend/src/server/api/endpoints/email-address/available.ts +++ b/packages/backend/src/server/api/endpoints/email-address/available.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/emoji.ts b/packages/backend/src/server/api/endpoints/emoji.ts index ead8c9979e926031482aab7982a04d99867b9b58..ccfbda0d448f66f07333e7f8f84f43e834123659 100644 --- a/packages/backend/src/server/api/endpoints/emoji.ts +++ b/packages/backend/src/server/api/endpoints/emoji.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/emojis.ts b/packages/backend/src/server/api/endpoints/emojis.ts index 2adf0a21b324557f5099ff3a285f6b657a044e3d..46ef4eca1b2c11875f75e8bec62d1ccbbe8fdb9f 100644 --- a/packages/backend/src/server/api/endpoints/emojis.ts +++ b/packages/backend/src/server/api/endpoints/emojis.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/endpoint.ts b/packages/backend/src/server/api/endpoints/endpoint.ts index 66ac8f664f200b2544423780a85133b6b25c5e6d..fe7e9c36f3ad7b7700421d7be93c78c37bffd8f0 100644 --- a/packages/backend/src/server/api/endpoints/endpoint.ts +++ b/packages/backend/src/server/api/endpoints/endpoint.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/endpoints.ts b/packages/backend/src/server/api/endpoints/endpoints.ts index 86def04acac0082b8e4bd712683ac87f0a5e2339..4aedf62a849f53417654183aecb95654d188df41 100644 --- a/packages/backend/src/server/api/endpoints/endpoints.ts +++ b/packages/backend/src/server/api/endpoints/endpoints.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts index 7380c593e3880de648c2433e971a0b31bc628c4e..5ff099524df709b42eefa92083c28e4f39c99ef5 100644 --- a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts +++ b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index a92cf6a9d8a528c545220d809e804589dd5fbd0e..ce4dd130670c99fda1842bbee6c3f568e717bfe8 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index d72ceeeea2f483ded2f038ea57469c581c88a82b..1a793889c7e9d478fec2de168b44bb3db96f3ec9 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index 617ca65733eadb750e1053ae15477bc0be11d206..4064a415a437b19e8276d70030a98d804618ce89 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -41,6 +41,7 @@ export const paramDef = { subscribing: { type: 'boolean', nullable: true }, publishing: { type: 'boolean', nullable: true }, nsfw: { type: 'boolean', nullable: true }, + bubble: { type: 'boolean', nullable: true }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, offset: { type: 'integer', default: 0 }, sort: { @@ -61,6 +62,7 @@ export const paramDef = { '-firstRetrievedAt', '+latestRequestReceivedAt', '-latestRequestReceivedAt', + null, ], }, }, @@ -98,6 +100,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- default: query.orderBy('instance.id', 'DESC'); break; } + if (me == null) { + ps.blocked = false; + ps.suspended = false; + ps.silenced = false; + } + if (typeof ps.blocked === 'boolean') { const meta = await this.metaService.fetch(true); if (ps.blocked) { @@ -148,6 +156,23 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } } + if (typeof ps.bubble === 'boolean') { + const meta = await this.metaService.fetch(true); + + if (ps.bubble) { + if (meta.bubbleInstances.length === 0) { + return []; + } + query.andWhere('instance.host IN (:...bubble)', { + bubble: meta.bubbleInstances, + }); + } else if (meta.bubbleInstances.length > 0) { + query.andWhere('instance.host NOT IN (:...bubble)', { + bubble: meta.bubbleInstances, + }); + } + } + if (typeof ps.federating === 'boolean') { if (ps.federating) { query.andWhere('((instance.followingCount > 0) OR (instance.followersCount > 0))'); diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts index 781c15e742e62897aaf810f94ca1d82c88a910dd..2972861a4baeffaf62d1d53e4e811eaaad47d646 100644 --- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts +++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -43,7 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const instance = await this.instancesRepository .findOneBy({ host: this.utilityService.toPuny(ps.host) }); - return instance ? await this.instanceEntityService.pack(instance) : null; + return instance ? await this.instanceEntityService.pack(instance, me) : null; }); } } diff --git a/packages/backend/src/server/api/endpoints/federation/stats.ts b/packages/backend/src/server/api/endpoints/federation/stats.ts index 262aa6877604671dc30e2bd53c549adaeb83d7a7..bac54970ab853c69993670c9e5932870c7304b94 100644 --- a/packages/backend/src/server/api/endpoints/federation/stats.ts +++ b/packages/backend/src/server/api/endpoints/federation/stats.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts index e6198ff6014492009f7afcf5ba6955a12df33470..f8430ef43146bc108fb3051ee72ccb8034552ded 100644 --- a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts +++ b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index d97171865a6495564c7cfac034cc078794a83dd9..71b1aeb07b2555db18f14da213007ab666cdfe54 100644 --- a/packages/backend/src/server/api/endpoints/federation/users.ts +++ b/packages/backend/src/server/api/endpoints/federation/users.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .limit(ps.limit) .getMany(); - return await this.userEntityService.packMany(users, me, { detail: true }); + return await this.userEntityService.packMany(users, me, { schema: 'UserDetailedNotMe' }); }); } } diff --git a/packages/backend/src/server/api/endpoints/fetch-external-resources.ts b/packages/backend/src/server/api/endpoints/fetch-external-resources.ts index cbe579eb6b75cc8942ed212dbafadbb478525cc1..f36136d53bd45fd10fcbc43bbba2a60faba4026c 100644 --- a/packages/backend/src/server/api/endpoints/fetch-external-resources.ts +++ b/packages/backend/src/server/api/endpoints/fetch-external-resources.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/fetch-rss.ts b/packages/backend/src/server/api/endpoints/fetch-rss.ts index b2dee83fe9ebf221b9ad78c7fdfde0a937228ea6..2085b063651821e824ef46ed69b64710cdaa0bd0 100644 --- a/packages/backend/src/server/api/endpoints/fetch-rss.ts +++ b/packages/backend/src/server/api/endpoints/fetch-rss.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/flash/create.ts b/packages/backend/src/server/api/endpoints/flash/create.ts index 674f3237347adeb7c21a8d4ed18765395bba2ab5..584d167a2900ab4efcff4c265dfb3e4d7ba48007 100644 --- a/packages/backend/src/server/api/endpoints/flash/create.ts +++ b/packages/backend/src/server/api/endpoints/flash/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/flash/delete.ts b/packages/backend/src/server/api/endpoints/flash/delete.ts index e5448c816acc572bf94c524084688c7654c8ed77..d3d47e5deb93b2fa5e0e19e36fddc740d598e3c2 100644 --- a/packages/backend/src/server/api/endpoints/flash/delete.ts +++ b/packages/backend/src/server/api/endpoints/flash/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/flash/featured.ts b/packages/backend/src/server/api/endpoints/flash/featured.ts index 1fa5612ac42283b856a18e62ac59da7c38fad68e..c2d6ab508577989d959df9f943f8cb27e2723b47 100644 --- a/packages/backend/src/server/api/endpoints/flash/featured.ts +++ b/packages/backend/src/server/api/endpoints/flash/featured.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/flash/like.ts b/packages/backend/src/server/api/endpoints/flash/like.ts index 1003249c0cd7d47803af1355ac3eaa51cfa6556a..e4dc5b61c5970733c101861b74387f63dfe08738 100644 --- a/packages/backend/src/server/api/endpoints/flash/like.ts +++ b/packages/backend/src/server/api/endpoints/flash/like.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -70,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } // if already liked - const exist = await this.flashLikesRepository.exist({ + const exist = await this.flashLikesRepository.exists({ where: { flashId: flash.id, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/flash/my-likes.ts b/packages/backend/src/server/api/endpoints/flash/my-likes.ts index e328bdbee574cc18e2f9d281c53b95b6193f9b75..755cc5acfc17457887a7122b90416a72a0eedd71 100644 --- a/packages/backend/src/server/api/endpoints/flash/my-likes.ts +++ b/packages/backend/src/server/api/endpoints/flash/my-likes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/flash/my.ts b/packages/backend/src/server/api/endpoints/flash/my.ts index 442d8dcd758f6ff45f698c4106ea6bdccf042d02..57460962322767937f12a253cf383d75d8dd312b 100644 --- a/packages/backend/src/server/api/endpoints/flash/my.ts +++ b/packages/backend/src/server/api/endpoints/flash/my.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/flash/show.ts b/packages/backend/src/server/api/endpoints/flash/show.ts index c41a27c925b979bad7357653cd38bbc549b90a9c..a6fbd8e76eabc95dc28bae20e1695968b4561290 100644 --- a/packages/backend/src/server/api/endpoints/flash/show.ts +++ b/packages/backend/src/server/api/endpoints/flash/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/flash/unlike.ts b/packages/backend/src/server/api/endpoints/flash/unlike.ts index d5c20a11674655a5f209f111ad06fe2940a76dba..7869bcdf52169814a28cc5ee1d2bd1229c48cf92 100644 --- a/packages/backend/src/server/api/endpoints/flash/unlike.ts +++ b/packages/backend/src/server/api/endpoints/flash/unlike.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/flash/update.ts b/packages/backend/src/server/api/endpoints/flash/update.ts index 8b5e1f99e91a6f0cccafe5eba984568fdfc1b3b0..e378669f0a6e9d1ab9cfa280c8993b1c35e214d9 100644 --- a/packages/backend/src/server/api/endpoints/flash/update.ts +++ b/packages/backend/src/server/api/endpoints/flash/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -51,7 +51,7 @@ export const paramDef = { } }, visibility: { type: 'string', enum: ['public', 'private'] }, }, - required: ['flashId', 'title', 'summary', 'script', 'permissions'], + required: ['flashId'], } as const; @Injectable() @@ -71,11 +71,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- await this.flashsRepository.update(flash.id, { updatedAt: new Date(), - title: ps.title, - summary: ps.summary, - script: ps.script, - permissions: ps.permissions, - visibility: ps.visibility, + ...Object.fromEntries( + Object.entries(ps).filter( + ([key, val]) => (key !== 'flashId') && Object.hasOwn(paramDef.properties, key) + ) + ), }); }); } diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index 9037944ef9efb364e144a23f2d1f0b6d6cc1778a..db320e712981482141baa572b395b76ee75019df 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -71,7 +71,7 @@ export const paramDef = { type: 'object', properties: { userId: { type: 'string', format: 'misskey:id' }, - withReplies: { type: 'boolean' } + withReplies: { type: 'boolean' }, }, required: ['userId'], } as const; @@ -100,22 +100,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw err; }); - // Check if already following - const exist = await this.followingsRepository.exist({ - where: { - followerId: follower.id, - followeeId: followee.id, - }, - }); - - if (exist) { - throw new ApiError(meta.errors.alreadyFollowing); - } - try { await this.userFollowingService.follow(follower, followee, { withReplies: ps.withReplies }); } catch (e) { if (e instanceof IdentifiableError) { + if (e.id === 'ec3f65c0-a9d1-47d9-8791-b2e7b9dcdced') throw new ApiError(meta.errors.alreadyFollowing); if (e.id === '710e8fb0-b8c3-4922-be49-d5d93d8e6a6e') throw new ApiError(meta.errors.blocking); if (e.id === '3338392a-f764-498d-8855-db939dcf8c48') throw new ApiError(meta.errors.blocked); } diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts index f44692ba6d7e744e001688a3b4ac36af35942b57..ba146b67035ea3b1203ca5ece7bec4d72b7852f1 100644 --- a/packages/backend/src/server/api/endpoints/following/delete.ts +++ b/packages/backend/src/server/api/endpoints/following/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -85,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }); // Check not following - const exist = await this.followingsRepository.exist({ + const exist = await this.followingsRepository.exists({ where: { followerId: follower.id, followeeId: followee.id, diff --git a/packages/backend/src/server/api/endpoints/following/invalidate.ts b/packages/backend/src/server/api/endpoints/following/invalidate.ts index 53ef925b2fbe33787e5fe44692ad98100a0ec898..8935c2c2daec2d2fc6346580e883edc8a551224e 100644 --- a/packages/backend/src/server/api/endpoints/following/invalidate.ts +++ b/packages/backend/src/server/api/endpoints/following/invalidate.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/following/requests/accept.ts b/packages/backend/src/server/api/endpoints/following/requests/accept.ts index 91fe922200553475aa4dcd25e3e86c8290e304a7..2d1446681c2f054779a81f4fb2ef9d4839dd2a86 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/accept.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/accept.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts index d9d5c7041b103a4236d407385ae42425a3099da1..6d663d480c824881ab717add4383ef26d7b89f58 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/following/requests/list.ts b/packages/backend/src/server/api/endpoints/following/requests/list.ts index c4faa88f65e4afe8bb7ea1e0da944866688b5618..88f559138b4f95ffef5247a73bf146d67ab055a8 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/list.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/following/requests/reject.ts b/packages/backend/src/server/api/endpoints/following/requests/reject.ts index 35f047bcefa41e9c577770e76eb93e58ae3d5711..4f78eae6774f867679523e5a9b984ae7fc8bef37 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/reject.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/reject.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/following/update-all.ts b/packages/backend/src/server/api/endpoints/following/update-all.ts index 28734cfdbd6366c3120d6ef872c6b68e624efd7a..c953feb3935a04efcbd742331c9d47f85990f6f8 100644 --- a/packages/backend/src/server/api/endpoints/following/update-all.ts +++ b/packages/backend/src/server/api/endpoints/following/update-all.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/following/update.ts b/packages/backend/src/server/api/endpoints/following/update.ts index db17d151dfdd500fd639b11560124a54646eaa9d..d62cf210ede0b9af5e9081cfb0169f656d2fb22c 100644 --- a/packages/backend/src/server/api/endpoints/following/update.ts +++ b/packages/backend/src/server/api/endpoints/following/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/gallery/featured.ts b/packages/backend/src/server/api/endpoints/gallery/featured.ts index cea42340650b8f1e007aba19c0ce72336778caed..7d2878e03fc985a37230f2fd4c73c100a70afaa5 100644 --- a/packages/backend/src/server/api/endpoints/gallery/featured.ts +++ b/packages/backend/src/server/api/endpoints/gallery/featured.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/gallery/popular.ts b/packages/backend/src/server/api/endpoints/gallery/popular.ts index c5d06f67dd1429dc1a561b538dafb9b61663917f..4ee252104a7e06cde0f443c263b7a1d1e4f947cb 100644 --- a/packages/backend/src/server/api/endpoints/gallery/popular.ts +++ b/packages/backend/src/server/api/endpoints/gallery/popular.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/gallery/posts.ts b/packages/backend/src/server/api/endpoints/gallery/posts.ts index 3ca5f4989ae7df48c4e385460a971ee6cfa2dbfd..d398418ab4b8642c4524e150eae6e47547699280 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts index 71e0ad4141cc663852f2bb2281beeeb7d15b0580..b07cdf1ed979cc503cdfede79664a84836ec39b5 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -12,6 +12,7 @@ import type { MiDriveFile } from '@/models/DriveFile.js'; import { IdService } from '@/core/IdService.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; +import { isNotNull } from '@/misc/is-not-null.js'; export const meta = { tags: ['gallery'], @@ -69,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- id: fileId, userId: me.id, }), - ))).filter((file): file is MiDriveFile => file != null); + ))).filter(isNotNull); if (files.length === 0) { throw new Error(); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts index deef2912bb1b6ed9a86dcfb7282846fd85c25e51..527e3fb52ddc03e40e37f1db3a808af6ec9f5dde 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts index cc424261b49c3317e90a1c79ee8f30f61b130f24..91e49e64637480d4068638269c54dc6dcbcb14b7 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -72,7 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } // if already liked - const exist = await this.galleryLikesRepository.exist({ + const exist = await this.galleryLikesRepository.exists({ where: { postId: post.id, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts index b3eda1be52183075b85bc2991ecd4af877cfadeb..bd6989822977f5915c221ccaa4f626f46deb6c60 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts index caa4d455533810093cfb30939afad4846095690f..f44e2c7afccc04f619d080f54b747a20f8f4084a 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts index 632214a0c240de29f6e7e3d47440d1a039b7e4f5..8bd83ff5ba0af3a4f154aaa99a312b89207b7426 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -10,6 +10,7 @@ import type { DriveFilesRepository, GalleryPostsRepository } from '@/models/_.js import type { MiDriveFile } from '@/models/DriveFile.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; +import { isNotNull } from '@/misc/is-not-null.js'; export const meta = { tags: ['gallery'], @@ -67,7 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- id: fileId, userId: me.id, }), - ))).filter((file): file is MiDriveFile => file != null); + ))).filter(isNotNull); if (files.length === 0) { throw new Error(); diff --git a/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts b/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts index dbe1626149a435dfe62f72f9e88ada29857c4797..52acee1cfbc3986f65a131020bc45fded3723060 100644 --- a/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts +++ b/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/get-online-users-count.ts b/packages/backend/src/server/api/endpoints/get-online-users-count.ts index 737d637b7e5d1c2bcd3e208af2db2dd27fda30e2..a57774be738e0115b851da87fae5feae165feab6 100644 --- a/packages/backend/src/server/api/endpoints/get-online-users-count.ts +++ b/packages/backend/src/server/api/endpoints/get-online-users-count.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/hashtags/list.ts b/packages/backend/src/server/api/endpoints/hashtags/list.ts index 21d863107d8f3a6ef8aeb5e9c4481afc03fa15ac..5cd3c6584de319be46d8844a0da7f5bf64d0db65 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/list.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/hashtags/search.ts b/packages/backend/src/server/api/endpoints/hashtags/search.ts index acfef16b1113fff68119d66e14ba76bd5f4dcd31..d4eb85105495ba8d937226491c680286a3546f0f 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/search.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/search.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -43,7 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- super(meta, paramDef, async (ps, me) => { const hashtags = await this.hashtagsRepository.createQueryBuilder('tag') .where('tag.name like :q', { q: sqlLikeEscape(ps.query.toLowerCase()) + '%' }) - .orderBy('tag.count', 'DESC') + .orderBy('tag.mentionedLocalUsersCount', 'DESC') .groupBy('tag.id') .limit(ps.limit) .offset(ps.offset) diff --git a/packages/backend/src/server/api/endpoints/hashtags/show.ts b/packages/backend/src/server/api/endpoints/hashtags/show.ts index 3ba16fdc85cdfede009fcc758a2556656a63ed15..940e3bd69d764d6b52054a96c929233bef6c7b29 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/show.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/hashtags/trend.ts b/packages/backend/src/server/api/endpoints/hashtags/trend.ts index 8f382eb96b723e130c6194f506be7fdd3bb67d4b..cb8065e3a6b5bf9b5210d6b4b71efe218bc27d3a 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/trend.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/trend.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index 50aea79943cea85d465ee311d49d250f57fd4dcf..30f0c1b0c8b1586ee480d1a42c4532a931394d64 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UsersRepository } from '@/models/_.js'; +import { safeForSql } from "@/misc/safe-for-sql.js"; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -47,8 +48,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, me) => { + if (!safeForSql(normalizeForSearch(ps.tag))) throw new Error('Injection'); const query = this.usersRepository.createQueryBuilder('user') - .where(':tag = ANY(user.tags)', { tag: normalizeForSearch(ps.tag) }) + .where(':tag <@ user.tags', { tag: [normalizeForSearch(ps.tag)] }) .andWhere('user.isSuspended = FALSE'); const recent = new Date(Date.now() - (1000 * 60 * 60 * 24 * 5)); @@ -74,7 +76,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const users = await query.limit(ps.limit).getMany(); - return await this.userEntityService.packMany(users, me, { detail: true }); + return await this.userEntityService.packMany(users, me, { schema: 'UserDetailed' }); }); } } diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index c24e049180e2f3ff420e501b6b142b16b68d6672..d324e3e64a40c1daee3e6532dfd883a4dd205f1c 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -71,8 +71,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- userProfile.loggedInDates = [...userProfile.loggedInDates, today]; } - return await this.userEntityService.pack<true, true>(userProfile.user!, userProfile.user!, { - detail: true, + return await this.userEntityService.pack(userProfile.user!, userProfile.user!, { + schema: 'MeDetailed', includeSecrets: isSecure, userProfile, }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/done.ts b/packages/backend/src/server/api/endpoints/i/2fa/done.ts index 9f8e2894b81c6511df911fdae877c5987e4989c1..2a30e8b0c3e4b6cf558001978638a4bec5d9b4ee 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -15,6 +15,19 @@ export const meta = { requireCredential: true, secure: true, + + res: { + type: 'object', + properties: { + backupCodes: { + type: 'array', + optional: false, + items: { + type: 'string', + }, + }, + }, + }, } as const; export const paramDef = { @@ -64,7 +77,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- // Publish meUpdated event this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { - detail: true, + schema: 'MeDetailed', includeSecrets: true, })); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index 4161553d2880b79af43350ef812dc51bf6a79b0a..74ee90b3dd15dc3e0a93d88da04a6e5dae5a6152 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -112,7 +112,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // Publish meUpdated event this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { - detail: true, + schema: 'MeDetailed', includeSecrets: true, })); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts index 2ed701014dc38caf5b0ece39e3099b0438bda9e3..bf039ccd16fd40f7fc15cd5d4e91405973d7f7be 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -74,7 +74,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- // Publish meUpdated event this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { - detail: true, + schema: 'MeDetailed', includeSecrets: true, })); }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts index 325d54d196bec1ad2d6c0574e384e9868f79d316..cc6e9ee42d242ffb144c0e4478731c38ecc10d43 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -48,7 +48,7 @@ export const meta = { properties: { id: { type: 'string', - nullable: true, + optional: true, }, }, }, @@ -104,13 +104,13 @@ export const meta = { items: { type: 'string', enum: [ - "ble", - "cable", - "hybrid", - "internal", - "nfc", - "smart-card", - "usb", + 'ble', + 'cable', + 'hybrid', + 'internal', + 'nfc', + 'smart-card', + 'usb', ], }, }, @@ -124,8 +124,8 @@ export const meta = { authenticatorAttachment: { type: 'string', enum: [ - "cross-platform", - "platform", + 'cross-platform', + 'platform', ], }, requireResidentKey: { @@ -134,9 +134,9 @@ export const meta = { userVerification: { type: 'string', enum: [ - "discouraged", - "preferred", - "required", + 'discouraged', + 'preferred', + 'required', ], }, }, @@ -145,10 +145,11 @@ export const meta = { type: 'string', nullable: true, enum: [ - "direct", - "enterprise", - "indirect", - "none", + 'direct', + 'enterprise', + 'indirect', + 'none', + null, ], }, extensions: { diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts index 15e50c49f3608365671c96064cbb165bdc2de647..7283159f874e55f4c4870a25c4f7736831cba42c 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts index 21e848fb5ce2243de699a8eb489e2849cebf9594..098fd5930328218462c7c8649afd2b3eb72ebb55 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -99,7 +99,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- // Publish meUpdated event this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { - detail: true, + schema: 'MeDetailed', includeSecrets: true, })); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts index 8dd880c9fab87bc774d202b85c0031b165fad63f..8da331505b82428ea8daa9568826e48bc0080d9a 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -77,7 +77,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- // Publish meUpdated event this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { - detail: true, + schema: 'MeDetailed', includeSecrets: true, })); }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts index 7056ec5a58f40569952996e7658a1a493373dcf7..deb56a3ac4fdc41e5f55c60e912d5971cee38024 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -69,7 +69,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- // Publish meUpdated event this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { - detail: true, + schema: 'MeDetailed', includeSecrets: true, })); diff --git a/packages/backend/src/server/api/endpoints/i/apps.ts b/packages/backend/src/server/api/endpoints/i/apps.ts index ef89f931810a42b2d16bde87af08f5f5582d9133..91c8597b1bd275e228b21f30be4d569a80a8b04c 100644 --- a/packages/backend/src/server/api/endpoints/i/apps.ts +++ b/packages/backend/src/server/api/endpoints/i/apps.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -21,26 +21,31 @@ export const meta = { properties: { id: { type: 'string', + optional: false, format: 'misskey:id', }, name: { type: 'string', + optional: true, }, createdAt: { type: 'string', + optional: false, format: 'date-time', }, lastUsedAt: { type: 'string', + optional: true, format: 'date-time', }, permission: { type: 'array', + optional: false, uniqueItems: true, items: { - type: 'string' + type: 'string', }, - } + }, }, }, }, diff --git a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts index a0ed371fb8373d62f134d8ed6fa11eaa904d6e80..0b4faf5ef8f96bd4d1746485587df294991609f0 100644 --- a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts +++ b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -23,23 +23,27 @@ export const meta = { id: { type: 'string', format: 'misskey:id', + optional: false, }, name: { type: 'string', + optional: false, }, callbackUrl: { type: 'string', - nullable: true, + optional: false, nullable: true, }, permission: { type: 'array', + optional: false, uniqueItems: true, items: { - type: 'string' + type: 'string', }, }, isAuthorized: { type: 'boolean', + optional: true, }, }, }, diff --git a/packages/backend/src/server/api/endpoints/i/change-password.ts b/packages/backend/src/server/api/endpoints/i/change-password.ts index fb0f2bc88e1c78ab095c077917c2d54cfef0d52a..6aedde717c93f65000c2b4ee53690290bd63e479 100644 --- a/packages/backend/src/server/api/endpoints/i/change-password.ts +++ b/packages/backend/src/server/api/endpoints/i/change-password.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/claim-achievement.ts b/packages/backend/src/server/api/endpoints/i/claim-achievement.ts index 3580d6ba1b54f1b4de330aadd2b22b3861c725a8..73231e8e09ad14f03f2712ce44787dd76199600a 100644 --- a/packages/backend/src/server/api/endpoints/i/claim-achievement.ts +++ b/packages/backend/src/server/api/endpoints/i/claim-achievement.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts index e0b40db91753f962ee9ac7ce6fbc1080c2621727..af4d601ad6a86d9e298234a41f47a2c4d8d2d762 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/export-antennas.ts b/packages/backend/src/server/api/endpoints/i/export-antennas.ts index 23b2f6b4cee7da861189d72d30bc7b73f79a5e1c..77fb4a895ffa1ee3fde6d235dc1fd1d5ed24e3d2 100644 --- a/packages/backend/src/server/api/endpoints/i/export-antennas.ts +++ b/packages/backend/src/server/api/endpoints/i/export-antennas.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/export-blocking.ts b/packages/backend/src/server/api/endpoints/i/export-blocking.ts index 8068a3b305c13a1874154d28944c26e0cf717a2e..7573018bec3004112f122821c36538b0d7060bce 100644 --- a/packages/backend/src/server/api/endpoints/i/export-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/export-blocking.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/export-clips.ts b/packages/backend/src/server/api/endpoints/i/export-clips.ts new file mode 100644 index 0000000000000000000000000000000000000000..10d1fdac736c93e0fa9a4d4cec23f3962a0b84c1 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/i/export-clips.ts @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import ms from 'ms'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueueService } from '@/core/QueueService.js'; + +export const meta = { + secure: true, + requireCredential: true, + limit: { + duration: ms('1day'), + max: 1, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private queueService: QueueService, + ) { + super(meta, paramDef, async (ps, me) => { + this.queueService.createExportClipsJob(me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/export-favorites.ts b/packages/backend/src/server/api/endpoints/i/export-favorites.ts index c22905bc67d3dc78d2d27051a829cf609bd05a4c..5e03f70170731b5147fccedd38b2f0270f448d68 100644 --- a/packages/backend/src/server/api/endpoints/i/export-favorites.ts +++ b/packages/backend/src/server/api/endpoints/i/export-favorites.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/export-following.ts b/packages/backend/src/server/api/endpoints/i/export-following.ts index 880833ab76a00f2ed82ec577ed0456c760a58e45..2e5ba14737d67ab7b0cf0ffa029f9ea24a4d09e6 100644 --- a/packages/backend/src/server/api/endpoints/i/export-following.ts +++ b/packages/backend/src/server/api/endpoints/i/export-following.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/export-mute.ts b/packages/backend/src/server/api/endpoints/i/export-mute.ts index 8eb70a387a3f0afb6b6f5f7548021be312558f4f..0384cf142bd065a88d931b58d21761b9c0d9485d 100644 --- a/packages/backend/src/server/api/endpoints/i/export-mute.ts +++ b/packages/backend/src/server/api/endpoints/i/export-mute.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/export-notes.ts b/packages/backend/src/server/api/endpoints/i/export-notes.ts index 791f6377902d634b465248062c5384b7e0f8003b..db4e78f667fecc61d20155a5ed2ab34d13a89288 100644 --- a/packages/backend/src/server/api/endpoints/i/export-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/export-notes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts index f387f6d0160d8b877ff80dc6e817343368824b6d..6cd662102c1a4afc8a66259886ef82b1008df708 100644 --- a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/favorites.ts b/packages/backend/src/server/api/endpoints/i/favorites.ts index d6f13c535a25fa3b8edbc1ee32d846eab08098a9..3558035eca2b1568ab487f8fef5e5457d5f47b07 100644 --- a/packages/backend/src/server/api/endpoints/i/favorites.ts +++ b/packages/backend/src/server/api/endpoints/i/favorites.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts index 7e37adc4ac8a24647b139d3bf392f2fe0e67514d..d492585ffa665b99c121ddaede85def05932f50a 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts index 148d38aa54b5876d614a31f55843bff1b9763e7d..73a6fcc98b5f3a20ceeb4dd273f347e850397c2e 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/import-antennas.ts b/packages/backend/src/server/api/endpoints/i/import-antennas.ts index 71db8710af923e3455a85f8b2b3d0f30e19c91a3..b4661a93e2b1859e57dc76b168a21af5a0c867ee 100644 --- a/packages/backend/src/server/api/endpoints/i/import-antennas.ts +++ b/packages/backend/src/server/api/endpoints/i/import-antennas.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -71,7 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private downloadService: DownloadService, ) { super(meta, paramDef, async (ps, me) => { - const userExist = await this.usersRepository.exist({ where: { id: me.id } }); + const userExist = await this.usersRepository.exists({ where: { id: me.id } }); if (!userExist) throw new ApiError(meta.errors.noSuchUser); const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (file === null) throw new ApiError(meta.errors.noSuchFile); diff --git a/packages/backend/src/server/api/endpoints/i/import-blocking.ts b/packages/backend/src/server/api/endpoints/i/import-blocking.ts index 965ad305478a0b6bab5bf29f86ed6c7043d11d88..8ddbe5663e836349af531ba7243b9635e68696f9 100644 --- a/packages/backend/src/server/api/endpoints/i/import-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/import-blocking.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/import-following.ts b/packages/backend/src/server/api/endpoints/i/import-following.ts index e5fa2ac96a120f037b8cffbb56165eff44695e36..390dd9cd715f5fc264305f19682cf00b889115dc 100644 --- a/packages/backend/src/server/api/endpoints/i/import-following.ts +++ b/packages/backend/src/server/api/endpoints/i/import-following.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/import-muting.ts b/packages/backend/src/server/api/endpoints/i/import-muting.ts index 926cf13d7fb77b52d88218734f27b9cef6cd8dcd..51a9cdf5a58a63ae460ba6b32bad5624b9a6ee65 100644 --- a/packages/backend/src/server/api/endpoints/i/import-muting.ts +++ b/packages/backend/src/server/api/endpoints/i/import-muting.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts index 21679964351f5eb909f7b9d91e35b862a670021a..a3b67301a790b228ff61a01262a890c4764113db 100644 --- a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/move.ts b/packages/backend/src/server/api/endpoints/i/move.ts index f3ba720c2bf080551d0c4c739243991ccab5c39c..1bd641232cbca10688e892290d5582f1813276fd 100644 --- a/packages/backend/src/server/api/endpoints/i/move.ts +++ b/packages/backend/src/server/api/endpoints/i/move.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts b/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts index 4ea94b07f64e5cb6196e0bac312a6f34afb64369..ad4577be58fa417cef9900ba5b01a92c13fd2c5a 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts @@ -1,13 +1,13 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import { Brackets, In } from 'typeorm'; +import { In } from 'typeorm'; import * as Redis from 'ioredis'; import { Inject, Injectable } from '@nestjs/common'; import type { NotesRepository } from '@/models/_.js'; -import { obsoleteNotificationTypes, notificationTypes, FilterUnionByProperty } from '@/types.js'; +import { obsoleteNotificationTypes, groupedNotificationTypes, FilterUnionByProperty } from '@/types.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteReadService } from '@/core/NoteReadService.js'; import { NotificationEntityService } from '@/core/entities/NotificationEntityService.js'; @@ -48,10 +48,10 @@ export const paramDef = { markAsRead: { type: 'boolean', default: true }, // 後方互æ›ã®ãŸã‚ã€å»ƒæ¢ã•ã‚ŒãŸé€šçŸ¥ã‚¿ã‚¤ãƒ—ã‚‚å—ã‘付ã‘ã‚‹ includeTypes: { type: 'array', items: { - type: 'string', enum: [...notificationTypes, ...obsoleteNotificationTypes], + type: 'string', enum: [...groupedNotificationTypes, ...obsoleteNotificationTypes], } }, excludeTypes: { type: 'array', items: { - type: 'string', enum: [...notificationTypes, ...obsoleteNotificationTypes], + type: 'string', enum: [...groupedNotificationTypes, ...obsoleteNotificationTypes], } }, }, required: [], @@ -79,12 +79,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- return []; } // excludeTypes ã«å…¨æŒ‡å®šã•ã‚Œã¦ã„ã‚‹å ´åˆã¯ã‚¯ã‚¨ãƒªã—ãªã„ - if (notificationTypes.every(type => ps.excludeTypes?.includes(type))) { + if (groupedNotificationTypes.every(type => ps.excludeTypes?.includes(type))) { return []; } - const includeTypes = ps.includeTypes && ps.includeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof notificationTypes[number][]; - const excludeTypes = ps.excludeTypes && ps.excludeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof notificationTypes[number][]; + const includeTypes = ps.includeTypes && ps.includeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof groupedNotificationTypes[number][]; + const excludeTypes = ps.excludeTypes && ps.excludeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof groupedNotificationTypes[number][]; const limit = (ps.limit + EXTRA_LIMIT) + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdã«æŒ‡å®šã—ãŸã‚‚ã®ã‚‚å«ã¾ã‚Œã‚‹ãŸã‚+1 const notificationsRes = await this.redisClient.xrevrange( @@ -162,9 +162,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } groupedNotifications = groupedNotifications.slice(0, ps.limit); - const noteIds = groupedNotifications - .filter((notification): notification is FilterUnionByProperty<MiNotification, 'type', 'mention' | 'reply' | 'quote'> => ['mention', 'reply', 'quote'].includes(notification.type)) + .filter((notification): notification is FilterUnionByProperty<MiNotification, 'type', 'mention' | 'reply' | 'quote' | 'edited'> => ['mention', 'reply', 'quote', 'edited'].includes(notification.type)) .map(notification => notification.noteId!); if (noteIds.length > 0) { diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 039fd9454cdc19d12edf5487249f195289c1ae35..594e8b95c8eddc665cac3a465464aa4653b2c24e 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -1,9 +1,9 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import { Brackets, In } from 'typeorm'; +import { In } from 'typeorm'; import * as Redis from 'ioredis'; import { Inject, Injectable } from '@nestjs/common'; import type { NotesRepository } from '@/models/_.js'; @@ -113,7 +113,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } const noteIds = notifications - .filter((notification): notification is FilterUnionByProperty<MiNotification, 'type', 'mention' | 'reply' | 'quote'> => ['mention', 'reply', 'quote'].includes(notification.type)) + .filter((notification): notification is FilterUnionByProperty<MiNotification, 'type', 'mention' | 'reply' | 'quote' | 'edited'> => ['mention', 'reply', 'quote', 'edited'].includes(notification.type)) .map(notification => notification.noteId); if (noteIds.length > 0) { diff --git a/packages/backend/src/server/api/endpoints/i/page-likes.ts b/packages/backend/src/server/api/endpoints/i/page-likes.ts index 6bf7e6aa9b93c2079f9694af6a58ae1ad3e97786..d4c09426a75e442a23a960cfe9b46514b1e08761 100644 --- a/packages/backend/src/server/api/endpoints/i/page-likes.ts +++ b/packages/backend/src/server/api/endpoints/i/page-likes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/pages.ts b/packages/backend/src/server/api/endpoints/i/pages.ts index b8082c018f77c92d84c0c058b14cf526a06ceaa9..1b6359a6332fb3c8d7ddce72718677b610790b0e 100644 --- a/packages/backend/src/server/api/endpoints/i/pages.ts +++ b/packages/backend/src/server/api/endpoints/i/pages.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/pin.ts b/packages/backend/src/server/api/endpoints/i/pin.ts index c89cdfa3a4e50bc9c89f760edcecdab9068b02c0..b7cafd74dfaa2a313e7da1541a89e02b202977e3 100644 --- a/packages/backend/src/server/api/endpoints/i/pin.ts +++ b/packages/backend/src/server/api/endpoints/i/pin.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -66,8 +66,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw err; }); - return await this.userEntityService.pack<true, true>(me.id, me, { - detail: true, + return await this.userEntityService.pack(me.id, me, { + schema: 'MeDetailed', }); }); } diff --git a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts index e43ab7c15eedb3071cca5360896e52b7e371576d..d1a8eccb1dc29d32b4ef87d8b4d68e501c664686 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/read-announcement.ts b/packages/backend/src/server/api/endpoints/i/read-announcement.ts index ba7859d0d416c9a953add63c9f5d057019183912..4db1ca73c145ae1e9552bd738eabf074870b1863 100644 --- a/packages/backend/src/server/api/endpoints/i/read-announcement.ts +++ b/packages/backend/src/server/api/endpoints/i/read-announcement.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts index 92295beeeef1ac03dcece3fd76a305ef6984ac5d..e1cdfdc18548b8abdfe701dfa3a0f76182bc8406 100644 --- a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts +++ b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts index 79a81cb73fbd9e8fa3f21d31cde8fabce4bad12f..f1797cfde7bb6b0a9cfac472b866593ac3e4bead 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts index d9b26cab2c89eeb18f56b8e8314cf8154f1f4594..d53c390460abc3ae573edf02358f697eb985093f 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -22,7 +22,16 @@ export const meta = { res: { type: 'object', - } + properties: { + updatedAt: { + type: 'string', + optional: false, + }, + value: { + optional: false, + }, + }, + }, } as const; export const paramDef = { @@ -50,7 +59,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } return { - updatedAt: item.updatedAt, + updatedAt: item.updatedAt.toISOString(), value: item.value, }; }); diff --git a/packages/backend/src/server/api/endpoints/i/registry/get.ts b/packages/backend/src/server/api/endpoints/i/registry/get.ts index c373410256f2eaa493f648dace689214a758c58a..d9a8fdd4499f905bd8c29e724970e9dcd203b961 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts index a91dcd95438132ee502676d7f601411675a070f1..3fe339606dbd080e5fdb3c18182fc06b53f5ee03 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -13,6 +13,9 @@ export const meta = { res: { type: 'object', + additionalProperties: { + type: 'string', + }, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys.ts b/packages/backend/src/server/api/endpoints/i/registry/keys.ts index ad203d5203f0d74215966840bba9c151e32a6ced..28f158c62d6caa6707ecff5d8183649d2361355e 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -10,6 +10,13 @@ import { RegistryApiService } from '@/core/RegistryApiService.js'; export const meta = { requireCredential: true, kind: 'read:account', + + res: { + type: 'array', + items: { + type: 'string', + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/i/registry/remove.ts b/packages/backend/src/server/api/endpoints/i/registry/remove.ts index 9cbe271b91a2bcdc7a38d99a08d0623c2285a2b7..cf965ba0cf60cb4e7e20c1eb6793e2b564d21be6 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/remove.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/remove.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/registry/scopes-with-domain.ts b/packages/backend/src/server/api/endpoints/i/registry/scopes-with-domain.ts index 0aca2a26fe4f30f887bea069515375c5731ed555..67a99b028a4f7d90427e1eee7127207c8c4b5542 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/scopes-with-domain.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/scopes-with-domain.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/registry/set.ts b/packages/backend/src/server/api/endpoints/i/registry/set.ts index c61d5b8727af306c0c1d49c10aca18b5527eda9e..8723035d84c7a352a8709f9092500f63d7385bdb 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/set.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/set.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/revoke-token.ts b/packages/backend/src/server/api/endpoints/i/revoke-token.ts index 98d866f867d4fa653dde407197a1a159ab8b2e93..c05ee93c6f596a9d99c1d454e97b5a9e70e66658 100644 --- a/packages/backend/src/server/api/endpoints/i/revoke-token.ts +++ b/packages/backend/src/server/api/endpoints/i/revoke-token.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -34,7 +34,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- ) { super(meta, paramDef, async (ps, me) => { if (ps.tokenId) { - const tokenExist = await this.accessTokensRepository.exist({ where: { id: ps.tokenId } }); + const tokenExist = await this.accessTokensRepository.exists({ where: { id: ps.tokenId } }); if (tokenExist) { await this.accessTokensRepository.delete({ @@ -43,7 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }); } } else if (ps.token) { - const tokenExist = await this.accessTokensRepository.exist({ where: { token: ps.token } }); + const tokenExist = await this.accessTokensRepository.exists({ where: { token: ps.token } }); if (tokenExist) { await this.accessTokensRepository.delete({ diff --git a/packages/backend/src/server/api/endpoints/i/signin-history.ts b/packages/backend/src/server/api/endpoints/i/signin-history.ts index f82e3f9b287bf5d99d1faee5a906c8e32530ceaf..76ad0bbe21a510cc8462da1d43a33eeccc7734f9 100644 --- a/packages/backend/src/server/api/endpoints/i/signin-history.ts +++ b/packages/backend/src/server/api/endpoints/i/signin-history.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/unpin.ts b/packages/backend/src/server/api/endpoints/i/unpin.ts index b59c0e954f5ad82a02354fd9c24e67ccb5593d49..74825cf9f3d529f29f39b2c494e97dacdb87b4ef 100644 --- a/packages/backend/src/server/api/endpoints/i/unpin.ts +++ b/packages/backend/src/server/api/endpoints/i/unpin.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -51,8 +51,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw err; }); - return await this.userEntityService.pack<true, true>(me.id, me, { - detail: true, + return await this.userEntityService.pack(me.id, me, { + schema: 'MeDetailed', }); }); } diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index 090b07be3c4be4cdf020574af3a89b5b29830a1b..08a8301bd1a136287d27e3238b352bf55520b4e4 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -44,7 +44,7 @@ export const meta = { res: { type: 'object', - ref: 'UserDetailed', + ref: 'MeDetailed', }, } as const; @@ -107,7 +107,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }); const iObj = await this.userEntityService.pack(me.id, me, { - detail: true, + schema: 'MeDetailed', includeSecrets: true, }); diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 22079de0420f70c07eac184ea6d0291cc92692da..06edb285781542f102b7748f361ce2c15c2f4107 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -1,10 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import RE2 from 're2'; -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { JSDOM } from 'jsdom'; @@ -33,6 +33,7 @@ import { HttpRequestService } from '@/core/HttpRequestService.js'; import type { Config } from '@/config.js'; import { safeForSql } from '@/misc/safe-for-sql.js'; import { AvatarDecorationService } from '@/core/AvatarDecorationService.js'; +import { notificationRecieveConfig } from '@/models/json-schema/user.js'; import { ApiLoggerService } from '../../ApiLoggerService.js'; import { ApiError } from '../../error.js'; @@ -200,7 +201,26 @@ export const paramDef = { mutedInstances: { type: 'array', items: { type: 'string', } }, - notificationRecieveConfig: { type: 'object' }, + notificationRecieveConfig: { + type: 'object', + nullable: false, + properties: { + note: notificationRecieveConfig, + follow: notificationRecieveConfig, + mention: notificationRecieveConfig, + reply: notificationRecieveConfig, + renote: notificationRecieveConfig, + quote: notificationRecieveConfig, + reaction: notificationRecieveConfig, + pollEnded: notificationRecieveConfig, + receiveFollowRequest: notificationRecieveConfig, + followRequestAccepted: notificationRecieveConfig, + roleAssigned: notificationRecieveConfig, + achievementEarned: notificationRecieveConfig, + app: notificationRecieveConfig, + test: notificationRecieveConfig, + }, + }, emailNotificationTypes: { type: 'array', items: { type: 'string', } }, @@ -468,9 +488,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- this.hashtagService.updateUsertags(user, tags); //#endregion - if (Object.keys(updates).length > 0) await this.usersRepository.update(user.id, updates); - if (Object.keys(updates).includes('alsoKnownAs')) { - this.cacheService.uriPersonCache.set(this.userEntityService.genLocalUserUri(user.id), { ...user, ...updates }); + if (Object.keys(updates).length > 0) { + await this.usersRepository.update(user.id, updates); + this.globalEventService.publishInternalEvent('localUserUpdated', { id: user.id }); } await this.userProfilesRepository.update(user.id, { @@ -478,8 +498,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- verifiedLinks: [], }); - const iObj = await this.userEntityService.pack<true, true>(user.id, user, { - detail: true, + const iObj = await this.userEntityService.pack(user.id, user, { + schema: 'MeDetailed', includeSecrets: isSecure, }); diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts index bdc9f9ea8b51750274cb7a8168586950fa2bb731..535a3ea308500c355975d1579214468af735386e 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -33,7 +33,7 @@ export const meta = { properties: { id: { type: 'string', - format: 'misskey:id' + format: 'misskey:id', }, userId: { type: 'string', @@ -45,7 +45,7 @@ export const meta = { items: { type: 'string', enum: webhookEventTypes, - } + }, }, url: { type: 'string' }, secret: { type: 'string' }, @@ -108,7 +108,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- url: webhook.url, secret: webhook.secret, active: webhook.active, - latestSentAt: webhook.latestSentAt?.toISOString(), + latestSentAt: webhook.latestSentAt ? webhook.latestSentAt.toISOString() : null, latestStatus: webhook.latestStatus, }; }); diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts b/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts index db7d0db13cf4de5af5408715af41d0cb379d538a..1b1ac0067018b8fe84bd3cd9b1cc0cc361c457f5 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts index afb2d0509e8e048b2810cdb3cd19590b60403a41..fe07afb2d08965bdb690696b5ce52cdcb0c676d1 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -23,7 +23,7 @@ export const meta = { properties: { id: { type: 'string', - format: 'misskey:id' + format: 'misskey:id', }, userId: { type: 'string', @@ -35,7 +35,7 @@ export const meta = { items: { type: 'string', enum: webhookEventTypes, - } + }, }, url: { type: 'string' }, secret: { type: 'string' }, @@ -43,8 +43,8 @@ export const meta = { latestSentAt: { type: 'string', format: 'date-time', nullable: true }, latestStatus: { type: 'integer', nullable: true }, }, - } - } + }, + }, } as const; export const paramDef = { @@ -73,7 +73,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- url: webhook.url, secret: webhook.secret, active: webhook.active, - latestSentAt: webhook.latestSentAt?.toISOString(), + latestSentAt: webhook.latestSentAt ? webhook.latestSentAt.toISOString() : null, latestStatus: webhook.latestStatus, } )); diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts index 5c6dd908b42c77f74b36b2afb735996c7c786c4b..5ddb79caf283c31b8378550a188be670f406b0f7 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -30,7 +30,7 @@ export const meta = { properties: { id: { type: 'string', - format: 'misskey:id' + format: 'misskey:id', }, userId: { type: 'string', @@ -42,7 +42,7 @@ export const meta = { items: { type: 'string', enum: webhookEventTypes, - } + }, }, url: { type: 'string' }, secret: { type: 'string' }, @@ -85,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- url: webhook.url, secret: webhook.secret, active: webhook.active, - latestSentAt: webhook.latestSentAt?.toISOString(), + latestSentAt: webhook.latestSentAt ? webhook.latestSentAt.toISOString() : null, latestStatus: webhook.latestStatus, }; }); diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts index b3e000524d0e115301c50983a81e4d1e91134e04..6e380d76f89e55cc671184e34e5bf50daf048666 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/invite/create.ts b/packages/backend/src/server/api/endpoints/invite/create.ts index 4f37f2f4bbf7665de13b64473e21c014401e0304..0ff125ad9cbe392cbb257114bc9256b0fa4db275 100644 --- a/packages/backend/src/server/api/endpoints/invite/create.ts +++ b/packages/backend/src/server/api/endpoints/invite/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/invite/delete.ts b/packages/backend/src/server/api/endpoints/invite/delete.ts index d84430a49f47b30f45292b3046f6aee8ee2b3ea0..e960ff9f4e890ac10b7d8cb952439ddc8e21d395 100644 --- a/packages/backend/src/server/api/endpoints/invite/delete.ts +++ b/packages/backend/src/server/api/endpoints/invite/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/invite/limit.ts b/packages/backend/src/server/api/endpoints/invite/limit.ts index fc3bb9bdc2468b9b33c0efaadb6ad74276ca1f94..2786bd98d5bf6a0c42e0b293c42c7667517c85f9 100644 --- a/packages/backend/src/server/api/endpoints/invite/limit.ts +++ b/packages/backend/src/server/api/endpoints/invite/limit.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/invite/list.ts b/packages/backend/src/server/api/endpoints/invite/list.ts index 6734f27e1400a7169e3523dc2dbcbfc922268714..23aefe83a2661549ed01122eeff3e4786e89c46b 100644 --- a/packages/backend/src/server/api/endpoints/invite/list.ts +++ b/packages/backend/src/server/api/endpoints/invite/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 1d0c102c9de0d7eac107ccbef740d1302c030ea4..5460635e1d406dc4a02bc06ecf5951fd6c757074 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -1,18 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import { IsNull, LessThanOrEqual, MoreThan, Brackets } from 'typeorm'; -import { Inject, Injectable } from '@nestjs/common'; -import JSON5 from 'json5'; -import type { AdsRepository, UsersRepository } from '@/models/_.js'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { MetaService } from '@/core/MetaService.js'; -import type { Config } from '@/config.js'; -import { DI } from '@/di-symbols.js'; -import { DEFAULT_POLICIES } from '@/core/RoleService.js'; +import { MetaEntityService } from '@/core/entities/MetaEntityService.js'; export const meta = { tags: ['meta'], @@ -21,284 +14,10 @@ export const meta = { res: { type: 'object', - optional: false, nullable: false, - properties: { - maintainerName: { - type: 'string', - optional: false, nullable: true, - }, - maintainerEmail: { - type: 'string', - optional: false, nullable: true, - }, - version: { - type: 'string', - optional: false, nullable: false, - }, - name: { - type: 'string', - optional: false, nullable: false, - }, - shortName: { - type: 'string', - optional: false, nullable: true, - }, - uri: { - type: 'string', - optional: false, nullable: false, - format: 'url', - example: 'https://misskey.example.com', - }, - description: { - type: 'string', - optional: false, nullable: true, - }, - langs: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'string', - optional: false, nullable: false, - }, - }, - tosUrl: { - type: 'string', - optional: false, nullable: true, - }, - repositoryUrl: { - type: 'string', - optional: false, nullable: false, - default: 'https://github.com/misskey-dev/misskey', - }, - feedbackUrl: { - type: 'string', - optional: false, nullable: false, - default: 'https://github.com/misskey-dev/misskey/issues/new', - }, - defaultDarkTheme: { - type: 'string', - optional: false, nullable: true, - }, - defaultLightTheme: { - type: 'string', - optional: false, nullable: true, - }, - defaultLike: { - type: 'string', - optional: false, nullable: true, - }, - disableRegistration: { - type: 'boolean', - optional: false, nullable: false, - }, - cacheRemoteFiles: { - type: 'boolean', - optional: false, nullable: false, - }, - cacheRemoteSensitiveFiles: { - type: 'boolean', - optional: false, nullable: false, - }, - emailRequiredForSignup: { - type: 'boolean', - optional: false, nullable: false, - }, - approvalRequiredForSignup: { - type: 'boolean', - optional: false, nullable: false, - }, - enableHcaptcha: { - type: 'boolean', - optional: false, nullable: false, - }, - hcaptchaSiteKey: { - type: 'string', - optional: false, nullable: true, - }, - enableRecaptcha: { - type: 'boolean', - optional: false, nullable: false, - }, - recaptchaSiteKey: { - type: 'string', - optional: false, nullable: true, - }, - enableTurnstile: { - type: 'boolean', - optional: false, nullable: false, - }, - turnstileSiteKey: { - type: 'string', - optional: false, nullable: true, - }, - swPublickey: { - type: 'string', - optional: false, nullable: true, - }, - mascotImageUrl: { - type: 'string', - optional: false, nullable: false, - default: '/assets/ai.png', - }, - bannerUrl: { - type: 'string', - optional: false, nullable: false, - }, - serverErrorImageUrl: { - type: 'string', - optional: false, nullable: true, - }, - infoImageUrl: { - type: 'string', - optional: false, nullable: true, - }, - notFoundImageUrl: { - type: 'string', - optional: false, nullable: true, - }, - iconUrl: { - type: 'string', - optional: false, nullable: true, - }, - maxNoteTextLength: { - type: 'number', - optional: false, nullable: false, - }, - ads: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - properties: { - id: { - type: 'string', - optional: false, nullable: false, - format: 'id', - example: 'xxxxxxxxxx', - }, - url: { - type: 'string', - optional: false, nullable: false, - format: 'url', - }, - place: { - type: 'string', - optional: false, nullable: false, - }, - ratio: { - type: 'number', - optional: false, nullable: false, - }, - imageUrl: { - type: 'string', - optional: false, nullable: false, - format: 'url', - }, - dayOfWeek: { - type: 'integer', - optional: false, nullable: false, - }, - }, - }, - }, - notesPerOneAd: { - type: 'number', - optional: false, nullable: false, - default: 0, - }, - requireSetup: { - type: 'boolean', - optional: false, nullable: false, - example: false, - }, - enableEmail: { - type: 'boolean', - optional: false, nullable: false, - }, - enableServiceWorker: { - type: 'boolean', - optional: false, nullable: false, - }, - translatorAvailable: { - type: 'boolean', - optional: false, nullable: false, - }, - proxyAccountName: { - type: 'string', - optional: false, nullable: true, - }, - mediaProxy: { - type: 'string', - optional: false, nullable: false, - }, - features: { - type: 'object', - optional: true, nullable: false, - properties: { - registration: { - type: 'boolean', - optional: false, nullable: false, - }, - localTimeline: { - type: 'boolean', - optional: false, nullable: false, - }, - globalTimeline: { - type: 'boolean', - optional: false, nullable: false, - }, - hcaptcha: { - type: 'boolean', - optional: false, nullable: false, - }, - recaptcha: { - type: 'boolean', - optional: false, nullable: false, - }, - objectStorage: { - type: 'boolean', - optional: false, nullable: false, - }, - serviceWorker: { - type: 'boolean', - optional: false, nullable: false, - }, - miauth: { - type: 'boolean', - optional: true, nullable: false, - default: true, - }, - }, - }, - backgroundImageUrl: { - type: 'string', - optional: false, nullable: true, - }, - impressumUrl: { - type: 'string', - optional: false, nullable: true, - }, - logoImageUrl: { - type: 'string', - optional: false, nullable: true, - }, - privacyPolicyUrl: { - type: 'string', - optional: false, nullable: true, - }, - serverRules: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'string', - }, - }, - themeColor: { - type: 'string', - optional: false, nullable: true, - }, - }, + oneOf: [ + { type: 'object', ref: 'MetaLite' }, + { type: 'object', ref: 'MetaDetailed' }, + ], }, } as const; @@ -313,118 +32,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.adsRepository) - private adsRepository: AdsRepository, - - private userEntityService: UserEntityService, - private metaService: MetaService, + private metaEntityService: MetaEntityService, ) { super(meta, paramDef, async (ps, me) => { - const instance = await this.metaService.fetch(true); - - const ads = await this.adsRepository.createQueryBuilder('ads') - .where('ads.expiresAt > :now', { now: new Date() }) - .andWhere('ads.startsAt <= :now', { now: new Date() }) - .andWhere(new Brackets(qb => { - // 曜日ã®ãƒ“ットフラグを確èªã™ã‚‹ - qb.where('ads.dayOfWeek & :dayOfWeek > 0', { dayOfWeek: 1 << new Date().getDay() }) - .orWhere('ads.dayOfWeek = 0'); - })) - .getMany(); - - const response: any = { - maintainerName: instance.maintainerName, - maintainerEmail: instance.maintainerEmail, - - version: this.config.version, - - name: instance.name, - shortName: instance.shortName, - uri: this.config.url, - description: instance.description, - langs: instance.langs, - tosUrl: instance.termsOfServiceUrl, - repositoryUrl: instance.repositoryUrl, - feedbackUrl: instance.feedbackUrl, - impressumUrl: instance.impressumUrl, - privacyPolicyUrl: instance.privacyPolicyUrl, - disableRegistration: instance.disableRegistration, - emailRequiredForSignup: instance.emailRequiredForSignup, - approvalRequiredForSignup: instance.approvalRequiredForSignup, - enableHcaptcha: instance.enableHcaptcha, - hcaptchaSiteKey: instance.hcaptchaSiteKey, - enableRecaptcha: instance.enableRecaptcha, - enableAchievements: instance.enableAchievements, - recaptchaSiteKey: instance.recaptchaSiteKey, - enableTurnstile: instance.enableTurnstile, - turnstileSiteKey: instance.turnstileSiteKey, - swPublickey: instance.swPublicKey, - themeColor: instance.themeColor, - mascotImageUrl: instance.mascotImageUrl, - bannerUrl: instance.bannerUrl, - infoImageUrl: instance.infoImageUrl, - serverErrorImageUrl: instance.serverErrorImageUrl, - notFoundImageUrl: instance.notFoundImageUrl, - iconUrl: instance.iconUrl, - backgroundImageUrl: instance.backgroundImageUrl, - logoImageUrl: instance.logoImageUrl, - maxNoteTextLength: this.config.maxNoteLength, - // クライアントã®æ‰‹é–“を減らã™ãŸã‚ã‚らã‹ã˜ã‚JSONã«å¤‰æ›ã—ã¦ãŠã - defaultLightTheme: instance.defaultLightTheme ? JSON.stringify(JSON5.parse(instance.defaultLightTheme)) : null, - defaultDarkTheme: instance.defaultDarkTheme ? JSON.stringify(JSON5.parse(instance.defaultDarkTheme)) : null, - defaultLike: instance.defaultLike, - ads: ads.map(ad => ({ - id: ad.id, - url: ad.url, - place: ad.place, - ratio: ad.ratio, - imageUrl: ad.imageUrl, - dayOfWeek: ad.dayOfWeek, - })), - notesPerOneAd: instance.notesPerOneAd, - enableEmail: instance.enableEmail, - enableServiceWorker: instance.enableServiceWorker, - - translatorAvailable: instance.deeplAuthKey != null, - - serverRules: instance.serverRules, - - policies: { ...DEFAULT_POLICIES, ...instance.policies }, - - mediaProxy: this.config.mediaProxy, - - ...(ps.detail ? { - cacheRemoteFiles: instance.cacheRemoteFiles, - cacheRemoteSensitiveFiles: instance.cacheRemoteSensitiveFiles, - requireSetup: (await this.usersRepository.countBy({ - host: IsNull(), - })) === 0, - } : {}), - }; - - if (ps.detail) { - const proxyAccount = instance.proxyAccountId ? await this.userEntityService.pack(instance.proxyAccountId).catch(() => null) : null; - - response.proxyAccountName = proxyAccount ? proxyAccount.username : null; - response.features = { - registration: !instance.disableRegistration, - emailRequiredForSignup: instance.emailRequiredForSignup, - hcaptcha: instance.enableHcaptcha, - recaptcha: instance.enableRecaptcha, - turnstile: instance.enableTurnstile, - objectStorage: instance.useObjectStorage, - serviceWorker: instance.enableServiceWorker, - miauth: true, - }; - } - - return response; + return ps.detail ? await this.metaEntityService.packDetailed() : await this.metaEntityService.pack(); }); } } diff --git a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts index cac8f41f8e4decff258acb9141b9591dfadffe58..fc9a8f3ebebd787b56a9f74a2ddc392de36a7635 100644 --- a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts +++ b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts index 49c2b5707deb6f2b2ab41c146ecb43de13ec9fc0..e39c133b43869901de41c494c4e8b7bc7c8b4edc 100644 --- a/packages/backend/src/server/api/endpoints/mute/create.ts +++ b/packages/backend/src/server/api/endpoints/mute/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -83,7 +83,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }); // Check if already muting - const exist = await this.mutingsRepository.exist({ + const exist = await this.mutingsRepository.exists({ where: { muterId: muter.id, muteeId: mutee.id, diff --git a/packages/backend/src/server/api/endpoints/mute/delete.ts b/packages/backend/src/server/api/endpoints/mute/delete.ts index a3fd2dd82f56ff1bce29f685df67e9312acb29ba..d11832858e3be250f097d406888fd972a3bc58a7 100644 --- a/packages/backend/src/server/api/endpoints/mute/delete.ts +++ b/packages/backend/src/server/api/endpoints/mute/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/mute/list.ts b/packages/backend/src/server/api/endpoints/mute/list.ts index 2a41182ebc61c2c4bc3650f5e5d67e75104685ad..23204f2829d417f381627e43d323f4a974fb0b48 100644 --- a/packages/backend/src/server/api/endpoints/mute/list.ts +++ b/packages/backend/src/server/api/endpoints/mute/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/my/apps.ts b/packages/backend/src/server/api/endpoints/my/apps.ts index 1b70b85b07dc1a7cd3d1dd3e8acc0e9e7ec59df1..c04a92626fefc61417ac31f2a086a6854d44953b 100644 --- a/packages/backend/src/server/api/endpoints/my/apps.ts +++ b/packages/backend/src/server/api/endpoints/my/apps.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 95ba5e8b64ce970a533edddcc9ee7f7a7ae83e6f..9938322a2ab435991779f11a1d566b183d98beef 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index a16740c816d6c14dc297aacdf491975d5287f35a..2654e196b2efbf9b459444d79ee8ab9ef2323c5f 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -74,7 +74,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- this.queryService.generateVisibilityQuery(query, me); if (me) { - this.queryService.generateMutedUserQuery(query, me); this.queryService.generateBlockedUserQuery(query, me); } diff --git a/packages/backend/src/server/api/endpoints/notes/clips.ts b/packages/backend/src/server/api/endpoints/notes/clips.ts index 677c0ea3076934c40dd82dbb37bc7c389fa74cab..29cab9f212d3993427ebf8c4a7be437031b6d38b 100644 --- a/packages/backend/src/server/api/endpoints/notes/clips.ts +++ b/packages/backend/src/server/api/endpoints/notes/clips.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index bb22ee4907bdad5070e817e283c36d7eb269998e..37bc5cc878e2e57e8e805d5653ec2a34a3ccd8fc 100644 --- a/packages/backend/src/server/api/endpoints/notes/conversation.ts +++ b/packages/backend/src/server/api/endpoints/notes/conversation.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/create.test.ts b/packages/backend/src/server/api/endpoints/notes/create.test.ts index 0de5a14a9378c102ef7357d7a4cd6541ec5435ab..f3d887bb200af9f3c8d00272c280d53b0b63fd0a 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.test.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.test.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -34,19 +34,18 @@ describe('api:notes/create', () => { .toBe(VALID); }); - // TODO - //test('null post', () => { - // expect(v({ text: null })) - // .toBe(INVALID); - //}); + test('null post', () => { + expect(v({ text: null })) + .toBe(INVALID); + }); test('0 characters post', () => { expect(v({ text: '' })) .toBe(INVALID); }); - test('over 3000 characters post', async () => { - expect(v({ text: await tooLong })) + test('whitespace-only post', () => { + expect(v({ text: ' ' })) .toBe(INVALID); }); }); diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index ac0a7f3b513db3ce587945480a54c1088fb5e348..95ebda2f2128e0ce715b4cc2fa85c59b202dd6c5 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -17,6 +17,9 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { NoteCreateService } from '@/core/NoteCreateService.js'; import { DI } from '@/di-symbols.js'; import { isPureRenote } from '@/misc/is-pure-renote.js'; +import { MetaService } from '@/core/MetaService.js'; +import { UtilityService } from '@/core/UtilityService.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -88,6 +91,12 @@ export const meta = { id: '3ac74a84-8fd5-4bb0-870f-01804f82ce16', }, + cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility: { + message: 'You cannot reply to a specified visibility note with extended visibility.', + code: 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY', + id: 'ed940410-535c-4d5e-bfa3-af798671e93c', + }, + cannotCreateAlreadyExpiredPoll: { message: 'Poll is already expired.', code: 'CANNOT_CREATE_ALREADY_EXPIRED_POLL', @@ -117,6 +126,18 @@ export const meta = { code: 'CANNOT_RENOTE_OUTSIDE_OF_CHANNEL', id: '33510210-8452-094c-6227-4a6c05d99f00', }, + + containsProhibitedWords: { + message: 'Cannot post because it contains prohibited words.', + code: 'CONTAINS_PROHIBITED_WORDS', + id: 'aa6e01d3-a85c-669d-758a-76aab43af334', + }, + + containsTooManyMentions: { + message: 'Cannot post because it exceeds the allowed number of mentions.', + code: 'CONTAINS_TOO_MANY_MENTIONS', + id: '4de0363a-3046-481b-9b0f-feff3e211025', + }, }, } as const; @@ -167,7 +188,7 @@ export const paramDef = { uniqueItems: true, minItems: 2, maxItems: 10, - items: { type: 'string', minLength: 1, maxLength: 50 }, + items: { type: 'string', minLength: 1, maxLength: 150 }, }, multiple: { type: 'boolean' }, expiresAt: { type: 'integer', nullable: true }, @@ -177,13 +198,32 @@ export const paramDef = { }, }, // (re)note with text, files and poll are optional - anyOf: [ - { required: ['text'] }, - { required: ['renoteId'] }, - { required: ['fileIds'] }, - { required: ['mediaIds'] }, - { required: ['poll'] }, - ], + if: { + properties: { + renoteId: { + type: 'null', + }, + fileIds: { + type: 'null', + }, + mediaIds: { + type: 'null', + }, + poll: { + type: 'null', + }, + }, + }, + then: { + properties: { + text: { + type: 'string', + minLength: 1, + pattern: '[^\\s]+', + }, + }, + required: ['text'], + }, } as const; @Injectable() @@ -252,7 +292,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- // Check blocking if (renote.userId !== me.id) { - const blockExist = await this.blockingsRepository.exist({ + const blockExist = await this.blockingsRepository.exists({ where: { blockerId: renote.userId, blockeeId: me.id, @@ -295,12 +335,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } else if (isPureRenote(reply)) { throw new ApiError(meta.errors.cannotReplyToPureRenote); } else if (!await this.noteEntityService.isVisibleForMe(reply, me.id)) { - throw new ApiError(meta.errors.noSuchReplyTarget); + throw new ApiError(meta.errors.cannotReplyToInvisibleNote); + } else if (reply.visibility === 'specified' && ps.visibility !== 'specified') { + throw new ApiError(meta.errors.cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility); } // Check blocking if (reply.userId !== me.id) { - const blockExist = await this.blockingsRepository.exist({ + const blockExist = await this.blockingsRepository.exists({ where: { blockerId: reply.userId, blockeeId: me.id, @@ -332,31 +374,43 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } // æŠ•ç¨¿ã‚’ä½œæˆ - const note = await this.noteCreateService.create(me, { - createdAt: new Date(), - files: files, - poll: ps.poll ? { - choices: ps.poll.choices, - multiple: ps.poll.multiple ?? false, - expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, - } : undefined, - text: ps.text ?? undefined, - reply, - renote, - cw: ps.cw, - localOnly: ps.localOnly, - reactionAcceptance: ps.reactionAcceptance, - visibility: ps.visibility, - visibleUsers, - channel, - apMentions: ps.noExtractMentions ? [] : undefined, - apHashtags: ps.noExtractHashtags ? [] : undefined, - apEmojis: ps.noExtractEmojis ? [] : undefined, - }); - - return { - createdNote: await this.noteEntityService.pack(note, me), - }; + try { + const note = await this.noteCreateService.create(me, { + createdAt: new Date(), + files: files, + poll: ps.poll ? { + choices: ps.poll.choices, + multiple: ps.poll.multiple ?? false, + expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, + } : undefined, + text: ps.text ?? undefined, + reply, + renote, + cw: ps.cw, + localOnly: ps.localOnly, + reactionAcceptance: ps.reactionAcceptance, + visibility: ps.visibility, + visibleUsers, + channel, + apMentions: ps.noExtractMentions ? [] : undefined, + apHashtags: ps.noExtractHashtags ? [] : undefined, + apEmojis: ps.noExtractEmojis ? [] : undefined, + }); + + return { + createdNote: await this.noteEntityService.pack(note, me), + }; + } catch (e) { + // TODO: ä»–ã®Errorã‚‚ã“ã“ã§ã‚ャッãƒã—ã¦ã‚¨ãƒ©ãƒ¼ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’当ã¦ã‚‹ã‚ˆã†ã«ã—ãŸã„ + if (e instanceof IdentifiableError) { + if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') { + throw new ApiError(meta.errors.containsProhibitedWords); + } else if (e.id === '9f466dab-c856-48cd-9e65-ff90ff750580') { + throw new ApiError(meta.errors.containsTooManyMentions); + } + } + throw e; + } }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index 55aaaf4f78255730d24a81467d7e4a738fa6747d..9d7c9a9081daf13e09670a1cdb0bb899027da89e 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/edit.ts b/packages/backend/src/server/api/endpoints/notes/edit.ts index 0c9c0d3baf91390ccc7e9dbdd8e97f92e52ff85a..3caeda288b3f3b1de7ad30285239fb1f5d592089 100644 --- a/packages/backend/src/server/api/endpoints/notes/edit.ts +++ b/packages/backend/src/server/api/endpoints/notes/edit.ts @@ -11,6 +11,8 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { NoteEditService } from '@/core/NoteEditService.js'; import { DI } from '@/di-symbols.js'; +import { isPureRenote } from '@/misc/is-pure-renote.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -18,6 +20,8 @@ export const meta = { requireCredential: true, + prohibitMoved: true, + limit: { duration: ms('1hour'), max: 300, @@ -52,18 +56,42 @@ export const meta = { id: 'fd4cc33e-2a37-48dd-99cc-9b806eb2031a', }, + cannotRenoteDueToVisibility: { + message: 'You can not Renote due to target visibility.', + code: 'CANNOT_RENOTE_DUE_TO_VISIBILITY', + id: 'be9529e9-fe72-4de0-ae43-0b363c4938af', + }, + noSuchReplyTarget: { message: 'No such reply target.', code: 'NO_SUCH_REPLY_TARGET', id: '749ee0f6-d3da-459a-bf02-282e2da4292c', }, + cannotReplyToInvisibleNote: { + message: 'You cannot reply to an invisible Note.', + code: 'CANNOT_REPLY_TO_AN_INVISIBLE_NOTE', + id: 'b98980fa-3780-406c-a935-b6d0eeee10d1', + }, + cannotReplyToPureRenote: { message: 'You can not reply to a pure Renote.', code: 'CANNOT_REPLY_TO_A_PURE_RENOTE', id: '3ac74a84-8fd5-4bb0-870f-01804f82ce15', }, + maxLength: { + message: 'You tried posting a note which is too long.', + code: 'MAX_LENGTH', + id: '3ac74a84-8fd5-4bb0-870f-01804f82ce16', + }, + + cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility: { + message: 'You cannot reply to a specified visibility note with extended visibility.', + code: 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY', + id: 'ed940410-535c-4d5e-bfa3-af798671e93c', + }, + cannotCreateAlreadyExpiredPoll: { message: 'Poll is already expired.', code: 'CANNOT_CREATE_ALREADY_EXPIRED_POLL', @@ -82,6 +110,12 @@ export const meta = { id: 'b390d7e1-8a5e-46ed-b625-06271cafd3d3', }, + noSuchFile: { + message: 'Some files are not found.', + code: 'NO_SUCH_FILE', + id: 'b6992544-63e7-67f0-fa7f-32444b1b5306', + }, + accountLocked: { message: 'You migrated. Your account is now locked.', code: 'ACCOUNT_LOCKED', @@ -136,10 +170,16 @@ export const meta = { id: '33510210-8452-094c-6227-4a6c05d99f02', }, - maxLength: { - message: 'You tried posting a note which is too long.', - code: 'MAX_LENGTH', - id: '3ac74a84-8fd5-4bb0-870f-01804f82ce16', + containsProhibitedWords: { + message: 'Cannot post because it contains prohibited words.', + code: 'CONTAINS_PROHIBITED_WORDS', + id: 'aa6e01d3-a85c-669d-758a-76aab43af334', + }, + + containsTooManyMentions: { + message: 'Cannot post because it exceeds the allowed number of mentions.', + code: 'CONTAINS_TOO_MANY_MENTIONS', + id: '4de0363a-3046-481b-9b0f-feff3e211025', }, }, } as const; @@ -194,7 +234,7 @@ export const paramDef = { uniqueItems: true, minItems: 2, maxItems: 10, - items: { type: 'string', minLength: 1, maxLength: 50 }, + items: { type: 'string', minLength: 1, maxLength: 150 }, }, multiple: { type: 'boolean' }, expiresAt: { type: 'integer', nullable: true }, @@ -203,38 +243,33 @@ export const paramDef = { required: ['choices'], }, }, - anyOf: [ - { - // (re)note with text, files and poll are optional - properties: { - text: { - type: 'string', - minLength: 1, - nullable: false, - }, + // (re)note with text, files and poll are optional + if: { + properties: { + renoteId: { + type: 'null', }, - required: ['text'], - }, - { - // (re)note with files, text and poll are optional - required: ['fileIds'], - }, - { - // (re)note with files, text and poll are optional - required: ['mediaIds'], - }, - { - // (re)note with poll, text and files are optional - properties: { - poll: { type: 'object', nullable: false }, + fileIds: { + type: 'null', + }, + mediaIds: { + type: 'null', + }, + poll: { + type: 'null', }, - required: ['poll'], }, - { - // pure renote - required: ['renoteId'], + }, + then: { + properties: { + text: { + type: 'string', + minLength: 1, + pattern: '[^\\s]+', + }, }, - ], + required: ['text'], + }, } as const; @Injectable() @@ -285,7 +320,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .getMany(); if (files.length !== fileIds.length) { - throw new ApiError(meta.errors.noSuchNote); + throw new ApiError(meta.errors.noSuchFile); } } @@ -294,14 +329,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (ps.renoteId === ps.editId) { throw new ApiError(meta.errors.cannotQuoteCurrentPost); } - + if (ps.renoteId != null) { // Fetch renote to note renote = await this.notesRepository.findOneBy({ id: ps.renoteId }); if (renote == null) { throw new ApiError(meta.errors.noSuchRenoteTarget); - } else if (renote.renoteId && !renote.text && !renote.fileIds && !renote.hasPoll) { + } else if (isPureRenote(renote)) { throw new ApiError(meta.errors.cannotReRenote); } @@ -311,7 +346,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- // Check blocking if (renote.userId !== me.id) { - const blockExist = await this.blockingsRepository.exist({ + const blockExist = await this.blockingsRepository.exists({ where: { blockerId: renote.userId, blockeeId: me.id, @@ -322,6 +357,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } } + if (renote.visibility === 'followers' && renote.userId !== me.id) { + // 他人ã®followers noteã¯reject + throw new ApiError(meta.errors.cannotRenoteDueToVisibility); + } else if (renote.visibility === 'specified') { + // specified / direct noteã¯reject + throw new ApiError(meta.errors.cannotRenoteDueToVisibility); + } + if (renote.channelId && renote.channelId !== ps.channelId) { // ãƒãƒ£ãƒ³ãƒãƒ«ã®ãƒŽãƒ¼ãƒˆã«å¯¾ã—リノートè¦æ±‚ãŒããŸã¨ãã€ãƒãƒ£ãƒ³ãƒãƒ«å¤–ã¸ã®ãƒªãƒŽãƒ¼ãƒˆå¯å¦ã‚’ãƒã‚§ãƒƒã‚¯ // リノートã®ãƒ¦ãƒ¼ã‚¹ã‚±ãƒ¼ã‚¹ã®ã†ã¡ã€ãƒãƒ£ãƒ³ãƒãƒ«å†…→ãƒãƒ£ãƒ³ãƒãƒ«å¤–ã¯å°‘æ•°ã ã¨è€ƒãˆã‚‰ã‚Œã‚‹ãŸã‚ã€JOINã¯ã›ãšå¿…è¦ãªæ™‚ã«éƒ½åº¦å–å¾—ã™ã‚‹ @@ -343,13 +386,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (reply == null) { throw new ApiError(meta.errors.noSuchReplyTarget); - } else if (reply.renoteId && !reply.text && !reply.fileIds && !reply.hasPoll) { + } else if (isPureRenote(reply)) { throw new ApiError(meta.errors.cannotReplyToPureRenote); + } else if (!await this.noteEntityService.isVisibleForMe(reply, me.id)) { + throw new ApiError(meta.errors.cannotReplyToInvisibleNote); + } else if (reply.visibility === 'specified' && ps.visibility !== 'specified') { + throw new ApiError(meta.errors.cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility); } // Check blocking if (reply.userId !== me.id) { - const blockExist = await this.blockingsRepository.exist({ + const blockExist = await this.blockingsRepository.exists({ where: { blockerId: reply.userId, blockeeId: me.id, @@ -379,32 +426,43 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw new ApiError(meta.errors.noSuchChannel); } } + try { + // æŠ•ç¨¿ã‚’ä½œæˆ + const note = await this.noteEditService.edit(me, ps.editId!, { + files: files, + poll: ps.poll ? { + choices: ps.poll.choices, + multiple: ps.poll.multiple ?? false, + expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, + } : undefined, + text: ps.text ?? undefined, + reply, + renote, + cw: ps.cw, + localOnly: ps.localOnly, + reactionAcceptance: ps.reactionAcceptance, + visibility: ps.visibility, + visibleUsers, + channel, + apMentions: ps.noExtractMentions ? [] : undefined, + apHashtags: ps.noExtractHashtags ? [] : undefined, + apEmojis: ps.noExtractEmojis ? [] : undefined, + }); - // æŠ•ç¨¿ã‚’ä½œæˆ - const note = await this.noteEditService.edit(me, ps.editId!, { - files: files, - poll: ps.poll ? { - choices: ps.poll.choices, - multiple: ps.poll.multiple ?? false, - expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, - } : undefined, - text: ps.text ?? undefined, - reply, - renote, - cw: ps.cw, - localOnly: ps.localOnly, - reactionAcceptance: ps.reactionAcceptance, - visibility: ps.visibility, - visibleUsers, - channel, - apMentions: ps.noExtractMentions ? [] : undefined, - apHashtags: ps.noExtractHashtags ? [] : undefined, - apEmojis: ps.noExtractEmojis ? [] : undefined, - }); - - return { - createdNote: await this.noteEntityService.pack(note, me), - }; + return { + createdNote: await this.noteEntityService.pack(note, me), + }; + } catch (e) { + // TODO: ä»–ã®Errorã‚‚ã“ã“ã§ã‚ャッãƒã—ã¦ã‚¨ãƒ©ãƒ¼ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’当ã¦ã‚‹ã‚ˆã†ã«ã—ãŸã„ + if (e instanceof IdentifiableError) { + if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') { + throw new ApiError(meta.errors.containsProhibitedWords); + } else if (e.id === '9f466dab-c856-48cd-9e65-ff90ff750580') { + throw new ApiError(meta.errors.containsTooManyMentions); + } + } + throw e; + } }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts index ed3dce7f35433d56b0d9d3688186873ed34647c2..804071b3d420a9f3423eb54caa8251027b5bb7e0 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -67,7 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }); // if already favorited - const exist = await this.noteFavoritesRepository.exist({ + const exist = await this.noteFavoritesRepository.exists({ where: { noteId: note.id, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts index 8ab9775a2cb75d61e73e81315acd58bfb95b8601..2036facdba6b8c07c5d4a8ecdbd63006b2ffdd03 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 31b8d1ad2d1cf5f732460f57b20c7c257ed7abff..dcd971360d2e44dd01e7e47d40058bcfaaa3206b 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 844de80268cc83f66c8601f062aa03d1f498b084..d660f3fb694a0a692853228c3d8d88a80ef8d5fe 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 13cfb31ad02416a3cb89a568fa5c5e9a4632aac0..ce5ddadb9e2d71183907e1e8e566f6507ef0d06d 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 8db7d25e03646936d8c6c87f4ca2bbb84ff641f4..5c3c7ae7d0c457f38c138d99889ba780f3804978 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 2317f8f7b2a7c4df987013f4d3cf6a434bd7ba8b..5558dd3a8b328abe41abe755ed72d50f392dfa61 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -61,9 +61,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(new Brackets(qb => { - qb - .where(`'{"${me.id}"}' <@ note.mentions`) - .orWhere(`'{"${me.id}"}' <@ note.visibleUserIds`); + qb // ã“ã®meIdAsListパラメータã¯queryServiceã®generateVisibilityQueryã§ã‚»ãƒƒãƒˆã•ã‚Œã‚‹ + .where(':meIdAsList <@ note.mentions') + .orWhere(':meIdAsList <@ note.visibleUserIds'); })) // Avoid scanning primary key index .orderBy('CONCAT(note.id)', 'DESC') diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts index 90af29a695f006a3413f9a00586bf974dc3d72a5..ba38573065c3c51f838b3eb0582379ee2386f0a1 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts index 734c3f0e631649f7d2d517d55d8338d00a1c53c7..a91c506afd2721e9e501969b070e40ac91c694a2 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index a2c17781993e8a9089ca542779eaa3bd9f494b7d..3beb5064ae3459cd15ddd836f5baaef9a8869f8e 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -74,6 +74,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- query.andWhere('reaction.reaction = :type', { type }); } + if (me) this.queryService.generateMutedUserQuery(query, me); + if (me) this.queryService.generateBlockedUserQuery(query, me); + const reactions = await query.limit(ps.limit).getMany(); return await Promise.all(reactions.map(reaction => this.noteReactionEntityService.pack(reaction, me))); diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts index ff22ef1322ba0abd941e1a6f71d64ac7330cb2b0..b9899608bf16238b977f464f8526d9921dda97f0 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts index b43ab044fad201390e6353b59e43bf57b18b599f..600c1e0019fb1e8e2ff545e11817b0893ecd82ad 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -19,8 +19,8 @@ export const meta = { limit: { duration: ms('1hour'), - max: 60, - minInterval: ms('3sec'), + max: 80, + minInterval: ms('1sec'), }, errors: { diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 063650b3c7636ec58f5fdae9ff06f5864aee2643..a88c286f646547dc730847a2e363a9560f6fe456 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 70142c9818b37a45efba237b9c6463bf6329629a..5f32332a6adb49afe0f23c0d17c16ed344de1d49 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index 89e05fd57e6c1ec89dd9ceab4b509f2baef2f1ee..55ff6771b1a2313f843fba3099b141a75c330ec5 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -104,14 +104,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- try { if (ps.tag) { if (!safeForSql(normalizeForSearch(ps.tag))) throw new Error('Injection'); - query.andWhere(`'{"${normalizeForSearch(ps.tag)}"}' <@ note.tags`); + query.andWhere(':tag <@ note.tags', { tag: [normalizeForSearch(ps.tag)] }); } else { query.andWhere(new Brackets(qb => { for (const tags of ps.query!) { qb.orWhere(new Brackets(qb => { for (const tag of tags) { if (!safeForSql(normalizeForSearch(tag))) throw new Error('Injection'); - qb.andWhere(`'{"${normalizeForSearch(tag)}"}' <@ note.tags`); + qb.andWhere(':tag <@ note.tags', { tag: [normalizeForSearch(tag)] }); } })); } diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index 06efa3d951a93589a6d7aeaca18cfb215de1c7b0..e140436d6b7f7daafefecf1d008118e19c75a519 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index b3107f675437036dcfdc1db9cf858df7cec01b44..f82ba5473d2d2ab3eb8a04edd61e69711683f6b0 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -54,7 +54,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- this.queryService.generateVisibilityQuery(query, me); if (me) { - this.queryService.generateMutedUserQuery(query, me); this.queryService.generateBlockedUserQuery(query, me); } diff --git a/packages/backend/src/server/api/endpoints/notes/state.ts b/packages/backend/src/server/api/endpoints/notes/state.ts index 20faea566d3479f338823961f99be27c44139486..4c1eb865426f3f1d73e2e10bc4e10cf243dc8a45 100644 --- a/packages/backend/src/server/api/endpoints/notes/state.ts +++ b/packages/backend/src/server/api/endpoints/notes/state.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts index b2cdaa00ac21db340a92b7b877b3de330e667849..732d644a290c42a509bad8a36dcbb34964f6bf14 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts index d3f1787ee47991bfc495c4de28b0d143f45b4002..d94d6cd652fdb5973cde6405c206e0dbdf154b7c 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 3dcebe7e297ba279637d37c20a4f6102da6bbca5..1e5869663fbbd36c22ea2d85a623d5b479cfeda7 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index 698c37b616c313f05fd1f170ac9a81dd041b60fe..a935f761b75fb7f58a903c671448f3eaf17d9545 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -81,19 +81,23 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const instance = await this.metaService.fetch(); - if (instance.deeplAuthKey == null) { + if (instance.deeplAuthKey == null && !instance.deeplFreeMode) { return 204; // TODO: 良ã„æ„Ÿã˜ã®ã‚¨ãƒ©ãƒ¼è¿”ã™ } + if (instance.deeplFreeMode && !instance.deeplFreeInstance) { + return 204; + } + let targetLang = ps.targetLang; if (targetLang.includes('-')) targetLang = targetLang.split('-')[0]; const params = new URLSearchParams(); - params.append('auth_key', instance.deeplAuthKey); + if (instance.deeplAuthKey) params.append('auth_key', instance.deeplAuthKey); params.append('text', note.text); params.append('target_lang', targetLang); - const endpoint = instance.deeplIsPro ? 'https://api.deepl.com/v2/translate' : 'https://api-free.deepl.com/v2/translate'; + const endpoint = instance.deeplFreeMode && instance.deeplFreeInstance ? instance.deeplFreeInstance : instance.deeplIsPro ? 'https://api.deepl.com/v2/translate' : 'https://api-free.deepl.com/v2/translate'; const res = await this.httpRequestService.send(endpoint, { method: 'POST', @@ -103,18 +107,37 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }, body: params.toString(), }); - - const json = (await res.json()) as { - translations: { - detected_source_language: string; - text: string; - }[]; - }; - - return { - sourceLang: json.translations[0].detected_source_language, - text: json.translations[0].text, - }; + if (instance.deeplAuthKey) { + const json = (await res.json()) as { + translations: { + detected_source_language: string; + text: string; + }[]; + }; + + return { + sourceLang: json.translations[0].detected_source_language, + text: json.translations[0].text, + }; + } else { + const json = (await res.json()) as { + code: number, + message: string, + data: string, + source_lang: string, + target_lang: string, + alternatives: string[], + }; + + const languageNames = new Intl.DisplayNames(['en'], { + type: 'language', + }); + + return { + sourceLang: languageNames.of(json.source_lang), + text: json.data, + }; + } }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index 249344a6f3161f9268c34cab6ac85a438fc6180b..58932bd83a459d26300e663036b31a24877f7c57 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 71c2b8054e99812b34d62e82fd65ab00b58dfee3..43877e61efaf4fc0bcf5f6cc3a27b0c1415049ec 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notes/versions.ts b/packages/backend/src/server/api/endpoints/notes/versions.ts index 416fddcb7b8ea63b800bb520473e8d274d3327f2..2b774ae2b0cc794cd98a7575f344ba5d9eed710a 100644 --- a/packages/backend/src/server/api/endpoints/notes/versions.ts +++ b/packages/backend/src/server/api/endpoints/notes/versions.ts @@ -3,9 +3,12 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import type { NotesRepository } from '@/models/_.js'; import { GetterService } from '@/server/api/GetterService.js'; +import { QueryService } from '@/core/QueryService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -38,9 +41,25 @@ export const paramDef = { @Injectable() export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + private getterService: GetterService, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { + const query = await this.notesRepository.createQueryBuilder('note') + .select('note.id') + .where('note.id = :noteId', { noteId: ps.noteId }); + + this.queryService.generateVisibilityQuery(query, me); + + const note = await query.getOne(); + + if (note === null) { + throw new ApiError(meta.errors.noSuchNote); + } + const edits = await this.getterService.getEdits(ps.noteId).catch(err => { if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw err; diff --git a/packages/backend/src/server/api/endpoints/notifications/create.ts b/packages/backend/src/server/api/endpoints/notifications/create.ts index 7c6a97916030700363c821d3ff253f4a091df0c1..7671b58e6bde781e95c8af8494c5ee04ddff9122 100644 --- a/packages/backend/src/server/api/endpoints/notifications/create.ts +++ b/packages/backend/src/server/api/endpoints/notifications/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notifications/flush.ts b/packages/backend/src/server/api/endpoints/notifications/flush.ts new file mode 100644 index 0000000000000000000000000000000000000000..47c0642fd15d1f3ba624fcaecec8f3e8f76f01ce --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notifications/flush.ts @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { NotificationService } from '@/core/NotificationService.js'; + +export const meta = { + tags: ['notifications', 'account'], + + requireCredential: true, + + kind: 'write:notifications', +} as const; + +export const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private notificationService: NotificationService, + ) { + super(meta, paramDef, async (ps, me) => { + this.notificationService.flushAllNotifications(me.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts index dc092c1f3a4b6650bc5448217b825b046a4d4c45..6565125c0002004841c1b5c7f284f3582059d2a8 100644 --- a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/notifications/test-notification.ts b/packages/backend/src/server/api/endpoints/notifications/test-notification.ts index 8f5f8485c3531aad9bf3eab41f37979248a0d0de..50b850a519d78ef8570cb63fa97806fee3dfefe7 100644 --- a/packages/backend/src/server/api/endpoints/notifications/test-notification.ts +++ b/packages/backend/src/server/api/endpoints/notifications/test-notification.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/page-push.ts b/packages/backend/src/server/api/endpoints/page-push.ts index 0a68516586505bd0ed8ac8cb7c43be411b121950..ce454ab24ad573cf3f7768045e1aa3430951dd2c 100644 --- a/packages/backend/src/server/api/endpoints/page-push.ts +++ b/packages/backend/src/server/api/endpoints/page-push.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -55,7 +55,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- var: ps.var, userId: me.id, user: await this.userEntityService.pack(me.id, { id: page.userId }, { - detail: true, + schema: 'UserDetailed', }), }); }); diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts index 4c2ef516e596b7276a12960ea9ca524374780aed..3a02d359f80bac522dd0eae953d8406f6c331460 100644 --- a/packages/backend/src/server/api/endpoints/pages/create.ts +++ b/packages/backend/src/server/api/endpoints/pages/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/pages/delete.ts b/packages/backend/src/server/api/endpoints/pages/delete.ts index 1291c0d2099b0fd7e9d731d54e8acc5c718b86f1..aa2ba75a4162e3fbabbb6e2dd651d2aa5b7f7ee9 100644 --- a/packages/backend/src/server/api/endpoints/pages/delete.ts +++ b/packages/backend/src/server/api/endpoints/pages/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/pages/featured.ts b/packages/backend/src/server/api/endpoints/pages/featured.ts index 1f43d6606c74ff756e32c8df77e9447d51edb8fa..a47b69e56eb02586d633d1df31660778b4be646e 100644 --- a/packages/backend/src/server/api/endpoints/pages/featured.ts +++ b/packages/backend/src/server/api/endpoints/pages/featured.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts index 8c18982b50a70a487d2f9b9758bcddb10118f831..11eed693ad953d7371288f960c01a68d635714fe 100644 --- a/packages/backend/src/server/api/endpoints/pages/like.ts +++ b/packages/backend/src/server/api/endpoints/pages/like.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -70,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } // if already liked - const exist = await this.pageLikesRepository.exist({ + const exist = await this.pageLikesRepository.exists({ where: { pageId: page.id, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/pages/show.ts b/packages/backend/src/server/api/endpoints/pages/show.ts index efb0bd0677eeb132ad103ef60018f75db87f877c..e08b832a3f9a26a9c71dc507f44b365f3f65dee7 100644 --- a/packages/backend/src/server/api/endpoints/pages/show.ts +++ b/packages/backend/src/server/api/endpoints/pages/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/pages/unlike.ts b/packages/backend/src/server/api/endpoints/pages/unlike.ts index 7a76cd74081647a428d0e621611b93071996c0f6..70c965e0ad655f93539cda9f0df9ff3017c5fce0 100644 --- a/packages/backend/src/server/api/endpoints/pages/unlike.ts +++ b/packages/backend/src/server/api/endpoints/pages/unlike.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/pages/update.ts b/packages/backend/src/server/api/endpoints/pages/update.ts index aaea1efa87b24fcc18d139c8a24c4207b5cbdfe2..b8e5e70a25a4fa5b1146dffbfca04d400c792e71 100644 --- a/packages/backend/src/server/api/endpoints/pages/update.ts +++ b/packages/backend/src/server/api/endpoints/pages/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/ping.ts b/packages/backend/src/server/api/endpoints/ping.ts index ee2fe4883485face4aebe897555c48b6e298a29a..e218a8f75513cd0129d2e5d7b81901ec683007bd 100644 --- a/packages/backend/src/server/api/endpoints/ping.ts +++ b/packages/backend/src/server/api/endpoints/ping.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts index 390042c815867393e5c956759cecfd8aa056c12b..784766bcb5510fab6c672e3e861f33dc30af573a 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -12,6 +12,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/core/MetaService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; +import { isNotNull } from '@/misc/is-not-null.js'; export const meta = { tags: ['users'], @@ -52,7 +53,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- host: acct.host ?? IsNull(), }))); - return await this.userEntityService.packMany(users.filter(x => x !== null) as MiUser[], me, { detail: true }); + return await this.userEntityService.packMany(users.filter(isNotNull), me, { schema: 'UserDetailed' }); }); } } diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts index f427939a7a2d531134e71eb95b6309c01d17d77d..9f7d078014fd70b9b9e391056db74aadcce22cf9 100644 --- a/packages/backend/src/server/api/endpoints/promo/read.ts +++ b/packages/backend/src/server/api/endpoints/promo/read.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -49,7 +49,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw err; }); - const exist = await this.promoReadsRepository.exist({ + const exist = await this.promoReadsRepository.exists({ where: { noteId: note.id, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/renote-mute/create.ts b/packages/backend/src/server/api/endpoints/renote-mute/create.ts index 7ff7b5de3a6df8f540aea0d149bf75b36c4dc5dc..39bf0cc428b5a1ff6fd52090e9bbf4ed50f3dbbf 100644 --- a/packages/backend/src/server/api/endpoints/renote-mute/create.ts +++ b/packages/backend/src/server/api/endpoints/renote-mute/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -73,7 +73,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } // Get mutee - const mutee = await getterService.getUser(ps.userId).catch(err => { + const mutee = await this.getterService.getUser(ps.userId).catch(err => { if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); throw err; }); diff --git a/packages/backend/src/server/api/endpoints/renote-mute/delete.ts b/packages/backend/src/server/api/endpoints/renote-mute/delete.ts index f4969896d97ab8732b2273e6dcdfc965ca689cd6..6e037cc07ef0aa2a50e2c123e16bb3bb10092477 100644 --- a/packages/backend/src/server/api/endpoints/renote-mute/delete.ts +++ b/packages/backend/src/server/api/endpoints/renote-mute/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/renote-mute/list.ts b/packages/backend/src/server/api/endpoints/renote-mute/list.ts index 493593ae2d6a1aa98e0fe11af98d6b03855f3826..3be01f989a80817984e27a8516b6e7affd613f10 100644 --- a/packages/backend/src/server/api/endpoints/renote-mute/list.ts +++ b/packages/backend/src/server/api/endpoints/renote-mute/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/request-reset-password.ts b/packages/backend/src/server/api/endpoints/request-reset-password.ts index f13710e1dda0eb23119055b0d887db6d36f23b5d..86fe6a2e6e0134ad5878d4ff69b9d228e4304c94 100644 --- a/packages/backend/src/server/api/endpoints/request-reset-password.ts +++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/reset-db.ts b/packages/backend/src/server/api/endpoints/reset-db.ts index 0eeee815802a10eba0ec240870fda0f23f4bde43..67d5fabd8676b75a78e3be43f87ae126fa53d966 100644 --- a/packages/backend/src/server/api/endpoints/reset-db.ts +++ b/packages/backend/src/server/api/endpoints/reset-db.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index e76b7c968300555bae29feb31703dac111564f85..1639b57bc5c6d4b8bf10070d1593c41dcbb2ca5f 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/retention.ts b/packages/backend/src/server/api/endpoints/retention.ts index dac6d65407598b6b913a5c3bb507d621e07598ca..4695f320422c6b5bd15bc4c174fe0575a56d8f1d 100644 --- a/packages/backend/src/server/api/endpoints/retention.ts +++ b/packages/backend/src/server/api/endpoints/retention.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -14,6 +14,32 @@ export const meta = { requireCredential: false, res: { + type: 'array', + items: { + type: 'object', + properties: { + createdAt: { + type: 'string', + format: 'date-time', + }, + users: { + type: 'number', + }, + data: { + type: 'object', + additionalProperties: { + anyOf: [{ + type: 'number', + }], + }, + }, + }, + required: [ + 'createdAt', + 'users', + 'data', + ], + }, }, allowGet: true, diff --git a/packages/backend/src/server/api/endpoints/reversi/cancel-match.ts b/packages/backend/src/server/api/endpoints/reversi/cancel-match.ts new file mode 100644 index 0000000000000000000000000000000000000000..dd6f273e01f4b984280b47414ff5a4046bbc84cf --- /dev/null +++ b/packages/backend/src/server/api/endpoints/reversi/cancel-match.ts @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { ReversiService } from '@/core/ReversiService.js'; + +export const meta = { + requireCredential: true, + + kind: 'write:account', + + errors: { + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id', nullable: true }, + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private reversiService: ReversiService, + ) { + super(meta, paramDef, async (ps, me) => { + if (ps.userId) { + await this.reversiService.matchSpecificUserCancel(me, ps.userId); + return; + } else { + await this.reversiService.matchAnyUserCancel(me); + } + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/reversi/games.ts b/packages/backend/src/server/api/endpoints/reversi/games.ts new file mode 100644 index 0000000000000000000000000000000000000000..6b060687275de382427f47e627bb8f437a11e45b --- /dev/null +++ b/packages/backend/src/server/api/endpoints/reversi/games.ts @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Brackets } from 'typeorm'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js'; +import { DI } from '@/di-symbols.js'; +import type { ReversiGamesRepository } from '@/models/_.js'; +import { QueryService } from '@/core/QueryService.js'; + +export const meta = { + requireCredential: false, + + res: { + type: 'array', + optional: false, nullable: false, + items: { ref: 'ReversiGameLite' }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + my: { type: 'boolean', default: false }, + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.reversiGamesRepository) + private reversiGamesRepository: ReversiGamesRepository, + + private reversiGameEntityService: ReversiGameEntityService, + private queryService: QueryService, + ) { + super(meta, paramDef, async (ps, me) => { + const query = this.queryService.makePaginationQuery(this.reversiGamesRepository.createQueryBuilder('game'), ps.sinceId, ps.untilId) + .innerJoinAndSelect('game.user1', 'user1') + .innerJoinAndSelect('game.user2', 'user2'); + + if (ps.my && me) { + query.andWhere(new Brackets(qb => { + qb + .where('game.user1Id = :userId', { userId: me.id }) + .orWhere('game.user2Id = :userId', { userId: me.id }); + })); + } else { + query.andWhere('game.isStarted = TRUE'); + } + + const games = await query.take(ps.limit).getMany(); + + return await this.reversiGameEntityService.packLiteMany(games); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/reversi/invitations.ts b/packages/backend/src/server/api/endpoints/reversi/invitations.ts new file mode 100644 index 0000000000000000000000000000000000000000..5b3b9da75b2a1d324f7a4ddc363fe9b02e05d3b1 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/reversi/invitations.ts @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { ReversiService } from '@/core/ReversiService.js'; + +export const meta = { + requireCredential: true, + + kind: 'read:account', + + res: { + type: 'array', + optional: false, nullable: false, + items: { ref: 'UserLite' }, + }, +} as const; + +export const paramDef = { +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private userEntityService: UserEntityService, + private reversiService: ReversiService, + ) { + super(meta, paramDef, async (ps, me) => { + const invitations = await this.reversiService.getInvitations(me); + + return await this.userEntityService.packMany(invitations, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/reversi/match.ts b/packages/backend/src/server/api/endpoints/reversi/match.ts new file mode 100644 index 0000000000000000000000000000000000000000..aa8b8a7d728e6adbfc81e6aa8511004a8f83c58a --- /dev/null +++ b/packages/backend/src/server/api/endpoints/reversi/match.ts @@ -0,0 +1,73 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { ReversiService } from '@/core/ReversiService.js'; +import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js'; +import { ApiError } from '../../error.js'; +import { GetterService } from '../../GetterService.js'; + +export const meta = { + requireCredential: true, + + kind: 'write:account', + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '0b4f0559-b484-4e31-9581-3f73cee89b28', + }, + + isYourself: { + message: 'Target user is yourself.', + code: 'TARGET_IS_YOURSELF', + id: '96fd7bd6-d2bc-426c-a865-d055dcd2828e', + }, + }, + + res: { + type: 'object', + optional: true, + ref: 'ReversiGameDetailed', + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id', nullable: true }, + noIrregularRules: { type: 'boolean', default: false }, + multiple: { type: 'boolean', default: false }, + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private getterService: GetterService, + private reversiService: ReversiService, + private reversiGameEntityService: ReversiGameEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + if (ps.userId === me.id) throw new ApiError(meta.errors.isYourself); + + const target = ps.userId ? await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; + }) : null; + + const game = target + ? await this.reversiService.matchSpecificUser(me, target, ps.multiple) + : await this.reversiService.matchAnyUser(me, { noIrregularRules: ps.noIrregularRules }, ps.multiple); + + if (game == null) return; + + return await this.reversiGameEntityService.packDetail(game); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/reversi/show-game.ts b/packages/backend/src/server/api/endpoints/reversi/show-game.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc3b96eb51a375173cd5572cbd9076d28f9885b2 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/reversi/show-game.ts @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { ReversiService } from '@/core/ReversiService.js'; +import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + requireCredential: false, + + errors: { + noSuchGame: { + message: 'No such game.', + code: 'NO_SUCH_GAME', + id: 'f13a03db-fae1-46c9-87f3-43c8165419e1', + }, + }, + + res: { + type: 'object', + optional: false, nullable: false, + ref: 'ReversiGameDetailed', + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + gameId: { type: 'string', format: 'misskey:id' }, + }, + required: ['gameId'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private reversiService: ReversiService, + private reversiGameEntityService: ReversiGameEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const game = await this.reversiService.get(ps.gameId); + + if (game == null) { + throw new ApiError(meta.errors.noSuchGame); + } + + return await this.reversiGameEntityService.packDetail(game); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/reversi/surrender.ts b/packages/backend/src/server/api/endpoints/reversi/surrender.ts new file mode 100644 index 0000000000000000000000000000000000000000..75e5372862320e64bdd24ea080d1b7ad0d0a6e31 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/reversi/surrender.ts @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { ReversiService } from '@/core/ReversiService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + requireCredential: true, + + kind: 'write:account', + + errors: { + noSuchGame: { + message: 'No such game.', + code: 'NO_SUCH_GAME', + id: 'ace0b11f-e0a6-4076-a30d-e8284c81b2df', + }, + + alreadyEnded: { + message: 'That game has already ended.', + code: 'ALREADY_ENDED', + id: '6c2ad4a6-cbf1-4a5b-b187-b772826cfc6d', + }, + + accessDenied: { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: '6e04164b-a992-4c93-8489-2123069973e1', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + gameId: { type: 'string', format: 'misskey:id' }, + }, + required: ['gameId'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private reversiService: ReversiService, + ) { + super(meta, paramDef, async (ps, me) => { + const game = await this.reversiService.get(ps.gameId); + + if (game == null) { + throw new ApiError(meta.errors.noSuchGame); + } + + if (game.isEnded) { + throw new ApiError(meta.errors.alreadyEnded); + } + + if ((game.user1Id !== me.id) && (game.user2Id !== me.id)) { + throw new ApiError(meta.errors.accessDenied); + } + + await this.reversiService.surrender(game.id, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/reversi/verify.ts b/packages/backend/src/server/api/endpoints/reversi/verify.ts new file mode 100644 index 0000000000000000000000000000000000000000..981735a3d74d072c203345acaed33510abd5b1e8 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/reversi/verify.ts @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { ReversiService } from '@/core/ReversiService.js'; +import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + errors: { + noSuchGame: { + message: 'No such game.', + code: 'NO_SUCH_GAME', + id: '8fb05624-b525-43dd-90f7-511852bdfeee', + }, + }, + + res: { + type: 'object', + optional: false, nullable: false, + properties: { + desynced: { type: 'boolean' }, + game: { + type: 'object', + optional: true, nullable: true, + ref: 'ReversiGameDetailed', + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + gameId: { type: 'string', format: 'misskey:id' }, + crc32: { type: 'string' }, + }, + required: ['gameId', 'crc32'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private reversiService: ReversiService, + private reversiGameEntityService: ReversiGameEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const game = await this.reversiService.checkCrc(ps.gameId, ps.crc32); + if (game) { + return { + desynced: true, + game: await this.reversiGameEntityService.packDetail(game), + }; + } else { + return { + desynced: false, + }; + } + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/roles/list.ts b/packages/backend/src/server/api/endpoints/roles/list.ts index d40e937d4e79cebcbbfd6a028dc93f2893e9e76a..b087aa242b68508a42123b65bb687e93fcadd921 100644 --- a/packages/backend/src/server/api/endpoints/roles/list.ts +++ b/packages/backend/src/server/api/endpoints/roles/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts index 4ce3fc8908da992150783b2995ac9db2df49e97d..71f2782a5d9f29a9b611a07cb90b107b2f54490b 100644 --- a/packages/backend/src/server/api/endpoints/roles/notes.ts +++ b/packages/backend/src/server/api/endpoints/roles/notes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/roles/show.ts b/packages/backend/src/server/api/endpoints/roles/show.ts index 6bfe52bb1a171cc14c3e98aa1132359ad9977924..38477c5e8e51520044063f796d9cd57b92dbd580 100644 --- a/packages/backend/src/server/api/endpoints/roles/show.ts +++ b/packages/backend/src/server/api/endpoints/roles/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/roles/users.ts b/packages/backend/src/server/api/endpoints/roles/users.ts index d304d075b293be860e562e2584704004373b7bd2..85d100ce1c5a030ec99e3d432668a9e1bea20078 100644 --- a/packages/backend/src/server/api/endpoints/roles/users.ts +++ b/packages/backend/src/server/api/endpoints/roles/users.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -33,11 +33,11 @@ export const meta = { properties: { id: { type: 'string', - format: 'misskey:id' + format: 'misskey:id', }, user: { type: 'object', - ref: 'User' + ref: 'UserDetailed', }, }, required: ['id', 'user'], @@ -94,7 +94,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- return await Promise.all(assigns.map(async assign => ({ id: assign.id, - user: await this.userEntityService.pack(assign.user!, me, { detail: true }), + user: await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }), }))); }); } diff --git a/packages/backend/src/server/api/endpoints/server-info.ts b/packages/backend/src/server/api/endpoints/server-info.ts index 079f2d7f1d53baafe9556e5029b019ba8bd7b061..c13802eb0689d51da2936cbab5a143f964af9e30 100644 --- a/packages/backend/src/server/api/endpoints/server-info.ts +++ b/packages/backend/src/server/api/endpoints/server-info.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/sponsors.ts b/packages/backend/src/server/api/endpoints/sponsors.ts index 79785673aa741013219abe40a4430f370a0e19a5..50e1c594f2e7b18bfa970df687672aa055a2b17a 100644 --- a/packages/backend/src/server/api/endpoints/sponsors.ts +++ b/packages/backend/src/server/api/endpoints/sponsors.ts @@ -1,20 +1,20 @@ import { Inject, Injectable } from '@nestjs/common'; +import * as Redis from 'ioredis'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; -import * as Redis from 'ioredis'; export const meta = { - tags: ["meta"], - description: "Get Sharkey GH Sponsors", + tags: ['meta'], + description: 'Get Sharkey GH Sponsors', requireCredential: false, requireCredentialPrivateMode: false, } as const; export const paramDef = { - type: "object", + type: 'object', properties: { - forceUpdate: { type: "boolean", default: false }, + forceUpdate: { type: 'boolean', default: false }, }, required: [], } as const; @@ -25,22 +25,29 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.redis) private redisClient: Redis.Redis, ) { super(meta, paramDef, async (ps, me) => { - let sponsors; - const cachedSponsors = await this.redisClient.get("sponsors"); - if (!ps.forceUpdate && cachedSponsors) { - sponsors = JSON.parse(cachedSponsors); - } else { - AbortSignal.timeout ??= function timeout(ms) { - const ctrl = new AbortController(); - setTimeout(() => ctrl.abort(), ms); - return ctrl.signal; - }; + let sponsors; + const cachedSponsors = await this.redisClient.get('sponsors'); + if (!ps.forceUpdate && cachedSponsors) { + sponsors = JSON.parse(cachedSponsors); + } else { + AbortSignal.timeout ??= function timeout(ms) { + const ctrl = new AbortController(); + setTimeout(() => ctrl.abort(), ms); + return ctrl.signal; + }; + + try { + sponsors = await fetch('https://kaifa.ch/transfem-sponsors.json', { signal: AbortSignal.timeout(2000) }) + .then((response) => response.json()); - sponsors = await fetch("https://kaifa.ch/transfem-sponsors.json", { signal: AbortSignal.timeout(2000) }) - .then((response) => response.json()); - await this.redisClient.set("sponsors", JSON.stringify(sponsors), "EX", 3600); - } - return { sponsor_data: sponsors['sponsors'] }; - }); - } + await this.redisClient.set('sponsors', JSON.stringify(sponsors), 'EX', 3600); + } catch (error) { + sponsors = { + sponsors: [], + }; + } + } + return { sponsor_data: sponsors['sponsors'] }; + }); + } } diff --git a/packages/backend/src/server/api/endpoints/stats.ts b/packages/backend/src/server/api/endpoints/stats.ts index 05468240d30c74e241d54b13a824492f61e0080f..1e6983177f647f8c36203d4d343708a437e646fb 100644 --- a/packages/backend/src/server/api/endpoints/stats.ts +++ b/packages/backend/src/server/api/endpoints/stats.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index bb50048d94c169a5e26d36952580327c02d834b4..a9a33149f95ac7aada8a087acbcd7b7ea5aaa22b 100644 --- a/packages/backend/src/server/api/endpoints/sw/register.ts +++ b/packages/backend/src/server/api/endpoints/sw/register.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -9,6 +9,7 @@ import type { SwSubscriptionsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/core/MetaService.js'; import { DI } from '@/di-symbols.js'; +import { PushNotificationService } from '@/core/PushNotificationService.js'; export const meta = { tags: ['account'], @@ -66,6 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private idService: IdService, private metaService: MetaService, + private pushNotificationService: PushNotificationService, ) { super(meta, paramDef, async (ps, me) => { // if already subscribed @@ -97,6 +99,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- sendReadMessage: ps.sendReadMessage, }); + this.pushNotificationService.refreshCache(me.id); + return { state: 'subscribed' as const, key: instance.swPublicKey, diff --git a/packages/backend/src/server/api/endpoints/sw/show-registration.ts b/packages/backend/src/server/api/endpoints/sw/show-registration.ts index 15d3df8587d7879351ccd5f198a797cc7f806a9f..797e4fd34d0d30b68f0a44238a2e3c5968ec2e86 100644 --- a/packages/backend/src/server/api/endpoints/sw/show-registration.ts +++ b/packages/backend/src/server/api/endpoints/sw/show-registration.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/sw/unregister.ts b/packages/backend/src/server/api/endpoints/sw/unregister.ts index f00fdd66976010df34a0d04abe1786bc2159d3f3..2edf7fab1b36625540e4cbcb3051f550d42cb7c3 100644 --- a/packages/backend/src/server/api/endpoints/sw/unregister.ts +++ b/packages/backend/src/server/api/endpoints/sw/unregister.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { SwSubscriptionsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; +import { PushNotificationService } from '@/core/PushNotificationService.js'; export const meta = { tags: ['account'], @@ -29,12 +30,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject(DI.swSubscriptionsRepository) private swSubscriptionsRepository: SwSubscriptionsRepository, + + private pushNotificationService: PushNotificationService, ) { super(meta, paramDef, async (ps, me) => { await this.swSubscriptionsRepository.delete({ ...(me ? { userId: me.id } : {}), endpoint: ps.endpoint, }); + + if (me) { + this.pushNotificationService.refreshCache(me.id); + } }); } } diff --git a/packages/backend/src/server/api/endpoints/sw/update-registration.ts b/packages/backend/src/server/api/endpoints/sw/update-registration.ts index 7bf59784a2cbdd7b29bbf743f331a58076931894..839a07c770b04fe1ff9a7c200a06c1107fbc406f 100644 --- a/packages/backend/src/server/api/endpoints/sw/update-registration.ts +++ b/packages/backend/src/server/api/endpoints/sw/update-registration.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { SwSubscriptionsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; +import { PushNotificationService } from '@/core/PushNotificationService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -58,6 +59,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject(DI.swSubscriptionsRepository) private swSubscriptionsRepository: SwSubscriptionsRepository, + + private pushNotificationService: PushNotificationService, ) { super(meta, paramDef, async (ps, me) => { const swSubscription = await this.swSubscriptionsRepository.findOneBy({ @@ -77,6 +80,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- sendReadMessage: swSubscription.sendReadMessage, }); + this.pushNotificationService.refreshCache(me.id); + return { userId: swSubscription.userId, endpoint: swSubscription.endpoint, diff --git a/packages/backend/src/server/api/endpoints/test.ts b/packages/backend/src/server/api/endpoints/test.ts index 949867c5729ee5c6ce203278ac878695d46c6c3e..9231f0ab9484d1416963667be37fda8fef45c304 100644 --- a/packages/backend/src/server/api/endpoints/test.ts +++ b/packages/backend/src/server/api/endpoints/test.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -18,24 +18,28 @@ export const meta = { properties: { id: { type: 'string', - format: 'misskey:id' + format: 'misskey:id', + optional: true, nullable: false, }, required: { type: 'boolean', + optional: false, nullable: false, }, string: { type: 'string', + optional: true, nullable: false, }, default: { type: 'string', + optional: true, nullable: false, }, nullableDefault: { type: 'string', default: 'hello', - nullable: true, + optional: true, nullable: true, }, - } - } + }, + }, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/username/available.ts b/packages/backend/src/server/api/endpoints/username/available.ts index e37df62c0c6be73b3c4561581a314cbabe7d188e..affb0996f182a477ebc02c4ac3f592b064951e97 100644 --- a/packages/backend/src/server/api/endpoints/username/available.ts +++ b/packages/backend/src/server/api/endpoints/username/available.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index 8dc5841314fd2d58d97002be94ffad13ab472db6..e8458530177e4d1b6ff92d4f0d87be4729ab6560 100644 --- a/packages/backend/src/server/api/endpoints/users.ts +++ b/packages/backend/src/server/api/endpoints/users.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -89,7 +89,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const users = await query.getMany(); - return await this.userEntityService.packMany(users, me, { detail: true }); + return await this.userEntityService.packMany(users, me, { schema: 'UserDetailed' }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/achievements.ts b/packages/backend/src/server/api/endpoints/users/achievements.ts index 3a584a819a0b579ac62eeb9cac3e840c0e1f8b23..f7139b36841fb5c8d2a3707add9887331cf43e0e 100644 --- a/packages/backend/src/server/api/endpoints/users/achievements.ts +++ b/packages/backend/src/server/api/endpoints/users/achievements.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts index 725e07db393a4f595a9294af9219ea273a33230c..7f7d2ea8cc251d11e249b6e10d90088c27b8bf5b 100644 --- a/packages/backend/src/server/api/endpoints/users/clips.ts +++ b/packages/backend/src/server/api/endpoints/users/clips.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/users/featured-notes.ts b/packages/backend/src/server/api/endpoints/users/featured-notes.ts index 7243aa3b3ead883e496c90fc62d59923d3331c21..e01f19ba7aaf41987b5d20886ce58c70be8582d0 100644 --- a/packages/backend/src/server/api/endpoints/users/featured-notes.ts +++ b/packages/backend/src/server/api/endpoints/users/featured-notes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/users/flashs.ts b/packages/backend/src/server/api/endpoints/users/flashs.ts index 18026dcefbaac6d909c2bf10773a80aa5c3a5bfe..e5ea4502157e672aeab13d9b6de770e98da25559 100644 --- a/packages/backend/src/server/api/endpoints/users/flashs.ts +++ b/packages/backend/src/server/api/endpoints/users/flashs.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index 5706e46b96216ca937853d1cf527d63466827f4d..7ce7734f53ff857bf070c454b6127f4b3b4c438c 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -101,7 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (me == null) { throw new ApiError(meta.errors.forbidden); } else if (me.id !== user.id) { - const isFollowing = await this.followingsRepository.exist({ + const isFollowing = await this.followingsRepository.exists({ where: { followeeId: user.id, followerId: me.id, diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 794fb04f102b1f4f40b020cca47384c7c015aae0..5d52ebba7660da705f59f0198e5e843f7cf53222 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -109,7 +109,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (me == null) { throw new ApiError(meta.errors.forbidden); } else if (me.id !== user.id) { - const isFollowing = await this.followingsRepository.exist({ + const isFollowing = await this.followingsRepository.exists({ where: { followeeId: user.id, followerId: me.id, diff --git a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts index 757af98e004eb26682aba09922a73ad0a7654c0a..553886374c982430d83a4969bbbdc4e95e0c1e9c 100644 --- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts index d6fb65cecb1f55ea3eec79e347f57154ee3c2ed8..02aa037466e5402b95551328a44901c0b15cd09b 100644 --- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts +++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -122,7 +122,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- // Make replies object (includes weights) const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({ - user: await this.userEntityService.pack(user, me, { detail: true }), + user: await this.userEntityService.pack(user, me, { schema: 'UserDetailed' }), weight: repliedUsers[user] / peak, }))); diff --git a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts index fa2e3338b88e5f724c8a365c45e97e1e52dc076c..e2db71c5c7f4b918aa51388204534364afafe219 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -90,7 +90,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private roleService: RoleService, ) { super(meta, paramDef, async (ps, me) => { - const listExist = await this.userListsRepository.exist({ + const listExist = await this.userListsRepository.exists({ where: { id: ps.listId, isPublic: true, @@ -121,7 +121,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }); if (currentUser.id !== me.id) { - const blockExist = await this.blockingsRepository.exist({ + const blockExist = await this.blockingsRepository.exists({ where: { blockerId: currentUser.id, blockeeId: me.id, @@ -132,7 +132,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } } - const exist = await this.userListMembershipsRepository.exist({ + const exist = await this.userListMembershipsRepository.exists({ where: { userListId: userList.id, userId: currentUser.id, diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts index e86e4c0dede3ca66398a078aca76292017c294a0..952580e639fd7f95f9b4378f3448e3f11d45fb64 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/users/lists/delete.ts b/packages/backend/src/server/api/endpoints/users/lists/delete.ts index 763f5afd9df37d2b2fb37ddd9e0384087e1e9555..dc0d28a0eb3f13dac8b70cf07770b4bc7ba32b9e 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/delete.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/delete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/users/lists/favorite.ts b/packages/backend/src/server/api/endpoints/users/lists/favorite.ts index 864cdc2ee0106823aab122e427db3a814d1c12ef..fd142d5a015022a3e9f9a7ea6319a3911a9ac8a4 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/favorite.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/favorite.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -47,7 +47,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { - const userListExist = await this.userListsRepository.exist({ + const userListExist = await this.userListsRepository.exists({ where: { id: ps.listId, isPublic: true, @@ -58,7 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw new ApiError(meta.errors.noSuchList); } - const exist = await this.userListFavoritesRepository.exist({ + const exist = await this.userListFavoritesRepository.exists({ where: { userId: me.id, userListId: ps.listId, diff --git a/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts b/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts index 985141515ee9f0bcb793d0b6fd3429ca79ecfbad..6d6e8d34ea04809cab9a1d2e95b97e5c0cf47630 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -46,7 +46,7 @@ export const meta = { }, user: { type: 'object', - ref: 'User', + ref: 'UserLite', }, withReplies: { type: 'boolean', diff --git a/packages/backend/src/server/api/endpoints/users/lists/list.ts b/packages/backend/src/server/api/endpoints/users/lists/list.ts index 0e86dd3a68359e5f07ae2a8079226d7330c3207e..4241ef1cd059e0add7adda733131b5e91190e190 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/list.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/users/lists/pull.ts b/packages/backend/src/server/api/endpoints/users/lists/pull.ts index e90122224c6487e4eea5dd552bd3f2abc5fcc1b3..94f06f3bea1a7f5826593eb83c69ba0d2454ec01 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/users/lists/push.ts b/packages/backend/src/server/api/endpoints/users/lists/push.ts index c4ceec575b92f880b1f78d1df999e4e3776582aa..c717b3959cba5414fba79886e386a0fddcdb8367 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/push.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -104,7 +104,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- // Check blocking if (user.id !== me.id) { - const blockExist = await this.blockingsRepository.exist({ + const blockExist = await this.blockingsRepository.exists({ where: { blockerId: user.id, blockeeId: me.id, @@ -115,7 +115,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } } - const exist = await this.userListMembershipsRepository.exist({ + const exist = await this.userListMembershipsRepository.exists({ where: { userListId: userList.id, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/users/lists/show.ts b/packages/backend/src/server/api/endpoints/users/lists/show.ts index df44870b041e22e8a5108d4c3ab0deaadc6705ae..8756801fe4a7f2057831fe1c8d53bda97600aa0e 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/show.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -74,7 +74,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { userListId: ps.listId, }); if (me !== null) { - additionalProperties.isLiked = await this.userListFavoritesRepository.exist({ + additionalProperties.isLiked = await this.userListFavoritesRepository.exists({ where: { userId: me.id, userListId: ps.listId, diff --git a/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts b/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts index d51d57343e29c97c67ce0bc5664aac8905aaa5fe..3f4bd5af8cb9a59439ea2378aeac285d3d6167ac 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -45,7 +45,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private userListFavoritesRepository: UserListFavoritesRepository, ) { super(meta, paramDef, async (ps, me) => { - const userListExist = await this.userListsRepository.exist({ + const userListExist = await this.userListsRepository.exists({ where: { id: ps.listId, isPublic: true, diff --git a/packages/backend/src/server/api/endpoints/users/lists/update-membership.ts b/packages/backend/src/server/api/endpoints/users/lists/update-membership.ts index b69465b940e84e8e70ab9e0bc96308da62ae23e5..3948ae1685672287987160ef07d8fc9905ebac62 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/update-membership.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/update-membership.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/users/lists/update.ts b/packages/backend/src/server/api/endpoints/users/lists/update.ts index eb6cfbaf26d4739af6e8b83f3d2af4b181e57469..a38f84d7b0cc5b58b7a1f0ea69033e5be0ff0482 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/update.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index b485126ed8848bd7380ac65a139a15cc684e9f04..cc76c12f1d897e3eb3ab3aa7188f6aee2af90474 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/users/pages.ts b/packages/backend/src/server/api/endpoints/users/pages.ts index cf2f274c70d167de8b22ad10bff2c0aa22a59a24..bb7de0e0b56fb6d06c4b28fc8cbffa1b5c769e1f 100644 --- a/packages/backend/src/server/api/endpoints/users/pages.ts +++ b/packages/backend/src/server/api/endpoints/users/pages.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index 372ab80c4c641bd88b8ce0500457846fca219013..aca883a052bb146f310ddf9be94482015920be45 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -9,6 +9,9 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteReactionEntityService } from '@/core/entities/NoteReactionEntityService.js'; import { DI } from '@/di-symbols.js'; +import { CacheService } from '@/core/CacheService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { RoleService } from '@/core/RoleService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -34,6 +37,11 @@ export const meta = { code: 'REACTIONS_NOT_PUBLIC', id: '673a7dd2-6924-1093-e0c0-e68456ceae5c', }, + isRemoteUser: { + message: 'Currently unavailable to display reactions of remote users. See https://github.com/misskey-dev/misskey/issues/12964', + code: 'IS_REMOTE_USER', + id: '6b95fa98-8cf9-2350-e284-f0ffdb54a805', + }, }, } as const; @@ -59,14 +67,24 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.noteReactionsRepository) private noteReactionsRepository: NoteReactionsRepository, + private cacheService: CacheService, + private userEntityService: UserEntityService, private noteReactionEntityService: NoteReactionEntityService, private queryService: QueryService, + private roleService: RoleService, ) { super(meta, paramDef, async (ps, me) => { - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: ps.userId }); + const iAmModerator = me ? await this.roleService.isModerator(me) : false; // Moderators can see reactions of all users + if (!iAmModerator) { + const user = await this.cacheService.findUserById(ps.userId); + if (this.userEntityService.isRemoteUser(user)) { + throw new ApiError(meta.errors.isRemoteUser); + } - if ((me == null || me.id !== ps.userId) && !profile.publicReactions) { - throw new ApiError(meta.errors.reactionsNotPublic); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: ps.userId }); + if ((me == null || me.id !== ps.userId) && !profile.publicReactions) { + throw new ApiError(meta.errors.reactionsNotPublic); + } } const query = this.queryService.makePaginationQuery(this.noteReactionsRepository.createQueryBuilder('reaction'), @@ -80,7 +98,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .limit(ps.limit) .getMany(); - return await Promise.all(reactions.map(reaction => this.noteReactionEntityService.pack(reaction, me, { withNote: true }))); + return await this.noteReactionEntityService.packMany(reactions, me, { withNote: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index 1b30e99b15d413f250ec825f6ac9c0b7d9a280e6..5b3b4527f79fddc4b431e00e54e09c1610af22aa 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -76,7 +76,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const users = await query.limit(ps.limit).offset(ps.offset).getMany(); - return await this.userEntityService.packMany(users, me, { detail: true }); + return await this.userEntityService.packMany(users, me, { schema: 'UserDetailed' }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts index 26b61c9fb2ce82dfa0024219af3094bf7f4183b8..6a5b2262fa6463ed6f979fe93ee8af84e2e07486 100644 --- a/packages/backend/src/server/api/endpoints/users/relation.ts +++ b/packages/backend/src/server/api/endpoints/users/relation.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index bdaf78758b74850c8dc9238a20c211ccd807b2fa..0685858d77fb2e9609d6b3ab6800018d256f746c 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 4bf25d9fbb676a195f2d5bbd2eb90f10fbb01d1a..7b3bdab327eb84e82f3b6593aa24cf21df10c3a1 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -131,7 +131,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .getMany(); } - return await this.userEntityService.packMany(users, me, { detail: !!ps.detail }); + return await this.userEntityService.packMany(users, me, { schema: ps.detail ? 'UserDetailed' : 'UserLite' }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index 32b5c123722715029ce4ffdbf821ac82f1c99212..df9d9f6312803f59fe3eb63b38874f90c80dfb01 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -141,7 +141,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } } - return await this.userEntityService.packMany(users, me, { detail: ps.detail }); + return await this.userEntityService.packMany(users, me, { schema: ps.detail ? 'UserDetailed' : 'UserLite' }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 389497301d396b8259359aec6db560888842ad08..bd81989cb9c463c5b9406572a5c9e2da2718f32c 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -116,7 +116,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } return await Promise.all(_users.map(u => this.userEntityService.pack(u, me, { - detail: true, + schema: 'UserDetailed', }))); } else { // Lookup user @@ -146,7 +146,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } return await this.userEntityService.pack(user, me, { - detail: true, + schema: 'UserDetailed', }); } }); diff --git a/packages/backend/src/server/api/endpoints/users/update-memo.ts b/packages/backend/src/server/api/endpoints/users/update-memo.ts index b3f67815efb6966b51f8445ee56e4fd3f8d67a5f..5a10de0c40ceb4db3d05b2456a5c83bff7a8b5fa 100644 --- a/packages/backend/src/server/api/endpoints/users/update-memo.ts +++ b/packages/backend/src/server/api/endpoints/users/update-memo.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/error.ts b/packages/backend/src/server/api/error.ts index 6506565a0d48385151610029f518dc548e60b971..2f8322a5689f42188ba1bda3a8d3ecca01c8736b 100644 --- a/packages/backend/src/server/api/error.ts +++ b/packages/backend/src/server/api/error.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/mastodon/converters.ts b/packages/backend/src/server/api/mastodon/converters.ts index cce7f798fe7049256b90fc2b8d075f6c93aae276..20fccec21d9e022dd26c7ab44cdd34cad44d63f9 100644 --- a/packages/backend/src/server/api/mastodon/converters.ts +++ b/packages/backend/src/server/api/mastodon/converters.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Entity } from 'megalodon'; -import mfm from '@sharkey/sfm-js'; +import mfm from '@transfem-org/sfm-js'; import { DI } from '@/di-symbols.js'; import { MfmService } from '@/core/MfmService.js'; import type { Config } from '@/config.js'; @@ -9,9 +9,9 @@ import type { MiUser } from '@/models/User.js'; import type { NoteEditRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; -import { GetterService } from '../GetterService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { IdService } from '@/core/IdService.js'; +import { GetterService } from '../GetterService.js'; export enum IdConvertType { MastodonId, @@ -94,10 +94,10 @@ export class MastoConverters { text_url: f.url, meta: { width: f.properties.width, - height: f.properties.height + height: f.properties.height, }, description: f.comment ? f.comment : null, - blurhash: f.blurhash ? f.blurhash : null + blurhash: f.blurhash ? f.blurhash : null, }; } @@ -185,7 +185,7 @@ export class MastoConverters { sensitive: files.then(files => files.length > 0 ? files.some((f) => f.isSensitive) : false), spoiler_text: edit.cw ?? '', poll: null, - media_attachments: files.then(files => files.length > 0 ? files.map((f) => this.encodeFile(f)) : []) + media_attachments: files.then(files => files.length > 0 ? files.map((f) => this.encodeFile(f)) : []), }; lastDate = edit.updatedAt; history.push(awaitAll(item)); @@ -278,7 +278,7 @@ export class MastoConverters { reactions: status.emoji_reactions, emoji_reactions: status.emoji_reactions, bookmarked: false, - quote: isQuote ? await this.convertReblog(status.reblog) : null, + quote: isQuote ? await this.convertReblog(status.reblog) : false, edited_at: note.updatedAt?.toISOString(), }); } diff --git a/packages/backend/src/server/api/openapi/OpenApiServerService.ts b/packages/backend/src/server/api/openapi/OpenApiServerService.ts index cb22d0f7c922ff3c7bd58fc094d9ed1aafe5c33e..5210e4d2bc3c83f42be0bb7e24a9967ec5802140 100644 --- a/packages/backend/src/server/api/openapi/OpenApiServerService.ts +++ b/packages/backend/src/server/api/openapi/OpenApiServerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/openapi/errors.ts b/packages/backend/src/server/api/openapi/errors.ts index 84c3c638fa5816e4a8691dd183e52819d60f6af3..7c50122f904adeb3743b205fdf59e027e40aad73 100644 --- a/packages/backend/src/server/api/openapi/errors.ts +++ b/packages/backend/src/server/api/openapi/errors.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts index 0e71510b4843e16b166a96f9cd3982092bbf142e..b5f91ff542f098cda1b7588f9dd81dd488c8cd67 100644 --- a/packages/backend/src/server/api/openapi/gen-spec.ts +++ b/packages/backend/src/server/api/openapi/gen-spec.ts @@ -1,16 +1,16 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import type { Config } from '@/config.js'; import endpoints, { IEndpoint } from '../endpoints.js'; import { errors as basicErrors } from './errors.js'; -import { schemas, convertSchemaToOpenApiSchema } from './schemas.js'; +import { getSchemas, convertSchemaToOpenApiSchema } from './schemas.js'; -export function genOpenapiSpec(config: Config) { +export function genOpenapiSpec(config: Config, includeSelfRef = false) { const spec = { - openapi: '3.0.0', + openapi: '3.1.0', info: { version: config.version, @@ -20,7 +20,7 @@ export function genOpenapiSpec(config: Config) { externalDocs: { description: 'Repository', - url: 'https://github.com/misskey-dev/misskey', + url: 'https://activitypub.software/TransFem-org/Sharkey', }, servers: [{ @@ -30,7 +30,7 @@ export function genOpenapiSpec(config: Config) { paths: {} as any, components: { - schemas: schemas, + schemas: getSchemas(includeSelfRef), securitySchemes: { bearerAuth: { @@ -56,7 +56,7 @@ export function genOpenapiSpec(config: Config) { } } - const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res) : {}; + const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res, 'res', includeSelfRef) : {}; let desc = (endpoint.meta.description ? endpoint.meta.description : 'No description provided.') + '\n\n'; @@ -71,7 +71,7 @@ export function genOpenapiSpec(config: Config) { } const requestType = endpoint.meta.requireFile ? 'multipart/form-data' : 'application/json'; - const schema = { ...endpoint.params }; + const schema = { ...convertSchemaToOpenApiSchema(endpoint.params, 'param', false) }; if (endpoint.meta.requireFile) { schema.properties = { @@ -98,7 +98,7 @@ export function genOpenapiSpec(config: Config) { description: desc, externalDocs: { description: 'Source code', - url: `https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/${endpoint.name}.ts`, + url: `https://activitypub.software/TransFem-org/Sharkey/-/tree/develop/packages/backend/src/server/api/endpoints/${endpoint.name}.ts`, }, ...(endpoint.meta.tags ? { tags: [endpoint.meta.tags[0]], @@ -210,7 +210,9 @@ export function genOpenapiSpec(config: Config) { }; spec.paths['/' + endpoint.name] = { - ...(endpoint.meta.allowGet ? { get: info } : {}), + ...(endpoint.meta.allowGet ? { + get: info, + } : {}), post: info, }; } diff --git a/packages/backend/src/server/api/openapi/schemas.ts b/packages/backend/src/server/api/openapi/schemas.ts index 2716f5f1629c5948e2d3bbdf06794ed0aeb007f1..eb854a71414fc38b2a585c33db0468bbd8b8019b 100644 --- a/packages/backend/src/server/api/openapi/schemas.ts +++ b/packages/backend/src/server/api/openapi/schemas.ts @@ -1,37 +1,40 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import type { Schema } from '@/misc/json-schema.js'; import { refs } from '@/misc/json-schema.js'; -export function convertSchemaToOpenApiSchema(schema: Schema) { - // optional, refã¯ã‚¹ã‚ーマ定義ã«å«ã¾ã‚Œãªã„ã®ã§åˆ†é›¢ã—ã¦ãŠã +export function convertSchemaToOpenApiSchema(schema: Schema, type: 'param' | 'res', includeSelfRef: boolean): any { + // optional, nullable, refã¯ã‚¹ã‚ーマ定義ã«å«ã¾ã‚Œãªã„ã®ã§åˆ†é›¢ã—ã¦ãŠã // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { optional, ref, ...res }: any = schema; + const { optional, nullable, ref, selfRef, ...res }: any = schema; if (schema.type === 'object' && schema.properties) { - const required = Object.entries(schema.properties).filter(([k, v]) => !v.optional).map(([k]) => k); - if (required.length > 0) { + if (type === 'res') { + const required = Object.entries(schema.properties).filter(([k, v]) => !v.optional).map(([k]) => k); + if (required.length > 0) { // 空é…列ã¯è¨±å¯ã•ã‚Œãªã„ - res.required = required; + res.required = required; + } } for (const k of Object.keys(schema.properties)) { - res.properties[k] = convertSchemaToOpenApiSchema(schema.properties[k]); + res.properties[k] = convertSchemaToOpenApiSchema(schema.properties[k], type, includeSelfRef); } } if (schema.type === 'array' && schema.items) { - res.items = convertSchemaToOpenApiSchema(schema.items); + res.items = convertSchemaToOpenApiSchema(schema.items, type, includeSelfRef); } - if (schema.anyOf) res.anyOf = schema.anyOf.map(convertSchemaToOpenApiSchema); - if (schema.oneOf) res.oneOf = schema.oneOf.map(convertSchemaToOpenApiSchema); - if (schema.allOf) res.allOf = schema.allOf.map(convertSchemaToOpenApiSchema); + for (const o of ['anyOf', 'oneOf', 'allOf'] as const) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (o in schema) res[o] = schema[o]!.map(schema => convertSchemaToOpenApiSchema(schema, type, includeSelfRef)); + } - if (schema.ref) { + if (type === 'res' && schema.ref && (!schema.selfRef || includeSelfRef)) { const $ref = `#/components/schemas/${schema.ref}`; if (schema.nullable || schema.optional) { res.allOf = [{ $ref }]; @@ -40,38 +43,48 @@ export function convertSchemaToOpenApiSchema(schema: Schema) { } } + if (schema.nullable) { + if (Array.isArray(schema.type) && !schema.type.includes('null')) { + res.type.push('null'); + } else if (typeof schema.type === 'string') { + res.type = [res.type, 'null']; + } + } + return res; } -export const schemas = { - Error: { - type: 'object', - properties: { - error: { - type: 'object', - description: 'An error object.', - properties: { - code: { - type: 'string', - description: 'An error code. Unique within the endpoint.', - }, - message: { - type: 'string', - description: 'An error message.', - }, - id: { - type: 'string', - format: 'uuid', - description: 'An error ID. This ID is static.', +export function getSchemas(includeSelfRef: boolean) { + return { + Error: { + type: 'object', + properties: { + error: { + type: 'object', + description: 'An error object.', + properties: { + code: { + type: 'string', + description: 'An error code. Unique within the endpoint.', + }, + message: { + type: 'string', + description: 'An error message.', + }, + id: { + type: 'string', + format: 'uuid', + description: 'An error ID. This ID is static.', + }, }, + required: ['code', 'id', 'message'], }, - required: ['code', 'id', 'message'], }, + required: ['error'], }, - required: ['error'], - }, - ...Object.fromEntries( - Object.entries(refs).map(([key, schema]) => [key, convertSchemaToOpenApiSchema(schema)]), - ), -}; + ...Object.fromEntries( + Object.entries(refs).map(([key, schema]) => [key, convertSchemaToOpenApiSchema(schema, 'res', includeSelfRef)]), + ), + }; +} diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index 3fc3f4d31af512970346bb8829633a900c1caac6..83c5fcdf52af461bcd093d483214bff162a4c050 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -20,6 +20,8 @@ import { AntennaChannelService } from './channels/antenna.js'; import { DriveChannelService } from './channels/drive.js'; import { HashtagChannelService } from './channels/hashtag.js'; import { RoleTimelineChannelService } from './channels/role-timeline.js'; +import { ReversiChannelService } from './channels/reversi.js'; +import { ReversiGameChannelService } from './channels/reversi-game.js'; import { type MiChannelService } from './channel.js'; @Injectable() @@ -40,6 +42,8 @@ export class ChannelsService { private serverStatsChannelService: ServerStatsChannelService, private queueStatsChannelService: QueueStatsChannelService, private adminChannelService: AdminChannelService, + private reversiChannelService: ReversiChannelService, + private reversiGameChannelService: ReversiGameChannelService, ) { } @@ -61,6 +65,8 @@ export class ChannelsService { case 'serverStats': return this.serverStatsChannelService; case 'queueStats': return this.queueStatsChannelService; case 'admin': return this.adminChannelService; + case 'reversi': return this.reversiChannelService; + case 'reversiGame': return this.reversiGameChannelService; default: throw new Error(`no such channel: ${name}`); diff --git a/packages/backend/src/server/api/stream/Connection.ts b/packages/backend/src/server/api/stream/Connection.ts index a89fbcc5e5427d881907b0655ae0b4fd6704de45..41c0feccc7e37df269bb1657f06cd71470bc9c34 100644 --- a/packages/backend/src/server/api/stream/Connection.ts +++ b/packages/backend/src/server/api/stream/Connection.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/stream/channel.ts b/packages/backend/src/server/api/stream/channel.ts index 80df3803ebbf354d1ddf9912ce0e0c4e835f0c4d..44a143538b4b5097a4d154fb185ccf76049774f6 100644 --- a/packages/backend/src/server/api/stream/channel.ts +++ b/packages/backend/src/server/api/stream/channel.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/stream/channels/admin.ts b/packages/backend/src/server/api/stream/channels/admin.ts index b8f369ce84f92a8f8fa4247629bf97212d8276cf..92b6d2ac040f9d4eb74858925c6d9bc2a4a87c82 100644 --- a/packages/backend/src/server/api/stream/channels/admin.ts +++ b/packages/backend/src/server/api/stream/channels/admin.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts index 200db8eb0eb39641f4574c879139178be37591d4..135d162e639515557b8d1ec8d998d42895bb18e8 100644 --- a/packages/backend/src/server/api/stream/channels/antenna.ts +++ b/packages/backend/src/server/api/stream/channels/antenna.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/stream/channels/bubble-timeline.ts b/packages/backend/src/server/api/stream/channels/bubble-timeline.ts index 4f8809edbe2d52e7e8fbf2c2f2e9b908012da496..2d85d65ba5daafc5f6fcfe3bdf2b204752742a2f 100644 --- a/packages/backend/src/server/api/stream/channels/bubble-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/bubble-timeline.ts @@ -78,6 +78,9 @@ class BubbleTimelineChannel extends Channel { // æµã‚Œã¦ããŸNoteãŒãƒ–ãƒãƒƒã‚¯ã•ã‚Œã¦ã„るユーザーãŒé–¢ã‚ã‚‹ã‚‚ã®ã ã£ãŸã‚‰ç„¡è¦–ã™ã‚‹ if (isUserRelated(note, this.userIdsWhoBlockingMe)) return; + if (note.renote && !note.text && note.renote.mentions?.some(mention => this.userIdsWhoMeMuting.has(mention))) return; + if (note.mentions?.some(mention => this.userIdsWhoMeMuting.has(mention))) return; + if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; if (this.user && note.renoteId && !note.text) { diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts index 20275249b81ea7486f4af36b79c4efda774911d8..90ee1ecda5d3ba7ec4305918a41dde751be549c9 100644 --- a/packages/backend/src/server/api/stream/channels/channel.ts +++ b/packages/backend/src/server/api/stream/channels/channel.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/stream/channels/drive.ts b/packages/backend/src/server/api/stream/channels/drive.ts index 4bf34a72c9777ebcabe0d0c9d57e18e696383a05..0d9b48630511e1b583721327602c3be0b4615fcb 100644 --- a/packages/backend/src/server/api/stream/channels/drive.ts +++ b/packages/backend/src/server/api/stream/channels/drive.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index e05e380aaeb24e14295ad46c02bfa28eb89b0fed..fc257247822d6b1320208dd7bcf90bd137e89574 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -74,6 +74,9 @@ class GlobalTimelineChannel extends Channel { // æµã‚Œã¦ããŸNoteãŒãƒ–ãƒãƒƒã‚¯ã•ã‚Œã¦ã„るユーザーãŒé–¢ã‚ã‚‹ã‚‚ã®ã ã£ãŸã‚‰ç„¡è¦–ã™ã‚‹ if (isUserRelated(note, this.userIdsWhoBlockingMe)) return; + if (note.renote && !note.text && note.renote.mentions?.some(mention => this.userIdsWhoMeMuting.has(mention))) return; + if (note.mentions?.some(mention => this.userIdsWhoMeMuting.has(mention))) return; + if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; if (this.user && note.renoteId && !note.text) { diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts index 3d4f2fc528f98171f3eb9505932a50371aa7390d..377b1a0162398bed515ac1b833871b892e580e87 100644 --- a/packages/backend/src/server/api/stream/channels/hashtag.ts +++ b/packages/backend/src/server/api/stream/channels/hashtag.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index 3b499b7bf3b20f040f044b6d258795b156e55188..0a4852ee8d10e388c47dffb73538ceb0f2aa8de7 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -73,13 +73,24 @@ class HomeTimelineChannel extends Channel { if (note.user.isSilenced && !this.following[note.userId] && note.userId !== this.user!.id) return; - if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; + // 純粋ãªãƒªãƒŽãƒ¼ãƒˆï¼ˆå¼•ç”¨ãƒªãƒŽãƒ¼ãƒˆã§ãªã„リノート)ã®å ´åˆ + if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && note.poll == null) { + if (!this.withRenotes) return; + if (note.renote.reply) { + const reply = note.renote.reply; + // 自分ã®ãƒ•ã‚©ãƒãƒ¼ã—ã¦ã„ãªã„ユーザー㮠visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ã®ãƒªãƒŽãƒ¼ãƒˆã¯å¼¾ã + if (reply.visibility === 'followers' && !Object.hasOwn(this.following, reply.userId)) return; + } + } // æµã‚Œã¦ããŸNoteãŒãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーãŒé–¢ã‚ã‚‹ã‚‚ã®ã ã£ãŸã‚‰ç„¡è¦–ã™ã‚‹ if (isUserRelated(note, this.userIdsWhoMeMuting)) return; // æµã‚Œã¦ããŸNoteãŒãƒ–ãƒãƒƒã‚¯ã•ã‚Œã¦ã„るユーザーãŒé–¢ã‚ã‚‹ã‚‚ã®ã ã£ãŸã‚‰ç„¡è¦–ã™ã‚‹ if (isUserRelated(note, this.userIdsWhoBlockingMe)) return; + if (note.renote && !note.text && note.renote.mentions?.some(mention => this.userIdsWhoMeMuting.has(mention))) return; + if (note.mentions?.some(mention => this.userIdsWhoMeMuting.has(mention))) return; + if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; if (this.user && note.renoteId && !note.text) { diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 26cbbebe8391577d369b16d3b46539436ada9d23..02786e9e16286db7fad7068d2b3a45161b9ea9b6 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -97,6 +97,9 @@ class HybridTimelineChannel extends Channel { // æµã‚Œã¦ããŸNoteãŒãƒ–ãƒãƒƒã‚¯ã•ã‚Œã¦ã„るユーザーãŒé–¢ã‚ã‚‹ã‚‚ã®ã ã£ãŸã‚‰ç„¡è¦–ã™ã‚‹ if (isUserRelated(note, this.userIdsWhoBlockingMe)) return; + if (note.renote && !note.text && note.renote.mentions?.some(mention => this.userIdsWhoMeMuting.has(mention))) return; + if (note.mentions?.some(mention => this.userIdsWhoMeMuting.has(mention))) return; + if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; if (this.user && note.renoteId && !note.text) { diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 40342b6c7b7a7c9f7ea7124937e98aa9901b6bfe..71b5675402efe3c56cf536860fa97785bcf7470d 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -73,6 +73,9 @@ class LocalTimelineChannel extends Channel { // æµã‚Œã¦ããŸNoteãŒãƒ–ãƒãƒƒã‚¯ã•ã‚Œã¦ã„るユーザーãŒé–¢ã‚ã‚‹ã‚‚ã®ã ã£ãŸã‚‰ç„¡è¦–ã™ã‚‹ if (isUserRelated(note, this.userIdsWhoBlockingMe)) return; + if (note.renote && !note.text && note.renote.mentions?.some(mention => this.userIdsWhoMeMuting.has(mention))) return; + if (note.mentions?.some(mention => this.userIdsWhoMeMuting.has(mention))) return; + if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; if (this.user && note.renoteId && !note.text) { diff --git a/packages/backend/src/server/api/stream/channels/main.ts b/packages/backend/src/server/api/stream/channels/main.ts index ab605e3ec5c0a1b4a672713f092e67c1718e79f3..a12976d69d1ab96d8348f0f9908ec47a82ac0b31 100644 --- a/packages/backend/src/server/api/stream/channels/main.ts +++ b/packages/backend/src/server/api/stream/channels/main.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/stream/channels/queue-stats.ts b/packages/backend/src/server/api/stream/channels/queue-stats.ts index 5ceb2c3bbc4fa971484e6df2fae8afaa3858dbd2..061aa76904a48984fec3b5e92b8e3c8f413c5418 100644 --- a/packages/backend/src/server/api/stream/channels/queue-stats.ts +++ b/packages/backend/src/server/api/stream/channels/queue-stats.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/stream/channels/reversi-game.ts b/packages/backend/src/server/api/stream/channels/reversi-game.ts new file mode 100644 index 0000000000000000000000000000000000000000..f4a3a09367e3a4f89bda8211739b5779edce4e86 --- /dev/null +++ b/packages/backend/src/server/api/stream/channels/reversi-game.ts @@ -0,0 +1,111 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import type { MiReversiGame } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; +import { ReversiService } from '@/core/ReversiService.js'; +import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js'; +import Channel, { type MiChannelService } from '../channel.js'; + +class ReversiGameChannel extends Channel { + public readonly chName = 'reversiGame'; + public static shouldShare = false; + public static requireCredential = false as const; + private gameId: MiReversiGame['id'] | null = null; + + constructor( + private reversiService: ReversiService, + private reversiGameEntityService: ReversiGameEntityService, + + id: string, + connection: Channel['connection'], + ) { + super(id, connection); + } + + @bindThis + public async init(params: any) { + this.gameId = params.gameId as string; + + this.subscriber.on(`reversiGameStream:${this.gameId}`, this.send); + } + + @bindThis + public onMessage(type: string, body: any) { + switch (type) { + case 'ready': this.ready(body); break; + case 'updateSettings': this.updateSettings(body.key, body.value); break; + case 'cancel': this.cancelGame(); break; + case 'putStone': this.putStone(body.pos, body.id); break; + case 'claimTimeIsUp': this.claimTimeIsUp(); break; + } + } + + @bindThis + private async updateSettings(key: string, value: any) { + if (this.user == null) return; + + this.reversiService.updateSettings(this.gameId!, this.user, key, value); + } + + @bindThis + private async ready(ready: boolean) { + if (this.user == null) return; + + this.reversiService.gameReady(this.gameId!, this.user, ready); + } + + @bindThis + private async cancelGame() { + if (this.user == null) return; + + this.reversiService.cancelGame(this.gameId!, this.user); + } + + @bindThis + private async putStone(pos: number, id: string) { + if (this.user == null) return; + + this.reversiService.putStoneToGame(this.gameId!, this.user, pos, id); + } + + @bindThis + private async claimTimeIsUp() { + if (this.user == null) return; + + this.reversiService.checkTimeout(this.gameId!); + } + + @bindThis + public dispose() { + // Unsubscribe events + this.subscriber.off(`reversiGameStream:${this.gameId}`, this.send); + } +} + +@Injectable() +export class ReversiGameChannelService implements MiChannelService<false> { + public readonly shouldShare = ReversiGameChannel.shouldShare; + public readonly requireCredential = ReversiGameChannel.requireCredential; + public readonly kind = ReversiGameChannel.kind; + + constructor( + private reversiService: ReversiService, + private reversiGameEntityService: ReversiGameEntityService, + ) { + } + + @bindThis + public create(id: string, connection: Channel['connection']): ReversiGameChannel { + return new ReversiGameChannel( + this.reversiService, + this.reversiGameEntityService, + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/channels/reversi.ts b/packages/backend/src/server/api/stream/channels/reversi.ts new file mode 100644 index 0000000000000000000000000000000000000000..3998a0fd363e2d37db2addef82912b222c54c181 --- /dev/null +++ b/packages/backend/src/server/api/stream/channels/reversi.ts @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; +import Channel, { type MiChannelService } from '../channel.js'; + +class ReversiChannel extends Channel { + public readonly chName = 'reversi'; + public static shouldShare = true; + public static requireCredential = true as const; + public static kind = 'read:account'; + + constructor( + id: string, + connection: Channel['connection'], + ) { + super(id, connection); + } + + @bindThis + public async init(params: any) { + this.subscriber.on(`reversiStream:${this.user!.id}`, this.send); + } + + @bindThis + public dispose() { + // Unsubscribe events + this.subscriber.off(`reversiStream:${this.user!.id}`, this.send); + } +} + +@Injectable() +export class ReversiChannelService implements MiChannelService<true> { + public readonly shouldShare = ReversiChannel.shouldShare; + public readonly requireCredential = ReversiChannel.requireCredential; + public readonly kind = ReversiChannel.kind; + + constructor( + ) { + } + + @bindThis + public create(id: string, connection: Channel['connection']): ReversiChannel { + return new ReversiChannel( + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/channels/role-timeline.ts b/packages/backend/src/server/api/stream/channels/role-timeline.ts index b3bbb77dbf20569c4c79782c9e9fb218a94cd7ed..80aab4b35ec122ec4553d904c1b876e9bf442509 100644 --- a/packages/backend/src/server/api/stream/channels/role-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/role-timeline.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/stream/channels/server-stats.ts b/packages/backend/src/server/api/stream/channels/server-stats.ts index 615b6946ccba7350c96e26d4da1cf79860f7e067..eb4d8c9992cba2aecba82ee712ee4a7e9769a607 100644 --- a/packages/backend/src/server/api/stream/channels/server-stats.ts +++ b/packages/backend/src/server/api/stream/channels/server-stats.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index 909b5a5e031fb7b6208e72aa59faf8da3f993204..f7bb106c03eb1f5df3860132debf09ef82f3489e 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -21,6 +21,7 @@ class UserListChannel extends Channel { private membershipsMap: Record<string, Pick<MiUserListMembership, 'withReplies'> | undefined> = {}; private listUsersClock: NodeJS.Timeout; private withFiles: boolean; + private withRenotes: boolean; constructor( private userListsRepository: UserListsRepository, @@ -39,9 +40,10 @@ class UserListChannel extends Channel { public async init(params: any) { this.listId = params.listId as string; this.withFiles = params.withFiles ?? false; + this.withRenotes = params.withRenotes ?? true; // Check existence and owner - const listExist = await this.userListsRepository.exist({ + const listExist = await this.userListsRepository.exists({ where: { id: this.listId, userId: this.user!.id, @@ -104,6 +106,8 @@ class UserListChannel extends Channel { } } + if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; + // æµã‚Œã¦ããŸNoteãŒãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーãŒé–¢ã‚ã‚‹ã‚‚ã®ã ã£ãŸã‚‰ç„¡è¦–ã™ã‚‹ if (isUserRelated(note, this.userIdsWhoMeMuting)) return; // æµã‚Œã¦ããŸNoteãŒãƒ–ãƒãƒƒã‚¯ã•ã‚Œã¦ã„るユーザーãŒé–¢ã‚ã‚‹ã‚‚ã®ã ã£ãŸã‚‰ç„¡è¦–ã™ã‚‹ diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts index 6de903862099138d45b735120060bd08c1181603..6598aa989176780f2b1cfa27b49763f1a75face7 100644 --- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts +++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/web/ClientLoggerService.ts b/packages/backend/src/server/web/ClientLoggerService.ts index 213266f59c8deb0d44c0357b90a13eb09ebd3e90..83d8b5bc38df11d539e1dfa35d0816d859c43d72 100644 --- a/packages/backend/src/server/web/ClientLoggerService.ts +++ b/packages/backend/src/server/web/ClientLoggerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index aa696046ea196a868dc3a1e73e281c82e1556571..cb41c4f338b521c3345ff8457e06c309d9143eb1 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -19,6 +19,7 @@ import fastifyView from '@fastify/view'; import fastifyCookie from '@fastify/cookie'; import fastifyProxy from '@fastify/http-proxy'; import vary from 'vary'; +import htmlSafeJsonStringify from 'htmlescape'; import type { Config } from '@/config.js'; import { getNoteSummary } from '@/misc/get-note-summary.js'; import { DI } from '@/di-symbols.js'; @@ -28,15 +29,17 @@ import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, Obj import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { PageEntityService } from '@/core/entities/PageEntityService.js'; +import { MetaEntityService } from '@/core/entities/MetaEntityService.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; -import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, MiMeta, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; +import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, MiMeta, NotesRepository, PagesRepository, ReversiGamesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; -import { deepClone } from '@/misc/clone.js'; +import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js'; import { bindThis } from '@/decorators.js'; import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; import { RoleService } from '@/core/RoleService.js'; +import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js'; import { FeedService } from './FeedService.js'; import { UrlPreviewService } from './UrlPreviewService.js'; import { ClientLoggerService } from './ClientLoggerService.js'; @@ -50,6 +53,7 @@ const clientAssets = `${_dirname}/../../../../frontend/assets/`; const assets = `${_dirname}/../../../../../built/_frontend_dist_/`; const swAssets = `${_dirname}/../../../../../built/_sw_dist_/`; const viteOut = `${_dirname}/../../../../../built/_vite_/`; +const tarball = `${_dirname}/../../../../../built/tarball/`; @Injectable() export class ClientServerService { @@ -83,13 +87,18 @@ export class ClientServerService { @Inject(DI.flashsRepository) private flashsRepository: FlashsRepository, + @Inject(DI.reversiGamesRepository) + private reversiGamesRepository: ReversiGamesRepository, + private flashEntityService: FlashEntityService, private userEntityService: UserEntityService, private noteEntityService: NoteEntityService, private pageEntityService: PageEntityService, + private metaEntityService: MetaEntityService, private galleryPostEntityService: GalleryPostEntityService, private clipEntityService: ClipEntityService, private channelEntityService: ChannelEntityService, + private reversiGameEntityService: ReversiGameEntityService, private metaService: MetaService, private urlPreviewService: UrlPreviewService, private feedService: FeedService, @@ -168,7 +177,7 @@ export class ClientServerService { } @bindThis - private generateCommonPugData(meta: MiMeta) { + private async generateCommonPugData(meta: MiMeta) { return { instanceName: meta.name ?? 'Sharkey', icon: meta.iconUrl, @@ -179,6 +188,8 @@ export class ClientServerService { notFoundImageUrl: meta.notFoundImageUrl ?? 'https://launcher.moe/missingpage.webp', instanceUrl: this.config.url, randomMOTD: this.config.customMOTD ? this.config.customMOTD[Math.floor(Math.random() * this.config.customMOTD.length)] : undefined, + metaJson: htmlSafeJsonStringify(await this.metaEntityService.packDetailed(meta)), + now: Date.now(), }; } @@ -250,11 +261,16 @@ export class ClientServerService { //#region vite assets if (this.config.clientManifestExists) { - fastify.register(fastifyStatic, { - root: viteOut, - prefix: '/vite/', - maxAge: ms('30 days'), - decorateReply: false, + fastify.register((fastify, options, done) => { + fastify.register(fastifyStatic, { + root: viteOut, + prefix: '/vite/', + maxAge: ms('30 days'), + immutable: true, + decorateReply: false, + }); + fastify.addHook('onRequest', handleRequestRedirectToOmitSearch); + done(); }); } else { const port = (process.env.VITE_PORT ?? '5173'); @@ -289,6 +305,18 @@ export class ClientServerService { decorateReply: false, }); + fastify.register((fastify, options, done) => { + fastify.register(fastifyStatic, { + root: tarball, + prefix: '/tarball/', + maxAge: ms('30 days'), + immutable: true, + decorateReply: false, + }); + fastify.addHook('onRequest', handleRequestRedirectToOmitSearch); + done(); + }); + fastify.get('/favicon.ico', async (request, reply) => { return reply.sendFile('/favicon.ico', staticAssets); }); @@ -327,6 +355,21 @@ export class ClientServerService { }); }); + fastify.get<{ Params: { path: string } }>('/tossface/:path(.*)', async (request, reply) => { + const path = request.params.path; + + if (!path.match(/^[0-9a-f-]+\.svg$/)) { + reply.code(404); + return; + } + + reply.header('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\''); + + return await reply.sendFile(path, `${_dirname}/../../../../../tossface-emojis/dist`, { + maxAge: ms('30 days'), + }); + }); + fastify.get<{ Params: { path: string } }>('/twemoji-badge/:path(.*)', async (request, reply) => { const path = request.params.path; @@ -412,7 +455,7 @@ export class ClientServerService { url: this.config.url, title: meta.name ?? 'Misskey', desc: meta.description, - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), }); }; @@ -479,6 +522,8 @@ export class ClientServerService { isSuspended: false, }); + vary(reply.raw, 'Accept'); + if (user != null) { const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); const meta = await this.metaService.fetch(); @@ -497,7 +542,7 @@ export class ClientServerService { user, profile, me, avatarUrl: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user), sub: request.params.sub, - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), }); } else { // リモートユーザーãªã®ã§ @@ -518,6 +563,8 @@ export class ClientServerService { return; } + vary(reply.raw, 'Accept'); + reply.redirect(`/@${user.username}${ user.host == null ? '' : '@' + user.host}`); }); @@ -545,7 +592,7 @@ export class ClientServerService { avatarUrl: _note.user.avatarUrl, // TODO: Let locale changeable by instance setting summary: getNoteSummary(_note), - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), }); } else { return await renderBase(reply); @@ -584,7 +631,7 @@ export class ClientServerService { page: _page, profile, avatarUrl: _page.user.avatarUrl, - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), }); } else { return await renderBase(reply); @@ -610,7 +657,7 @@ export class ClientServerService { flash: _flash, profile, avatarUrl: _flash.user.avatarUrl, - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), }); } else { return await renderBase(reply); @@ -636,7 +683,7 @@ export class ClientServerService { clip: _clip, profile, avatarUrl: _clip.user.avatarUrl, - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), }); } else { return await renderBase(reply); @@ -660,7 +707,7 @@ export class ClientServerService { post: _post, profile, avatarUrl: _post.user.avatarUrl, - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), }); } else { return await renderBase(reply); @@ -679,7 +726,26 @@ export class ClientServerService { reply.header('Cache-Control', 'public, max-age=15'); return await reply.view('channel', { channel: _channel, - ...this.generateCommonPugData(meta), + ...await this.generateCommonPugData(meta), + }); + } else { + return await renderBase(reply); + } + }); + + // Reversi game + fastify.get<{ Params: { game: string; } }>('/reversi/g/:game', async (request, reply) => { + const game = await this.reversiGamesRepository.findOneBy({ + id: request.params.game, + }); + + if (game) { + const _game = await this.reversiGameEntityService.packDetail(game); + const meta = await this.metaService.fetch(); + reply.header('Cache-Control', 'public, max-age=3600'); + return await reply.view('reversi-game', { + game: _game, + ...await this.generateCommonPugData(meta), }); } else { return await renderBase(reply); diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts index aaa9566a763cea172ee194142f3f9022bb48fa17..dc7f6452c8dc544b96bd9fd250d5480348f386f1 100644 --- a/packages/backend/src/server/web/FeedService.ts +++ b/packages/backend/src/server/web/FeedService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index d590244e34a73a382c01caa29ef92e01d4f833a2..c6a96e94cbbd38f7e00b1e388ced7f618e72fe71 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -1,10 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { Inject, Injectable } from '@nestjs/common'; -import { summaly } from 'summaly'; +import { summaly } from '@misskey-dev/summaly'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { MetaService } from '@/core/MetaService.js'; diff --git a/packages/backend/src/server/web/bios.css b/packages/backend/src/server/web/bios.css index c934a55fa9a3e1cd0ef9061f7f8f7f36cbb61153..91d1af10b41c605bb6f9b227a96f7d10cc5377c2 100644 --- a/packages/backend/src/server/web/bios.css +++ b/packages/backend/src/server/web/bios.css @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/web/bios.js b/packages/backend/src/server/web/bios.js index 029eb92aad98b65ef834f1f8c4c17f495662e7f5..9ff5dca72ace8c8d9d1c4165722de5d321bb94fd 100644 --- a/packages/backend/src/server/web/bios.js +++ b/packages/backend/src/server/web/bios.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index aac6689e12e7b2da08070dc6222371aff81b75e1..6b9dc0a103f59863ca1d9f84fba5087db320a083 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/web/cli.css b/packages/backend/src/server/web/cli.css index b7737c3f21ebe8909b8b2e93d1eb3731e74778ee..4e6136d59c9314f94fcbd33f2756110c4bd6e20f 100644 --- a/packages/backend/src/server/web/cli.css +++ b/packages/backend/src/server/web/cli.css @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/web/cli.js b/packages/backend/src/server/web/cli.js index e63a80327c6e89433b5ccf16af3a1a95c1e1a5a3..30ee77f4d91db24f1f570f27e42a6ebec0fd571e 100644 --- a/packages/backend/src/server/web/cli.js +++ b/packages/backend/src/server/web/cli.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/web/error.css b/packages/backend/src/server/web/error.css index caa179e25c9a8bafd3979fa1f087560d07e30e91..6c96241970d06569d52bca772fd7f18956dca0ac 100644 --- a/packages/backend/src/server/web/error.css +++ b/packages/backend/src/server/web/error.css @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * * SPDX-License-Identifier: AGPL-3.0-only */ @@ -113,4 +113,4 @@ summary > * { details { width: 50%; } -} \ No newline at end of file +} diff --git a/packages/backend/src/server/web/style.css b/packages/backend/src/server/web/style.css index 171827a523bbaf90da11e1d7efd84116a6daa93a..b60a6da49a222954b585f40cd23b9fcc77786b34 100644 --- a/packages/backend/src/server/web/style.css +++ b/packages/backend/src/server/web/style.css @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index c15e123a15130c53147f19f3cd662af82a714153..2210e65e7df3bd321b0a06a1f96b7187e1bd66ec 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -18,7 +18,7 @@ doctype html Thank you for using Sharkey! If you are reading this message... how about joining the development? - https://git.joinsharkey.org/Sharkey/Sharkey + https://activitypub.software/TransFem-org/Sharkey html @@ -75,6 +75,9 @@ html var CLIENT_ENTRY = "#{clientEntry.file}"; window.libopenmpt = window.Module; + script(type='application/json' id='misskey_meta' data-generated-at=now) + != metaJson + script include ../boot.js diff --git a/packages/backend/src/server/web/views/error.pug b/packages/backend/src/server/web/views/error.pug index 00a2a72d7ac59cd819985e878bac1b6fad7061b1..39b75abc4cdfdf2b6ec0393caa4bb14889537fa2 100644 --- a/packages/backend/src/server/web/views/error.pug +++ b/packages/backend/src/server/web/views/error.pug @@ -13,7 +13,7 @@ doctype html Thank you for using Sharkey! If you are reading this message... how about joining the development? - https://git.joinsharkey.org/Sharkey/Sharkey + https://activitypub.software/TransFem-org/Sharkey html diff --git a/packages/backend/src/server/web/views/reversi-game.pug b/packages/backend/src/server/web/views/reversi-game.pug new file mode 100644 index 0000000000000000000000000000000000000000..0b5ffb2bb0959f1a025c1e9b8e53c484e591a99c --- /dev/null +++ b/packages/backend/src/server/web/views/reversi-game.pug @@ -0,0 +1,20 @@ +extends ./base + +block vars + - const user1 = game.user1; + - const user2 = game.user2; + - const title = `${user1.username} vs ${user2.username}`; + - const url = `${config.url}/reversi/g/${game.id}`; + +block title + = `${title} | ${instanceName}` + +block desc + meta(name='description' content='⚫⚪Misskey Reversi⚪⚫') + +block og + meta(property='og:type' content='article') + meta(property='og:title' content= title) + meta(property='og:description' content='⚫⚪Misskey Reversi⚪⚫') + meta(property='og:url' content= url) + meta(property='twitter:card' content='summary') diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index e55952f29661c96226aa9f195354cdd42f128836..edcf2530bc6e7cff768f15f2a7864eb1c89b5662 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -28,12 +28,21 @@ export const notificationTypes = [ 'quote', 'reaction', 'pollEnded', + 'edited', 'receiveFollowRequest', 'followRequestAccepted', 'roleAssigned', 'achievementEarned', 'app', - 'test'] as const; + 'test', +] as const; + +export const groupedNotificationTypes = [ + ...notificationTypes, + 'reaction:grouped', + 'renote:grouped', +] as const; + export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; @@ -70,6 +79,7 @@ export const moderationLogTypes = [ 'resetPassword', 'suspendRemoteInstance', 'unsuspendRemoteInstance', + 'updateRemoteInstanceNote', 'markSensitiveDriveFile', 'unmarkSensitiveDriveFile', 'resolveAbuseReport', @@ -215,6 +225,12 @@ export type ModerationLogPayloads = { id: string; host: string; }; + updateRemoteInstanceNote: { + id: string; + host: string; + before: string | null; + after: string | null; + }; markSensitiveDriveFile: { fileId: string; fileUserId: string | null; @@ -283,7 +299,11 @@ export type Serialized<T> = { ? (string | null) : T[K] extends Record<string, any> ? Serialized<T[K]> - : T[K]; + : T[K] extends (Record<string, any> | null) + ? (Serialized<T[K]> | null) + : T[K] extends (Record<string, any> | undefined) + ? (Serialized<T[K]> | undefined) + : T[K]; }; export type FilterUnionByProperty< diff --git a/packages/backend/test-server/.eslintrc.cjs b/packages/backend/test-server/.eslintrc.cjs new file mode 100644 index 0000000000000000000000000000000000000000..c261741a36a14a28b0925800a7672bbcdf676c9d --- /dev/null +++ b/packages/backend/test-server/.eslintrc.cjs @@ -0,0 +1,32 @@ +module.exports = { + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + }, + extends: [ + '../../shared/.eslintrc.js', + ], + rules: { + 'import/order': ['warn', { + 'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'], + 'pathGroups': [ + { + 'pattern': '@/**', + 'group': 'external', + 'position': 'after' + } + ], + }], + 'no-restricted-globals': [ + 'error', + { + 'name': '__dirname', + 'message': 'Not in ESModule. Use `import.meta.url` instead.' + }, + { + 'name': '__filename', + 'message': 'Not in ESModule. Use `import.meta.url` instead.' + } + ] + }, +}; diff --git a/packages/backend/test-server/.swcrc b/packages/backend/test-server/.swcrc new file mode 100644 index 0000000000000000000000000000000000000000..e3d6935169eddf10cb1000eeb356d0bb1f98c9e8 --- /dev/null +++ b/packages/backend/test-server/.swcrc @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/swcrc", + "jsc": { + "parser": { + "syntax": "typescript", + "dynamicImport": true, + "decorators": true + }, + "transform": { + "legacyDecorator": true, + "decoratorMetadata": true + }, + "experimental": { + "keepImportAssertions": true + }, + "baseUrl": "../built", + "paths": { + "@/*": ["*"] + }, + "target": "es2022" + }, + "minify": false +} diff --git a/packages/backend/test-server/entry.ts b/packages/backend/test-server/entry.ts new file mode 100644 index 0000000000000000000000000000000000000000..866a7e1f5bc5da05e5172ef021fae5daa501788c --- /dev/null +++ b/packages/backend/test-server/entry.ts @@ -0,0 +1,80 @@ +import { portToPid } from 'pid-port'; +import fkill from 'fkill'; +import Fastify from 'fastify'; +import { NestFactory } from '@nestjs/core'; +import { MainModule } from '@/MainModule.js'; +import { ServerService } from '@/server/ServerService.js'; +import { loadConfig } from '@/config.js'; +import { NestLogger } from '@/NestLogger.js'; + +const config = loadConfig(); +const originEnv = JSON.stringify(process.env); + +process.env.NODE_ENV = 'test'; + +/** + * テスト用ã®ã‚µãƒ¼ãƒã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã‚’èµ·å‹•ã™ã‚‹ + */ +async function launch() { + await killTestServer(); + + console.log('starting application...'); + + const app = await NestFactory.createApplicationContext(MainModule, { + logger: new NestLogger(), + }); + const serverService = app.get(ServerService); + await serverService.launch(); + + await startControllerEndpoints(); + + // ジョブã‚ューã¯å¿…è¦ãªæ™‚ã«ãƒ†ã‚¹ãƒˆã‚³ãƒ¼ãƒ‰å´ã§èµ·å‹•ã™ã‚‹ + // ジョブã‚ューãŒå‹•ãã¨ãƒ†ã‚¹ãƒˆçµæžœã®ç¢ºèªã«æ”¯éšœãŒå‡ºã‚‹ã“ã¨ãŒã‚ã‚‹ã®ã§æ„図的ã«å‹•ã‹ã•ãªã„ã§ã„ã‚‹ + + console.log('application initialized.'); +} + +/** + * æ—¢ã«é‡è¤‡ã—ãŸãƒãƒ¼ãƒˆã§å¾…ã¡å—ã‘ã—ã¦ã„るサーãƒãŒã‚ã‚‹å ´åˆã¯killã™ã‚‹ + */ +async function killTestServer() { + // + try { + const pid = await portToPid(config.port); + if (pid) { + await fkill(pid, { force: true }); + } + } catch { + // NOP; + } +} + +/** + * 別プãƒã‚»ã‚¹ã«åˆ‡ã‚Šé›¢ã—ã¦ã—ã¾ã£ãŸãŒæ•…ã«å‡ºæ¥ãªããªã£ãŸç’°å¢ƒå¤‰æ•°ã®æ›¸ãæ›ãˆç‰ã‚’実ç¾ã™ã‚‹ãŸã‚ã®ã‚¨ãƒ³ãƒ‰ãƒã‚¤ãƒ³ãƒˆã‚’作る + * @param port + */ +async function startControllerEndpoints(port = config.port + 1000) { + const fastify = Fastify(); + + fastify.post<{ Body: { key?: string, value?: string } }>('/env', async (req, res) => { + console.log(req.body); + const key = req.body['key']; + if (!key) { + res.code(400).send({ success: false }); + return; + } + + process.env[key] = req.body['value']; + + res.code(200).send({ success: true }); + }); + + fastify.post<{ Body: { key?: string, value?: string } }>('/env-reset', async (req, res) => { + process.env = JSON.parse(originEnv); + res.code(200).send({ success: true }); + }); + + await fastify.listen({ port: port, host: 'localhost' }); +} + +export default launch; diff --git a/packages/backend/test-server/tsconfig.json b/packages/backend/test-server/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..10313699c2efb659d68d63ff977c6b31cd026dda --- /dev/null +++ b/packages/backend/test-server/tsconfig.json @@ -0,0 +1,52 @@ +{ + "compilerOptions": { + "allowJs": true, + "noEmitOnError": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noUnusedParameters": false, + "noUnusedLocals": false, + "noFallthroughCasesInSwitch": true, + "declaration": false, + "sourceMap": true, + "target": "ES2022", + "module": "nodenext", + "moduleResolution": "nodenext", + "allowSyntheticDefaultImports": true, + "removeComments": false, + "noLib": false, + "strict": true, + "strictNullChecks": true, + "strictPropertyInitialization": false, + "skipLibCheck": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "resolveJsonModule": true, + "isolatedModules": true, + "rootDir": "../src", + "baseUrl": "./", + "paths": { + "@/*": ["../src/*"] + }, + "outDir": "../built-test", + "types": [ + "node" + ], + "typeRoots": [ + "../src/@types", + "../node_modules/@types", + "../node_modules" + ], + "lib": [ + "esnext" + ] + }, + "compileOnSave": false, + "include": [ + "./**/*.ts", + "../src/**/*.ts" + ], + "exclude": [ + "../src/**/*.test.ts" + ] +} diff --git a/packages/backend/test/e2e/2fa.ts b/packages/backend/test/e2e/2fa.ts index ed967d2620da49a03466e3fb2953934bc2436bf8..87a3c227d64dbd090920e3c9ab4c43673f3f6341 100644 --- a/packages/backend/test/e2e/2fa.ts +++ b/packages/backend/test/e2e/2fa.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -10,7 +10,7 @@ import * as crypto from 'node:crypto'; import cbor from 'cbor'; import * as OTPAuth from 'otpauth'; import { loadConfig } from '@/config.js'; -import { api, signup, startServer } from '../utils.js'; +import { api, signup } from '../utils.js'; import type { AuthenticationResponseJSON, AuthenticatorAssertionResponseJSON, @@ -18,13 +18,11 @@ import type { PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON, -} from '@simplewebauthn/typescript-types'; -import type { INestApplicationContext } from '@nestjs/common'; +} from '@simplewebauthn/types'; import type * as misskey from 'misskey-js'; describe('2è¦ç´ èªè¨¼', () => { - let app: INestApplicationContext; - let alice: misskey.entities.MeSignup; + let alice: misskey.entities.SignupResponse; const config = loadConfig(); const password = 'test'; @@ -185,14 +183,9 @@ describe('2è¦ç´ èªè¨¼', () => { }; beforeAll(async () => { - app = await startServer(); alice = await signup({ username, password }); }, 1000 * 60 * 2); - afterAll(async () => { - await app.close(); - }); - test('ãŒè¨å®šã§ãã€OTPã§ãƒã‚°ã‚¤ãƒ³ã§ãる。', async () => { const registerResponse = await api('/i/2fa/register', { password, diff --git a/packages/backend/test/e2e/antennas.ts b/packages/backend/test/e2e/antennas.ts index c0317f1435461c25b65bce6f3c8dea7056a0e6f5..1a9d5bf1f01f0a79c25822630cc2e6dc24666d25 100644 --- a/packages/backend/test/e2e/antennas.ts +++ b/packages/backend/test/e2e/antennas.ts @@ -1,29 +1,25 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { inspect } from 'node:util'; import { DEFAULT_POLICIES } from '@/core/RoleService.js'; import type { Packed } from '@/misc/json-schema.js'; import { - signup, + api, + failedApiCall, post, - userList, - page, role, - startServer, - api, + signup, successfulApiCall, - failedApiCall, - uploadFile, testPaginationConsistency, + uploadFile, + userList, } from '../utils.js'; import type * as misskey from 'misskey-js'; -import type { INestApplicationContext } from '@nestjs/common'; const compareBy = <T extends { id: string }>(selector: (s: T) => string = (s: T): string => s.id) => (a: T, b: T): number => { return selector(a).localeCompare(selector(b)); @@ -37,7 +33,7 @@ describe('アンテナ', () => { // - srcã®enumã«groupãŒæ®‹ã£ã¦ã„ã‚‹ // - userGroupIdãŒæ®‹ã£ã¦ã„ã‚‹, isActiveãŒãªã„ type Antenna = misskey.entities.Antenna | Packed<'Antenna'>; - type User = misskey.entities.MeSignup; + type User = misskey.entities.SignupResponse; type Note = misskey.entities.Note; // アンテナを作æˆã§ãる最å°ã®ãƒ‘ラメタ @@ -54,8 +50,6 @@ describe('アンテナ', () => { withReplies: false, }; - let app: INestApplicationContext; - let root: User; let alice: User; let bob: User; @@ -79,10 +73,6 @@ describe('アンテナ', () => { let userMutingAlice: User; let userMutedByAlice: User; - beforeAll(async () => { - app = await startServer(); - }, 1000 * 60 * 2); - beforeAll(async () => { root = await signup({ username: 'root' }); alice = await signup({ username: 'alice' }); @@ -136,10 +126,6 @@ describe('アンテナ', () => { await api('mute/create', { userId: userMutedByAlice.id }, alice); }, 1000 * 60 * 10); - afterAll(async () => { - await app.close(); - }); - beforeEach(async () => { // テスト間ã§å½±éŸ¿ã—åˆã‚ãªã„よã†ã«æ¯Žå›žå…¨éƒ¨æ¶ˆã™ã€‚ for (const user of [alice, bob]) { diff --git a/packages/backend/test/e2e/api-visibility.ts b/packages/backend/test/e2e/api-visibility.ts index 33c8d03fdb33a077ccd151866dd31637063dd1f9..f92384525cf92c19f012205182f3a37e88215612 100644 --- a/packages/backend/test/e2e/api-visibility.ts +++ b/packages/backend/test/e2e/api-visibility.ts @@ -1,38 +1,27 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { signup, api, post, startServer } from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; +import { api, post, signup } from '../utils.js'; import type * as misskey from 'misskey-js'; describe('API visibility', () => { - let app: INestApplicationContext; - - beforeAll(async () => { - app = await startServer(); - }, 1000 * 60 * 2); - - afterAll(async () => { - await app.close(); - }); - describe('Note visibility', () => { //#region vars /** ヒãƒã‚¤ãƒ³ */ - let alice: misskey.entities.MeSignup; + let alice: misskey.entities.SignupResponse; /** フォãƒãƒ¯ãƒ¼ */ - let follower: misskey.entities.MeSignup; + let follower: misskey.entities.SignupResponse; /** éžãƒ•ã‚©ãƒãƒ¯ãƒ¼ */ - let other: misskey.entities.MeSignup; + let other: misskey.entities.SignupResponse; /** éžãƒ•ã‚©ãƒãƒ¯ãƒ¼ã§ã‚‚リプライやメンションをã•ã‚ŒãŸäºº */ - let target: misskey.entities.MeSignup; + let target: misskey.entities.SignupResponse; /** specified mentionã§mentionを飛ã°ã•ã‚Œã‚‹äºº */ - let target2: misskey.entities.MeSignup; + let target2: misskey.entities.SignupResponse; /** public-post */ let pub: any; diff --git a/packages/backend/test/e2e/api.ts b/packages/backend/test/e2e/api.ts index cf24228b8393356208afd908c15cd6fa445c8de5..b6eeec99d76258f3ee1a45b985b8427b24681bd3 100644 --- a/packages/backend/test/e2e/api.ts +++ b/packages/backend/test/e2e/api.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -7,27 +7,30 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import { IncomingMessage } from 'http'; -import { signup, api, startServer, successfulApiCall, failedApiCall, uploadFile, waitFire, connectStream, relativeFetch, createAppToken } from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; +import { + api, + connectStream, + createAppToken, + failedApiCall, + relativeFetch, + signup, + successfulApiCall, + uploadFile, + waitFire, +} from '../utils.js'; import type * as misskey from 'misskey-js'; describe('API', () => { - let app: INestApplicationContext; - let alice: misskey.entities.MeSignup; - let bob: misskey.entities.MeSignup; - let carol: misskey.entities.MeSignup; + let alice: misskey.entities.SignupResponse; + let bob: misskey.entities.SignupResponse; + let carol: misskey.entities.SignupResponse; beforeAll(async () => { - app = await startServer(); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); carol = await signup({ username: 'carol' }); }, 1000 * 60 * 2); - afterAll(async () => { - await app.close(); - }); - describe('General validation', () => { test('wrong type', async () => { const res = await api('/test', { diff --git a/packages/backend/test/e2e/block.ts b/packages/backend/test/e2e/block.ts index 4445d9036cdae8531b823f3c402fe84df9c29988..cbd91e6e42ac61def7dc37b769e61d1f3425bc39 100644 --- a/packages/backend/test/e2e/block.ts +++ b/packages/backend/test/e2e/block.ts @@ -1,34 +1,26 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { signup, api, post, startServer } from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; +import { api, post, signup } from '../utils.js'; import type * as misskey from 'misskey-js'; describe('Block', () => { - let app: INestApplicationContext; - // alice blocks bob - let alice: misskey.entities.MeSignup; - let bob: misskey.entities.MeSignup; - let carol: misskey.entities.MeSignup; + let alice: misskey.entities.SignupResponse; + let bob: misskey.entities.SignupResponse; + let carol: misskey.entities.SignupResponse; beforeAll(async () => { - app = await startServer(); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); carol = await signup({ username: 'carol' }); }, 1000 * 60 * 2); - afterAll(async () => { - await app.close(); - }); - test('Block作æˆ', async () => { const res = await api('/blocking/create', { userId: bob.id, diff --git a/packages/backend/test/e2e/clips.ts b/packages/backend/test/e2e/clips.ts index 49092fba638b3c04f668d05f2f7d853dd5659f5f..2cf397e22da92c017f870648db7b9bd98968860a 100644 --- a/packages/backend/test/e2e/clips.ts +++ b/packages/backend/test/e2e/clips.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -18,25 +18,13 @@ import { paramDef as UnfavoriteParamDef } from '@/server/api/endpoints/clips/unf import { paramDef as AddNoteParamDef } from '@/server/api/endpoints/clips/add-note.js'; import { paramDef as RemoveNoteParamDef } from '@/server/api/endpoints/clips/remove-note.js'; import { paramDef as NotesParamDef } from '@/server/api/endpoints/clips/notes.js'; -import { - signup, - post, - startServer, - api, - successfulApiCall, - failedApiCall, - ApiRequest, - hiddenNote, -} from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; +import { api, ApiRequest, failedApiCall, hiddenNote, post, signup, successfulApiCall } from '../utils.js'; describe('クリップ', () => { type User = Packed<'User'>; type Note = Packed<'Note'>; type Clip = Packed<'Clip'>; - let app: INestApplicationContext; - let alice: User; let bob: User; let aliceNote: Note; @@ -145,7 +133,6 @@ describe('クリップ', () => { }; beforeAll(async () => { - app = await startServer(); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); @@ -160,10 +147,6 @@ describe('クリップ', () => { bobSpecifiedNote = await post(bob, { text: 'specified only', visibility: 'specified' }) as any; }, 1000 * 60 * 2); - afterAll(async () => { - await app.close(); - }); - afterEach(async () => { // テスト間ã§å½±éŸ¿ã—åˆã‚ãªã„よã†ã«æ¯Žå›žå…¨éƒ¨æ¶ˆã™ã€‚ for (const user of [alice, bob]) { diff --git a/packages/backend/test/e2e/drive.ts b/packages/backend/test/e2e/drive.ts new file mode 100644 index 0000000000000000000000000000000000000000..22ec66e2afcb8c594dc461f947f5f5a5238f0af7 --- /dev/null +++ b/packages/backend/test/e2e/drive.ts @@ -0,0 +1,95 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +process.env.NODE_ENV = 'test'; + +import * as assert from 'assert'; +import { MiNote } from '@/models/Note.js'; +import { api, initTestDb, makeStreamCatcher, post, signup, uploadFile } from '../utils.js'; +import type * as misskey from 'misskey-js'; +import type{ Repository } from 'typeorm' +import type { Packed } from '@/misc/json-schema.js'; + + +describe('Drive', () => { + let Notes: Repository<MiNote>; + + let alice: misskey.entities.SignupResponse; + let bob: misskey.entities.SignupResponse; + + beforeAll(async () => { + const connection = await initTestDb(true); + Notes = connection.getRepository(MiNote); + alice = await signup({ username: 'alice' }); + bob = await signup({ username: 'bob' }); + }, 1000 * 60 * 2); + + test('ファイルURLã‹ã‚‰ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã§ãã‚‹', async () => { + // utils.js uploadUrl ã®å‡¦ç†ã ãŒAPIレスãƒãƒ³ã‚¹ã‚‚見るãŸã‚ã“ã“ã§åŒæ§˜ã®å‡¦ç†ã‚’書ã„ã¦ã„ã‚‹ + + const marker = Math.random().toString(); + + const url = 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg' + + const catcher = makeStreamCatcher( + alice, + 'main', + (msg) => msg.type === 'urlUploadFinished' && msg.body.marker === marker, + (msg) => msg.body.file as Packed<'DriveFile'>, + 10 * 1000); + + const res = await api('drive/files/upload-from-url', { + url, + marker, + force: true, + }, alice); + + const file = await catcher; + + assert.strictEqual(res.status, 204); + assert.strictEqual(file.name, 'Lenna.jpg'); + assert.strictEqual(file.type, 'image/jpeg'); + }) + + test('ãƒãƒ¼ã‚«ãƒ«ã‹ã‚‰ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã§ãã‚‹', async () => { + // APIレスãƒãƒ³ã‚¹ã‚’直接使用ã™ã‚‹ã®ã§ utils.js uploadFile ãŒé€šéŽã™ã‚‹ã“ã¨ã§æˆåŠŸã¨ã™ã‚‹ + + const res = await uploadFile(alice, { path: 'Lenna.jpg', name: 'テスト画åƒ' }); + + assert.strictEqual(res.body?.name, 'テスト画åƒ.jpg'); + assert.strictEqual(res.body?.type, 'image/jpeg'); + }) + + test('添付ノート一覧をå–å¾—ã§ãã‚‹', async () => { + const ids = (await Promise.all([uploadFile(alice), uploadFile(alice), uploadFile(alice)])).map(elm => elm.body!.id) + + const note0 = await post(alice, { fileIds: [ids[0]] }); + const note1 = await post(alice, { fileIds: [ids[0], ids[1]] }); + + const attached0 = await api('drive/files/attached-notes', { fileId: ids[0] }, alice); + assert.strictEqual(attached0.body.length, 2); + assert.strictEqual(attached0.body[0].id, note1.id) + assert.strictEqual(attached0.body[1].id, note0.id) + + const attached1 = await api('drive/files/attached-notes', { fileId: ids[1] }, alice); + assert.strictEqual(attached1.body.length, 1); + assert.strictEqual(attached1.body[0].id, note1.id) + + const attached2 = await api('drive/files/attached-notes', { fileId: ids[2] }, alice); + assert.strictEqual(attached2.body.length, 0) + }) + + test('添付ノート一覧ã¯ä»–ã®äººã‹ã‚‰è¦‹ãˆãªã„', async () => { + const file = await uploadFile(alice); + + await post(alice, { fileIds: [file.body!.id] }); + + const res = await api('drive/files/attached-notes', { fileId: file.body!.id }, bob); + assert.strictEqual(res.status, 400); + assert.strictEqual('error' in res.body, true); + + }) +}); + diff --git a/packages/backend/test/e2e/endpoints.ts b/packages/backend/test/e2e/endpoints.ts index 2ef3434bcab1478fc9d46a27aac734283787beed..d4695978053ecdf7f7912d21aea590ed37cfaec7 100644 --- a/packages/backend/test/e2e/endpoints.ts +++ b/packages/backend/test/e2e/endpoints.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -10,30 +10,22 @@ import * as assert from 'assert'; // https://github.com/node-fetch/node-fetch/pull/1664 import { Blob } from 'node-fetch'; import { MiUser } from '@/models/_.js'; -import { startServer, signup, post, api, uploadFile, simpleGet, initTestDb } from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; +import { api, initTestDb, post, signup, simpleGet, uploadFile } from '../utils.js'; import type * as misskey from 'misskey-js'; describe('Endpoints', () => { - let app: INestApplicationContext; - - let alice: misskey.entities.MeSignup; - let bob: misskey.entities.MeSignup; - let carol: misskey.entities.MeSignup; - let dave: misskey.entities.MeSignup; + let alice: misskey.entities.SignupResponse; + let bob: misskey.entities.SignupResponse; + let carol: misskey.entities.SignupResponse; + let dave: misskey.entities.SignupResponse; beforeAll(async () => { - app = await startServer(); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); carol = await signup({ username: 'carol' }); dave = await signup({ username: 'dave' }); }, 1000 * 60 * 2); - afterAll(async () => { - await app.close(); - }); - describe('signup', () => { test('ä¸æ£ãªãƒ¦ãƒ¼ã‚¶ãƒ¼åã§ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒä½œæˆã§ããªã„', async () => { const res = await api('signup', { @@ -710,6 +702,18 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); + test('ä¸æ£ãªãƒ•ã‚¡ã‚¤ãƒ«åã§æ€’られる', async () => { + const file = (await uploadFile(alice)).body; + const newName = ''; + + const res = await api('/drive/files/update', { + fileId: file.id, + name: newName, + }, alice); + + assert.strictEqual(res.status, 400); + }); + test('é–“é•ã£ãŸIDã§æ€’られる', async () => { const res = await api('/drive/files/update', { fileId: 'kyoppie', diff --git a/packages/backend/test/e2e/exports.ts b/packages/backend/test/e2e/exports.ts new file mode 100644 index 0000000000000000000000000000000000000000..eb03935a2ad61c634fd3cf50b063f9bded2315f6 --- /dev/null +++ b/packages/backend/test/e2e/exports.ts @@ -0,0 +1,193 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +process.env.NODE_ENV = 'test'; + +import * as assert from 'assert'; +import { api, port, post, signup, startJobQueue } from '../utils.js'; +import type { INestApplicationContext } from '@nestjs/common'; +import type * as misskey from 'misskey-js'; + +describe('export-clips', () => { + let queue: INestApplicationContext; + let alice: misskey.entities.SignupResponse; + let bob: misskey.entities.SignupResponse; + + // XXX: Any better way to get the result? + async function pollFirstDriveFile() { + while (true) { + const files = (await api('/drive/files', {}, alice)).body; + if (!files.length) { + await new Promise(r => setTimeout(r, 100)); + continue; + } + if (files.length > 1) { + throw new Error('Too many files?'); + } + const file = (await api('/drive/files/show', { fileId: files[0].id }, alice)).body; + const res = await fetch(new URL(new URL(file.url).pathname, `http://127.0.0.1:${port}`)); + return await res.json(); + } + } + + beforeAll(async () => { + queue = await startJobQueue(); + alice = await signup({ username: 'alice' }); + bob = await signup({ username: 'bob' }); + }, 1000 * 60 * 2); + + afterAll(async () => { + await queue.close(); + }); + + beforeEach(async () => { + // Clean all clips and files of alice + const clips = (await api('/clips/list', {}, alice)).body; + for (const clip of clips) { + const res = await api('/clips/delete', { clipId: clip.id }, alice); + if (res.status !== 204) { + throw new Error('Failed to delete clip'); + } + } + const files = (await api('/drive/files', {}, alice)).body; + for (const file of files) { + const res = await api('/drive/files/delete', { fileId: file.id }, alice); + if (res.status !== 204) { + throw new Error('Failed to delete file'); + } + } + }); + + test('basic export', async () => { + let res = await api('/clips/create', { + name: 'foo', + description: 'bar', + }, alice); + assert.strictEqual(res.status, 200); + + res = await api('/i/export-clips', {}, alice); + assert.strictEqual(res.status, 204); + + const exported = await pollFirstDriveFile(); + assert.strictEqual(exported[0].name, 'foo'); + assert.strictEqual(exported[0].description, 'bar'); + assert.strictEqual(exported[0].clipNotes.length, 0); + }); + + test('export with notes', async () => { + let res = await api('/clips/create', { + name: 'foo', + description: 'bar', + }, alice); + assert.strictEqual(res.status, 200); + const clip = res.body; + + const note1 = await post(alice, { + text: 'baz1', + }); + + const note2 = await post(alice, { + text: 'baz2', + poll: { + choices: ['sakura', 'izumi', 'ako'], + }, + }); + + for (const note of [note1, note2]) { + res = await api('/clips/add-note', { + clipId: clip.id, + noteId: note.id, + }, alice); + assert.strictEqual(res.status, 204); + } + + res = await api('/i/export-clips', {}, alice); + assert.strictEqual(res.status, 204); + + const exported = await pollFirstDriveFile(); + assert.strictEqual(exported[0].name, 'foo'); + assert.strictEqual(exported[0].description, 'bar'); + assert.strictEqual(exported[0].clipNotes.length, 2); + assert.strictEqual(exported[0].clipNotes[0].note.text, 'baz1'); + assert.strictEqual(exported[0].clipNotes[1].note.text, 'baz2'); + assert.deepStrictEqual(exported[0].clipNotes[1].note.poll.choices[0], 'sakura'); + }); + + test('multiple clips', async () => { + let res = await api('/clips/create', { + name: 'kawaii', + description: 'kawaii', + }, alice); + assert.strictEqual(res.status, 200); + const clip1 = res.body; + + res = await api('/clips/create', { + name: 'yuri', + description: 'yuri', + }, alice); + assert.strictEqual(res.status, 200); + const clip2 = res.body; + + const note1 = await post(alice, { + text: 'baz1', + }); + + const note2 = await post(alice, { + text: 'baz2', + }); + + res = await api('/clips/add-note', { + clipId: clip1.id, + noteId: note1.id, + }, alice); + assert.strictEqual(res.status, 204); + + res = await api('/clips/add-note', { + clipId: clip2.id, + noteId: note2.id, + }, alice); + assert.strictEqual(res.status, 204); + + res = await api('/i/export-clips', {}, alice); + assert.strictEqual(res.status, 204); + + const exported = await pollFirstDriveFile(); + assert.strictEqual(exported[0].name, 'kawaii'); + assert.strictEqual(exported[0].clipNotes.length, 1); + assert.strictEqual(exported[0].clipNotes[0].note.text, 'baz1'); + assert.strictEqual(exported[1].name, 'yuri'); + assert.strictEqual(exported[1].clipNotes.length, 1); + assert.strictEqual(exported[1].clipNotes[0].note.text, 'baz2'); + }); + + test('Clipping other user\'s note', async () => { + let res = await api('/clips/create', { + name: 'kawaii', + description: 'kawaii', + }, alice); + assert.strictEqual(res.status, 200); + const clip = res.body; + + const note = await post(bob, { + text: 'baz', + visibility: 'followers', + }); + + res = await api('/clips/add-note', { + clipId: clip.id, + noteId: note.id, + }, alice); + assert.strictEqual(res.status, 204); + + res = await api('/i/export-clips', {}, alice); + assert.strictEqual(res.status, 204); + + const exported = await pollFirstDriveFile(); + assert.strictEqual(exported[0].name, 'kawaii'); + assert.strictEqual(exported[0].clipNotes.length, 1); + assert.strictEqual(exported[0].clipNotes[0].note.text, 'baz'); + assert.strictEqual(exported[0].clipNotes[0].note.user.username, 'bob'); + }); +}); diff --git a/packages/backend/test/e2e/fetch-resource.ts b/packages/backend/test/e2e/fetch-resource.ts index 251d662760eeb440e3319d35ccc38d07d7f96ee0..74033b7dfff2c52963dcb4d76e009b9031a96058 100644 --- a/packages/backend/test/e2e/fetch-resource.ts +++ b/packages/backend/test/e2e/fetch-resource.ts @@ -1,14 +1,13 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { startServer, channel, clip, cookie, galleryPost, signup, page, play, post, simpleGet, uploadFile } from '../utils.js'; +import { channel, clip, cookie, galleryPost, page, play, post, signup, simpleGet, uploadFile } from '../utils.js'; import type { SimpleGetResponse } from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; import type * as misskey from 'misskey-js'; // Request Accept @@ -23,9 +22,7 @@ const HTML = 'text/html; charset=utf-8'; const JSON_UTF8 = 'application/json; charset=utf-8'; describe('Webリソース', () => { - let app: INestApplicationContext; - - let alice: misskey.entities.MeSignup; + let alice: misskey.entities.SignupResponse; let aliceUploadedFile: any; let alicesPost: any; let alicePage: any; @@ -34,7 +31,7 @@ describe('Webリソース', () => { let aliceGalleryPost: any; let aliceChannel: any; - let bob: misskey.entities.MeSignup; + let bob: misskey.entities.SignupResponse; type Request = { path: string, @@ -79,7 +76,6 @@ describe('Webリソース', () => { }; beforeAll(async () => { - app = await startServer(); alice = await signup({ username: 'alice' }); aliceUploadedFile = await uploadFile(alice); alicesPost = await post(alice, { @@ -96,10 +92,6 @@ describe('Webリソース', () => { bob = await signup({ username: 'bob' }); }, 1000 * 60 * 2); - afterAll(async () => { - await app.close(); - }); - describe.each([ { path: '/', type: HTML }, { path: '/docs/ja-JP/about', type: HTML }, // "指定ã•ã‚ŒãŸURLã«è©²å½“ã™ã‚‹ãƒšãƒ¼ã‚¸ã¯ã‚ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚" diff --git a/packages/backend/test/e2e/ff-visibility.ts b/packages/backend/test/e2e/ff-visibility.ts index 1fbd45c741cc7f1290bce755c41e5e403382c92e..b59dd8824ab1ccf1ae84c193d444a14e1e742670 100644 --- a/packages/backend/test/e2e/ff-visibility.ts +++ b/packages/backend/test/e2e/ff-visibility.ts @@ -1,31 +1,23 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { signup, api, startServer, simpleGet } from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; +import { api, signup, simpleGet } from '../utils.js'; import type * as misskey from 'misskey-js'; describe('FF visibility', () => { - let app: INestApplicationContext; - - let alice: misskey.entities.MeSignup; - let bob: misskey.entities.MeSignup; + let alice: misskey.entities.SignupResponse; + let bob: misskey.entities.SignupResponse; beforeAll(async () => { - app = await startServer(); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); }, 1000 * 60 * 2); - afterAll(async () => { - await app.close(); - }); - test('followingVisibility, followersVisibility ãŒã¨ã‚‚ã« public ãªãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ•ã‚©ãƒãƒ¼/フォãƒãƒ¯ãƒ¼ã‚’誰ã§ã‚‚見れる', async () => { await api('/i/update', { followingVisibility: 'public', diff --git a/packages/backend/test/e2e/move.ts b/packages/backend/test/e2e/move.ts index b009ef124a1b4831373a7ef3cf7bb113a6a79e44..f6417e39b5ec0079eef7cd956b0711e1af57f808 100644 --- a/packages/backend/test/e2e/move.ts +++ b/packages/backend/test/e2e/move.ts @@ -1,37 +1,37 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ +import { INestApplicationContext } from '@nestjs/common'; + process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import { loadConfig } from '@/config.js'; import { MiUser, UsersRepository } from '@/models/_.js'; -import { jobQueue } from '@/boot/common.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; -import { uploadFile, signup, startServer, initTestDb, api, sleep, successfulApiCall } from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; +import { jobQueue } from '@/boot/common.js'; +import { api, initTestDb, signup, sleep, successfulApiCall, uploadFile } from '../utils.js'; import type * as misskey from 'misskey-js'; describe('Account Move', () => { - let app: INestApplicationContext; let jq: INestApplicationContext; let url: URL; let root: any; - let alice: misskey.entities.MeSignup; - let bob: misskey.entities.MeSignup; - let carol: misskey.entities.MeSignup; - let dave: misskey.entities.MeSignup; - let eve: misskey.entities.MeSignup; - let frank: misskey.entities.MeSignup; + let alice: misskey.entities.SignupResponse; + let bob: misskey.entities.SignupResponse; + let carol: misskey.entities.SignupResponse; + let dave: misskey.entities.SignupResponse; + let eve: misskey.entities.SignupResponse; + let frank: misskey.entities.SignupResponse; let Users: UsersRepository; beforeAll(async () => { - app = await startServer(); jq = await jobQueue(); + const config = loadConfig(); url = new URL(config.url); const connection = await initTestDb(false); @@ -46,7 +46,7 @@ describe('Account Move', () => { }, 1000 * 60 * 2); afterAll(async () => { - await Promise.all([app.close(), jq.close()]); + await jq.close(); }); describe('Create Alias', () => { diff --git a/packages/backend/test/e2e/mute.ts b/packages/backend/test/e2e/mute.ts index a4b57a1eba6ef1c7983bbcc4c51f10d88ea7d984..1d28e07b7d72ebd9be30b08258e7243586e08c18 100644 --- a/packages/backend/test/e2e/mute.ts +++ b/packages/backend/test/e2e/mute.ts @@ -1,34 +1,26 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { signup, api, post, react, startServer, waitFire } from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; +import { api, post, react, signup, waitFire } from '../utils.js'; import type * as misskey from 'misskey-js'; describe('Mute', () => { - let app: INestApplicationContext; - // alice mutes carol - let alice: misskey.entities.MeSignup; - let bob: misskey.entities.MeSignup; - let carol: misskey.entities.MeSignup; + let alice: misskey.entities.SignupResponse; + let bob: misskey.entities.SignupResponse; + let carol: misskey.entities.SignupResponse; beforeAll(async () => { - app = await startServer(); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); carol = await signup({ username: 'carol' }); }, 1000 * 60 * 2); - afterAll(async () => { - await app.close(); - }); - test('ミュート作æˆ', async () => { const res = await api('/mute/create', { userId: carol.id, @@ -125,5 +117,185 @@ describe('Mute', () => { assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒªãƒ—ライãŒå«ã¾ã‚Œãªã„', async () => { + const aliceNote = await post(alice, { text: 'hi' }); + await post(bob, { text: '@alice hi', replyId: aliceNote.id }); + await post(carol, { text: '@alice hi', replyId: aliceNote.id }); + + const res = await api('/i/notifications', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒªãƒ—ライãŒå«ã¾ã‚Œãªã„', async () => { + await post(alice, { text: 'hi' }); + await post(bob, { text: '@alice hi' }); + await post(carol, { text: '@alice hi' }); + + const res = await api('/i/notifications', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®å¼•ç”¨ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => { + const aliceNote = await post(alice, { text: 'hi' }); + await post(bob, { text: 'hi', renoteId: aliceNote.id }); + await post(carol, { text: 'hi', renoteId: aliceNote.id }); + + const res = await api('/i/notifications', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => { + const aliceNote = await post(alice, { text: 'hi' }); + await post(bob, { renoteId: aliceNote.id }); + await post(carol, { renoteId: aliceNote.id }); + + const res = await api('/i/notifications', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒ¼é€šçŸ¥ãŒå«ã¾ã‚Œãªã„', async () => { + await api('/i/follow', { userId: alice.id }, bob); + await api('/i/follow', { userId: alice.id }, carol); + + const res = await api('/i/notifications', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒå«ã¾ã‚Œãªã„', async () => { + await api('/i/update/', { isLocked: true }, alice); + await api('/following/create', { userId: alice.id }, bob); + await api('/following/create', { userId: alice.id }, carol); + + const res = await api('/i/notifications', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + }); + + describe('Notification (Grouped)', () => { + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã®é€šçŸ¥ãŒå«ã¾ã‚Œãªã„(リアクション)', async () => { + const aliceNote = await post(alice, { text: 'hi' }); + await react(bob, aliceNote, 'like'); + await react(carol, aliceNote, 'like'); + + const res = await api('/i/notifications-grouped', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒªãƒ—ライãŒå«ã¾ã‚Œãªã„', async () => { + const aliceNote = await post(alice, { text: 'hi' }); + await post(bob, { text: '@alice hi', replyId: aliceNote.id }); + await post(carol, { text: '@alice hi', replyId: aliceNote.id }); + + const res = await api('/i/notifications-grouped', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒªãƒ—ライãŒå«ã¾ã‚Œãªã„', async () => { + await post(alice, { text: 'hi' }); + await post(bob, { text: '@alice hi' }); + await post(carol, { text: '@alice hi' }); + + const res = await api('/i/notifications-grouped', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®å¼•ç”¨ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => { + const aliceNote = await post(alice, { text: 'hi' }); + await post(bob, { text: 'hi', renoteId: aliceNote.id }); + await post(carol, { text: 'hi', renoteId: aliceNote.id }); + + const res = await api('/i/notifications-grouped', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„', async () => { + const aliceNote = await post(alice, { text: 'hi' }); + await post(bob, { renoteId: aliceNote.id }); + await post(carol, { renoteId: aliceNote.id }); + + const res = await api('/i/notifications-grouped', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒ¼é€šçŸ¥ãŒå«ã¾ã‚Œãªã„', async () => { + await api('/i/follow', { userId: alice.id }, bob); + await api('/i/follow', { userId: alice.id }, carol); + + const res = await api('/i/notifications-grouped', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); + + test('通知ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るユーザーã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒå«ã¾ã‚Œãªã„', async () => { + await api('/i/update/', { isLocked: true }, alice); + await api('/following/create', { userId: alice.id }, bob); + await api('/following/create', { userId: alice.id }, carol); + + const res = await api('/i/notifications-grouped', {}, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + + assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); + assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + }); }); }); diff --git a/packages/backend/test/e2e/nodeinfo.ts b/packages/backend/test/e2e/nodeinfo.ts index 7eed39c5ed95e560e3f558f24fd088452e796875..28b96fe8c89172fb304cf82675e89decaccf6341 100644 --- a/packages/backend/test/e2e/nodeinfo.ts +++ b/packages/backend/test/e2e/nodeinfo.ts @@ -1,25 +1,14 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { relativeFetch, startServer } from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; +import { relativeFetch } from '../utils.js'; describe('nodeinfo', () => { - let app: INestApplicationContext; - - beforeAll(async () => { - app = await startServer(); - }, 1000 * 60 * 2); - - afterAll(async () => { - await app.close(); - }); - test('nodeinfo 2.1', async () => { const res = await relativeFetch('nodeinfo/2.1'); assert.ok(res.ok); diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts index 961df99cc27f75d25f8bfbed8428f50f541d3847..2406204f4101208fa8819469375cc6bb57db7550 100644 --- a/packages/backend/test/e2e/note.ts +++ b/packages/backend/test/e2e/note.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -8,29 +8,24 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import { MiNote } from '@/models/Note.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; -import { signup, post, uploadUrl, startServer, initTestDb, api, uploadFile } from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; +import { api, initTestDb, post, signup, uploadFile, uploadUrl } from '../utils.js'; import type * as misskey from 'misskey-js'; describe('Note', () => { - let app: INestApplicationContext; let Notes: any; - let alice: misskey.entities.MeSignup; - let bob: misskey.entities.MeSignup; + let alice: misskey.entities.SignupResponse; + let bob: misskey.entities.SignupResponse; + let tom: misskey.entities.SignupResponse; beforeAll(async () => { - app = await startServer(); const connection = await initTestDb(true); Notes = connection.getRepository(MiNote); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); + tom = await signup({ username: 'tom', host: 'example.com' }); }, 1000 * 60 * 2); - afterAll(async () => { - await app.close(); - }); - test('投稿ã§ãã‚‹', async () => { const post = { text: 'test', @@ -143,6 +138,19 @@ describe('Note', () => { assert.strictEqual(res.body.createdNote.renote.text, bobPost.text); }); + test('引用renoteã§ç©ºç™½æ–‡å—ã®ã¿ã§æ§‹æˆã•ã‚ŒãŸtextã«ã™ã‚‹ã¨ãƒ¬ã‚¹ãƒãƒ³ã‚¹ãŒtext: nullã«ãªã‚‹', async () => { + const bobPost = await post(bob, { + text: 'test', + }); + const res = await api('/notes/create', { + text: ' ', + renoteId: bobPost.id, + }, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(res.body.createdNote.text, null); + }); + test('visibility: followersã§renoteã§ãã‚‹', async () => { const createRes = await api('/notes/create', { text: 'test', @@ -168,6 +176,87 @@ describe('Note', () => { assert.strictEqual(deleteRes.status, 204); }); + test('visibility: followersãªãƒŽãƒ¼ãƒˆã«å¯¾ã—ã¦ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã¯ãƒªãƒ—ライã§ãã‚‹', async () => { + await api('/following/create', { + userId: alice.id, + }, bob); + + const aliceNote = await api('/notes/create', { + text: 'direct note to bob', + visibility: 'followers', + }, alice); + + assert.strictEqual(aliceNote.status, 200); + + const replyId = aliceNote.body.createdNote.id; + const bobReply = await api('/notes/create', { + text: 'reply to alice note', + replyId, + }, bob); + + assert.strictEqual(bobReply.status, 200); + assert.strictEqual(bobReply.body.createdNote.replyId, replyId); + + await api('/following/delete', { + userId: alice.id, + }, bob); + }); + + test('visibility: followersãªãƒŽãƒ¼ãƒˆã«å¯¾ã—ã¦ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã§ãªã„ユーザーãŒãƒªãƒ—ライã—よã†ã¨ã™ã‚‹ã¨æ€’られる', async () => { + const aliceNote = await api('/notes/create', { + text: 'direct note to bob', + visibility: 'followers', + }, alice); + + assert.strictEqual(aliceNote.status, 200); + + const bobReply = await api('/notes/create', { + text: 'reply to alice note', + replyId: aliceNote.body.createdNote.id, + }, bob); + + assert.strictEqual(bobReply.status, 400); + assert.strictEqual(bobReply.body.error.code, 'CANNOT_REPLY_TO_AN_INVISIBLE_NOTE'); + }); + + test('visibility: specifiedãªãƒŽãƒ¼ãƒˆã«å¯¾ã—ã¦visibility: specifiedã§è¿”ä¿¡ã§ãã‚‹', async () => { + const aliceNote = await api('/notes/create', { + text: 'direct note to bob', + visibility: 'specified', + visibleUserIds: [bob.id], + }, alice); + + assert.strictEqual(aliceNote.status, 200); + + const bobReply = await api('/notes/create', { + text: 'reply to alice note', + replyId: aliceNote.body.createdNote.id, + visibility: 'specified', + visibleUserIds: [alice.id], + }, bob); + + assert.strictEqual(bobReply.status, 200); + }); + + test('visibility: specifiedãªãƒŽãƒ¼ãƒˆã«å¯¾ã—ã¦visibility: follwersã§è¿”ä¿¡ã—よã†ã¨ã™ã‚‹ã¨æ€’られる', async () => { + const aliceNote = await api('/notes/create', { + text: 'direct note to bob', + visibility: 'specified', + visibleUserIds: [bob.id], + }, alice); + + assert.strictEqual(aliceNote.status, 200); + + const bobReply = await api('/notes/create', { + text: 'reply to alice note with visibility: followers', + replyId: aliceNote.body.createdNote.id, + visibility: 'followers', + }, bob); + + assert.strictEqual(bobReply.status, 400); + assert.strictEqual(bobReply.body.error.code, 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY'); + }); + test('æ–‡å—æ•°ãŽã‚ŠãŽã‚Šã§æ€’られãªã„', async () => { const post = { text: '!'.repeat(MAX_NOTE_TEXT_LENGTH), // 3000æ–‡å— @@ -601,6 +690,242 @@ describe('Note', () => { assert.strictEqual(note2.status, 200); assert.strictEqual(note2.body.createdNote.visibility, 'home'); }); + + test('ç¦æ¢ãƒ¯ãƒ¼ãƒ‰ã‚’å«ã‚€æŠ•ç¨¿ã¯ã‚¨ãƒ©ãƒ¼ã«ãªã‚‹ (å˜èªžæŒ‡å®š)', async () => { + const prohibited = await api('admin/update-meta', { + prohibitedWords: [ + 'test', + ], + }, alice); + + assert.strictEqual(prohibited.status, 204); + + await new Promise(x => setTimeout(x, 2)); + + const note1 = await api('/notes/create', { + text: 'hogetesthuge', + }, alice); + + assert.strictEqual(note1.status, 400); + assert.strictEqual(note1.body.error.code, 'CONTAINS_PROHIBITED_WORDS'); + }); + + test('ç¦æ¢ãƒ¯ãƒ¼ãƒ‰ã‚’å«ã‚€æŠ•ç¨¿ã¯ã‚¨ãƒ©ãƒ¼ã«ãªã‚‹ (æ£è¦è¡¨ç¾)', async () => { + const prohibited = await api('admin/update-meta', { + prohibitedWords: [ + '/Test/i', + ], + }, alice); + + assert.strictEqual(prohibited.status, 204); + + const note2 = await api('/notes/create', { + text: 'hogetesthuge', + }, alice); + + assert.strictEqual(note2.status, 400); + assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS'); + }); + + test('ç¦æ¢ãƒ¯ãƒ¼ãƒ‰ã‚’å«ã‚€æŠ•ç¨¿ã¯ã‚¨ãƒ©ãƒ¼ã«ãªã‚‹ (スペースアンド)', async () => { + const prohibited = await api('admin/update-meta', { + prohibitedWords: [ + 'Test hoge', + ], + }, alice); + + assert.strictEqual(prohibited.status, 204); + + const note2 = await api('/notes/create', { + text: 'hogeTesthuge', + }, alice); + + assert.strictEqual(note2.status, 400); + assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS'); + }); + + test('ç¦æ¢ãƒ¯ãƒ¼ãƒ‰ã‚’å«ã‚“ã§ã‚‹ãƒªãƒ¢ãƒ¼ãƒˆãƒŽãƒ¼ãƒˆã‚‚エラーã«ãªã‚‹', async () => { + const prohibited = await api('admin/update-meta', { + prohibitedWords: [ + 'test', + ], + }, alice); + + assert.strictEqual(prohibited.status, 204); + + await new Promise(x => setTimeout(x, 2)); + + const note1 = await api('/notes/create', { + text: 'hogetesthuge', + }, tom); + + assert.strictEqual(note1.status, 400); + }); + + test('メンションã®æ•°ãŒä¸Šé™ã‚’超ãˆã‚‹ã¨ã‚¨ãƒ©ãƒ¼ã«ãªã‚‹', async () => { + const res = await api('admin/roles/create', { + name: 'test', + description: '', + color: null, + iconUrl: null, + displayOrder: 0, + target: 'manual', + condFormula: {}, + isAdministrator: false, + isModerator: false, + isPublic: false, + isExplorable: false, + asBadge: false, + canEditMembersByModerator: false, + policies: { + mentionLimit: { + useDefault: false, + priority: 1, + value: 0, + }, + }, + }, alice); + + assert.strictEqual(res.status, 200); + + await new Promise(x => setTimeout(x, 2)); + + const assign = await api('admin/roles/assign', { + userId: alice.id, + roleId: res.body.id, + }, alice); + + assert.strictEqual(assign.status, 204); + + await new Promise(x => setTimeout(x, 2)); + + const note = await api('/notes/create', { + text: '@bob potentially annoying text', + }, alice); + + assert.strictEqual(note.status, 400); + assert.strictEqual(note.body.error.code, 'CONTAINS_TOO_MANY_MENTIONS'); + + await api('admin/roles/unassign', { + userId: alice.id, + roleId: res.body.id, + }); + + await api('admin/roles/delete', { + roleId: res.body.id, + }, alice); + }); + + test('ダイレクト投稿もエラーã«ãªã‚‹', async () => { + const res = await api('admin/roles/create', { + name: 'test', + description: '', + color: null, + iconUrl: null, + displayOrder: 0, + target: 'manual', + condFormula: {}, + isAdministrator: false, + isModerator: false, + isPublic: false, + isExplorable: false, + asBadge: false, + canEditMembersByModerator: false, + policies: { + mentionLimit: { + useDefault: false, + priority: 1, + value: 0, + }, + }, + }, alice); + + assert.strictEqual(res.status, 200); + + await new Promise(x => setTimeout(x, 2)); + + const assign = await api('admin/roles/assign', { + userId: alice.id, + roleId: res.body.id, + }, alice); + + assert.strictEqual(assign.status, 204); + + await new Promise(x => setTimeout(x, 2)); + + const note = await api('/notes/create', { + text: 'potentially annoying text', + visibility: 'specified', + visibleUserIds: [ bob.id ], + }, alice); + + assert.strictEqual(note.status, 400); + assert.strictEqual(note.body.error.code, 'CONTAINS_TOO_MANY_MENTIONS'); + + await api('admin/roles/unassign', { + userId: alice.id, + roleId: res.body.id, + }); + + await api('admin/roles/delete', { + roleId: res.body.id, + }, alice); + }); + + test('ダイレクトã®å®›å…ˆã¨ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ãŒåŒã˜å ´åˆã¯é‡è¤‡ã—ã¦ã‚«ã‚¦ãƒ³ãƒˆã—ãªã„', async () => { + const res = await api('admin/roles/create', { + name: 'test', + description: '', + color: null, + iconUrl: null, + displayOrder: 0, + target: 'manual', + condFormula: {}, + isAdministrator: false, + isModerator: false, + isPublic: false, + isExplorable: false, + asBadge: false, + canEditMembersByModerator: false, + policies: { + mentionLimit: { + useDefault: false, + priority: 1, + value: 1, + }, + }, + }, alice); + + assert.strictEqual(res.status, 200); + + await new Promise(x => setTimeout(x, 2)); + + const assign = await api('admin/roles/assign', { + userId: alice.id, + roleId: res.body.id, + }, alice); + + assert.strictEqual(assign.status, 204); + + await new Promise(x => setTimeout(x, 2)); + + const note = await api('/notes/create', { + text: '@bob potentially annoying text', + visibility: 'specified', + visibleUserIds: [ bob.id ], + }, alice); + + assert.strictEqual(note.status, 200); + + await api('admin/roles/unassign', { + userId: alice.id, + roleId: res.body.id, + }); + + await api('admin/roles/delete', { + roleId: res.body.id, + }, alice); + }); }); describe('notes/delete', () => { diff --git a/packages/backend/test/e2e/oauth.ts b/packages/backend/test/e2e/oauth.ts index 3a5e4ebdae050a376aee5c16c3dcecc1ee771c7b..ef7a6a579d807ef25f9272605b770423b937d24b 100644 --- a/packages/backend/test/e2e/oauth.ts +++ b/packages/backend/test/e2e/oauth.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -11,13 +11,18 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { AuthorizationCode, ResourceOwnerPassword, type AuthorizationTokenConfig, ClientCredentials, ModuleOptions } from 'simple-oauth2'; +import { + AuthorizationCode, + type AuthorizationTokenConfig, + ClientCredentials, + ModuleOptions, + ResourceOwnerPassword, +} from 'simple-oauth2'; import pkceChallenge from 'pkce-challenge'; import { JSDOM } from 'jsdom'; -import Fastify, { type FastifyReply, type FastifyInstance } from 'fastify'; -import { api, port, signup, startServer } from '../utils.js'; +import Fastify, { type FastifyInstance, type FastifyReply } from 'fastify'; +import { api, port, sendEnvUpdateRequest, signup } from '../utils.js'; import type * as misskey from 'misskey-js'; -import type { INestApplicationContext } from '@nestjs/common'; const host = `http://127.0.0.1:${port}`; @@ -75,7 +80,7 @@ function getMeta(html: string): { transactionId: string | undefined, clientName: }; } -function fetchDecision(transactionId: string, user: misskey.entities.MeSignup, { cancel }: { cancel?: boolean } = {}): Promise<Response> { +function fetchDecision(transactionId: string, user: misskey.entities.SignupResponse, { cancel }: { cancel?: boolean } = {}): Promise<Response> { return fetch(new URL('/oauth/decision', host), { method: 'post', body: new URLSearchParams({ @@ -90,14 +95,14 @@ function fetchDecision(transactionId: string, user: misskey.entities.MeSignup, { }); } -async function fetchDecisionFromResponse(response: Response, user: misskey.entities.MeSignup, { cancel }: { cancel?: boolean } = {}): Promise<Response> { +async function fetchDecisionFromResponse(response: Response, user: misskey.entities.SignupResponse, { cancel }: { cancel?: boolean } = {}): Promise<Response> { const { transactionId } = getMeta(await response.text()); assert.ok(transactionId); return await fetchDecision(transactionId, user, { cancel }); } -async function fetchAuthorizationCode(user: misskey.entities.MeSignup, scope: string, code_challenge: string): Promise<{ client: AuthorizationCode, code: string }> { +async function fetchAuthorizationCode(user: misskey.entities.SignupResponse, scope: string, code_challenge: string): Promise<{ client: AuthorizationCode, code: string }> { const client = new AuthorizationCode(clientConfig); const response = await fetch(client.authorizeURL({ @@ -147,16 +152,14 @@ async function assertDirectError(response: Response, status: number, error: stri } describe('OAuth', () => { - let app: INestApplicationContext; let fastify: FastifyInstance; - let alice: misskey.entities.MeSignup; - let bob: misskey.entities.MeSignup; + let alice: misskey.entities.SignupResponse; + let bob: misskey.entities.SignupResponse; let sender: (reply: FastifyReply) => void; beforeAll(async () => { - app = await startServer(); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); @@ -168,7 +171,7 @@ describe('OAuth', () => { }, 1000 * 60 * 2); beforeEach(async () => { - process.env.MISSKEY_TEST_CHECK_IP_RANGE = ''; + await sendEnvUpdateRequest({ key: 'MISSKEY_TEST_CHECK_IP_RANGE', value: '' }); sender = (reply): void => { reply.send(` <!DOCTYPE html> @@ -180,7 +183,6 @@ describe('OAuth', () => { afterAll(async () => { await fastify.close(); - await app.close(); }); test('Full flow', async () => { @@ -881,7 +883,7 @@ describe('OAuth', () => { }); test('Disallow loopback', async () => { - process.env.MISSKEY_TEST_CHECK_IP_RANGE = '1'; + await sendEnvUpdateRequest({ key: 'MISSKEY_TEST_CHECK_IP_RANGE', value: '1' }); const client = new AuthorizationCode(clientConfig); const response = await fetch(client.authorizeURL({ diff --git a/packages/backend/test/e2e/renote-mute.ts b/packages/backend/test/e2e/renote-mute.ts index 7d57ba17b6fbd386f8ab6f4691af36b202638e39..403de0cb8d81d3bb8b3c1b6a69daacbe92185b8b 100644 --- a/packages/backend/test/e2e/renote-mute.ts +++ b/packages/backend/test/e2e/renote-mute.ts @@ -1,34 +1,26 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { signup, api, post, react, startServer, waitFire, sleep } from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; +import { api, post, signup, sleep, waitFire } from '../utils.js'; import type * as misskey from 'misskey-js'; describe('Renote Mute', () => { - let app: INestApplicationContext; - // alice mutes carol - let alice: misskey.entities.MeSignup; - let bob: misskey.entities.MeSignup; - let carol: misskey.entities.MeSignup; + let alice: misskey.entities.SignupResponse; + let bob: misskey.entities.SignupResponse; + let carol: misskey.entities.SignupResponse; beforeAll(async () => { - app = await startServer(); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); carol = await signup({ username: 'carol' }); }, 1000 * 60 * 2); - afterAll(async () => { - await app.close(); - }); - test('ミュート作æˆ', async () => { const res = await api('/renote-mute/create', { userId: carol.id, diff --git a/packages/backend/test/e2e/streaming.ts b/packages/backend/test/e2e/streaming.ts index 288c54bdbc91c39c77733c383fac401f2f90a778..57ce73ba603b376ca1edc481e5df6d98326c007b 100644 --- a/packages/backend/test/e2e/streaming.ts +++ b/packages/backend/test/e2e/streaming.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -8,12 +8,10 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import { WebSocket } from 'ws'; import { MiFollowing } from '@/models/Following.js'; -import { signup, api, post, startServer, initTestDb, waitFire, createAppToken, port } from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; +import { api, createAppToken, initTestDb, port, post, signup, waitFire } from '../utils.js'; import type * as misskey from 'misskey-js'; describe('Streaming', () => { - let app: INestApplicationContext; let Followings: any; const follow = async (follower: any, followee: any) => { @@ -32,23 +30,22 @@ describe('Streaming', () => { describe('Streaming', () => { // Local users - let ayano: misskey.entities.MeSignup; - let kyoko: misskey.entities.MeSignup; - let chitose: misskey.entities.MeSignup; - let kanako: misskey.entities.MeSignup; + let ayano: misskey.entities.SignupResponse; + let kyoko: misskey.entities.SignupResponse; + let chitose: misskey.entities.SignupResponse; + let kanako: misskey.entities.SignupResponse; // Remote users - let akari: misskey.entities.MeSignup; - let chinatsu: misskey.entities.MeSignup; - let takumi: misskey.entities.MeSignup; + let akari: misskey.entities.SignupResponse; + let chinatsu: misskey.entities.SignupResponse; + let takumi: misskey.entities.SignupResponse; - let kyokoNote: any; - let kanakoNote: any; - let takumiNote: any; + let kyokoNote: misskey.entities.Note; + let kanakoNote: misskey.entities.Note; + let takumiNote: misskey.entities.Note; let list: any; beforeAll(async () => { - app = await startServer(); const connection = await initTestDb(true); Followings = connection.getRepository(MiFollowing); @@ -71,6 +68,9 @@ describe('Streaming', () => { // Follow: ayano => akari await follow(ayano, akari); + // Follow: kyoko => chitose + await api('following/create', { userId: chitose.id }, kyoko); + // Mute: chitose => kanako await api('mute/create', { userId: kanako.id }, chitose); @@ -95,10 +95,6 @@ describe('Streaming', () => { }, chitose); }, 1000 * 60 * 2); - afterAll(async () => { - await app.close(); - }); - describe('Events', () => { test('mention event', async () => { const fired = await waitFire( @@ -177,7 +173,28 @@ describe('Streaming', () => { */ test('フォãƒãƒ¼ã—ã¦ã„るユーザーã®ãƒ•ã‚©ãƒãƒ¼ã—ã¦ã„ãªã„ユーザー㮠visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ãŒæµã‚Œãªã„', async () => { - // TODO + const chitoseNote = await post(chitose, { text: 'followers-only post', visibility: 'followers' }); + + const fired = await waitFire( + ayano, 'homeTimeline', // ayano:home + () => api('notes/create', { text: 'reply to chitose\'s followers-only post', replyId: chitoseNote.id }, kyoko), // kyoko's reply to chitose's followers-only post + msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko + ); + + assert.strictEqual(fired, false); + }); + + test('フォãƒãƒ¼ã—ã¦ã„るユーザーã®ãƒ•ã‚©ãƒãƒ¼ã—ã¦ã„ãªã„ユーザー㮠visibility: followers ãªæŠ•ç¨¿ã¸ã®è¿”ä¿¡ã®ãƒªãƒŽãƒ¼ãƒˆãŒæµã‚Œãªã„', async () => { + const chitoseNote = await post(chitose, { text: 'followers-only post', visibility: 'followers' }); + const kyokoReply = await post(kyoko, { text: 'reply to followers-only post', replyId: chitoseNote.id }); + + const fired = await waitFire( + ayano, 'homeTimeline', // ayano:home + () => api('notes/create', { renoteId: kyokoReply.id }, kyoko), // kyoko's renote of kyoko's reply to chitose's followers-only post + msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko + ); + + assert.strictEqual(fired, false); }); test('フォãƒãƒ¼ã—ã¦ã„ãªã„ユーザーã®æŠ•ç¨¿ã¯æµã‚Œãªã„', async () => { @@ -209,6 +226,79 @@ describe('Streaming', () => { assert.strictEqual(fired, false); }); + + /** + * TODO: è½ã¡ã‚‹ + * @see https://github.com/misskey-dev/misskey/issues/13474 + test('visibility: specified ãªãƒŽãƒ¼ãƒˆã§ visibleUserIds ã«è‡ªåˆ†ãŒå«ã¾ã‚Œã¦ã„ã‚‹ã¨ããã®ãƒŽãƒ¼ãƒˆã¸ã®ãƒªãƒ—ライãŒæµã‚Œã¦ãã‚‹', async () => { + const chitoseToKyokoAndAyano = await post(chitose, { text: 'direct note from chitose to kyoko and ayano', visibility: 'specified', visibleUserIds: [kyoko.id, ayano.id] }); + + const fired = await waitFire( + ayano, 'homeTimeline', // ayano:home + () => api('notes/create', { text: 'direct reply from kyoko to chitose and ayano', replyId: chitoseToKyokoAndAyano.id, visibility: 'specified', visibleUserIds: [chitose.id, ayano.id] }, kyoko), + msg => msg.type === 'note' && msg.body.userId === kyoko.id, + ); + + assert.strictEqual(fired, true); + }); + */ + + test('visibility: specified ãªæŠ•ç¨¿ã«å¯¾ã™ã‚‹ãƒªãƒ—ライ㧠visibleUserIds ãŒæ‹¡å¼µã•ã‚ŒãŸã¨ãã€ãã®æ‹¡å¼µã•ã‚ŒãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã® HTL ã«ã¯ãã®ãƒªãƒ—ライãŒæµã‚Œãªã„', async () => { + const chitoseToKyoko = await post(chitose, { text: 'direct note from chitose to kyoko', visibility: 'specified', visibleUserIds: [kyoko.id] }); + + const fired = await waitFire( + ayano, 'homeTimeline', // ayano:home + () => api('notes/create', { text: 'direct reply from kyoko to chitose and ayano', replyId: chitoseToKyoko.id, visibility: 'specified', visibleUserIds: [chitose.id, ayano.id] }, kyoko), + msg => msg.type === 'note' && msg.body.userId === kyoko.id, + ); + + assert.strictEqual(fired, false); + }); + + test('visibility: specified ãªæŠ•ç¨¿ã«å¯¾ã™ã‚‹ãƒªãƒ—ライ㧠visibleUserIds ãŒåŽç¸®ã•ã‚ŒãŸã¨ãã€ãã®åŽç¸®ã•ã‚ŒãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã® HTL ã«ã¯ãã®ãƒªãƒ—ライãŒæµã‚Œãªã„', async () => { + const chitoseToKyokoAndAyano = await post(chitose, { text: 'direct note from chitose to kyoko and ayano', visibility: 'specified', visibleUserIds: [kyoko.id, ayano.id] }); + + const fired = await waitFire( + ayano, 'homeTimeline', // ayano:home + () => api('notes/create', { text: 'direct reply from kyoko to chitose', replyId: chitoseToKyokoAndAyano.id, visibility: 'specified', visibleUserIds: [chitose.id] }, kyoko), + msg => msg.type === 'note' && msg.body.userId === kyoko.id, + ); + + assert.strictEqual(fired, false); + }); + + test('withRenotes: false ã®ã¨ãリノートãŒæµã‚Œãªã„', async () => { + const fired = await waitFire( + ayano, 'homeTimeline', // ayano:home + () => api('notes/create', { renoteId: kyokoNote.id }, kyoko), // kyoko renote + msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko + { withRenotes: false }, + ); + + assert.strictEqual(fired, false); + }); + + test('withRenotes: false ã®ã¨ã引用リノートãŒæµã‚Œã‚‹', async () => { + const fired = await waitFire( + ayano, 'homeTimeline', // ayano:home + () => api('notes/create', { text: 'quote', renoteId: kyokoNote.id }, kyoko), // kyoko quote + msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko + { withRenotes: false }, + ); + + assert.strictEqual(fired, true); + }); + + test('withRenotes: false ã®ã¨ã投票ã®ã¿ã®ãƒªãƒŽãƒ¼ãƒˆãŒæµã‚Œã‚‹', async () => { + const fired = await waitFire( + ayano, 'homeTimeline', // ayano:home + () => api('notes/create', { poll: { choices: ['kinoko', 'takenoko'] }, renoteId: kyokoNote.id }, kyoko), // kyoko renote with poll + msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko + { withRenotes: false }, + ); + + assert.strictEqual(fired, true); + }); }); // Home describe('Local Timeline', () => { diff --git a/packages/backend/test/e2e/thread-mute.ts b/packages/backend/test/e2e/thread-mute.ts index 0e487976dcaa383ba9992fe0d7e4d39bfb758e63..b4570cdef15cc9e5f233e934ba57e7681ac42af7 100644 --- a/packages/backend/test/e2e/thread-mute.ts +++ b/packages/backend/test/e2e/thread-mute.ts @@ -1,33 +1,25 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { signup, api, post, connectStream, startServer } from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; +import { api, connectStream, post, signup } from '../utils.js'; import type * as misskey from 'misskey-js'; describe('Note thread mute', () => { - let app: INestApplicationContext; - - let alice: misskey.entities.MeSignup; - let bob: misskey.entities.MeSignup; - let carol: misskey.entities.MeSignup; + let alice: misskey.entities.SignupResponse; + let bob: misskey.entities.SignupResponse; + let carol: misskey.entities.SignupResponse; beforeAll(async () => { - app = await startServer(); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); carol = await signup({ username: 'carol' }); }, 1000 * 60 * 2); - afterAll(async () => { - await app.close(); - }); - test('notes/mentions ã«ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るスレッドã®æŠ•ç¨¿ãŒå«ã¾ã‚Œãªã„', async () => { const bobNote = await post(bob, { text: '@alice @carol root note' }); const aliceReply = await post(alice, { replyId: bobNote.id, text: '@bob @carol child note' }); diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index cb9558b416e84168e8d63fa413511dd97c43dd2f..0e71d707dd130d056870e3fd64e7eb95acd7a77d 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -1,17 +1,13 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ // How to run: // pnpm jest -- e2e/timelines.ts -process.env.NODE_ENV = 'test'; -process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING = 'true'; - import * as assert from 'assert'; -import { api, post, randomString, signup, sleep, startServer, uploadUrl } from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; +import { api, post, randomString, sendEnvUpdateRequest, signup, sleep, uploadUrl } from '../utils.js'; function genHost() { return randomString() + '.example.com'; @@ -21,16 +17,6 @@ function waitForPushToTl() { return sleep(500); } -let app: INestApplicationContext; - -beforeAll(async () => { - app = await startServer(); -}, 1000 * 60 * 2); - -afterAll(async () => { - await app.close(); -}); - describe('Timelines', () => { describe('Home TL', () => { test.concurrent('自分㮠visibility: followers ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => { @@ -334,8 +320,9 @@ describe('Timelines', () => { test.concurrent('フォãƒãƒ¼ã—ã¦ã„るリモートユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => { const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); + await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); await api('/following/create', { userId: bob.id }, alice); - await sleep(1000); + const bobNote = await post(bob, { text: 'hi' }); await waitForPushToTl(); @@ -348,8 +335,9 @@ describe('Timelines', () => { test.concurrent('フォãƒãƒ¼ã—ã¦ã„るリモートユーザー㮠visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => { const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); + await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); await api('/following/create', { userId: bob.id }, alice); - await sleep(1000); + const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); await waitForPushToTl(); @@ -762,8 +750,9 @@ describe('Timelines', () => { test.concurrent('フォãƒãƒ¼ã—ã¦ã„るリモートユーザーã®ãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => { const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); + await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); await api('/following/create', { userId: bob.id }, alice); - await sleep(1000); + const bobNote = await post(bob, { text: 'hi' }); await waitForPushToTl(); @@ -776,8 +765,9 @@ describe('Timelines', () => { test.concurrent('フォãƒãƒ¼ã—ã¦ã„るリモートユーザー㮠visibility: home ãªãƒŽãƒ¼ãƒˆãŒå«ã¾ã‚Œã‚‹', async () => { const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); + await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); await api('/following/create', { userId: bob.id }, alice); - await sleep(1000); + const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); await waitForPushToTl(); diff --git a/packages/backend/test/e2e/user-notes.ts b/packages/backend/test/e2e/user-notes.ts index b5f00a632752acf3ddd082756e3fff182eccabe4..6897cf08c62608698877d0af337effe47cc7a51e 100644 --- a/packages/backend/test/e2e/user-notes.ts +++ b/packages/backend/test/e2e/user-notes.ts @@ -1,25 +1,21 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { signup, api, post, uploadUrl, startServer } from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; +import { api, post, signup, uploadUrl } from '../utils.js'; import type * as misskey from 'misskey-js'; describe('users/notes', () => { - let app: INestApplicationContext; - - let alice: misskey.entities.MeSignup; + let alice: misskey.entities.SignupResponse; let jpgNote: any; let pngNote: any; let jpgPngNote: any; beforeAll(async () => { - app = await startServer(); alice = await signup({ username: 'alice' }); const jpg = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'); const png = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.png'); @@ -34,10 +30,6 @@ describe('users/notes', () => { }); }, 1000 * 60 * 2); - afterAll(async() => { - await app.close(); - }); - test('withFiles', async () => { const res = await api('/users/notes', { userId: alice.id, diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts index be6f0ec855bfe7afba348c7cbd9792846e288617..19b905dd472350da9562de9a56b661cf1781ad37 100644 --- a/packages/backend/test/e2e/users.ts +++ b/packages/backend/test/e2e/users.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -8,20 +8,8 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import { inspect } from 'node:util'; import { DEFAULT_POLICIES } from '@/core/RoleService.js'; -import type { Packed } from '@/misc/json-schema.js'; -import { - signup, - post, - page, - role, - startServer, - api, - successfulApiCall, - failedApiCall, - uploadFile, -} from '../utils.js'; +import { api, page, post, role, signup, successfulApiCall, uploadFile } from '../utils.js'; import type * as misskey from 'misskey-js'; -import type { INestApplicationContext } from '@nestjs/common'; describe('ユーザー', () => { // エンティティã¨ã—ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’主眼ã«ãŠã„ãŸãƒ†ã‚¹ãƒˆã‚’記述ã™ã‚‹ @@ -188,8 +176,6 @@ describe('ユーザー', () => { }); }; - let app: INestApplicationContext; - let root: User; let alice: User; let aliceNote: misskey.entities.Note; @@ -233,10 +219,6 @@ describe('ユーザー', () => { let userFollowRequesting: User; let userFollowRequested: User; - beforeAll(async () => { - app = await startServer(); - }, 1000 * 60 * 2); - beforeAll(async () => { root = await signup({ username: 'root' }); alice = await signup({ username: 'alice' }); @@ -324,10 +306,6 @@ describe('ユーザー', () => { await api('following/create', { userId: userFollowRequested.id }, userFollowRequesting); }, 1000 * 60 * 10); - afterAll(async () => { - await app.close(); - }); - beforeEach(async () => { alice = { ...alice, diff --git a/packages/backend/test/e2e/well-known.ts b/packages/backend/test/e2e/well-known.ts index 14e32e16271c41308a8af605dddb56eabed0bf90..bdb298dfe44fd49d056f4760b909f908869ed2be 100644 --- a/packages/backend/test/e2e/well-known.ts +++ b/packages/backend/test/e2e/well-known.ts @@ -1,29 +1,21 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { host, origin, relativeFetch, signup, startServer } from '../utils.js'; -import type { INestApplicationContext } from '@nestjs/common'; +import { host, origin, relativeFetch, signup } from '../utils.js'; import type * as misskey from 'misskey-js'; describe('.well-known', () => { - let app: INestApplicationContext; let alice: misskey.entities.User; beforeAll(async () => { - app = await startServer(); - alice = await signup({ username: 'alice' }); }, 1000 * 60 * 2); - afterAll(async () => { - await app.close(); - }); - test('nodeinfo', async () => { const res = await relativeFetch('.well-known/nodeinfo'); assert.ok(res.ok); diff --git a/packages/backend/test/jest.setup.ts b/packages/backend/test/jest.setup.ts new file mode 100644 index 0000000000000000000000000000000000000000..cf5b9bf24ddb5cf65b3068cc55cd8d5f2ff5adbc --- /dev/null +++ b/packages/backend/test/jest.setup.ts @@ -0,0 +1,8 @@ +import { initTestDb, sendEnvResetRequest } from './utils.js'; + +beforeAll(async () => { + await Promise.all([ + initTestDb(false), + sendEnvResetRequest(), + ]); +}); diff --git a/packages/backend/test/misc/mock-resolver.ts b/packages/backend/test/misc/mock-resolver.ts index 7cba7a2aa8183a9958f932d7fbce8b81fbc9bc1a..3c7e796700da32b0b6846941005f60a7d7ebdbe7 100644 --- a/packages/backend/test/misc/mock-resolver.ts +++ b/packages/backend/test/misc/mock-resolver.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -15,7 +15,13 @@ import type { LoggerService } from '@/core/LoggerService.js'; import type { MetaService } from '@/core/MetaService.js'; import type { UtilityService } from '@/core/UtilityService.js'; import { bindThis } from '@/decorators.js'; -import type { NoteReactionsRepository, NotesRepository, PollsRepository, UsersRepository, FollowRequestsRepository } from '@/models/_.js'; +import type { + FollowRequestsRepository, + NoteReactionsRepository, + NotesRepository, + PollsRepository, + UsersRepository, +} from '@/models/_.js'; type MockResponse = { type: string; diff --git a/packages/backend/test/prelude/get-api-validator.ts b/packages/backend/test/prelude/get-api-validator.ts index cccd63299aafd45122f0fd5f61b4fa2def32009c..b86a7a978defb21f1d1856ef7dde2b723a2ba5cb 100644 --- a/packages/backend/test/prelude/get-api-validator.ts +++ b/packages/backend/test/prelude/get-api-validator.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/test/prelude/maybe.ts b/packages/backend/test/prelude/maybe.ts index 37ccfbf7fe50299eb3f24fc02016bb1080d14f39..16e92216d483ae249701471d485782745a1088b0 100644 --- a/packages/backend/test/prelude/maybe.ts +++ b/packages/backend/test/prelude/maybe.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/test/prelude/url.ts b/packages/backend/test/prelude/url.ts index 340c6451ce8e6c4229db7629ae61994ab4ee3098..b26ae094443d7b75a2fa741fcae310f2cd0050c3 100644 --- a/packages/backend/test/prelude/url.ts +++ b/packages/backend/test/prelude/url.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/test/unit/AnnouncementService.ts b/packages/backend/test/unit/AnnouncementService.ts index f2aa5d35e4a8cd9db69aa898386cee0962c3550f..fc358374204c817e9489adacffc0913b4535eb6e 100644 --- a/packages/backend/test/unit/AnnouncementService.ts +++ b/packages/backend/test/unit/AnnouncementService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -10,7 +10,13 @@ import { ModuleMocker } from 'jest-mock'; import { Test } from '@nestjs/testing'; import { GlobalModule } from '@/GlobalModule.js'; import { AnnouncementService } from '@/core/AnnouncementService.js'; -import type { MiAnnouncement, AnnouncementsRepository, AnnouncementReadsRepository, UsersRepository, MiUser } from '@/models/_.js'; +import type { + AnnouncementReadsRepository, + AnnouncementsRepository, + MiAnnouncement, + MiUser, + UsersRepository, +} from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { genAidx } from '@/misc/id/aidx.js'; import { CacheService } from '@/core/CacheService.js'; diff --git a/packages/backend/test/unit/ApMfmService.ts b/packages/backend/test/unit/ApMfmService.ts new file mode 100644 index 0000000000000000000000000000000000000000..2b79041c86d7e6a3b6a58a3c35b9797500227959 --- /dev/null +++ b/packages/backend/test/unit/ApMfmService.ts @@ -0,0 +1,44 @@ +import * as assert from 'assert'; +import { Test } from '@nestjs/testing'; + +import { CoreModule } from '@/core/CoreModule.js'; +import { ApMfmService } from '@/core/activitypub/ApMfmService.js'; +import { GlobalModule } from '@/GlobalModule.js'; +import { MiNote } from '@/models/Note.js'; + +describe('ApMfmService', () => { + let apMfmService: ApMfmService; + + beforeAll(async () => { + const app = await Test.createTestingModule({ + imports: [GlobalModule, CoreModule], + }).compile(); + apMfmService = app.get<ApMfmService>(ApMfmService); + }); + + describe('getNoteHtml', () => { + test('Do not provide _misskey_content for simple text', () => { + const note: MiNote = { + text: 'テã‚スト #ã‚¿ã‚° @mention 🊠:emoji: https://example.com', + mentionedRemoteUsers: '[]', + } as any; + + const { content, noMisskeyContent } = apMfmService.getNoteHtml(note); + + assert.equal(noMisskeyContent, true, 'noMisskeyContent'); + assert.equal(content, '<p>テã‚スト <a href="http://misskey.local/tags/ã‚¿ã‚°" rel="tag">#ã‚¿ã‚°</a> <a href="http://misskey.local/@mention" class="u-url mention">@mention</a> 🊠​:emoji:​ <a href="https://example.com">https://example.com</a></p>', 'content'); + }); + + test('Provide _misskey_content for MFM', () => { + const note: MiNote = { + text: '$[tada foo]', + mentionedRemoteUsers: '[]', + } as any; + + const { content, noMisskeyContent } = apMfmService.getNoteHtml(note); + + assert.equal(noMisskeyContent, false, 'noMisskeyContent'); + assert.equal(content, '<p><i>foo</i></p>', 'content'); + }); + }); +}); diff --git a/packages/backend/test/unit/DriveService.ts b/packages/backend/test/unit/DriveService.ts index 7234da2e363e9374aa97f50c48c376cf633fc578..964c65ccaaff108138125502f72ac066bb4e267e 100644 --- a/packages/backend/test/unit/DriveService.ts +++ b/packages/backend/test/unit/DriveService.ts @@ -1,12 +1,18 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ process.env.NODE_ENV = 'test'; import { Test } from '@nestjs/testing'; -import { DeleteObjectCommandOutput, DeleteObjectCommand, NoSuchKey, InvalidObjectState, S3Client } from '@aws-sdk/client-s3'; +import { + DeleteObjectCommand, + DeleteObjectCommandOutput, + InvalidObjectState, + NoSuchKey, + S3Client, +} from '@aws-sdk/client-s3'; import { mockClient } from 'aws-sdk-client-mock'; import { GlobalModule } from '@/GlobalModule.js'; import { DriveService } from '@/core/DriveService.js'; diff --git a/packages/backend/test/unit/FetchInstanceMetadataService.ts b/packages/backend/test/unit/FetchInstanceMetadataService.ts index 34200899d4c8274bab275802173ac3e314bcd572..e6e68ccd6db31e4153eec3079b95ca5e6c7b0552 100644 --- a/packages/backend/test/unit/FetchInstanceMetadataService.ts +++ b/packages/backend/test/unit/FetchInstanceMetadataService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -55,7 +55,8 @@ describe('FetchInstanceMetadataService', () => { return { fetch: jest.fn() }; } else if (token === DI.redis) { return mockRedis; - }}) + } + }) .compile(); app.enableShutdownHooks(); diff --git a/packages/backend/test/unit/FileInfoService.ts b/packages/backend/test/unit/FileInfoService.ts index ba524adff443dcf113d8c4f82c7197b282c27002..7d898860643e9c0bf6079b8b8b684e0d5d43ce9c 100644 --- a/packages/backend/test/unit/FileInfoService.ts +++ b/packages/backend/test/unit/FileInfoService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -10,7 +10,7 @@ import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; import { ModuleMocker } from 'jest-mock'; import { Test } from '@nestjs/testing'; -import { describe, beforeAll, afterAll, test } from '@jest/globals'; +import { afterAll, beforeAll, describe, test } from '@jest/globals'; import { GlobalModule } from '@/GlobalModule.js'; import { FileInfoService } from '@/core/FileInfoService.js'; //import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/test/unit/MetaService.ts b/packages/backend/test/unit/MetaService.ts index ab30f482831e379dd91b4bee67a90993f05a09b1..19c98eab3dc06a7220b066dc4465b6f07bdc1376 100644 --- a/packages/backend/test/unit/MetaService.ts +++ b/packages/backend/test/unit/MetaService.ts @@ -1,20 +1,18 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ process.env.NODE_ENV = 'test'; import { jest } from '@jest/globals'; -import { ModuleMocker } from 'jest-mock'; import { Test } from '@nestjs/testing'; import { GlobalModule } from '@/GlobalModule.js'; -import type { MetasRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; import { CoreModule } from '@/core/CoreModule.js'; -import type { DataSource } from 'typeorm'; import type { TestingModule } from '@nestjs/testing'; +import type { DataSource } from 'typeorm'; describe('MetaService', () => { let app: TestingModule; diff --git a/packages/backend/test/unit/MfmService.ts b/packages/backend/test/unit/MfmService.ts index 49e84ccec8758be9860d92281f1ccc6aecbacc0a..85242f2bc72e7be62680a2334f348604256c8766 100644 --- a/packages/backend/test/unit/MfmService.ts +++ b/packages/backend/test/unit/MfmService.ts @@ -1,10 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import * as assert from 'assert'; -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; import { Test } from '@nestjs/testing'; import { CoreModule } from '@/core/CoreModule.js'; @@ -33,6 +33,12 @@ describe('MfmService', () => { const output = '<p><span>foo<br>bar<br>baz</span></p>'; assert.equal(mfmService.toHtml(mfm.parse(input)), output); }); + + test('Do not generate unnecessary span', () => { + const input = 'foo $[tada bar]'; + const output = '<p>foo <i>bar</i></p>'; + assert.equal(mfmService.toHtml(mfm.parse(input)), output); + }); }); describe('fromHtml', () => { diff --git a/packages/backend/test/unit/ReactionService.ts b/packages/backend/test/unit/ReactionService.ts index 7b5bf7d0a07bff924acf10fed8755f7726063ac3..1957f4544cf76838b273a7b5df39d54656e891a4 100644 --- a/packages/backend/test/unit/ReactionService.ts +++ b/packages/backend/test/unit/ReactionService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -90,4 +90,45 @@ describe('ReactionService', () => { assert.strictEqual(await reactionService.normalize('unknown'), 'â¤'); }); }); + + describe('convertLegacyReactions', () => { + test('空ã®å…¥åŠ›ã«å¯¾ã—ã¦ã¯ä½•ã‚‚ã—ãªã„', () => { + const input = {}; + assert.deepStrictEqual(reactionService.convertLegacyReactions(input), input); + }); + + test('Unicode絵文å—リアクションを変æ›ã—ã¦ã—ã¾ã‚ãªã„', () => { + const input = { 'ðŸ‘': 1, 'ðŸ®': 2 }; + assert.deepStrictEqual(reactionService.convertLegacyReactions(input), input); + }); + + test('カスタム絵文å—リアクションを変æ›ã—ã¦ã—ã¾ã‚ãªã„', () => { + const input = { ':like@.:': 1, ':pudding@example.tld:': 2 }; + assert.deepStrictEqual(reactionService.convertLegacyReactions(input), input); + }); + + test('æ–‡å—列ã«ã‚ˆã‚‹ãƒ¬ã‚¬ã‚·ãƒ¼ãªãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’変æ›ã™ã‚‹', () => { + const input = { 'like': 1, 'pudding': 2 }; + const output = { 'ðŸ‘': 1, 'ðŸ®': 2 }; + assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output); + }); + + test('host部分ãŒçœç•¥ã•ã‚ŒãŸãƒ¬ã‚¬ã‚·ãƒ¼ãªã‚«ã‚¹ã‚¿ãƒ 絵文å—リアクションを変æ›ã™ã‚‹', () => { + const input = { ':custom_emoji:': 1 }; + const output = { ':custom_emoji@.:': 1 }; + assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output); + }); + + test('「0個ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã€æƒ…å ±ã‚’å‰Šé™¤ã™ã‚‹', () => { + const input = { 'angry': 0 }; + const output = {}; + assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output); + }); + + test('host部分ã®æœ‰ç„¡ã«ã‚ˆã‚Šãƒ‡ã‚³ãƒ¼ãƒ‰ã™ã‚‹ã¨åŒã˜è¡¨è¨˜ã«ãªã‚‹ã‚«ã‚¹ã‚¿ãƒ 絵文å—リアクションã®å€‹æ•°æƒ…å ±ã‚’æ£ã—ã足ã—åˆã‚ã›ã‚‹', () => { + const input = { ':custom_emoji:': 1, ':custom_emoji@.:': 2 }; + const output = { ':custom_emoji@.:': 3 }; + assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output); + }); + }); }); diff --git a/packages/backend/test/unit/RelayService.ts b/packages/backend/test/unit/RelayService.ts index f780a25388cce662fdaf5d26fcc0816a9fe470c1..f2a67dba46b41bd683dd7a515d5d05e2a3bd0177 100644 --- a/packages/backend/test/unit/RelayService.ts +++ b/packages/backend/test/unit/RelayService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts index 9879eb8e3ebb17cde1b0a4e8834011227564c282..fe5ad31597ad47053fd0f4c84048a2d59b0c7383 100644 --- a/packages/backend/test/unit/RoleService.ts +++ b/packages/backend/test/unit/RoleService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -11,7 +11,7 @@ import { Test } from '@nestjs/testing'; import * as lolex from '@sinonjs/fake-timers'; import { GlobalModule } from '@/GlobalModule.js'; import { RoleService } from '@/core/RoleService.js'; -import type { MiRole, RolesRepository, RoleAssignmentsRepository, UsersRepository, MiUser } from '@/models/_.js'; +import type { MiRole, MiUser, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; import { genAidx } from '@/misc/id/aidx.js'; @@ -251,6 +251,34 @@ describe('RoleService', () => { expect(user2Policies.canManageCustomEmojis).toBe(true); }); + test('コンディショナルãƒãƒ¼ãƒ«: マニュアルãƒãƒ¼ãƒ«ã«ã‚¢ã‚µã‚¤ãƒ³æ¸ˆã¿', async () => { + const [user1, user2, role1] = await Promise.all([ + createUser(), + createUser(), + createRole({ + name: 'manual role', + }), + ]); + const role2 = await createRole({ + name: 'conditional role', + target: 'conditional', + condFormula: { + // idã¯ãƒãƒƒã‚¯ã‚¨ãƒ³ãƒ‰ã®ãƒã‚¸ãƒƒã‚¯ã«å¿…è¦ãªã„? + id: 'bdc612bd-9d54-4675-ae83-0499c82ea670', + type: 'roleAssignedTo', + roleId: role1.id, + }, + }); + await roleService.assign(user2.id, role1.id); + + const [u1role, u2role] = await Promise.all([ + roleService.getUserRoles(user1.id), + roleService.getUserRoles(user2.id), + ]); + expect(u1role.some(r => r.id === role2.id)).toBe(false); + expect(u2role.some(r => r.id === role2.id)).toBe(true); + }); + test('expired role', async () => { const user = await createUser(); const role = await createRole({ diff --git a/packages/backend/test/unit/S3Service.ts b/packages/backend/test/unit/S3Service.ts index c1eafc96b7522cf11a9e5394054339bf8eb9e549..151f3b826a9fd38f11dcc475f0503f93e37da846 100644 --- a/packages/backend/test/unit/S3Service.ts +++ b/packages/backend/test/unit/S3Service.ts @@ -1,12 +1,18 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ process.env.NODE_ENV = 'test'; import { Test } from '@nestjs/testing'; -import { UploadPartCommand, CompleteMultipartUploadCommand, CreateMultipartUploadCommand, S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; +import { + CompleteMultipartUploadCommand, + CreateMultipartUploadCommand, + PutObjectCommand, + S3Client, + UploadPartCommand, +} from '@aws-sdk/client-s3'; import { mockClient } from 'aws-sdk-client-mock'; import { GlobalModule } from '@/GlobalModule.js'; import { CoreModule } from '@/core/CoreModule.js'; diff --git a/packages/backend/test/unit/activitypub.ts b/packages/backend/test/unit/activitypub.ts index 014e862f9c4fbba3007d2d596727f97e6f0dddfd..bd335bb2fea82201ac53167e9b80f4530bc2ca07 100644 --- a/packages/backend/test/unit/activitypub.ts +++ b/packages/backend/test/unit/activitypub.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -100,6 +100,7 @@ describe('ActivityPub', () => { perRemoteUserUserTimelineCacheMax: 800, blockedHosts: [] as string[], sensitiveWords: [] as string[], + prohibitedWords: [] as string[], } as MiMeta; let meta = metaInitial; @@ -222,7 +223,7 @@ describe('ActivityPub', () => { await personService.createPerson(actor.id, resolver); // All notes in `featured` are same-origin, no need to fetch notes again - assert.deepStrictEqual(resolver.remoteGetTrials(), [actor.id, actor.featured]); + assert.deepStrictEqual(resolver.remoteGetTrials(), [actor.id, `${actor.id}/outbox`, actor.featured]); // Created notes without resolving anything for (const item of featured.items as IPost[]) { @@ -255,7 +256,7 @@ describe('ActivityPub', () => { // actor2Note is from a different server and needs to be fetched again assert.deepStrictEqual( resolver.remoteGetTrials(), - [actor1.id, actor1.featured, actor2Note.id, actor2.id], + [actor1.id, `${actor1.id}/outbox`, actor1.featured, actor2Note.id, actor2.id, `${actor2.id}/outbox` ], ); const note = await noteService.fetchNote(actor2Note.id); diff --git a/packages/backend/test/unit/ap-request.ts b/packages/backend/test/unit/ap-request.ts index 9edd53d274c9cb97e7f9020ff7192afc76c03f17..d3d39240dc9ab0ccf8ef4c6d198b6531763abcb4 100644 --- a/packages/backend/test/unit/ap-request.ts +++ b/packages/backend/test/unit/ap-request.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/test/unit/chart.ts b/packages/backend/test/unit/chart.ts index 036e73fd5e46920712621a671e0820cbc8c70e7b..9dedd3a79daef3ce0d85d24545b4ab6ed69f1791 100644 --- a/packages/backend/test/unit/chart.ts +++ b/packages/backend/test/unit/chart.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/test/unit/extract-mentions.ts b/packages/backend/test/unit/extract-mentions.ts index 195e9b81988cb59c22e0a8f2578ca0ae0f7faa24..2aad89d65b43f78185bcb3bc7a8723aed7e263e8 100644 --- a/packages/backend/test/unit/extract-mentions.ts +++ b/packages/backend/test/unit/extract-mentions.ts @@ -1,11 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import * as assert from 'assert'; -import { parse } from '@sharkey/sfm-js'; +import { parse } from '@transfem-org/sfm-js'; import { extractMentions } from '@/misc/extract-mentions.js'; describe('Extract mentions', () => { diff --git a/packages/backend/test/unit/misc/check-word-mute.ts b/packages/backend/test/unit/misc/check-word-mute.ts index 12bfca8bd7e81e3bc1393bede54fb9da073d7ba9..eb0ca0f6cfe6efee5b51f8620827948a0d6f98e7 100644 --- a/packages/backend/test/unit/misc/check-word-mute.ts +++ b/packages/backend/test/unit/misc/check-word-mute.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/test/unit/misc/correct-filename.ts b/packages/backend/test/unit/misc/correct-filename.ts index 0c4482e0bf724a17c385c47dae8b5b670f480e56..c76fb4c494b8e8b1425a85869364031cfbdee9bc 100644 --- a/packages/backend/test/unit/misc/correct-filename.ts +++ b/packages/backend/test/unit/misc/correct-filename.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/backend/test/unit/misc/id.ts b/packages/backend/test/unit/misc/id.ts index 59783a9fa1b869c305ff5cca09cfeb69e91c444b..d14efb10a6f455badf21a8bf5fbcae8a5d05c0fb 100644 --- a/packages/backend/test/unit/misc/id.ts +++ b/packages/backend/test/unit/misc/id.ts @@ -1,16 +1,16 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { ulid } from 'ulid'; -import { describe, test, expect } from '@jest/globals'; +import { describe, expect, test } from '@jest/globals'; import { aidRegExp, genAid, parseAid } from '@/misc/id/aid.js'; import { aidxRegExp, genAidx, parseAidx } from '@/misc/id/aidx.js'; import { genMeid, meidRegExp, parseMeid } from '@/misc/id/meid.js'; import { genMeidg, meidgRegExp, parseMeidg } from '@/misc/id/meidg.js'; import { genObjectId, objectIdRegExp, parseObjectId } from '@/misc/id/object-id.js'; -import { ulidRegExp, parseUlid } from '@/misc/id/ulid.js'; +import { parseUlid, ulidRegExp } from '@/misc/id/ulid.js'; describe('misc:id', () => { test('aid', () => { diff --git a/packages/backend/test/unit/misc/others.ts b/packages/backend/test/unit/misc/others.ts index b16d26d866fb29e052fe9e383c367328ab85b5a5..3bc134a2b8e7bd297e70093438b549636d3526cb 100644 --- a/packages/backend/test/unit/misc/others.ts +++ b/packages/backend/test/unit/misc/others.ts @@ -1,9 +1,9 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import { describe, test, expect } from '@jest/globals'; +import { describe, expect, test } from '@jest/globals'; import { contentDisposition } from '@/misc/content-disposition.js'; describe('misc:content-disposition', () => { diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index 8c5fa0006c512093092a2db7a80c0cc6c0795ce3..cd5dddd68d8e46005e027621c63eb9d13d89cf9b 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -1,11 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import * as assert from 'node:assert'; import { readFile } from 'node:fs/promises'; -import { isAbsolute, basename } from 'node:path'; +import { basename, isAbsolute } from 'node:path'; import { randomUUID } from 'node:crypto'; import { inspect } from 'node:util'; import WebSocket, { ClientOptions } from 'ws'; @@ -19,7 +19,7 @@ import { entities } from '../src/postgres.js'; import { loadConfig } from '../src/config.js'; import type * as misskey from 'misskey-js'; -export { server as startServer } from '@/boot/common.js'; +export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js'; interface UserToken { token: string; @@ -70,7 +70,11 @@ export const failedApiCall = async <T, >(request: ApiRequest, assertion: { return res.body; }; -const request = async (path: string, params: any, me?: UserToken): Promise<{ status: number, headers: Headers, body: any }> => { +const request = async (path: string, params: any, me?: UserToken): Promise<{ + status: number, + headers: Headers, + body: any +}> => { const bodyAuth: Record<string, string> = {}; const headers: Record<string, string> = { 'Content-Type': 'application/json', @@ -291,7 +295,11 @@ interface UploadOptions { * Upload file * @param user User */ -export const uploadFile = async (user?: UserToken, { path, name, blob }: UploadOptions = {}): Promise<{ status: number, headers: Headers, body: misskey.Endpoints['drive/files/create']['res'] | null }> => { +export const uploadFile = async (user?: UserToken, { path, name, blob }: UploadOptions = {}): Promise<{ + status: number, + headers: Headers, + body: misskey.Endpoints['drive/files/create']['res'] | null +}> => { const absPath = path == null ? new URL('resources/Lenna.jpg', import.meta.url) : isAbsolute(path.toString()) @@ -327,9 +335,7 @@ export const uploadFile = async (user?: UserToken, { path, name, blob }: UploadO }; }; -export const uploadUrl = async (user: UserToken, url: string) => { - let resolve: unknown; - const file = new Promise(ok => resolve = ok); +export const uploadUrl = async (user: UserToken, url: string): Promise<Packed<'DriveFile'>> => { const marker = Math.random().toString(); const catcher = makeStreamCatcher( @@ -346,10 +352,10 @@ export const uploadUrl = async (user: UserToken, url: string) => { force: true, }, user); - return file; + return catcher; }; -export function connectStream(user: UserToken, channel: string, listener: (message: Record<string, any>) => any, params?: any): Promise<WebSocket> { +export function connectStream<C extends keyof misskey.Channels>(user: UserToken, channel: C, listener: (message: Record<string, any>) => any, params?: misskey.Channels[C]['params']): Promise<WebSocket> { return new Promise((res, rej) => { const url = new URL(`ws://127.0.0.1:${port}/streaming`); const options: ClientOptions = {}; @@ -384,7 +390,7 @@ export function connectStream(user: UserToken, channel: string, listener: (messa }); } -export const waitFire = async (user: UserToken, channel: string, trgr: () => any, cond: (msg: Record<string, any>) => boolean, params?: any) => { +export const waitFire = async <C extends keyof misskey.Channels>(user: UserToken, channel: C, trgr: () => any, cond: (msg: Record<string, any>) => boolean, params?: misskey.Channels[C]['params']) => { return new Promise<boolean>(async (res, rej) => { let timer: NodeJS.Timeout | null = null; @@ -429,7 +435,7 @@ export const waitFire = async (user: UserToken, channel: string, trgr: () => any */ export function makeStreamCatcher<T>( user: UserToken, - channel: string, + channel: keyof misskey.Channels, cond: (message: Record<string, any>) => boolean, extractor: (message: Record<string, any>) => T, timeout = 60 * 1000): Promise<T> { @@ -479,8 +485,8 @@ export const simpleGet = async (path: string, accept = '*/*', cookie: any = unde } const body = - jsonTypes.includes(res.headers.get('content-type') ?? '') ? await res.json() : - htmlTypes.includes(res.headers.get('content-type') ?? '') ? new JSDOM(await res.text()) : + jsonTypes.includes(res.headers.get('content-type') ?? '') ? await res.json() : + htmlTypes.includes(res.headers.get('content-type') ?? '') ? new JSDOM(await res.text()) : null; return { @@ -610,3 +616,34 @@ export function sleep(msec: number) { }, msec); }); } + +export async function sendEnvUpdateRequest(params: { key: string, value?: string }) { + const res = await fetch( + `http://localhost:${port + 1000}/env`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(params), + }, + ); + + if (res.status !== 200) { + throw new Error('server env update failed.'); + } +} + +export async function sendEnvResetRequest() { + const res = await fetch( + `http://localhost:${port + 1000}/env-reset`, + { + method: 'POST', + body: JSON.stringify({}), + }, + ); + + if (res.status !== 200) { + throw new Error('server env update failed.'); + } +} diff --git a/packages/backend/watch.mjs b/packages/backend/watch.mjs index 81c23a0f508195b5f6bf9a7d4a1943b06a978a1a..a0ccea3b16b7e5de3c1afc2111d2dcf447c1c8dc 100644 --- a/packages/backend/watch.mjs +++ b/packages/backend/watch.mjs @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/.storybook/changes.ts b/packages/frontend/.storybook/changes.ts index 0cc648fbae2cc7ce3a603fe9a8056204ae828615..7c70972e1e3d8d1ea610bb641771779099fb1fa8 100644 --- a/packages/frontend/.storybook/changes.ts +++ b/packages/frontend/.storybook/changes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/.storybook/fakes.ts b/packages/frontend/.storybook/fakes.ts index fdf98665d05c3f8061c8ef3300304b1f9f3f3745..de28d343d10cb132e905cf2d042b0d12018ee164 100644 --- a/packages/frontend/.storybook/fakes.ts +++ b/packages/frontend/.storybook/fakes.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx index d61df9e7bede94989102e4816e07a259770db038..1e925aede61847ac899bafde30e1c8ed7559603f 100644 --- a/packages/frontend/.storybook/generate.tsx +++ b/packages/frontend/.storybook/generate.tsx @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -401,7 +401,8 @@ function toStories(component: string): Promise<string> { // glob('src/{components,pages,ui,widgets}/**/*.vue') (async () => { const globs = await Promise.all([ - glob('src/components/global/*.vue'), + glob('src/components/global/Mk*.vue'), + glob('src/components/global/RouterView.vue'), glob('src/components/Mk{A,B}*.vue'), glob('src/components/MkDigitalClock.vue'), glob('src/components/MkGalleryPostPreview.vue'), diff --git a/packages/frontend/.storybook/main.ts b/packages/frontend/.storybook/main.ts index a450f8b46be94c2acc7778f59caeb57a3dba73d4..0a87488573ed5bf502b9809b8982d040d93a269f 100644 --- a/packages/frontend/.storybook/main.ts +++ b/packages/frontend/.storybook/main.ts @@ -1,27 +1,30 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import { resolve } from 'node:path'; +import { createRequire } from 'node:module'; +import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import type { StorybookConfig } from '@storybook/vue3-vite'; import { type Plugin, mergeConfig } from 'vite'; import turbosnap from 'vite-plugin-turbosnap'; -const dirname = fileURLToPath(new URL('.', import.meta.url)); +const require = createRequire(import.meta.url); +const _dirname = fileURLToPath(new URL('.', import.meta.url)); const config = { stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], addons: [ - '@storybook/addon-essentials', - '@storybook/addon-interactions', - '@storybook/addon-links', - '@storybook/addon-storysource', - resolve(dirname, '../node_modules/storybook-addon-misskey-theme'), + getAbsolutePath('@storybook/addon-essentials'), + getAbsolutePath('@storybook/addon-interactions'), + getAbsolutePath('@storybook/addon-links'), + getAbsolutePath('@storybook/addon-storysource'), + getAbsolutePath('@storybook/addon-mdx-gfm'), + resolve(_dirname, '../node_modules/storybook-addon-misskey-theme'), ], framework: { - name: '@storybook/vue3-vite', + name: getAbsolutePath('@storybook/vue3-vite') as '@storybook/vue3-vite', options: {}, }, docs: { @@ -37,10 +40,13 @@ const config = { } return mergeConfig(config, { plugins: [ - // XXX: https://github.com/IanVS/vite-plugin-turbosnap/issues/8 - (turbosnap as any as typeof turbosnap['default'])({ - rootDir: config.root ?? process.cwd(), - }), + { + // XXX: https://github.com/IanVS/vite-plugin-turbosnap/issues/8 + ...(turbosnap as any as typeof turbosnap['default'])({ + rootDir: config.root ?? process.cwd(), + }), + name: 'fake-turbosnap', + }, ], build: { target: [ @@ -53,3 +59,7 @@ const config = { }, } satisfies StorybookConfig; export default config; + +function getAbsolutePath(value: string): string { + return dirname(require.resolve(join(value, 'package.json'))); +} diff --git a/packages/frontend/.storybook/manager.ts b/packages/frontend/.storybook/manager.ts index 8f501111d0a9fa7b701cec72583f288670dc93f3..7375a1f2a94facc62bbcbed073f7a8f999f90ec5 100644 --- a/packages/frontend/.storybook/manager.ts +++ b/packages/frontend/.storybook/manager.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/.storybook/mocks.ts b/packages/frontend/.storybook/mocks.ts index 80e5157c5a2d7a3d5150f8fcf8f06ed2006b1637..817b0125e7e9097d60573b00d9af4e0bf856829f 100644 --- a/packages/frontend/.storybook/mocks.ts +++ b/packages/frontend/.storybook/mocks.ts @@ -1,9 +1,9 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import { type SharedOptions, rest } from 'msw'; +import { type SharedOptions, http, HttpResponse } from 'msw'; export const onUnhandledRequest = ((req, print) => { if (req.url.hostname !== 'localhost' || /^\/(?:client-assets\/|fluent-emojis?\/|iframe.html$|node_modules\/|src\/|sb-|static-assets\/|vite\/)/.test(req.url.pathname)) { @@ -13,19 +13,31 @@ export const onUnhandledRequest = ((req, print) => { }) satisfies SharedOptions['onUnhandledRequest']; export const commonHandlers = [ - rest.get('/fluent-emoji/:codepoints.png', async (req, res, ctx) => { - const { codepoints } = req.params; + http.get('/fluent-emoji/:codepoints.png', async ({ params }) => { + const { codepoints } = params; const value = await fetch(`https://raw.githubusercontent.com/misskey-dev/emojis/main/dist/${codepoints}.png`).then((response) => response.blob()); - return res(ctx.set('Content-Type', 'image/png'), ctx.body(value)); + return new HttpResponse(value, { + headers: { + 'Content-Type': 'image/png', + }, + }); }), - rest.get('/fluent-emojis/:codepoints.png', async (req, res, ctx) => { - const { codepoints } = req.params; + http.get('/fluent-emojis/:codepoints.png', async ({ params }) => { + const { codepoints } = params; const value = await fetch(`https://raw.githubusercontent.com/misskey-dev/emojis/main/dist/${codepoints}.png`).then((response) => response.blob()); - return res(ctx.set('Content-Type', 'image/png'), ctx.body(value)); + return new HttpResponse(value, { + headers: { + 'Content-Type': 'image/png', + }, + }); }), - rest.get('/twemoji/:codepoints.svg', async (req, res, ctx) => { - const { codepoints } = req.params; + http.get('/twemoji/:codepoints.svg', async ({ params }) => { + const { codepoints } = params; const value = await fetch(`https://unpkg.com/@discordapp/twemoji@15.0.2/dist/svg/${codepoints}.svg`).then((response) => response.blob()); - return res(ctx.set('Content-Type', 'image/svg+xml'), ctx.body(value)); + return new HttpResponse(value, { + headers: { + 'Content-Type': 'image/svg+xml', + }, + }); }), ]; diff --git a/packages/frontend/.storybook/preload-locale.ts b/packages/frontend/.storybook/preload-locale.ts index 349cc135080a86048f5e9f5fe99e1cc9af6de65b..c823ff9beed622a0db97cb6984e78313e94c5235 100644 --- a/packages/frontend/.storybook/preload-locale.ts +++ b/packages/frontend/.storybook/preload-locale.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/.storybook/preload-theme.ts b/packages/frontend/.storybook/preload-theme.ts index 8f32c6e6220db3c2e7d4ec6e3ddc553abc479cb5..e174c72b4856ebf030fc76e4daee4b1e57b27237 100644 --- a/packages/frontend/.storybook/preload-theme.ts +++ b/packages/frontend/.storybook/preload-theme.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/.storybook/preview.ts b/packages/frontend/.storybook/preview.ts index 9860b60c678901ac4ff6fa208a9d022cab4064a0..982a2979ac8ceac74cbb816e923746136b50baf5 100644 --- a/packages/frontend/.storybook/preview.ts +++ b/packages/frontend/.storybook/preview.ts @@ -1,10 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import { addons } from '@storybook/addons'; import { FORCE_REMOUNT } from '@storybook/core-events'; +import { addons } from '@storybook/preview-api'; import { type Preview, setup } from '@storybook/vue3'; import isChromatic from 'chromatic/isChromatic'; import { initialize, mswDecorator } from 'msw-storybook-addon'; diff --git a/packages/frontend/@types/global.d.ts b/packages/frontend/@types/global.d.ts index 7d9335cc52ec29a6710e712b88154a5457b891ef..1025d1bedbb8a6f69fef263191c43bbd677548be 100644 --- a/packages/frontend/@types/global.d.ts +++ b/packages/frontend/@types/global.d.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -16,3 +16,8 @@ declare const _DATA_TRANSFER_DECK_COLUMN_: string; // for dev-mode declare const _LANGS_FULL_: string[][]; + +// TagCanvas +interface Window { + TagCanvas: any; +} diff --git a/packages/frontend/@types/theme.d.ts b/packages/frontend/@types/theme.d.ts index 376bbb0e9cccb4fe213d349a273e7e48da145e71..0a7281898d9896a1fc82f38c2d61bcb23102c0f2 100644 --- a/packages/frontend/@types/theme.d.ts +++ b/packages/frontend/@types/theme.d.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/assets/drop-and-fusion/bgm_1.mp3 b/packages/frontend/assets/drop-and-fusion/bgm_1.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..cafc34ad9ce449da55786c5e38bccad10c49de34 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/bgm_1.mp3 differ diff --git a/packages/frontend/assets/drop-and-fusion/click.mp3 b/packages/frontend/assets/drop-and-fusion/click.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..ef03e60f61f68af7b8f182db7e3148ea8ee8861f Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/click.mp3 differ diff --git a/packages/frontend/assets/drop-and-fusion/collision.mp3 b/packages/frontend/assets/drop-and-fusion/collision.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..59dae90965d34400449b0334f87ca02615dc8ce0 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/collision.mp3 differ diff --git a/packages/frontend/assets/drop-and-fusion/collision_yen.mp3 b/packages/frontend/assets/drop-and-fusion/collision_yen.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..6737357f6263dca17cc1c009d36304dc6e1155e4 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/collision_yen.mp3 differ diff --git a/packages/frontend/assets/drop-and-fusion/drop-arrow.svg b/packages/frontend/assets/drop-and-fusion/drop-arrow.svg new file mode 100644 index 0000000000000000000000000000000000000000..f98bb8a1aca587ea14b98623bd400238c824c2ce Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/drop-arrow.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/drop.mp3 b/packages/frontend/assets/drop-and-fusion/drop.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..a65c653891833c20d19db01e69bedc9da16e5fca Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/drop.mp3 differ diff --git a/packages/frontend/assets/drop-and-fusion/drop_yen.mp3 b/packages/frontend/assets/drop-and-fusion/drop_yen.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..bbf385f15af1377f0c6d29a06c6b01461dca3e03 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/drop_yen.mp3 differ diff --git a/packages/frontend/assets/drop-and-fusion/dropper.png b/packages/frontend/assets/drop-and-fusion/dropper.png new file mode 100644 index 0000000000000000000000000000000000000000..f4300aa5c06f6001a87c01f8525847b294f43be3 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/dropper.png differ diff --git a/packages/frontend/assets/drop-and-fusion/frame-dark.svg b/packages/frontend/assets/drop-and-fusion/frame-dark.svg new file mode 100644 index 0000000000000000000000000000000000000000..3fa7c0da81e0b8eb7aae48b18e85d0f9ee72441a Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/frame-dark.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/frame-light.svg b/packages/frontend/assets/drop-and-fusion/frame-light.svg new file mode 100644 index 0000000000000000000000000000000000000000..6052ccbaa09bcc6120bbd81eb1988b666aedafbd Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/frame-light.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/fusion.mp3 b/packages/frontend/assets/drop-and-fusion/fusion.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..8b4f8df6e9c17daec54da10bb50e790194c767d2 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/fusion.mp3 differ diff --git a/packages/frontend/assets/drop-and-fusion/fusion_yen.mp3 b/packages/frontend/assets/drop-and-fusion/fusion_yen.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e8d203fb5dfe895a9d07e9ff053fa59cd7102cda Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/fusion_yen.mp3 differ diff --git a/packages/frontend/assets/drop-and-fusion/gameover.mp3 b/packages/frontend/assets/drop-and-fusion/gameover.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..23b41c56995a8adc0033d46b946f967876073d1b Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/gameover.mp3 differ diff --git a/packages/frontend/assets/drop-and-fusion/gameover.png b/packages/frontend/assets/drop-and-fusion/gameover.png new file mode 100644 index 0000000000000000000000000000000000000000..8b622577cad3e88aca2affbb9638935779472fde Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/gameover.png differ diff --git a/packages/frontend/assets/drop-and-fusion/gameover_yen.mp3 b/packages/frontend/assets/drop-and-fusion/gameover_yen.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..c7fdcb5c8fbec6098ba55457bc169472402bf3df Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/gameover_yen.mp3 differ diff --git a/packages/frontend/assets/drop-and-fusion/go.png b/packages/frontend/assets/drop-and-fusion/go.png new file mode 100644 index 0000000000000000000000000000000000000000..37468f1395263f77d1af9ad21e49c2e7617de45c Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/go.png differ diff --git a/packages/frontend/assets/drop-and-fusion/hold.mp3 b/packages/frontend/assets/drop-and-fusion/hold.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f064c976d3b91b18fafe5efc68ca633a9cb334f1 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/hold.mp3 differ diff --git a/packages/frontend/assets/drop-and-fusion/logo.png b/packages/frontend/assets/drop-and-fusion/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c6725bea88b9f5dcb8851475e92b5e20fb338ffe Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/logo.png differ diff --git a/packages/frontend/assets/drop-and-fusion/normal_monos/cold_face.png b/packages/frontend/assets/drop-and-fusion/normal_monos/cold_face.png new file mode 100644 index 0000000000000000000000000000000000000000..f5f53e9efc2f9210c51ca45b106f9809cf9b031c Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/normal_monos/cold_face.png differ diff --git a/packages/frontend/assets/drop-and-fusion/normal_monos/exploding_head.png b/packages/frontend/assets/drop-and-fusion/normal_monos/exploding_head.png new file mode 100644 index 0000000000000000000000000000000000000000..e8ec5182c884b094fb5b4cda8b025b2326a370fd Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/normal_monos/exploding_head.png differ diff --git a/packages/frontend/assets/drop-and-fusion/normal_monos/face_with_open_mouth.png b/packages/frontend/assets/drop-and-fusion/normal_monos/face_with_open_mouth.png new file mode 100644 index 0000000000000000000000000000000000000000..c523020f6287754f4d87a0a8bee4a83090cad462 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/normal_monos/face_with_open_mouth.png differ diff --git a/packages/frontend/assets/drop-and-fusion/normal_monos/face_with_symbols_on_mouth.png b/packages/frontend/assets/drop-and-fusion/normal_monos/face_with_symbols_on_mouth.png new file mode 100644 index 0000000000000000000000000000000000000000..db9e839c848cff962c4389fc60133452b82956bc Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/normal_monos/face_with_symbols_on_mouth.png differ diff --git a/packages/frontend/assets/drop-and-fusion/normal_monos/grinning_squinting_face.png b/packages/frontend/assets/drop-and-fusion/normal_monos/grinning_squinting_face.png new file mode 100644 index 0000000000000000000000000000000000000000..fd72d749a1ab144d4cf7752563c53837361c91fd Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/normal_monos/grinning_squinting_face.png differ diff --git a/packages/frontend/assets/drop-and-fusion/normal_monos/heart_suit.png b/packages/frontend/assets/drop-and-fusion/normal_monos/heart_suit.png new file mode 100644 index 0000000000000000000000000000000000000000..b0105f85829fcbe36c095034f13d04652e1e41f8 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/normal_monos/heart_suit.png differ diff --git a/packages/frontend/assets/drop-and-fusion/normal_monos/pleading_face.png b/packages/frontend/assets/drop-and-fusion/normal_monos/pleading_face.png new file mode 100644 index 0000000000000000000000000000000000000000..42f58d411ca6a3b94a9d7fa341eab014ebe8c7e1 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/normal_monos/pleading_face.png differ diff --git a/packages/frontend/assets/drop-and-fusion/normal_monos/smiling_face_with_hearts.png b/packages/frontend/assets/drop-and-fusion/normal_monos/smiling_face_with_hearts.png new file mode 100644 index 0000000000000000000000000000000000000000..416ef0410ad0f801a8f69b9aeb2cd8fda4af6ed8 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/normal_monos/smiling_face_with_hearts.png differ diff --git a/packages/frontend/assets/drop-and-fusion/normal_monos/smiling_face_with_sunglasses.png b/packages/frontend/assets/drop-and-fusion/normal_monos/smiling_face_with_sunglasses.png new file mode 100644 index 0000000000000000000000000000000000000000..c0f72254c27a60ab3e874159af746cff91cb3d5a Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/normal_monos/smiling_face_with_sunglasses.png differ diff --git a/packages/frontend/assets/drop-and-fusion/normal_monos/zany_face.png b/packages/frontend/assets/drop-and-fusion/normal_monos/zany_face.png new file mode 100644 index 0000000000000000000000000000000000000000..f14f9db20b2aeed697070460db6fe8e7ecdc745c Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/normal_monos/zany_face.png differ diff --git a/packages/frontend/assets/drop-and-fusion/ready.png b/packages/frontend/assets/drop-and-fusion/ready.png new file mode 100644 index 0000000000000000000000000000000000000000..10a87fcf58ba11362387339173fcb29f6058c40b Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/ready.png differ diff --git a/packages/frontend/assets/drop-and-fusion/square_monos/keycap_1.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_1.png new file mode 100644 index 0000000000000000000000000000000000000000..d672f2854a8e22451f7329a1603c3241615c5936 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_1.png differ diff --git a/packages/frontend/assets/drop-and-fusion/square_monos/keycap_10.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_10.png new file mode 100644 index 0000000000000000000000000000000000000000..32cf19354065bc8b720e46553c4da1ba5cedf4ea Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_10.png differ diff --git a/packages/frontend/assets/drop-and-fusion/square_monos/keycap_2.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_2.png new file mode 100644 index 0000000000000000000000000000000000000000..81c3f58e6e26e2d8829e762db67892451bae2ebe Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_2.png differ diff --git a/packages/frontend/assets/drop-and-fusion/square_monos/keycap_3.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_3.png new file mode 100644 index 0000000000000000000000000000000000000000..424d8c123dd535754e606c2afcd04cf04b54fe76 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_3.png differ diff --git a/packages/frontend/assets/drop-and-fusion/square_monos/keycap_4.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_4.png new file mode 100644 index 0000000000000000000000000000000000000000..ea6ae505316bb6b1419586e7ecaf9e2f837a924d Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_4.png differ diff --git a/packages/frontend/assets/drop-and-fusion/square_monos/keycap_5.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_5.png new file mode 100644 index 0000000000000000000000000000000000000000..ad435da69a23be77195348a29790eee48ecc826b Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_5.png differ diff --git a/packages/frontend/assets/drop-and-fusion/square_monos/keycap_6.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_6.png new file mode 100644 index 0000000000000000000000000000000000000000..70c9522b4358aa3971d8d7187df98ce3dae4a6f4 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_6.png differ diff --git a/packages/frontend/assets/drop-and-fusion/square_monos/keycap_7.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_7.png new file mode 100644 index 0000000000000000000000000000000000000000..5a24307487e4a56d6bb53f39aea4f1568770bcdf Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_7.png differ diff --git a/packages/frontend/assets/drop-and-fusion/square_monos/keycap_8.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_8.png new file mode 100644 index 0000000000000000000000000000000000000000..9689d8ecfb720ee02a270ca1a88608a226369a60 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_8.png differ diff --git a/packages/frontend/assets/drop-and-fusion/square_monos/keycap_9.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_9.png new file mode 100644 index 0000000000000000000000000000000000000000..ac3f638841207751545ee904ef57fd327fd7b9e9 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_9.png differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/candy_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/candy_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..6eab3ca49b84773eed8fcb66f806103860ae1926 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/candy_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/chocolate_bar_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/chocolate_bar_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..eea5fec1861194539939508f97ef13b09b027174 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/chocolate_bar_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/cookie_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/cookie_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..42b628cca13536d363969cbe8d0b7c3f782d32a6 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/cookie_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/custard_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/custard_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..d967fec7fe9c84d61d1fb61f09c030a15449f09f Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/custard_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/doughnut_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/doughnut_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..e8e225bc0a9fdef290f3dedfb02e54ec722be572 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/doughnut_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/lollipop_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/lollipop_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..ad90ac6f528e588e818d39f89cf3c46b2cfb202a Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/lollipop_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/pancakes_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/pancakes_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..94b2ab69b5ff9332deaa26c41ecdb25bc540078e Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/pancakes_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/shaved_ice_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/shaved_ice_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..64dfef8e05cf5bf113809a51c23f45781aaccc0f Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/shaved_ice_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/shortcake_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/shortcake_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..66e8f91f195f5d678271dc7ca2be4efdded073f4 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/shortcake_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/soft_ice_cream_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/soft_ice_cream_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..37be9c0cb3c2f8a11f6f48b79dc55be133bad4a7 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/soft_ice_cream_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/candy_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/candy_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..e673f430f5e3c0acfeba874ac976add228547acc Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/candy_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/chocolate_bar_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/chocolate_bar_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..5cd39cc9e30e1b2e6a112fdf528d74cea0798445 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/chocolate_bar_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/custard_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/custard_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..dd4870dd7dc1cc3c87a13ea691a1875f907f1dcc Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/custard_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/doughnut_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/doughnut_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..a8d5557f5cbd115cb463473c65b7672092dfb21a Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/doughnut_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/lollipop_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/lollipop_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..d3778737a90c282cdc189a309c577ea39c00a8f3 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/lollipop_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/pancakes_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/pancakes_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..b1a1a322e06b7cfd600fd06dabba40feaf669f2c Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/pancakes_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/shaved_ice_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/shaved_ice_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..00872c7a0c99475d9c549d3a573eeb7dae8d9931 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/shaved_ice_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/shortcake_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/shortcake_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..e6ed1fbbf9eb8051c1d1616c9afb9adeed81d29f Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/shortcake_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/soft_ice_cream_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/soft_ice_cream_color.svg new file mode 100644 index 0000000000000000000000000000000000000000..b77e0c3655d99a461d5e0b3416fb51acd119196c Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/soft_ice_cream_color.svg differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/10000yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/10000yen.png new file mode 100644 index 0000000000000000000000000000000000000000..bda777719d1264314827ec9f100706bdd2fa2d47 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/10000yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/1000yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/1000yen.png new file mode 100644 index 0000000000000000000000000000000000000000..4c462fb1f6b9f88a1a21c835e35b22337236b35b Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/1000yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/100yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/100yen.png new file mode 100644 index 0000000000000000000000000000000000000000..8911543af97cfcb508d872dcb8a7f60c1a8ce418 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/100yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/10yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/10yen.png new file mode 100644 index 0000000000000000000000000000000000000000..041f7738911152366ffb47a52f055441f300044c Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/10yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/1yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/1yen.png new file mode 100644 index 0000000000000000000000000000000000000000..cc6dcfd7401f08ef490477ad7e9e47c8c698e433 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/1yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/2000yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/2000yen.png new file mode 100644 index 0000000000000000000000000000000000000000..6048b7c99634522d6b457fcae49701489f12e20e Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/2000yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/5000yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/5000yen.png new file mode 100644 index 0000000000000000000000000000000000000000..b0fe26db1123a7dbd2c27f89dbb513037ebb9f0d Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/5000yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/500yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/500yen.png new file mode 100644 index 0000000000000000000000000000000000000000..9e3d2b766b84363b460c437524f001f52d3a0b9f Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/500yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/50yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/50yen.png new file mode 100644 index 0000000000000000000000000000000000000000..c8ef08997249d67c0d40e518bc35b7d02b006269 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/50yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/5yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/5yen.png new file mode 100644 index 0000000000000000000000000000000000000000..b120bdca36a3bbc433a3d551763d9fd062e206f1 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/5yen.png differ diff --git a/packages/frontend/assets/oneko.gif b/packages/frontend/assets/oneko.gif new file mode 100644 index 0000000000000000000000000000000000000000..a009c2cc19c96b001ac76e96f27e5fa1a9e56577 Binary files /dev/null and b/packages/frontend/assets/oneko.gif differ diff --git a/packages/frontend/assets/reversi/logo.png b/packages/frontend/assets/reversi/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..724a311ea120479ffc4dd6e4f7165dee3ac616f2 Binary files /dev/null and b/packages/frontend/assets/reversi/logo.png differ diff --git a/packages/frontend/assets/reversi/lose.mp3 b/packages/frontend/assets/reversi/lose.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..b62d50baf778bfe2beaf879ad10cd46433c50164 Binary files /dev/null and b/packages/frontend/assets/reversi/lose.mp3 differ diff --git a/packages/frontend/assets/reversi/matched.mp3 b/packages/frontend/assets/reversi/matched.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f26d07614edd8a32a5fb0866a35aef482cbedc40 Binary files /dev/null and b/packages/frontend/assets/reversi/matched.mp3 differ diff --git a/packages/frontend/assets/reversi/put.mp3 b/packages/frontend/assets/reversi/put.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..baa1b83195434d3af77ecf04b6d81c22c8fd2109 Binary files /dev/null and b/packages/frontend/assets/reversi/put.mp3 differ diff --git a/packages/frontend/assets/reversi/stone_b.png b/packages/frontend/assets/reversi/stone_b.png new file mode 100644 index 0000000000000000000000000000000000000000..9e98455a3ec345f0c52893f66f857c000dc8db79 Binary files /dev/null and b/packages/frontend/assets/reversi/stone_b.png differ diff --git a/packages/frontend/assets/reversi/stone_w.png b/packages/frontend/assets/reversi/stone_w.png new file mode 100644 index 0000000000000000000000000000000000000000..f2bee593dc8104bffd24bd7fa0d0be3744b6349c Binary files /dev/null and b/packages/frontend/assets/reversi/stone_w.png differ diff --git a/packages/frontend/assets/reversi/win.mp3 b/packages/frontend/assets/reversi/win.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..25402ce2a683a7591017d9c6edaee9c84b4ad4ff Binary files /dev/null and b/packages/frontend/assets/reversi/win.mp3 differ diff --git a/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts b/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts index d78f05439583799ed0d1139ec4318500cd51fc7c..948cc8b2c91dc01041ba2df38f957fe2617b845f 100644 --- a/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts +++ b/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.ts b/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.ts index 68cdc0bc78637305bc113d0603d2aa5c8c3ac4e0..0ed2e14d2a81e896070f699e5515de764f6d4203 100644 --- a/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.ts +++ b/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 68aa501c8425af05ce7ccf587c10c0f86ce3218c..72b26961c3794ed8f24129e57b7d8b21d99e326b 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -4,11 +4,11 @@ "type": "module", "scripts": { "watch": "vite", - "dev": "vite --config vite.config.local-dev.ts", + "dev": "vite --config vite.config.local-dev.ts --debug hmr", "build": "vite build", "storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"", "build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js", - "build-storybook": "pnpm build-storybook-pre && storybook build", + "build-storybook": "pnpm build-storybook-pre && storybook build --webpack-stats-json storybook-static", "chromatic": "chromatic", "test": "vitest --run --globals", "test-and-coverage": "vitest --run --coverage --globals", @@ -19,120 +19,124 @@ "dependencies": { "@discordapp/twemoji": "15.0.2", "@github/webauthn-json": "2.1.1", + "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", + "@misskey-dev/browser-image-resizer": "2024.1.0", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-replace": "5.0.5", "@rollup/pluginutils": "5.1.0", - "@sharkey/sfm-js": "0.24.3", - "@syuilo/aiscript": "0.16.0", + "@transfem-org/sfm-js": "0.24.4", + "@syuilo/aiscript": "0.17.0", "@phosphor-icons/web": "^2.0.3", "@twemoji/parser": "15.0.0", - "@vitejs/plugin-vue": "4.5.2", - "@vue/compiler-sfc": "3.3.12", - "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6", + "@vitejs/plugin-vue": "5.0.4", + "@vue/compiler-sfc": "3.4.21", + "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2", "astring": "1.8.6", "broadcast-channel": "7.0.0", - "browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3", "buraha": "0.0.1", - "canvas-confetti": "1.6.1", - "chart.js": "4.4.1", + "canvas-confetti": "1.9.2", + "chart.js": "4.4.2", "chartjs-adapter-date-fns": "3.0.0", "chartjs-chart-matrix": "2.0.1", "chartjs-plugin-gradient": "0.6.1", "chartjs-plugin-zoom": "2.0.1", - "chromatic": "10.1.0", + "chromatic": "11.0.0", "compare-versions": "6.1.0", "cropperjs": "2.0.0-beta.4", "date-fns": "2.30.0", "escape-regexp": "0.0.1", "estree-walker": "3.0.3", "eventemitter3": "5.0.1", - "gsap": "3.12.4", "idb-keyval": "6.2.1", "insert-text-at-cursor": "0.3.0", "is-file-animated": "1.0.2", "json5": "2.2.3", "katex": "0.16.9", "matter-js": "0.19.0", + "misskey-bubble-game": "workspace:*", "misskey-js": "workspace:*", + "misskey-reversi": "workspace:*", "photoswipe": "5.4.3", "punycode": "2.3.1", - "rollup": "4.9.1", - "sanitize-html": "2.11.0", - "sass": "1.69.5", - "shiki": "0.14.7", + "rollup": "4.12.0", + "sanitize-html": "2.12.1", + "sass": "1.71.1", + "shiki": "1.1.7", "strict-event-emitter-types": "2.0.0", "textarea-caret": "3.1.0", - "three": "0.159.0", + "three": "0.162.0", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tsc-alias": "1.8.8", "tsconfig-paths": "4.2.0", "typescript": "5.3.3", "uuid": "9.0.1", - "v-code-diff": "1.7.2", - "vite": "5.0.10", - "vue": "3.3.12", + "v-code-diff": "1.9.0", + "vite": "5.1.4", + "vue": "3.4.21", "vuedraggable": "next" }, "devDependencies": { - "@storybook/addon-actions": "7.6.5", - "@storybook/addon-essentials": "7.6.5", - "@storybook/addon-interactions": "7.6.5", - "@storybook/addon-links": "7.6.5", - "@storybook/addon-storysource": "7.6.5", - "@storybook/addons": "7.6.5", - "@storybook/blocks": "7.6.5", - "@storybook/core-events": "7.6.5", - "@storybook/jest": "0.2.3", - "@storybook/manager-api": "7.6.5", - "@storybook/preview-api": "7.6.5", - "@storybook/react": "7.6.5", - "@storybook/react-vite": "7.6.5", - "@storybook/testing-library": "0.2.2", - "@storybook/theming": "7.6.5", - "@storybook/types": "7.6.5", - "@storybook/vue3": "7.6.5", - "@storybook/vue3-vite": "7.6.5", - "@testing-library/vue": "8.0.1", + "@misskey-dev/eslint-plugin": "1.0.0", + "@misskey-dev/summaly": "5.0.3", + "@storybook/addon-actions": "8.0.0-beta.6", + "@storybook/addon-essentials": "8.0.0-beta.6", + "@storybook/addon-interactions": "8.0.0-beta.6", + "@storybook/addon-links": "8.0.0-beta.6", + "@storybook/addon-mdx-gfm": "8.0.0-beta.6", + "@storybook/addon-storysource": "8.0.0-beta.6", + "@storybook/blocks": "8.0.0-beta.6", + "@storybook/components": "8.0.0-beta.6", + "@storybook/core-events": "8.0.0-beta.6", + "@storybook/manager-api": "8.0.0-beta.6", + "@storybook/preview-api": "8.0.0-beta.6", + "@storybook/react": "8.0.0-beta.6", + "@storybook/react-vite": "8.0.0-beta.6", + "@storybook/test": "8.0.0-beta.6", + "@storybook/theming": "8.0.0-beta.6", + "@storybook/types": "8.0.0-beta.6", + "@storybook/vue3": "8.0.0-beta.6", + "@storybook/vue3-vite": "8.0.0-beta.6", + "@testing-library/vue": "8.0.2", "@types/escape-regexp": "0.0.3", "@types/estree": "1.0.5", - "@types/matter-js": "0.19.5", + "@types/matter-js": "0.19.6", "@types/micromatch": "4.0.6", - "@types/node": "20.10.5", - "@types/punycode": "2.1.3", - "@types/sanitize-html": "2.9.5", + "@types/node": "20.11.22", + "@types/punycode": "2.1.4", + "@types/sanitize-html": "2.11.0", "@types/throttle-debounce": "5.0.2", "@types/tinycolor2": "1.4.6", - "@types/uuid": "9.0.7", + "@types/uuid": "9.0.8", "@types/ws": "8.5.10", - "@typescript-eslint/eslint-plugin": "6.14.0", - "@typescript-eslint/parser": "6.14.0", + "@typescript-eslint/eslint-plugin": "7.1.0", + "@typescript-eslint/parser": "7.1.0", "@vitest/coverage-v8": "0.34.6", - "@vue/runtime-core": "3.3.12", - "acorn": "8.11.2", + "@vue/runtime-core": "3.4.21", + "acorn": "8.11.3", "cross-env": "7.0.3", - "cypress": "13.6.1", - "eslint": "8.56.0", + "cypress": "13.6.6", + "eslint": "8.57.0", "eslint-plugin-import": "2.29.1", - "eslint-plugin-vue": "9.19.2", + "eslint-plugin-vue": "9.22.0", "fast-glob": "3.3.2", - "happy-dom": "10.0.3", + "happy-dom": "13.6.2", "intersection-observer": "0.12.2", "micromatch": "4.0.5", - "msw": "1.3.2", - "msw-storybook-addon": "1.10.0", - "nodemon": "3.0.2", - "prettier": "3.1.1", + "msw": "2.1.7", + "msw-storybook-addon": "2.0.0-beta.1", + "nodemon": "3.1.0", + "prettier": "3.2.5", "react": "18.2.0", "react-dom": "18.2.0", "start-server-and-test": "2.0.3", - "storybook": "7.6.5", + "storybook": "8.0.0-beta.6", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", - "summaly": "github:misskey-dev/summaly", "vite-plugin-turbosnap": "1.0.3", "vitest": "0.34.6", "vitest-fetch-mock": "0.2.2", - "vue-eslint-parser": "9.3.2", - "vue-tsc": "1.8.25" + "vue-component-type-helpers": "1.8.27", + "vue-eslint-parser": "9.4.2", + "vue-tsc": "1.8.27" } } diff --git a/packages/frontend/public/mockServiceWorker.js b/packages/frontend/public/mockServiceWorker.js index 5384ce6b94952673c09cdc1e52a8d00d0603bdb3..3bb1e669106d48b7034782e34e8acedbbfb63d5d 100644 --- a/packages/frontend/public/mockServiceWorker.js +++ b/packages/frontend/public/mockServiceWorker.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/_boot_.ts b/packages/frontend/src/_boot_.ts index efb78fe447b19abf2f541135750175d9be6299c4..875353f8a482f984ca8855461ec387f8f812f9d8 100644 --- a/packages/frontend/src/_boot_.ts +++ b/packages/frontend/src/_boot_.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/_dev_boot_.ts b/packages/frontend/src/_dev_boot_.ts index d419ade5274379edcaa702189fb25116a130db3e..09495dece4c45eddc926b6da3856a03766183324 100644 --- a/packages/frontend/src/_dev_boot_.ts +++ b/packages/frontend/src/_dev_boot_.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts index 05008194f04774ad75409e23ac7c27102137bd0a..171826c9d8be2766b2ce471e57388a3e81fb1c82 100644 --- a/packages/frontend/src/account.ts +++ b/packages/frontend/src/account.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -11,7 +11,8 @@ import { miLocalStorage } from '@/local-storage.js'; import { MenuButton } from '@/types/menu.js'; import { del, get, set } from '@/scripts/idb-proxy.js'; import { apiUrl } from '@/config.js'; -import { waiting, api, popup, popupMenu, success, alert } from '@/os.js'; +import { waiting, popup, popupMenu, success, alert } from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { unisonReload, reloadChannel } from '@/scripts/unison-reload.js'; // TODO: ä»–ã®ã‚¿ãƒ–ã¨æ°¸ç¶šåŒ–ã•ã‚ŒãŸstateã‚’åŒæœŸ @@ -23,9 +24,14 @@ const accountData = miLocalStorage.getItem('account'); // TODO: 外部ã‹ã‚‰ã¯readonlyã« export const $i = accountData ? reactive(JSON.parse(accountData) as Account) : null; -export const iAmModerator = $i != null && ($i.isAdmin || $i.isModerator); +export const iAmModerator = $i != null && ($i.isAdmin === true || $i.isModerator === true); export const iAmAdmin = $i != null && $i.isAdmin; +export function signinRequired() { + if ($i == null) throw new Error('signin required'); + return $i; +} + export let notesCount = $i == null ? 0 : $i.notesCount; export function incNotesCount() { notesCount++; @@ -246,7 +252,7 @@ export async function openAccountMenu(opts: { } const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== $i.id)); - const accountsPromise = api('users/show', { userIds: storedAccounts.map(x => x.id) }); + const accountsPromise = misskeyApi('users/show', { userIds: storedAccounts.map(x => x.id) }); function createItem(account: Misskey.entities.UserDetailed) { return { @@ -284,7 +290,7 @@ export async function openAccountMenu(opts: { text: i18n.ts.profile, to: `/@${ $i.username }`, avatar: $i, - }, { type: 'divider' }, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, { + }, { type: 'divider' as const }, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, { type: 'parent' as const, icon: 'ph-plus ph-bold ph-lg', text: i18n.ts.addAccount, diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts index 63f169d9adc1d6a729d025e270bec2f1ec148417..9694a5b6271e001c5871b41218aa1619553cd634 100644 --- a/packages/frontend/src/boot/common.ts +++ b/packages/frontend/src/boot/common.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -22,6 +22,7 @@ import { getAccountFromId } from '@/scripts/get-account-from-id.js'; import { deckStore } from '@/ui/deck/deck-store.js'; import { miLocalStorage } from '@/local-storage.js'; import { fetchCustomEmojis } from '@/custom-emojis.js'; +import { setupRouter } from '@/router/definition.js'; export async function common(createVue: () => App<Element>) { console.info(`Sharkey v${version}`); @@ -59,12 +60,6 @@ export async function common(createVue: () => App<Element>) { }); } - const splash = document.getElementById('splash'); - // 念ã®ãŸã‚nullãƒã‚§ãƒƒã‚¯(HTMLãŒå¤ã„å ´åˆãŒã‚ã‚‹ãŸã‚(ãã®ã†ã¡æ¶ˆã™)) - if (splash) splash.addEventListener('transitionend', () => { - splash.remove(); - }); - let isClientUpdated = false; //#region クライアントãŒæ›´æ–°ã•ã‚ŒãŸã‹ãƒã‚§ãƒƒã‚¯ @@ -245,6 +240,8 @@ export async function common(createVue: () => App<Element>) { const app = createVue(); + setupRouter(app); + if (_DEV_) { app.config.performance = true; } @@ -290,5 +287,10 @@ function removeSplash() { if (splash) { splash.style.opacity = '0'; splash.style.pointerEvents = 'none'; + + // transitionendイベントãŒç™ºç«ã—ãªã„å ´åˆãŒã‚ã‚‹ãŸã‚ + window.setTimeout(() => { + splash.remove(); + }, 1000); } } diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index cdc1d11ca2967279ba32ae48897175295846f6cc..fbb4baebdc728666997c298eec0bdd0f901efc24 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -1,25 +1,26 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import { createApp, markRaw, defineAsyncComponent } from 'vue'; +import { createApp, defineAsyncComponent, markRaw } from 'vue'; import { common } from './common.js'; import { ui } from '@/config.js'; import { i18n } from '@/i18n.js'; -import { confirm, alert, post, popup, toast } from '@/os.js'; +import { alert, confirm, popup, post, toast } from '@/os.js'; import { useStream } from '@/stream.js'; import * as sound from '@/scripts/sound.js'; -import { $i, updateAccount, signout } from '@/account.js'; -import { defaultStore, ColdDeviceStorage } from '@/store.js'; +import { $i, signout, updateAccount } from '@/account.js'; +import { instance } from '@/instance.js'; +import { ColdDeviceStorage, defaultStore } from '@/store.js'; import { makeHotkey } from '@/scripts/hotkey.js'; import { reactionPicker } from '@/scripts/reaction-picker.js'; import { miLocalStorage } from '@/local-storage.js'; import { claimAchievement, claimedAchievements } from '@/scripts/achievements.js'; -import { mainRouter } from '@/router.js'; import { initializeSw } from '@/scripts/initialize-sw.js'; import { deckStore } from '@/ui/deck/deck-store.js'; import { emojiPicker } from '@/scripts/emoji-picker.js'; +import { mainRouter } from '@/router/main.js'; export async function mainBoot() { const { isClientUpdated } = await common(() => createApp( @@ -75,9 +76,23 @@ export async function mainBoot() { if (defaultStore.state.enableSeasonalScreenEffect) { const month = new Date().getMonth() + 1; - if (month === 12 || month === 1) { - const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; - new SnowfallEffect().render(); + if (defaultStore.state.hemisphere === 'S') { + // â–¼å—åŠçƒ + if (month === 7 || month === 8) { + const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; + new SnowfallEffect({}).render(); + } + } else { + // ▼北åŠçƒ + if (month === 12 || month === 1) { + const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; + new SnowfallEffect({}).render(); + } else if (month === 3 || month === 4) { + const SakuraEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; + new SakuraEffect({ + sakura: true, + }).render(); + } } } @@ -203,7 +218,7 @@ export async function mainBoot() { const lastUsedDate = parseInt(lastUsed, 10); // 二時間以上å‰ãªã‚‰ if (Date.now() - lastUsedDate > 1000 * 60 * 60 * 2) { - toast(i18n.t('welcomeBackWithName', { + toast(i18n.tsx.welcomeBackWithName({ name: $i.name || $i.username, })); } @@ -218,6 +233,11 @@ export async function mainBoot() { } } + const modifiedVersionMustProminentlyOfferInAgplV3Section13Read = miLocalStorage.getItem('modifiedVersionMustProminentlyOfferInAgplV3Section13Read'); + if (modifiedVersionMustProminentlyOfferInAgplV3Section13Read !== 'true' && instance.repositoryUrl !== 'https://activitypub.software/TransFem-org/Sharkey/') { + popup(defineAsyncComponent(() => import('@/components/MkSourceCodeAvailablePopup.vue')), {}, {}, 'closed'); + } + if ('Notification' in window) { // 許å¯ã‚’å¾—ã¦ã„ãªã‹ã£ãŸã‚‰ãƒªã‚¯ã‚¨ã‚¹ãƒˆ if (Notification.permission === 'default') { @@ -269,7 +289,7 @@ export async function mainBoot() { main.on('unreadAntenna', () => { updateAccount({ hasUnreadAntenna: true }); - sound.play('antenna'); + sound.playMisskeySfx('antenna'); }); main.on('readAllAnnouncements', () => { diff --git a/packages/frontend/src/boot/sub-boot.ts b/packages/frontend/src/boot/sub-boot.ts index 92ee074afb9adfc2a4fd6f085fd7af216c88c8ab..017457822bc6d8feced606440a07b13151ba73df 100644 --- a/packages/frontend/src/boot/sub-boot.ts +++ b/packages/frontend/src/boot/sub-boot.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/cache.ts b/packages/frontend/src/cache.ts index 25d2b3c15f3f56dff77abd84a42075ff1dc1fea6..b286528de6c9f91bfbbb3aa0148585198dc1ca28 100644 --- a/packages/frontend/src/cache.ts +++ b/packages/frontend/src/cache.ts @@ -1,13 +1,13 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import * as Misskey from 'misskey-js'; import { Cache } from '@/scripts/cache.js'; -import { api } from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; -export const clipsCache = new Cache<Misskey.entities.Clip[]>(1000 * 60 * 30, () => api('clips/list')); -export const rolesCache = new Cache(1000 * 60 * 30, () => api('admin/roles/list')); -export const userListsCache = new Cache<Misskey.entities.UserList[]>(1000 * 60 * 30, () => api('users/lists/list')); -export const antennasCache = new Cache<Misskey.entities.Antenna[]>(1000 * 60 * 30, () => api('antennas/list')); +export const clipsCache = new Cache<Misskey.entities.Clip[]>(1000 * 60 * 30, () => misskeyApi('clips/list')); +export const rolesCache = new Cache(1000 * 60 * 30, () => misskeyApi('admin/roles/list')); +export const userListsCache = new Cache<Misskey.entities.UserList[]>(1000 * 60 * 30, () => misskeyApi('users/lists/list')); +export const antennasCache = new Cache<Misskey.entities.Antenna[]>(1000 * 60 * 30, () => misskeyApi('antennas/list')); diff --git a/packages/frontend/src/components/MkAbuseReport.stories.impl.ts b/packages/frontend/src/components/MkAbuseReport.stories.impl.ts index 77e7c84d5c9cf65fc7516bb25b281f990778bb65..cf09c96fd410a89c977dde8ed1b1ea1ef9df0f93 100644 --- a/packages/frontend/src/components/MkAbuseReport.stories.impl.ts +++ b/packages/frontend/src/components/MkAbuseReport.stories.impl.ts @@ -1,12 +1,12 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { action } from '@storybook/addon-actions'; import { StoryObj } from '@storybook/vue3'; -import { rest } from 'msw'; +import { HttpResponse, http } from 'msw'; import { abuseUserReport } from '../../.storybook/fakes.js'; import { commonHandlers } from '../../.storybook/mocks.js'; import MkAbuseReport from './MkAbuseReport.vue'; @@ -44,9 +44,9 @@ export const Default = { msw: { handlers: [ ...commonHandlers, - rest.post('/api/admin/resolve-abuse-user-report', async (req, res, ctx) => { - action('POST /api/admin/resolve-abuse-user-report')(await req.json()); - return res(ctx.json({})); + http.post('/api/admin/resolve-abuse-user-report', async ({ request }) => { + action('POST /api/admin/resolve-abuse-user-report')(await request.json()); + return HttpResponse.json({}); }), ], }, diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue index 611c8a1782331d2ab956cfa1385f8ba98418f19a..0493e885b9db853828b88f8bffa833b514060488 100644 --- a/packages/frontend/src/components/MkAbuseReport.vue +++ b/packages/frontend/src/components/MkAbuseReport.vue @@ -1,12 +1,12 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <div class="bcekxzvu _margin _panel"> <div class="target"> - <MkA v-user-preview="report.targetUserId" class="info" :to="`/admin/user/${report.targetUserId}`"> + <MkA v-user-preview="report.targetUserId" class="info" :to="`/admin/user/${report.targetUserId}`" :behavior="'window'"> <MkAvatar class="avatar" :user="report.targetUser" indicator/> <div class="names"> <MkUserName class="name" :user="report.targetUser"/> @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only <Mfm :text="report.comment"/> </div> <hr/> - <div>{{ i18n.ts.reporter }}: <MkA :to="`/admin/user/${report.reporter.id}`" class="_link">@{{ report.reporter.username }}</MkA></div> + <div>{{ i18n.ts.reporter }}: <MkA :to="`/admin/user/${report.reporter.id}`" class="_link" :behavior="'window'">@{{ report.reporter.username }}</MkA></div> <div v-if="report.assignee"> {{ i18n.ts.moderator }}: <MkAcct :user="report.assignee"/> diff --git a/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts b/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts index dc842b3d1bd2b81460c7272ceb754f6c193fcb82..9df957f3ec8d2d57512b7bff456263c4ea089fda 100644 --- a/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts +++ b/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts @@ -1,12 +1,12 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { action } from '@storybook/addon-actions'; import { StoryObj } from '@storybook/vue3'; -import { rest } from 'msw'; +import { HttpResponse, http } from 'msw'; import { userDetailed } from '../../.storybook/fakes.js'; import { commonHandlers } from '../../.storybook/mocks.js'; import MkAbuseReportWindow from './MkAbuseReportWindow.vue'; @@ -44,9 +44,9 @@ export const Default = { msw: { handlers: [ ...commonHandlers, - rest.post('/api/users/report-abuse', async (req, res, ctx) => { - action('POST /api/users/report-abuse')(await req.json()); - return res(ctx.json({})); + http.post('/api/users/report-abuse', async ({ request }) => { + action('POST /api/users/report-abuse')(await request.json()); + return HttpResponse.json({}); }), ], }, diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue index 6819630b74c3592bf913da7e1d04c97f7f0c14a7..f228df85a64bbb1d43adeff23db475bd261d096f 100644 --- a/packages/frontend/src/components/MkAbuseReportWindow.vue +++ b/packages/frontend/src/components/MkAbuseReportWindow.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -39,7 +39,7 @@ import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ - user: Misskey.entities.User; + user: Misskey.entities.UserDetailed; initialComment?: string; }>(); diff --git a/packages/frontend/src/components/MkAccountMoved.stories.impl.ts b/packages/frontend/src/components/MkAccountMoved.stories.impl.ts index 33c6c24631c88c41e1e61cedc83d6bea659c1370..f1cfdc157a840675006ab2aebc9afb13ddef47a5 100644 --- a/packages/frontend/src/components/MkAccountMoved.stories.impl.ts +++ b/packages/frontend/src/components/MkAccountMoved.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/MkAccountMoved.vue b/packages/frontend/src/components/MkAccountMoved.vue index b11cf1c8a0fb5afc79594f4cdee26bc8b7170e4d..83283a70736a76b6ba33f8f9a7cc22af205a09c2 100644 --- a/packages/frontend/src/components/MkAccountMoved.vue +++ b/packages/frontend/src/components/MkAccountMoved.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -17,7 +17,7 @@ import * as Misskey from 'misskey-js'; import MkMention from './MkMention.vue'; import { i18n } from '@/i18n.js'; import { host as localHost } from '@/config.js'; -import { api } from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const user = ref<Misskey.entities.UserLite>(); @@ -25,7 +25,7 @@ const props = defineProps<{ movedTo: string; // user id }>(); -api('users/show', { userId: props.movedTo }).then(u => user.value = u); +misskeyApi('users/show', { userId: props.movedTo }).then(u => user.value = u); </script> <style lang="scss" module> diff --git a/packages/frontend/src/components/MkAchievements.stories.impl.ts b/packages/frontend/src/components/MkAchievements.stories.impl.ts index 6d972467b153d178f7262a7b97437a9874a86f7c..7614da51da9cb49ccd4135d92cf1b1584c97e300 100644 --- a/packages/frontend/src/components/MkAchievements.stories.impl.ts +++ b/packages/frontend/src/components/MkAchievements.stories.impl.ts @@ -1,11 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { StoryObj } from '@storybook/vue3'; -import { rest } from 'msw'; +import { HttpResponse, http } from 'msw'; import { userDetailed } from '../../.storybook/fakes.js'; import { commonHandlers } from '../../.storybook/mocks.js'; import MkAchievements from './MkAchievements.vue'; @@ -39,8 +39,8 @@ export const Empty = { msw: { handlers: [ ...commonHandlers, - rest.post('/api/users/achievements', (req, res, ctx) => { - return res(ctx.json([])); + http.post('/api/users/achievements', () => { + return HttpResponse.json([]); }), ], }, @@ -52,8 +52,8 @@ export const All = { msw: { handlers: [ ...commonHandlers, - rest.post('/api/users/achievements', (req, res, ctx) => { - return res(ctx.json(ACHIEVEMENT_TYPES.map((name) => ({ name, unlockedAt: 0 })))); + http.post('/api/users/achievements', () => { + return HttpResponse.json(ACHIEVEMENT_TYPES.map((name) => ({ name, unlockedAt: 0 }))); }), ], }, diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue index cdd9cb87b12c0debda170e14b6b61d6d41711594..8ec3ec0505ffcffff1d0b6c592bc39da353b3c1f 100644 --- a/packages/frontend/src/components/MkAchievements.vue +++ b/packages/frontend/src/components/MkAchievements.vue @@ -1,12 +1,12 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <div> <div v-if="achievements" :class="$style.root"> - <div v-for="achievement in achievements" :key="achievement" :class="$style.achievement" class="_panel"> + <div v-for="achievement in achievements" :key="achievement.name" :class="$style.achievement" class="_panel"> <div :class="$style.icon"> <div :class="[$style.iconFrame, { @@ -55,6 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only import * as Misskey from 'misskey-js'; import { onMounted, ref, computed } from 'vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements.js'; @@ -71,7 +72,7 @@ const achievements = ref<Misskey.entities.UsersAchievementsResponse | null>(null const lockedAchievements = computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements.value ?? []).some(a => a.name === x))); function fetch() { - os.api('users/achievements', { userId: props.user.id }).then(res => { + misskeyApi('users/achievements', { userId: props.user.id }).then(res => { achievements.value = []; for (const t of ACHIEVEMENT_TYPES) { const a = res.find(x => x.name === t); @@ -120,8 +121,8 @@ onMounted(() => { .iconFrame { position: relative; - width: 58px; - height: 58px; + width: var(--avatar); + height: var(--avatar); padding: 6px; border-radius: var(--radius-full); box-sizing: border-box; diff --git a/packages/frontend/src/components/MkAnalogClock.stories.impl.ts b/packages/frontend/src/components/MkAnalogClock.stories.impl.ts index f87ad30f9b853dcbedc950db9aa636afe1ac5595..270ca408255d143f18bd9abc7b68b194d345b9e2 100644 --- a/packages/frontend/src/components/MkAnalogClock.stories.impl.ts +++ b/packages/frontend/src/components/MkAnalogClock.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/MkAnalogClock.vue b/packages/frontend/src/components/MkAnalogClock.vue index 0e252f7b1dbf0996dd488d4ec3e2d1afbce88bff..835efbd6cd71e334bd2224b4fc48287ed577c6c1 100644 --- a/packages/frontend/src/components/MkAnalogClock.vue +++ b/packages/frontend/src/components/MkAnalogClock.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkAnimBg.vue b/packages/frontend/src/components/MkAnimBg.vue index 284ee8f3f81c0d9721c5db0aa07d6b24401c6b5a..4bf6125af5efbe7a66561541f57a4de0310729d3 100644 --- a/packages/frontend/src/components/MkAnimBg.vue +++ b/packages/frontend/src/components/MkAnimBg.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts b/packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts index 42cfb90f7c1d4b57cea50342da32deb8d48a57a7..ffa4e56f5f265a2399ef50fe04e9286bbfe08dec 100644 --- a/packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts +++ b/packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/MkAnnouncementDialog.vue b/packages/frontend/src/components/MkAnnouncementDialog.vue index 4c6e3e693adcbfc08d630cbd8efbd85cdeee3542..74d0e7214fdecb6d667233f1d5127559e65cc272 100644 --- a/packages/frontend/src/components/MkAnnouncementDialog.vue +++ b/packages/frontend/src/components/MkAnnouncementDialog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -25,6 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { onMounted, shallowRef } from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import MkModal from '@/components/MkModal.vue'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; @@ -43,20 +44,20 @@ async function ok() { const confirm = await os.confirm({ type: 'question', title: i18n.ts._announcement.readConfirmTitle, - text: i18n.t('_announcement.readConfirmText', { title: props.announcement.title }), + text: i18n.tsx._announcement.readConfirmText({ title: props.announcement.title }), }); if (confirm.canceled) return; } - modal.value.close(); - os.api('i/read-announcement', { announcementId: props.announcement.id }); + modal.value?.close(); + misskeyApi('i/read-announcement', { announcementId: props.announcement.id }); updateAccount({ unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== props.announcement.id), }); } function onBgClick() { - rootEl.value.animate([{ + rootEl.value?.animate([{ offset: 0, transform: 'scale(1)', }, { diff --git a/packages/frontend/src/components/MkAsUi.stories.impl.ts b/packages/frontend/src/components/MkAsUi.stories.impl.ts index 564fa902baae72995982655441d6cb7074077288..cf8d5483b9523a789238fabfe323daa3d724da0c 100644 --- a/packages/frontend/src/components/MkAsUi.stories.impl.ts +++ b/packages/frontend/src/components/MkAsUi.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue index 8475233dfaa92f68291ceca623af1165a8876e2c..11f454daa24bb1acd2b99c2855a86703b3c3b509 100644 --- a/packages/frontend/src/components/MkAsUi.vue +++ b/packages/frontend/src/components/MkAsUi.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -10,8 +10,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkAsUi v-if="!g(child).hidden" :component="g(child)" :components="props.components" :size="size"/> </template> </div> - <span v-else-if="c.type === 'text'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }">{{ c.text }}</span> - <Mfm v-else-if="c.type === 'mfm'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }" :text="c.text" @clickEv="c.onClickEv"/> + <span v-else-if="c.type === 'text'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : undefined, fontWeight: c.bold ? 'bold' : undefined, color: c.color }">{{ c.text }}</span> + <Mfm v-else-if="c.type === 'mfm'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }" :text="c.text ?? ''" @clickEv="c.onClickEv"/> <MkButton v-else-if="c.type === 'button'" :primary="c.primary" :rounded="c.rounded" :disabled="c.disabled" :small="size === 'small'" inline @click="c.onClick">{{ c.text }}</MkButton> <div v-else-if="c.type === 'buttons'" class="_buttons" :style="{ justifyContent: align }"> <MkButton v-for="button in c.buttons" :primary="button.primary" :rounded="button.rounded" :disabled="button.disabled" inline :small="size === 'small'" @click="button.onClick">{{ button.text }}</MkButton> @@ -20,19 +20,19 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-if="c.label" #label>{{ c.label }}</template> <template v-if="c.caption" #caption>{{ c.caption }}</template> </MkSwitch> - <MkTextarea v-else-if="c.type === 'textarea'" :modelValue="c.default" @update:modelValue="c.onInput"> + <MkTextarea v-else-if="c.type === 'textarea'" :modelValue="c.default ?? null" @update:modelValue="c.onInput"> <template v-if="c.label" #label>{{ c.label }}</template> <template v-if="c.caption" #caption>{{ c.caption }}</template> </MkTextarea> - <MkInput v-else-if="c.type === 'textInput'" :small="size === 'small'" :modelValue="c.default" @update:modelValue="c.onInput"> + <MkInput v-else-if="c.type === 'textInput'" :small="size === 'small'" :modelValue="c.default ?? null" @update:modelValue="c.onInput"> <template v-if="c.label" #label>{{ c.label }}</template> <template v-if="c.caption" #caption>{{ c.caption }}</template> </MkInput> - <MkInput v-else-if="c.type === 'numberInput'" :small="size === 'small'" :modelValue="c.default" type="number" @update:modelValue="c.onInput"> + <MkInput v-else-if="c.type === 'numberInput'" :small="size === 'small'" :modelValue="c.default ?? null" type="number" @update:modelValue="c.onInput"> <template v-if="c.label" #label>{{ c.label }}</template> <template v-if="c.caption" #caption>{{ c.caption }}</template> </MkInput> - <MkSelect v-else-if="c.type === 'select'" :small="size === 'small'" :modelValue="c.default" @update:modelValue="c.onChange"> + <MkSelect v-else-if="c.type === 'select'" :small="size === 'small'" :modelValue="c.default ?? null" @update:modelValue="c.onChange"> <template v-if="c.label" #label>{{ c.label }}</template> <template v-if="c.caption" #caption>{{ c.caption }}</template> <option v-for="item in c.items" :key="item.value" :value="item.value">{{ item.text }}</option> @@ -42,8 +42,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPostForm fixed :instant="true" - :initialText="c.form.text" - :initialCw="c.form.cw" + :initialText="c.form?.text" + :initialCw="c.form?.cw" /> </div> <MkFolder v-else-if="c.type === 'folder'" :defaultOpen="c.opened"> @@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkAsUi v-if="!g(child).hidden" :component="g(child)" :components="props.components" :size="size"/> </template> </MkFolder> - <div v-else-if="c.type === 'container'" :class="[$style.container, { [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }]" :style="{ textAlign: c.align ?? null, backgroundColor: c.bgColor ?? null, color: c.fgColor ?? null, borderWidth: c.borderWidth ? `${c.borderWidth}px` : 0, borderColor: c.borderColor ?? 'var(--divider)', padding: c.padding ? `${c.padding}px` : 0, borderRadius: c.rounded ? '8px' : 0 }"> + <div v-else-if="c.type === 'container'" :class="[$style.container, { [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }]" :style="{ textAlign: c.align, backgroundColor: c.bgColor, color: c.fgColor, borderWidth: c.borderWidth ? `${c.borderWidth}px` : 0, borderColor: c.borderColor ?? 'var(--divider)', padding: c.padding ? `${c.padding}px` : 0, borderRadius: c.rounded ? '8px' : 0 }"> <template v-for="child in c.children" :key="child"> <MkAsUi v-if="!g(child).hidden" :component="g(child)" :components="props.components" :size="size" :align="c.align"/> </template> @@ -68,7 +68,7 @@ import MkInput from '@/components/MkInput.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkTextarea from '@/components/MkTextarea.vue'; import MkSelect from '@/components/MkSelect.vue'; -import { AsUiComponent } from '@/scripts/aiscript/ui.js'; +import { AsUiComponent, AsUiRoot, AsUiPostFormButton } from '@/scripts/aiscript/ui.js'; import MkFolder from '@/components/MkFolder.vue'; import MkPostForm from '@/components/MkPostForm.vue'; @@ -85,20 +85,32 @@ const props = withDefaults(defineProps<{ const c = props.component; function g(id) { - return props.components.find(x => x.value.id === id).value; + const v = props.components.find(x => x.value.id === id)?.value; + if (v) return v; + + return { + id: 'dummy', + type: 'root', + children: [], + } as AsUiRoot; } -const valueForSwitch = ref(c.default ?? false); +const valueForSwitch = ref('default' in c && typeof c.default === 'boolean' ? c.default : false); function onSwitchUpdate(v) { valueForSwitch.value = v; - if (c.onChange) c.onChange(v); + if ('onChange' in c && c.onChange) { + c.onChange(v as never); + } } function openPostForm() { + const form = (c as AsUiPostFormButton).form; + if (!form) return; + os.post({ - initialText: c.form.text, - initialCw: c.form.cw, + initialText: form.text, + initialCw: form.cw, instant: true, }); } diff --git a/packages/frontend/src/components/MkAutocomplete.stories.impl.ts b/packages/frontend/src/components/MkAutocomplete.stories.impl.ts index 969519386fc9733da1fd23a485330aaa728b820a..ec24b8c24052eb5184c9c0e259791a7070230396 100644 --- a/packages/frontend/src/components/MkAutocomplete.stories.impl.ts +++ b/packages/frontend/src/components/MkAutocomplete.stories.impl.ts @@ -1,14 +1,13 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { action } from '@storybook/addon-actions'; -import { expect } from '@storybook/jest'; -import { userEvent, waitFor, within } from '@storybook/testing-library'; +import { expect, userEvent, waitFor, within } from '@storybook/test'; import { StoryObj } from '@storybook/vue3'; -import { rest } from 'msw'; +import { HttpResponse, http } from 'msw'; import { userDetailed } from '../../.storybook/fakes.js'; import { commonHandlers } from '../../.storybook/mocks.js'; import MkAutocomplete from './MkAutocomplete.vue'; @@ -99,11 +98,11 @@ export const User = { msw: { handlers: [ ...commonHandlers, - rest.post('/api/users/search-by-username-and-host', (req, res, ctx) => { - return res(ctx.json([ + http.post('/api/users/search-by-username-and-host', () => { + return HttpResponse.json([ userDetailed('44', 'mizuki', 'misskey-hub.net', 'Mizuki'), userDetailed('49', 'momoko', 'misskey-hub.net', 'Momoko'), - ])); + ]); }), ], }, @@ -132,12 +131,12 @@ export const Hashtag = { msw: { handlers: [ ...commonHandlers, - rest.post('/api/hashtags/search', (req, res, ctx) => { - return res(ctx.json([ + http.post('/api/hashtags/search', () => { + return HttpResponse.json([ '気象è¦å ±æ³¨æ„å ±', '気象è¦å ±', 'æ°—è±¡æƒ…å ±', - ])); + ]); }), ], }, diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 1f819cf601984f557be39564f889b58da9cef511..8b665bfacdc320afc74f9af065823fee36e7565b 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only </ol> <ol v-else-if="emojis.length > 0" ref="suggests" :class="$style.list"> <li v-for="emoji in emojis" :key="emoji.emoji" :class="$style.item" tabindex="-1" @click="complete(type, emoji.emoji)" @keydown="onKeydown"> - <MkCustomEmoji v-if="'isCustomEmoji' in emoji && emoji.isCustomEmoji" :name="emoji.emoji" :class="$style.emoji"/> + <MkCustomEmoji v-if="'isCustomEmoji' in emoji && emoji.isCustomEmoji" :name="emoji.emoji" :class="$style.emoji" :fallbackToImage="true"/> <MkEmoji v-else :emoji="emoji.emoji" :class="$style.emoji"/> <!-- eslint-disable-next-line vue/no-v-html --> <span v-if="q" :class="$style.emojiName" v-html="sanitizeHtml(emoji.name.replace(q, `<b>${q}</b>`))"></span> @@ -35,6 +35,11 @@ SPDX-License-Identifier: AGPL-3.0-only <span>{{ tag }}</span> </li> </ol> + <ol v-else-if="mfmParams.length > 0" ref="suggests" :class="$style.list"> + <li v-for="param in mfmParams" tabindex="-1" :class="$style.item" @click="complete(type, q.params.toSpliced(-1, 1, param).join(','))" @keydown="onKeydown"> + <span>{{ param }}</span> + </li> + </ol> </div> </template> @@ -42,33 +47,23 @@ SPDX-License-Identifier: AGPL-3.0-only import { markRaw, ref, shallowRef, computed, onUpdated, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'; import sanitizeHtml from 'sanitize-html'; import contains from '@/scripts/contains.js'; -import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base.js'; +import { char2twemojiFilePath, char2fluentEmojiFilePath, char2tossfaceFilePath } from '@/scripts/emoji-base.js'; import { acct } from '@/filters/user.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { emojilist, getEmojiName } from '@/scripts/emojilist.js'; import { i18n } from '@/i18n.js'; import { miLocalStorage } from '@/local-storage.js'; import { customEmojis } from '@/custom-emojis.js'; -import { MFM_TAGS } from '@/const.js'; - -type EmojiDef = { - emoji: string; - name: string; - url: string; - aliasOf?: string; -} | { - emoji: string; - name: string; - aliasOf?: string; - isCustomEmoji?: true; -}; +import { MFM_TAGS, MFM_PARAMS } from '@/const.js'; +import { searchEmoji, EmojiDef } from '@/scripts/search-emoji.js'; const lib = emojilist.filter(x => x.category !== 'flags'); const emojiDb = computed(() => { //#region Unicode Emoji - const char2path = defaultStore.reactiveState.emojiStyle.value === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath; + const char2path = defaultStore.reactiveState.emojiStyle.value === 'twemoji' ? char2twemojiFilePath : defaultStore.reactiveState.emojiStyle.value === 'tossface' ? char2tossfaceFilePath : char2fluentEmojiFilePath; const unicodeEmojiDB: EmojiDef[] = lib.map(x => ({ emoji: x.char, @@ -82,7 +77,7 @@ const emojiDb = computed(() => { unicodeEmojiDB.push({ emoji: emoji, name: k, - aliasOf: getEmojiName(emoji)!, + aliasOf: getEmojiName(emoji), url: char2path(emoji), }); } @@ -129,7 +124,7 @@ export default { <script lang="ts" setup> const props = defineProps<{ type: string; - q: string | null; + q: any; textarea: HTMLTextAreaElement; close: () => void; x: number; @@ -150,6 +145,7 @@ const hashtags = ref<any[]>([]); const emojis = ref<(EmojiDef)[]>([]); const items = ref<Element[] | HTMLCollection>([]); const mfmTags = ref<string[]>([]); +const mfmParams = ref<string[]>([]); const select = ref(-1); const zIndex = os.claimZIndex('high'); @@ -201,7 +197,7 @@ function exec() { users.value = JSON.parse(cache); fetching.value = false; } else { - os.api('users/search-by-username-and-host', { + misskeyApi('users/search-by-username-and-host', { username: props.q, limit: 10, detail: false, @@ -224,7 +220,7 @@ function exec() { hashtags.value = hashtags; fetching.value = false; } else { - os.api('hashtags/search', { + misskeyApi('hashtags/search', { query: props.q, limit: 30, }).then(searchedHashtags => { @@ -242,7 +238,7 @@ function exec() { return; } - emojis.value = emojiAutoComplete(props.q.toLowerCase(), emojiDb.value); + emojis.value = searchEmoji(props.q.toLowerCase(), emojiDb.value); } else if (props.type === 'mfmTag') { if (!props.q || props.q === '') { mfmTags.value = MFM_TAGS; @@ -250,79 +246,14 @@ function exec() { } mfmTags.value = MFM_TAGS.filter(tag => tag.startsWith(props.q ?? '')); - } -} - -type EmojiScore = { emoji: EmojiDef, score: number }; - -function emojiAutoComplete(query: string | null, emojiDb: EmojiDef[], max = 30): EmojiDef[] { - if (!query) { - return []; - } - - const matched = new Map<string, EmojiScore>(); - - // å‰æ–¹ä¸€è‡´ï¼ˆã‚¨ã‚¤ãƒªã‚¢ã‚¹ãªã—) - emojiDb.some(x => { - if (x.name.toLowerCase().startsWith(query) && !x.aliasOf) { - matched.set(x.name, { emoji: x, score: query.length + 1 }); - } - return matched.size === max; - }); - - // å‰æ–¹ä¸€è‡´ï¼ˆã‚¨ã‚¤ãƒªã‚¢ã‚¹è¾¼ã¿ï¼‰ - if (matched.size < max) { - emojiDb.some(x => { - if (x.name.toLowerCase().startsWith(query) && !matched.has(x.aliasOf ?? x.name)) { - matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length }); - } - return matched.size === max; - }); - } - - // 部分一致(エイリアス込ã¿ï¼‰ - if (matched.size < max) { - emojiDb.some(x => { - if (x.name.toLowerCase().includes(query) && !matched.has(x.aliasOf ?? x.name)) { - matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length - 1 }); - } - return matched.size === max; - }); - } - - // 簡易ã‚ã„ã¾ã„検索(3æ–‡å—以上) - if (matched.size < max && query.length > 3) { - const queryChars = [...query]; - const hitEmojis = new Map<string, EmojiScore>(); - - for (const x of emojiDb) { - // æ–‡å—列ã®ä½ç½®ã‚’進ã‚ãªãŒã‚‰ã€ã‚¯ã‚¨ãƒªã®æ–‡å—ã‚’é †ç•ªã«æŽ¢ã™ - - let pos = 0; - let hit = 0; - for (const c of queryChars) { - pos = x.name.toLowerCase().indexOf(c, pos); - if (pos <= -1) break; - hit++; - } - - // åŠåˆ†ä»¥ä¸Šã®æ–‡å—ãŒå«ã¾ã‚Œã¦ã„ã‚Œã°ãƒ’ットã¨ã™ã‚‹ - if (hit > Math.ceil(queryChars.length / 2) && hit - 2 > (matched.get(x.aliasOf ?? x.name)?.score ?? 0)) { - hitEmojis.set(x.aliasOf ?? x.name, { emoji: x, score: hit - 2 }); - } + } else if (props.type === 'mfmParam') { + if (props.q.params.at(-1) === '') { + mfmParams.value = MFM_PARAMS[props.q.tag] ?? []; + return; } - // ヒットã—ãŸã‚‚ã®ã‚’å…¨éƒ¨è¿½åŠ ã™ã‚‹ã¨é›‘多ã«ãªã‚‹ã®ã§ã€å…ˆé ã®6件程度ã ã‘ã«ã—ã¦ãŠã(6件ï¼ã‚ªãƒ¼ãƒˆã‚³ãƒ³ãƒ—リートã®ãƒãƒƒãƒ—アップã®ã‚µã‚¤ã‚ºåˆ†ï¼‰ - [...hitEmojis.values()] - .sort((x, y) => y.score - x.score) - .slice(0, 6) - .forEach(it => matched.set(it.emoji.name, it)); + mfmParams.value = MFM_PARAMS[props.q.tag].filter(param => param.startsWith(props.q.params.at(-1) ?? '')); } - - return [...matched.values()] - .sort((x, y) => y.score - x.score) - .slice(0, max) - .map(it => it.emoji); } function onMousedown(event: Event) { @@ -408,7 +339,7 @@ function applySelect() { function chooseUser() { props.close(); - os.selectUser().then(user => { + os.selectUser({ includeSelf: true }).then(user => { complete('user', user); props.textarea.focus(); }); diff --git a/packages/frontend/src/components/MkAvatars.stories.impl.ts b/packages/frontend/src/components/MkAvatars.stories.impl.ts index d41b64695fdcfb1fc14e0c364b26a7420096d310..d2a4a9f03bcdbfa918c69236a84c92ad76dff0f0 100644 --- a/packages/frontend/src/components/MkAvatars.stories.impl.ts +++ b/packages/frontend/src/components/MkAvatars.stories.impl.ts @@ -1,11 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { StoryObj } from '@storybook/vue3'; -import { rest } from 'msw'; +import { HttpResponse, http } from 'msw'; import { userDetailed } from '../../.storybook/fakes.js'; import { commonHandlers } from '../../.storybook/mocks.js'; import MkAvatars from './MkAvatars.vue'; @@ -38,12 +38,12 @@ export const Default = { msw: { handlers: [ ...commonHandlers, - rest.post('/api/users/show', (req, res, ctx) => { - return res(ctx.json([ + http.post('/api/users/show', () => { + return HttpResponse.json([ userDetailed('17'), userDetailed('20'), userDetailed('18'), - ])); + ]); }), ], }, diff --git a/packages/frontend/src/components/MkAvatars.vue b/packages/frontend/src/components/MkAvatars.vue index 5644a324cf60d21a74b8964ec786c997f73a2745..8236d0ddb94dfea399fcf19b5a4e4bb7973657a4 100644 --- a/packages/frontend/src/components/MkAvatars.vue +++ b/packages/frontend/src/components/MkAvatars.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onMounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const props = withDefaults(defineProps<{ userIds: string[]; @@ -27,7 +27,7 @@ const props = withDefaults(defineProps<{ const users = ref<Misskey.entities.UserLite[]>([]); onMounted(async () => { - users.value = await os.api('users/show', { + users.value = await misskeyApi('users/show', { userIds: props.userIds, }) as unknown as Misskey.entities.UserLite[]; }); diff --git a/packages/frontend/src/components/MkButton.stories.impl.ts b/packages/frontend/src/components/MkButton.stories.impl.ts index e852557b123dfe4bd9fd873ffb84ef85729be98c..e8802e4f8f9fa37fd0f252058dd6449bbe1dc0cb 100644 --- a/packages/frontend/src/components/MkButton.stories.impl.ts +++ b/packages/frontend/src/components/MkButton.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index 9fcc49d3f090898269f7147fc846da1282d9bc10..c0f41b64d0bb32fd1d0a00443d2d940e636518db 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkA v-else class="_button" :class="[$style.root, { [$style.inline]: inline, [$style.primary]: primary, [$style.gradate]: gradate, [$style.danger]: danger, [$style.rounded]: rounded, [$style.full]: full, [$style.small]: small, [$style.large]: large, [$style.transparent]: transparent, [$style.asLike]: asLike }]" - :to="to" + :to="to ?? '#'" @mousedown="onMousedown" > <div ref="ripples" :class="$style.ripples" :data-children-class="$style.ripple"></div> @@ -131,6 +131,10 @@ function onMousedown(evt: MouseEvent): void { box-sizing: border-box; transition: background 0.1s ease; + &:hover { + text-decoration: none; + } + &:not(:disabled):hover { background: var(--buttonHoverBg); } diff --git a/packages/frontend/src/components/MkCaptcha.stories.impl.ts b/packages/frontend/src/components/MkCaptcha.stories.impl.ts index fb50e50b187c784e589f522b638154dbb5a1eaf8..475257cc45595a8981faad828fa4ec486c96e00f 100644 --- a/packages/frontend/src/components/MkCaptcha.stories.impl.ts +++ b/packages/frontend/src/components/MkCaptcha.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue index 40bca11e647f142f6472c3588e404172d8ff107f..c64bb47e771b517edb383ffac5e22dc0894250bc 100644 --- a/packages/frontend/src/components/MkCaptcha.vue +++ b/packages/frontend/src/components/MkCaptcha.vue @@ -1,19 +1,22 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <div> - <span v-if="!available">{{ i18n.ts.waiting }}<MkEllipsis/></span> - <div ref="captchaEl"></div> + <span v-if="!available">Loading<MkEllipsis/></span> + <div v-if="props.provider == 'mcaptcha'"> + <div id="mcaptcha__widget-container" class="m-captcha-style"></div> + <div ref="captchaEl"></div> + </div> + <div v-else ref="captchaEl"></div> </div> </template> <script lang="ts" setup> -import { ref, shallowRef, computed, onMounted, onBeforeUnmount, watch } from 'vue'; +import { ref, shallowRef, computed, onMounted, onBeforeUnmount, watch, onUnmounted } from 'vue'; import { defaultStore } from '@/store.js'; -import { i18n } from '@/i18n.js'; // APIs provided by Captcha services export type Captcha = { @@ -26,7 +29,7 @@ export type Captcha = { getResponse(id: string): string; }; -export type CaptchaProvider = 'hcaptcha' | 'recaptcha' | 'turnstile'; +export type CaptchaProvider = 'hcaptcha' | 'recaptcha' | 'turnstile' | 'mcaptcha'; type CaptchaContainer = { readonly [_ in CaptchaProvider]?: Captcha; @@ -39,6 +42,7 @@ declare global { const props = defineProps<{ provider: CaptchaProvider; sitekey: string | null; // null will show error on request + instanceUrl?: string | null; modelValue?: string | null; }>(); @@ -55,6 +59,7 @@ const variable = computed(() => { case 'hcaptcha': return 'hcaptcha'; case 'recaptcha': return 'grecaptcha'; case 'turnstile': return 'turnstile'; + case 'mcaptcha': return 'mcaptcha'; } }); @@ -65,6 +70,7 @@ const src = computed(() => { case 'hcaptcha': return 'https://js.hcaptcha.com/1/api.js?render=explicit&recaptchacompat=off'; case 'recaptcha': return 'https://www.recaptcha.net/recaptcha/api.js?render=explicit'; case 'turnstile': return 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit'; + case 'mcaptcha': return null; } }); @@ -72,9 +78,9 @@ const scriptId = computed(() => `script-${props.provider}`); const captcha = computed<Captcha>(() => window[variable.value] || {} as unknown as Captcha); -if (loaded) { +if (loaded || props.provider === 'mcaptcha') { available.value = true; -} else { +} else if (src.value !== null) { (document.getElementById(scriptId.value) ?? document.head.appendChild(Object.assign(document.createElement('script'), { async: true, id: scriptId.value, @@ -87,7 +93,7 @@ function reset() { if (captcha.value.reset) captcha.value.reset(); } -function requestRender() { +async function requestRender() { if (captcha.value.render && captchaEl.value instanceof Element) { captcha.value.render(captchaEl.value, { sitekey: props.sitekey, @@ -96,6 +102,15 @@ function requestRender() { 'expired-callback': callback, 'error-callback': callback, }); + } else if (props.provider === 'mcaptcha' && props.instanceUrl && props.sitekey) { + const { default: Widget } = await import('@mcaptcha/vanilla-glue'); + // @ts-expect-error avoid typecheck error + new Widget({ + siteKey: { + instanceUrl: new URL(props.instanceUrl), + key: props.sitekey, + }, + }); } else { window.setTimeout(requestRender, 1); } @@ -105,14 +120,27 @@ function callback(response?: string) { emit('update:modelValue', typeof response === 'string' ? response : null); } +function onReceivedMessage(message: MessageEvent) { + if (message.data.token) { + if (props.instanceUrl && new URL(message.origin).host === new URL(props.instanceUrl).host) { + callback(message.data.token); + } + } +} + onMounted(() => { if (available.value) { + window.addEventListener('message', onReceivedMessage); requestRender(); } else { watch(available, requestRender); } }); +onUnmounted(() => { + window.removeEventListener('message', onReceivedMessage); +}); + onBeforeUnmount(() => { reset(); }); diff --git a/packages/frontend/src/components/MkChannelFollowButton.vue b/packages/frontend/src/components/MkChannelFollowButton.vue index 4a58204b5b64ba682a1510513f58dca2f80a4f5b..07732d9205e92ce6c09daf2efa7591fbb7e1e30f 100644 --- a/packages/frontend/src/components/MkChannelFollowButton.vue +++ b/packages/frontend/src/components/MkChannelFollowButton.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const props = withDefaults(defineProps<{ @@ -44,12 +44,12 @@ async function onClick() { try { if (isFollowing.value) { - await os.api('channels/unfollow', { + await misskeyApi('channels/unfollow', { channelId: props.channel.id, }); isFollowing.value = false; } else { - await os.api('channels/follow', { + await misskeyApi('channels/follow', { channelId: props.channel.id, }); isFollowing.value = true; diff --git a/packages/frontend/src/components/MkChannelList.vue b/packages/frontend/src/components/MkChannelList.vue index 83d4401d2e36bb23232462ef398c138d99766459..2850ecca16b2769d6047576204bd803d5b8e0023 100644 --- a/packages/frontend/src/components/MkChannelList.vue +++ b/packages/frontend/src/components/MkChannelList.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkChannelPreview.vue b/packages/frontend/src/components/MkChannelPreview.vue index f870b0eef1f3ec708054ef7a10ee020d6d12db33..1bac59d6dfb51fa2fa089d9ed0c37027441637f0 100644 --- a/packages/frontend/src/components/MkChannelPreview.vue +++ b/packages/frontend/src/components/MkChannelPreview.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only </I18n> </div> <div> - <i class="ph-pencil ph-bold ph-lg"></i> + <i class="ph-pencil-simple ph-bold ph-lg"></i> <I18n :src="i18n.ts._channel.notesCount" tag="span" style="margin-left: 4px;"> <template #n> <b>{{ channel.notesCount }}</b> diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue index adb3c134ae96dea4a83de93835cca87145c2d6e5..04b6d2f29c0e81b403d6778cd8e3000386e75a07 100644 --- a/packages/frontend/src/components/MkChart.vue +++ b/packages/frontend/src/components/MkChart.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -21,13 +21,13 @@ SPDX-License-Identifier: AGPL-3.0-only */ import { onMounted, ref, shallowRef, watch, PropType } from 'vue'; import { Chart } from 'chart.js'; -import gradient from 'chartjs-plugin-gradient'; -import * as os from '@/os.js'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import { chartVLine } from '@/scripts/chart-vline.js'; import { alpha } from '@/scripts/color.js'; import date from '@/filters/date.js'; +import bytes from '@/filters/bytes.js'; import { initChart } from '@/scripts/init-chart.js'; import { chartLegend } from '@/scripts/chart-legend.js'; import MkChartLegend from '@/components/MkChartLegend.vue'; @@ -95,7 +95,7 @@ const getColor = (i) => { }; const now = new Date(); -let chartInstance: Chart = null; +let chartInstance: Chart | null = null; let chartData: { series: { name: string; @@ -108,9 +108,10 @@ let chartData: { y: number; }[]; }[]; -} = null; + bytes?: boolean; +} | null = null; -const chartEl = shallowRef<HTMLCanvasElement>(null); +const chartEl = shallowRef<HTMLCanvasElement | null>(null); const fetching = ref(true); const getDate = (ago: number) => { @@ -132,6 +133,7 @@ const format = (arr) => { const { handler: externalTooltipHandler } = useChartTooltip(); const render = () => { + if (chartData == null || chartEl.value == null) return; if (chartInstance) { chartInstance.destroy(); } @@ -188,7 +190,6 @@ const render = () => { stacked: props.stacked, offset: false, time: { - stepSize: 1, unit: props.span === 'day' ? 'month' : 'day', displayFormats: { day: 'M/d', @@ -198,6 +199,7 @@ const render = () => { grid: { }, ticks: { + stepSize: 1, display: props.detailed, maxRotation: 0, autoSkipPadding: 16, @@ -237,6 +239,9 @@ const render = () => { duration: 0, }, external: externalTooltipHandler, + callbacks: { + label: (item) => `${item.dataset.label}: ${chartData?.bytes ? bytes(item.parsed.y * 1000, 1) : item.parsed.y.toString()}`, + }, }, zoom: props.detailed ? { pan: { @@ -265,10 +270,9 @@ const render = () => { }, }, } : undefined, - gradient, }, }, - plugins: [chartVLine(vLineColor), ...(props.detailed ? [chartLegend(legendEl.value)] : [])], + plugins: [chartVLine(vLineColor), ...(props.detailed && legendEl.value ? [chartLegend(legendEl.value)] : [])], }); }; @@ -277,7 +281,7 @@ const exportData = () => { }; const fetchFederationChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/federation', { limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/federation', { limit: props.limit, span: props.span }); return { series: [{ name: 'Received', @@ -327,7 +331,7 @@ const fetchFederationChart = async (): Promise<typeof chartData> => { }; const fetchApRequestChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/ap-request', { limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/ap-request', { limit: props.limit, span: props.span }); return { series: [{ name: 'In', @@ -349,7 +353,7 @@ const fetchApRequestChart = async (): Promise<typeof chartData> => { }; const fetchNotesChart = async (type: string): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/notes', { limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/notes', { limit: props.limit, span: props.span }); return { series: [{ name: 'All', @@ -396,7 +400,7 @@ const fetchNotesChart = async (type: string): Promise<typeof chartData> => { }; const fetchNotesTotalChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/notes', { limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/notes', { limit: props.limit, span: props.span }); return { series: [{ name: 'Combined', @@ -415,7 +419,7 @@ const fetchNotesTotalChart = async (): Promise<typeof chartData> => { }; const fetchUsersChart = async (total: boolean): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/users', { limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/users', { limit: props.limit, span: props.span }); return { series: [{ name: 'Combined', @@ -443,7 +447,7 @@ const fetchUsersChart = async (total: boolean): Promise<typeof chartData> => { }; const fetchActiveUsersChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/active-users', { limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/active-users', { limit: props.limit, span: props.span }); return { series: [{ name: 'Read & Write', @@ -495,7 +499,7 @@ const fetchActiveUsersChart = async (): Promise<typeof chartData> => { }; const fetchDriveChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/drive', { limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/drive', { limit: props.limit, span: props.span }); return { bytes: true, series: [{ @@ -531,7 +535,7 @@ const fetchDriveChart = async (): Promise<typeof chartData> => { }; const fetchDriveFilesChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/drive', { limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/drive', { limit: props.limit, span: props.span }); return { series: [{ name: 'All', @@ -566,7 +570,7 @@ const fetchDriveFilesChart = async (): Promise<typeof chartData> => { }; const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); return { series: [{ name: 'In', @@ -588,7 +592,7 @@ const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => { }; const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); return { series: [{ name: 'Users', @@ -603,7 +607,7 @@ const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData }; const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); return { series: [{ name: 'Notes', @@ -618,7 +622,7 @@ const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData }; const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); return { series: [{ name: 'Following', @@ -641,7 +645,7 @@ const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> = }; const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); return { bytes: true, series: [{ @@ -649,7 +653,7 @@ const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof char type: 'area', color: '#008FFB', data: format(total - ? raw.drive.totalUsage + ? sum(raw.drive.incUsage) : sum(raw.drive.incUsage, negate(raw.drive.decUsage)), ), }], @@ -657,7 +661,7 @@ const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof char }; const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); return { series: [{ name: 'Drive files', @@ -672,11 +676,11 @@ const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof char }; const fetchPerUserNotesChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/user/notes', { userId: props.args.user.id, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/user/notes', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); return { - series: [...(props.args.withoutAll ? [] : [{ + series: [...(props.args?.withoutAll ? [] : [{ name: 'All', - type: 'line', + type: 'line' as const, data: format(sum(raw.inc, negate(raw.dec))), color: '#888888', }]), { @@ -704,7 +708,7 @@ const fetchPerUserNotesChart = async (): Promise<typeof chartData> => { }; const fetchPerUserPvChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/user/pv', { userId: props.args.user.id, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/user/pv', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); return { series: [{ name: 'Unique PV (user)', @@ -731,7 +735,7 @@ const fetchPerUserPvChart = async (): Promise<typeof chartData> => { }; const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); return { series: [{ name: 'Local', @@ -746,7 +750,7 @@ const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => { }; const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); return { series: [{ name: 'Local', @@ -761,8 +765,9 @@ const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => { }; const fetchPerUserDriveChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/user/drive', { userId: props.args.user.id, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/user/drive', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); return { + bytes: true, series: [{ name: 'Inc', type: 'area', @@ -806,6 +811,8 @@ const fetchAndRender = async () => { case 'per-user-following': return fetchPerUserFollowingChart(); case 'per-user-followers': return fetchPerUserFollowersChart(); case 'per-user-drive': return fetchPerUserDriveChart(); + + default: return null; } }; fetching.value = true; diff --git a/packages/frontend/src/components/MkChartLegend.vue b/packages/frontend/src/components/MkChartLegend.vue index c265fe6e97746d58bdeb3778555fe84e718d7463..240c9c919eee773c509abfac383e19ce1ec5b5fb 100644 --- a/packages/frontend/src/components/MkChartLegend.vue +++ b/packages/frontend/src/components/MkChartLegend.vue @@ -1,12 +1,12 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <div :class="$style.root"> <button v-for="item in items" class="_button item" :class="{ disabled: item.hidden }" @click="onClick(item)"> - <span class="box" :style="{ background: chart.config.type === 'line' ? item.strokeStyle?.toString() : item.fillStyle?.toString() }"></span> + <span class="box" :style="{ background: type === 'line' ? item.strokeStyle?.toString() : item.fillStyle?.toString() }"></span> {{ item.text }} </button> </div> @@ -16,25 +16,23 @@ SPDX-License-Identifier: AGPL-3.0-only import { shallowRef } from 'vue'; import { Chart, LegendItem } from 'chart.js'; -const props = defineProps({ -}); - const chart = shallowRef<Chart>(); +const type = shallowRef<string>(); const items = shallowRef<LegendItem[]>([]); function update(_chart: Chart, _items: LegendItem[]) { chart.value = _chart, items.value = _items; + if ('type' in _chart.config) type.value = _chart.config.type; } function onClick(item: LegendItem) { if (chart.value == null) return; - const { type } = chart.value.config; - if (type === 'pie' || type === 'doughnut') { + if (type.value === 'pie' || type.value === 'doughnut') { // Pie and doughnut charts only have a single dataset and visibility is per item - chart.value.toggleDataVisibility(item.index); + if (item.index != null) chart.value.toggleDataVisibility(item.index); } else { - chart.value.setDatasetVisibility(item.datasetIndex, !chart.value.isDatasetVisible(item.datasetIndex)); + if (item.datasetIndex != null) chart.value.setDatasetVisibility(item.datasetIndex, !chart.value.isDatasetVisible(item.datasetIndex)); } chart.value.update(); } diff --git a/packages/frontend/src/components/MkChartTooltip.vue b/packages/frontend/src/components/MkChartTooltip.vue index c11f516e370a14fe372ad139fbd6aefd8b8cdca1..51081ede230b58c8ffd89980efec2259cf16c98f 100644 --- a/packages/frontend/src/components/MkChartTooltip.vue +++ b/packages/frontend/src/components/MkChartTooltip.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkClickerGame.vue b/packages/frontend/src/components/MkClickerGame.vue index 1e72319010c53f7f42608965794b0e9b02eba7e7..892ad31b09a241f6b83c98169b30430e443bf4e1 100644 --- a/packages/frontend/src/components/MkClickerGame.vue +++ b/packages/frontend/src/components/MkClickerGame.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkClipPreview.vue b/packages/frontend/src/components/MkClipPreview.vue index 2f6790fa49b90404d103f4b8a95e5e1f9b89781d..c51ad4356da0b9584bb821a5a38c9e13012a06a6 100644 --- a/packages/frontend/src/components/MkClipPreview.vue +++ b/packages/frontend/src/components/MkClipPreview.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkCode.core.vue b/packages/frontend/src/components/MkCode.core.vue index 579c72b18676fe61c94c26f5d6b13d41395ea6a9..f9aaf4eff3e8fb90adb3699af98a5ff614e8d860 100644 --- a/packages/frontend/src/components/MkCode.core.vue +++ b/packages/frontend/src/components/MkCode.core.vue @@ -1,18 +1,19 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <!-- eslint-disable vue/no-v-html --> <template> -<div :class="['codeBlockRoot', { 'codeEditor': codeEditor }]" v-html="html"></div> +<div :class="[$style.codeBlockRoot, { [$style.codeEditor]: codeEditor }, (darkMode ? $style.dark : $style.light)]" v-html="html"></div> </template> <script lang="ts" setup> import { ref, computed, watch } from 'vue'; -import { BUNDLED_LANGUAGES } from 'shiki'; -import type { Lang as ShikiLang } from 'shiki'; -import { getHighlighter } from '@/scripts/code-highlighter.js'; +import { bundledLanguagesInfo } from 'shiki'; +import type { BuiltinLanguage } from 'shiki'; +import { getHighlighter, getTheme } from '@/scripts/code-highlighter.js'; +import { defaultStore } from '@/store.js'; const props = defineProps<{ code: string; @@ -21,25 +22,38 @@ const props = defineProps<{ }>(); const highlighter = await getHighlighter(); +const darkMode = defaultStore.reactiveState.darkMode; +const codeLang = ref<BuiltinLanguage | 'aiscript'>('js'); + +const [lightThemeName, darkThemeName] = await Promise.all([ + getTheme('light', true), + getTheme('dark', true), +]); -const codeLang = ref<ShikiLang | 'aiscript'>('js'); const html = computed(() => highlighter.codeToHtml(props.code, { lang: codeLang.value, - theme: 'dark-plus', + themes: { + fallback: 'dark-plus', + light: lightThemeName, + dark: darkThemeName, + }, + defaultColor: false, + cssVariablePrefix: '--shiki-', })); async function fetchLanguage(to: string): Promise<void> { - const language = to as ShikiLang; + const language = to as BuiltinLanguage; // Check for the loaded languages, and load the language if it's not loaded yet. if (!highlighter.getLoadedLanguages().includes(language)) { // Check if the language is supported by Shiki - const bundles = BUNDLED_LANGUAGES.filter((bundle) => { + const bundles = bundledLanguagesInfo.filter((bundle) => { // Languages are specified by their id, they can also have aliases (i. e. "js" and "javascript") return bundle.id === language || bundle.aliases?.includes(language); }); if (bundles.length > 0) { - await highlighter.loadLanguage(language); + if (_DEV_) console.log(`Loading language: ${language}`); + await highlighter.loadLanguage(bundles[0].import); codeLang.value = language; } else { codeLang.value = 'js'; @@ -57,12 +71,37 @@ watch(() => props.lang, (to) => { }, { immediate: true }); </script> -<style scoped lang="scss"> -.codeBlockRoot :deep(.shiki) { +<style module lang="scss"> +.codeBlockRoot :global(.shiki) > code { + counter-reset: step; + counter-increment: step 0; +} + +.codeBlockRoot :global(.shiki) > code > .line::before { + content: counter(step); + counter-increment: step; + width: 1rem; + margin-right: 1.5rem; + display: inline-block; + text-align: right; + color: rgba(115,138,148,.4) +} + +.codeBlockRoot :global(.shiki) { padding: 1em; margin: .5em 0; overflow: auto; border-radius: var(--radius-sm); + border: 1px solid var(--divider); + font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace; + + color: var(--shiki-fallback); + background-color: var(--shiki-fallback-bg); + + & span { + color: var(--shiki-fallback); + background-color: var(--shiki-fallback-bg); + } & pre, & code { @@ -70,14 +109,35 @@ watch(() => props.lang, (to) => { } } +.light.codeBlockRoot :global(.shiki) { + color: var(--shiki-light); + background-color: var(--shiki-light-bg); + + & span { + color: var(--shiki-light); + background-color: var(--shiki-light-bg); + } +} + +.dark.codeBlockRoot :global(.shiki) { + color: var(--shiki-dark); + background-color: var(--shiki-dark-bg); + + & span { + color: var(--shiki-dark); + background-color: var(--shiki-dark-bg); + } +} + .codeBlockRoot.codeEditor { min-width: 100%; height: 100%; - & :deep(.shiki) { + & :global(.shiki) { padding: 12px; margin: 0; border-radius: var(--radius-sm); + border: none; min-height: 130px; pointer-events: none; min-width: calc(100% - 24px); @@ -89,6 +149,11 @@ watch(() => props.lang, (to) => { text-rendering: inherit; text-transform: inherit; white-space: pre; + + & span { + display: inline-block; + min-height: 1em; + } } } </style> diff --git a/packages/frontend/src/components/MkCode.vue b/packages/frontend/src/components/MkCode.vue index e0973b676a3b784dc586bac33063365a11e2bd93..acd2ea6f97ded57e149e0da8a72f67f878a9327e 100644 --- a/packages/frontend/src/components/MkCode.vue +++ b/packages/frontend/src/components/MkCode.vue @@ -1,58 +1,72 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> -<Suspense> - <template #fallback> - <MkLoading v-if="!inline ?? true"/> - </template> - <code v-if="inline" :class="$style.codeInlineRoot">{{ code }}</code> - <XCode v-else-if="show && lang" :code="code" :lang="lang"/> - <pre v-else-if="show" :class="$style.codeBlockFallbackRoot"><code :class="$style.codeBlockFallbackCode">{{ code }}</code></pre> - <button v-else :class="$style.codePlaceholderRoot" @click="show = true"> - <div :class="$style.codePlaceholderContainer"> - <div><i class="ph-code ph-bold ph-lg"></i> {{ i18n.ts.code }}</div> - <div>{{ i18n.ts.clickToShow }}</div> - </div> +<div :class="$style.codeBlockRoot"> + <button :class="$style.codeBlockCopyButton" class="_button" @click="copy"> + <i class="ph-copy ph-bold ph-lg"></i> </button> -</Suspense> + <Suspense> + <template #fallback> + <MkLoading /> + </template> + <XCode v-if="show && lang" :code="code" :lang="lang"/> + <pre v-else-if="show" :class="$style.codeBlockFallbackRoot"><code :class="$style.codeBlockFallbackCode">{{ code }}</code></pre> + <button v-else :class="$style.codePlaceholderRoot" @click="show = true"> + <div :class="$style.codePlaceholderContainer"> + <div><i class="ph-code ph-bold ph-lg"></i> {{ i18n.ts.code }}</div> + <div>{{ i18n.ts.clickToShow }}</div> + </div> + </button> + </Suspense> +</div> </template> <script lang="ts" setup> import { defineAsyncComponent, ref } from 'vue'; +import * as os from '@/os.js'; import MkLoading from '@/components/global/MkLoading.vue'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; +import copyToClipboard from '@/scripts/copy-to-clipboard.js'; -defineProps<{ +const props = defineProps<{ code: string; lang?: string; - inline?: boolean; }>(); const show = ref(!defaultStore.state.dataSaver.code); const XCode = defineAsyncComponent(() => import('@/components/MkCode.core.vue')); + +function copy() { + copyToClipboard(props.code); + os.success(); +} </script> <style module lang="scss"> -.codeInlineRoot { - display: inline-block; - font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace; - overflow-wrap: anywhere; - color: #D4D4D4; - background: #1E1E1E; - padding: .1em; - border-radius: .3em; +.codeBlockRoot { + position: relative; +} + +.codeBlockCopyButton { + position: absolute; + top: 8px; + right: 8px; + opacity: 0.5; + + &:hover { + opacity: 0.8; + } } .codeBlockFallbackRoot { display: block; overflow-wrap: anywhere; - color: #D4D4D4; - background: #1E1E1E; + background: var(--bg); padding: 1em; margin: .5em 0; overflow: auto; @@ -77,8 +91,8 @@ const XCode = defineAsyncComponent(() => import('@/components/MkCode.core.vue')) border-radius: var(--radius-sm); padding: 24px; margin-top: 4px; - color: #D4D4D4; - background: #1E1E1E; + color: var(--fg); + background: var(--bg); } .codePlaceholderContainer { diff --git a/packages/frontend/src/components/MkCodeEditor.vue b/packages/frontend/src/components/MkCodeEditor.vue index 0ec69a69af2c4df705066d943f7156ee1b178b51..30e518f8f037279b61ef9977ad03f5bc019fdabc 100644 --- a/packages/frontend/src/components/MkCodeEditor.vue +++ b/packages/frontend/src/components/MkCodeEditor.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.codeEditorScroller"> <textarea ref="inputEl" - v-model="vModel" + v-model="v" :class="[$style.textarea]" :disabled="disabled" :required="required" @@ -58,7 +58,6 @@ const emit = defineEmits<{ }>(); const { modelValue } = toRefs(props); -const vModel = ref<string>(modelValue.value ?? ''); const v = ref<string>(modelValue.value ?? ''); const focused = ref(false); const changed = ref(false); @@ -79,15 +78,14 @@ const onKeydown = (ev: KeyboardEvent) => { if (ev.code === 'Enter') { const pos = inputEl.value?.selectionStart ?? 0; - const posEnd = inputEl.value?.selectionEnd ?? vModel.value.length; + const posEnd = inputEl.value?.selectionEnd ?? v.value.length; if (pos === posEnd) { - const lines = vModel.value.slice(0, pos).split('\n'); + const lines = v.value.slice(0, pos).split('\n'); const currentLine = lines[lines.length - 1]; const currentLineSpaces = currentLine.match(/^\s+/); const posDelta = currentLineSpaces ? currentLineSpaces[0].length : 0; ev.preventDefault(); - vModel.value = vModel.value.slice(0, pos) + '\n' + (currentLineSpaces ? currentLineSpaces[0] : '') + vModel.value.slice(pos); - v.value = vModel.value; + v.value = v.value.slice(0, pos) + '\n' + (currentLineSpaces ? currentLineSpaces[0] : '') + v.value.slice(pos); nextTick(() => { inputEl.value?.setSelectionRange(pos + 1 + posDelta, pos + 1 + posDelta); }); @@ -97,9 +95,8 @@ const onKeydown = (ev: KeyboardEvent) => { if (ev.key === 'Tab') { const pos = inputEl.value?.selectionStart ?? 0; - const posEnd = inputEl.value?.selectionEnd ?? vModel.value.length; - vModel.value = vModel.value.slice(0, pos) + '\t' + vModel.value.slice(posEnd); - v.value = vModel.value; + const posEnd = inputEl.value?.selectionEnd ?? v.value.length; + v.value = v.value.slice(0, pos) + '\t' + v.value.slice(posEnd); nextTick(() => { inputEl.value?.setSelectionRange(pos + 1, pos + 1); }); @@ -199,20 +196,23 @@ watch(v, newValue => { resize: none; text-align: left; color: transparent; - caret-color: rgb(225, 228, 232); + caret-color: var(--fg); background-color: transparent; border: 0; border-radius: var(--radius-sm); + box-sizing: border-box; outline: 0; min-width: calc(100% - 24px); height: 100%; padding: 12px; + // the +2.5 rem is because of the line numbers + padding-left: calc(12px + 2.5rem); line-height: 1.5em; font-size: 1em; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace; } .textarea::selection { - color: #fff; + color: var(--bg); } </style> diff --git a/packages/frontend/src/components/MkCodeInline.vue b/packages/frontend/src/components/MkCodeInline.vue new file mode 100644 index 0000000000000000000000000000000000000000..6add80d1bc13af73cd31774f117bbf28f2e5900b --- /dev/null +++ b/packages/frontend/src/components/MkCodeInline.vue @@ -0,0 +1,25 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<code :class="$style.root">{{ code }}</code> +</template> + +<script lang="ts" setup> +const props = defineProps<{ + code: string; +}>(); +</script> + +<style module lang="scss"> +.root { + display: inline-block; + font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace; + overflow-wrap: anywhere; + background: var(--bg); + padding: .1em; + border-radius: .3em; +} +</style> diff --git a/packages/frontend/src/components/MkColorInput.vue b/packages/frontend/src/components/MkColorInput.vue index 4f15e889510e572888da16d330ffecd146718ffa..99aa46d56198733443c9e7762b1cd01392479b35 100644 --- a/packages/frontend/src/components/MkColorInput.vue +++ b/packages/frontend/src/components/MkColorInput.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -41,8 +41,8 @@ const { modelValue } = toRefs(props); const v = ref(modelValue.value); const inputEl = shallowRef<HTMLElement>(); -const onInput = (ev: KeyboardEvent) => { - emit('update:modelValue', v.value); +const onInput = () => { + emit('update:modelValue', v.value ?? ''); }; </script> diff --git a/packages/frontend/src/components/MkContainer.vue b/packages/frontend/src/components/MkContainer.vue index 42c6cc1075a296acd2cb51cd28cab5942d26f73a..95188c335e330e1b07f5cd93ba15dba15ded6055 100644 --- a/packages/frontend/src/components/MkContainer.vue +++ b/packages/frontend/src/components/MkContainer.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkContextMenu.vue b/packages/frontend/src/components/MkContextMenu.vue index e29cf472f7dfed4fd5ca2031a4a695ca2c42dfc4..5ca3c77fb23f297603ab994655e31a121502f023 100644 --- a/packages/frontend/src/components/MkContextMenu.vue +++ b/packages/frontend/src/components/MkContextMenu.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -44,8 +44,8 @@ onMounted(() => { let left = props.ev.pageX + 1; // é–“é•ã£ã¦å³ãƒ€ãƒ–ルクリックã—ãŸå ´åˆã«æ„図ã›ãšã‚¢ã‚¤ãƒ†ãƒ ãŒã‚¯ãƒªãƒƒã‚¯ã•ã‚Œã‚‹ã®ã‚’防ããŸã‚ + 1 let top = props.ev.pageY + 1; // é–“é•ã£ã¦å³ãƒ€ãƒ–ルクリックã—ãŸå ´åˆã«æ„図ã›ãšã‚¢ã‚¤ãƒ†ãƒ ãŒã‚¯ãƒªãƒƒã‚¯ã•ã‚Œã‚‹ã®ã‚’防ããŸã‚ + 1 - const width = rootEl.value.offsetWidth; - const height = rootEl.value.offsetHeight; + const width = rootEl.value!.offsetWidth; + const height = rootEl.value!.offsetHeight; if (left + width - window.pageXOffset >= (window.innerWidth - SCROLLBAR_THICKNESS)) { left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset; @@ -63,8 +63,10 @@ onMounted(() => { left = 0; } - rootEl.value.style.top = `${top}px`; - rootEl.value.style.left = `${left}px`; + if (rootEl.value) { + rootEl.value.style.top = `${top}px`; + rootEl.value.style.left = `${left}px`; + } document.body.addEventListener('mousedown', onMousedown); }); diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue index 0a1ddd3171db7ef9a349e9f50365f73e1c2a36a2..54f6f39c9dc50679b16cd9f1a6273483db656bd7 100644 --- a/packages/frontend/src/components/MkCropperDialog.vue +++ b/packages/frontend/src/components/MkCropperDialog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -63,18 +63,25 @@ const loading = ref(true); const ok = async () => { const promise = new Promise<Misskey.entities.DriveFile>(async (res) => { - const croppedCanvas = await cropper?.getCropperSelection()?.$toCanvas(); + const croppedImage = await cropper?.getCropperImage(); + const croppedSection = await cropper?.getCropperSelection(); + + // 拡大率を計算ã—ã€(ã»ã¼)å…ƒã®å¤§ãã•ã«æˆ»ã™ + const zoomedRate = croppedImage.getBoundingClientRect().width / croppedImage.clientWidth; + const widthToRender = croppedSection.getBoundingClientRect().width / zoomedRate; + + const croppedCanvas = await croppedSection?.$toCanvas({ width: widthToRender }); croppedCanvas?.toBlob(blob => { if (!blob) return; const formData = new FormData(); formData.append('file', blob); formData.append('name', `cropped_${props.file.name}`); formData.append('isSensitive', props.file.isSensitive ? 'true' : 'false'); - formData.append('comment', props.file.comment ?? 'null'); + if (props.file.comment) { formData.append('comment', props.file.comment);} formData.append('i', $i!.token); - if (props.uploadFolder || props.uploadFolder === null) { - formData.append('folderId', props.uploadFolder ?? 'null'); - } else if (defaultStore.state.uploadFolder) { + if (props.uploadFolder) { + formData.append('folderId', props.uploadFolder); + } else if (props.uploadFolder !== null && defaultStore.state.uploadFolder) { formData.append('folderId', defaultStore.state.uploadFolder); } diff --git a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..84b5375a41b74103ab69bc75008310f8b41e215a --- /dev/null +++ b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue @@ -0,0 +1,104 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> + <MkModalWindow ref="dialogEl" @close="cancel()" @closed="$emit('closed')"> + <template #header>:{{ emoji.name }}:</template> + <template #default> + <MkSpacer> + <div style="display: flex; flex-direction: column; gap: 1em;"> + <div :class="$style.emojiImgWrapper"> + <MkCustomEmoji :name="emoji.name" :normal="true" :useOriginalSize="true" style="height: 100%;"></MkCustomEmoji> + </div> + <MkKeyValue :copy="`:${emoji.name}:`"> + <template #key>{{ i18n.ts.name }}</template> + <template #value>{{ emoji.name }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>{{ i18n.ts.tags }}</template> + <template #value> + <div v-if="emoji.aliases.length === 0">{{ i18n.ts.none }}</div> + <div v-else :class="$style.aliases"> + <span v-for="alias in emoji.aliases" :key="alias" :class="$style.alias"> + {{ alias }} + </span> + </div> + </template> + </MkKeyValue> + <MkKeyValue> + <template #key>{{ i18n.ts.category }}</template> + <template #value>{{ emoji.category ?? i18n.ts.none }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>{{ i18n.ts.sensitive }}</template> + <template #value>{{ emoji.isSensitive ? i18n.ts.yes : i18n.ts.no }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>{{ i18n.ts.localOnly }}</template> + <template #value>{{ emoji.localOnly ? i18n.ts.yes : i18n.ts.no }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>{{ i18n.ts.license }}</template> + <template #value><Mfm :text="emoji.license ?? i18n.ts.none" /></template> + </MkKeyValue> + <MkKeyValue :copy="emoji.url"> + <template #key>{{ i18n.ts.emojiUrl }}</template> + <template #value> + <MkLink :url="emoji.url" target="_blank">{{ emoji.url }}</MkLink> + </template> + </MkKeyValue> + </div> + </MkSpacer> + </template> + </MkModalWindow> +</template> + +<script lang="ts" setup> +import * as Misskey from 'misskey-js'; +import { defineProps, shallowRef } from 'vue'; +import { i18n } from '@/i18n.js'; +import MkModalWindow from '@/components/MkModalWindow.vue'; +import MkKeyValue from '@/components/MkKeyValue.vue'; +import MkLink from './MkLink.vue'; +const props = defineProps<{ + emoji: Misskey.entities.EmojiDetailed, +}>(); +const emit = defineEmits<{ + (ev: 'ok', cropped: Misskey.entities.DriveFile): void; + (ev: 'cancel'): void; + (ev: 'closed'): void; +}>(); +const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>(); +const cancel = () => { + emit('cancel'); + dialogEl.value!.close(); +}; +</script> + +<style lang="scss" module> +.emojiImgWrapper { + max-width: 100%; + height: 40cqh; + background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, var(--X5) 8px, var(--X5) 14px); + border-radius: var(--radius); + margin: auto; + overflow-y: hidden; +} + +.aliases { + display: flex; + flex-wrap: wrap; + gap: 3px; +} + +.alias { + display: inline-block; + word-break: break-all; + padding: 3px 10px; + background-color: var(--X5); + border: solid 1px var(--divider); + border-radius: var(--radius); +} +</style> diff --git a/packages/frontend/src/components/MkCwButton.vue b/packages/frontend/src/components/MkCwButton.vue index 4a6d2dfba240ab64e44dff7b160afefaf6d97d47..a2cb3185f44701b5d55c41dc51acc6fb51bd1525 100644 --- a/packages/frontend/src/components/MkCwButton.vue +++ b/packages/frontend/src/components/MkCwButton.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { computed } from 'vue'; import * as Misskey from 'misskey-js'; +import type { PollEditorModelValue } from '@/components/MkPollEditor.vue'; import { concat } from '@/scripts/array.js'; import { i18n } from '@/i18n.js'; import MkButton from '@/components/MkButton.vue'; @@ -17,22 +18,9 @@ import MkButton from '@/components/MkButton.vue'; const props = defineProps<{ modelValue: boolean; text: string | null; - renote: Misskey.entities.Note | null; - files: Misskey.entities.DriveFile[]; - poll?: { - expiresAt: string | null; - multiple: boolean; - choices: { - isVoted: boolean; - text: string; - votes: number; - }[]; - } | { - choices: string[]; - multiple: boolean; - expiresAt: string | null; - expiredAfter: string | null; - }; + renote?: Misskey.entities.Note | null; + files?: Misskey.entities.DriveFile[]; + poll?: Misskey.entities.Note['poll'] | PollEditorModelValue | null; }>(); const emit = defineEmits<{ @@ -41,9 +29,9 @@ const emit = defineEmits<{ const label = computed(() => { return concat([ - props.text ? [i18n.t('_cw.chars', { count: props.text.length })] : [], + props.text ? [i18n.tsx._cw.chars({ count: props.text.length })] : [], props.renote ? [i18n.ts.quote] : [], - props.files.length !== 0 ? [i18n.t('_cw.files', { count: props.files.length })] : [], + props.files && props.files.length !== 0 ? [i18n.tsx._cw.files({ count: props.files.length })] : [], props.poll != null ? [i18n.ts.poll] : [], ] as string[][]).join(' / '); }); diff --git a/packages/frontend/src/components/MkDateSeparatedList.vue b/packages/frontend/src/components/MkDateSeparatedList.vue index b45aef45ffe898407c89055605ec35de34dd615f..475fbcb397abf287f225c7eb79d48e09e390eead 100644 --- a/packages/frontend/src/components/MkDateSeparatedList.vue +++ b/packages/frontend/src/components/MkDateSeparatedList.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -46,7 +46,7 @@ export default defineComponent({ function getDateText(time: string) { const date = new Date(time).getDate(); const month = new Date(time).getMonth() + 1; - return i18n.t('monthAndDay', { + return i18n.tsx.monthAndDay({ month: month.toString(), day: date.toString(), }); @@ -118,34 +118,36 @@ export default defineComponent({ return children; }; - function onBeforeLeave(el: HTMLElement) { + function onBeforeLeave(element: Element) { + const el = element as HTMLElement; el.style.top = `${el.offsetTop}px`; el.style.left = `${el.offsetLeft}px`; } - function onLeaveCanceled(el: HTMLElement) { + function onLeaveCancelled(element: Element) { + const el = element as HTMLElement; el.style.top = ''; el.style.left = ''; } - return () => h( - defaultStore.state.animation ? TransitionGroup : 'div', - { - class: { - [$style['date-separated-list']]: true, - [$style['date-separated-list-nogap']]: props.noGap, - [$style['reversed']]: props.reversed, - [$style['direction-down']]: props.direction === 'down', - [$style['direction-up']]: props.direction === 'up', - }, - ...(defaultStore.state.animation ? { - name: 'list', - tag: 'div', - onBeforeLeave, - onLeaveCanceled, - } : {}), - }, - { default: renderChildren }); + // eslint-disable-next-line vue/no-setup-props-destructure + const classes = { + [$style['date-separated-list']]: true, + [$style['date-separated-list-nogap']]: props.noGap, + [$style['reversed']]: props.reversed, + [$style['direction-down']]: props.direction === 'down', + [$style['direction-up']]: props.direction === 'up', + }; + + return () => defaultStore.state.animation ? h(TransitionGroup, { + class: classes, + name: 'list', + tag: 'div', + onBeforeLeave, + onLeaveCancelled, + }, { default: renderChildren }) : h('div', { + class: classes, + }, { default: renderChildren }); }, }); </script> diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue index 2c0f6a4d788a3bda1926b16df48753c9d8485e12..b81ebbbb111c3e51f3c1023fb2c4c2f48d5645ef 100644 --- a/packages/frontend/src/components/MkDialog.vue +++ b/packages/frontend/src/components/MkDialog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -30,19 +30,14 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" :autocomplete="input.autocomplete" @keydown="onInputKeydown"> <template v-if="input.type === 'password'" #prefix><i class="ph-lock ph-bold ph-lg"></i></template> <template #caption> - <span v-if="okButtonDisabledReason === 'charactersExceeded'" v-text="i18n.t('_dialog.charactersExceeded', { current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })"/> - <span v-else-if="okButtonDisabledReason === 'charactersBelow'" v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })"/> + <span v-if="okButtonDisabledReason === 'charactersExceeded'" v-text="i18n.tsx._dialog.charactersExceeded({ current: (inputValue as string)?.length ?? 0, max: input.maxLength ?? 'NaN' })"/> + <span v-else-if="okButtonDisabledReason === 'charactersBelow'" v-text="i18n.tsx._dialog.charactersBelow({ current: (inputValue as string)?.length ?? 0, min: input.minLength ?? 'NaN' })"/> </template> </MkInput> <MkSelect v-if="select" v-model="selectedValue" autofocus> <template v-if="select.items"> <option v-for="item in select.items" :value="item.value">{{ item.text }}</option> </template> - <template v-else> - <optgroup v-for="groupedItem in select.groupedItems" :label="groupedItem.label"> - <option v-for="item in groupedItem.items" :value="item.value">{{ item.text }}</option> - </optgroup> - </template> </MkSelect> <div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons"> <MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabledReason" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton> @@ -64,7 +59,7 @@ import MkSelect from '@/components/MkSelect.vue'; import { i18n } from '@/i18n.js'; type Input = { - type: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local'; + type?: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local'; placeholder?: string | null; autocomplete?: string; default: string | number | null; @@ -74,22 +69,17 @@ type Input = { type Select = { items: { - value: string; + value: any; text: string; }[]; - groupedItems: { - label: string; - items: { - value: string; - text: string; - }[]; - }[]; default: string | null; }; +type Result = string | number | true | null; + const props = withDefaults(defineProps<{ type?: 'success' | 'error' | 'warning' | 'info' | 'question' | 'waiting'; - title: string; + title?: string; text?: string; input?: Input; select?: Select; @@ -113,7 +103,7 @@ const props = withDefaults(defineProps<{ }); const emit = defineEmits<{ - (ev: 'done', v: { canceled: boolean; result: any }): void; + (ev: 'done', v: { canceled: true } | { canceled: false, result: Result }): void; (ev: 'closed'): void; }>(); @@ -125,7 +115,7 @@ const selectedValue = ref(props.select?.default ?? null); const okButtonDisabledReason = computed<null | 'charactersExceeded' | 'charactersBelow'>(() => { if (props.input) { if (props.input.minLength) { - if ((inputValue.value || inputValue.value === '') && (inputValue.value as string).length < props.input.minLength) { + if (inputValue.value == null || (inputValue.value as string).length < props.input.minLength) { return 'charactersBelow'; } } @@ -139,8 +129,11 @@ const okButtonDisabledReason = computed<null | 'charactersExceeded' | 'character return null; }); -function done(canceled: boolean, result?) { - emit('done', { canceled, result }); +// overload function を使ã„ãŸã„ã®ã§ lint エラーを無視ã™ã‚‹ +function done(canceled: true): void; +function done(canceled: false, result: Result): void; // eslint-disable-line no-redeclare +function done(canceled: boolean, result?: Result): void { // eslint-disable-line no-redeclare + emit('done', { canceled, result } as { canceled: true } | { canceled: false, result: Result }); modal.value?.close(); } diff --git a/packages/frontend/src/components/MkDigitalClock.stories.impl.ts b/packages/frontend/src/components/MkDigitalClock.stories.impl.ts index 5d16c09bc5307dcac3cad8746f7675c6260a30db..e3391bcf7e27385bda6e6152778aeb512ac4d22f 100644 --- a/packages/frontend/src/components/MkDigitalClock.stories.impl.ts +++ b/packages/frontend/src/components/MkDigitalClock.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/MkDigitalClock.vue b/packages/frontend/src/components/MkDigitalClock.vue index dff6e7d4dd71537c34a0e2df75ebfc636203bf34..2e2321e6ac613f507a0d564f30b68375e799f14f 100644 --- a/packages/frontend/src/components/MkDigitalClock.vue +++ b/packages/frontend/src/components/MkDigitalClock.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkDonation.vue b/packages/frontend/src/components/MkDonation.vue index a77ff42f9449bd451406198c46acd9e44415da2f..a2780ddfe93ec5286b7e0490a1f3bf76bc254cbd 100644 --- a/packages/frontend/src/components/MkDonation.vue +++ b/packages/frontend/src/components/MkDonation.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -26,6 +26,16 @@ SPDX-License-Identifier: AGPL-3.0-only <MkLink target="_blank" url="https://ko-fi.com/transfem">{{ i18n.ts.learnMore }}</MkLink> </div> </div> + <div v-if="instance.donationUrl" :class="$style.text"> + <I18n :src="i18n.ts.pleaseDonateInstance" tag="span"> + <template #host> + {{ instance.name ?? host }} + </template> + </I18n> + <div style="margin-top: 0.2em;"> + <MkLink target="_blank" :url="instance.donationUrl">{{ i18n.ts.learnMore }}</MkLink> + </div> + </div> <div class="_buttons"> <MkButton @click="close">{{ i18n.ts.remindMeLater }}</MkButton> <MkButton @click="neverShow">{{ i18n.ts.neverShow }}</MkButton> diff --git a/packages/frontend/src/components/MkDrive.file.vue b/packages/frontend/src/components/MkDrive.file.vue index 9969c1025802745feaa0e36902602ac9e7a8be9a..13a2a2126cd484d08fe83d539e5e0eaf2e71bcca 100644 --- a/packages/frontend/src/components/MkDrive.file.vue +++ b/packages/frontend/src/components/MkDrive.file.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -49,9 +49,9 @@ import bytes from '@/filters/bytes.js'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; -import { useRouter } from '@/router.js'; import { getDriveFileMenu } from '@/scripts/get-drive-file-menu.js'; import { deviceKind } from '@/scripts/device-kind.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue index dcaaa72cf4a7238f5a9e4b9fed030998d12e6f1e..945f45c01237ec9434ef32b5084810998b659d2b 100644 --- a/packages/frontend/src/components/MkDrive.folder.vue +++ b/packages/frontend/src/components/MkDrive.folder.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -35,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed, defineAsyncComponent, ref } from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; import { claimAchievement } from '@/scripts/achievements.js'; @@ -144,7 +145,7 @@ function onDrop(ev: DragEvent) { if (driveFile != null && driveFile !== '') { const file = JSON.parse(driveFile); emit('removeFile', file.id); - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: file.id, folderId: props.folder.id, }); @@ -160,7 +161,7 @@ function onDrop(ev: DragEvent) { if (folder.id === props.folder.id) return; emit('removeFolder', folder.id); - os.api('drive/folders/update', { + misskeyApi('drive/folders/update', { folderId: folder.id, parentId: props.folder.id, }).then(() => { @@ -204,7 +205,7 @@ function onDragend() { } function go() { - emit('move', props.folder.id); + emit('move', props.folder); } function rename() { @@ -214,7 +215,7 @@ function rename() { default: props.folder.name, }).then(({ canceled, result: name }) => { if (canceled) return; - os.api('drive/folders/update', { + misskeyApi('drive/folders/update', { folderId: props.folder.id, name: name, }); @@ -222,7 +223,7 @@ function rename() { } function deleteFolder() { - os.api('drive/folders/delete', { + misskeyApi('drive/folders/delete', { folderId: props.folder.id, }).then(() => { if (defaultStore.state.uploadFolder === props.folder.id) { diff --git a/packages/frontend/src/components/MkDrive.navFolder.vue b/packages/frontend/src/components/MkDrive.navFolder.vue index cac3c17c85092759beff883f8e04ec140fe8ac7f..d78c215328777f3e67eb973d1266a0f22d9563ee 100644 --- a/packages/frontend/src/components/MkDrive.navFolder.vue +++ b/packages/frontend/src/components/MkDrive.navFolder.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; import * as Misskey from 'misskey-js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ @@ -112,7 +112,7 @@ function onDrop(ev: DragEvent) { if (driveFile != null && driveFile !== '') { const file = JSON.parse(driveFile); emit('removeFile', file.id); - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: file.id, folderId: props.folder ? props.folder.id : null, }); @@ -126,7 +126,7 @@ function onDrop(ev: DragEvent) { // 移動先ãŒè‡ªåˆ†è‡ªèº«ãªã‚‰reject if (props.folder && folder.id === props.folder.id) return; emit('removeFolder', folder.id); - os.api('drive/folders/update', { + misskeyApi('drive/folders/update', { folderId: folder.id, parentId: props.folder ? props.folder.id : null, }); diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index 00bb0e6e2b5ca34048017ccb284ceecf9f916495..2990ea6861a57310c2a7881dd0e65aedd2fa485c 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -82,8 +82,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton v-show="moreFiles" ref="loadMoreFiles" @click="fetchMoreFiles">{{ i18n.ts.loadMore }}</MkButton> </div> <div v-if="files.length == 0 && folders.length == 0 && !fetching" :class="$style.empty"> - <div v-if="draghover">{{ i18n.t('empty-draghover') }}</div> - <div v-if="!draghover && folder == null"><strong>{{ i18n.ts.emptyDrive }}</strong><br/>{{ i18n.t('empty-drive-description') }}</div> + <div v-if="draghover">{{ i18n.ts['empty-draghover'] }}</div> + <div v-if="!draghover && folder == null"><strong>{{ i18n.ts.emptyDrive }}</strong><br/>{{ i18n.ts['empty-drive-description'] }}</div> <div v-if="!draghover && folder != null">{{ i18n.ts.emptyFolder }}</div> </div> </div> @@ -98,10 +98,12 @@ SPDX-License-Identifier: AGPL-3.0-only import { nextTick, onActivated, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'; import * as Misskey from 'misskey-js'; import MkButton from './MkButton.vue'; +import type { MenuItem } from '@/types/menu.js'; import XNavFolder from '@/components/MkDrive.navFolder.vue'; import XFolder from '@/components/MkDrive.folder.vue'; import XFile from '@/components/MkDrive.file.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { useStream } from '@/stream.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; @@ -254,7 +256,7 @@ function onDrop(ev: DragEvent): any { const file = JSON.parse(driveFile); if (files.value.some(f => f.id === file.id)) return; removeFile(file.id); - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: file.id, folderId: folder.value ? folder.value.id : null, }); @@ -270,7 +272,7 @@ function onDrop(ev: DragEvent): any { if (folder.value && droppedFolder.id === folder.value.id) return false; if (folders.value.some(f => f.id === droppedFolder.id)) return false; removeFolder(droppedFolder.id); - os.api('drive/folders/update', { + misskeyApi('drive/folders/update', { folderId: droppedFolder.id, parentId: folder.value ? folder.value.id : null, }).then(() => { @@ -307,7 +309,7 @@ function urlUpload() { placeholder: i18n.ts.uploadFromUrlDescription, }).then(({ canceled, result: url }) => { if (canceled || !url) return; - os.api('drive/files/upload-from-url', { + misskeyApi('drive/files/upload-from-url', { url: url, folderId: folder.value ? folder.value.id : undefined, }); @@ -325,7 +327,7 @@ function createFolder() { placeholder: i18n.ts.folderName, }).then(({ canceled, result: name }) => { if (canceled) return; - os.api('drive/folders/create', { + misskeyApi('drive/folders/create', { name: name, parentId: folder.value ? folder.value.id : undefined, }).then(createdFolder => { @@ -341,7 +343,7 @@ function renameFolder(folderToRename: Misskey.entities.DriveFolder) { default: folderToRename.name, }).then(({ canceled, result: name }) => { if (canceled) return; - os.api('drive/folders/update', { + misskeyApi('drive/folders/update', { folderId: folderToRename.id, name: name, }).then(updatedFolder => { @@ -352,7 +354,7 @@ function renameFolder(folderToRename: Misskey.entities.DriveFolder) { } function deleteFolder(folderToDelete: Misskey.entities.DriveFolder) { - os.api('drive/folders/delete', { + misskeyApi('drive/folders/delete', { folderId: folderToDelete.id, }).then(() => { // 削除時ã«è¦ªãƒ•ã‚©ãƒ«ãƒ€ã«ç§»å‹• @@ -426,7 +428,7 @@ function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) { } } -function move(target?: Misskey.entities.DriveFolder) { +function move(target?: Misskey.entities.DriveFolder | Misskey.entities.DriveFolder['id' | 'parentId']) { if (!target) { goRoot(); return; @@ -436,7 +438,7 @@ function move(target?: Misskey.entities.DriveFolder) { fetching.value = true; - os.api('drive/folders/show', { + misskeyApi('drive/folders/show', { folderId: target, }).then(folderToMove => { folder.value = folderToMove; @@ -535,7 +537,7 @@ async function fetch() { const foldersMax = 30; const filesMax = 30; - const foldersPromise = os.api('drive/folders', { + const foldersPromise = misskeyApi('drive/folders', { folderId: folder.value ? folder.value.id : null, limit: foldersMax + 1, }).then(fetchedFolders => { @@ -546,7 +548,7 @@ async function fetch() { return fetchedFolders; }); - const filesPromise = os.api('drive/files', { + const filesPromise = misskeyApi('drive/files', { folderId: folder.value ? folder.value.id : null, type: props.type, limit: filesMax + 1, @@ -571,7 +573,7 @@ function fetchMoreFolders() { const max = 30; - os.api('drive/folders', { + misskeyApi('drive/folders', { folderId: folder.value ? folder.value.id : null, type: props.type, untilId: folders.value.at(-1)?.id, @@ -594,7 +596,7 @@ function fetchMoreFiles() { const max = 30; // ファイル一覧å–å¾— - os.api('drive/files', { + misskeyApi('drive/files', { folderId: folder.value ? folder.value.id : null, type: props.type, untilId: files.value.at(-1)?.id, @@ -612,7 +614,7 @@ function fetchMoreFiles() { } function getMenu() { - return [{ + const menu: MenuItem[] = [{ type: 'switch', text: i18n.ts.keepOriginalUploading, ref: keepOriginal, @@ -633,7 +635,7 @@ function getMenu() { }, folder.value ? { text: i18n.ts.renameFolder, icon: 'ph-textbox ph-bold ph-lg', - action: () => { renameFolder(folder.value); }, + action: () => { if (folder.value) renameFolder(folder.value); }, } : undefined, folder.value ? { text: i18n.ts.deleteFolder, icon: 'ph-trash ph-bold ph-lg', @@ -643,6 +645,8 @@ function getMenu() { icon: 'ph-folder ph-bold ph-lg-plus', action: () => { createFolder(); }, }]; + + return menu; } function showMenu(ev: MouseEvent) { diff --git a/packages/frontend/src/components/MkDriveFileThumbnail.vue b/packages/frontend/src/components/MkDriveFileThumbnail.vue index 306352379146830bf494fc2c2c1445023bdd4247..2f1fef4ea658bc1fbc0d186bb24d7f7a0681470e 100644 --- a/packages/frontend/src/components/MkDriveFileThumbnail.vue +++ b/packages/frontend/src/components/MkDriveFileThumbnail.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkDriveSelectDialog.vue b/packages/frontend/src/components/MkDriveSelectDialog.vue index e65f4dd4034127693cac4c8064a5b8d3201e6590..f1ecc27123b33d1b2ae2fe9cd88b447373c9ff37 100644 --- a/packages/frontend/src/components/MkDriveSelectDialog.vue +++ b/packages/frontend/src/components/MkDriveSelectDialog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -39,13 +39,13 @@ withDefaults(defineProps<{ }); const emit = defineEmits<{ - (ev: 'done', r?: Misskey.entities.DriveFile[]): void; + (ev: 'done', r?: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]): void; (ev: 'closed'): void; }>(); const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); -const selected = ref<Misskey.entities.DriveFile[]>([]); +const selected = ref<Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]>([]); function ok() { emit('done', selected.value); @@ -57,7 +57,7 @@ function cancel() { dialog.value?.close(); } -function onChangeSelection(files: Misskey.entities.DriveFile[]) { - selected.value = files; +function onChangeSelection(v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]) { + selected.value = v; } </script> diff --git a/packages/frontend/src/components/MkDriveWindow.vue b/packages/frontend/src/components/MkDriveWindow.vue index 72aa79b153983a2b9661d5811ad9776ef7108940..c0142ec76e3e43f02bb8d3356683ae866f9264db 100644 --- a/packages/frontend/src/components/MkDriveWindow.vue +++ b/packages/frontend/src/components/MkDriveWindow.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue index dabc12237a4efadaecdb82388af90bb7dbb1cd2a..a5839586b680173b59a0b636009d799498a3d88d 100644 --- a/packages/frontend/src/components/MkEmojiPicker.section.vue +++ b/packages/frontend/src/components/MkEmojiPicker.section.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -16,10 +16,11 @@ SPDX-License-Identifier: AGPL-3.0-only :key="emoji" :data-emoji="emoji" class="_button item" + :disabled="disabledEmojis?.value.includes(emoji)" @pointerenter="computeButtonTitle" @click="emit('chosen', emoji, $event)" > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> + <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true" :fallbackToImage="true"/> <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> </button> </div> @@ -27,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only <!-- フォルダã®ä¸ã«ã¯ã‚«ã‚¹ã‚¿ãƒ 絵文å—やフォルダãŒã‚ã‚‹ --> <section v-else v-panel style="border-radius: var(--radius-sm); border-bottom: 0.5px solid var(--divider);"> <header class="_acrylic" @click="shown = !shown"> - <i class="toggle ti-fw" :class="shown ? 'ph-caret-down ph-bold ph-lg' : 'ph-caret-up ph-bold ph-lg'"></i> <slot></slot> (<i class="ph-folder ph-bold ph-lg"></i>:{{ customEmojiTree.length }} <i class="ph-smiley-sticker ph-bold ph-lg ti-fw"></i>:{{ emojis.length }}) + <i class="toggle ti-fw" :class="shown ? 'ph-caret-down ph-bold ph-lg' : 'ph-caret-up ph-bold ph-lg'"></i> <slot></slot> (<i class="ph-folder ph-bold ph-lg"></i>:{{ customEmojiTree?.length }} <i class="ph-smiley-sticker ph-bold ph-lg ti-fw"></i>:{{ emojis.length }}) </header> <div v-if="shown" style="padding-left: 9px;"> <MkEmojiPickerSection @@ -48,6 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only :key="emoji" :data-emoji="emoji" class="_button item" + :disabled="disabledEmojis?.value.includes(emoji)" @pointerenter="computeButtonTitle" @click="emit('chosen', emoji, $event)" > @@ -60,13 +62,14 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref, computed, Ref } from 'vue'; -import { i18n } from '../i18n.js'; import { CustomEmojiFolderTree, getEmojiName } from '@/scripts/emojilist.js'; +import { i18n } from '@/i18n.js'; import { customEmojis } from '@/custom-emojis.js'; import MkEmojiPickerSection from '@/components/MkEmojiPicker.section.vue'; const props = defineProps<{ emojis: string[] | Ref<string[]>; + disabledEmojis?: Ref<string[]>; initialShown?: boolean; hasChildSection?: boolean; customEmojiTree?: CustomEmojiFolderTree[]; @@ -84,10 +87,10 @@ const shown = ref(!!props.initialShown); function computeButtonTitle(ev: MouseEvent): void { const elm = ev.target as HTMLElement; const emoji = elm.dataset.emoji as string; - elm.title = getEmojiName(emoji) ?? emoji; + elm.title = getEmojiName(emoji); } -function nestedChosen(emoji: any, ev?: MouseEvent) { +function nestedChosen(emoji: any, ev: MouseEvent) { emit('chosen', emoji, ev); } </script> diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index b7e329d7c22cc087c4d538be7f4dc70ee0bdd0fb..1219a29d857d5d15d240e6cc66fe28d5ec1c7c48 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -14,11 +14,12 @@ SPDX-License-Identifier: AGPL-3.0-only v-for="emoji in searchResultCustom" :key="emoji.name" class="_button item" + :disabled="!canReact(emoji)" :title="emoji.name" tabindex="0" @click="chosen(emoji, $event)" > - <MkCustomEmoji class="emoji" :name="emoji.name"/> + <MkCustomEmoji class="emoji" :name="emoji.name" :fallbackToImage="true"/> </button> </div> <div v-if="searchResultUnicode.length > 0" class="body"> @@ -36,19 +37,20 @@ SPDX-License-Identifier: AGPL-3.0-only </section> <div v-if="tab === 'index'" class="group index"> - <section v-if="showPinned && pinned.length > 0"> + <section v-if="showPinned && (pinned && pinned.length > 0)"> <div class="body"> <button - v-for="emoji in pinned" - :key="emoji" - :data-emoji="emoji" + v-for="emoji in pinnedEmojisDef" + :key="getKey(emoji)" + :data-emoji="getKey(emoji)" class="_button item" + :disabled="!canReact(emoji)" tabindex="0" @pointerenter="computeButtonTitle" @click="chosen(emoji, $event)" > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + <MkCustomEmoji v-if="!emoji.hasOwnProperty('char')" class="emoji" :name="getKey(emoji)" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="getKey(emoji)" :normal="true"/> </button> </div> </section> @@ -57,15 +59,16 @@ SPDX-License-Identifier: AGPL-3.0-only <header class="_acrylic"><i class="ph-clock ph-bold ph-lg ti-fw"></i> {{ i18n.ts.recentUsed }}</header> <div class="body"> <button - v-for="emoji in recentlyUsedEmojis" - :key="emoji" + v-for="emoji in recentlyUsedEmojisDef" + :key="getKey(emoji)" class="_button item" - :data-emoji="emoji" + :disabled="!canReact(emoji)" + :data-emoji="getKey(emoji)" @pointerenter="computeButtonTitle" @click="chosen(emoji, $event)" > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + <MkCustomEmoji v-if="!emoji.hasOwnProperty('char')" class="emoji" :name="getKey(emoji)" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="getKey(emoji)" :normal="true"/> </button> </div> </section> @@ -76,7 +79,8 @@ SPDX-License-Identifier: AGPL-3.0-only v-for="child in customEmojiFolderRoot.children" :key="`custom:${child.value}`" :initialShown="false" - :emojis="computed(() => customEmojis.filter(e => child.value === '' ? (e.category === 'null' || !e.category) : e.category === child.value).filter(filterAvailable).map(e => `:${e.name}:`))" + :emojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value)).map(e => `:${e.name}:`))" + :disabledEmojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value)).filter(e => !canReact(e)).map(e => `:${e.name}:`))" :hasChildSection="child.children.length !== 0" :customEmojiTree="child.children" @chosen="chosen" @@ -109,6 +113,7 @@ import { unicodeEmojiCategories as categories, getEmojiName, CustomEmojiFolderTree, + getUnicodeEmoji, } from '@/scripts/emojilist.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import * as os from '@/os.js'; @@ -118,6 +123,7 @@ import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; import { customEmojiCategories, customEmojis, customEmojisMap } from '@/custom-emojis.js'; import { $i } from '@/account.js'; +import { checkReactionPermissions } from '@/scripts/check-reaction-permissions.js'; const props = withDefaults(defineProps<{ showPinned?: boolean; @@ -126,6 +132,7 @@ const props = withDefaults(defineProps<{ asDrawer?: boolean; asWindow?: boolean; asReactionPicker?: boolean; // 今ã¯ä½¿ã‚ã‚Œã¦ãªã„ãŒå°†æ¥çš„ã«ä½¿ã„ãㆠ+ targetNote?: Misskey.entities.Note; }>(), { showPinned: true, }); @@ -144,6 +151,13 @@ const { recentlyUsedEmojis, } = defaultStore.reactiveState; +const recentlyUsedEmojisDef = computed(() => { + return recentlyUsedEmojis.value.map(getDef).filter(x => x != null); +}); +const pinnedEmojisDef = computed(() => { + return pinned.value?.map(getDef).filter(x => x != null); +}); + const pinned = computed(() => props.pinnedEmojis); const size = computed(() => emojiPickerScale.value); const width = computed(() => emojiPickerWidth.value); @@ -221,6 +235,19 @@ watch(q, () => { } } } else { + if (customEmojisMap.has(newQ)) { + matches.add(customEmojisMap.get(newQ)!); + } + if (matches.size >= max) return matches; + + for (const emoji of emojis) { + if (emoji.aliases.some(alias => alias === newQ)) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; + for (const emoji of emojis) { if (emoji.name.startsWith(newQ)) { matches.add(emoji); @@ -322,12 +349,16 @@ watch(q, () => { return matches; }; - searchResultCustom.value = Array.from(searchCustom()).filter(filterAvailable); + searchResultCustom.value = Array.from(searchCustom()); searchResultUnicode.value = Array.from(searchUnicode()); }); -function filterAvailable(emoji: Misskey.entities.EmojiSimple): boolean { - return (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.includes(r.id))); +function canReact(emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef | string): boolean { + return !props.targetNote || checkReactionPermissions($i!, props.targetNote, emoji); +} + +function filterCategory(emoji: Misskey.entities.EmojiSimple, category: string): boolean { + return category === '' ? (emoji.category === 'null' || !emoji.category) : emoji.category === category; } function focus() { @@ -347,11 +378,22 @@ function getKey(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef): return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`; } +function getDef(emoji: string): string | Misskey.entities.EmojiSimple | UnicodeEmojiDef { + if (emoji.includes(':')) { + // カスタム絵文å—ãŒå˜åœ¨ã™ã‚‹å ´åˆã¯ãã®æƒ…å ±ã‚’æŒã¤ã‚ªãƒ–ジェクトを返ã—〠+ // サーãƒã®ç®¡ç†ç”»é¢ã‹ã‚‰å‰Šé™¤ã•ã‚ŒãŸç‰ã§æƒ…å ±ãŒè¦‹ã¤ã‹ã‚‰ãªã„å ´åˆã¯åå‰ã®æ–‡å—列をãã®ã¾ã¾è¿”ã—ã¦ãŠã(undefinedã‚’è¿”ã™ã¨ã‚¨ãƒ©ãƒ¼ã«ãªã‚‹ãŸã‚) + const name = emoji.replaceAll(':', ''); + return customEmojisMap.get(name) ?? emoji; + } else { + return getUnicodeEmoji(emoji); + } +} + /** @see MkEmojiPicker.section.vue */ function computeButtonTitle(ev: MouseEvent): void { const elm = ev.target as HTMLElement; const emoji = elm.dataset.emoji as string; - elm.title = getEmojiName(emoji) ?? emoji; + elm.title = getEmojiName(emoji); } function chosen(emoji: any, ev?: MouseEvent) { @@ -511,6 +553,18 @@ defineExpose({ width: auto; height: auto; min-width: 0; + + &:disabled { + cursor: not-allowed; + background: linear-gradient(-45deg, transparent 0% 48%, var(--X6) 48% 52%, transparent 52% 100%); + opacity: 1; + + > .emoji { + filter: grayscale(1); + mix-blend-mode: exclusion; + opacity: 0.8; + } + } } } } @@ -533,6 +587,18 @@ defineExpose({ width: auto; height: auto; min-width: 0; + + &:disabled { + cursor: not-allowed; + background: linear-gradient(-45deg, transparent 0% 48%, var(--X6) 48% 52%, transparent 52% 100%); + opacity: 1; + + > .emoji { + filter: grayscale(1); + mix-blend-mode: exclusion; + opacity: 0.8; + } + } } } } @@ -648,6 +714,18 @@ defineExpose({ box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15); } + &:disabled { + cursor: not-allowed; + background: linear-gradient(-45deg, transparent 0% 48%, var(--X6) 48% 52%, transparent 52% 100%); + opacity: 1; + + > .emoji { + filter: grayscale(1); + mix-blend-mode: exclusion; + opacity: 0.8; + } + } + > .emoji { height: 1.25em; vertical-align: -.25em; diff --git a/packages/frontend/src/components/MkEmojiPickerDialog.vue b/packages/frontend/src/components/MkEmojiPickerDialog.vue index 4068a79f085f280a9c2069c0245b2b84f4eb6d37..c6b38969894789710fd768ffcf5687d7319d22bd 100644 --- a/packages/frontend/src/components/MkEmojiPickerDialog.vue +++ b/packages/frontend/src/components/MkEmojiPickerDialog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -24,6 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only :showPinned="showPinned" :pinnedEmojis="pinnedEmojis" :asReactionPicker="asReactionPicker" + :targetNote="targetNote" :asDrawer="type === 'drawer'" :max-height="maxHeight" @chosen="chosen" @@ -32,6 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import * as Misskey from 'misskey-js'; import { shallowRef } from 'vue'; import MkModal from '@/components/MkModal.vue'; import MkEmojiPicker from '@/components/MkEmojiPicker.vue'; @@ -43,6 +45,7 @@ const props = withDefaults(defineProps<{ showPinned?: boolean; pinnedEmojis?: string[], asReactionPicker?: boolean; + targetNote?: Misskey.entities.Note; choseAndClose?: boolean; }>(), { manualShowing: null, @@ -53,7 +56,7 @@ const props = withDefaults(defineProps<{ }); const emit = defineEmits<{ - (ev: 'done', v: any): void; + (ev: 'done', v: string): void; (ev: 'close'): void; (ev: 'closed'): void; }>(); @@ -61,7 +64,7 @@ const emit = defineEmits<{ const modal = shallowRef<InstanceType<typeof MkModal>>(); const picker = shallowRef<InstanceType<typeof MkEmojiPicker>>(); -function chosen(emoji: any) { +function chosen(emoji: string) { emit('done', emoji); if (props.choseAndClose) { modal.value?.close(); diff --git a/packages/frontend/src/components/MkEmojiPickerWindow.vue b/packages/frontend/src/components/MkEmojiPickerWindow.vue deleted file mode 100644 index 1a2c55e785d569fc4a53d9130a657bd1546538ef..0000000000000000000000000000000000000000 --- a/packages/frontend/src/components/MkEmojiPickerWindow.vue +++ /dev/null @@ -1,47 +0,0 @@ -<!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors -SPDX-License-Identifier: AGPL-3.0-only ---> - -<template> -<MkWindow - ref="window" - :initialWidth="300" - :initialHeight="290" - :canResize="true" - :mini="true" - :front="true" - @closed="emit('closed')" -> - <MkEmojiPicker :showPinned="showPinned" :asReactionPicker="asReactionPicker" asWindow :class="$style.picker" @chosen="chosen"/> -</MkWindow> -</template> - -<script lang="ts" setup> -import { } from 'vue'; -import MkWindow from '@/components/MkWindow.vue'; -import MkEmojiPicker from '@/components/MkEmojiPicker.vue'; - -withDefaults(defineProps<{ - src?: HTMLElement; - showPinned?: boolean; - asReactionPicker?: boolean; -}>(), { - showPinned: true, -}); - -const emit = defineEmits<{ - (ev: 'chosen', v: any): void; - (ev: 'closed'): void; -}>(); - -function chosen(emoji: any) { - emit('chosen', emoji); -} -</script> - -<style lang="scss" module> -.picker { - height: 100%; -} -</style> diff --git a/packages/frontend/src/components/MkFeaturedPhotos.vue b/packages/frontend/src/components/MkFeaturedPhotos.vue index 6d1bad74334c1cfdfcfaf544cbda183746d6ec9d..8d875790bc349545680940c24e712efcc643c3ef 100644 --- a/packages/frontend/src/components/MkFeaturedPhotos.vue +++ b/packages/frontend/src/components/MkFeaturedPhotos.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -10,11 +10,11 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; import * as Misskey from 'misskey-js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const meta = ref<Misskey.entities.MetaResponse>(); -os.api('meta', { detail: true }).then(gotMeta => { +misskeyApi('meta', { detail: true }).then(gotMeta => { meta.value = gotMeta; }); </script> diff --git a/packages/frontend/src/components/MkFileCaptionEditWindow.vue b/packages/frontend/src/components/MkFileCaptionEditWindow.vue index b799fb94471ef55bd3f197c070f9ebd825251d4e..39551e6b3c78f9b3d080d11d5ee727f081c3f981 100644 --- a/packages/frontend/src/components/MkFileCaptionEditWindow.vue +++ b/packages/frontend/src/components/MkFileCaptionEditWindow.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only :withOkButton="true" :okButtonDisabled="false" @ok="ok()" - @close="dialog.close()" + @close="dialog?.close()" @closed="emit('closed')" > <template #header>{{ i18n.ts.describeFile }}</template> @@ -48,6 +48,6 @@ const caption = ref(props.default); async function ok() { emit('done', caption.value); - dialog.value.close(); + dialog.value?.close(); } </script> diff --git a/packages/frontend/src/components/MkFileListForAdmin.vue b/packages/frontend/src/components/MkFileListForAdmin.vue index eb0d4d61ac2922c1575655ebede2b843a6a70986..f3305e9f54b768bcd914467e9ae2dbad61934dbd 100644 --- a/packages/frontend/src/components/MkFileListForAdmin.vue +++ b/packages/frontend/src/components/MkFileListForAdmin.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div> <MkPagination v-slot="{items}" :pagination="pagination" class="urempief" :class="{ grid: viewMode === 'grid' }"> <MkA - v-for="file in items" + v-for="file in (items as Misskey.entities.DriveFile[])" :key="file.id" v-tooltip.mfm="`${file.type}\n${bytes(file.size)}\n${dateString(file.createdAt)}\nby ${file.user ? '@' + Misskey.acct.toString(file.user) : 'system'}`" :to="`/admin/file/${file.id}`" diff --git a/packages/frontend/src/components/MkFlashPreview.vue b/packages/frontend/src/components/MkFlashPreview.vue index ab435585d90c35590700e92e95df337a07b3f7df..c5dd87797105aa160490c040d7c452e648cafc80 100644 --- a/packages/frontend/src/components/MkFlashPreview.vue +++ b/packages/frontend/src/components/MkFlashPreview.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -9,7 +9,9 @@ SPDX-License-Identifier: AGPL-3.0-only <header> <h1 :title="flash.title">{{ flash.title }}</h1> </header> - <p v-if="flash.summary" :title="flash.summary">{{ flash.summary.length > 85 ? flash.summary.slice(0, 85) + '…' : flash.summary }}</p> + <p v-if="flash.summary" :title="flash.summary"> + <Mfm class="summaryMfm" :text="flash.summary" :plain="true" :nowrap="true"/> + </p> <footer> <img class="icon" :src="flash.user.avatarUrl"/> <p>{{ userName(flash.user) }}</p> @@ -54,6 +56,12 @@ const props = defineProps<{ margin: 0; color: var(--urlPreviewText); font-size: 0.8em; + overflow: clip; + + > .summaryMfm { + display: block; + width: 100%; + } } > footer { diff --git a/packages/frontend/src/components/MkFoldableSection.vue b/packages/frontend/src/components/MkFoldableSection.vue index 65afc48f062a958b05f691276792684c268b5724..51bcafd1c22be333eef4dc5c81e6369a21b204fa 100644 --- a/packages/frontend/src/components/MkFoldableSection.vue +++ b/packages/frontend/src/components/MkFoldableSection.vue @@ -1,10 +1,10 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div ref="el" :class="$style.root"> +<div ref="rootEl" :class="$style.root"> <header :class="$style.header" class="_button" :style="{ background: bg }" @click="showBody = !showBody"> <div :class="$style.title"><div><slot name="header"></slot></div></div> <div :class="$style.divider"></div> @@ -14,7 +14,10 @@ SPDX-License-Identifier: AGPL-3.0-only </button> </header> <Transition - :name="defaultStore.state.animation ? 'folder-toggle' : ''" + :enterActiveClass="defaultStore.state.animation ? $style.folderToggleEnterActive : ''" + :leaveActiveClass="defaultStore.state.animation ? $style.folderToggleLeaveActive : ''" + :enterFromClass="defaultStore.state.animation ? $style.folderToggleEnterFrom : ''" + :leaveToClass="defaultStore.state.animation ? $style.folderToggleLeaveTo : ''" @enter="enter" @afterEnter="afterEnter" @leave="leave" @@ -42,8 +45,8 @@ const props = withDefaults(defineProps<{ expanded: true, }); -const el = shallowRef<HTMLDivElement>(); -const bg = ref<string | null>(null); +const rootEl = shallowRef<HTMLDivElement>(); +const bg = ref<string>(); const showBody = ref((props.persistKey && miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`)) ? (miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`) === 't') : props.expanded); watch(showBody, () => { @@ -52,40 +55,44 @@ watch(showBody, () => { } }); -function enter(el: Element) { +function enter(element: Element) { + const el = element as HTMLElement; const elementHeight = el.getBoundingClientRect().height; - el.style.height = 0; + el.style.height = '0'; el.offsetHeight; // reflow el.style.height = elementHeight + 'px'; } -function afterEnter(el: Element) { - el.style.height = null; +function afterEnter(element: Element) { + const el = element as HTMLElement; + el.style.height = 'unset'; } -function leave(el: Element) { +function leave(element: Element) { + const el = element as HTMLElement; const elementHeight = el.getBoundingClientRect().height; el.style.height = elementHeight + 'px'; el.offsetHeight; // reflow - el.style.height = 0; + el.style.height = '0'; } -function afterLeave(el: Element) { - el.style.height = null; +function afterLeave(element: Element) { + const el = element as HTMLElement; + el.style.height = 'unset'; } onMounted(() => { - function getParentBg(el: HTMLElement | null): string { + function getParentBg(el?: HTMLElement | null): string { if (el == null || el.tagName === 'BODY') return 'var(--bg)'; - const bg = el.style.background || el.style.backgroundColor; - if (bg) { - return bg; + const background = el.style.background || el.style.backgroundColor; + if (background) { + return background; } else { return getParentBg(el.parentElement); } } - const rawBg = getParentBg(el.value); + const rawBg = getParentBg(rootEl.value); const _bg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); _bg.setAlpha(0.85); bg.value = _bg.toRgbString(); @@ -93,14 +100,12 @@ onMounted(() => { </script> <style lang="scss" module> -.folder-toggle-enter-active, .folder-toggle-leave-active { +.folderToggleEnterActive, .folderToggleLeaveActive { overflow-y: clip; transition: opacity 0.5s, height 0.5s !important; } -.folder-toggle-enter-from { - opacity: 0; -} -.folder-toggle-leave-to { + +.folderToggleEnterFrom, .folderToggleLeaveTo { opacity: 0; } diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue index 03621a4255188c8b7f1fc926995b732fc614c692..64d390f52b4b8f9ecbef7fb69f50f268d5b802b5 100644 --- a/packages/frontend/src/components/MkFolder.vue +++ b/packages/frontend/src/components/MkFolder.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </template> - <div v-if="openedAtLeastOnce" :class="[$style.body, { [$style.bgSame]: bgSame }]" :style="{ maxHeight: maxHeight ? `${maxHeight}px` : null, overflow: maxHeight ? `auto` : null }" :aria-hidden="!opened"> + <div v-if="openedAtLeastOnce" :class="[$style.body, { [$style.bgSame]: bgSame }]" :style="{ maxHeight: maxHeight ? `${maxHeight}px` : undefined, overflow: maxHeight ? `auto` : undefined }" :aria-hidden="!opened"> <Transition :enterActiveClass="defaultStore.state.animation ? $style.transition_toggle_enterActive : ''" :leaveActiveClass="defaultStore.state.animation ? $style.transition_toggle_leaveActive : ''" @@ -109,7 +109,7 @@ function toggle() { onMounted(() => { const computedStyle = getComputedStyle(document.documentElement); - const parentBg = getBgColor(rootEl.value.parentElement); + const parentBg = getBgColor(rootEl.value!.parentElement!); const myBg = computedStyle.getPropertyValue('--panel'); bgSame.value = parentBg === myBg; }); diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue index d1b1956a0317d0cb31ac0b6a4d36e018585becd3..d0e8750e6a4ee884505f6086978b9dd1f769135e 100644 --- a/packages/frontend/src/components/MkFollowButton.vue +++ b/packages/frontend/src/components/MkFollowButton.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -38,11 +38,12 @@ SPDX-License-Identifier: AGPL-3.0-only import { onBeforeUnmount, onMounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { useStream } from '@/stream.js'; import { i18n } from '@/i18n.js'; import { claimAchievement } from '@/scripts/achievements.js'; import { $i } from '@/account.js'; -import { defaultStore } from "@/store.js"; +import { defaultStore } from '@/store.js'; const props = withDefaults(defineProps<{ user: Misskey.entities.UserDetailed, @@ -63,7 +64,7 @@ const wait = ref(false); const connection = useStream().useChannel('main'); if (props.user.isFollowing == null) { - os.api('users/show', { + misskeyApi('users/show', { userId: props.user.id, }) .then(onFollowChange); @@ -83,22 +84,22 @@ async function onClick() { if (isFollowing.value) { const { canceled } = await os.confirm({ type: 'warning', - text: i18n.t('unfollowConfirm', { name: props.user.name || props.user.username }), + text: i18n.tsx.unfollowConfirm({ name: props.user.name || props.user.username }), }); if (canceled) return; - await os.api('following/delete', { + await misskeyApi('following/delete', { userId: props.user.id, }); } else { if (hasPendingFollowRequestFromYou.value) { - await os.api('following/requests/cancel', { + await misskeyApi('following/requests/cancel', { userId: props.user.id, }); hasPendingFollowRequestFromYou.value = false; } else { - await os.api('following/create', { + await misskeyApi('following/create', { userId: props.user.id, withReplies: defaultStore.state.defaultWithReplies, }); diff --git a/packages/frontend/src/components/MkForgotPassword.vue b/packages/frontend/src/components/MkForgotPassword.vue index 9b57688a027084856997336dd37e83522890b04a..35112ad45d3c1d9e965aaf6c4ff169bfea33823f 100644 --- a/packages/frontend/src/components/MkForgotPassword.vue +++ b/packages/frontend/src/components/MkForgotPassword.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only ref="dialog" :width="370" :height="400" - @close="dialog.close()" + @close="dialog?.close()" @closed="emit('closed')" > <template #header>{{ i18n.ts.forgotPassword }}</template> @@ -66,6 +66,6 @@ async function onSubmit() { email: email.value, }); emit('done'); - dialog.value.close(); + dialog.value?.close(); } </script> diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue index 6f882cfab772a9d4157f23729985fea63b8d5518..deedc5badb1bdc60ee0dd249049042c7c5832968 100644 --- a/packages/frontend/src/components/MkFormDialog.vue +++ b/packages/frontend/src/components/MkFormDialog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -20,41 +20,45 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <MkSpacer :marginMin="20" :marginMax="32"> - <div class="_gaps_m"> - <template v-for="item in Object.keys(form).filter(item => !form[item].hidden)"> - <MkInput v-if="form[item].type === 'number'" v-model="values[item]" type="number" :step="form[item].step || 1"> - <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template> - <template v-if="form[item].description" #caption>{{ form[item].description }}</template> + <div v-if="Object.keys(form).filter(item => !form[item].hidden).length > 0" class="_gaps_m"> + <template v-for="(v, k) in Object.fromEntries(Object.entries(form).filter(([_, v]) => !('hidden' in v) || 'hidden' in v && !v.hidden))"> + <MkInput v-if="v.type === 'number'" v-model="values[k]" type="number" :step="v.step || 1"> + <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template> + <template v-if="v.description" #caption>{{ v.description }}</template> </MkInput> - <MkInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model="values[item]" type="text" :mfmAutocomplete="form[item].treatAsMfm"> - <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template> - <template v-if="form[item].description" #caption>{{ form[item].description }}</template> + <MkInput v-else-if="v.type === 'string' && !v.multiline" v-model="values[k]" type="text" :mfmAutocomplete="v.treatAsMfm"> + <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template> + <template v-if="v.description" #caption>{{ v.description }}</template> </MkInput> - <MkTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model="values[item]" :mfmAutocomplete="form[item].treatAsMfm" :mfmPreview="form[item].treatAsMfm"> - <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template> - <template v-if="form[item].description" #caption>{{ form[item].description }}</template> + <MkTextarea v-else-if="v.type === 'string' && v.multiline" v-model="values[k]" :mfmAutocomplete="v.treatAsMfm" :mfmPreview="v.treatAsMfm"> + <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template> + <template v-if="v.description" #caption>{{ v.description }}</template> </MkTextarea> - <MkSwitch v-else-if="form[item].type === 'boolean'" v-model="values[item]"> - <span v-text="form[item].label || item"></span> - <template v-if="form[item].description" #caption>{{ form[item].description }}</template> + <MkSwitch v-else-if="v.type === 'boolean'" v-model="values[k]"> + <span v-text="v.label || k"></span> + <template v-if="v.description" #caption>{{ v.description }}</template> </MkSwitch> - <MkSelect v-else-if="form[item].type === 'enum'" v-model="values[item]"> - <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template> - <option v-for="item in form[item].enum" :key="item.value" :value="item.value">{{ item.label }}</option> + <MkSelect v-else-if="v.type === 'enum'" v-model="values[k]"> + <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template> + <option v-for="option in v.enum" :key="option.value" :value="option.value">{{ option.label }}</option> </MkSelect> - <MkRadios v-else-if="form[item].type === 'radio'" v-model="values[item]"> - <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template> - <option v-for="item in form[item].options" :key="item.value" :value="item.value">{{ item.label }}</option> + <MkRadios v-else-if="v.type === 'radio'" v-model="values[k]"> + <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template> + <option v-for="option in v.options" :key="option.value" :value="option.value">{{ option.label }}</option> </MkRadios> - <MkRange v-else-if="form[item].type === 'range'" v-model="values[item]" :min="form[item].min" :max="form[item].max" :step="form[item].step" :textConverter="form[item].textConverter"> - <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template> - <template v-if="form[item].description" #caption>{{ form[item].description }}</template> + <MkRange v-else-if="v.type === 'range'" v-model="values[k]" :min="v.min" :max="v.max" :step="v.step" :textConverter="v.textConverter"> + <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template> + <template v-if="v.description" #caption>{{ v.description }}</template> </MkRange> - <MkButton v-else-if="form[item].type === 'button'" @click="form[item].action($event, values)"> - <span v-text="form[item].content || item"></span> + <MkButton v-else-if="v.type === 'button'" @click="v.action($event, values)"> + <span v-text="v.content || k"></span> </MkButton> </template> </div> + <div v-else class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.nothing }}</div> + </div> </MkSpacer> </MkModalWindow> </template> @@ -68,19 +72,23 @@ import MkSelect from './MkSelect.vue'; import MkRange from './MkRange.vue'; import MkButton from './MkButton.vue'; import MkRadios from './MkRadios.vue'; +import type { Form } from '@/scripts/form.js'; import MkModalWindow from '@/components/MkModalWindow.vue'; import { i18n } from '@/i18n.js'; +import { infoImageUrl } from '@/instance.js'; const props = defineProps<{ title: string; - form: any; + form: Form; }>(); const emit = defineEmits<{ (ev: 'done', v: { - canceled?: boolean; - result?: any; + canceled: true; + } | { + result: Record<string, any>; }): void; + (ev: 'closed'): void; }>(); const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); @@ -94,13 +102,13 @@ function ok() { emit('done', { result: values, }); - dialog.value.close(); + dialog.value?.close(); } function cancel() { emit('done', { canceled: true, }); - dialog.value.close(); + dialog.value?.close(); } </script> diff --git a/packages/frontend/src/components/MkGalleryPostPreview.stories.impl.ts b/packages/frontend/src/components/MkGalleryPostPreview.stories.impl.ts index 035b727a35c2e2a491d013db97f799137007c19b..a433ad680bbefe3b408b67fbce88f20a218e3a74 100644 --- a/packages/frontend/src/components/MkGalleryPostPreview.stories.impl.ts +++ b/packages/frontend/src/components/MkGalleryPostPreview.stories.impl.ts @@ -1,11 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { expect } from '@storybook/jest'; -import { userEvent, waitFor, within } from '@storybook/testing-library'; +import { expect, userEvent, waitFor, within } from '@storybook/test'; import { StoryObj } from '@storybook/vue3'; import { galleryPost } from '../../.storybook/fakes.js'; import MkGalleryPostPreview from './MkGalleryPostPreview.vue'; diff --git a/packages/frontend/src/components/MkGalleryPostPreview.vue b/packages/frontend/src/components/MkGalleryPostPreview.vue index 316632b1a65ef3d2ae267f0ab74019220cdae30d..47cccd9b7cf544b86d187b3c00d0c1cef47f8eb6 100644 --- a/packages/frontend/src/components/MkGalleryPostPreview.vue +++ b/packages/frontend/src/components/MkGalleryPostPreview.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -14,8 +14,8 @@ SPDX-License-Identifier: AGPL-3.0-only leaveActiveClass: $style.transition_toggle_leaveActive, leaveToClass: $style.transition_toggle_leaveTo, }" - :src="post.files[0].thumbnailUrl" - :hash="post.files[0].blurhash" + :src="post.files?.[0]?.thumbnailUrl" + :hash="post.files?.[0]?.blurhash" :forceBlurhash="!show" /> </Transition> diff --git a/packages/frontend/src/components/MkGoogle.vue b/packages/frontend/src/components/MkGoogle.vue index c0b20507fcf16bef1581cc6cec2a6b9359a4eab3..c92a49d32ade8aaccb7bfb8829c39b0f11903b7f 100644 --- a/packages/frontend/src/components/MkGoogle.vue +++ b/packages/frontend/src/components/MkGoogle.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkHeatmap.vue b/packages/frontend/src/components/MkHeatmap.vue index a57e6c92922e2f4b4fe41a421a7c117c160a0ae4..0cc0df991162ce08e13c347702db9ab3c3dfe083 100644 --- a/packages/frontend/src/components/MkHeatmap.vue +++ b/packages/frontend/src/components/MkHeatmap.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -15,7 +15,8 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onMounted, nextTick, watch, shallowRef, ref } from 'vue'; import { Chart } from 'chart.js'; -import * as os from '@/os.js'; +import * as Misskey from 'misskey-js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import { alpha } from '@/scripts/color.js'; @@ -23,14 +24,21 @@ import { initChart } from '@/scripts/init-chart.js'; initChart(); -const props = defineProps<{ - src: string; -}>(); +export type HeatmapSource = 'active-users' | 'notes' | 'ap-requests-inbox-received' | 'ap-requests-deliver-succeeded' | 'ap-requests-deliver-failed'; -const rootEl = shallowRef<HTMLDivElement>(null); -const chartEl = shallowRef<HTMLCanvasElement>(null); +const props = withDefaults(defineProps<{ + src: HeatmapSource; + user?: Misskey.entities.User; + label?: string; +}>(), { + user: undefined, + label: '', +}); + +const rootEl = shallowRef<HTMLDivElement | null>(null); +const chartEl = shallowRef<HTMLCanvasElement | null>(null); const now = new Date(); -let chartInstance: Chart = null; +let chartInstance: Chart | null = null; const fetching = ref(true); const { handler: externalTooltipHandler } = useChartTooltip({ @@ -38,6 +46,7 @@ const { handler: externalTooltipHandler } = useChartTooltip({ }); async function renderChart() { + if (rootEl.value == null) return; if (chartInstance) { chartInstance.destroy(); } @@ -56,7 +65,7 @@ async function renderChart() { return new Date(y, m, d - ago); }; - const format = (arr) => { + const format = (arr: number[]) => { return arr.map((v, i) => { const dt = getDate(i); const iso = `${dt.getFullYear()}-${(dt.getMonth() + 1).toString().padStart(2, '0')}-${dt.getDate().toString().padStart(2, '0')}`; @@ -69,22 +78,27 @@ async function renderChart() { }); }; - let values; + let values: number[] = []; if (props.src === 'active-users') { - const raw = await os.api('charts/active-users', { limit: chartLimit, span: 'day' }); + const raw = await misskeyApi('charts/active-users', { limit: chartLimit, span: 'day' }); values = raw.readWrite; } else if (props.src === 'notes') { - const raw = await os.api('charts/notes', { limit: chartLimit, span: 'day' }); - values = raw.local.inc; + if (props.user) { + const raw = await misskeyApi('charts/user/notes', { userId: props.user.id, limit: chartLimit, span: 'day' }); + values = raw.inc; + } else { + const raw = await misskeyApi('charts/notes', { limit: chartLimit, span: 'day' }); + values = raw.local.inc; + } } else if (props.src === 'ap-requests-inbox-received') { - const raw = await os.api('charts/ap-request', { limit: chartLimit, span: 'day' }); + const raw = await misskeyApi('charts/ap-request', { limit: chartLimit, span: 'day' }); values = raw.inboxReceived; } else if (props.src === 'ap-requests-deliver-succeeded') { - const raw = await os.api('charts/ap-request', { limit: chartLimit, span: 'day' }); + const raw = await misskeyApi('charts/ap-request', { limit: chartLimit, span: 'day' }); values = raw.deliverSucceeded; } else if (props.src === 'ap-requests-deliver-failed') { - const raw = await os.api('charts/ap-request', { limit: chartLimit, span: 'day' }); + const raw = await misskeyApi('charts/ap-request', { limit: chartLimit, span: 'day' }); values = raw.deliverFailed; } @@ -101,25 +115,25 @@ async function renderChart() { const marginEachCell = 4; + if (chartEl.value == null) return; + chartInstance = new Chart(chartEl.value, { type: 'matrix', data: { datasets: [{ - label: 'Read & Write', - data: format(values), - pointRadius: 0, + label: props.label, + data: format(values) as any, borderWidth: 0, - borderJoinStyle: 'round', borderRadius: 3, backgroundColor(c) { - const value = c.dataset.data[c.dataIndex].v; + // @ts-expect-error TS(2339) + const value = c.dataset.data[c.dataIndex].v as number; let a = (value - min) / max; if (value !== 0) { // 0ã§ãªã„é™ã‚Šã¯å®Œå…¨ã«ä¸å¯è¦–ã«ã¯ã—ãªã„ a = Math.max(a, 0.05); } return alpha(color, a); }, - fill: true, width(c) { const a = c.chart.chartArea ?? {}; return (a.right - a.left) / weeks - marginEachCell; @@ -128,6 +142,9 @@ async function renderChart() { const a = c.chart.chartArea ?? {}; return (a.bottom - a.top) / 7 - marginEachCell; }, + /* @see <https://github.com/misskey-dev/misskey/pull/10365#discussion_r1155511107> + }] satisfies ChartData[], + */ }], }, options: { @@ -190,12 +207,14 @@ async function renderChart() { enabled: false, callbacks: { title(context) { - const v = context[0].dataset.data[context[0].dataIndex]; - return v.d; + // @ts-expect-error TS(2339) + return context[0].dataset.data[context[0].dataIndex].d; }, label(context) { const v = context.dataset.data[context.dataIndex]; - return ['Active: ' + v.v]; + + // @ts-expect-error TS(2339) + return [v.v]; }, }, //mode: 'index', diff --git a/packages/frontend/src/components/MkHorizontalSwipe.vue b/packages/frontend/src/components/MkHorizontalSwipe.vue new file mode 100644 index 0000000000000000000000000000000000000000..196c962a06b52e4d7515a6a82dd71a27a6f332d2 --- /dev/null +++ b/packages/frontend/src/components/MkHorizontalSwipe.vue @@ -0,0 +1,239 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div + ref="rootEl" + :class="[$style.transitionRoot, { [$style.enableAnimation]: shouldAnimate }]" + @touchstart.passive="touchStart" + @touchmove.passive="touchMove" + @touchend.passive="touchEnd" +> + <Transition + :class="[$style.transitionChildren, { [$style.swiping]: isSwipingForClass }]" + :enterActiveClass="$style.swipeAnimation_enterActive" + :leaveActiveClass="$style.swipeAnimation_leaveActive" + :enterFromClass="transitionName === 'swipeAnimationLeft' ? $style.swipeAnimationLeft_enterFrom : $style.swipeAnimationRight_enterFrom" + :leaveToClass="transitionName === 'swipeAnimationLeft' ? $style.swipeAnimationLeft_leaveTo : $style.swipeAnimationRight_leaveTo" + :style="`--swipe: ${pullDistance}px;`" + > + <!-- ã€æ³¨æ„】slot内ã®æœ€ä¸Šä½è¦ç´ ã«å‹•çš„ã«keyã‚’è¨å®šã™ã‚‹ã“㨠--> + <!-- å„最上ä½è¦ç´ ã«ãƒ¦ãƒ‹ãƒ¼ã‚¯ãªkeyã®æŒ‡å®šãŒãªã„ã¨TransitionãŒã†ã¾ãå‹•ãã¾ã›ã‚“ --> + <slot></slot> + </Transition> +</div> +</template> +<script lang="ts" setup> +import { ref, shallowRef, computed, nextTick, watch } from 'vue'; +import type { Tab } from '@/components/global/MkPageHeader.tabs.vue'; +import { defaultStore } from '@/store.js'; +import { isHorizontalSwipeSwiping as isSwiping } from '@/scripts/touch.js'; + +const rootEl = shallowRef<HTMLDivElement>(); + +// eslint-disable-next-line no-undef +const tabModel = defineModel<string>('tab'); + +const props = defineProps<{ + tabs: Tab[]; +}>(); + +const emit = defineEmits<{ + (ev: 'swiped', newKey: string, direction: 'left' | 'right'): void; +}>(); + +const shouldAnimate = computed(() => defaultStore.reactiveState.enableHorizontalSwipe.value || defaultStore.reactiveState.animation.value); + +// â–¼ ã—ãã„値 â–¼ // + +// スワイプã¨åˆ¤å®šã•ã‚Œã‚‹æœ€å°ã®è·é›¢ +const MIN_SWIPE_DISTANCE = 20; + +// スワイプ時ã®å‹•ä½œã‚’発ç«ã™ã‚‹æœ€å°ã®è·é›¢ +const SWIPE_DISTANCE_THRESHOLD = 70; + +// スワイプをä¸æ–ã™ã‚‹Yæ–¹å‘ã®ç§»å‹•è·é›¢ +const SWIPE_ABORT_Y_THRESHOLD = 75; + +// スワイプã§ãる最大ã®è·é›¢ +const MAX_SWIPE_DISTANCE = 120; + +// â–² ã—ãã„値 â–² // + +let startScreenX: number | null = null; +let startScreenY: number | null = null; + +const currentTabIndex = computed(() => props.tabs.findIndex(tab => tab.key === tabModel.value)); + +const pullDistance = ref(0); +const isSwipingForClass = ref(false); +let swipeAborted = false; + +function touchStart(event: TouchEvent) { + if (!defaultStore.reactiveState.enableHorizontalSwipe.value) return; + + if (event.touches.length !== 1) return; + + if (hasSomethingToDoWithXSwipe(event.target as HTMLElement)) return; + + startScreenX = event.touches[0].screenX; + startScreenY = event.touches[0].screenY; +} + +function touchMove(event: TouchEvent) { + if (!defaultStore.reactiveState.enableHorizontalSwipe.value) return; + + if (event.touches.length !== 1) return; + + if (startScreenX == null || startScreenY == null) return; + + if (swipeAborted) return; + + if (hasSomethingToDoWithXSwipe(event.target as HTMLElement)) return; + + let distanceX = event.touches[0].screenX - startScreenX; + let distanceY = event.touches[0].screenY - startScreenY; + + if (Math.abs(distanceY) > SWIPE_ABORT_Y_THRESHOLD) { + swipeAborted = true; + + pullDistance.value = 0; + isSwiping.value = false; + setTimeout(() => { + isSwipingForClass.value = false; + }, 400); + + return; + } + + if (Math.abs(distanceX) < MIN_SWIPE_DISTANCE) return; + if (Math.abs(distanceX) > MAX_SWIPE_DISTANCE) return; + + if (currentTabIndex.value === 0 || props.tabs[currentTabIndex.value - 1].onClick) { + distanceX = Math.min(distanceX, 0); + } + if (currentTabIndex.value === props.tabs.length - 1 || props.tabs[currentTabIndex.value + 1].onClick) { + distanceX = Math.max(distanceX, 0); + } + if (distanceX === 0) return; + + isSwiping.value = true; + isSwipingForClass.value = true; + nextTick(() => { + // グリッãƒã‚’控ãˆã‚‹ãŸã‚ã€1.5px以上ã®å·®ãŒãªã„ã¨æ›´æ–°ã—ãªã„ + if (Math.abs(distanceX - pullDistance.value) < 1.5) return; + pullDistance.value = distanceX; + }); +} + +function touchEnd(event: TouchEvent) { + if (swipeAborted) { + swipeAborted = false; + return; + } + + if (!defaultStore.reactiveState.enableHorizontalSwipe.value) return; + + if (event.touches.length !== 0) return; + + if (startScreenX == null) return; + + if (!isSwiping.value) return; + + if (hasSomethingToDoWithXSwipe(event.target as HTMLElement)) return; + + const distance = event.changedTouches[0].screenX - startScreenX; + + if (Math.abs(distance) > SWIPE_DISTANCE_THRESHOLD) { + if (distance > 0) { + if (props.tabs[currentTabIndex.value - 1] && !props.tabs[currentTabIndex.value - 1].onClick) { + tabModel.value = props.tabs[currentTabIndex.value - 1].key; + emit('swiped', props.tabs[currentTabIndex.value - 1].key, 'right'); + } + } else { + if (props.tabs[currentTabIndex.value + 1] && !props.tabs[currentTabIndex.value + 1].onClick) { + tabModel.value = props.tabs[currentTabIndex.value + 1].key; + emit('swiped', props.tabs[currentTabIndex.value + 1].key, 'left'); + } + } + } + + pullDistance.value = 0; + isSwiping.value = false; + window.setTimeout(() => { + isSwipingForClass.value = false; + }, 400); +} + +/** 横スワイプã«é–¢ä¸Žã™ã‚‹å¯èƒ½æ€§ã®ã‚ã‚‹è¦ç´ を調ã¹ã‚‹ */ +function hasSomethingToDoWithXSwipe(el: HTMLElement) { + if (['INPUT', 'TEXTAREA'].includes(el.tagName)) return true; + if (el.isContentEditable) return true; + if (el.scrollWidth > el.clientWidth) return true; + + const style = window.getComputedStyle(el); + if (['absolute', 'fixed', 'sticky'].includes(style.position)) return true; + if (['scroll', 'auto'].includes(style.overflowX)) return true; + if (style.touchAction === 'pan-x') return true; + + if (el.parentElement && el.parentElement !== rootEl.value) { + return hasSomethingToDoWithXSwipe(el.parentElement); + } else { + return false; + } +} + +const transitionName = ref<'swipeAnimationLeft' | 'swipeAnimationRight' | undefined>(undefined); + +watch(tabModel, (newTab, oldTab) => { + const newIndex = props.tabs.findIndex(tab => tab.key === newTab); + const oldIndex = props.tabs.findIndex(tab => tab.key === oldTab); + + if (oldIndex >= 0 && newIndex && oldIndex < newIndex) { + transitionName.value = 'swipeAnimationLeft'; + } else { + transitionName.value = 'swipeAnimationRight'; + } + + window.setTimeout(() => { + transitionName.value = undefined; + }, 400); +}); +</script> + +<style lang="scss" module> +.transitionRoot { + touch-action: pan-y pinch-zoom; + display: grid; + grid-template-columns: 100%; + overflow: clip; +} + +.transitionChildren { + grid-area: 1 / 1 / 2 / 2; + transform: translateX(var(--swipe)); +} + +.enableAnimation .transitionChildren { + &.swipeAnimation_enterActive, + &.swipeAnimation_leaveActive { + transition: transform .3s cubic-bezier(0.65, 0.05, 0.36, 1); + } + + &.swipeAnimationRight_leaveTo, + &.swipeAnimationLeft_enterFrom { + transform: translateX(calc(100% + 24px)); + } + + &.swipeAnimationRight_enterFrom, + &.swipeAnimationLeft_leaveTo { + transform: translateX(calc(-100% - 24px)); + } +} + +.swiping { + transition: transform .2s ease-out; +} +</style> diff --git a/packages/frontend/src/components/MkImgWithBlurhash.vue b/packages/frontend/src/components/MkImgWithBlurhash.vue index 942861e1f4241439b27472d5920ebbbcd32e51a3..4e3fafe845fc5ee8e136908a0c4ff4d08c309b6b 100644 --- a/packages/frontend/src/components/MkImgWithBlurhash.vue +++ b/packages/frontend/src/components/MkImgWithBlurhash.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -73,7 +73,7 @@ const props = withDefaults(defineProps<{ leaveFromClass?: string; } | null; src?: string | null; - hash?: string; + hash?: string | null; alt?: string | null; title?: string | null; height?: number; diff --git a/packages/frontend/src/components/MkInfo.vue b/packages/frontend/src/components/MkInfo.vue index 6e643639f2d070fd0bf8a46283d14fd555ef298f..9a5874b5c08f9508d8a3449b37ba848b1d2cbe4f 100644 --- a/packages/frontend/src/components/MkInfo.vue +++ b/packages/frontend/src/components/MkInfo.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue index b4b4e1b0b755fa9a976c9c72fe01f56f32118d6a..b026903b6628b6fa1ec9943d0af5935e9ae33a65 100644 --- a/packages/frontend/src/components/MkInput.vue +++ b/packages/frontend/src/components/MkInput.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -88,17 +88,18 @@ const focused = ref(false); const changed = ref(false); const invalid = ref(false); const filled = computed(() => v.value !== '' && v.value != null); -const inputEl = shallowRef<HTMLElement>(); +const inputEl = shallowRef<HTMLInputElement>(); const prefixEl = shallowRef<HTMLElement>(); const suffixEl = shallowRef<HTMLElement>(); const height = props.small ? 33 : props.large ? 39 : 36; -let autocomplete: Autocomplete; +let autocompleteWorker: Autocomplete | null = null; -const focus = () => inputEl.value.focus(); -const onInput = (ev: KeyboardEvent) => { +const focus = () => inputEl.value?.focus(); +const onInput = (event: Event) => { + const ev = event as KeyboardEvent; changed.value = true; emit('change', ev); }; @@ -115,9 +116,9 @@ const onKeydown = (ev: KeyboardEvent) => { const updated = () => { changed.value = false; if (type.value === 'number') { - emit('update:modelValue', parseFloat(v.value)); + emit('update:modelValue', typeof v.value === 'number' ? v.value : parseFloat(v.value ?? '0')); } else { - emit('update:modelValue', v.value); + emit('update:modelValue', v.value ?? ''); } }; @@ -127,7 +128,7 @@ watch(modelValue, newValue => { v.value = newValue; }); -watch(v, newValue => { +watch(v, () => { if (!props.manualSave) { if (props.debounce) { debouncedUpdated(); @@ -136,12 +137,14 @@ watch(v, newValue => { } } - invalid.value = inputEl.value.validity.badInput; + invalid.value = inputEl.value?.validity.badInput ?? true; }); // ã“ã®ã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆãŒä½œæˆã•ã‚ŒãŸæ™‚ã€éžè¡¨ç¤ºçŠ¶æ…‹ã§ã‚ã‚‹å ´åˆãŒã‚ã‚‹ // éžè¡¨ç¤ºçŠ¶æ…‹ã ã¨è¦ç´ ã®å¹…ãªã©ã¯0ã«ãªã£ã¦ã—ã¾ã†ã®ã§ã€å®šæœŸçš„ã«è¨ˆç®—ã™ã‚‹ useInterval(() => { + if (inputEl.value == null) return; + if (prefixEl.value) { if (prefixEl.value.offsetWidth) { inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px'; @@ -163,15 +166,15 @@ onMounted(() => { focus(); } }); - - if (props.mfmAutocomplete) { - autocomplete = new Autocomplete(inputEl.value, v, props.mfmAutocomplete === true ? null : props.mfmAutocomplete); + + if (props.mfmAutocomplete && inputEl.value) { + autocompleteWorker = new Autocomplete(inputEl.value, v, props.mfmAutocomplete === true ? undefined : props.mfmAutocomplete); } }); onUnmounted(() => { - if (autocomplete) { - autocomplete.detach(); + if (autocompleteWorker) { + autocompleteWorker.detach(); } }); diff --git a/packages/frontend/src/components/MkInstanceCardMini.vue b/packages/frontend/src/components/MkInstanceCardMini.vue index 9cde197e1916ebd9356f1cb8a6d091d2d675649a..feb62415aa1b2d3fd13268897a343567efbbd0c1 100644 --- a/packages/frontend/src/components/MkInstanceCardMini.vue +++ b/packages/frontend/src/components/MkInstanceCardMini.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkMiniChart from '@/components/MkMiniChart.vue'; -import * as os from '@/os.js'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; const props = defineProps<{ @@ -27,7 +27,7 @@ const props = defineProps<{ const chartValues = ref<number[] | null>(null); -os.apiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => { +misskeyApiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => { // 今日ã®ã¶ã‚“ã®å€¤ã¯ã¾ã 途ä¸ã®å€¤ã§ã‚ã‚Šã€ãれもå«ã‚ã‚‹ã¨å¤§æŠµã®å ´åˆå‰æ—¥ã‚ˆã‚Šã‚‚下é™ã—ã¦ã„るよã†ãªã‚°ãƒ©ãƒ•ã«ãªã£ã¦ã—ã¾ã†ãŸã‚今日ã¯å¼¾ã res['requests.received'].splice(0, 1); chartValues.value = res['requests.received']; diff --git a/packages/frontend/src/components/MkInstanceStats.vue b/packages/frontend/src/components/MkInstanceStats.vue index 7b763ad3856cf367f421182ff8be16ddc12931c0..d74c885041f3fa40f244be11593b82a8eabe1b9c 100644 --- a/packages/frontend/src/components/MkInstanceStats.vue +++ b/packages/frontend/src/components/MkInstanceStats.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only <option value="ap-requests-deliver-failed">AP Requests: deliverFailed</option> </MkSelect> <div class="_panel" :class="$style.heatmap"> - <MkHeatmap :src="heatmapSrc"/> + <MkHeatmap :src="heatmapSrc" :label="'Read & Write'"/> </div> </MkFoldableSection> @@ -90,8 +90,9 @@ import MkSelect from '@/components/MkSelect.vue'; import MkChart from '@/components/MkChart.vue'; import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import * as os from '@/os.js'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; -import MkHeatmap from '@/components/MkHeatmap.vue'; +import MkHeatmap, { type HeatmapSource } from '@/components/MkHeatmap.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkRetentionHeatmap from '@/components/MkRetentionHeatmap.vue'; import MkRetentionLineChart from '@/components/MkRetentionLineChart.vue'; @@ -102,7 +103,7 @@ initChart(); const chartLimit = 500; const chartSpan = ref<'hour' | 'day'>('hour'); const chartSrc = ref('active-users'); -const heatmapSrc = ref('active-users'); +const heatmapSrc = ref<HeatmapSource>('active-users'); const subDoughnutEl = shallowRef<HTMLCanvasElement>(); const pubDoughnutEl = shallowRef<HTMLCanvasElement>(); @@ -137,7 +138,8 @@ function createDoughnut(chartEl, tooltip, data) { }, }, onClick: (ev) => { - const hit = chartInstance.getElementsAtEventForMode(ev, 'nearest', { intersect: true }, false)[0]; + if (ev.native == null) return; + const hit = chartInstance.getElementsAtEventForMode(ev.native, 'nearest', { intersect: true }, false)[0]; if (hit && data[hit.index].onClick) { data[hit.index].onClick(); } @@ -162,24 +164,47 @@ function createDoughnut(chartEl, tooltip, data) { } onMounted(() => { - os.apiGet('federation/stats', { limit: 30 }).then(fedStats => { - createDoughnut(subDoughnutEl.value, externalTooltipHandler1, fedStats.topSubInstances.map(x => ({ + misskeyApiGet('federation/stats', { limit: 30 }).then(fedStats => { + type ChartData = { + name: string, + color: string | null, + value: number, + onClick?: () => void, + }[]; + + const subs: ChartData = fedStats.topSubInstances.map(x => ({ name: x.host, color: x.themeColor, value: x.followersCount, onClick: () => { os.pageWindow(`/instance-info/${x.host}`); }, - })).concat([{ name: '(other)', color: '#80808080', value: fedStats.otherFollowersCount }])); + })); + + subs.push({ + name: '(other)', + color: '#80808080', + value: fedStats.otherFollowersCount, + }); + + createDoughnut(subDoughnutEl.value, externalTooltipHandler1, subs); - createDoughnut(pubDoughnutEl.value, externalTooltipHandler2, fedStats.topPubInstances.map(x => ({ + const pubs: ChartData = fedStats.topPubInstances.map(x => ({ name: x.host, color: x.themeColor, value: x.followingCount, onClick: () => { os.pageWindow(`/instance-info/${x.host}`); }, - })).concat([{ name: '(other)', color: '#80808080', value: fedStats.otherFollowingCount }])); + })); + + pubs.push({ + name: '(other)', + color: '#80808080', + value: fedStats.otherFollowingCount, + }); + + createDoughnut(pubDoughnutEl.value, externalTooltipHandler2, pubs); }); }); </script> diff --git a/packages/frontend/src/components/MkInstanceTicker.vue b/packages/frontend/src/components/MkInstanceTicker.vue index e358a1c54937773984db5635848652e6cbd83b1c..094d2f177f3ba1b9edd0d9558f3f9974a05f6ec7 100644 --- a/packages/frontend/src/components/MkInstanceTicker.vue +++ b/packages/frontend/src/components/MkInstanceTicker.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -18,9 +18,9 @@ import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; const props = defineProps<{ instance?: { - faviconUrl?: string - name: string - themeColor?: string + faviconUrl?: string | null + name?: string | null + themeColor?: string | null } }>(); @@ -30,7 +30,7 @@ const instance = props.instance ?? { themeColor: (document.querySelector('meta[name="theme-color-orig"]') as HTMLMetaElement).content, }; -const faviconUrl = computed(() => props.instance ? getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview') : getProxiedImageUrlNullable(Instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(Instance.faviconUrl, 'preview') ?? '/favicon.ico'); +const faviconUrl = computed(() => props.instance ? getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview') : getProxiedImageUrlNullable(Instance.iconUrl, 'preview') ?? '/favicon.ico'); const themeColor = instance.themeColor ?? '#777777'; diff --git a/packages/frontend/src/components/MkInviteCode.stories.impl.ts b/packages/frontend/src/components/MkInviteCode.stories.impl.ts index 2ea32dd3b67a5afb31e6ec374bc1dc95d25275c8..456d2152887937e47032056544f78aa3da3f6f16 100644 --- a/packages/frontend/src/components/MkInviteCode.stories.impl.ts +++ b/packages/frontend/src/components/MkInviteCode.stories.impl.ts @@ -1,11 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { StoryObj } from '@storybook/vue3'; -import { rest } from 'msw'; +import { HttpResponse, http } from 'msw'; import { userDetailed, inviteCode } from '../../.storybook/fakes.js'; import { commonHandlers } from '../../.storybook/mocks.js'; import MkInviteCode from './MkInviteCode.vue'; @@ -39,8 +39,8 @@ export const Default = { msw: { handlers: [ ...commonHandlers, - rest.post('/api/users/show', (req, res, ctx) => { - return res(ctx.json(userDetailed(req.params.userId as string))); + http.post('/api/users/show', ({ params }) => { + return HttpResponse.json(userDetailed(params.userId as string)); }), ], }, diff --git a/packages/frontend/src/components/MkInviteCode.vue b/packages/frontend/src/components/MkInviteCode.vue index 54d997d1c90fcb8292b70ef372295c2eb1bb81df..b095df20e584b0c975268444cfed40b82ac5a7a2 100644 --- a/packages/frontend/src/components/MkInviteCode.vue +++ b/packages/frontend/src/components/MkInviteCode.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkKeyValue.vue b/packages/frontend/src/components/MkKeyValue.vue index 7a1a5eb016b596405db7db229c969ecc6b89842f..2175c0e888de6d38898bb9949e9fd412dafff3a7 100644 --- a/packages/frontend/src/components/MkKeyValue.vue +++ b/packages/frontend/src/components/MkKeyValue.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue index 099082f5390d9ef3fe531c80d3c1003e5ff53871..e232b4d66f79777e9ddee2cff33097d02f4f1d8c 100644 --- a/packages/frontend/src/components/MkLaunchPad.vue +++ b/packages/frontend/src/components/MkLaunchPad.vue @@ -1,10 +1,10 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkModal ref="modal" v-slot="{ type, maxHeight }" :preferType="preferedModalType" :anchor="anchor" :transparentBg="true" :src="src" @click="modal.close()" @closed="emit('closed')"> +<MkModal ref="modal" v-slot="{ type, maxHeight }" :preferType="preferedModalType" :anchor="anchor" :transparentBg="true" :src="src" @click="modal?.close()" @closed="emit('closed')"> <div class="szkkfdyq _popup _shadow" :class="{ asDrawer: type === 'drawer' }" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : '' }"> <div class="main"> <template v-for="item in items" :key="item.text"> @@ -63,7 +63,7 @@ const items = Object.keys(navbarItemDef).filter(k => !menu.includes(k)).map(k => })); function close() { - modal.value.close(); + modal.value?.close(); } </script> @@ -119,6 +119,7 @@ function close() { margin-top: 12px; font-size: 0.8em; line-height: 1.5em; + text-align: center; } > .indicatorWithValue { @@ -138,7 +139,7 @@ function close() { left: 32px; color: var(--indicator); font-size: 8px; - animation: blink 1s infinite; + animation: global-blink 1s infinite; @media (max-width: 500px) { top: 16px; diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue index bda683002d3746d41f2abf2703f174b4745d0109..95de0d02472a84ea4717ed34eebb6f2674317ff0 100644 --- a/packages/frontend/src/components/MkLink.vue +++ b/packages/frontend/src/components/MkLink.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <component :is="self ? 'MkA' : 'a'" ref="el" style="word-break: break-all;" class="_link" :[attr]="self ? url.substring(local.length) : url" :rel="rel ?? 'nofollow noopener'" :target="target" :title="url" + @click.stop > <slot></slot> <i v-if="target === '_blank'" class="ph-arrow-square-out ph-bold ph-lg" :class="$style.icon"></i> diff --git a/packages/frontend/src/components/MkMarquee.vue b/packages/frontend/src/components/MkMarquee.vue index 145b60c8e7819b74e5844ef8b5528e3196203f0f..4a89d21b922bddaca5cc4275f8c330e29f0f21e3 100644 --- a/packages/frontend/src/components/MkMarquee.vue +++ b/packages/frontend/src/components/MkMarquee.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -30,6 +30,7 @@ export default { const contentEl = ref<HTMLElement>(); function calc() { + if (contentEl.value == null) return; const eachLength = contentEl.value.offsetWidth / props.repeat; const factor = 3000; const duration = props.duration / ((1 / eachLength) * factor); diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue new file mode 100644 index 0000000000000000000000000000000000000000..6351f5cfbe988568348443fe25947c34461e99f0 --- /dev/null +++ b/packages/frontend/src/components/MkMediaAudio.vue @@ -0,0 +1,364 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div + :class="[ + $style.audioContainer, + (audio.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitive, + ]" + @contextmenu.stop +> + <button v-if="hide" :class="$style.hidden" @click="hide = false"> + <div :class="$style.hiddenTextWrapper"> + <b v-if="audio.isSensitive" style="display: block;"><i class="ph-eye-slash ph-bold ph-lg"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.dataSaver.media ? ` (${i18n.ts.audio}${audio.size ? ' ' + bytes(audio.size) : ''})` : '' }}</b> + <b v-else style="display: block;"><i class="ph-music-notes ph-bold ph-lg"></i> {{ defaultStore.state.dataSaver.media && audio.size ? bytes(audio.size) : i18n.ts.audio }}</b> + <span style="display: block;">{{ i18n.ts.clickToShow }}</span> + </div> + </button> + <div v-else :class="$style.audioControls"> + <audio + ref="audioEl" + preload="metadata" + > + <source :src="audio.url"> + </audio> + <div :class="[$style.controlsChild, $style.controlsLeft]"> + <button class="_button" :class="$style.controlButton" @click="togglePlayPause"> + <i v-if="isPlaying" class="ph-pause ph-bold ph-lg"></i> + <i v-else class="ph-play ph-bold ph-lg"></i> + </button> + </div> + <div :class="[$style.controlsChild, $style.controlsRight]"> + <a class="_button" :class="$style.controlButton" :href="audio.url" :download="audio.name" target="_blank"> + <i class="ph-download ph-bold ph-lg"></i> + </a> + <button class="_button" :class="$style.controlButton" @click="showMenu"> + <i class="ph-gear ph-bold ph-lg"></i> + </button> + </div> + <div :class="[$style.controlsChild, $style.controlsTime]">{{ hms(elapsedTimeMs) }}</div> + <div :class="[$style.controlsChild, $style.controlsVolume]"> + <button class="_button" :class="$style.controlButton" @click="toggleMute"> + <i v-if="volume === 0" class="ph-speaker-x ph-bold ph-lg"></i> + <i v-else class="ph-speaker-high ph-bold ph-lg"></i> + </button> + <MkMediaRange + v-model="volume" + :class="$style.volumeSeekbar" + /> + </div> + <MkMediaRange + v-model="rangePercent" + :class="$style.seekbarRoot" + :buffer="bufferedDataRatio" + /> + </div> +</div> +</template> + +<script lang="ts" setup> +import { shallowRef, watch, computed, ref, onDeactivated, onActivated, onMounted } from 'vue'; +import * as Misskey from 'misskey-js'; +import type { MenuItem } from '@/types/menu.js'; +import { defaultStore } from '@/store.js'; +import { i18n } from '@/i18n.js'; +import * as os from '@/os.js'; +import bytes from '@/filters/bytes.js'; +import { hms } from '@/filters/hms.js'; +import MkMediaRange from '@/components/MkMediaRange.vue'; +import { iAmModerator } from '@/account.js'; + +const props = defineProps<{ + audio: Misskey.entities.DriveFile; +}>(); + +const audioEl = shallowRef<HTMLAudioElement>(); + +// eslint-disable-next-line vue/no-setup-props-destructure +const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.audio.isSensitive && defaultStore.state.nsfw !== 'ignore')); + +// Menu +const menuShowing = ref(false); + +function showMenu(ev: MouseEvent) { + let menu: MenuItem[] = []; + + menu = [ + // TODO: å†ç”Ÿã‚ューã«è¿½åŠ + { + text: i18n.ts.hide, + icon: 'ph-eye-closed ph-bold ph-lg', + action: () => { + hide.value = true; + }, + }, + ]; + + if (iAmModerator) { + menu.push({ + type: 'divider', + }, { + text: props.audio.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive, + icon: props.audio.isSensitive ? 'ph-eye ph-bold ph-lg' : 'ph-eye-slash ph-bold ph-lg', + danger: true, + action: () => toggleSensitive(props.audio), + }); + } + + menuShowing.value = true; + os.popupMenu(menu, ev.currentTarget ?? ev.target, { + align: 'right', + onClosing: () => { + menuShowing.value = false; + }, + }); +} + +function toggleSensitive(file: Misskey.entities.DriveFile) { + os.apiWithDialog('drive/files/update', { + fileId: file.id, + isSensitive: !file.isSensitive, + }); +} + +// MediaControl: Common State +const oncePlayed = ref(false); +const isReady = ref(false); +const isPlaying = ref(false); +const isActuallyPlaying = ref(false); +const elapsedTimeMs = ref(0); +const durationMs = ref(0); +const rangePercent = computed({ + get: () => { + return (elapsedTimeMs.value / durationMs.value) || 0; + }, + set: (to) => { + if (!audioEl.value) return; + audioEl.value.currentTime = to * durationMs.value / 1000; + }, +}); +const volume = ref(.25); +const bufferedEnd = ref(0); +const bufferedDataRatio = computed(() => { + if (!audioEl.value) return 0; + return bufferedEnd.value / audioEl.value.duration; +}); + +// MediaControl Events +function togglePlayPause() { + if (!isReady.value || !audioEl.value) return; + + if (isPlaying.value) { + audioEl.value.pause(); + isPlaying.value = false; + } else { + audioEl.value.play(); + isPlaying.value = true; + oncePlayed.value = true; + } +} + +function toggleMute() { + if (volume.value === 0) { + volume.value = .25; + } else { + volume.value = 0; + } +} + +let onceInit = false; +let stopAudioElWatch: () => void; + +function init() { + if (onceInit) return; + onceInit = true; + + stopAudioElWatch = watch(audioEl, () => { + if (audioEl.value) { + isReady.value = true; + + function updateMediaTick() { + if (audioEl.value) { + try { + bufferedEnd.value = audioEl.value.buffered.end(0); + } catch (err) { + bufferedEnd.value = 0; + } + + elapsedTimeMs.value = audioEl.value.currentTime * 1000; + } + window.requestAnimationFrame(updateMediaTick); + } + + updateMediaTick(); + + audioEl.value.addEventListener('play', () => { + isActuallyPlaying.value = true; + }); + + audioEl.value.addEventListener('pause', () => { + isActuallyPlaying.value = false; + isPlaying.value = false; + }); + + audioEl.value.addEventListener('ended', () => { + oncePlayed.value = false; + isActuallyPlaying.value = false; + isPlaying.value = false; + }); + + durationMs.value = audioEl.value.duration * 1000; + audioEl.value.addEventListener('durationchange', () => { + if (audioEl.value) { + durationMs.value = audioEl.value.duration * 1000; + } + }); + + audioEl.value.volume = volume.value; + } + }, { + immediate: true, + }); +} + +watch(volume, (to) => { + if (audioEl.value) audioEl.value.volume = to; +}); + +onMounted(() => { + init(); +}); + +onActivated(() => { + init(); +}); + +onDeactivated(() => { + isReady.value = false; + isPlaying.value = false; + isActuallyPlaying.value = false; + elapsedTimeMs.value = 0; + durationMs.value = 0; + bufferedEnd.value = 0; + hide.value = (defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.audio.isSensitive && defaultStore.state.nsfw !== 'ignore'); + stopAudioElWatch(); + onceInit = false; +}); +</script> + +<style lang="scss" module> +.audioContainer { + container-type: inline-size; + position: relative; + border: .5px solid var(--divider); + border-radius: var(--radius); + overflow: clip; +} + +.sensitive { + position: relative; + + &::after { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + border-radius: inherit; + box-shadow: inset 0 0 0 4px var(--warn); + } +} + +.hidden { + width: 100%; + background: #000; + border: none; + outline: none; + font: inherit; + color: inherit; + cursor: pointer; + padding: 12px 0; + display: flex; + align-items: center; + justify-content: center; +} + +.hiddenTextWrapper { + text-align: center; + font-size: 0.8em; + color: #fff; +} + +.audioControls { + display: grid; + grid-template-areas: + "left time . volume right" + "seekbar seekbar seekbar seekbar seekbar"; + grid-template-columns: auto auto 1fr auto auto; + align-items: center; + gap: 4px 8px; + padding: 10px; +} + +.controlsChild { + display: flex; + align-items: center; + gap: 4px; + + .controlButton { + padding: 6px; + border-radius: calc(var(--radius) / 2); + font-size: 1.05rem; + + &:hover { + color: var(--accent); + background-color: var(--accentedBg); + } + } +} + +.controlsLeft { + grid-area: left; +} + +.controlsRight { + grid-area: right; +} + +.controlsTime { + grid-area: time; + font-size: .9rem; +} + +.controlsVolume { + grid-area: volume; + + .volumeSeekbar { + display: none; + } +} + +.seekbarRoot { + grid-area: seekbar; +} + +@container (min-width: 500px) { + .audioControls { + grid-template-areas: "left seekbar time volume right"; + grid-template-columns: auto 1fr auto auto auto; + } + + .controlsVolume { + .volumeSeekbar { + max-width: 90px; + display: block; + flex-grow: 1; + } + } +} +</style> diff --git a/packages/frontend/src/components/MkMediaBanner.vue b/packages/frontend/src/components/MkMediaBanner.vue index 7b0387cefe616ba9f2d40dbf6dc204c1a20a9fcd..605c1a4c80681a114cff11b8d997d8ab6311dae6 100644 --- a/packages/frontend/src/components/MkMediaBanner.vue +++ b/packages/frontend/src/components/MkMediaBanner.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -10,15 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <b>{{ i18n.ts.sensitive }}</b> <span>{{ i18n.ts.clickToShow }}</span> </div> - <div v-else-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" :class="$style.audio"> - <audio - ref="audioEl" - :src="media.url" - :title="media.comment ?? undefined" - controls - preload="metadata" - /> - </div> + <MkMediaAudio v-else-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" :audio="media"/> <a v-else :class="$style.download" :href="media.url" @@ -35,6 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { shallowRef, watch, ref } from 'vue'; import * as Misskey from 'misskey-js'; import { i18n } from '@/i18n.js'; +import MkMediaAudio from '@/components/MkMediaAudio.vue'; const props = withDefaults(defineProps<{ media: Misskey.entities.DriveFile; diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue index ef57cea32ae2559e708d97d05a0c2fd2317e7eb7..3f9cff8b710d2da4ee0028dcfb19d0dddf25738a 100644 --- a/packages/frontend/src/components/MkMediaImage.vue +++ b/packages/frontend/src/components/MkMediaImage.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -43,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="['image/gif', 'image/apng'].includes(image.type)" :class="$style.indicator">GIF</div> <div v-if="image.comment" :class="$style.indicator">ALT</div> <div v-if="image.isSensitive" :class="$style.indicator" style="color: var(--warn);" :title="i18n.ts.sensitive"><i class="ph-eye-closed ph-bold ph-lg"></i></div> - <div v-if="!image.comment" :class="$style.indicator" title="Image lacks descriptive text"><i class="ph-pencil ph-bold ph-lg-off"></i></div> + <div v-if="!image.comment" :class="$style.indicator" title="Image lacks descriptive text"><i class="ph-pencil-simple ph-bold ph-lg-off"></i></div> </div> <button :class="$style.menu" class="_button" @click.stop="showMenu"><i class="ph-dots-three ph-bold ph-lg" style="vertical-align: middle;"></i></button> <i class="ph-eye-slash ph-bold ph-lg" :class="$style.hide" @click.stop="hide = true"></i> diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue index 8f73018734b45c6777180d182db992d616834173..3bf44aea8ed8fbb04dc068b8140b56897f3ceaa2 100644 --- a/packages/frontend/src/components/MkMediaList.vue +++ b/packages/frontend/src/components/MkMediaList.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -54,7 +54,7 @@ const count = computed(() => props.mediaList.filter(media => previewable(media)) let lightbox: PhotoSwipeLightbox | null; const popstateHandler = (): void => { - if (lightbox.pswp && lightbox.pswp.isOpen === true) { + if (lightbox?.pswp && lightbox.pswp.isOpen === true) { lightbox.pswp.close(); } }; @@ -69,7 +69,10 @@ async function calcAspectRatio() { return; } - const ratioMax = (ratio: number) => `${Math.max(ratio, img.properties.width / img.properties.height).toString()} / 1`; + const ratioMax = (ratio: number) => { + if (img.properties.width == null || img.properties.height == null) return ''; + return `${Math.max(ratio, img.properties.width / img.properties.height).toString()} / 1`; + }; switch (defaultStore.state.mediaListWithOneImageAppearance) { case '16_9': @@ -145,7 +148,7 @@ onMounted(() => { // element is children const { element } = itemData; - const id = element.dataset.id; + const id = element?.dataset.id; const file = props.mediaList.find(media => media.id === id); if (!file) return; @@ -155,14 +158,14 @@ onMounted(() => { if (file.properties.orientation != null && file.properties.orientation >= 5) { [itemData.w, itemData.h] = [itemData.h, itemData.w]; } - itemData.msrc = file.thumbnailUrl; + itemData.msrc = file.thumbnailUrl ?? undefined; itemData.alt = file.comment ?? undefined; itemData.comment = file.comment; itemData.thumbCropped = true; }); lightbox.on('uiRegister', () => { - lightbox.pswp.ui.registerElement({ + lightbox?.pswp?.ui?.registerElement({ name: 'altText', className: 'pwsp__alt-text-container', appendTo: 'wrapper', @@ -178,7 +181,7 @@ onMounted(() => { textBox.style.display = 'none'; } - textBox.textContent = pwsp.currSlide.data.comment; + textBox.textContent = pwsp.currSlide?.data.comment; }); }, }); diff --git a/packages/frontend/src/components/MkMediaRange.vue b/packages/frontend/src/components/MkMediaRange.vue new file mode 100644 index 0000000000000000000000000000000000000000..86ed8ba2cf96039c9c7484854d3b92380f2564d8 --- /dev/null +++ b/packages/frontend/src/components/MkMediaRange.vue @@ -0,0 +1,152 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<!-- Media系専用ã®input range --> +<template> +<div :style="sliderBgWhite ? '--sliderBg: rgba(255,255,255,.25);' : '--sliderBg: var(--scrollbarHandle);'"> + <div :class="$style.controlsSeekbar"> + <progress v-if="buffer !== undefined" :class="$style.buffer" :value="isNaN(buffer) ? 0 : buffer" min="0" max="1">{{ Math.round(buffer * 100) }}% buffered</progress> + <input v-model="model" :class="$style.seek" :style="`--value: ${modelValue * 100}%;`" type="range" min="0" max="1" step="any" @change="emit('dragEnded', modelValue)"/> + </div> +</div> +</template> + +<script setup lang="ts"> +import { computed, ModelRef } from 'vue'; + +withDefaults(defineProps<{ + buffer?: number; + sliderBgWhite?: boolean; +}>(), { + buffer: undefined, + sliderBgWhite: false, +}); + +const emit = defineEmits<{ + (ev: 'dragEnded', value: number): void; +}>(); + +// eslint-disable-next-line no-undef +const model = defineModel({ required: true }) as ModelRef<string | number>; +const modelValue = computed({ + get: () => typeof model.value === 'number' ? model.value : parseFloat(model.value), + set: v => { model.value = v; }, +}); +</script> + +<style lang="scss" module> +.controlsSeekbar { + position: relative; +} + +.seek { + position: relative; + -webkit-appearance: none; + appearance: none; + background: transparent; + border: 0; + border-radius: 26px; + color: var(--accent); + display: block; + height: 19px; + margin: 0; + min-width: 0; + padding: 0; + transition: box-shadow .3s ease; + width: 100%; + + &::-webkit-slider-runnable-track { + background-color: var(--sliderBg); + background-image: linear-gradient(to right,currentColor var(--value,0),transparent var(--value,0)); + border: 0; + border-radius: 99rem; + height: 5px; + transition: box-shadow .3s ease; + user-select: none; + } + + &::-moz-range-track { + background: transparent; + border: 0; + border-radius: 99rem; + height: 5px; + transition: box-shadow .3s ease; + user-select: none; + background-color: var(--sliderBg); + } + + &::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + background: #fff; + border: 0; + border-radius: 100%; + box-shadow: 0 1px 1px rgba(35, 40, 47, .15),0 0 0 1px rgba(35, 40, 47, .2); + height: 13px; + margin-top: -4px; + position: relative; + transition: all .2s ease; + width: 13px; + + &:active { + box-shadow: 0 1px 1px rgba(35, 40, 47, .15), 0 0 0 1px rgba(35, 40, 47, .15), 0 0 0 3px rgba(255, 255, 255, .5); + } + } + + &::-moz-range-thumb { + background: #fff; + border: 0; + border-radius: 100%; + box-shadow: 0 1px 1px rgba(35, 40, 47, .15),0 0 0 1px rgba(35, 40, 47, .2); + height: 13px; + position: relative; + transition: all .2s ease; + width: 13px; + + &:active { + box-shadow: 0 1px 1px rgba(35, 40, 47, .15), 0 0 0 1px rgba(35, 40, 47, .15), 0 0 0 3px rgba(255, 255, 255, .5); + } + } + + &::-moz-range-progress { + background: currentColor; + border-radius: 99rem; + height: 5px; + } +} + +.buffer { + appearance: none; + background: transparent; + color: var(--sliderBg); + border: 0; + border-radius: 99rem; + height: 5px; + left: 0; + margin-top: -2.5px; + padding: 0; + position: absolute; + top: 50%; + width: 100%; + + &::-webkit-progress-bar { + background: transparent; + } + + &::-webkit-progress-value { + background: currentColor; + border-radius: 100px; + min-width: 5px; + transition: width .2s ease; + } + + &::-moz-progress-bar { + background: currentColor; + border-radius: 100px; + min-width: 5px; + transition: width .2s ease; + } +} +</style> diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue index a1950b110a0490697c24d98654aebae23f2e7b93..7c14ade13006bcaf1e0328a7c8f41c54884e1078 100644 --- a/packages/frontend/src/components/MkMediaVideo.vue +++ b/packages/frontend/src/components/MkMediaVideo.vue @@ -1,71 +1,351 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div v-if="hide" :class="[$style.hidden, (video.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitiveContainer]" @click="hide = false"> - <!-- ã€æ³¨æ„】dataSaverMode ãŒæœ‰åŠ¹ã«ãªã£ã¦ã„ã‚‹éš›ã«ã¯ã€hide ㌠false ã«ãªã‚‹ã¾ã§ã‚µãƒ ãƒã‚¤ãƒ«ã‚„動画をèªã¿è¾¼ã¾ãªã„よã†ã«ã™ã‚‹ã“㨠--> - <div :class="$style.sensitive"> - <b v-if="video.isSensitive" style="display: block;"><i class="ph-warning ph-bold ph-lg"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.dataSaver.media ? ` (${i18n.ts.video}${video.size ? ' ' + bytes(video.size) : ''})` : '' }}</b> - <b v-else style="display: block;"><i class="ph-film-strip ph-bold ph-lg"></i> {{ defaultStore.state.dataSaver.media && video.size ? bytes(video.size) : i18n.ts.video }}</b> - <span>{{ i18n.ts.clickToShow }}</span> - </div> -</div> -<div v-else :class="[$style.visible, (video.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitiveContainer]"> - <video - ref="videoEl" - :class="$style.video" - :poster="video.thumbnailUrl" - :title="video.comment ?? undefined" - :alt="video.comment" - preload="none" - controls - @contextmenu.stop - > - <source - :src="video.url" +<div + ref="playerEl" + :class="[ + $style.videoContainer, + controlsShowing && $style.active, + (video.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitive, + ]" + @mouseover="onMouseOver" + @mouseleave="onMouseLeave" + @contextmenu.stop +> + <button v-if="hide" :class="$style.hidden" @click="hide = false"> + <div :class="$style.hiddenTextWrapper"> + <b v-if="video.isSensitive" style="display: block;"><i class="ph-warning ph-bold ph-lg"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.dataSaver.media ? ` (${i18n.ts.video}${video.size ? ' ' + bytes(video.size) : ''})` : '' }}</b> + <b v-else style="display: block;"><i class="ph-film-strip ph-bold ph-lg"></i> {{ defaultStore.state.dataSaver.media && video.size ? bytes(video.size) : i18n.ts.video }}</b> + <span style="display: block;">{{ i18n.ts.clickToShow }}</span> + </div> + </button> + <div v-else :class="$style.videoRoot" @click.self="togglePlayPause"> + <video + ref="videoEl" + :class="$style.video" + :poster="video.thumbnailUrl ?? undefined" + :title="video.comment ?? undefined" + :alt="video.comment" + preload="metadata" + playsinline > - </video> - <i class="ph-eye-slash ph-bold ph-lg" :class="$style.hide" @click="hide = true"></i> + <source :src="video.url"> + </video> + <button v-if="isReady && !isPlaying" class="_button" :class="$style.videoOverlayPlayButton" @click="togglePlayPause"><i class="ph-play ph-bold ph-lg"></i></button> + <div v-else-if="!isActuallyPlaying" :class="$style.videoLoading"> + <MkLoading/> + </div> + <i class="ph-eye-closed ph-bold ph-lg" :class="$style.hide" @click="hide = true"></i> + <div :class="$style.indicators"> + <div v-if="video.comment" :class="$style.indicator">ALT</div> + <div v-if="video.isSensitive" :class="$style.indicator" style="color: var(--warn);" :title="i18n.ts.sensitive"><i class="ph-warning ph-bold ph-lg"></i></div> + </div> + <div :class="$style.videoControls" @click.self="togglePlayPause"> + <div :class="[$style.controlsChild, $style.controlsLeft]"> + <button class="_button" :class="$style.controlButton" @click="togglePlayPause"> + <i v-if="isPlaying" class="ph-pause ph-bold ph-lg"></i> + <i v-else class="ph-play ph-bold ph-lg"></i> + </button> + </div> + <div :class="[$style.controlsChild, $style.controlsRight]"> + <a class="_button" :class="$style.controlButton" :href="video.url" :download="video.name" target="_blank"> + <i class="ph-download ph-bold ph-lg"></i> + </a> + <button class="_button" :class="$style.controlButton" @click="showMenu"> + <i class="ph-gear ph-bold ph-lg"></i> + </button> + <button class="_button" :class="$style.controlButton" @click="toggleFullscreen"> + <i v-if="isFullscreen" class="ph-arrows-in ph-bold ph-lg"></i> + <i v-else class="ph-arrows-out ph-bold ph-lg"></i> + </button> + </div> + <div :class="[$style.controlsChild, $style.controlsTime]">{{ hms(elapsedTimeMs) }}</div> + <div :class="[$style.controlsChild, $style.controlsVolume]"> + <button class="_button" :class="$style.controlButton" @click="toggleMute"> + <i v-if="volume === 0" class="ph-speaker-x ph-bold ph-lg"></i> + <i v-else class="ph-speaker-high ph-bold ph-lg"></i> + </button> + <MkMediaRange + v-model="volume" + :sliderBgWhite="true" + :class="$style.volumeSeekbar" + /> + </div> + <MkMediaRange + v-model="rangePercent" + :sliderBgWhite="true" + :class="$style.seekbarRoot" + :buffer="bufferedDataRatio" + /> + </div> + </div> </div> </template> <script lang="ts" setup> -import { ref, shallowRef, watch } from 'vue'; +import { ref, shallowRef, computed, watch, onDeactivated, onActivated, onMounted } from 'vue'; import * as Misskey from 'misskey-js'; +import type { MenuItem } from '@/types/menu.js'; import bytes from '@/filters/bytes.js'; +import { hms } from '@/filters/hms.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; +import * as os from '@/os.js'; +import { isFullscreenNotSupported } from '@/scripts/device-kind.js'; import hasAudio from '@/scripts/media-has-audio.js'; +import MkMediaRange from '@/components/MkMediaRange.vue'; +import { iAmModerator } from '@/account.js'; const props = defineProps<{ video: Misskey.entities.DriveFile; }>(); +// eslint-disable-next-line vue/no-setup-props-destructure const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore')); +// Menu +const menuShowing = ref(false); + +function showMenu(ev: MouseEvent) { + let menu: MenuItem[] = []; + + menu = [ + // TODO: å†ç”Ÿã‚ューã«è¿½åŠ + { + text: i18n.ts.hide, + icon: 'ph-eye-closed ph-bold ph-lg', + action: () => { + hide.value = true; + }, + }, + ]; + + if (iAmModerator) { + menu.push({ + type: 'divider', + }, { + text: props.video.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive, + icon: props.video.isSensitive ? 'ph-eye ph-bold ph-lg' : 'ph-eye-slash ph-bold ph-lg', + danger: true, + action: () => toggleSensitive(props.video), + }); + } + + menuShowing.value = true; + os.popupMenu(menu, ev.currentTarget ?? ev.target, { + align: 'right', + onClosing: () => { + menuShowing.value = false; + }, + }); +} + +function toggleSensitive(file: Misskey.entities.DriveFile) { + os.apiWithDialog('drive/files/update', { + fileId: file.id, + isSensitive: !file.isSensitive, + }); +} + +// MediaControl: Video State const videoEl = shallowRef<HTMLVideoElement>(); +const playerEl = shallowRef<HTMLDivElement>(); +const isHoverring = ref(false); +const controlsShowing = computed(() => { + if (!oncePlayed.value) return true; + if (isHoverring.value) return true; + if (menuShowing.value) return true; + return false; +}); +const isFullscreen = ref(false); +let controlStateTimer: string | number; -watch(videoEl, () => { - if (videoEl.value) { - videoEl.value.volume = 0.3; - hasAudio(videoEl.value).then(had => { - if (!had) { - videoEl.value.loop = videoEl.value.muted = true; - videoEl.value.play(); +// MediaControl: Common State +const oncePlayed = ref(false); +const isReady = ref(false); +const isPlaying = ref(false); +const isActuallyPlaying = ref(false); +const elapsedTimeMs = ref(0); +const durationMs = ref(0); +const rangePercent = computed({ + get: () => { + return (elapsedTimeMs.value / durationMs.value) || 0; + }, + set: (to) => { + if (!videoEl.value) return; + videoEl.value.currentTime = to * durationMs.value / 1000; + }, +}); +const volume = ref(.25); +const bufferedEnd = ref(0); +const bufferedDataRatio = computed(() => { + if (!videoEl.value) return 0; + return bufferedEnd.value / videoEl.value.duration; +}); + +// MediaControl Events +function onMouseOver() { + if (controlStateTimer) { + clearTimeout(controlStateTimer); + } + isHoverring.value = true; +} + +function onMouseLeave() { + controlStateTimer = window.setTimeout(() => { + isHoverring.value = false; + }, 100); +} + +function togglePlayPause() { + if (!isReady.value || !videoEl.value) return; + + if (isPlaying.value) { + videoEl.value.pause(); + isPlaying.value = false; + } else { + videoEl.value.play(); + isPlaying.value = true; + oncePlayed.value = true; + } +} + +function toggleFullscreen() { + if (isFullscreenNotSupported && videoEl.value) { + if (isFullscreen.value) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + videoEl.value.webkitExitFullscreen(); + isFullscreen.value = false; + } else { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + videoEl.value.webkitEnterFullscreen(); + isFullscreen.value = true; + } + } else if (playerEl.value) { + if (isFullscreen.value) { + document.exitFullscreen(); + isFullscreen.value = false; + } else { + playerEl.value.requestFullscreen({ navigationUI: 'hide' }); + isFullscreen.value = true; + } + } +} + +function toggleMute() { + if (volume.value === 0) { + volume.value = .25; + } else { + volume.value = 0; + } +} + +let onceInit = false; +let stopVideoElWatch: () => void; + +function init() { + if (onceInit) return; + onceInit = true; + + stopVideoElWatch = watch(videoEl, () => { + if (videoEl.value) { + isReady.value = true; + + function updateMediaTick() { + if (videoEl.value) { + try { + bufferedEnd.value = videoEl.value.buffered.end(0); + } catch (err) { + bufferedEnd.value = 0; + } + + elapsedTimeMs.value = videoEl.value.currentTime * 1000; + } + window.requestAnimationFrame(updateMediaTick); } - }); + + updateMediaTick(); + + videoEl.value.addEventListener('play', () => { + isActuallyPlaying.value = true; + }); + + videoEl.value.addEventListener('pause', () => { + isActuallyPlaying.value = false; + isPlaying.value = false; + }); + + videoEl.value.addEventListener('ended', () => { + oncePlayed.value = false; + isActuallyPlaying.value = false; + isPlaying.value = false; + }); + + durationMs.value = videoEl.value.duration * 1000; + videoEl.value.addEventListener('durationchange', () => { + if (videoEl.value) { + durationMs.value = videoEl.value.duration * 1000; + } + }); + + videoEl.value.volume = volume.value; + hasAudio(videoEl.value).then(had => { + if (!had && videoEl.value) { + videoEl.value.loop = videoEl.value.muted = true; + videoEl.value.play(); + } + }); + } + }, { + immediate: true, + }); +} + +watch(volume, (to) => { + if (videoEl.value) videoEl.value.volume = to; +}); + +watch(hide, (to) => { + if (to && isFullscreen.value) { + document.exitFullscreen(); + isFullscreen.value = false; } }); + +onMounted(() => { + init(); +}); + +onActivated(() => { + init(); +}); + +onDeactivated(() => { + isReady.value = false; + isPlaying.value = false; + isActuallyPlaying.value = false; + elapsedTimeMs.value = 0; + durationMs.value = 0; + bufferedEnd.value = 0; + hide.value = (defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore'); + stopVideoElWatch(); + onceInit = false; +}); </script> <style lang="scss" module> -.visible { +.videoContainer { + container-type: inline-size; position: relative; + overflow: clip; } -.sensitiveContainer { +.sensitive { position: relative; &::after { @@ -81,45 +361,199 @@ watch(videoEl, () => { } } +.indicators { + display: inline-flex; + position: absolute; + top: 10px; + left: 10px; + pointer-events: none; + opacity: .5; + gap: 6px; +} + +.indicator { + /* Hardcode to black because either --bg or --fg makes it hard to read in dark/light mode */ + background-color: black; + border-radius: 6px; + color: var(--accentLighten); + display: inline-block; + font-weight: bold; + font-size: 0.8em; + padding: 2px 5px; +} + .hide { display: block; position: absolute; border-radius: var(--radius-sm); background-color: black; color: var(--accentLighten); - font-size: 14px; + font-size: 12px; opacity: .5; - padding: 3px 6px; + padding: 5px 8px; text-align: center; cursor: pointer; top: 12px; right: 12px; } -.video { +.hidden { + width: 100%; + height: 100%; + background: #000; + border: none; + outline: none; + font: inherit; + color: inherit; + cursor: pointer; + padding: 120px 0; display: flex; - justify-content: center; align-items: center; - font-size: 3.5em; - overflow: hidden; - background-position: center; - background-size: cover; + justify-content: center; +} + +.hiddenTextWrapper { + text-align: center; + font-size: 0.8em; + color: #fff; +} + +.videoRoot { + background: #000; + position: relative; width: 100%; height: 100%; + object-fit: contain; } -.hidden { +.video { + display: block; + height: 100%; + width: 100%; +} + +.videoOverlayPlayButton { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%,-50%); + + opacity: 0; + transition: opacity .4s ease-in-out; + + background: var(--accent); + color: #fff; + padding: 1rem; + border-radius: 99rem; + + font-size: 1.1rem; +} + +.videoLoading { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; display: flex; + align-items: center; justify-content: center; +} + +.videoControls { + display: grid; + grid-template-areas: + "left time . volume right" + "seekbar seekbar seekbar seekbar seekbar"; + grid-template-columns: auto auto 1fr auto auto; + align-items: center; + gap: 4px 8px; + + padding: 35px 10px 10px 10px; + background: linear-gradient(rgba(0, 0, 0, 0),rgba(0, 0, 0, .75)); + + position: absolute; + left: 0; + right: 0; + bottom: 0; + + transform: translateY(100%); + pointer-events: none; + opacity: 0; + transition: opacity .4s ease-in-out, transform .4s ease-in-out; +} + +.active { + .videoControls { + transform: translateY(0); + opacity: 1; + pointer-events: auto; + } + + .videoOverlayPlayButton { + opacity: 1; + } +} + +.controlsChild { + display: flex; align-items: center; - background: #111; + gap: 4px; color: #fff; + + .controlButton { + padding: 6px; + border-radius: calc(var(--radius) / 2); + transition: background-color .2s ease-in-out; + font-size: 1.05rem; + + &:hover { + background-color: var(--accent); + } + } } -.sensitive { - display: table-cell; - text-align: center; - font-size: 12px; +.controlsLeft { + grid-area: left; +} + +.controlsRight { + grid-area: right; +} + +.controlsTime { + grid-area: time; + font-size: .9rem; +} + +.controlsVolume { + grid-area: volume; + + .volumeSeekbar { + display: none; + } +} + +.seekbarRoot { + grid-area: seekbar; + /* ▼シークãƒãƒ¼æ“作をやりやã™ãã™ã‚‹ãŸã‚ã«ã‚¯ãƒªãƒƒã‚¯ã‚¤ãƒ™ãƒ³ãƒˆãŒä¼æ’ã•ã‚Œãªã„エリアを拡張ã™ã‚‹ */ + margin: -10px; + padding: 10px; +} + +@container (min-width: 500px) { + .videoControls { + grid-template-areas: "left seekbar time volume right"; + grid-template-columns: auto 1fr auto auto auto; + } + + .controlsVolume { + .volumeSeekbar { + max-width: 90px; + display: block; + flex-grow: 1; + } + } } .indicators { display: inline-flex; diff --git a/packages/frontend/src/components/MkMention.vue b/packages/frontend/src/components/MkMention.vue index 4d420536579f17986ed82e3d82ece7e136604bb2..942c23a14535f17b3539026cdf355beadafb7a70 100644 --- a/packages/frontend/src/components/MkMention.vue +++ b/packages/frontend/src/components/MkMention.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -51,6 +51,7 @@ const avatarUrl = computed(() => defaultStore.state.disableShowingAnimatedImages padding: 4px 8px 4px 4px; border-radius: var(--radius-ellipse); color: var(--mention); + white-space: nowrap; &.isMe { color: var(--mentionMe); diff --git a/packages/frontend/src/components/MkMenu.child.vue b/packages/frontend/src/components/MkMenu.child.vue index 962dcd91eb5952b5ff87d4e75bd51d2de4de312c..dfb6d346182ef56d1f1dee0154938b0c5318cbf7 100644 --- a/packages/frontend/src/components/MkMenu.child.vue +++ b/packages/frontend/src/components/MkMenu.child.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -33,6 +33,7 @@ const align = 'left'; const SCROLLBAR_THICKNESS = 16; function setPosition() { + if (el.value == null) return; const rootRect = props.rootElement.getBoundingClientRect(); const parentRect = props.targetElement.getBoundingClientRect(); const myRect = el.value.getBoundingClientRect(); @@ -66,7 +67,7 @@ const ro = new ResizeObserver((entries, observer) => { }); onMounted(() => { - ro.observe(el.value); + if (el.value) ro.observe(el.value); setPosition(); nextTick(() => { setPosition(); @@ -79,7 +80,7 @@ onUnmounted(() => { defineExpose({ checkHit: (ev: MouseEvent) => { - return (ev.target === el.value || el.value.contains(ev.target)); + return (ev.target === el.value || el.value?.contains(ev.target as Node)); }, }); </script> diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index 5f48f43bfbb77af386ff5a520fe4d9afbe4b43ef..8395879d02db3e2dd1616779fb58dd75fbc0b31d 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only :style="{ width: (width && !asDrawer) ? width + 'px' : '', maxHeight: maxHeight ? maxHeight + 'px' : '' }" @contextmenu.self="e => e.preventDefault()" > - <template v-for="(item, i) in items2"> + <template v-for="(item, i) in (items2 ?? [])"> <div v-if="item.type === 'divider'" role="separator" :class="$style.divider"></div> <span v-else-if="item.type === 'label'" role="menuitem" :class="[$style.label, $style.item]"> <span style="opacity: 0.7;">{{ item.text }}</span> @@ -54,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span :class="$style.caret" style="pointer-events: none;"><i class="ph-caret-right ph-bold ph-lg ti-fw"></i></span> </div> </button> - <button v-else :tabindex="i" class="_button" role="menuitem" :class="[$style.item, { [$style.danger]: item.danger, [$style.active]: item.active }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> + <button v-else :tabindex="i" class="_button" role="menuitem" :class="[$style.item, { [$style.danger]: item.danger, [$style.active]: getValue(item.active) }]" :disabled="getValue(item.active)" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i> <MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/> <div :class="$style.item_content"> @@ -63,18 +63,18 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </button> </template> - <span v-if="items2.length === 0" :class="[$style.none, $style.item]"> + <span v-if="items2 == null || items2.length === 0" :class="[$style.none, $style.item]"> <span>{{ i18n.ts.none }}</span> </span> </div> <div v-if="childMenu"> - <XChild ref="child" :items="childMenu" :targetElement="childTarget" :rootElement="itemsEl" showing @actioned="childActioned" @close="close(false)"/> + <XChild ref="child" :items="childMenu" :targetElement="childTarget!" :rootElement="itemsEl!" showing @actioned="childActioned" @close="close(false)"/> </div> </div> </template> <script lang="ts"> -import { computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'; +import { ComputedRef, computed, defineAsyncComponent, isRef, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'; import { focusPrev, focusNext } from '@/scripts/focus.js'; import MkSwitchButton from '@/components/MkSwitch.button.vue'; import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu.js'; @@ -104,7 +104,7 @@ const emit = defineEmits<{ const itemsEl = shallowRef<HTMLDivElement>(); -const items2 = ref<InnerMenuItem[]>([]); +const items2 = ref<InnerMenuItem[]>(); const child = shallowRef<InstanceType<typeof XChild>>(); @@ -119,15 +119,15 @@ const childShowingItem = ref<MenuItem | null>(); let preferClick = isTouchUsing || props.asDrawer; watch(() => props.items, () => { - const items: (MenuItem | MenuPending)[] = [...props.items].filter(item => item !== undefined); + const items = [...props.items].filter(item => item !== undefined) as (NonNullable<MenuItem> | MenuPending)[]; for (let i = 0; i < items.length; i++) { const item = items[i]; - if (item && 'then' in item) { // if item is Promise + if ('then' in item) { // if item is Promise items[i] = { type: 'pending' }; item.then(actualItem => { - items2.value[i] = actualItem; + if (items2.value?.[i]) items2.value[i] = actualItem; }); } } @@ -151,7 +151,7 @@ function childActioned() { } const onGlobalMousedown = (event: MouseEvent) => { - if (childTarget.value && (event.target === childTarget.value || childTarget.value.contains(event.target))) return; + if (childTarget.value && (event.target === childTarget.value || childTarget.value.contains(event.target as Node))) return; if (child.value && child.value.checkHit(event)) return; closeChild(); }; @@ -169,7 +169,7 @@ function onItemMouseLeave(item) { } async function showChildren(item: MenuParent, ev: MouseEvent) { - const children = await (async () => { + const children: MenuItem[] = await (async () => { if (childrenCache.has(item)) { return childrenCache.get(item)!; } else { @@ -189,7 +189,7 @@ async function showChildren(item: MenuParent, ev: MouseEvent) { }); emit('hide'); } else { - childTarget.value = ev.currentTarget ?? ev.target; + childTarget.value = (ev.currentTarget ?? ev.target) as HTMLElement; // ã“ã‚Œã§ã‚‚リアクティビティã¯ä¿ãŸã‚Œã‚‹ childMenu.value = children; childShowingItem.value = item; @@ -218,6 +218,10 @@ function switchItem(item: MenuSwitch & { ref: any }) { item.ref = !item.ref; } +function getValue<T>(item?: ComputedRef<T> | T) { + return isRef(item) ? item.value : item; +} + onMounted(() => { if (props.viaKeyboard) { nextTick(() => { @@ -450,7 +454,7 @@ onBeforeUnmount(() => { align-items: center; color: var(--indicator); font-size: 12px; - animation: blink 1s infinite; + animation: global-blink 1s infinite; } .divider { diff --git a/packages/frontend/src/components/MkMiniChart.vue b/packages/frontend/src/components/MkMiniChart.vue index f0a2c232bd0b1f5bd9bd3a3f7bb1c48eef6e2518..f2f2bf47a8b59a1bf0d26be87ea8f10c7dd34b70 100644 --- a/packages/frontend/src/components/MkMiniChart.vue +++ b/packages/frontend/src/components/MkMiniChart.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -22,8 +22,8 @@ SPDX-License-Identifier: AGPL-3.0-only stroke-width="2" /> <circle - :cx="headX" - :cy="headY" + :cx="headX ?? undefined" + :cy="headY ?? undefined" r="3" :fill="color" /> diff --git a/packages/frontend/src/components/MkModPlayer.vue b/packages/frontend/src/components/MkModPlayer.vue index f61144cbcafc5b6f9da6420263ef9da46f9f7964..75053cbc376931cb5afa0133e9ab43c936fb77ca 100644 --- a/packages/frontend/src/components/MkModPlayer.vue +++ b/packages/frontend/src/components/MkModPlayer.vue @@ -7,14 +7,17 @@ </div> <div v-else class="mod-player-enabled"> - <div class="pattern-display" @click="togglePattern()"> + <div class="pattern-display" @click="togglePattern()" @scroll="scrollHandler" @scrollend="scrollEndHandle"> <div v-if="patternHide" class="pattern-hide"> <b><i class="ph-eye ph-bold ph-lg"></i> Pattern Hidden</b> <span>{{ i18n.ts.clickToShow }}</span> </div> + <span class="patternShadowTop"></span> + <span class="patternShadowBottom"></span> <canvas ref="displayCanvas" class="pattern-canvas"></canvas> </div> <div class="controls"> + <input v-if="patternScrollSliderShow" ref="patternScrollSlider" v-model="patternScrollSliderPos" class="pattern-slider" type="range" min="0" max="100" step="0.01" style=""/> <button class="play" @click="playPause()"> <i v-if="playing" class="ph-pause ph-bold ph-lg"></i> <i v-else class="ph-play ph-bold ph-lg"></i> @@ -33,44 +36,31 @@ </template> <script lang="ts" setup> -import { ref, nextTick, computed } from 'vue'; +import { ref, nextTick, computed, watch, onDeactivated, onMounted } from 'vue'; import * as Misskey from 'misskey-js'; import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; import { ChiptuneJsPlayer, ChiptuneJsConfig } from '@/scripts/chiptune2.js'; - -const CHAR_WIDTH = 6; -const CHAR_HEIGHT = 12; -const ROW_OFFSET_Y = 10; +import { isTouchUsing } from '@/scripts/touch.js'; const colours = { background: '#000000', - default: { - active: '#ffffff', - inactive: '#808080', - }, - quarter: { - active: '#ffff00', - inactive: '#ffe135', - }, - instr: { - active: '#80e0ff', - inactive: '#0099cc', - }, - volume: { - active: '#80ff80', - inactive: '#008000', - }, - fx: { - active: '#ff80e0', - inactive: '#800060', - }, - operant: { - active: '#ffe080', - inactive: '#806000', + foreground: { + default: '#ffffff', + quarter: '#ffff00', + instr: '#80e0ff', + volume: '#80ff80', + fx: '#ff80e0', + operant: '#ffe080', }, }; +const CHAR_WIDTH = 6; +const CHAR_HEIGHT = 12; +const ROW_OFFSET_Y = 10; +const MAX_TIME_SPENT = 50; +const MAX_TIME_PER_ROW = 15; + const props = defineProps<{ module: Misskey.entities.DriveFile }>(); @@ -79,29 +69,57 @@ const isSensitive = computed(() => { return props.module.isSensitive; }); const url = computed(() => { return props.module.url; }); let hide = ref((defaultStore.state.nsfw === 'force') ? true : isSensitive.value && (defaultStore.state.nsfw !== 'ignore')); let patternHide = ref(false); -let firstFrame = ref(true); let playing = ref(false); let displayCanvas = ref<HTMLCanvasElement>(); let progress = ref<HTMLProgressElement>(); let position = ref(0); +let patternScrollSlider = ref<HTMLProgressElement>(); +let patternScrollSliderShow = ref(false); +let patternScrollSliderPos = ref(0); const player = ref(new ChiptuneJsPlayer(new ChiptuneJsConfig())); -const rowBuffer = 24; +const maxRowNumbers = 0xFF; +const rowBuffer = 26; let buffer = null; let isSeeking = false; +let firstFrame = true; +let lastPattern = -1; +let lastDrawnRow = -1; +let numberRowCanvas = new OffscreenCanvas(2 * CHAR_WIDTH + 1, maxRowNumbers * CHAR_HEIGHT + 1); +let alreadyHiddenOnce = false; +let alreadyDrawn = [false]; +let patternTime = { 'current': 0, 'max': 0, 'initial': 0 }; + +function bakeNumberRow() { + let ctx = numberRowCanvas.getContext('2d', { alpha: false }) as OffscreenCanvasRenderingContext2D; + ctx.font = '10px monospace'; -player.value.load(url.value).then((result) => { - buffer = result; - try { - player.value.play(buffer); - progress.value!.max = player.value.duration(); - display(); - } catch (err) { - console.warn(err); + for (let i = 0; i < maxRowNumbers; i++) { + let rowText = i.toString(16); + if (rowText.length === 1) rowText = '0' + rowText; + + ctx.fillStyle = colours.foreground.default; + if (i % 4 === 0) ctx.fillStyle = colours.foreground.quarter; + + ctx.fillText(rowText, 0, 10 + i * 12); } - player.value.stop(); -}).catch((error) => { - console.error(error); +} + +onMounted(() => { + player.value.load(url.value).then((result) => { + buffer = result; + try { + player.value.play(buffer); + progress.value!.max = player.value.duration(); + bakeNumberRow(); + display(); + } catch (err) { + console.warn(err); + } + player.value.stop(); + }).catch((error) => { + console.error(error); + }); }); function playPause() { @@ -133,7 +151,7 @@ function stop(noDisplayUpdate = false) { if (!noDisplayUpdate) { try { player.value.play(buffer); - display(); + display(true); } catch (err) { console.warn(err); } @@ -162,104 +180,256 @@ function performSeek() { function toggleVisible() { hide.value = !hide.value; - if (!hide.value && patternHide.value) { - firstFrame.value = true; - patternHide.value = false; + if (!hide.value) { + lastPattern = -1; + lastDrawnRow = -1; } nextTick(() => { stop(hide.value); }); } function togglePattern() { patternHide.value = !patternHide.value; - if (!patternHide.value) { - if (player.value.getRow() === 0) { - try { - player.value.play(buffer); - display(); - } catch (err) { - console.warn(err); - } - player.value.stop(); + handleScrollBarEnable(); + + if (player.value.getRow() === 0 && player.value.getPattern() === 0) { + try { + player.value.play(buffer); + display(true); + } catch (err) { + console.warn(err); } + player.value.stop(); + } else { + display(true); } } -function display() { - if (!displayCanvas.value) { - stop(); - return; +function drawPattern() { + if (!displayCanvas.value) return; + const canvas = displayCanvas.value; + + const startTime = performance.now(); + const pattern = player.value.getPattern(); + const nbRows = player.value.getPatternNumRows(pattern); + const row = player.value.getRow(); + const halfbuf = rowBuffer / 2; + const minRow = row - halfbuf; + const maxRow = row + halfbuf; + + let rowDif = 0; + + let nbChannels = 0; + if (player.value.currentPlayingNode) { + nbChannels = player.value.currentPlayingNode.nbChannels; } + if (pattern === lastPattern) { + rowDif = row - lastDrawnRow; + } else { + if (patternTime.initial !== 0 && !alreadyHiddenOnce) { + const trackerTime = player.value.currentPlayingNode.getProcessTime(); - if (patternHide.value) return; + if (patternTime.initial + trackerTime.max > MAX_TIME_SPENT && trackerTime.max + patternTime.max > MAX_TIME_PER_ROW) { + alreadyHiddenOnce = true; + togglePattern(); + return; + } + } - if (firstFrame.value) { - firstFrame.value = false; - patternHide.value = true; + patternTime = { 'current': 0, 'max': 0, 'initial': 0 }; + alreadyDrawn = []; + if (canvas.width !== (12 + 84 * nbChannels + 2)) canvas.width = 12 + 84 * nbChannels + 2; + if (canvas.height !== (12 * nbRows)) canvas.height = 12 * nbRows; + } + + const ctx = canvas.getContext('2d', { alpha: false, desynchronized: true }) as CanvasRenderingContext2D; + if (ctx.font !== '10px monospace') ctx.font = '10px monospace'; + ctx.imageSmoothingEnabled = false; + if (pattern !== lastPattern) { + ctx.fillStyle = colours.background; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.drawImage( numberRowCanvas, 0, 0 ); } + ctx.fillStyle = colours.foreground.default; + for (let rowOffset = minRow + rowDif; rowOffset < maxRow + rowDif; rowOffset++) { + const rowToDraw = rowOffset - rowDif; + + if (alreadyDrawn[rowToDraw] === true) continue; + + if (rowToDraw >= 0 && rowToDraw < nbRows) { + const baseOffset = 2 * CHAR_WIDTH; + const baseRowOffset = ROW_OFFSET_Y + rowToDraw * CHAR_HEIGHT; + let done = drawRow(ctx, rowToDraw, nbChannels, pattern, baseOffset, baseRowOffset); + + alreadyDrawn[rowToDraw] = done; + } + } + + lastDrawnRow = row; + lastPattern = pattern; + + patternTime.current = performance.now() - startTime; + if (patternTime.initial !== 0 && patternTime.current > patternTime.max) patternTime.max = patternTime.current; + else if (patternTime.initial === 0) patternTime.initial = patternTime.current; +} + +function drawPetternPreview() { + if (!displayCanvas.value) return; const canvas = displayCanvas.value; const pattern = player.value.getPattern(); + const nbRows = player.value.getPatternNumRows(pattern); const row = player.value.getRow(); + const halfbuf = rowBuffer / 2; + alreadyDrawn = []; + let nbChannels = 0; if (player.value.currentPlayingNode) { nbChannels = player.value.currentPlayingNode.nbChannels; } - if (canvas.width !== 12 + 84 * nbChannels + 2) { - canvas.width = 12 + 84 * nbChannels + 2; - canvas.height = 12 * rowBuffer; - } - const nbRows = player.value.getPatternNumRows(pattern); - const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; + if (canvas.width !== (12 + 84 * nbChannels + 2)) canvas.width = 12 + 84 * nbChannels + 2; + if (canvas.height !== (12 * rowBuffer)) canvas.height = 12 * rowBuffer; + + const ctx = canvas.getContext('2d', { alpha: false }) as CanvasRenderingContext2D; ctx.font = '10px monospace'; + ctx.imageSmoothingEnabled = false; ctx.fillStyle = colours.background; ctx.fillRect(0, 0, canvas.width, canvas.height); - ctx.fillStyle = colours.default.inactive; + ctx.drawImage( numberRowCanvas, 0, (halfbuf - row) * CHAR_HEIGHT ); + for (let rowOffset = 0; rowOffset < rowBuffer; rowOffset++) { - const rowToDraw = row - rowBuffer / 2 + rowOffset; + const rowToDraw = rowOffset + row - halfbuf; + if (rowToDraw >= 0 && rowToDraw < nbRows) { - const active = (rowToDraw === row) ? 'active' : 'inactive'; - let rowText = parseInt(rowToDraw).toString(16); - if (rowText.length === 1) { - rowText = '0' + rowText; - } - ctx.fillStyle = colours.default[active]; - if (rowToDraw % 4 === 0) { - ctx.fillStyle = colours.quarter[active]; - } - ctx.fillText(rowText, 0, 10 + rowOffset * 12); - for (let channel = 0; channel < nbChannels; channel++) { - const part = player.value.getPatternRowChannel(pattern, rowToDraw, channel); - const baseOffset = (2 + (part.length + 1) * channel) * CHAR_WIDTH; - const baseRowOffset = ROW_OFFSET_Y + rowOffset * CHAR_HEIGHT; - - ctx.fillStyle = colours.default[active]; - ctx.fillText('|', baseOffset, baseRowOffset); - - const note = part.substring(0, 3); - ctx.fillStyle = colours.default[active]; - ctx.fillText(note, baseOffset + CHAR_WIDTH, baseRowOffset); - - const instr = part.substring(4, 6); - ctx.fillStyle = colours.instr[active]; - ctx.fillText(instr, baseOffset + CHAR_WIDTH * 5, baseRowOffset); - - const volume = part.substring(6, 9); - ctx.fillStyle = colours.volume[active]; - ctx.fillText(volume, baseOffset + CHAR_WIDTH * 7, baseRowOffset); - - const fx = part.substring(10, 11); - ctx.fillStyle = colours.fx[active]; - ctx.fillText(fx, baseOffset + CHAR_WIDTH * 11, baseRowOffset); - - const op = part.substring(11, 13); - ctx.fillStyle = colours.operant[active]; - ctx.fillText(op, baseOffset + CHAR_WIDTH * 12, baseRowOffset); - } + const baseOffset = 2 * CHAR_WIDTH; + const baseRowOffset = ROW_OFFSET_Y + rowOffset * CHAR_HEIGHT; + drawRow(ctx, rowToDraw, nbChannels, pattern, baseOffset, baseRowOffset); + } else if (rowToDraw >= 0) { + const baseRowOffset = ROW_OFFSET_Y + rowOffset * CHAR_HEIGHT; + ctx.fillStyle = colours.background; + ctx.fillRect(0, baseRowOffset - CHAR_HEIGHT, CHAR_WIDTH * 2, baseRowOffset); } } + + lastPattern = -1; + lastDrawnRow = -1; +} + +function drawRow(ctx: CanvasRenderingContext2D, row: number, channels: number, pattern: number, drawX = (2 * CHAR_WIDTH), drawY = ROW_OFFSET_Y) { + if (!player.value.currentPlayingNode) return false; + if (alreadyDrawn[row]) return true; + const spacer = 11; + const space = ' '; + let seperators = ''; + let note = ''; + let instr = ''; + let volume = ''; + let fx = ''; + let op = ''; + for (let channel = 0; channel < channels; channel++) { + const part = player.value.getPatternRowChannel(pattern, row, channel); + + seperators += '|' + space.repeat( spacer + 2 ); + note += part.substring(0, 3) + space.repeat( spacer ); + instr += part.substring(4, 6) + space.repeat( spacer + 1 ); + volume += part.substring(6, 9) + space.repeat( spacer ); + fx += part.substring(10, 11) + space.repeat( spacer + 2 ); + op += part.substring(11, 13) + space.repeat( spacer + 1 ); + } + + ctx.fillStyle = colours.foreground.default; + ctx.fillText(seperators, drawX, drawY); + + ctx.fillStyle = colours.foreground.default; + ctx.fillText(note, drawX + CHAR_WIDTH, drawY); + + ctx.fillStyle = colours.foreground.instr; + ctx.fillText(instr, drawX + CHAR_WIDTH * 5, drawY); + + ctx.fillStyle = colours.foreground.volume; + ctx.fillText(volume, drawX + CHAR_WIDTH * 7, drawY); + + ctx.fillStyle = colours.foreground.fx; + ctx.fillText(fx, drawX + CHAR_WIDTH * 11, drawY); + + ctx.fillStyle = colours.foreground.operant; + ctx.fillText(op, drawX + CHAR_WIDTH * 12, drawY); + + return true; +} + +function display(skipOptimizationChecks = false) { + if (!displayCanvas.value || !displayCanvas.value.parentElement) { + stop(); + return; + } + + if (patternHide.value && !skipOptimizationChecks) return; + + if (firstFrame) { + // Changing it to false should enable pattern display by default. + patternHide.value = true; + handleScrollBarEnable(); + firstFrame = false; + } + + const row = player.value.getRow(); + const pattern = player.value.getPattern(); + + if ( row === lastDrawnRow && pattern === lastPattern && !skipOptimizationChecks) return; + + // Size vs speed + if (patternHide.value) drawPetternPreview(); + else drawPattern(); + + displayCanvas.value.style.top = !patternHide.value ? 'calc( 50% - ' + (row * CHAR_HEIGHT) + 'px )' : '0%'; +} + +let suppressScrollSliderWatcher = false; + +function scrollHandler() { + suppressScrollSliderWatcher = true; + + if (!patternScrollSlider.value) return; + if (!displayCanvas.value) return; + if (!displayCanvas.value.parentElement) return; + + patternScrollSliderPos.value = (displayCanvas.value.parentElement.scrollLeft) / (displayCanvas.value.width - displayCanvas.value.parentElement.offsetWidth) * 100; + patternScrollSlider.value.style.opacity = '1'; +} + +function scrollEndHandle() { + suppressScrollSliderWatcher = false; + + if (!patternScrollSlider.value) return; + patternScrollSlider.value.style.opacity = ''; +} + +function handleScrollBarEnable() { + patternScrollSliderShow.value = (!patternHide.value && !isTouchUsing); + if (patternScrollSliderShow.value !== true) return; + + if (!displayCanvas.value) return; + if (!displayCanvas.value.parentElement) return; + if (firstFrame) { + patternScrollSliderShow.value = (12 + 84 * player.value.getPatternNumRows(player.value.getPattern()) + 2 > displayCanvas.value.parentElement.offsetWidth); + } else { + patternScrollSliderShow.value = (displayCanvas.value.width > displayCanvas.value.parentElement.offsetWidth); + } } +watch(patternScrollSliderPos, () => { + if (suppressScrollSliderWatcher) return; + if (!displayCanvas.value) return; + if (!displayCanvas.value.parentElement) return; + + displayCanvas.value.parentElement.scrollLeft = (displayCanvas.value.width - displayCanvas.value.parentElement.offsetWidth) * patternScrollSliderPos.value / 100; +}); + +onDeactivated(() => { + stop(); +}); + </script> <style lang="scss" scoped> @@ -290,6 +460,7 @@ function display() { cursor: pointer; top: 12px; right: 12px; + z-index: 4; } > .pattern-display { @@ -299,22 +470,55 @@ function display() { overflow-y: hidden; background-color: black; text-align: center; + max-height: 312px; /* magic_number = CHAR_HEIGHT * rowBuffer, needs to be in px */ + + scrollbar-width: none; + + &::-webkit-scrollbar { + display: none; + } + .pattern-canvas { + position: relative; background-color: black; - height: 100%; + image-rendering: pixelated; + pointer-events: none; + z-index: 0; } + + .patternShadowTop { + background: #00000080; + width: 100%; + height: calc( 50% - 14px ); + translate: 0 -100%; + top: calc( 50% - 14px ); + position: absolute; + pointer-events: none; + z-index: 2; + } + + .patternShadowBottom { + background: #00000080; + width: 100%; + height: calc( 50% - 12px ); + top: calc( 50% - 1px ); + position: absolute; + pointer-events: none; + z-index: 2; + } + .pattern-hide { display: flex; flex-direction: column; justify-content: center; align-items: center; background: rgba(64, 64, 64, 0.3); - backdrop-filter: blur(2em); + backdrop-filter: var(--modalBgFilter); color: #fff; font-size: 12px; position: absolute; - z-index: 0; + z-index: 4; width: 100%; height: 100%; @@ -328,7 +532,7 @@ function display() { display: flex; width: 100%; background-color: var(--bg); - z-index: 1; + z-index: 5; > * { padding: 4px 8px; @@ -353,6 +557,18 @@ function display() { margin: 4px 8px; overflow-x: hidden; + &.pattern-slider { + position: absolute; + width: calc( 100% - 8px * 2 ); + top: calc( 100% - 21px * 3 ); + opacity: 0%; + transition: opacity 0.2s; + + &:hover { + opacity: 100%; + } + } + &:focus { outline: none; diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue index 5cd31cdf7c91df69c9d5991a4cb10921968a7f82..40e67fb4e088031b0d13dd8ce18af0f2cf5a37c3 100644 --- a/packages/frontend/src/components/MkModal.vue +++ b/packages/frontend/src/components/MkModal.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkModalWindow.vue b/packages/frontend/src/components/MkModalWindow.vue index b91988304df645ee6e9e785d823f4642f1d61d25..fc634176c73e6f828e968317f6078af273d56343 100644 --- a/packages/frontend/src/components/MkModalWindow.vue +++ b/packages/frontend/src/components/MkModalWindow.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -51,7 +51,7 @@ const bodyWidth = ref(0); const bodyHeight = ref(0); const close = () => { - modal.value.close(); + modal.value?.close(); }; const onBgClick = () => { @@ -67,11 +67,13 @@ const onKeydown = (evt) => { }; const ro = new ResizeObserver((entries, observer) => { + if (rootEl.value == null || headerEl.value == null) return; bodyWidth.value = rootEl.value.offsetWidth; bodyHeight.value = rootEl.value.offsetHeight - headerEl.value.offsetHeight; }); onMounted(() => { + if (rootEl.value == null || headerEl.value == null) return; bodyWidth.value = rootEl.value.offsetWidth; bodyHeight.value = rootEl.value.offsetHeight - headerEl.value.offsetHeight; ro.observe(rootEl.value); diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 8a3b4cef481fceaea353c0beb802b40a9bf73b15..9a667c3118fdd81e9ccdceec96f6374b9774f2ae 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -1,13 +1,13 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <div - v-if="!hardMuted && !muted" + v-if="!hardMuted && muted === false" v-show="!isDeleted" - ref="el" + ref="rootEl" v-hotkey="keymap" :class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover }]" :tabindex="!isDeleted ? '-1' : undefined" @@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only </span> <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span> <span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ph-television ph-bold ph-lg"></i></span> - <span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span> + <span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil-simple ph-bold ph-lg"></i></span> </div> </div> <div v-if="renoteCollapsed" :class="$style.collapsedRenoteTarget"> @@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only <article v-else :class="$style.article" @contextmenu.stop="onContextmenu"> <div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div> <MkAvatar :class="$style.avatar" :user="appearNote.user" :link="!mock" :preview="!mock"/> - <div :class="[$style.main, { [$style.clickToOpen]: defaultStore.state.clickToOpen }]" @click="defaultStore.state.clickToOpen ? noteclick(appearNote.id) : undefined"> + <div :class="[$style.main, { [$style.clickToOpen]: defaultStore.state.clickToOpen }]" @click.stop="defaultStore.state.clickToOpen ? noteclick(appearNote.id) : undefined"> <MkNoteHeader :note="appearNote" :mini="true" @click.stop/> <MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/> <div style="container-type: inline-size;"> @@ -74,18 +74,18 @@ SPDX-License-Identifier: AGPL-3.0-only /> <div v-if="translating || translation" :class="$style.translation"> <MkLoading v-if="translating" mini/> - <div v-else> - <b>{{ i18n.t('translatedFrom', { x: translation.sourceLang }) }}: </b> + <div v-else-if="translation"> + <b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b> <Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> </div> </div> <MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton> <MkButton v-else-if="!defaultStore.state.animatedMfm && allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton> </div> - <div v-if="appearNote.files.length > 0"> + <div v-if="appearNote.files && appearNote.files.length > 0"> <MkMediaList :mediaList="appearNote.files" @click.stop/> </div> - <MkPoll v-if="appearNote.poll" :note="appearNote" :class="$style.poll" @click.stop/> + <MkPoll v-if="appearNote.poll" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll" @click.stop/> <MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :class="$style.urlPreview" @click.stop/> <div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div> <button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click.stop @click="collapsed = false"> @@ -145,7 +145,7 @@ SPDX-License-Identifier: AGPL-3.0-only <button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown="clip()"> <i class="ph-paperclip ph-bold ph-lg"></i> </button> - <button ref="menuButton" :class="$style.footerButton" class="_button" @mousedown="menu()"> + <button ref="menuButton" :class="$style.footerButton" class="_button" @mousedown="showMenu()"> <i class="ph-dots-three ph-bold ph-lg"></i> </button> </footer> @@ -153,7 +153,14 @@ SPDX-License-Identifier: AGPL-3.0-only </article> </div> <div v-else-if="!hardMuted" :class="$style.muted" @click="muted = false"> - <I18n :src="i18n.ts.userSaysSomething" tag="small"> + <I18n v-if="muted === 'sensitiveMute'" :src="i18n.ts.userSaysSomethingSensitive" tag="small"> + <template #name> + <MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)"> + <MkUserName :user="appearNote.user"/> + </MkA> + </template> + </I18n> + <I18n v-else :src="i18n.ts.userSaysSomething" tag="small"> <template #name> <MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)"> <MkUserName :user="appearNote.user"/> @@ -171,7 +178,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { computed, inject, onMounted, ref, shallowRef, Ref, watch, provide } from 'vue'; -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; import * as Misskey from 'misskey-js'; import MkNoteSub from '@/components/MkNoteSub.vue'; import MkNoteHeader from '@/components/MkNoteHeader.vue'; @@ -190,6 +197,7 @@ import { checkWordMute } from '@/scripts/check-word-mute.js'; import { userPage } from '@/filters/user.js'; import * as os from '@/os.js'; import * as sound from '@/scripts/sound.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore, noteViewInterruptors } from '@/store.js'; import { reactionPicker } from '@/scripts/reaction-picker.js'; import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js'; @@ -207,7 +215,8 @@ import { MenuItem } from '@/types/menu.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; import { shouldCollapsed } from '@/scripts/collapsed.js'; -import { useRouter } from '@/router.js'; +import { useRouter } from '@/router/supplier.js'; +import { boostMenuItems, type Visibility } from '@/scripts/boost-quote.js'; const props = withDefaults(defineProps<{ note: Misskey.entities.Note; @@ -227,6 +236,7 @@ const emit = defineEmits<{ const router = useRouter(); +const inTimeline = inject<boolean>('inTimeline', false); const inChannel = inject('inChannel', null); const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null); @@ -245,7 +255,7 @@ if (noteViewInterruptors.length > 0) { let result: Misskey.entities.Note | null = deepClone(note.value); for (const interruptor of noteViewInterruptors) { try { - result = await interruptor.handler(result); + result = await interruptor.handler(result!) as Misskey.entities.Note | null; if (result === null) { isDeleted.value = true; return; @@ -254,7 +264,7 @@ if (noteViewInterruptors.length > 0) { console.error(err); } } - note.value = result; + note.value = result as Misskey.entities.Note; }); } @@ -262,11 +272,11 @@ const isRenote = ( note.value.renote != null && note.value.text == null && note.value.cw == null && - note.value.fileIds.length === 0 && + note.value.fileIds && note.value.fileIds.length === 0 && note.value.poll == null ); -const el = shallowRef<HTMLElement>(); +const rootEl = shallowRef<HTMLElement>(); const menuButton = shallowRef<HTMLElement>(); const menuVersionsButton = shallowRef<HTMLElement>(); const renoteButton = shallowRef<HTMLElement>(); @@ -276,50 +286,61 @@ const quoteButton = shallowRef<HTMLElement>(); const clipButton = shallowRef<HTMLElement>(); const likeButton = shallowRef<HTMLElement>(); const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value); -const renoteUrl = appearNote.value.renote ? appearNote.value.renote.url : null; -const renoteUri = appearNote.value.renote ? appearNote.value.renote.uri : null; const isMyRenote = $i && ($i.id === note.value.userId); const showContent = ref(defaultStore.state.uncollapseCW); -const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text).filter(u => u !== renoteUrl && u !== renoteUri) : null); -const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value) : null); +const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null); +const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null); const isLong = shouldCollapsed(appearNote.value, urls.value ?? []); -const collapsed = defaultStore.state.expandLongNote && appearNote.value.cw == null ? false : ref(appearNote.value.cw == null && isLong); +const collapsed = ref(defaultStore.state.expandLongNote && appearNote.value.cw == null && isLong ? false : appearNote.value.cw == null && isLong); const isDeleted = ref(false); const renoted = ref(false); const muted = ref(checkMute(appearNote.value, $i?.mutedWords)); -const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords)); +const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords, true)); const translation = ref<Misskey.entities.NotesTranslateResponse | null>(null); const translating = ref(false); const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance); -const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i.id)); -const renoteCollapsed = ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || (appearNote.value.myReaction != null))); +const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i?.id)); +const renoteCollapsed = ref( + defaultStore.state.collapseRenotes && isRenote && ( + ($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || // `||` must be `||`! See https://github.com/misskey-dev/misskey/issues/13131 + (appearNote.value.myReaction != null) + ) +); const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null); const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null); const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false); -function checkMute(note: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null): boolean { +/* Overload Functionã«LintãŒå¯¾å¿œã—ã¦ã„ãªã„ã®ã§ã‚³ãƒ¡ãƒ³ãƒˆã‚¢ã‚¦ãƒˆ +function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: true): boolean; +function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: false): boolean | 'sensitiveMute'; +*/ +function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly = false): boolean | 'sensitiveMute' { if (mutedWords == null) return false; - if (checkWordMute(note, $i, mutedWords)) return true; - if (note.reply && checkWordMute(note.reply, $i, mutedWords)) return true; - if (note.renote && checkWordMute(note.renote, $i, mutedWords)) return true; + if (checkWordMute(noteToCheck, $i, mutedWords)) return true; + if (noteToCheck.reply && checkWordMute(noteToCheck.reply, $i, mutedWords)) return true; + if (noteToCheck.renote && checkWordMute(noteToCheck.renote, $i, mutedWords)) return true; + + if (checkOnly) return false; + + if (inTimeline && !defaultStore.state.tl.filter.withSensitive && noteToCheck.files?.some((v) => v.isSensitive)) return 'sensitiveMute'; return false; } const keymap = { 'r': () => reply(true), 'e|a|plus': () => react(true), - 'q': () => renoteButton.value.renote(true), + 'q': () => renote(appearNote.value.visibility), 'up|k|shift+tab': focusBefore, 'down|j|tab': focusAfter, 'esc': blur, - 'm|o': () => menu(true), + 'm|o': () => showMenu(true), 's': () => showContent.value !== showContent.value, }; provide('react', (reaction: string) => { - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, reaction: reaction, }); @@ -331,7 +352,7 @@ if (props.mock) { }, { deep: true }); } else { useNoteCapture({ - rootEl: el, + rootEl: rootEl, note: appearNote, pureNote: note, isDeletedRef: isDeleted, @@ -340,7 +361,7 @@ if (props.mock) { if (!props.mock) { useTooltip(renoteButton, async (showing) => { - const renotes = await os.api('notes/renotes', { + const renotes = await misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 11, }); @@ -358,7 +379,7 @@ if (!props.mock) { }); useTooltip(quoteButton, async (showing) => { - const renotes = await os.api('notes/renotes', { + const renotes = await misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 11, quote: true, @@ -377,7 +398,7 @@ if (!props.mock) { }); if ($i) { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -387,54 +408,15 @@ if (!props.mock) { } } -type Visibility = 'public' | 'home' | 'followers' | 'specified'; - -// defaultStore.state.visibilityãŒstringãªãŸã‚stringã‚‚å—ã‘付ã‘ã¦ã„ã‚‹ -function smallerVisibility(a: Visibility | string, b: Visibility | string): Visibility { - if (a === 'specified' || b === 'specified') return 'specified'; - if (a === 'followers' || b === 'followers') return 'followers'; - if (a === 'home' || b === 'home') return 'home'; - // if (a === 'public' || b === 'public') - return 'public'; -} - function boostVisibility() { - os.popupMenu([ - { - type: 'button', - icon: 'ph-globe-hemisphere-west ph-bold ph-lg', - text: i18n.ts._visibility['public'], - action: () => { - renote('public'); - }, - }, - { - type: 'button', - icon: 'ph-house ph-bold ph-lg', - text: i18n.ts._visibility['home'], - action: () => { - renote('home'); - }, - }, - { - type: 'button', - icon: 'ph-lock ph-bold ph-lg', - text: i18n.ts._visibility['followers'], - action: () => { - renote('followers'); - }, - }, - { - type: 'button', - icon: 'ph-planet ph-bold ph-lg', - text: i18n.ts._timelines.local, - action: () => { - renote('local'); - }, - }], renoteButton.value); + if (!defaultStore.state.showVisibilitySelectorOnBoost) { + renote(defaultStore.state.visibilityOnBoost); + } else { + os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value); + } } -function renote(visibility: Visibility | 'local') { +function renote(visibility: Visibility, localOnly: boolean = false) { pleaseLogin(); showMovedDialog(); @@ -448,7 +430,7 @@ function renote(visibility: Visibility | 'local') { } if (!props.mock) { - os.api('notes/create', { + misskeyApi('notes/create', { renoteId: appearNote.value.id, channelId: appearNote.value.channelId, }).then(() => { @@ -456,7 +438,7 @@ function renote(visibility: Visibility | 'local') { renoted.value = true; }); } - } else if (!appearNote.value.channel || appearNote.value.channel?.allowRenoteToExternal) { + } else if (!appearNote.value.channel || appearNote.value.channel.allowRenoteToExternal) { const el = renoteButton.value as HTMLElement | null | undefined; if (el) { const rect = el.getBoundingClientRect(); @@ -465,18 +447,10 @@ function renote(visibility: Visibility | 'local') { os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility; - const localOnlySetting = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly; - - let noteVisibility = visibility === 'local' || visibility === 'specified' ? smallerVisibility(appearNote.value.visibility, configuredVisibility) : smallerVisibility(visibility, configuredVisibility); - if (appearNote.value.channel?.isSensitive) { - noteVisibility = smallerVisibility(visibility === 'local' || visibility === 'specified' ? appearNote.value.visibility : visibility, 'home'); - } - if (!props.mock) { - os.api('notes/create', { - localOnly: visibility === 'local' ? true : localOnlySetting, - visibility: noteVisibility, + misskeyApi('notes/create', { + localOnly: localOnly, + visibility: visibility, renoteId: appearNote.value.id, }).then(() => { os.toast(i18n.ts.renoted); @@ -498,9 +472,9 @@ function quote() { renote: appearNote.value, channel: appearNote.value.channel, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, - userId: $i.id, + userId: $i?.id, limit: 1, quote: true, }).then((res) => { @@ -520,9 +494,9 @@ function quote() { os.post({ renote: appearNote.value, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, - userId: $i.id, + userId: $i?.id, limit: 1, quote: true, }).then((res) => { @@ -550,7 +524,7 @@ function reply(viaKeyboard = false): void { reply: appearNote.value, channel: appearNote.value.channel, animation: !viaKeyboard, - }, () => { + }).then(() => { focus(); }); } @@ -558,10 +532,11 @@ function reply(viaKeyboard = false): void { function like(): void { pleaseLogin(); showMovedDialog(); + sound.playMisskeySfx('reaction'); if (props.mock) { return; } - os.api('notes/like', { + misskeyApi('notes/like', { noteId: appearNote.value.id, override: defaultLike.value, }); @@ -578,17 +553,17 @@ function react(viaKeyboard = false): void { pleaseLogin(); showMovedDialog(); if (appearNote.value.reactionAcceptance === 'likeOnly') { - sound.play('reaction'); + sound.playMisskeySfx('reaction'); if (props.mock) { return; } - os.api('notes/like', { + misskeyApi('notes/like', { noteId: appearNote.value.id, override: defaultLike.value, }); - const el = reactButton.value as HTMLElement | null | undefined; + const el = reactButton.value; if (el) { const rect = el.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); @@ -597,15 +572,15 @@ function react(viaKeyboard = false): void { } } else { blur(); - reactionPicker.show(reactButton.value, reaction => { - sound.play('reaction'); + reactionPicker.show(reactButton.value ?? null, note.value, reaction => { + sound.playMisskeySfx('reaction'); if (props.mock) { emit('reaction', reaction); return; } - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, reaction: reaction, }); @@ -618,8 +593,8 @@ function react(viaKeyboard = false): void { } } -function undoReact(note): void { - const oldReaction = note.myReaction; +function undoReact(targetNote: Misskey.entities.Note): void { + const oldReaction = targetNote.myReaction; if (!oldReaction) return; if (props.mock) { @@ -627,8 +602,8 @@ function undoReact(note): void { return; } - os.api('notes/reactions/delete', { - noteId: note.id, + misskeyApi('notes/reactions/delete', { + noteId: targetNote.id, }); } @@ -636,7 +611,7 @@ function undoRenote(note) : void { if (props.mock) { return; } - os.api('notes/unrenote', { + misskeyApi('notes/unrenote', { noteId: note.id, }); os.toast(i18n.ts.rmboost); @@ -656,32 +631,34 @@ function onContextmenu(ev: MouseEvent): void { return; } - const isLink = (el: HTMLElement) => { + const isLink = (el: HTMLElement): boolean => { if (el.tagName === 'A') return true; // å†ç”Ÿé€Ÿåº¦ã®é¸æŠžãªã©ã®ãŸã‚ã«ã€Audioè¦ç´ ã®ã‚³ãƒ³ãƒ†ã‚ストメニューã¯ãƒ–ラウザデフォルトã¨ã™ã‚‹ã€‚ if (el.tagName === 'AUDIO') return true; if (el.parentElement) { return isLink(el.parentElement); } + return false; }; - if (isLink(ev.target)) return; - if (window.getSelection().toString() !== '') return; + + if (ev.target && isLink(ev.target as HTMLElement)) return; + if (window.getSelection()?.toString() !== '') return; if (defaultStore.state.useReactionPickerForContextMenu) { ev.preventDefault(); react(); } else { - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value }); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); os.contextMenu(menu, ev).then(focus).finally(cleanup); } } -function menu(viaKeyboard = false): void { +function showMenu(viaKeyboard = false): void { if (props.mock) { return; } - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value }); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); os.popupMenu(menu, menuButton.value, { viaKeyboard, }).then(focus).finally(cleanup); @@ -713,7 +690,7 @@ function showRenoteMenu(viaKeyboard = false): void { icon: 'ph-trash ph-bold ph-lg', danger: true, action: () => { - os.api('notes/delete', { + misskeyApi('notes/delete', { noteId: note.value.id, }); isDeleted.value = true; @@ -735,7 +712,7 @@ function showRenoteMenu(viaKeyboard = false): void { getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote), { type: 'divider' }, getAbuseNoteMenu(note.value, i18n.ts.reportAbuseRenote), - $i.isModerator || $i.isAdmin ? getUnrenote() : undefined, + ($i?.isModerator || $i?.isAdmin) ? getUnrenote() : undefined, ], renoteTime.value, { viaKeyboard: viaKeyboard, }); @@ -755,23 +732,23 @@ function animatedMFM() { } function focus() { - el.value.focus(); + rootEl.value?.focus(); } function blur() { - el.value.blur(); + rootEl.value?.blur(); } function focusBefore() { - focusPrev(el.value); + focusPrev(rootEl.value ?? null); } function focusAfter() { - focusNext(el.value); + focusNext(rootEl.value ?? null); } function readPromo() { - os.api('promo/read', { + misskeyApi('promo/read', { noteId: appearNote.value.id, }); isDeleted.value = true; @@ -825,12 +802,13 @@ function emitUpdReaction(emoji: string, delta: number) { } .footer { + display: flex; + align-items: center; + justify-content: space-between; position: relative; z-index: 1; margin-top: 0.4em; - width: max-content; - min-width: min-content; - max-width: fit-content; + max-width: 400px; } &:hover > .article > .main > .footer > .footerButton { @@ -986,8 +964,8 @@ function emitUpdReaction(emoji: string, delta: number) { flex-shrink: 0; display: block !important; margin: 0 14px 0 0; - width: 58px; - height: 58px; + width: var(--avatar); + height: var(--avatar); position: sticky !important; top: calc(22px + var(--stickyTop, 0px)); left: 0; @@ -1249,5 +1227,6 @@ function emitUpdReaction(emoji: string, delta: number) { .clickToOpen { cursor: pointer; + -webkit-tap-highlight-color: transparent; } </style> diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index e287890e2c0b97caf4f425f6d403b0acc33f60ea..3d15f69f7311031cee4cee85f14939dd0b3a91cc 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="!muted" v-show="!isDeleted" - ref="el" + ref="rootEl" v-hotkey="keymap" :class="$style.root" > @@ -58,7 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only <i v-else-if="appearNote.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i> <i v-else-if="appearNote.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i> </span> - <span v-if="appearNote.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span> + <span v-if="appearNote.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil-simple ph-bold ph-lg"></i></span> <span v-if="appearNote.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span> </div> </div> @@ -88,17 +88,17 @@ SPDX-License-Identifier: AGPL-3.0-only <a v-if="appearNote.renote != null" :class="$style.rn">RN:</a> <div v-if="translating || translation" :class="$style.translation"> <MkLoading v-if="translating" mini/> - <div v-else> - <b>{{ i18n.t('translatedFrom', { x: translation.sourceLang }) }}: </b> + <div v-else-if="translation"> + <b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b> <Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> </div> </div> <MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton> <MkButton v-else-if="!defaultStore.state.animatedMfm && allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton> - <div v-if="appearNote.files.length > 0"> + <div v-if="appearNote.files && appearNote.files.length > 0"> <MkMediaList :mediaList="appearNote.files"/> </div> - <MkPoll v-if="appearNote.poll" ref="pollViewer" :note="appearNote" :class="$style.poll"/> + <MkPoll v-if="appearNote.poll" ref="pollViewer" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll"/> <MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="true" style="margin-top: 6px;"/> <div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote" :expandAllCws="props.expandAllCws"/></div> </div> @@ -154,7 +154,7 @@ SPDX-License-Identifier: AGPL-3.0-only <button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown="clip()"> <i class="ph-paperclip ph-bold ph-lg"></i> </button> - <button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown="menu()"> + <button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown="showMenu()"> <i class="ph-dots-three ph-bold ph-lg"></i> </button> </footer> @@ -166,11 +166,11 @@ SPDX-License-Identifier: AGPL-3.0-only <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'reactions' }]" @click="tab = 'reactions'"><i class="ph-smiley ph-bold ph-lg"></i> {{ i18n.ts.reactions }}</button> </div> <div> - <div v-if="tab === 'replies'" :class="$style.tab_replies"> + <div v-if="tab === 'replies'"> <div v-if="!repliesLoaded" style="padding: 16px"> <MkButton style="margin: 0 auto;" primary rounded @click="loadReplies">{{ i18n.ts.loadReplies }}</MkButton> </div> - <MkNoteSub v-for="note in replies" :key="note.id" :note="note" :class="$style.reply" :detail="true" :expandAllCws="props.expandAllCws" :onDeleteCallback="removeReply" /> + <MkNoteSub v-for="note in replies" :key="note.id" :note="note" :class="$style.reply" :detail="true" :expandAllCws="props.expandAllCws" :onDeleteCallback="removeReply"/> </div> <div v-else-if="tab === 'renotes'" :class="$style.tab_renotes"> <MkPagination :pagination="renotesPagination" :disableAutoLoad="true"> @@ -183,7 +183,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </MkPagination> </div> - <div v-if="tab === 'quotes'" :class="$style.tab_replies"> + <div v-if="tab === 'quotes'"> <div v-if="!quotesLoaded" style="padding: 16px"> <MkButton style="margin: 0 auto;" primary rounded @click="loadQuotes">{{ i18n.ts.loadReplies }}</MkButton> </div> @@ -221,7 +221,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { computed, inject, onMounted, provide, ref, shallowRef, watch } from 'vue'; -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; import * as Misskey from 'misskey-js'; import MkNoteSub from '@/components/MkNoteSub.vue'; import MkNoteSimple from '@/components/MkNoteSimple.vue'; @@ -237,6 +237,7 @@ import { checkWordMute } from '@/scripts/check-word-mute.js'; import { userPage } from '@/filters/user.js'; import { notePage } from '@/filters/note.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import * as sound from '@/scripts/sound.js'; import { defaultStore, noteViewInterruptors } from '@/store.js'; import { reactionPicker } from '@/scripts/reaction-picker.js'; @@ -253,9 +254,10 @@ import { checkAnimationFromMfm } from '@/scripts/check-animated-mfm.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; -import MkPagination from '@/components/MkPagination.vue'; +import MkPagination, { type Paging } from '@/components/MkPagination.vue'; import MkReactionIcon from '@/components/MkReactionIcon.vue'; import MkButton from '@/components/MkButton.vue'; +import { boostMenuItems, type Visibility } from '@/scripts/boost-quote.js'; const props = defineProps<{ note: Misskey.entities.Note; @@ -272,7 +274,7 @@ if (noteViewInterruptors.length > 0) { let result: Misskey.entities.Note | null = deepClone(note.value); for (const interruptor of noteViewInterruptors) { try { - result = await interruptor.handler(result); + result = await interruptor.handler(result!) as Misskey.entities.Note | null; if (result === null) { isDeleted.value = true; return; @@ -281,18 +283,18 @@ if (noteViewInterruptors.length > 0) { console.error(err); } } - note.value = result; + note.value = result as Misskey.entities.Note; }); } const isRenote = ( note.value.renote != null && note.value.text == null && - note.value.fileIds.length === 0 && + note.value.fileIds && note.value.fileIds.length === 0 && note.value.poll == null ); -const el = shallowRef<HTMLElement>(); +const rootEl = shallowRef<HTMLElement>(); const menuButton = shallowRef<HTMLElement>(); const menuVersionsButton = shallowRef<HTMLElement>(); const renoteButton = shallowRef<HTMLElement>(); @@ -302,8 +304,6 @@ const quoteButton = shallowRef<HTMLElement>(); const clipButton = shallowRef<HTMLElement>(); const likeButton = shallowRef<HTMLElement>(); const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value); -const renoteUrl = appearNote.value.renote ? appearNote.value.renote.url : null; -const renoteUri = appearNote.value.renote ? appearNote.value.renote.uri : null; const isMyRenote = $i && ($i.id === note.value.userId); const showContent = ref(defaultStore.state.uncollapseCW); const isDeleted = ref(false); @@ -312,14 +312,14 @@ const muted = ref($i ? checkWordMute(appearNote.value, $i, $i.mutedWords) : fals const translation = ref<Misskey.entities.NotesTranslateResponse | null>(null); const translating = ref(false); const parsed = appearNote.value.text ? mfm.parse(appearNote.value.text) : null; -const urls = parsed ? extractUrlFromMfm(parsed).filter(u => u !== renoteUrl && u !== renoteUri) : null; +const urls = parsed ? extractUrlFromMfm(parsed).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null; const animated = computed(() => parsed ? checkAnimationFromMfm(parsed) : null); const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false); const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance); const conversation = ref<Misskey.entities.Note[]>([]); const replies = ref<Misskey.entities.Note[]>([]); const quotes = ref<Misskey.entities.Note[]>([]); -const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || appearNote.value.userId === $i.id); +const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i?.id)); const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null); watch(() => props.expandAllCws, (expandAllCws) => { @@ -327,7 +327,7 @@ watch(() => props.expandAllCws, (expandAllCws) => { }); if ($i) { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -339,14 +339,14 @@ if ($i) { const keymap = { 'r': () => reply(true), 'e|a|plus': () => react(true), - 'q': () => renoteButton.value.renote(true), + 'q': () => renote(appearNote.value.visibility), 'esc': blur, - 'm|o': () => menu(true), + 'm|o': () => showMenu(true), 's': () => showContent.value !== showContent.value, }; provide('react', (reaction: string) => { - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, reaction: reaction, }); @@ -355,7 +355,7 @@ provide('react', (reaction: string) => { const tab = ref('replies'); const reactionTabType = ref<string | null>(null); -const renotesPagination = computed(() => ({ +const renotesPagination = computed<Paging>(() => ({ endpoint: 'notes/renotes', limit: 10, params: { @@ -363,7 +363,7 @@ const renotesPagination = computed(() => ({ }, })); -const reactionsPagination = computed(() => ({ +const reactionsPagination = computed<Paging>(() => ({ endpoint: 'notes/reactions', limit: 10, params: { @@ -373,20 +373,20 @@ const reactionsPagination = computed(() => ({ })); async function addReplyTo(replyNote: Misskey.entities.Note) { - replies.value.unshift(replyNote); - appearNote.value.repliesCount += 1; + replies.value.unshift(replyNote); + appearNote.value.repliesCount += 1; } async function removeReply(id: Misskey.entities.Note['id']) { - const replyIdx = replies.value.findIndex(note => note.id === id); - if (replyIdx >= 0) { - replies.value.splice(replyIdx, 1); - appearNote.value.repliesCount -= 1; - } + const replyIdx = replies.value.findIndex(note => note.id === id); + if (replyIdx >= 0) { + replies.value.splice(replyIdx, 1); + appearNote.value.repliesCount -= 1; + } } useNoteCapture({ - rootEl: el, + rootEl: rootEl, note: appearNote, pureNote: note, isDeletedRef: isDeleted, @@ -394,7 +394,7 @@ useNoteCapture({ }); useTooltip(renoteButton, async (showing) => { - const renotes = await os.api('notes/renotes', { + const renotes = await misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 11, }); @@ -412,7 +412,7 @@ useTooltip(renoteButton, async (showing) => { }); useTooltip(quoteButton, async (showing) => { - const renotes = await os.api('notes/renotes', { + const renotes = await misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 11, quote: true, @@ -430,53 +430,15 @@ useTooltip(quoteButton, async (showing) => { }, {}, 'closed'); }); -type Visibility = 'public' | 'home' | 'followers' | 'specified'; - -function smallerVisibility(a: Visibility | string, b: Visibility | string): Visibility { - if (a === 'specified' || b === 'specified') return 'specified'; - if (a === 'followers' || b === 'followers') return 'followers'; - if (a === 'home' || b === 'home') return 'home'; - // if (a === 'public' || b === 'public') - return 'public'; -} - function boostVisibility() { - os.popupMenu([ - { - type: 'button', - icon: 'ph-globe-hemisphere-west ph-bold ph-lg', - text: i18n.ts._visibility['public'], - action: () => { - renote('public'); - }, - }, - { - type: 'button', - icon: 'ph-house ph-bold ph-lg', - text: i18n.ts._visibility['home'], - action: () => { - renote('home'); - }, - }, - { - type: 'button', - icon: 'ph-lock ph-bold ph-lg', - text: i18n.ts._visibility['followers'], - action: () => { - renote('followers'); - }, - }, - { - type: 'button', - icon: 'ph-planet ph-bold ph-lg', - text: i18n.ts._timelines.local, - action: () => { - renote('local'); - }, - }], renoteButton.value); + if (!defaultStore.state.showVisibilitySelectorOnBoost) { + renote(defaultStore.state.visibilityOnBoost); + } else { + os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value); + } } -function renote(visibility: Visibility | 'local') { +function renote(visibility: Visibility, localOnly: boolean = false) { pleaseLogin(); showMovedDialog(); @@ -489,14 +451,14 @@ function renote(visibility: Visibility | 'local') { os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - os.api('notes/create', { + misskeyApi('notes/create', { renoteId: appearNote.value.id, channelId: appearNote.value.channelId, }).then(() => { os.toast(i18n.ts.renoted); renoted.value = true; }); - } else if (!appearNote.value.channel || appearNote.value.channel?.allowRenoteToExternal) { + } else if (!appearNote.value.channel || appearNote.value.channel.allowRenoteToExternal) { const el = renoteButton.value as HTMLElement | null | undefined; if (el) { const rect = el.getBoundingClientRect(); @@ -505,17 +467,9 @@ function renote(visibility: Visibility | 'local') { os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility; - const localOnlySetting = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly; - - let noteVisibility = visibility === 'local' || visibility === 'specified' ? smallerVisibility(appearNote.value.visibility, configuredVisibility) : smallerVisibility(visibility, configuredVisibility); - if (appearNote.value.channel?.isSensitive) { - noteVisibility = smallerVisibility(visibility === 'local' || visibility === 'specified' ? appearNote.value.visibility : visibility, 'home'); - } - - os.api('notes/create', { - localOnly: visibility === 'local' ? true : localOnlySetting, - visibility: noteVisibility, + misskeyApi('notes/create', { + localOnly: localOnly, + visibility: visibility, renoteId: appearNote.value.id, }).then(() => { os.toast(i18n.ts.renoted); @@ -533,9 +487,9 @@ function quote() { renote: appearNote.value, channel: appearNote.value.channel, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, - userId: $i.id, + userId: $i?.id, limit: 1, quote: true, }).then((res) => { @@ -555,9 +509,9 @@ function quote() { os.post({ renote: appearNote.value, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, - userId: $i.id, + userId: $i?.id, limit: 1, quote: true, }).then((res) => { @@ -583,7 +537,7 @@ function reply(viaKeyboard = false): void { reply: appearNote.value, channel: appearNote.value.channel, animation: !viaKeyboard, - }, () => { + }).then(() => { focus(); }); } @@ -592,7 +546,9 @@ function react(viaKeyboard = false): void { pleaseLogin(); showMovedDialog(); if (appearNote.value.reactionAcceptance === 'likeOnly') { - os.api('notes/like', { + sound.playMisskeySfx('reaction'); + + misskeyApi('notes/like', { noteId: appearNote.value.id, override: defaultLike.value, }); @@ -605,10 +561,10 @@ function react(viaKeyboard = false): void { } } else { blur(); - reactionPicker.show(reactButton.value, reaction => { - sound.play('reaction'); + reactionPicker.show(reactButton.value ?? null, note.value, reaction => { + sound.playMisskeySfx('reaction'); - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, reaction: reaction, }); @@ -624,7 +580,8 @@ function react(viaKeyboard = false): void { function like(): void { pleaseLogin(); showMovedDialog(); - os.api('notes/like', { + sound.playMisskeySfx('reaction'); + misskeyApi('notes/like', { noteId: appearNote.value.id, override: defaultLike.value, }); @@ -640,14 +597,14 @@ function like(): void { function undoReact(note): void { const oldReaction = note.myReaction; if (!oldReaction) return; - os.api('notes/reactions/delete', { + misskeyApi('notes/reactions/delete', { noteId: note.id, }); } function undoRenote() : void { if (!renoted.value) return; - os.api('notes/unrenote', { + misskeyApi('notes/unrenote', { noteId: appearNote.value.id, }); os.toast(i18n.ts.rmboost); @@ -663,26 +620,28 @@ function undoRenote() : void { } function onContextmenu(ev: MouseEvent): void { - const isLink = (el: HTMLElement) => { + const isLink = (el: HTMLElement): boolean => { if (el.tagName === 'A') return true; if (el.parentElement) { return isLink(el.parentElement); } + return false; }; - if (isLink(ev.target)) return; - if (window.getSelection().toString() !== '') return; + + if (ev.target && isLink(ev.target as HTMLElement)) return; + if (window.getSelection()?.toString() !== '') return; if (defaultStore.state.useReactionPickerForContextMenu) { ev.preventDefault(); react(); } else { - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted }); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted }); os.contextMenu(menu, ev).then(focus).finally(cleanup); } } -function menu(viaKeyboard = false): void { - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted }); +function showMenu(viaKeyboard = false): void { + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted }); os.popupMenu(menu, menuButton.value, { viaKeyboard, }).then(focus).finally(cleanup); @@ -707,7 +666,7 @@ function showRenoteMenu(viaKeyboard = false): void { icon: 'ph-trash ph-bold ph-lg', danger: true, action: () => { - os.api('notes/delete', { + misskeyApi('notes/delete', { noteId: note.value.id, }); isDeleted.value = true; @@ -718,18 +677,18 @@ function showRenoteMenu(viaKeyboard = false): void { } function focus() { - el.value.focus(); + rootEl.value?.focus(); } function blur() { - el.value.blur(); + rootEl.value?.blur(); } const repliesLoaded = ref(false); function loadReplies() { repliesLoaded.value = true; - os.api('notes/children', { + misskeyApi('notes/children', { noteId: appearNote.value.id, limit: 30, showQuotes: false, @@ -744,7 +703,7 @@ const quotesLoaded = ref(false); function loadQuotes() { quotesLoaded.value = true; - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 30, quote: true, @@ -759,7 +718,8 @@ const conversationLoaded = ref(false); function loadConversation() { conversationLoaded.value = true; - os.api('notes/conversation', { + if (appearNote.value.replyId == null) return; + misskeyApi('notes/conversation', { noteId: appearNote.value.replyId, }).then(res => { conversation.value = res.reverse(); @@ -871,8 +831,8 @@ function animatedMFM() { .noteHeaderAvatar { display: block; flex-shrink: 0; - width: 58px; - height: 58px; + width: var(--avatar); + height: var(--avatar); } .noteHeaderBody { diff --git a/packages/frontend/src/components/MkNoteHeader.vue b/packages/frontend/src/components/MkNoteHeader.vue index 6121db3f8f3577d13b007a4cca41c9babd5a17ca..e643590e86a8a11804477764981352e43e2aa547 100644 --- a/packages/frontend/src/components/MkNoteHeader.vue +++ b/packages/frontend/src/components/MkNoteHeader.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="note.user.isBot" :class="$style.isBot">bot</div> <div :class="$style.username"><MkAcct :user="note.user"/></div> <div v-if="note.user.badgeRoles" :class="$style.badgeRoles"> - <img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/> + <img v-for="(role, i) in note.user.badgeRoles" :key="i" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl!"/> </div> <div :class="$style.info"> <div v-if="mock"> @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only <i v-else-if="note.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i> <i v-else-if="note.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i> </span> - <span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em; cursor: pointer;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span> + <span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em; cursor: pointer;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil-simple ph-bold ph-lg"></i></span> <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span> <span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ph-television ph-bold ph-lg"></i></span> </div> diff --git a/packages/frontend/src/components/MkNotePreview.vue b/packages/frontend/src/components/MkNotePreview.vue index c517bc6800636f1788c0d81d3df94cb5c1a5b73d..3fcd7593ba71b7a39e0ab3bf54950465fb89e8a4 100644 --- a/packages/frontend/src/components/MkNotePreview.vue +++ b/packages/frontend/src/components/MkNotePreview.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div> <p v-if="useCw" :class="$style.cw"> - <Mfm v-if="cw != ''" :text="cw" :author="user" :nyaize="'respect'" :i="user" style="margin-right: 8px;"/> + <Mfm v-if="cw != null && cw != ''" :text="cw" :author="user" :nyaize="'respect'" :i="user" style="margin-right: 8px;"/> <MkCwButton v-model="showContent" :text="text.trim()" :files="files" :poll="poll" style="margin: 4px 0;"/> </p> <div v-show="!useCw || showContent"> @@ -26,6 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; import * as Misskey from 'misskey-js'; +import type { PollEditorModelValue } from '@/components/MkPollEditor.vue'; import MkCwButton from '@/components/MkCwButton.vue'; const showContent = ref(false); @@ -33,12 +34,7 @@ const showContent = ref(false); const props = defineProps<{ text: string; files: Misskey.entities.DriveFile[]; - poll?: { - choices: string[]; - multiple: boolean; - expiresAt: string | null; - expiredAfter: string | null; - }; + poll?: PollEditorModelValue; useCw: boolean; cw: string | null; user: Misskey.entities.User; diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index 7a6109ee0bfa212e1a1f7c5c2249ef35ff0e870f..477cf4521a10e41539ae622241718ba06e118bc8 100644 --- a/packages/frontend/src/components/MkNoteSimple.vue +++ b/packages/frontend/src/components/MkNoteSimple.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkCwButton v-model="showContent" :text="note.text" :files="note.files" :poll="note.poll" @click.stop/> </p> <div v-show="note.cw == null || showContent"> - <MkSubNoteContent :hideFiles="hideFiles" :class="$style.text" :note="note"/> + <MkSubNoteContent :hideFiles="hideFiles" :class="$style.text" :note="note" :expandAllCws="props.expandAllCws"/> </div> </div> </div> diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue index d96785a2d93ec8604b034d118024e39cb9a5a15a..37811dd52e8d994d48a0908d0aa6c0d61c31ef35 100644 --- a/packages/frontend/src/components/MkNoteSub.vue +++ b/packages/frontend/src/components/MkNoteSub.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkCwButton v-model="showContent" :text="note.text" :files="note.files" :poll="note.poll"/> </p> <div v-show="note.cw == null || showContent"> - <MkSubNoteContent :class="$style.text" :note="note" :translating="translating" :translation="translation"/> + <MkSubNoteContent :class="$style.text" :note="note" :translating="translating" :translation="translation" :expandAllCws="props.expandAllCws"/> </div> </div> <footer :class="$style.footer"> @@ -91,6 +91,8 @@ import MkSubNoteContent from '@/components/MkSubNoteContent.vue'; import MkCwButton from '@/components/MkCwButton.vue'; import { notePage } from '@/filters/note.js'; import * as os from '@/os.js'; +import * as sound from '@/scripts/sound.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; import { userPage } from '@/filters/user.js'; @@ -103,6 +105,7 @@ import { reactionPicker } from '@/scripts/reaction-picker.js'; import { claimAchievement } from '@/scripts/achievements.js'; import { getNoteMenu } from '@/scripts/get-note-menu.js'; import { useNoteCapture } from '@/scripts/use-note-capture.js'; +import { boostMenuItems, type Visibility } from '@/scripts/boost-quote.js'; const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id); @@ -138,21 +141,21 @@ const replies = ref<Misskey.entities.Note[]>([]); const isRenote = ( props.note.renote != null && props.note.text == null && - props.note.fileIds.length === 0 && + props.note.fileIds && props.note.fileIds.length === 0 && props.note.poll == null ); async function addReplyTo(replyNote: Misskey.entities.Note) { - replies.value.unshift(replyNote); - appearNote.value.repliesCount += 1; + replies.value.unshift(replyNote); + appearNote.value.repliesCount += 1; } async function removeReply(id: Misskey.entities.Note['id']) { - const replyIdx = replies.value.findIndex(note => note.id === id); - if (replyIdx >= 0) { - replies.value.splice(replyIdx, 1); - appearNote.value.repliesCount -= 1; - } + const replyIdx = replies.value.findIndex(note => note.id === id); + if (replyIdx >= 0) { + replies.value.splice(replyIdx, 1); + appearNote.value.repliesCount -= 1; + } } useNoteCapture({ @@ -165,7 +168,7 @@ useNoteCapture({ }); if ($i) { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -193,8 +196,9 @@ function reply(viaKeyboard = false): void { function react(viaKeyboard = false): void { pleaseLogin(); showMovedDialog(); + sound.playMisskeySfx('reaction'); if (props.note.reactionAcceptance === 'likeOnly') { - os.api('notes/like', { + misskeyApi('notes/like', { noteId: props.note.id, override: defaultLike.value, }); @@ -207,8 +211,8 @@ function react(viaKeyboard = false): void { } } else { blur(); - reactionPicker.show(reactButton.value, reaction => { - os.api('notes/reactions/create', { + reactionPicker.show(reactButton.value ?? null, props.note, reaction => { + misskeyApi('notes/reactions/create', { noteId: props.note.id, reaction: reaction, }); @@ -224,7 +228,8 @@ function react(viaKeyboard = false): void { function like(): void { pleaseLogin(); showMovedDialog(); - os.api('notes/like', { + sound.playMisskeySfx('reaction'); + misskeyApi('notes/like', { noteId: props.note.id, override: defaultLike.value, }); @@ -240,14 +245,14 @@ function like(): void { function undoReact(note): void { const oldReaction = note.myReaction; if (!oldReaction) return; - os.api('notes/reactions/delete', { + misskeyApi('notes/reactions/delete', { noteId: note.id, }); } function undoRenote() : void { if (!renoted.value) return; - os.api('notes/unrenote', { + misskeyApi('notes/unrenote', { noteId: appearNote.value.id, }); os.toast(i18n.ts.rmboost); @@ -269,42 +274,14 @@ watch(() => props.expandAllCws, (expandAllCws) => { }); function boostVisibility() { - os.popupMenu([ - { - type: 'button', - icon: 'ph-globe-hemisphere-west ph-bold ph-lg', - text: i18n.ts._visibility['public'], - action: () => { - renote('public'); - }, - }, - { - type: 'button', - icon: 'ph-house ph-bold ph-lg', - text: i18n.ts._visibility['home'], - action: () => { - renote('home'); - }, - }, - { - type: 'button', - icon: 'ph-lock ph-bold ph-lg', - text: i18n.ts._visibility['followers'], - action: () => { - renote('followers'); - }, - }, - { - type: 'button', - icon: 'ph-planet ph-bold ph-lg', - text: i18n.ts._timelines.local, - action: () => { - renote('local'); - }, - }], renoteButton.value); + if (!defaultStore.state.showVisibilitySelectorOnBoost) { + renote(defaultStore.state.visibilityOnBoost); + } else { + os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value); + } } -function renote(visibility: 'public' | 'home' | 'followers' | 'specified' | 'local') { +function renote(visibility: Visibility, localOnly: boolean = false) { pleaseLogin(); showMovedDialog(); @@ -317,9 +294,9 @@ function renote(visibility: 'public' | 'home' | 'followers' | 'specified' | 'loc os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - os.api('notes/create', { - renoteId: props.note.id, - channelId: props.note.channelId, + misskeyApi('notes/create', { + renoteId: appearNote.value.id, + channelId: appearNote.value.channelId, }).then(() => { os.toast(i18n.ts.renoted); renoted.value = true; @@ -333,10 +310,10 @@ function renote(visibility: 'public' | 'home' | 'followers' | 'specified' | 'loc os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - os.api('notes/create', { - renoteId: props.note.id, - localOnly: visibility === 'local' ? true : false, - visibility: visibility === 'local' || visibility === 'specified' ? props.note.visibility : visibility, + misskeyApi('notes/create', { + renoteId: appearNote.value.id, + localOnly: localOnly, + visibility: visibility, }).then(() => { os.toast(i18n.ts.renoted); renoted.value = true; @@ -353,7 +330,7 @@ function quote() { renote: appearNote.value, channel: appearNote.value.channel, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: props.note.id, userId: $i.id, limit: 1, @@ -375,7 +352,7 @@ function quote() { os.post({ renote: appearNote.value, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: props.note.id, userId: $i.id, limit: 1, @@ -404,7 +381,7 @@ function menu(viaKeyboard = false): void { } if (props.detail) { - os.api('notes/children', { + misskeyApi('notes/children', { noteId: props.note.id, limit: numberOfReplies.value, showQuotes: false, diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotes.vue index fc1c8a0f09ebfa2289ca5ee7914e6173ae5ec857..afe43d965cae4b3581b55543f9e6bd4c57bf48ce 100644 --- a/packages/frontend/src/components/MkNotes.vue +++ b/packages/frontend/src/components/MkNotes.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only :ad="true" :class="$style.notes" > - <SkNote :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note"/> + <SkNote :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note" :withHardMute="true"/> </MkDateSeparatedList> </div> </template> diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index ed79ca0d8609a0820e97db5aa1f42d49b76f0058..562cc38bf35f2098fc93a9c5627d44dafb266f50 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -1,15 +1,13 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <div :class="$style.root"> <div :class="$style.head"> - <MkAvatar v-if="notification.type === 'pollEnded'" :class="$style.icon" :user="notification.note.user" link preview/> - <MkAvatar v-else-if="notification.type === 'note'" :class="$style.icon" :user="notification.note.user" link preview/> - <MkAvatar v-else-if="notification.type === 'roleAssigned'" :class="$style.icon" :user="$i" link preview/> - <MkAvatar v-else-if="notification.type === 'achievementEarned'" :class="$style.icon" :user="$i" link preview/> + <MkAvatar v-if="['pollEnded', 'note', 'edited'].includes(notification.type) && notification.note" :class="$style.icon" :user="notification.note.user" link preview/> + <MkAvatar v-else-if="['roleAssigned', 'achievementEarned'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/> <div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ph-smiley ph-bold ph-lg" style="line-height: 1;"></i></div> <div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ph-rocket-launch ph-bold ph-lg" style="line-height: 1;"></i></div> <img v-else-if="notification.type === 'test'" :class="$style.icon" :src="infoImageUrl"/> @@ -26,8 +24,10 @@ SPDX-License-Identifier: AGPL-3.0-only [$style.t_quote]: notification.type === 'quote', [$style.t_pollEnded]: notification.type === 'pollEnded', [$style.t_achievementEarned]: notification.type === 'achievementEarned', + [$style.t_roleAssigned]: notification.type === 'roleAssigned' && notification.role.iconUrl == null, + [$style.t_pollEnded]: notification.type === 'edited', }]" - > + > <!-- we re-use t_pollEnded for "edited" instead of making an identical style --> <i v-if="notification.type === 'follow'" class="ph-plus ph-bold ph-lg"></i> <i v-else-if="notification.type === 'receiveFollowRequest'" class="ph-clock ph-bold ph-lg"></i> <i v-else-if="notification.type === 'followRequestAccepted'" class="ph-check ph-bold ph-lg"></i> @@ -37,12 +37,16 @@ SPDX-License-Identifier: AGPL-3.0-only <i v-else-if="notification.type === 'quote'" class="ph-quotes ph-bold ph-lg"></i> <i v-else-if="notification.type === 'pollEnded'" class="ph-chart-bar-horizontal ph-bold ph-lg"></i> <i v-else-if="notification.type === 'achievementEarned'" class="ph-trophy ph-bold ph-lg"></i> - <img v-else-if="notification.type === 'roleAssigned'" style="height: 1.3em; vertical-align: -22%;" :src="notification.role.iconUrl" alt=""/> + <template v-else-if="notification.type === 'roleAssigned'"> + <img v-if="notification.role.iconUrl" style="height: 1.3em; vertical-align: -22%;" :src="notification.role.iconUrl" alt=""/> + <i v-else class="ph-seal-check ph-bold ph-lg"></i> + </template> + <i v-else-if="notification.type === 'edited'" class="ph-pencil ph-bold ph-lg"></i> <!-- notification.reaction ㌠null ã«ãªã‚‹ã“ã¨ã¯ã¾ãšãªã„ãŒã€ã“ã“ã§optional chaining使ã†ã¨ä¸€éƒ¨ãƒ–ラウザã§åˆºã•ã‚‹ã®ã§å¿µã®ç‚º --> <MkReactionIcon v-else-if="notification.type === 'reaction'" :withTooltip="true" - :reaction="notification.reaction ? notification.reaction.replace(/^:(\w+):$/, ':$1@.:') : notification.reaction" + :reaction="notification.reaction.replace(/^:(\w+):$/, ':$1@.:')" :noStyle="true" style="width: 100%; height: 100%;" /> @@ -55,10 +59,11 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-else-if="notification.type === 'roleAssigned'">{{ i18n.ts._notification.roleAssigned }}</span> <span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span> <span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</span> - <MkA v-else-if="notification.user" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA> - <span v-else-if="notification.type === 'reaction:grouped'">{{ i18n.t('_notification.reactedBySomeUsers', { n: notification.reactions.length }) }}</span> - <span v-else-if="notification.type === 'renote:grouped'">{{ i18n.t('_notification.renotedBySomeUsers', { n: notification.users.length }) }}</span> - <span v-else>{{ notification.header }}</span> + <MkA v-else-if="notification.type === 'follow' || notification.type === 'mention' || notification.type === 'reply' || notification.type === 'renote' || notification.type === 'quote' || notification.type === 'reaction' || notification.type === 'receiveFollowRequest' || notification.type === 'followRequestAccepted'" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA> + <span v-else-if="notification.type === 'reaction:grouped'">{{ i18n.tsx._notification.reactedBySomeUsers({ n: notification.reactions.length }) }}</span> + <span v-else-if="notification.type === 'renote:grouped'">{{ i18n.tsx._notification.renotedBySomeUsers({ n: notification.users.length }) }}</span> + <span v-else-if="notification.type === 'app'">{{ notification.header }}</span> + <span v-else-if="notification.type === 'edited'">{{ i18n.ts._notification.edited }}</span> <MkTime v-if="withTime" :time="notification.createdAt" :class="$style.headerTime"/> </header> <div> @@ -97,7 +102,6 @@ SPDX-License-Identifier: AGPL-3.0-only </MkA> <template v-else-if="notification.type === 'follow'"> <span :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.youGotNewFollower }}</span> - <div v-if="full"><MkFollowButton :user="notification.user" :full="true"/></div> </template> <span v-else-if="notification.type === 'followRequestAccepted'" :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.followRequestAccepted }}</span> <template v-else-if="notification.type === 'receiveFollowRequest'"> @@ -113,12 +117,12 @@ SPDX-License-Identifier: AGPL-3.0-only </span> <div v-if="notification.type === 'reaction:grouped'"> - <div v-for="reaction of notification.reactions" :class="$style.reactionsItem"> + <div v-for="reaction of notification.reactions" :key="reaction.user.id + reaction.reaction" :class="$style.reactionsItem"> <MkAvatar :class="$style.reactionsItemAvatar" :user="reaction.user" link preview/> <div :class="$style.reactionsItemReaction"> <MkReactionIcon :withTooltip="true" - :reaction="reaction.reaction ? reaction.reaction.replace(/^:(\w+):$/, ':$1@.:') : reaction.reaction" + :reaction="reaction.reaction.replace(/^:(\w+):$/, ':$1@.:')" :noStyle="true" style="width: 100%; height: 100%;" /> @@ -126,10 +130,16 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> <div v-else-if="notification.type === 'renote:grouped'"> - <div v-for="user of notification.users" :class="$style.reactionsItem"> + <div v-for="user of notification.users" :key="user.id" :class="$style.reactionsItem"> <MkAvatar :class="$style.reactionsItemAvatar" :user="user" link preview/> </div> </div> + + <MkA v-else-if="notification.type === 'edited'" :class="$style.text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> + <i class="ph-quotes ph-bold ph-lg" :class="$style.quote"></i> + <Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="true" :author="notification.note.user"/> + <i class="ph-quotes ph-bold ph-lg" :class="$style.quote"></i> + </MkA> </div> </div> </div> @@ -139,16 +149,17 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkReactionIcon from '@/components/MkReactionIcon.vue'; -import MkFollowButton from '@/components/MkFollowButton.vue'; import MkButton from '@/components/MkButton.vue'; import { getNoteSummary } from '@/scripts/get-note-summary.js'; import { notePage } from '@/filters/note.js'; import { userPage } from '@/filters/user.js'; import { i18n } from '@/i18n.js'; -import * as os from '@/os.js'; -import { $i } from '@/account.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; +import { signinRequired } from '@/account.js'; import { infoImageUrl } from '@/instance.js'; +const $i = signinRequired(); + const props = withDefaults(defineProps<{ notification: Misskey.entities.Notification; withTime?: boolean; @@ -161,13 +172,15 @@ const props = withDefaults(defineProps<{ const followRequestDone = ref(false); const acceptFollowRequest = () => { + if (props.notification.user == null) return; followRequestDone.value = true; - os.api('following/requests/accept', { userId: props.notification.user.id }); + misskeyApi('following/requests/accept', { userId: props.notification.user.id }); }; const rejectFollowRequest = () => { + if (props.notification.user == null) return; followRequestDone.value = true; - os.api('following/requests/reject', { userId: props.notification.user.id }); + misskeyApi('following/requests/reject', { userId: props.notification.user.id }); }; </script> @@ -283,6 +296,12 @@ const rejectFollowRequest = () => { pointer-events: none; } +.t_roleAssigned { + padding: 3px; + background: #88a6b7; + pointer-events: none; +} + .tail { flex: 1; min-width: 0; diff --git a/packages/frontend/src/components/MkNotificationSelectWindow.vue b/packages/frontend/src/components/MkNotificationSelectWindow.vue index 6725776f43c8d27ac7f54e378da5a22075c19c1d..71b38d99ed9c92897842402b5ca269ce79634b8c 100644 --- a/packages/frontend/src/components/MkNotificationSelectWindow.vue +++ b/packages/frontend/src/components/MkNotificationSelectWindow.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton> <MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton> </div> - <MkSwitch v-for="ntype in notificationTypes" :key="ntype" v-model="typesMap[ntype].value">{{ i18n.t(`_notification._types.${ntype}`) }}</MkSwitch> + <MkSwitch v-for="ntype in notificationTypes" :key="ntype" v-model="typesMap[ntype].value">{{ i18n.ts._notification._types[ntype] }}</MkSwitch> </div> </MkSpacer> </MkModalWindow> diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue index a157820d56295945a327d794af5358dc6a5b5ac0..68bf1bf3d8f252e43de75e4ec6aaa579d4d07c65 100644 --- a/packages/frontend/src/components/MkNotifications.vue +++ b/packages/frontend/src/components/MkNotifications.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -15,11 +15,11 @@ SPDX-License-Identifier: AGPL-3.0-only <template #default="{ items: notifications }"> <MkDateSeparatedList v-if="defaultStore.state.noteDesign === 'misskey'" v-slot="{ item: notification }" :class="$style.list" :items="notifications" :noGap="true"> - <MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :key="notification.id" :note="notification.note" :withHardMute="true"/> + <MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :key="notification.id + ':note'" :note="notification.note" :withHardMute="true"/> <XNotification v-else :key="notification.id" :notification="notification" :withTime="true" :full="true" class="_panel"/> </MkDateSeparatedList> <MkDateSeparatedList v-else-if="defaultStore.state.noteDesign === 'sharkey'" v-slot="{ item: notification }" :class="$style.list" :items="notifications" :noGap="true"> - <SkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :key="notification.id" :note="notification.note" :withHardMute="true"/> + <SkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :key="notification.id + ':note'" :note="notification.note" :withHardMute="true"/> <XNotification v-else :key="notification.id" :notification="notification" :withTime="true" :full="true" class="_panel"/> </MkDateSeparatedList> </template> @@ -40,6 +40,7 @@ import { notificationTypes } from '@/const.js'; import { infoImageUrl } from '@/instance.js'; import { defaultStore } from '@/store.js'; import MkPullToRefresh from '@/components/MkPullToRefresh.vue'; +import * as Misskey from 'misskey-js'; const props = defineProps<{ excludeTypes?: typeof notificationTypes[number][]; @@ -68,7 +69,7 @@ function onNotification(notification) { } if (!isMuted) { - pagingComponent.value.prepend(notification); + pagingComponent.value?.prepend(notification); } } @@ -80,17 +81,19 @@ function reload() { }); } -let connection; +let connection: Misskey.ChannelConnection<Misskey.Channels['main']>; onMounted(() => { connection = useStream().useChannel('main'); connection.on('notification', onNotification); + connection.on('notificationFlushed', reload); }); onActivated(() => { pagingComponent.value?.reload(); connection = useStream().useChannel('main'); connection.on('notification', onNotification); + connection.on('notificationFlushed', reload); }); onUnmounted(() => { diff --git a/packages/frontend/src/components/MkNumber.vue b/packages/frontend/src/components/MkNumber.vue index aa04ab253b4d13616e9b4101b6f5761c181fe928..a278205b61269b46820be3fb0e861bb501067026 100644 --- a/packages/frontend/src/components/MkNumber.vue +++ b/packages/frontend/src/components/MkNumber.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -9,7 +9,6 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { reactive, watch } from 'vue'; -import gsap from 'gsap'; import number from '@/filters/number.js'; const props = defineProps<{ @@ -20,8 +19,24 @@ const tweened = reactive({ number: 0, }); -watch(() => props.value, (n) => { - gsap.to(tweened, { duration: 1, number: Number(n) || 0 }); +watch(() => props.value, (to, from) => { + // requestAnimationFrameを利用ã—ã¦ã€500msã§fromã‹ã‚‰toã¾ã§ã‚’1次関数的ã«å¤‰åŒ–ã•ã›ã‚‹ + let start: number | null = null; + + function step(timestamp: number) { + if (start === null) { + start = timestamp; + } + const elapsed = timestamp - start; + tweened.number = (from ?? 0) + (to - (from ?? 0)) * elapsed / 500; + if (elapsed < 500) { + window.requestAnimationFrame(step); + } else { + tweened.number = to; + } + } + + window.requestAnimationFrame(step); }, { immediate: true, }); diff --git a/packages/frontend/src/components/MkNumberDiff.vue b/packages/frontend/src/components/MkNumberDiff.vue index a98b6c4713c4f2bf2199637b465a03138996d65a..1825cc5405df8123477a8fe1c31cc113ffd4f791 100644 --- a/packages/frontend/src/components/MkNumberDiff.vue +++ b/packages/frontend/src/components/MkNumberDiff.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkObjectView.value.vue b/packages/frontend/src/components/MkObjectView.value.vue index aa05c43c0b83a7af77dde4c6d44c5783f7533b76..870599aa94b101b0ff315e28f6ab51312da1db5d 100644 --- a/packages/frontend/src/components/MkObjectView.value.vue +++ b/packages/frontend/src/components/MkObjectView.value.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkObjectView.vue b/packages/frontend/src/components/MkObjectView.vue index 30ec896ce46daa38e00bbbf880153106e365a65f..bb9122c976459922cc5c8c151dd8387358e83677 100644 --- a/packages/frontend/src/components/MkObjectView.vue +++ b/packages/frontend/src/components/MkObjectView.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkOmit.vue b/packages/frontend/src/components/MkOmit.vue index 702bb95dc7f8e6a8f240527ad040242944c51e96..a0bc0c628e8753ba766506f0b692f45fc39ad38e 100644 --- a/packages/frontend/src/components/MkOmit.vue +++ b/packages/frontend/src/components/MkOmit.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -27,7 +27,7 @@ const omitted = ref(false); const ignoreOmit = ref(false); const calcOmit = () => { - if (omitted.value || ignoreOmit.value) return; + if (omitted.value || ignoreOmit.value || content.value == null) return; omitted.value = content.value.offsetHeight > props.maxHeight; }; @@ -37,7 +37,7 @@ const omitObserver = new ResizeObserver((entries, observer) => { onMounted(() => { calcOmit(); - omitObserver.observe(content.value); + omitObserver.observe(content.value as HTMLElement); }); onUnmounted(() => { diff --git a/packages/frontend/src/components/MkPagePreview.vue b/packages/frontend/src/components/MkPagePreview.vue index 6c8a0e56a63e84757226f8995019f1bccfd96118..f6dc00698ccb6bc9343ea85e63f902194cdbf3b5 100644 --- a/packages/frontend/src/components/MkPagePreview.vue +++ b/packages/frontend/src/components/MkPagePreview.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only </header> <p v-if="page.summary" :title="page.summary">{{ page.summary.length > 85 ? page.summary.slice(0, 85) + '…' : page.summary }}</p> <footer> - <img class="icon" :src="page.user.avatarUrl"/> + <img v-if="page.user.avatarUrl" class="icon" :src="page.user.avatarUrl"/> <p>{{ userName(page.user) }}</p> </footer> </article> diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue index 13a703e9f639795dcd7573ad6e01aa863de96e30..c3fa724a7ada3868e50cc09083c4d9b50a380366 100644 --- a/packages/frontend/src/components/MkPageWindow.vue +++ b/packages/frontend/src/components/MkPageWindow.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -16,33 +16,33 @@ SPDX-License-Identifier: AGPL-3.0-only @closed="$emit('closed')" > <template #header> - <template v-if="pageMetadata?.value"> - <i v-if="pageMetadata.value.icon" :class="pageMetadata.value.icon" style="margin-right: 0.5em;"></i> - <span>{{ pageMetadata.value.title }}</span> + <template v-if="pageMetadata"> + <i v-if="pageMetadata.icon" :class="pageMetadata.icon" style="margin-right: 0.5em;"></i> + <span>{{ pageMetadata.title }}</span> </template> </template> <div ref="contents" :class="$style.root" style="container-type: inline-size;"> - <RouterView :key="reloadCount" :router="router"/> + <RouterView :key="reloadCount" :router="windowRouter"/> </div> </MkWindow> </template> <script lang="ts" setup> -import { ComputedRef, onMounted, onUnmounted, provide, shallowRef, ref, computed } from 'vue'; +import { computed, onMounted, onUnmounted, provide, ref, shallowRef } from 'vue'; import RouterView from '@/components/global/RouterView.vue'; import MkWindow from '@/components/MkWindow.vue'; import { popout as _popout } from '@/scripts/popout.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { url } from '@/config.js'; -import { mainRouter, routes, page } from '@/router.js'; -import { $i } from '@/account.js'; -import { Router, useScrollPositionManager } from '@/nirax.js'; +import { useScrollPositionManager } from '@/nirax.js'; import { i18n } from '@/i18n.js'; -import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js'; +import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; import { openingWindowsCount } from '@/os.js'; import { claimAchievement } from '@/scripts/achievements.js'; import { getScrollContainer } from '@/scripts/scroll.js'; +import { useRouterFactory } from '@/router/supplier.js'; +import { mainRouter } from '@/router/main.js'; const props = defineProps<{ initialPath: string; @@ -52,17 +52,18 @@ defineEmits<{ (ev: 'closed'): void; }>(); -const router = new Router(routes, props.initialPath, !!$i, page(() => import('@/pages/not-found.vue'))); +const routerFactory = useRouterFactory(); +const windowRouter = routerFactory(props.initialPath); -const contents = shallowRef<HTMLElement>(); -const pageMetadata = ref<null | ComputedRef<PageMetadata>>(); +const contents = shallowRef<HTMLElement | null>(null); +const pageMetadata = ref<null | PageMetadata>(null); const windowEl = shallowRef<InstanceType<typeof MkWindow>>(); const history = ref<{ path: string; key: any; }[]>([{ - path: router.getCurrentPath(), - key: router.getCurrentKey(), + path: windowRouter.getCurrentPath(), + key: windowRouter.getCurrentKey(), }]); const buttonsLeft = computed(() => { - const buttons = []; + const buttons: Record<string, unknown>[] = []; if (history.value.length > 1) { buttons.push({ @@ -75,7 +76,7 @@ const buttonsLeft = computed(() => { }); const buttonsRight = computed(() => { const buttons = [{ - icon: 'ph-arrow-clockwise ph-bold ph-lg', + icon: 'ph-arrows-clockwise ph-bold ph-lg', title: i18n.ts.reload, onClick: reload, }, { @@ -88,14 +89,23 @@ const buttonsRight = computed(() => { }); const reloadCount = ref(0); -router.addListener('push', ctx => { +windowRouter.addListener('push', ctx => { history.value.push({ path: ctx.path, key: ctx.key }); }); -provide('router', router); -provideMetadataReceiver((info) => { +windowRouter.addListener('replace', ctx => { + history.value.pop(); + history.value.push({ path: ctx.path, key: ctx.key }); +}); + +windowRouter.init(); + +provide('router', windowRouter); +provideMetadataReceiver((metadataGetter) => { + const info = metadataGetter(); pageMetadata.value = info; }); +provideReactiveMetadata(pageMetadata); provide('shouldOmitHeaderTitle', true); provide('shouldHeaderThin', true); provide('forceSpacerMin', true); @@ -113,20 +123,20 @@ const contextmenu = computed(() => ([{ icon: 'ph-arrow-square-out ph-bold ph-lg', text: i18n.ts.openInNewTab, action: () => { - window.open(url + router.getCurrentPath(), '_blank', 'noopener'); - windowEl.value.close(); + window.open(url + windowRouter.getCurrentPath(), '_blank', 'noopener'); + windowEl.value?.close(); }, }, { icon: 'ph-link ph-bold ph-lg', text: i18n.ts.copyLink, action: () => { - copyToClipboard(url + router.getCurrentPath()); + copyToClipboard(url + windowRouter.getCurrentPath()); }, }])); function back() { history.value.pop(); - router.replace(history.value.at(-1)!.path, history.value.at(-1)!.key); + windowRouter.replace(history.value.at(-1)!.path, history.value.at(-1)!.key); } function reload() { @@ -134,20 +144,20 @@ function reload() { } function close() { - windowEl.value.close(); + windowEl.value?.close(); } function expand() { - mainRouter.push(router.getCurrentPath(), 'forcePage'); - windowEl.value.close(); + mainRouter.push(windowRouter.getCurrentPath(), 'forcePage'); + windowEl.value?.close(); } function popout() { - _popout(router.getCurrentPath(), windowEl.value.$el); - windowEl.value.close(); + _popout(windowRouter.getCurrentPath(), windowEl.value?.$el); + windowEl.value?.close(); } -useScrollPositionManager(() => getScrollContainer(contents.value), router); +useScrollPositionManager(() => getScrollContainer(contents.value), windowRouter); onMounted(() => { openingWindowsCount.value++; diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index bdd96238d3f0a60b9ea6665adbbbb2ef16fc2645..62a85389ad967b57a2693e21d96ad5607a2a62a3 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -46,6 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, shallowRef, watch } from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll.js'; import { useDocumentVisibility } from '@/scripts/use-document-visibility.js'; import { defaultStore } from '@/store.js'; @@ -203,7 +204,7 @@ async function init(): Promise<void> { queue.value = new Map(); fetching.value = true; const params = props.pagination.params ? isRef(props.pagination.params) ? props.pagination.params.value : props.pagination.params : {}; - await os.api(props.pagination.endpoint, { + await misskeyApi<MisskeyEntity[]>(props.pagination.endpoint, { ...params, limit: props.pagination.limit ?? 10, allowPartial: true, @@ -239,7 +240,7 @@ const fetchMore = async (): Promise<void> => { if (!more.value || fetching.value || moreFetching.value || items.value.size === 0) return; moreFetching.value = true; const params = props.pagination.params ? isRef(props.pagination.params) ? props.pagination.params.value : props.pagination.params : {}; - await os.api(props.pagination.endpoint, { + await misskeyApi<MisskeyEntity[]>(props.pagination.endpoint, { ...params, limit: SECOND_FETCH_LIMIT, ...(props.pagination.offsetMode ? { @@ -303,7 +304,7 @@ const fetchMoreAhead = async (): Promise<void> => { if (!more.value || fetching.value || moreFetching.value || items.value.size === 0) return; moreFetching.value = true; const params = props.pagination.params ? isRef(props.pagination.params) ? props.pagination.params.value : props.pagination.params : {}; - await os.api(props.pagination.endpoint, { + await misskeyApi<MisskeyEntity[]>(props.pagination.endpoint, { ...params, limit: SECOND_FETCH_LIMIT, ...(props.pagination.offsetMode ? { diff --git a/packages/frontend/src/components/MkPasswordDialog.vue b/packages/frontend/src/components/MkPasswordDialog.vue index 85dd4027303cc0fa17726c65dafac988c29f3d75..3c0cdaa7863a6953742d3c2876f3d9d27e052982 100644 --- a/packages/frontend/src/components/MkPasswordDialog.vue +++ b/packages/frontend/src/components/MkPasswordDialog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -41,7 +41,9 @@ import MkInput from '@/components/MkInput.vue'; import MkButton from '@/components/MkButton.vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; import { i18n } from '@/i18n.js'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; + +const $i = signinRequired(); const emit = defineEmits<{ (ev: 'done', v: { password: string; token: string | null; }): void; diff --git a/packages/frontend/src/components/MkPlusOneEffect.vue b/packages/frontend/src/components/MkPlusOneEffect.vue index a741a3f7a8f915905a8d705ec1059c311d289c66..6c22edb94381e5465b08cd44c06d7210d364b53c 100644 --- a/packages/frontend/src/components/MkPlusOneEffect.vue +++ b/packages/frontend/src/components/MkPlusOneEffect.vue @@ -1,11 +1,11 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <div :class="$style.root" :style="{ zIndex, top: `${y - 64}px`, left: `${x - 64}px` }"> - <span class="text" :class="{ up }">+1</span> + <span class="text" :class="{ up }">+{{ value }}</span> </div> </template> @@ -16,7 +16,9 @@ import * as os from '@/os.js'; const props = withDefaults(defineProps<{ x: number; y: number; + value?: number | string; }>(), { + value: 1, }); const emit = defineEmits<{ @@ -40,6 +42,7 @@ onMounted(() => { <style lang="scss" module> .root { + user-select: none; pointer-events: none; position: fixed; width: 128px; diff --git a/packages/frontend/src/components/MkPoll.vue b/packages/frontend/src/components/MkPoll.vue index 6ee0c446585775186a6c16ccc3ec58939eb53e6c..8c0804de049b6ad2731aebd151befcd03ec6c356 100644 --- a/packages/frontend/src/components/MkPoll.vue +++ b/packages/frontend/src/components/MkPoll.vue @@ -1,29 +1,28 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <div :class="{ [$style.done]: closed || isVoted }"> <ul :class="$style.choices"> - <li v-for="(choice, i) in note.poll.choices" :key="i" :class="$style.choice" @click="vote(i)"> + <li v-for="(choice, i) in poll.choices" :key="i" :class="$style.choice" @click="vote(i)"> <div :class="$style.bg" :style="{ 'width': `${showResult ? (choice.votes / total * 100) : 0}%` }"></div> <span :class="$style.fg"> <template v-if="choice.isVoted"><i class="ph-check ph-bold ph-lg" style="margin-right: 4px; color: var(--accent);"></i></template> <Mfm :text="choice.text" :plain="true"/> - <span v-if="showResult" style="margin-left: 4px; opacity: 0.7;">({{ i18n.t('_poll.votesCount', { n: choice.votes }) }})</span> + <span v-if="showResult" style="margin-left: 4px; opacity: 0.7;">({{ i18n.tsx._poll.votesCount({ n: choice.votes }) }})</span> </span> </li> </ul> <p v-if="!readOnly" :class="$style.info"> - <span>{{ i18n.t('_poll.totalVotes', { n: total }) }}</span> - <span v-if="note.poll.multiple"> · </span> - <span v-if="note.poll.multiple" style="color: var(--accent); font-weight: bolder;">{{ i18n.ts._poll.multiple }}</span> + <span>{{ i18n.tsx._poll.totalVotes({ n: total }) }}</span> + <span v-if="poll.multiple"> · </span> + <span v-if="poll.multiple" style="color: var(--accent); font-weight: bolder;">{{ i18n.ts._poll.multiple }}</span> <span> · </span> <a v-if="!closed && !isVoted" style="color: inherit;" @click="showResult = !showResult">{{ showResult ? i18n.ts._poll.vote : i18n.ts._poll.showResult }}</a> <span v-if="isVoted">{{ i18n.ts._poll.voted }}</span> <span v-else-if="closed">{{ i18n.ts._poll.closed }}</span> - <span v-if="!isLocal"><span> · </span><a @click.stop="refresh">{{ i18n.ts.reload }}</a></span> <span v-if="remaining > 0"> · {{ timer }}</span> </p> </div> @@ -35,36 +34,38 @@ import * as Misskey from 'misskey-js'; import { sum } from '@/scripts/array.js'; import { pleaseLogin } from '@/scripts/please-login.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { useInterval } from '@/scripts/use-interval.js'; const props = defineProps<{ - note: Misskey.entities.Note; + noteId: string; + poll: NonNullable<Misskey.entities.Note['poll']>; readOnly?: boolean; }>(); const remaining = ref(-1); -const total = computed(() => sum(props.note.poll.choices.map(x => x.votes))); +const total = computed(() => sum(props.poll.choices.map(x => x.votes))); const closed = computed(() => remaining.value === 0); -const isLocal = computed(() => !props.note.uri); -const isVoted = computed(() => !props.note.poll.multiple && props.note.poll.choices.some(c => c.isVoted)); -const timer = computed(() => i18n.t( - remaining.value >= 86400 ? '_poll.remainingDays' : - remaining.value >= 3600 ? '_poll.remainingHours' : - remaining.value >= 60 ? '_poll.remainingMinutes' : '_poll.remainingSeconds', { - s: Math.floor(remaining.value % 60), - m: Math.floor(remaining.value / 60) % 60, - h: Math.floor(remaining.value / 3600) % 24, - d: Math.floor(remaining.value / 86400), - })); +const isVoted = computed(() => !props.poll.multiple && props.poll.choices.some(c => c.isVoted)); +const timer = computed(() => i18n.tsx._poll[ + remaining.value >= 86400 ? 'remainingDays' : + remaining.value >= 3600 ? 'remainingHours' : + remaining.value >= 60 ? 'remainingMinutes' : 'remainingSeconds' +]({ + s: Math.floor(remaining.value % 60), + m: Math.floor(remaining.value / 60) % 60, + h: Math.floor(remaining.value / 3600) % 24, + d: Math.floor(remaining.value / 86400), +})); const showResult = ref(props.readOnly || isVoted.value); // 期é™ä»˜ãアンケート -if (props.note.poll.expiresAt) { +if (props.poll.expiresAt) { const tick = () => { - remaining.value = Math.floor(Math.max(new Date(props.note.poll.expiresAt).getTime() - Date.now(), 0) / 1000); + remaining.value = Math.floor(Math.max(new Date(props.poll.expiresAt!).getTime() - Date.now(), 0) / 1000); if (remaining.value === 0) { showResult.value = true; } @@ -80,34 +81,26 @@ const vote = async (id) => { pleaseLogin(); if (props.readOnly || closed.value || isVoted.value) return; - if (!props.note.poll.multiple) { + if (!props.poll.multiple) { const { canceled } = await os.confirm({ type: 'question', - text: i18n.t('voteConfirm', { choice: props.note.poll.choices[id].text }), + text: i18n.tsx.voteConfirm({ choice: props.poll.choices[id].text }), }); if (canceled) return; } else { const { canceled } = await os.confirm({ type: 'question', - text: i18n.t('voteConfirmMulti', { choice: props.note.poll.choices[id].text }), + text: i18n.tsx.voteConfirmMulti({ choice: props.poll.choices[id].text }), }); if (canceled) return; } - await os.api('notes/polls/vote', { - noteId: props.note.id, + await misskeyApi('notes/polls/vote', { + noteId: props.noteId, choice: id, }); - if (!showResult.value) showResult.value = !props.note.poll.multiple; + if (!showResult.value) showResult.value = !props.poll.multiple; }; - -async function refresh() { - if (!props.note.uri) return; - const obj = await os.apiWithDialog('ap/show', { uri: props.note.uri }); - if (obj.type === 'Note' && obj.object.poll) { - props.note.poll = obj.object.poll; // eslint-disable-line vue/no-mutating-props - } -} </script> <style lang="scss" module> diff --git a/packages/frontend/src/components/MkPollEditor.vue b/packages/frontend/src/components/MkPollEditor.vue index f46779a6323cd20b95f38191abcee13a8bb5cc67..98fbf253701e9266241b0d59f0746ed1d3988d95 100644 --- a/packages/frontend/src/components/MkPollEditor.vue +++ b/packages/frontend/src/components/MkPollEditor.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only </p> <ul> <li v-for="(choice, i) in choices" :key="i"> - <MkInput class="input" small :modelValue="choice" :placeholder="i18n.t('_poll.choiceN', { n: i + 1 })" @update:modelValue="onInput(i, $event)"> + <MkInput class="input" small :modelValue="choice" :placeholder="i18n.tsx._poll.choiceN({ n: i + 1 })" @update:modelValue="onInput(i, $event)"> </MkInput> <button class="_button" @click="remove(i)"> <i class="ph-x ph-bold ph-lg"></i> @@ -62,21 +62,18 @@ import { formatDateTimeString } from '@/scripts/format-time-string.js'; import { addTime } from '@/scripts/time.js'; import { i18n } from '@/i18n.js'; +export type PollEditorModelValue = { + expiresAt: number | null; + expiredAfter: number | null; + choices: string[]; + multiple: boolean; +}; + const props = defineProps<{ - modelValue: { - expiresAt: string; - expiredAfter: number; - choices: string[]; - multiple: boolean; - }; + modelValue: PollEditorModelValue; }>(); const emit = defineEmits<{ - (ev: 'update:modelValue', v: { - expiresAt: string; - expiredAfter: number; - choices: string[]; - multiple: boolean; - }): void; + (ev: 'update:modelValue', v: PollEditorModelValue): void; }>(); const choices = ref(props.modelValue.choices); @@ -89,7 +86,9 @@ const unit = ref('second'); if (props.modelValue.expiresAt) { expiration.value = 'at'; - atDate.value = atTime.value = props.modelValue.expiresAt; + const expiresAt = new Date(props.modelValue.expiresAt); + atDate.value = formatDateTimeString(expiresAt, 'yyyy-MM-dd'); + atTime.value = formatDateTimeString(expiresAt, 'HH:mm'); } else if (typeof props.modelValue.expiredAfter === 'number') { expiration.value = 'after'; after.value = props.modelValue.expiredAfter / 1000; @@ -113,20 +112,21 @@ function remove(i) { choices.value = choices.value.filter((_, _i) => _i !== i); } -function get() { +function get(): PollEditorModelValue { const calcAt = () => { return new Date(`${atDate.value} ${atTime.value}`).getTime(); }; const calcAfter = () => { - let base = parseInt(after.value); + let base = parseInt(after.value.toString()); switch (unit.value) { + // @ts-expect-error fallthrough case 'day': base *= 24; - // fallthrough + // @ts-expect-error fallthrough case 'hour': base *= 60; - // fallthrough + // @ts-expect-error fallthrough case 'minute': base *= 60; - // fallthrough + // eslint-disable-next-line no-fallthrough case 'second': return base *= 1000; default: return null; } @@ -135,10 +135,8 @@ function get() { return { choices: choices.value, multiple: multiple.value, - ...( - expiration.value === 'at' ? { expiresAt: calcAt() } : - expiration.value === 'after' ? { expiredAfter: calcAfter() } : {} - ), + expiresAt: expiration.value === 'at' ? calcAt() : null, + expiredAfter: expiration.value === 'after' ? calcAfter() : null, }; } diff --git a/packages/frontend/src/components/MkPopupMenu.vue b/packages/frontend/src/components/MkPopupMenu.vue index 1d92374f4f31adc705f89b05999da9404181096d..3748f0cc642ce5ff5c18a468a752799cfaa13077 100644 --- a/packages/frontend/src/components/MkPopupMenu.vue +++ b/packages/frontend/src/components/MkPopupMenu.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index aa37cef6c2538354f9c4198ed343f32c8eaf7bd1..d9e50fbb793fbb2a7ca771d63f97b78d7f863437 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -84,7 +84,7 @@ SPDX-License-Identifier: AGPL-3.0-only <button v-tooltip="i18n.ts.useCw" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: useCw }]" @click="useCw = !useCw"><i class="ph-eye-slash ph-bold ph-lg"></i></button> <button v-tooltip="i18n.ts.mention" class="_button" :class="$style.footerButton" @click="insertMention"><i class="ph-at ph-bold ph-lg"></i></button> <button v-tooltip="i18n.ts.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ph-hash ph-bold ph-lg"></i></button> - <button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugin" class="_button" :class="$style.footerButton" @click="showActions"><i class="ph-plug ph-bold ph-lg"></i></button> + <button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugins" class="_button" :class="$style.footerButton" @click="showActions"><i class="ph-plug ph-bold ph-lg"></i></button> <button v-tooltip="i18n.ts.emoji" :class="['_button', $style.footerButton]" @click="insertEmoji"><i class="ph-smiley ph-bold ph-lg"></i></button> <button v-if="showAddMfmFunction" v-tooltip="i18n.ts.addMfmFunction" :class="['_button', $style.footerButton]" @click="insertMfmFunction"><i class="ph-palette ph-bold ph-lg"></i></button> </div> @@ -101,27 +101,28 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed } from 'vue'; -import * as mfm from '@sharkey/sfm-js'; +import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed, toRaw } from 'vue'; +import * as mfm from '@transfem-org/sfm-js'; import * as Misskey from 'misskey-js'; import insertTextAtCursor from 'insert-text-at-cursor'; import { toASCII } from 'punycode/'; import MkNoteSimple from '@/components/MkNoteSimple.vue'; import MkNotePreview from '@/components/MkNotePreview.vue'; import XPostFormAttaches from '@/components/MkPostFormAttaches.vue'; -import MkPollEditor from '@/components/MkPollEditor.vue'; +import MkPollEditor, { type PollEditorModelValue } from '@/components/MkPollEditor.vue'; import { host, url } from '@/config.js'; import { erase, unique } from '@/scripts/array.js'; import { extractMentions } from '@/scripts/extract-mentions.js'; import { formatTimeString } from '@/scripts/format-time-string.js'; import { Autocomplete } from '@/scripts/autocomplete.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { selectFiles } from '@/scripts/select-file.js'; import { defaultStore, notePostInterruptors, postFormActions } from '@/store.js'; import MkInfo from '@/components/MkInfo.vue'; import { i18n } from '@/i18n.js'; import { instance } from '@/instance.js'; -import { $i, notesCount, incNotesCount, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account.js'; +import { signinRequired, notesCount, incNotesCount, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account.js'; import { uploadFile } from '@/scripts/upload.js'; import { deepClone } from '@/scripts/clone.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; @@ -130,6 +131,8 @@ import { claimAchievement } from '@/scripts/achievements.js'; import { emojiPicker } from '@/scripts/emoji-picker.js'; import { mfmFunctionPicker } from '@/scripts/mfm-function-picker.js'; +const $i = signinRequired(); + const modal = inject('modal'); const props = withDefaults(defineProps<{ @@ -137,13 +140,13 @@ const props = withDefaults(defineProps<{ renote?: Misskey.entities.Note; channel?: Misskey.entities.Channel; // TODO mention?: Misskey.entities.User; - specified?: Misskey.entities.User; + specified?: Misskey.entities.UserDetailed; initialText?: string; initialCw?: string; initialVisibility?: (typeof Misskey.noteVisibilities)[number]; initialFiles?: Misskey.entities.DriveFile[]; initialLocalOnly?: boolean; - initialVisibleUsers?: Misskey.entities.User[]; + initialVisibleUsers?: Misskey.entities.UserDetailed[]; initialNote?: Misskey.entities.Note; instant?: boolean; fixed?: boolean; @@ -171,18 +174,13 @@ const emit = defineEmits<{ const textareaEl = shallowRef<HTMLTextAreaElement | null>(null); const cwInputEl = shallowRef<HTMLInputElement | null>(null); const hashtagsInputEl = shallowRef<HTMLInputElement | null>(null); -const visibilityButton = shallowRef<HTMLElement | null>(null); +const visibilityButton = shallowRef<HTMLElement>(); const posting = ref(false); const posted = ref(false); const text = ref(props.initialText ?? ''); const files = ref(props.initialFiles ?? []); -const poll = ref<{ - choices: string[]; - multiple: boolean; - expiresAt: string | null; - expiredAfter: string | null; -} | null>(null); +const poll = ref<PollEditorModelValue | null>(null); const useCw = ref<boolean>(!!props.initialCw); const showPreview = ref(defaultStore.state.showPreview); watch(showPreview, () => defaultStore.set('showPreview', showPreview.value)); @@ -309,7 +307,7 @@ if (props.reply && props.reply.text != null) { } } -if ($i?.isSilenced && visibility.value === 'public') { +if ($i.isSilenced && visibility.value === 'public') { visibility.value = 'home'; } @@ -330,15 +328,15 @@ if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visib if (visibility.value === 'specified') { if (props.reply.visibleUserIds) { - os.api('users/show', { - userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply.userId), + misskeyApi('users/show', { + userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply?.userId), }).then(users => { users.forEach(pushVisibleUser); }); } if (props.reply.userId !== $i.id) { - os.api('users/show', { userId: props.reply.userId }).then(user => { + misskeyApi('users/show', { userId: props.reply.userId }).then(user => { pushVisibleUser(user); }); } @@ -389,7 +387,7 @@ function addMissingMention() { for (const x of extractMentions(ast)) { if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) { - os.api('users/show', { username: x.username, host: x.host }).then(user => { + misskeyApi('users/show', { username: x.username, host: x.host }).then(user => { visibleUsers.value.push(user); }); } @@ -466,9 +464,10 @@ function setVisibility() { os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), { currentVisibility: visibility.value, - isSilenced: $i?.isSilenced, + isSilenced: $i.isSilenced, localOnly: localOnly.value, src: visibilityButton.value, + ...(props.reply ? { isReplyVisibilitySpecified: props.reply.visibility === 'specified' } : {}), }, { changeVisibility: v => { visibility.value = v; @@ -537,7 +536,7 @@ async function toggleReactionAcceptance() { reactionAcceptance.value = select.result; } -function pushVisibleUser(user) { +function pushVisibleUser(user: Misskey.entities.UserDetailed) { if (!visibleUsers.value.some(u => u.username === user.username && u.host === user.host)) { visibleUsers.value.push(user); } @@ -579,10 +578,12 @@ function onCompositionEnd(ev: CompositionEvent) { async function onPaste(ev: ClipboardEvent) { if (props.mock) return; + if (!ev.clipboardData) return; - for (const { item, i } of Array.from(ev.clipboardData.items, (item, i) => ({ item, i }))) { + for (const { item, i } of Array.from(ev.clipboardData.items, (data, x) => ({ item: data, i: x }))) { if (item.kind === 'file') { const file = item.getAsFile(); + if (!file) continue; const lio = file.name.lastIndexOf('.'); const ext = lio >= 0 ? file.name.slice(lio) : ''; const formatted = `${formatTimeString(new Date(file.lastModified), defaultStore.state.pastedFileName).replace(/{{number}}/g, `${i + 1}`)}${ext}`; @@ -604,7 +605,7 @@ async function onPaste(ev: ClipboardEvent) { return; } - quoteId.value = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)[1]; + quoteId.value = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)?.[1] ?? null; }); } } @@ -635,26 +636,26 @@ function onDragover(ev) { } } -function onDragenter(ev) { +function onDragenter() { draghover.value = true; } -function onDragleave(ev) { +function onDragleave() { draghover.value = false; } -function onDrop(ev): void { +function onDrop(ev: DragEvent): void { draghover.value = false; // ファイルã ã£ãŸã‚‰ - if (ev.dataTransfer.files.length > 0) { + if (ev.dataTransfer && ev.dataTransfer.files.length > 0) { ev.preventDefault(); for (const x of Array.from(ev.dataTransfer.files)) upload(x); return; } //#region ドライブã®ãƒ•ã‚¡ã‚¤ãƒ« - const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_); + const driveFile = ev.dataTransfer?.getData(_DATA_TRANSFER_DRIVE_FILE_); if (driveFile != null && driveFile !== '') { const file = JSON.parse(driveFile); files.value.push(file); @@ -702,11 +703,14 @@ async function post(ev?: MouseEvent) { } if (ev) { - const el = ev.currentTarget ?? ev.target; - const rect = el.getBoundingClientRect(); - const x = rect.left + (el.offsetWidth / 2); - const y = rect.top + (el.offsetHeight / 2); - os.popup(MkRippleEffect, { x, y }, {}, 'end'); + const el = (ev.currentTarget ?? ev.target) as HTMLElement | null; + + if (el) { + const rect = el.getBoundingClientRect(); + const x = rect.left + (el.offsetWidth / 2); + const y = rect.top + (el.offsetHeight / 2); + os.popup(MkRippleEffect, { x, y }, {}, 'end'); + } } if (props.mock) return; @@ -741,6 +745,29 @@ async function post(ev?: MouseEvent) { visibility.value = 'home'; } } + + if (defaultStore.state.warnMissingAltText) { + const filesData = toRaw(files.value); + + const isMissingAltText = filesData.some(file => !file.comment); + + if (isMissingAltText) { + const { canceled, result } = await os.actions({ + type: 'warning', + text: i18n.ts.thisPostIsMissingAltText, + actions: [{ + value: 'cancel', + text: i18n.ts.thisPostIsMissingAltTextCancel, + }, { + value: 'ignore', + text: i18n.ts.thisPostIsMissingAltTextIgnore, + }], + }); + + if (canceled) return; + if (result === 'cancel') return; + } + } let postData = { text: text.value === '' ? null : text.value, @@ -759,29 +786,39 @@ async function post(ev?: MouseEvent) { if (withHashtags.value && hashtags.value && hashtags.value.trim() !== '') { const hashtags_ = hashtags.value.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' '); - postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_; + if (!postData.text) { + postData.text = hashtags_; + } else { + const postTextLines = postData.text.split('\n'); + if (postTextLines[postTextLines.length - 1].trim() === '') { + postTextLines[postTextLines.length - 1] += hashtags_; + } else { + postTextLines[postTextLines.length - 1] += ' ' + hashtags_; + } + postData.text = postTextLines.join('\n'); + } } // plugin if (notePostInterruptors.length > 0) { for (const interruptor of notePostInterruptors) { try { - postData = await interruptor.handler(deepClone(postData)); + postData = await interruptor.handler(deepClone(postData)) as typeof postData; } catch (err) { console.error(err); } } } - let token = undefined; + let token: string | undefined = undefined; if (postAccount.value) { const storedAccounts = await getAccounts(); - token = storedAccounts.find(x => x.id === postAccount.value.id)?.token; + token = storedAccounts.find(x => x.id === postAccount.value?.id)?.token; } posting.value = true; - os.api(postData.editId ? 'notes/edit' : 'notes/create', postData, token).then(() => { + misskeyApi(postData.editId ? 'notes/edit' : 'notes/create', postData, token).then(() => { if (props.freezeAfterPosted) { posted.value = true; } else { @@ -791,7 +828,7 @@ async function post(ev?: MouseEvent) { deleteDraft(); emit('posted'); if (postData.text && postData.text !== '') { - const hashtags_ = mfm.parse(postData.text).filter(x => x.type === 'hashtag').map(x => x.props.hashtag); + const hashtags_ = mfm.parse(postData.text).map(x => x.type === 'hashtag' && x.props.hashtag).filter(x => x) as string[]; const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[]; miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history)))); } @@ -854,16 +891,17 @@ function cancel() { } function insertMention() { - os.selectUser().then(user => { + os.selectUser({ localOnly: localOnly.value, includeSelf: true }).then(user => { insertTextAtCursor(textareaEl.value, '@' + Misskey.acct.toString(user) + ' '); }); } async function insertEmoji(ev: MouseEvent) { textAreaReadOnly.value = true; - + const target = ev.currentTarget ?? ev.target; + if (target == null) return; emojiPicker.show( - ev.currentTarget ?? ev.target, + target as HTMLElement, emoji => { insertTextAtCursor(textareaEl.value, emoji); }, @@ -875,6 +913,7 @@ async function insertEmoji(ev: MouseEvent) { } async function insertMfmFunction(ev: MouseEvent) { + if (textareaEl.value == null) return; mfmFunctionPicker( ev.currentTarget ?? ev.target, textareaEl.value, @@ -882,14 +921,15 @@ async function insertMfmFunction(ev: MouseEvent) { ); } -function showActions(ev) { +function showActions(ev: MouseEvent) { os.popupMenu(postFormActions.map(action => ({ text: action.title, action: () => { action.handler({ text: text.value, cw: cw.value, - }, (key, value) => { + }, (key, value: any) => { + if (typeof key !== 'string') return; if (key === 'text') { text.value = value; } if (key === 'cw') { useCw.value = value !== null; cw.value = value; } }); @@ -926,9 +966,9 @@ onMounted(() => { } // TODO: detach when unmount - new Autocomplete(textareaEl.value, text); - new Autocomplete(cwInputEl.value, cw); - new Autocomplete(hashtagsInputEl.value, hashtags); + if (textareaEl.value) new Autocomplete(textareaEl.value, text); + if (cwInputEl.value) new Autocomplete(cwInputEl.value, cw); + if (hashtagsInputEl.value) new Autocomplete(hashtagsInputEl.value, hashtags); nextTick(() => { // 書ãã‹ã‘ã®æŠ•ç¨¿ã‚’復元 @@ -958,19 +998,19 @@ onMounted(() => { if (props.initialNote) { const init = props.initialNote; text.value = init.text ? init.text : ''; - files.value = init.files; - cw.value = init.cw; + files.value = init.files ?? []; + cw.value = init.cw ?? null; useCw.value = init.cw != null; if (init.poll) { poll.value = { choices: init.poll.choices.map(x => x.text), multiple: init.poll.multiple, - expiresAt: init.poll.expiresAt ? new Date(init.poll.expiresAt).getTime().toString() : null, - expiredAfter: init.poll.expiredAfter ? new Date(init.poll.expiredAfter).getTime().toString() : null, + expiresAt: init.poll.expiresAt ? (new Date(init.poll.expiresAt)).getTime() : null, + expiredAfter: null, }; } visibility.value = init.visibility; - localOnly.value = init.localOnly; + localOnly.value = init.localOnly ?? false; quoteId.value = init.renote ? init.renote.id : null; } diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue index b2597d090ba2441c7938fcda065fd1a3d32406f5..956dad802131cef6dd7bd88544b68a41fb5238fb 100644 --- a/packages/frontend/src/components/MkPostFormAttaches.vue +++ b/packages/frontend/src/components/MkPostFormAttaches.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -24,6 +24,7 @@ import { defineAsyncComponent, inject } from 'vue'; import * as Misskey from 'misskey-js'; import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); @@ -55,13 +56,30 @@ function detachMedia(id: string) { } } +async function detachAndDeleteMedia(file: Misskey.entities.DriveFile) { + if (mock) return; + + detachMedia(file.id); + + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.t('driveFileDeleteConfirm', { name: file.name }), + }); + + if (canceled) return; + + os.apiWithDialog('drive/files/delete', { + fileId: file.id, + }); +} + function toggleSensitive(file) { if (mock) { emit('changeSensitive', file, !file.isSensitive); return; } - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: file.id, isSensitive: !file.isSensitive, }).then(() => { @@ -75,10 +93,10 @@ async function rename(file) { const { canceled, result } = await os.inputText({ title: i18n.ts.enterFileName, default: file.name, - allowEmpty: false, + minLength: 1, }); if (canceled) return; - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: file.id, name: result, }).then(() => { @@ -96,7 +114,7 @@ async function describe(file) { }, { done: caption => { let comment = caption.length === 0 ? null : caption; - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: file.id, comment: comment, }).then(() => { @@ -134,9 +152,16 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent): void { icon: 'ph-crop ph-bold ph-lg', action: () : void => { crop(file); }, }] : [], { + type: 'divider', + }, { text: i18n.ts.attachCancel, icon: 'ph-x-circle ph-bold ph-lg', action: () => { detachMedia(file.id); }, + }, { + text: i18n.ts.deleteFile, + icon: 'ph-trash ph-bold ph-lg', + danger: true, + action: () => { detachAndDeleteMedia(file); }, }], ev.currentTarget ?? ev.target).then(() => menuShowing = false); menuShowing = true; } diff --git a/packages/frontend/src/components/MkPostFormDialog.vue b/packages/frontend/src/components/MkPostFormDialog.vue index cd25077bfbb83394626315127db2b2d8af27edfb..5260ac2a082971e305e55d03ef468520d47bf272 100644 --- a/packages/frontend/src/components/MkPostFormDialog.vue +++ b/packages/frontend/src/components/MkPostFormDialog.vue @@ -1,11 +1,11 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkModal ref="modal" :preferType="'dialog'" @click="modal.close()" @closed="onModalClosed()"> - <MkPostForm ref="form" :class="$style.form" v-bind="props" autofocus freezeAfterPosted @posted="onPosted" @cancel="modal.close()" @esc="modal.close()"/> +<MkModal ref="modal" :preferType="'dialog'" @click="modal?.close()" @closed="onModalClosed()"> + <MkPostForm ref="form" :class="$style.form" v-bind="props" autofocus freezeAfterPosted @posted="onPosted" @cancel="modal?.close()" @esc="modal?.close()"/> </MkModal> </template> @@ -20,13 +20,13 @@ const props = defineProps<{ renote?: Misskey.entities.Note; channel?: any; // TODO mention?: Misskey.entities.User; - specified?: Misskey.entities.User; + specified?: Misskey.entities.UserDetailed; initialText?: string; initialCw?: string; - initialVisibility?: typeof Misskey.noteVisibilities; + initialVisibility?: (typeof Misskey.noteVisibilities)[number]; initialFiles?: Misskey.entities.DriveFile[]; initialLocalOnly?: boolean; - initialVisibleUsers?: Misskey.entities.User[]; + initialVisibleUsers?: Misskey.entities.UserDetailed[]; initialNote?: Misskey.entities.Note; instant?: boolean; fixed?: boolean; @@ -42,7 +42,7 @@ const modal = shallowRef<InstanceType<typeof MkModal>>(); const form = shallowRef<InstanceType<typeof MkPostForm>>(); function onPosted() { - modal.value.close({ + modal.value?.close({ useSendAnimation: true, }); } diff --git a/packages/frontend/src/components/MkPullToRefresh.vue b/packages/frontend/src/components/MkPullToRefresh.vue index e96369799798fd5312cd01d757dd2fca29ae8a6f..b1ec440e42cec8a24ae7123f6915f8d9471f3b37 100644 --- a/packages/frontend/src/components/MkPullToRefresh.vue +++ b/packages/frontend/src/components/MkPullToRefresh.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -26,6 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { onMounted, onUnmounted, ref, shallowRef } from 'vue'; import { i18n } from '@/i18n.js'; import { getScrollContainer } from '@/scripts/scroll.js'; +import { isHorizontalSwipeSwiping } from '@/scripts/touch.js'; const SCROLL_STOP = 10; const MAX_PULL_DISTANCE = Infinity; @@ -129,7 +130,7 @@ function moveEnd() { function moving(event: TouchEvent | PointerEvent) { if (!isPullStart.value || isRefreshing.value || disabled) return; - if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? SCROLL_STOP : SCROLL_STOP + pullDistance.value)) { + if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? SCROLL_STOP : SCROLL_STOP + pullDistance.value) || isHorizontalSwipeSwiping.value) { pullDistance.value = 0; isPullEnd.value = false; moveEnd(); @@ -148,6 +149,10 @@ function moving(event: TouchEvent | PointerEvent) { if (event.cancelable) event.preventDefault(); } + if (pullDistance.value > SCROLL_STOP) { + event.stopPropagation(); + } + isPullEnd.value = pullDistance.value >= FIRE_THRESHOLD; } diff --git a/packages/frontend/src/components/MkPushNotificationAllowButton.vue b/packages/frontend/src/components/MkPushNotificationAllowButton.vue index ebbd5e6cdcc40cb16fec8b1301b484269fb56314..5e42df479508459893d7e655eb8a7184bc025700 100644 --- a/packages/frontend/src/components/MkPushNotificationAllowButton.vue +++ b/packages/frontend/src/components/MkPushNotificationAllowButton.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -45,7 +45,8 @@ import { ref } from 'vue'; import { $i, getAccounts } from '@/account.js'; import MkButton from '@/components/MkButton.vue'; import { instance } from '@/instance.js'; -import { api, apiWithDialog, promiseDialog } from '@/os.js'; +import { apiWithDialog, promiseDialog } from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; defineProps<{ @@ -82,7 +83,7 @@ function subscribe() { pushSubscription.value = subscription; // Register - pushRegistrationInServer.value = await api('sw/register', { + pushRegistrationInServer.value = await misskeyApi('sw/register', { endpoint: subscription.endpoint, auth: encode(subscription.getKey('auth')), publickey: encode(subscription.getKey('p256dh')), @@ -125,7 +126,7 @@ async function unsubscribe() { } function encode(buffer: ArrayBuffer | null) { - return btoa(String.fromCharCode.apply(null, new Uint8Array(buffer))); + return btoa(String.fromCharCode.apply(null, buffer ? new Uint8Array(buffer) as any : [])); } /** @@ -159,7 +160,7 @@ if (navigator.serviceWorker == null) { supported.value = true; if (pushSubscription.value) { - const res = await api('sw/show-registration', { + const res = await misskeyApi('sw/show-registration', { endpoint: pushSubscription.value.endpoint, }); diff --git a/packages/frontend/src/components/MkRadio.vue b/packages/frontend/src/components/MkRadio.vue index edb3abe5f7bd00af4d07da6fd52b89845641c8ee..0b4023f2542fda9f589bfdc7c56b109daa231cc6 100644 --- a/packages/frontend/src/components/MkRadio.vue +++ b/packages/frontend/src/components/MkRadio.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkRadios.vue b/packages/frontend/src/components/MkRadios.vue index d9178f33625d054d5b3db428766fd41289a3c11a..549438f61b35ec2f880f0331be69047134212e54 100644 --- a/packages/frontend/src/components/MkRadios.vue +++ b/packages/frontend/src/components/MkRadios.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -18,6 +18,9 @@ export default defineComponent({ watch(value, () => { context.emit('update:modelValue', value.value); }); + watch(() => props.modelValue, v => { + value.value = v; + }); if (!context.slots.default) return null; let options = context.slots.default(); const label = context.slots.label && context.slots.label(); @@ -35,7 +38,7 @@ export default defineComponent({ h('div', { class: 'body', }, options.map(option => h(MkRadio, { - key: option.key, + key: option.key as string, value: option.props?.value, modelValue: value.value, 'onUpdate:modelValue': _v => value.value = _v, diff --git a/packages/frontend/src/components/MkRange.vue b/packages/frontend/src/components/MkRange.vue index c1f5b6a790026cdea2733cb55f49946381cceea1..46d76e255165de3bec452c1ecb8fb4ed4fa858fd 100644 --- a/packages/frontend/src/components/MkRange.vue +++ b/packages/frontend/src/components/MkRange.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -43,6 +43,7 @@ const props = withDefaults(defineProps<{ const emit = defineEmits<{ (ev: 'update:modelValue', value: number): void; + (ev: 'dragEnded', value: number): void; }>(); const containerEl = shallowRef<HTMLElement>(); @@ -85,7 +86,7 @@ onMounted(() => { ro = new ResizeObserver((entries, observer) => { calcThumbPosition(); }); - ro.observe(containerEl.value); + if (containerEl.value) ro.observe(containerEl.value); }); onUnmounted(() => { @@ -121,7 +122,7 @@ const onMousedown = (ev: MouseEvent | TouchEvent) => { const onDrag = (ev: MouseEvent | TouchEvent) => { ev.preventDefault(); const containerRect = containerEl.value!.getBoundingClientRect(); - const pointerX = ev.touches && ev.touches.length > 0 ? ev.touches[0].clientX : ev.clientX; + const pointerX = 'touches' in ev && ev.touches.length > 0 ? ev.touches[0].clientX : 'clientX' in ev ? ev.clientX : 0; const pointerPositionOnContainer = pointerX - (containerRect.left + (thumbWidth / 2)); rawValue.value = Math.min(1, Math.max(0, pointerPositionOnContainer / (containerEl.value!.offsetWidth - thumbWidth))); @@ -143,6 +144,7 @@ const onMousedown = (ev: MouseEvent | TouchEvent) => { // 値ãŒå¤‰ã‚ã£ã¦ãŸã‚‰é€šçŸ¥ if (beforeValue !== finalValue.value) { emit('update:modelValue', finalValue.value); + emit('dragEnded', finalValue.value); } }; diff --git a/packages/frontend/src/components/MkReactionEffect.vue b/packages/frontend/src/components/MkReactionEffect.vue index 75eb91e7ad3bab4d6c81239e8a897795dc927d6f..361e246e9fbb91844b33bee184cf810042f3c968 100644 --- a/packages/frontend/src/components/MkReactionEffect.vue +++ b/packages/frontend/src/components/MkReactionEffect.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkReactionIcon.vue b/packages/frontend/src/components/MkReactionIcon.vue index fdc3bfd23c295edd7d4cb3368594964bffa5b919..068a2968dbe7dd5aa63aa15283ea7e01a4fde865 100644 --- a/packages/frontend/src/components/MkReactionIcon.vue +++ b/packages/frontend/src/components/MkReactionIcon.vue @@ -1,10 +1,10 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkCustomEmoji v-if="reaction[0] === ':'" ref="elRef" :name="reaction" :normal="true" :noStyle="noStyle" :url="emojiUrl"/> +<MkCustomEmoji v-if="reaction[0] === ':'" ref="elRef" :name="reaction" :normal="true" :noStyle="noStyle" :url="emojiUrl" :fallbackToImage="true"/> <MkEmoji v-else ref="elRef" :emoji="reaction" :normal="true" :noStyle="noStyle"/> </template> diff --git a/packages/frontend/src/components/MkReactionTooltip.vue b/packages/frontend/src/components/MkReactionTooltip.vue index 8527b453479a4d5dec111c220a702d31a425541e..15409a216a300cb814b45f321ca7d4a7a4069201 100644 --- a/packages/frontend/src/components/MkReactionTooltip.vue +++ b/packages/frontend/src/components/MkReactionTooltip.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkReactionsViewer.details.vue b/packages/frontend/src/components/MkReactionsViewer.details.vue index 1b0d8f74a325718b2e90c80ccfc5aee4c05d76f6..8b5e6efdf3c690135e15dd2830a01a329c75cae5 100644 --- a/packages/frontend/src/components/MkReactionsViewer.details.vue +++ b/packages/frontend/src/components/MkReactionsViewer.details.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -44,7 +44,7 @@ function getReactionName(reaction: string): string { if (trimLocal.startsWith(':')) { return trimLocal; } - return getEmojiName(reaction) ?? reaction; + return getEmojiName(reaction); } </script> diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index 09e864e497116dd83a2443b16687cb2dfa46c1c9..2464d21b6a5e5f50d6e51b24f260aaaeb7d6e75a 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -10,8 +10,9 @@ SPDX-License-Identifier: AGPL-3.0-only class="_button" :class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]" @click="toggleReaction()" + @contextmenu.prevent.stop="menu" > - <MkReactionIcon :class="defaultStore.state.limitWidthOfReaction ? $style.limitWidth : ''" :reaction="reaction" :emojiUrl="note.reactionEmojis[reaction.substring(1, reaction.length - 1)]" @click="toggleReaction()"/> + <MkReactionIcon :class="defaultStore.state.limitWidthOfReaction ? $style.limitWidth : ''" :reaction="reaction" :emojiUrl="note.reactionEmojis[reaction.substring(1, reaction.length - 1)]" @click="toggleReaction()" @click.stop/> <span :class="$style.count">{{ count }}</span> </button> </template> @@ -19,9 +20,11 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { computed, inject, onMounted, shallowRef, watch } from 'vue'; import * as Misskey from 'misskey-js'; +import MkCustomEmojiDetailedDialog from './MkCustomEmojiDetailedDialog.vue'; import XDetails from '@/components/MkReactionsViewer.details.vue'; import MkReactionIcon from '@/components/MkReactionIcon.vue'; import * as os from '@/os.js'; +import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js'; import { useTooltip } from '@/scripts/use-tooltip.js'; import { $i } from '@/account.js'; import MkReactionEffect from '@/components/MkReactionEffect.vue'; @@ -29,6 +32,9 @@ import { claimAchievement } from '@/scripts/achievements.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import * as sound from '@/scripts/sound.js'; +import { checkReactionPermissions } from '@/scripts/check-reaction-permissions.js'; +import { customEmojisMap } from '@/custom-emojis.js'; +import { getUnicodeEmoji } from '@/scripts/emojilist.js'; const props = defineProps<{ reaction: string; @@ -45,13 +51,17 @@ const emit = defineEmits<{ const buttonEl = shallowRef<HTMLElement>(); -const canToggle = computed(() => !props.reaction.match(/@\w/) && $i); +const emojiName = computed(() => props.reaction.replace(/:/g, '').replace(/@\./, '')); +const emoji = computed(() => customEmojisMap.get(emojiName.value) ?? getUnicodeEmoji(props.reaction)); + +const canToggle = computed(() => { + return !props.reaction.match(/@\w/) && $i && emoji.value && checkReactionPermissions($i, props.note, emoji.value); +}); +const canGetInfo = computed(() => !props.reaction.match(/@\w/) && props.reaction.includes(':')); async function toggleReaction() { if (!canToggle.value) return; - // TODO: ãã®çµµæ–‡å—を使ã†æ¨©é™ãŒã‚ã‚‹ã‹ã©ã†ã‹ç¢ºèª - const oldReaction = props.note.myReaction; if (oldReaction) { const confirm = await os.confirm({ @@ -61,7 +71,7 @@ async function toggleReaction() { if (confirm.canceled) return; if (oldReaction !== props.reaction) { - sound.play('reaction'); + sound.playMisskeySfx('reaction'); } if (mock) { @@ -69,25 +79,25 @@ async function toggleReaction() { return; } - os.api('notes/reactions/delete', { + misskeyApi('notes/reactions/delete', { noteId: props.note.id, }).then(() => { if (oldReaction !== props.reaction) { - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: props.note.id, reaction: props.reaction, }); } }); } else { - sound.play('reaction'); + sound.playMisskeySfx('reaction'); if (mock) { emit('reactionToggled', props.reaction, (props.count + 1)); return; } - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: props.note.id, reaction: props.reaction, }); @@ -97,9 +107,24 @@ async function toggleReaction() { } } +async function menu(ev) { + if (!canGetInfo.value) return; + + os.popupMenu([{ + text: i18n.ts.info, + icon: 'ph-info ph-bold ph-lg', + action: async () => { + os.popup(MkCustomEmojiDetailedDialog, { + emoji: await misskeyApiGet('emoji', { + name: props.reaction.replace(/:/g, '').replace(/@\./, ''), + }), + }); + }, + }], ev.currentTarget ?? ev.target); +} + function anime() { - if (document.hidden) return; - if (!defaultStore.state.animation) return; + if (document.hidden || !defaultStore.state.animation || buttonEl.value == null) return; const rect = buttonEl.value.getBoundingClientRect(); const x = rect.left + 16; @@ -117,7 +142,7 @@ onMounted(() => { if (!mock) { useTooltip(buttonEl, async (showing) => { - const reactions = await os.apiGet('notes/reactions', { + const reactions = await misskeyApiGet('notes/reactions', { noteId: props.note.id, type: props.reaction, limit: 10, diff --git a/packages/frontend/src/components/MkReactionsViewer.vue b/packages/frontend/src/components/MkReactionsViewer.vue index d2a5c431fe0e0c678dd82f6d160741019c11c6f0..3d3130cd51b63e1892803ea1febc2c5d14471d62 100644 --- a/packages/frontend/src/components/MkReactionsViewer.vue +++ b/packages/frontend/src/components/MkReactionsViewer.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkRemoteCaution.vue b/packages/frontend/src/components/MkRemoteCaution.vue index e8ca9260bb2afd116ef8379cc0ed7c15ddd7c981..5106cdfd6a7d92d19ee2428ff390381f8b99df28 100644 --- a/packages/frontend/src/components/MkRemoteCaution.vue +++ b/packages/frontend/src/components/MkRemoteCaution.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkRetentionHeatmap.vue b/packages/frontend/src/components/MkRetentionHeatmap.vue index e69aa1be804db3c90632b778b5dda68ce00e6ce9..64b573c4d3c0682de0f22304c08af6acaa67e7c1 100644 --- a/packages/frontend/src/components/MkRetentionHeatmap.vue +++ b/packages/frontend/src/components/MkRetentionHeatmap.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onMounted, nextTick, shallowRef, ref } from 'vue'; import { Chart } from 'chart.js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import { alpha } from '@/scripts/color.js'; @@ -23,10 +23,9 @@ import { initChart } from '@/scripts/init-chart.js'; initChart(); -const rootEl = shallowRef<HTMLDivElement>(null); -const chartEl = shallowRef<HTMLCanvasElement>(null); -const now = new Date(); -let chartInstance: Chart = null; +const rootEl = shallowRef<HTMLDivElement | null>(null); +const chartEl = shallowRef<HTMLCanvasElement | null>(null); +let chartInstance: Chart | null = null; const fetching = ref(true); const { handler: externalTooltipHandler } = useChartTooltip({ @@ -34,6 +33,7 @@ const { handler: externalTooltipHandler } = useChartTooltip({ }); async function renderChart() { + if (rootEl.value == null) return; if (chartInstance) { chartInstance.destroy(); } @@ -43,11 +43,16 @@ async function renderChart() { const maxDays = wide ? 10 : narrow ? 5 : 7; - let raw = await os.api('retention', { }); + let raw = await misskeyApi('retention', { }); raw = raw.slice(0, maxDays + 1); - const data = []; + const data: { + x: number; + y: string; + v: number; + }[] = []; + for (const record of raw) { data.push({ x: 0, @@ -83,19 +88,20 @@ async function renderChart() { const marginEachCell = 12; + if (chartEl.value == null) return; + chartInstance = new Chart(chartEl.value, { type: 'matrix', data: { datasets: [{ label: 'Active', - data: data, - pointRadius: 0, + data: data as any, borderWidth: 0, - borderJoinStyle: 'round', borderRadius: 3, backgroundColor(c) { - const value = c.dataset.data[c.dataIndex].v; - const m = max(c.dataset.data[c.dataIndex].y); + const v = c.dataset.data[c.dataIndex] as unknown as typeof data[0]; + const value = v.v; + const m = max(v.y); if (m === 0) { return alpha(color, 0); } else { @@ -103,7 +109,6 @@ async function renderChart() { return alpha(color, a); } }, - fill: true, width(c) { const a = c.chart.chartArea ?? {}; return (a.right - a.left) / maxDays - marginEachCell; @@ -146,7 +151,6 @@ async function renderChart() { }, y: { type: 'time', - min: new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate() - maxDays), offset: true, reverse: true, position: 'left', @@ -179,7 +183,7 @@ async function renderChart() { return getYYYYMMDD(new Date(new Date(v.y).getTime() + (v.x * 86400000))); }, label(context) { - const v = context.dataset.data[context.dataIndex]; + const v = context.dataset.data[context.dataIndex] as unknown as typeof data[0]; const m = max(v.y); if (m === 0) { return [`Active: ${v.v} (-%)`]; diff --git a/packages/frontend/src/components/MkRetentionLineChart.vue b/packages/frontend/src/components/MkRetentionLineChart.vue index e2682ec06b331b46b670106af47448dc73d6225c..c3daa9c9a42c314b2afd9a8f58e972b529c0d6c9 100644 --- a/packages/frontend/src/components/MkRetentionLineChart.vue +++ b/packages/frontend/src/components/MkRetentionLineChart.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -16,15 +16,15 @@ import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import { chartVLine } from '@/scripts/chart-vline.js'; import { alpha } from '@/scripts/color.js'; import { initChart } from '@/scripts/init-chart.js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; initChart(); -const chartEl = shallowRef<HTMLCanvasElement>(null); +const chartEl = shallowRef<HTMLCanvasElement | null>(null); const { handler: externalTooltipHandler } = useChartTooltip(); -let chartInstance: Chart; +let chartInstance: Chart | null = null; const getYYYYMMDD = (date: Date) => { const y = date.getFullYear().toString().padStart(2, '0'); @@ -40,13 +40,15 @@ const getDate = (ymd: string) => { }; onMounted(async () => { - let raw = await os.api('retention', { }); + let raw = await misskeyApi('retention', { }); const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent')); const color = accent.toHex(); + if (chartEl.value == null) return; + chartInstance = new Chart(chartEl.value, { type: 'line', data: { @@ -67,7 +69,7 @@ onMounted(async () => { x: (i + 1).toString(), y: (v / record.users) * 100, d: getYYYYMMDD(new Date(record.createdAt)), - }))], + }))] as any, })), }, options: { @@ -109,11 +111,11 @@ onMounted(async () => { enabled: false, callbacks: { title(context) { - const v = context[0].dataset.data[context[0].dataIndex]; + const v = context[0].dataset.data[context[0].dataIndex] as unknown as { x: string, y: number, d: string }; return `${v.x} days later`; }, label(context) { - const v = context.dataset.data[context.dataIndex]; + const v = context.dataset.data[context.dataIndex] as unknown as { x: string, y: number, d: string }; const p = Math.round(v.y) + '%'; return `${v.d} ${p}`; }, diff --git a/packages/frontend/src/components/MkRippleEffect.vue b/packages/frontend/src/components/MkRippleEffect.vue index 860b083327ec154ce68b4fec9968b0536e921ce8..ee5bb73ebfda97c78b085711e0ed5ae398dd5b9f 100644 --- a/packages/frontend/src/components/MkRippleEffect.vue +++ b/packages/frontend/src/components/MkRippleEffect.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -77,7 +77,14 @@ const emit = defineEmits<{ (ev: 'end'): void; }>(); -const particles = []; +const particles: { + size: number; + xA: number; + yA: number; + xB: number; + yB: number; + color: string; +}[] = []; const origin = 64; const colors = ['#FF1493', '#00FFFF', '#FFE202']; const zIndex = os.claimZIndex('high'); diff --git a/packages/frontend/src/components/MkRolePreview.vue b/packages/frontend/src/components/MkRolePreview.vue index bd1767155b8667a2dc23555108f9e6aeba3e21cc..f0343d499bedaee7bae426b9872d6ef3cbec12b6 100644 --- a/packages/frontend/src/components/MkRolePreview.vue +++ b/packages/frontend/src/components/MkRolePreview.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue index 665ae2b81317a7dd729adbec7dec32b2c02b61cf..ecac99ae455b117b19f8fe3f209a82703019cd34 100644 --- a/packages/frontend/src/components/MkSelect.vue +++ b/packages/frontend/src/components/MkSelect.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -27,16 +27,17 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div :class="$style.caption"><slot name="caption"></slot></div> - <MkButton v-if="manualSave && changed" primary @click="updated"><i class="ph-floppy-disk ph-bold ph-lg"></i> {{ i18n.ts.save }}</MkButton> + <MkButton v-if="manualSave && changed" primary :class="$style.save" @click="updated"><i class="ph-floppy-disk ph-bold ph-lg"></i> {{ i18n.ts.save }}</MkButton> </div> </template> <script lang="ts" setup> -import { onMounted, nextTick, ref, watch, computed, toRefs, VNode, useSlots } from 'vue'; +import { onMounted, nextTick, ref, watch, computed, toRefs, VNode, useSlots, VNodeChild } from 'vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; import { useInterval } from '@/scripts/use-interval.js'; import { i18n } from '@/i18n.js'; +import { MenuItem } from '@/types/menu.js'; const props = defineProps<{ modelValue: string | null; @@ -52,7 +53,7 @@ const props = defineProps<{ }>(); const emit = defineEmits<{ - (ev: 'change', _ev: KeyboardEvent): void; + (ev: 'changeByUser', value: string | null): void; (ev: 'update:modelValue', value: string | null): void; }>(); @@ -74,10 +75,9 @@ const height = props.large ? 39 : 36; -const focus = () => inputEl.value.focus(); +const focus = () => inputEl.value?.focus(); const onInput = (ev) => { changed.value = true; - emit('change', ev); }; const updated = () => { @@ -89,17 +89,19 @@ watch(modelValue, newValue => { v.value = newValue; }); -watch(v, newValue => { +watch(v, () => { if (!props.manualSave) { updated(); } - invalid.value = inputEl.value.validity.badInput; + invalid.value = inputEl.value?.validity.badInput ?? true; }); // ã“ã®ã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆãŒä½œæˆã•ã‚ŒãŸæ™‚ã€éžè¡¨ç¤ºçŠ¶æ…‹ã§ã‚ã‚‹å ´åˆãŒã‚ã‚‹ // éžè¡¨ç¤ºçŠ¶æ…‹ã ã¨è¦ç´ ã®å¹…ãªã©ã¯0ã«ãªã£ã¦ã—ã¾ã†ã®ã§ã€å®šæœŸçš„ã«è¨ˆç®—ã™ã‚‹ useInterval(() => { + if (inputEl.value == null) return; + if (prefixEl.value) { if (prefixEl.value.offsetWidth) { inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px'; @@ -123,35 +125,38 @@ onMounted(() => { }); }); -function show(ev: MouseEvent) { +function show() { focused.value = true; opening.value = true; - const menu = []; + const menu: MenuItem[] = []; let options = slots.default!(); const pushOption = (option: VNode) => { menu.push({ - text: option.children, - active: computed(() => v.value === option.props.value), + text: option.children as string, + active: computed(() => v.value === option.props?.value), action: () => { - v.value = option.props.value; + v.value = option.props?.value; + changed.value = true; + emit('changeByUser', v.value); }, }); }; - const scanOptions = (options: VNode[]) => { + const scanOptions = (options: VNodeChild[]) => { for (const vnode of options) { + if (typeof vnode !== 'object' || vnode === null || Array.isArray(vnode)) continue; if (vnode.type === 'optgroup') { const optgroup = vnode; menu.push({ type: 'label', - text: optgroup.props.label, + text: optgroup.props?.label, }); - scanOptions(optgroup.children); + if (Array.isArray(optgroup.children)) scanOptions(optgroup.children); } else if (Array.isArray(vnode.children)) { // 何故ã‹ãƒ•ãƒ©ã‚°ãƒ¡ãƒ³ãƒˆã«ãªã£ã¦ãã‚‹ã“ã¨ãŒã‚ã‚‹ const fragment = vnode; - scanOptions(fragment.children); + if (Array.isArray(fragment.children)) scanOptions(fragment.children); } else if (vnode.props == null) { // v-if ã§æ¡ä»¶ãŒ false ã®ã¨ãã«ã“ã†ãªã‚‹ // nop? } else { @@ -164,7 +169,7 @@ function show(ev: MouseEvent) { scanOptions(options); os.popupMenu(menu, container.value, { - width: container.value.offsetWidth, + width: container.value?.offsetWidth, onClosing: () => { opening.value = false; }, @@ -284,6 +289,10 @@ function show(ev: MouseEvent) { padding-left: 6px; } +.save { + margin: 8px 0 0 0; +} + .chevron { transition: transform 0.1s ease-out; } diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue index c884ce53ea8a6b9ece2a96c25c481ab8e390986a..dc68a9959362d04869eafad1cb8b072db090b754 100644 --- a/packages/frontend/src/components/MkSignin.vue +++ b/packages/frontend/src/components/MkSignin.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -59,6 +59,7 @@ import MkInput from '@/components/MkInput.vue'; import MkInfo from '@/components/MkInfo.vue'; import { host as configHost } from '@/config.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { login } from '@/account.js'; import { i18n } from '@/i18n.js'; @@ -95,7 +96,7 @@ const props = defineProps({ }); function onUsernameChange(): void { - os.api('users/show', { + misskeyApi('users/show', { username: username.value, }).then(userResponse => { user.value = userResponse; @@ -111,6 +112,7 @@ function onLogin(res: any): Promise<void> | void { } async function queryKey(): Promise<void> { + if (credentialRequest.value == null) return; queryingKey.value = true; await webAuthnRequest(credentialRequest.value) .catch(() => { @@ -120,7 +122,7 @@ async function queryKey(): Promise<void> { credentialRequest.value = null; queryingKey.value = false; signing.value = true; - return os.api('signin', { + return misskeyApi('signin', { username: username.value, password: password.value, credential: credential.toJSON(), @@ -142,7 +144,7 @@ function onSubmit(): void { signing.value = true; if (!totpLogin.value && user.value && user.value.twoFactorEnabled) { if (webAuthnSupported() && user.value.securityKeys) { - os.api('signin', { + misskeyApi('signin', { username: username.value, password: password.value, }).then(res => { @@ -159,7 +161,7 @@ function onSubmit(): void { signing.value = false; } } else { - os.api('signin', { + misskeyApi('signin', { username: username.value, password: password.value, token: user.value?.twoFactorEnabled ? token.value : undefined, diff --git a/packages/frontend/src/components/MkSigninDialog.vue b/packages/frontend/src/components/MkSigninDialog.vue index 6f961cff0570a6d5b776da33e693e448508dda2d..33355bb99eb9c04280837ee409678872e2c9483c 100644 --- a/packages/frontend/src/components/MkSigninDialog.vue +++ b/packages/frontend/src/components/MkSigninDialog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue index 9984b09c1a8f01db6b661dc3e95d7bca9c6ce95c..7d03381a49c14eee64bab9b2d86864a50ae64c67 100644 --- a/packages/frontend/src/components/MkSignupDialog.form.vue +++ b/packages/frontend/src/components/MkSignupDialog.form.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -67,6 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #prefix><i class="ph-chalkboard-teacher ph-bold ph-lg"></i></template> </MkInput> <MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :class="$style.captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/> + <MkCaptcha v-if="instance.enableMcaptcha" ref="mcaptcha" v-model="mCaptchaResponse" :class="$style.captcha" provider="mcaptcha" :sitekey="instance.mcaptchaSiteKey" :instanceUrl="instance.mcaptchaInstanceUrl"/> <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" :class="$style.captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/> <MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" :class="$style.captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/> <MkButton type="submit" :disabled="shouldDisableSubmitting" large gradate rounded data-cy-signup-submit style="margin: 0 auto;"> @@ -83,11 +84,13 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref, computed } from 'vue'; import { toUnicode } from 'punycode/'; +import * as Misskey from 'misskey-js'; import MkButton from './MkButton.vue'; import MkInput from './MkInput.vue'; import MkCaptcha, { type Captcha } from '@/components/MkCaptcha.vue'; import * as config from '@/config.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { login } from '@/account.js'; import { instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; @@ -99,7 +102,7 @@ const props = withDefaults(defineProps<{ }); const emit = defineEmits<{ - (ev: 'signup', user: Record<string, any>): void; + (ev: 'signup', user: Misskey.entities.SigninResponse): void; (ev: 'signupEmailPending'): void; (ev: 'approvalPending'): void; }>(); @@ -122,6 +125,7 @@ const passwordStrength = ref<'' | 'low' | 'medium' | 'high'>(''); const passwordRetypeState = ref<null | 'match' | 'not-match'>(null); const submitting = ref<boolean>(false); const hCaptchaResponse = ref<string | null>(null); +const mCaptchaResponse = ref<string | null>(null); const reCaptchaResponse = ref<string | null>(null); const turnstileResponse = ref<string | null>(null); const usernameAbortController = ref<null | AbortController>(null); @@ -130,6 +134,7 @@ const emailAbortController = ref<null | AbortController>(null); const shouldDisableSubmitting = computed((): boolean => { return submitting.value || instance.enableHcaptcha && !hCaptchaResponse.value || + instance.enableMcaptcha && !mCaptchaResponse.value || instance.enableRecaptcha && !reCaptchaResponse.value || instance.enableTurnstile && !turnstileResponse.value || instance.emailRequiredForSignup && emailState.value !== 'ok' || @@ -186,7 +191,7 @@ function onChangeUsername(): void { usernameState.value = 'wait'; usernameAbortController.value = new AbortController(); - os.api('username/available', { + misskeyApi('username/available', { username: username.value, }, undefined, usernameAbortController.value.signal).then(result => { usernameState.value = result.available ? 'ok' : 'unavailable'; @@ -209,7 +214,7 @@ function onChangeEmail(): void { emailState.value = 'wait'; emailAbortController.value = new AbortController(); - os.api('email-address/available', { + misskeyApi('email-address/available', { emailAddress: email.value, }, undefined, emailAbortController.value.signal).then(result => { emailState.value = result.available ? 'ok' : @@ -251,20 +256,22 @@ async function onSubmit(): Promise<void> { submitting.value = true; try { - await os.api('signup', { + await misskeyApi('signup', { username: username.value, password: password.value, emailAddress: email.value, invitationCode: invitationCode.value, reason: reason.value, 'hcaptcha-response': hCaptchaResponse.value, + 'm-captcha-response': mCaptchaResponse.value, 'g-recaptcha-response': reCaptchaResponse.value, + 'turnstile-response': turnstileResponse.value, }); if (instance.emailRequiredForSignup) { os.alert({ type: 'success', title: i18n.ts._signup.almostThere, - text: i18n.t('_signup.emailSent', { email: email.value }), + text: i18n.tsx._signup.emailSent({ email: email.value }), }); emit('signupEmailPending'); } else if (instance.approvalRequiredForSignup) { @@ -275,7 +282,7 @@ async function onSubmit(): Promise<void> { }); emit('approvalPending'); } else { - const res = await os.api('signin', { + const res = await misskeyApi('signin', { username: username.value, password: password.value, }); diff --git a/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts b/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts index ab26df6342f35a5925aabb55172e19c84d97b960..fcd1ffde3e94814d4ff1aa923f2e5c283dc994a4 100644 --- a/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts +++ b/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts @@ -1,11 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { expect } from '@storybook/jest'; -import { userEvent, waitFor, within } from '@storybook/testing-library'; +import { expect, userEvent, waitFor, within } from '@storybook/test'; import { StoryObj } from '@storybook/vue3'; import { onBeforeUnmount } from 'vue'; import MkSignupServerRules from './MkSignupDialog.rules.vue'; diff --git a/packages/frontend/src/components/MkSignupDialog.rules.vue b/packages/frontend/src/components/MkSignupDialog.rules.vue index bc4fec305b405de7e9ee6d25800c38c90fa0eb8e..18a9eeda23deda65bb01900fe676101bd97e9783 100644 --- a/packages/frontend/src/components/MkSignupDialog.rules.vue +++ b/packages/frontend/src/components/MkSignupDialog.rules.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #suffix><i v-if="agreeServerRules" class="ph-check ph-bold ph-lg" style="color: var(--success)"></i></template> <ol class="_gaps_s" :class="$style.rules"> - <li v-for="item in instance.serverRules" :class="$style.rule"><div :class="$style.ruleText" v-html="item"></div></li> + <li v-for="item in instance.serverRules" :class="$style.rule"><div :class="$style.ruleText" v-html="sanitizeHtml(item)"></div></li> </ol> <MkSwitch :modelValue="agreeServerRules" style="margin-top: 16px;" @update:modelValue="updateAgreeServerRules">{{ i18n.ts.agree }}</MkSwitch> @@ -34,8 +34,8 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ tosPrivacyPolicyLabel }}</template> <template #suffix><i v-if="agreeTosAndPrivacyPolicy" class="ph-check ph-bold ph-lg" style="color: var(--success)"></i></template> <div class="_gaps_s"> - <div v-if="availableTos"><a :href="instance.tosUrl" class="_link" target="_blank">{{ i18n.ts.termsOfService }} <i class="ph-arrow-square-out ph-bold ph-lg"></i></a></div> - <div v-if="availablePrivacyPolicy"><a :href="instance.privacyPolicyUrl" class="_link" target="_blank">{{ i18n.ts.privacyPolicy }} <i class="ph-arrow-square-out ph-bold ph-lg"></i></a></div> + <div v-if="availableTos"><a :href="instance.tosUrl ?? undefined" class="_link" target="_blank">{{ i18n.ts.termsOfService }} <i class="ph-arrow-square-out ph-bold ph-lg"></i></a></div> + <div v-if="availablePrivacyPolicy"><a :href="instance.privacyPolicyUrl ?? undefined" class="_link" target="_blank">{{ i18n.ts.privacyPolicy }} <i class="ph-arrow-square-out ph-bold ph-lg"></i></a></div> </div> <MkSwitch :modelValue="agreeTosAndPrivacyPolicy" style="margin-top: 16px;" @update:modelValue="updateAgreeTosAndPrivacyPolicy">{{ i18n.ts.agree }}</MkSwitch> @@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.basicNotesBeforeCreateAccount }}</template> <template #suffix><i v-if="agreeNote" class="ph-check ph-bold ph-lg" style="color: var(--success)"></i></template> - <a href="https://git.joinsharkey.org/Sharkey/JoinSharkey/src/branch/main/IMPORTANT_NOTES.md" class="_link" target="_blank">{{ i18n.ts.basicNotesBeforeCreateAccount }} <i class="ph-arrow-square-out ph-bold ph-lg"></i></a> + <a href="https://activitypub.software/TransFem-org/Sharkey/-/blob/stable/IMPORTANT_NOTES.md" class="_link" target="_blank">{{ i18n.ts.basicNotesBeforeCreateAccount }} <i class="ph-arrow-square-out ph-bold ph-lg"></i></a> <MkSwitch :modelValue="agreeNote" style="margin-top: 16px;" data-cy-signup-rules-notes-agree @update:modelValue="updateAgreeNote">{{ i18n.ts.agree }}</MkSwitch> </MkFolder> @@ -65,6 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed, ref } from 'vue'; import { instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; +import sanitizeHtml from 'sanitize-html'; import MkButton from '@/components/MkButton.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkSwitch from '@/components/MkSwitch.vue'; @@ -105,7 +106,7 @@ async function updateAgreeServerRules(v: boolean) { const confirm = await os.confirm({ type: 'question', title: i18n.ts.doYouAgree, - text: i18n.t('iHaveReadXCarefullyAndAgree', { x: i18n.ts.serverRules }), + text: i18n.tsx.iHaveReadXCarefullyAndAgree({ x: i18n.ts.serverRules }), }); if (confirm.canceled) return; agreeServerRules.value = true; @@ -119,7 +120,7 @@ async function updateAgreeTosAndPrivacyPolicy(v: boolean) { const confirm = await os.confirm({ type: 'question', title: i18n.ts.doYouAgree, - text: i18n.t('iHaveReadXCarefullyAndAgree', { + text: i18n.tsx.iHaveReadXCarefullyAndAgree({ x: tosPrivacyPolicyLabel.value, }), }); @@ -135,7 +136,7 @@ async function updateAgreeNote(v: boolean) { const confirm = await os.confirm({ type: 'question', title: i18n.ts.doYouAgree, - text: i18n.t('iHaveReadXCarefullyAndAgree', { x: i18n.ts.basicNotesBeforeCreateAccount }), + text: i18n.tsx.iHaveReadXCarefullyAndAgree({ x: i18n.ts.basicNotesBeforeCreateAccount }), }); if (confirm.canceled) return; agreeNote.value = true; diff --git a/packages/frontend/src/components/MkSignupDialog.vue b/packages/frontend/src/components/MkSignupDialog.vue index 6efdced69ff7752624f84266cbff82a70c2f307e..91e7d5dd5377706327a98ff26f7468c32e311f81 100644 --- a/packages/frontend/src/components/MkSignupDialog.vue +++ b/packages/frontend/src/components/MkSignupDialog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only ref="dialog" :width="500" :height="600" - @close="dialog.close()" + @close="dialog?.close()" @closed="$emit('closed')" > <template #header>{{ i18n.ts.signup }}</template> @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only :leaveToClass="$style.transition_x_leaveTo" > <template v-if="!isAcceptedServerRule"> - <XServerRules @done="isAcceptedServerRule = true" @cancel="dialog.close()"/> + <XServerRules @done="isAcceptedServerRule = true" @cancel="dialog?.close()"/> </template> <template v-else> <XSignup :autoSet="autoSet" @signup="onSignup" @signupEmailPending="onSignupEmailPending" @approvalPending="onApprovalPending"/> @@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { shallowRef, ref } from 'vue'; - +import * as Misskey from 'misskey-js'; import XSignup from '@/components/MkSignupDialog.form.vue'; import XServerRules from '@/components/MkSignupDialog.rules.vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; @@ -47,7 +47,7 @@ const props = withDefaults(defineProps<{ }); const emit = defineEmits<{ - (ev: 'done'): void; + (ev: 'done', res: Misskey.entities.SigninResponse): void; (ev: 'closed'): void; }>(); @@ -55,13 +55,13 @@ const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); const isAcceptedServerRule = ref(false); -function onSignup(res) { +function onSignup(res: Misskey.entities.SigninResponse) { emit('done', res); - dialog.value.close(); + dialog.value?.close(); } function onSignupEmailPending() { - dialog.value.close(); + dialog.value?.close(); } function onApprovalPending() { diff --git a/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue b/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue new file mode 100644 index 0000000000000000000000000000000000000000..7b936b656c7abd589da05bf0da95c6cc6e708ba1 --- /dev/null +++ b/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue @@ -0,0 +1,113 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div class="_panel _shadow" :class="$style.root"> + <div :class="$style.icon"> + <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-open-source" width="40" height="40" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> + <path stroke="none" d="M0 0h24v24H0z" fill="none"/> + <path d="M12 3a9 9 0 0 1 3.618 17.243l-2.193 -5.602a3 3 0 1 0 -2.849 0l-2.193 5.603a9 9 0 0 1 3.617 -17.244z"/> + </svg> + </div> + <div :class="$style.main"> + <div :class="$style.title"> + <I18n :src="i18n.ts.aboutX" tag="span"> + <template #x> + {{ instance.name ?? host }} + </template> + </I18n> + </div> + <div :class="$style.text"> + <I18n :src="i18n.ts._aboutMisskey.thisIsModifiedVersion" tag="span"> + <template #name> + {{ instance.name ?? host }} + </template> + </I18n> + <I18n :src="i18n.ts.correspondingSourceIsAvailable" tag="span"> + <template #anchor> + <MkA to="/about-sharkey" class="_link">{{ i18n.ts.aboutMisskey }}</MkA> + </template> + </I18n> + </div> + <div class="_buttons"> + <MkButton @click="close">{{ i18n.ts.gotIt }}</MkButton> + </div> + </div> + <button class="_button" :class="$style.close" @click="close"><i class="ph-x ph-bold ph-lg"></i></button> +</div> +</template> + +<script lang="ts" setup> +import MkButton from '@/components/MkButton.vue'; +import { host } from '@/config.js'; +import { i18n } from '@/i18n.js'; +import { instance } from '@/instance.js'; +import { miLocalStorage } from '@/local-storage.js'; +import * as os from '@/os.js'; + +const emit = defineEmits<{ + (ev: 'closed'): void; +}>(); + +const zIndex = os.claimZIndex('low'); + +function close() { + miLocalStorage.setItem('modifiedVersionMustProminentlyOfferInAgplV3Section13Read', 'true'); + emit('closed'); +} +</script> + +<style lang="scss" module> +.root { + position: fixed; + z-index: v-bind(zIndex); + bottom: var(--margin); + left: 0; + right: 0; + margin: auto; + box-sizing: border-box; + width: calc(100% - (var(--margin) * 2)); + max-width: 500px; + display: flex; + backdrop-filter: var(--blur, blur(15px)); +} + +.icon { + text-align: center; + padding-top: 25px; + width: 100px; + color: var(--accent); +} +@media (max-width: 500px) { + .icon { + width: 80px; + } +} +@media (max-width: 450px) { + .icon { + width: 70px; + } +} + +.main { + padding: 25px 25px 25px 0; + flex: 1; +} + +.close { + position: absolute; + top: 8px; + right: 8px; + padding: 8px; +} + +.title { + font-weight: bold; +} + +.text { + margin: 0.7em 0 1em 0; +} +</style> diff --git a/packages/frontend/src/components/MkSparkle.vue b/packages/frontend/src/components/MkSparkle.vue index 269825e25e40015df64672c774e6dd2036b1dda8..8491ce2f84075c086016a2e6d5957fd632dab377 100644 --- a/packages/frontend/src/components/MkSparkle.vue +++ b/packages/frontend/src/components/MkSparkle.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -89,10 +89,11 @@ let ro: ResizeObserver | undefined; onMounted(() => { ro = new ResizeObserver((entries, observer) => { - width.value = el.value?.offsetWidth + 64; - height.value = el.value?.offsetHeight + 64; + if (el.value == null) return; + width.value = el.value.offsetWidth + 64; + height.value = el.value.offsetHeight + 64; }); - ro.observe(el.value); + if (el.value) ro.observe(el.value); const add = () => { if (stop) return; const x = (Math.random() * (width.value - 64)); diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue index c071fb938a21a7096e09b1d0dddb745e980d630c..7e63bbe82db2ff2690b5d137c952b112d35d7bb5 100644 --- a/packages/frontend/src/components/MkSubNoteContent.vue +++ b/packages/frontend/src/components/MkSubNoteContent.vue @@ -1,13 +1,13 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <div :class="[$style.root, { [$style.collapsed]: collapsed }]"> - <div :class="{ [$style.clickToOpen]: defaultStore.state.clickToOpen }" @click="defaultStore.state.clickToOpen ? noteclick(note.id) : undefined"> + <div :class="{ [$style.clickToOpen]: defaultStore.state.clickToOpen }" @click.stop="defaultStore.state.clickToOpen ? noteclick(note.id) : undefined"> <span v-if="note.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span> - <span v-if="note.deletedAt" style="opacity: 0.5">({{ i18n.ts.deleted }})</span> + <span v-if="note.deletedAt" style="opacity: 0.5">({{ i18n.ts.deletedNote }})</span> <MkA v-if="note.replyId" :class="$style.reply" :to="`/notes/${note.replyId}`" @click.stop><i class="ph-arrow-bend-left-up ph-bold ph-lg"></i></MkA> <Mfm v-if="note.text" :text="note.text" :author="note.user" :nyaize="'respect'" :isAnim="allowAnim" :emojiUrls="note.emojis"/> <MkButton v-if="!allowAnim && animated && !hideFiles" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton> @@ -15,40 +15,40 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="note.text && translating || note.text && translation" :class="$style.translation"> <MkLoading v-if="translating" mini/> <div v-else> - <b>{{ i18n.t('translatedFrom', { x: translation.sourceLang }) }}: </b> + <b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b> <Mfm :text="translation.text" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/> </div> </div> <MkA v-if="note.renoteId" :class="$style.rp" :to="`/notes/${note.renoteId}`" @click.stop>RN: ...</MkA> </div> - <details v-if="note.files.length > 0" :open="!defaultStore.state.collapseFiles && !hideFiles"> - <summary>({{ i18n.t('withNFiles', { n: note.files.length }) }})</summary> + <details v-if="note.files && note.files.length > 0" :open="!defaultStore.state.collapseFiles && !hideFiles"> + <summary>({{ i18n.tsx.withNFiles({ n: note.files.length }) }})</summary> <MkMediaList :mediaList="note.files"/> </details> <details v-if="note.poll"> <summary>{{ i18n.ts.poll }}</summary> - <MkPoll :note="note"/> + <MkPoll :noteId="note.id" :poll="note.poll"/> </details> - <button v-if="isLong && collapsed" :class="$style.fade" class="_button" @click="collapsed = false"> + <button v-if="isLong && collapsed" :class="$style.fade" class="_button" @click.stop="collapsed = false"> <span :class="$style.fadeLabel">{{ i18n.ts.showMore }}</span> </button> - <button v-else-if="isLong && !collapsed" :class="$style.showLess" class="_button" @click="collapsed = true"> + <button v-else-if="isLong && !collapsed" :class="$style.showLess" class="_button" @click.stop="collapsed = true"> <span :class="$style.showLessLabel">{{ i18n.ts.showLess }}</span> </button> </div> </template> <script lang="ts" setup> -import { ref, computed } from 'vue'; +import { ref, computed, watch } from 'vue'; import * as Misskey from 'misskey-js'; -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; import MkMediaList from '@/components/MkMediaList.vue'; import MkPoll from '@/components/MkPoll.vue'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; import { shouldCollapsed } from '@/scripts/collapsed.js'; import { defaultStore } from '@/store.js'; -import { useRouter } from '@/router.js'; +import { useRouter } from '@/router/supplier.js'; import * as os from '@/os.js'; import { checkAnimationFromMfm } from '@/scripts/check-animated-mfm.js'; @@ -57,6 +57,7 @@ const props = defineProps<{ translating?: boolean; translation?: any; hideFiles?: boolean; + expandAllCws?: boolean; }>(); const router = useRouter(); @@ -87,6 +88,10 @@ function animatedMFM() { } const collapsed = ref(isLong); + +watch(() => props.expandAllCws, (expandAllCws) => { + if (expandAllCws) collapsed.value = false; +}); </script> <style lang="scss" module> @@ -165,5 +170,6 @@ const collapsed = ref(isLong); .clickToOpen { cursor: pointer; + -webkit-tap-highlight-color: transparent; } </style> diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue index 93296dd9d5592f00dbe208bfd63999ad65440821..2a7c72ccd95a319b9534ab59358095712927671a 100644 --- a/packages/frontend/src/components/MkSuperMenu.vue +++ b/packages/frontend/src/components/MkSuperMenu.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkSwitch.button.vue b/packages/frontend/src/components/MkSwitch.button.vue index b82f36cdd30bf520cab618263cd3febf8cfb7aef..21339d1b4e775162e2ca2ca20869d783eb8a0db8 100644 --- a/packages/frontend/src/components/MkSwitch.button.vue +++ b/packages/frontend/src/components/MkSwitch.button.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -24,7 +24,7 @@ import { i18n } from '@/i18n.js'; const props = withDefaults(defineProps<{ checked: boolean | Ref<boolean>; - disabled?: boolean; + disabled?: boolean | Ref<boolean>; }>(), { disabled: false, }); diff --git a/packages/frontend/src/components/MkSwitch.vue b/packages/frontend/src/components/MkSwitch.vue index 35e5aebbddda29f90e52477f5b9f5b94ef5056c7..5672c8e9f733d4b6b10454247448027ac4953450 100644 --- a/packages/frontend/src/components/MkSwitch.vue +++ b/packages/frontend/src/components/MkSwitch.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkTab.vue b/packages/frontend/src/components/MkTab.vue index 2b56b946d253ce661e06bcedc4a2a0c0137e89d6..54ab8fc663e37182ba08d1fd493d9b2761dbe5ac 100644 --- a/packages/frontend/src/components/MkTab.vue +++ b/packages/frontend/src/components/MkTab.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -13,18 +13,18 @@ export default defineComponent({ }, }, setup(props, { emit, slots }) { - const options = slots.default(); + const options = slots.default?.() ?? []; return () => h('div', { class: 'pxhvhrfw', }, options.map(option => withDirectives(h('button', { - class: ['_button', { active: props.modelValue === option.props.value }], - key: option.key, - disabled: props.modelValue === option.props.value, + class: ['_button', { active: props.modelValue === option.props?.value }], + key: option.key as string, + disabled: props.modelValue === option.props?.value, onClick: () => { - emit('update:modelValue', option.props.value); + emit('update:modelValue', option.props?.value); }, - }, option.children), [ + }, option.children ?? []), [ [resolveDirective('click-anime')], ]))); }, diff --git a/packages/frontend/src/components/MkTagCloud.vue b/packages/frontend/src/components/MkTagCloud.vue index 083c34906ff8c71713de5026be862fb990520223..6b9c18159768d168d12e5eadd48180cf29b0ce3f 100644 --- a/packages/frontend/src/components/MkTagCloud.vue +++ b/packages/frontend/src/components/MkTagCloud.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -52,7 +52,7 @@ watch(available, () => { }); onMounted(() => { - width.value = rootEl.value.offsetWidth; + if (rootEl.value) width.value = rootEl.value.offsetWidth; if (loaded) { available.value = true; diff --git a/packages/frontend/src/components/MkTextarea.vue b/packages/frontend/src/components/MkTextarea.vue index 5c70adde11f28b9a7b3654f1036c4dc1c9c5e201..3082842699ecc5fe958fceaaf7aef3ce7395cd78 100644 --- a/packages/frontend/src/components/MkTextarea.vue +++ b/packages/frontend/src/components/MkTextarea.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only :readonly="readonly" :placeholder="placeholder" :pattern="pattern" - :autocomplete="props.autocomplete" + :autocomplete="autocomplete" :spellcheck="spellcheck" @focus="focused = true" @blur="focused = false" @@ -76,9 +76,9 @@ const invalid = ref(false); const filled = computed(() => v.value !== '' && v.value != null); const inputEl = shallowRef<HTMLTextAreaElement>(); const preview = ref(false); -let autocomplete: Autocomplete; +let autocompleteWorker: Autocomplete | null = null; -const focus = () => inputEl.value.focus(); +const focus = () => inputEl.value?.focus(); const onInput = (ev) => { changed.value = true; emit('change', ev); @@ -111,10 +111,10 @@ const updated = () => { const debouncedUpdated = debounce(1000, updated); watch(modelValue, newValue => { - v.value = newValue; + v.value = newValue ?? ''; }); -watch(v, newValue => { +watch(v, () => { if (!props.manualSave) { if (props.debounce) { debouncedUpdated(); @@ -123,7 +123,7 @@ watch(v, newValue => { } } - invalid.value = inputEl.value.validity.badInput; + invalid.value = inputEl.value?.validity.badInput ?? true; }); onMounted(() => { @@ -133,14 +133,14 @@ onMounted(() => { } }); - if (props.mfmAutocomplete) { - autocomplete = new Autocomplete(inputEl.value, v, props.mfmAutocomplete === true ? null : props.mfmAutocomplete); + if (props.mfmAutocomplete && inputEl.value) { + autocompleteWorker = new Autocomplete(inputEl.value, v, props.mfmAutocomplete === true ? undefined : props.mfmAutocomplete); } }); onUnmounted(() => { - if (autocomplete) { - autocomplete.detach(); + if (autocompleteWorker) { + autocompleteWorker.detach(); } }); </script> diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 8bd68c0fd2a748054703637d0c80504b91d226e7..1c14174a37d4eef6138e5ce156902df1fe778960 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -11,14 +11,14 @@ SPDX-License-Identifier: AGPL-3.0-only :pagination="paginationQuery" :noGap="!defaultStore.state.showGapBetweenNotesInTimeline" @queue="emit('queue', $event)" - @status="prComponent.setDisabled($event)" + @status="prComponent?.setDisabled($event)" /> </MkPullToRefresh> </template> <script lang="ts" setup> -import { computed, watch, onUnmounted, provide, ref } from 'vue'; -import { Connection } from 'misskey-js/built/streaming.js'; +import { computed, watch, onUnmounted, provide, ref, shallowRef } from 'vue'; +import * as Misskey from 'misskey-js'; import MkNotes from '@/components/MkNotes.vue'; import MkPullToRefresh from '@/components/MkPullToRefresh.vue'; import { useStream } from '@/stream.js'; @@ -29,7 +29,7 @@ import { defaultStore } from '@/store.js'; import { Paging } from '@/components/MkPagination.vue'; const props = withDefaults(defineProps<{ - src: string; + src: 'home' | 'local' | 'social' | 'bubble' | 'global' | 'mentions' | 'directs' | 'list' | 'antenna' | 'channel' | 'role'; list?: string; antenna?: string; channel?: string; @@ -51,6 +51,7 @@ const emit = defineEmits<{ (ev: 'queue', count: number): void; }>(); +provide('inTimeline', true); provide('inChannel', computed(() => props.src === 'channel')); type TimelineQueryType = { @@ -65,12 +66,14 @@ type TimelineQueryType = { roleId?: string } -const prComponent = ref<InstanceType<typeof MkPullToRefresh>>(); -const tlComponent = ref<InstanceType<typeof MkNotes>>(); +const prComponent = shallowRef<InstanceType<typeof MkPullToRefresh>>(); +const tlComponent = shallowRef<InstanceType<typeof MkNotes>>(); let tlNotesCount = 0; -const prepend = note => { +function prepend(note) { + if (tlComponent.value == null) return; + tlNotesCount++; if (instance.notesPerOneAd > 0 && tlNotesCount % instance.notesPerOneAd === 0) { @@ -82,18 +85,19 @@ const prepend = note => { emit('note'); if (props.sound) { - sound.play($i && (note.userId === $i.id) ? 'noteMy' : 'note'); + sound.playMisskeySfx($i && (note.userId === $i.id) ? 'noteMy' : 'note'); } -}; +} -let connection: Connection; -let connection2: Connection; +let connection: Misskey.ChannelConnection | null = null; +let connection2: Misskey.ChannelConnection | null = null; let paginationQuery: Paging | null = null; const stream = useStream(); function connectChannel() { if (props.src === 'antenna') { + if (props.antenna == null) return; connection = stream.useChannel('antenna', { antennaId: props.antenna, }); @@ -141,20 +145,24 @@ function connectChannel() { connection = stream.useChannel('main'); connection.on('mention', onNote); } else if (props.src === 'list') { + if (props.list == null) return; connection = stream.useChannel('userList', { + withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined, listId: props.list, }); } else if (props.src === 'channel') { + if (props.channel == null) return; connection = stream.useChannel('channel', { channelId: props.channel, }); } else if (props.src === 'role') { + if (props.role == null) return; connection = stream.useChannel('roleTimeline', { roleId: props.role, }); } - if (props.src !== 'directs' || props.src !== 'mentions') connection.on('note', prepend); + if (props.src !== 'directs' && props.src !== 'mentions') connection?.on('note', prepend); } function disconnectChannel() { @@ -163,7 +171,7 @@ function disconnectChannel() { } function updatePaginationQuery() { - let endpoint: string | null; + let endpoint: keyof Misskey.Endpoints | null; let query: TimelineQueryType | null; if (props.src === 'antenna') { @@ -219,6 +227,7 @@ function updatePaginationQuery() { } else if (props.src === 'list') { endpoint = 'notes/user-list-timeline'; query = { + withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined, listId: props.list, }; @@ -257,8 +266,9 @@ function refreshEndpointAndChannel() { updatePaginationQuery(); } +// デッã‚ã®ãƒªã‚¹ãƒˆã‚«ãƒ©ãƒ ã§withRenotesを変更ã—ãŸå ´åˆã«è‡ªå‹•çš„ã«æ›´æ–°ã•ã‚Œã‚‹ã‚ˆã†ã«ã•ã›ã‚‹ // IDãŒåˆ‡ã‚Šæ›¿ã‚ã£ãŸã‚‰åˆ‡ã‚Šæ›¿ãˆå…ˆã®TLを表示ã•ã›ãŸã„ -watch(() => [props.list, props.antenna, props.channel, props.role], refreshEndpointAndChannel); +watch(() => [props.list, props.antenna, props.channel, props.role, props.withRenotes], refreshEndpointAndChannel); // åˆå›žè¡¨ç¤ºç”¨ refreshEndpointAndChannel(); @@ -269,6 +279,8 @@ onUnmounted(() => { function reloadTimeline() { return new Promise<void>((res) => { + if (tlComponent.value == null) return; + tlNotesCount = 0; tlComponent.value.pagingComponent?.reload().then(() => { diff --git a/packages/frontend/src/components/MkToast.vue b/packages/frontend/src/components/MkToast.vue index 82cd236193ae7a5ded6ac9013a2c9295952acecd..a117e493505012fd5025a51d0c7e81a2110ef967 100644 --- a/packages/frontend/src/components/MkToast.vue +++ b/packages/frontend/src/components/MkToast.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue index 8e8e26ed5f50a3cd786f68ab415baf2d1a5bcae9..b32066c95047f84fbfdfa091e1012d017db9ee00 100644 --- a/packages/frontend/src/components/MkTokenGenerateWindow.vue +++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only :withOkButton="true" :okButtonDisabled="false" :canClose="false" - @close="dialog.close()" + @close="dialog?.close()" @closed="$emit('closed')" @ok="ok()" > @@ -33,7 +33,13 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton> </div> <div class="_gaps_s"> - <MkSwitch v-for="kind in Object.keys(permissions)" :key="kind" v-model="permissions[kind]">{{ i18n.t(`_permissions.${kind}`) }}</MkSwitch> + <MkSwitch v-for="kind in Object.keys(permissionSwitches)" :key="kind" v-model="permissionSwitches[kind]">{{ i18n.ts._permissions[kind] }}</MkSwitch> + </div> + <div v-if="iAmAdmin" :class="$style.adminPermissions"> + <div :class="$style.adminPermissionsHeader"><b>{{ i18n.ts.adminPermission }}</b></div> + <div class="_gaps_s"> + <MkSwitch v-for="kind in Object.keys(permissionSwitchesForAdmin)" :key="kind" v-model="permissionSwitchesForAdmin[kind]">{{ i18n.ts._permissions[kind] }}</MkSwitch> + </div> </div> </div> </MkSpacer> @@ -49,6 +55,7 @@ import MkButton from './MkButton.vue'; import MkInfo from './MkInfo.vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; import { i18n } from '@/i18n.js'; +import { iAmAdmin } from '@/account.js'; const props = withDefaults(defineProps<{ title?: string | null; @@ -68,37 +75,76 @@ const emit = defineEmits<{ }>(); const defaultPermissions = Misskey.permissions.filter(p => !p.startsWith('read:admin') && !p.startsWith('write:admin')); +const adminPermissions = Misskey.permissions.filter(p => p.startsWith('read:admin') || p.startsWith('write:admin')); + const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); const name = ref(props.initialName); -const permissions = ref(<Record<(typeof Misskey.permissions)[number], boolean>>{}); +const permissionSwitches = ref(<Record<(typeof Misskey.permissions)[number], boolean>>{}); +const permissionSwitchesForAdmin = ref(<Record<(typeof Misskey.permissions)[number], boolean>>{}); if (props.initialPermissions) { for (const kind of props.initialPermissions) { - permissions.value[kind] = true; + permissionSwitches.value[kind] = true; } } else { for (const kind of defaultPermissions) { - permissions.value[kind] = false; + permissionSwitches.value[kind] = false; + } + + if (iAmAdmin) { + for (const kind of adminPermissions) { + permissionSwitchesForAdmin.value[kind] = false; + } } } function ok(): void { emit('done', { name: name.value, - permissions: Object.keys(permissions.value).filter(p => permissions.value[p]), + permissions: [ + ...Object.keys(permissionSwitches.value).filter(p => permissionSwitches.value[p]), + ...(iAmAdmin ? Object.keys(permissionSwitchesForAdmin.value).filter(p => permissionSwitchesForAdmin.value[p]) : []), + ], }); - dialog.value.close(); + dialog.value?.close(); } function disableAll(): void { - for (const p in permissions.value) { - permissions.value[p] = false; + for (const p in permissionSwitches.value) { + permissionSwitches.value[p] = false; + } + if (iAmAdmin) { + for (const p in permissionSwitchesForAdmin.value) { + permissionSwitchesForAdmin.value[p] = false; + } } } function enableAll(): void { - for (const p in permissions.value) { - permissions.value[p] = true; + for (const p in permissionSwitches.value) { + permissionSwitches.value[p] = true; + } + if (iAmAdmin) { + for (const p in permissionSwitchesForAdmin.value) { + permissionSwitchesForAdmin.value[p] = true; + } } } </script> + +<style module lang="scss"> +.adminPermissions { + margin: 8px -6px 0; + padding: 24px 6px 6px; + border: 2px solid var(--error); + border-radius: calc(var(--radius) / 2); +} + +.adminPermissionsHeader { + margin: -34px 0 6px 12px; + padding: 0 4px; + width: fit-content; + color: var(--error); + background: var(--panel); +} +</style> diff --git a/packages/frontend/src/components/MkTooltip.vue b/packages/frontend/src/components/MkTooltip.vue index eeb9325a290e6fbe1df0599e88b2948d895ee194..aac07008a4020e9b101a67cbf8581c8bd5dfa9dd 100644 --- a/packages/frontend/src/components/MkTooltip.vue +++ b/packages/frontend/src/components/MkTooltip.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -13,8 +13,10 @@ SPDX-License-Identifier: AGPL-3.0-only > <div v-show="showing" ref="el" :class="$style.root" class="_acrylic _shadow" :style="{ zIndex, maxWidth: maxWidth + 'px' }"> <slot> - <Mfm v-if="asMfm" :text="text"/> - <span v-else>{{ text }}</span> + <template v-if="text"> + <Mfm v-if="asMfm" :text="text"/> + <span v-else>{{ text }}</span> + </template> </slot> </div> </Transition> @@ -53,6 +55,7 @@ const el = shallowRef<HTMLElement>(); const zIndex = os.claimZIndex('high'); function setPosition() { + if (el.value == null) return; const data = calcPopupPosition(el.value, { anchorElement: props.targetElement, direction: props.direction, diff --git a/packages/frontend/src/components/MkTutorialDialog.Note.vue b/packages/frontend/src/components/MkTutorialDialog.Note.vue index 3fca958055d65279a3b75a6af74c0db4b1001fb6..5544434b5f6d469664f63adafaeb145fb76aa3ed 100644 --- a/packages/frontend/src/components/MkTutorialDialog.Note.vue +++ b/packages/frontend/src/components/MkTutorialDialog.Note.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-else-if="phase === 'howToReact'" class="_gaps"> <div style="text-align: center; padding: 0 16px;">{{ i18n.ts._initialTutorial._reaction.description }}</div> <div>{{ i18n.ts._initialTutorial._reaction.letsTryReacting }}</div> - <MkNote :class="$style.exampleNoteRoot" :note="exampleNote" :mock="true" @reaction="addReaction" @removeReaction="removeReaction" @updateReaction="updateReaction"/> + <MkNote :class="$style.exampleNoteRoot" :note="exampleNote" :mock="true" @reaction="addReaction" @removeReaction="removeReaction"/> <div v-if="onceReacted"><b style="color: var(--accent);"><i class="ph-check ph-bold ph-lg"></i> {{ i18n.ts._initialTutorial.wellDone }}</b> {{ i18n.ts._initialTutorial._reaction.reactNotification }}<br>{{ i18n.ts._initialTutorial._reaction.reactDone }}</div> </div> </template> @@ -53,7 +53,7 @@ const exampleNote = reactive<Misskey.entities.Note>({ isBot: false, isCat: true, emojis: {}, - onlineStatus: null, + onlineStatus: 'unknown', badgeRoles: [], }, text: 'just setting up my shonk', @@ -86,7 +86,6 @@ function doNotification(emoji: string): void { const notification: Misskey.entities.Notification = { id: Math.random().toString(), createdAt: new Date().toUTCString(), - isRead: false, type: 'reaction', reaction: emoji, user: $i, diff --git a/packages/frontend/src/components/MkTutorialDialog.PostNote.vue b/packages/frontend/src/components/MkTutorialDialog.PostNote.vue index f093d6d9ef2ab15656db98974ccaa8193823fb95..1771559a9b59541633c814b829c0c5727d9a318d 100644 --- a/packages/frontend/src/components/MkTutorialDialog.PostNote.vue +++ b/packages/frontend/src/components/MkTutorialDialog.PostNote.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -58,7 +58,7 @@ const exampleCWNote = reactive<Misskey.entities.Note>({ isBot: false, isCat: true, emojis: {}, - onlineStatus: null, + onlineStatus: 'unknown', badgeRoles: [], }, text: i18n.ts._initialTutorial._postNote._cw._exampleNote.note, diff --git a/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue b/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue index dd255a2214cf2d8ad5191cccea7729a25b9cad38..4b4e8ea8f86a475f7ccaa1902e46de467528c892 100644 --- a/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue +++ b/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -40,7 +40,7 @@ const emit = defineEmits<{ const onceSucceeded = ref<boolean>(false); function doSucceeded(fileId: string, to: boolean) { - if (fileId === exampleNote.fileIds[0] && to) { + if (fileId === exampleNote.fileIds?.[0] && to) { onceSucceeded.value = true; emit('succeeded'); } diff --git a/packages/frontend/src/components/MkTutorialDialog.Timeline.vue b/packages/frontend/src/components/MkTutorialDialog.Timeline.vue index c2384423fd43f0b1825389ce38c73cb4befd9482..f5670c7ebdbe836130fa60e8f3678f7430a87e98 100644 --- a/packages/frontend/src/components/MkTutorialDialog.Timeline.vue +++ b/packages/frontend/src/components/MkTutorialDialog.Timeline.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkTutorialDialog.vue b/packages/frontend/src/components/MkTutorialDialog.vue index a734f93ec99e61d97fd7510500a64b1449ad8a06..6cd7019fed0dd89db3eb2bf712a96a43fd291a53 100644 --- a/packages/frontend/src/components/MkTutorialDialog.vue +++ b/packages/frontend/src/components/MkTutorialDialog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only @close="close(true)" @closed="emit('closed')" > - <template v-if="page === 1" #header><i class="ph-pencil ph-bold pg-lg"></i> {{ i18n.ts._initialTutorial._note.title }}</template> + <template v-if="page === 1" #header><i class="ph-pencil-simple ph-bold pg-lg"></i> {{ i18n.ts._initialTutorial._note.title }}</template> <template v-else-if="page === 2" #header><i class="ph-smiley ph-bold pg-lg"></i> {{ i18n.ts._initialTutorial._reaction.title }}</template> <template v-else-if="page === 3" #header><i class="ph-house ph-bold pg-lg"></i> {{ i18n.ts._initialTutorial._timeline.title }}</template> <template v-else-if="page === 4" #header><i class="ph-plus ph-bold pg-lg"></i> {{ i18n.ts._initialTutorial._postNote.title }}</template> @@ -133,7 +133,7 @@ SPDX-License-Identifier: AGPL-3.0-only <a href="https://misskey-hub.net/docs/for-users/" target="_blank" class="_link">{{ i18n.ts.help }}</a> </template> </I18n> - <div>{{ i18n.t('_initialAccountSetting.haveFun', { name: instance.name ?? host }) }}</div> + <div>{{ i18n.tsx._initialAccountSetting.haveFun({ name: instance.name ?? host }) }}</div> <div class="_buttonsCenter" style="margin-top: 16px;"> <MkButton v-if="initialPage !== 4" rounded @click="page--"><i class="ph-arrow-left ph-bold pg-lg"></i> {{ i18n.ts.goBack }}</MkButton> <MkButton rounded primary gradate @click="close(false)">{{ i18n.ts.close }}</MkButton> diff --git a/packages/frontend/src/components/MkUpdated.vue b/packages/frontend/src/components/MkUpdated.vue index 07efaf89825e4dc7d6e255b4e7a717f081d75fab..4fb0749931dcd88e47f82100cc6df6d41bfe1385 100644 --- a/packages/frontend/src/components/MkUpdated.vue +++ b/packages/frontend/src/components/MkUpdated.vue @@ -1,15 +1,15 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkModal ref="modal" :zPriority="'middle'" @click="$refs.modal.close()" @closed="$emit('closed')"> +<MkModal ref="modal" :zPriority="'middle'" @click="modal?.close()" @closed="$emit('closed')"> <div :class="$style.root"> <div :class="$style.title"><MkSparkle>{{ i18n.ts.misskeyUpdated }}</MkSparkle></div> <div :class="$style.version">✨{{ version }}🚀</div> <MkButton full @click="whatIsNew">{{ i18n.ts.whatIsNew }}</MkButton> - <MkButton :class="$style.gotIt" primary full @click="$refs.modal.close()">{{ i18n.ts.gotIt }}</MkButton> + <MkButton :class="$style.gotIt" primary full @click="modal?.close()">{{ i18n.ts.gotIt }}</MkButton> </div> </MkModal> </template> @@ -26,8 +26,8 @@ import { confetti } from '@/scripts/confetti.js'; const modal = shallowRef<InstanceType<typeof MkModal>>(); const whatIsNew = () => { - modal.value.close(); - window.open(`https://git.joinsharkey.org/Sharkey/Sharkey/releases/tag/${version}`, '_blank'); + modal.value?.close(); + window.open(`https://activitypub.software/TransFem-org/Sharkey/-/releases/${version}`, '_blank'); }; onMounted(() => { diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue index 486aaa0bbd5624759303a164f70abee17930cb76..10ba137b9496654b4c6d5b3cccf35d3a87caf353 100644 --- a/packages/frontend/src/components/MkUrlPreview.vue +++ b/packages/frontend/src/components/MkUrlPreview.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only v-if="player.url.startsWith('http://') || player.url.startsWith('https://')" sandbox="allow-popups allow-scripts allow-storage-access-by-user-activation allow-same-origin" scrolling="no" - :allow="player.allow.join(';')" + :allow="player.allow == null ? 'autoplay;encrypted-media;fullscreen' : player.allow.filter(x => ['autoplay', 'clipboard-write', 'fullscreen', 'encrypted-media', 'picture-in-picture', 'web-share'].includes(x)).join(';')" :class="$style.playerIframe" :src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')" :style="{ border: 0 }" @@ -83,8 +83,8 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { defineAsyncComponent, onUnmounted, ref } from 'vue'; -import type { summaly } from 'summaly'; +import { defineAsyncComponent, onDeactivated, onUnmounted, ref } from 'vue'; +import type { summaly } from '@misskey-dev/summaly'; import { url as local } from '@/config.js'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; @@ -131,6 +131,10 @@ const embedId = `embed${Math.random().toString().replace(/\D/, '')}`; const tweetHeight = ref(150); const unknownUrl = ref(false); +onDeactivated(() => { + playerEnabled.value = false; +}); + const requestUrl = new URL(props.url); if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url'); diff --git a/packages/frontend/src/components/MkUrlPreviewPopup.vue b/packages/frontend/src/components/MkUrlPreviewPopup.vue index 81c383540cacf66fa499174a8feafd7257ef3c08..cf75064be7954f78870121266861794aed1b426c 100644 --- a/packages/frontend/src/components/MkUrlPreviewPopup.vue +++ b/packages/frontend/src/components/MkUrlPreviewPopup.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue index 3fbadbe34f56c14d0de7ee394c3a7c9aa1549036..13ab6fd76351a166fddc85fc5339c21b4d99f883 100644 --- a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue +++ b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkModalWindow ref="dialog" :width="400" - @close="dialog.close()" + @close="dialog?.close()" @closed="$emit('closed')" > <template v-if="announcement" #header>:{{ announcement.title }}:</template> @@ -56,6 +56,7 @@ import MkModalWindow from '@/components/MkModalWindow.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import MkTextarea from '@/components/MkTextarea.vue'; import MkSwitch from '@/components/MkSwitch.vue'; @@ -63,14 +64,14 @@ import MkRadios from '@/components/MkRadios.vue'; const props = defineProps<{ user: Misskey.entities.User, - announcement?: any, + announcement?: Misskey.entities.Announcement, }>(); const dialog = ref<InstanceType<typeof MkModalWindow> | null>(null); -const title = ref<string>(props.announcement ? props.announcement.title : ''); -const text = ref<string>(props.announcement ? props.announcement.text : ''); -const icon = ref<string>(props.announcement ? props.announcement.icon : 'info'); -const display = ref<string>(props.announcement ? props.announcement.display : 'dialog'); +const title = ref(props.announcement ? props.announcement.title : ''); +const text = ref(props.announcement ? props.announcement.text : ''); +const icon = ref(props.announcement ? props.announcement.icon : 'info'); +const display = ref(props.announcement ? props.announcement.display : 'dialog'); const needConfirmationToRead = ref(props.announcement ? props.announcement.needConfirmationToRead : false); const emit = defineEmits<{ @@ -91,18 +92,18 @@ async function done() { if (props.announcement) { await os.apiWithDialog('admin/announcements/update', { - id: props.announcement.id, ...params, + id: props.announcement.id, }); emit('done', { updated: { - id: props.announcement.id, ...params, + id: props.announcement.id, }, }); - dialog.value.close(); + dialog.value?.close(); } else { const created = await os.apiWithDialog('admin/announcements/create', params); @@ -110,25 +111,27 @@ async function done() { created: created, }); - dialog.value.close(); + dialog.value?.close(); } } async function del() { const { canceled } = await os.confirm({ type: 'warning', - text: i18n.t('removeAreYouSure', { x: title.value }), + text: i18n.tsx.removeAreYouSure({ x: title.value }), }); if (canceled) return; - os.api('admin/announcements/delete', { - id: props.announcement.id, - }).then(() => { - emit('done', { - deleted: true, + if (props.announcement) { + await misskeyApi('admin/announcements/delete', { + id: props.announcement.id, }); - dialog.value.close(); + } + + emit('done', { + deleted: true, }); + dialog.value?.close(); } </script> diff --git a/packages/frontend/src/components/MkUserCardMini.vue b/packages/frontend/src/components/MkUserCardMini.vue index b9c73779729cadaef56c58e44fa3447d29fc08ae..603f9f243567a57ab76bd3100ca07b02d13f068a 100644 --- a/packages/frontend/src/components/MkUserCardMini.vue +++ b/packages/frontend/src/components/MkUserCardMini.vue @@ -1,16 +1,16 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div v-adaptive-bg :class="[$style.root, { yellow: user.isSilenced, blue: !user.approved, red: user.isSuspended, gray: false }]"> - <MkAvatar class="avatar" :user="user" indicator/> - <div class="body"> - <span class="name"><MkUserName class="name" :user="user"/></span> - <span class="sub"><span class="acct _monospace">@{{ acct(user) }}</span></span> +<div v-adaptive-bg :class="[$style.root]"> + <MkAvatar :class="$style.avatar" :user="user" indicator/> + <div :class="$style.body"> + <span :class="$style.name"><MkUserName :user="user"/></span> + <span :class="$style.sub"><span class="_monospace">@{{ acct(user) }}</span></span> </div> - <MkMiniChart v-if="chartValues" class="chart" :src="chartValues"/> + <MkMiniChart v-if="chartValues" :class="$style.chart" :src="chartValues"/> </div> </template> @@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only import * as Misskey from 'misskey-js'; import { onMounted, ref } from 'vue'; import MkMiniChart from '@/components/MkMiniChart.vue'; -import * as os from '@/os.js'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; import { acct } from '@/filters/user.js'; const props = withDefaults(defineProps<{ @@ -32,7 +32,7 @@ const chartValues = ref<number[] | null>(null); onMounted(() => { if (props.withChart) { - os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16 + 1, span: 'day' }).then(res => { + misskeyApiGet('charts/user/notes', { userId: props.user.id, limit: 16 + 1, span: 'day' }).then(res => { // 今日ã®ã¶ã‚“ã®å€¤ã¯ã¾ã 途ä¸ã®å€¤ã§ã‚ã‚Šã€ãれもå«ã‚ã‚‹ã¨å¤§æŠµã®å ´åˆå‰æ—¥ã‚ˆã‚Šã‚‚下é™ã—ã¦ã„るよã†ãªã‚°ãƒ©ãƒ•ã«ãªã£ã¦ã—ã¾ã†ãŸã‚今日ã¯å¼¾ã res.inc.splice(0, 1); chartValues.value = res.inc; @@ -42,77 +42,53 @@ onMounted(() => { </script> <style lang="scss" module> -.root { - $bodyTitleHieght: 18px; - $bodyInfoHieght: 16px; +$bodyTitleHieght: 18px; +$bodyInfoHieght: 16px; +.root { display: flex; align-items: center; padding: 16px; background: var(--panel); border-radius: var(--radius-sm); +} - > :global(.avatar) { - display: block; - width: ($bodyTitleHieght + $bodyInfoHieght); - height: ($bodyTitleHieght + $bodyInfoHieght); - margin-right: 12px; - } - - > :global(.body) { - flex: 1; - overflow: hidden; - font-size: 0.9em; - color: var(--fg); - padding-right: 8px; - - > :global(.name) { - display: block; - width: 100%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - line-height: $bodyTitleHieght; - } - - > :global(.sub) { - display: block; - width: 100%; - font-size: 95%; - opacity: 0.7; - line-height: $bodyInfoHieght; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - } - - > :global(.chart) { - height: 30px; - } +.avatar { + display: block; + width: ($bodyTitleHieght + $bodyInfoHieght); + height: ($bodyTitleHieght + $bodyInfoHieght); + margin-right: 12px; +} - &:global(.yellow) { - --c: rgb(255 196 0 / 15%); - background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%); - background-size: 16px 16px; - } +.body { + flex: 1; + overflow: hidden; + font-size: 0.9em; + color: var(--fg); + padding-right: 8px; +} - &:global(.blue) { - --c: rgba(0 153 255 / 15%); - background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%); - background-size: 16px 16px; - } +.name { + display: block; + width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + line-height: $bodyTitleHieght; +} - &:global(.red) { - --c: rgb(255 0 0 / 15%); - background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%); - background-size: 16px 16px; - } +.sub { + display: block; + width: 100%; + font-size: 95%; + opacity: 0.7; + line-height: $bodyInfoHieght; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} - &:global(.gray) { - --c: var(--bg); - background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%); - background-size: 16px 16px; - } +.chart { + height: 30px; } </style> diff --git a/packages/frontend/src/components/MkUserInfo.vue b/packages/frontend/src/components/MkUserInfo.vue index 4e326911d8317cfa747d1c829e7f7b8d399cdf29..63c4af41a0174ed2fd403b37eea01c1d872e31d4 100644 --- a/packages/frontend/src/components/MkUserInfo.vue +++ b/packages/frontend/src/components/MkUserInfo.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -65,8 +65,8 @@ defineProps<{ top: 62px; left: 13px; z-index: 2; - width: 58px; - height: 58px; + width: var(--avatar); + height: var(--avatar); border: solid 4px var(--panel); } diff --git a/packages/frontend/src/components/MkUserList.vue b/packages/frontend/src/components/MkUserList.vue index 56a61dce23805d1fb51f3680f2ebb7e35f5765c5..17a9254d019d68bd198287931db263488f39438c 100644 --- a/packages/frontend/src/components/MkUserList.vue +++ b/packages/frontend/src/components/MkUserList.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkUserOnlineIndicator.vue b/packages/frontend/src/components/MkUserOnlineIndicator.vue index 76470cba8886a1836985631643861da27ab423c8..9f04353f629d1850798133d8205bf20396c38f8b 100644 --- a/packages/frontend/src/components/MkUserOnlineIndicator.vue +++ b/packages/frontend/src/components/MkUserOnlineIndicator.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue index ec2c48b1cf86f581f7a668f8900ed058dc40e8ff..6550fc4ec1852e8f19a2cbc4d02c3a73a77dfdef 100644 --- a/packages/frontend/src/components/MkUserPopup.vue +++ b/packages/frontend/src/components/MkUserPopup.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only </dt> <dd :class="$style.fieldvalue"> <Mfm :text="field.value" :nyaize="false" :author="user" :colored="false"/> - <i v-if="user.verifiedLinks.includes(field.value)" v-tooltip:dialog="i18n.ts.verifiedLink" class="ph-seal-check ph-bold ph-lg" :class="$style.verifiedLink"></i> + <i v-if="user.verifiedLinks.includes(field.value)" v-tooltip:dialog="i18n.ts.verifiedLink" class="ph-seal-check ph-bold ph-lg"></i> </dd> </dl> </div> @@ -72,6 +72,7 @@ import * as Misskey from 'misskey-js'; import MkFollowButton from '@/components/MkFollowButton.vue'; import { userPage } from '@/filters/user.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { getUserMenu } from '@/scripts/get-user-menu.js'; import number from '@/filters/number.js'; import { i18n } from '@/i18n.js'; @@ -97,6 +98,7 @@ const top = ref(0); const left = ref(0); function showMenu(ev: MouseEvent) { + if (user.value == null) return; const { menu, cleanup } = getUserMenu(user.value); os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup); } @@ -109,7 +111,7 @@ onMounted(() => { Misskey.acct.parse(props.q.substring(1)) : { userId: props.q }; - os.api('users/show', query).then(res => { + misskeyApi('users/show', query).then(res => { if (!props.showing) return; user.value = res; }); @@ -199,8 +201,8 @@ onMounted(() => { right: 0; margin: 0 auto; z-index: 2; - width: 58px; - height: 58px; + width: var(--avatar); + height: var(--avatar); } .title { diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue index 9d41147bd2dec4805ddc968fe3f23842791188ab..b76be051d842d45e6b75afbe54033e1ac81e0612 100644 --- a/packages/frontend/src/components/MkUserSelectDialog.vue +++ b/packages/frontend/src/components/MkUserSelectDialog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -16,7 +16,11 @@ SPDX-License-Identifier: AGPL-3.0-only <template #header>{{ i18n.ts.selectUser }}</template> <div> <div :class="$style.form"> - <FormSplit :minWidth="170"> + <MkInput v-if="localOnly" v-model="username" :autofocus="true" @update:modelValue="search"> + <template #label>{{ i18n.ts.username }}</template> + <template #prefix>@</template> + </MkInput> + <FormSplit v-else :minWidth="170"> <MkInput v-model="username" :autofocus="true" @update:modelValue="search"> <template #label>{{ i18n.ts.username }}</template> <template #prefix>@</template> @@ -62,11 +66,11 @@ import * as Misskey from 'misskey-js'; import MkInput from '@/components/MkInput.vue'; import FormSplit from '@/components/form/split.vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; -import { hostname } from '@/config.js'; +import { host as currentHost, hostname } from '@/config.js'; const emit = defineEmits<{ (ev: 'ok', selected: Misskey.entities.UserDetailed): void; @@ -74,58 +78,85 @@ const emit = defineEmits<{ (ev: 'closed'): void; }>(); -const props = defineProps<{ +const props = withDefaults(defineProps<{ includeSelf?: boolean; -}>(); + localOnly?: boolean; +}>(), { + includeSelf: false, + localOnly: false, +}); const username = ref(''); const host = ref(''); -const users = ref<Misskey.entities.UserDetailed[]>([]); +const users = ref<Misskey.entities.UserLite[]>([]); const recentUsers = ref<Misskey.entities.UserDetailed[]>([]); -const selected = ref<Misskey.entities.UserDetailed | null>(null); +const selected = ref<Misskey.entities.UserLite | null>(null); const dialogEl = ref(); -const search = () => { +function search() { if (username.value === '' && host.value === '') { users.value = []; return; } - os.api('users/search-by-username-and-host', { + + misskeyApi('users/search-by-username-and-host', { username: username.value, - host: host.value, + host: props.localOnly ? '.' : host.value, limit: 10, detail: false, }).then(_users => { - users.value = _users; + users.value = _users.filter((u) => { + if (props.includeSelf) { + return true; + } else { + return u.id !== $i?.id; + } + }); }); -}; +} -const ok = () => { +async function ok() { if (selected.value == null) return; - emit('ok', selected.value); + + const user = await misskeyApi('users/show', { + userId: selected.value.id, + }); + emit('ok', user); + dialogEl.value.close(); // 最近使ã£ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼æ›´æ–° let recents = defaultStore.state.recentlyUsedUsers; - recents = recents.filter(x => x !== selected.value.id); + recents = recents.filter(x => x !== selected.value?.id); recents.unshift(selected.value.id); defaultStore.set('recentlyUsedUsers', recents.splice(0, 16)); -}; +} -const cancel = () => { +function cancel() { emit('cancel'); dialogEl.value.close(); -}; +} onMounted(() => { - os.api('users/show', { + misskeyApi('users/show', { userIds: defaultStore.state.recentlyUsedUsers, - }).then(users => { - if (props.includeSelf && users.find(x => $i ? x.id === $i.id : true) == null) { - recentUsers.value = [$i, ...users]; - } else { - recentUsers.value = users; - } + }).then(foundUsers => { + let _users = foundUsers; + _users = _users.filter((u) => { + if (props.localOnly) { + return u.host == null; + } else { + return true; + } + }); + _users = _users.filter((u) => { + if (props.includeSelf) { + return true; + } else { + return u.id !== $i?.id; + } + }); + recentUsers.value = _users; }); }); </script> @@ -133,7 +164,7 @@ onMounted(() => { <style lang="scss" module> .form { - padding: 0 var(--root-margin); + padding: calc(var(--root-margin) / 2) var(--root-margin); } .result, diff --git a/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts index 45c7da40ce2e51171e07aa0cd00ae0a571a7dcdf..638bfb4372ef21a0e20d95537f952990aeb45c1a 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts +++ b/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts @@ -1,11 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { StoryObj } from '@storybook/vue3'; -import { rest } from 'msw'; +import { HttpResponse, http } from 'msw'; import { commonHandlers } from '../../.storybook/mocks.js'; import { userDetailed } from '../../.storybook/fakes.js'; import MkUserSetupDialog_Follow from './MkUserSetupDialog.Follow.vue'; @@ -38,17 +38,17 @@ export const Default = { msw: { handlers: [ ...commonHandlers, - rest.post('/api/users', (req, res, ctx) => { - return res(ctx.json([ + http.post('/api/users', () => { + return HttpResponse.json([ userDetailed('44'), userDetailed('49'), - ])); + ]); }), - rest.post('/api/pinned-users', (req, res, ctx) => { - return res(ctx.json([ + http.post('/api/pinned-users', () => { + return HttpResponse.json([ userDetailed('44'), userDetailed('49'), - ])); + ]); }), ], }, diff --git a/packages/frontend/src/components/MkUserSetupDialog.Follow.vue b/packages/frontend/src/components/MkUserSetupDialog.Follow.vue index 5f3f5b81dd08b8816c93e2b93b6de6a7ab1550ea..1524ea0ec989f928022bf1149dac3d4e70e21780 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.Follow.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.Follow.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPagination :pagination="pinnedUsers"> <template #default="{ items }"> <div :class="$style.users"> - <XUser v-for="item in items" :key="item.id" :user="item"/> + <XUser v-for="item in (items as Misskey.entities.UserDetailed[])" :key="item.id" :user="item"/> </div> </template> </MkPagination> @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPagination :pagination="popularUsers"> <template #default="{ items }"> <div :class="$style.users"> - <XUser v-for="item in items" :key="item.id" :user="item"/> + <XUser v-for="item in (items as Misskey.entities.UserDetailed[])" :key="item.id" :user="item"/> </div> </template> </MkPagination> @@ -34,18 +34,28 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import * as Misskey from 'misskey-js'; import { i18n } from '@/i18n.js'; import MkFolder from '@/components/MkFolder.vue'; import XUser from '@/components/MkUserSetupDialog.User.vue'; -import MkPagination from '@/components/MkPagination.vue'; +import MkPagination, { type Paging } from '@/components/MkPagination.vue'; -const pinnedUsers = { endpoint: 'pinned-users', noPaging: true }; +const pinnedUsers: Paging = { + endpoint: 'pinned-users', + noPaging: true, + limit: 10, +}; -const popularUsers = { endpoint: 'users', limit: 10, noPaging: true, params: { - state: 'alive', - origin: 'local', - sort: '+follower', -} }; +const popularUsers: Paging = { + endpoint: 'users', + limit: 10, + noPaging: true, + params: { + state: 'alive', + origin: 'local', + sort: '+follower', + }, +}; </script> <style lang="scss" module> diff --git a/packages/frontend/src/components/MkUserSetupDialog.Privacy.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.Privacy.stories.impl.ts index 0f81c0817d6211e1a09a28fa72f4af29c3f5808a..2a7947c6f86e9e0f8cf68cfa8f66f361a58ff8d7 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.Privacy.stories.impl.ts +++ b/packages/frontend/src/components/MkUserSetupDialog.Privacy.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue b/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue index 664c4da2033814a5f4a009ba35951712abc0d53d..6d2f0bbb995a53dac3e72274b6f6831887ede442 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -41,14 +41,14 @@ import { i18n } from '@/i18n.js'; import MkSwitch from '@/components/MkSwitch.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkFolder from '@/components/MkFolder.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const isLocked = ref(false); const hideOnlineStatus = ref(false); const noCrawle = ref(false); watch([isLocked, hideOnlineStatus, noCrawle], () => { - os.api('i/update', { + misskeyApi('i/update', { isLocked: !!isLocked.value, hideOnlineStatus: !!hideOnlineStatus.value, noCrawle: !!noCrawle.value, diff --git a/packages/frontend/src/components/MkUserSetupDialog.Profile.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.Profile.stories.impl.ts index d2c6f7d47958a58441ac9cad4edcfbf91cbfb306..c6088a5ae3392ef4345028f04131c5cac53551e4 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.Profile.stories.impl.ts +++ b/packages/frontend/src/components/MkUserSetupDialog.Profile.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue index 37aa677b440f744fdbba8330159643a2e4ca39d2..3194641cdb7e1b931bfb54a664f7b0ea1644841f 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -39,7 +39,9 @@ import FormSlot from '@/components/form/slot.vue'; import MkInfo from '@/components/MkInfo.vue'; import { chooseFileFromPc } from '@/scripts/select-file.js'; import * as os from '@/os.js'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; + +const $i = signinRequired(); const name = ref($i.name ?? ''); const description = ref($i.description ?? ''); @@ -68,7 +70,7 @@ function setAvatar(ev) { const { canceled } = await os.confirm({ type: 'question', - text: i18n.t('cropImageAsk'), + text: i18n.ts.cropImageAsk, okText: i18n.ts.cropYes, cancelText: i18n.ts.cropNo, }); diff --git a/packages/frontend/src/components/MkUserSetupDialog.User.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.User.stories.impl.ts index 31176c0832a2cad220331d00b9246dc4d1a93c1f..f0206e0cb41644ab6d6a801e005af5a5113edf04 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.User.stories.impl.ts +++ b/packages/frontend/src/components/MkUserSetupDialog.User.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/MkUserSetupDialog.User.vue b/packages/frontend/src/components/MkUserSetupDialog.User.vue index 621995cc5bec24e494c1a347c1f78defe409c56b..a4b9746f4b762a0d74b9b5d8bc001c71ff804efe 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.User.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.User.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -29,7 +29,7 @@ import * as Misskey from 'misskey-js'; import { ref } from 'vue'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const props = defineProps<{ user: Misskey.entities.UserDetailed; @@ -39,7 +39,7 @@ const isFollowing = ref(false); async function follow() { isFollowing.value = true; - os.api('following/create', { + misskeyApi('following/create', { userId: props.user.id, }); } @@ -59,8 +59,8 @@ async function follow() { top: 30px; left: 13px; z-index: 2; - width: 58px; - height: 58px; + width: var(--avatar); + height: var(--avatar); border: solid 4px var(--panel); } diff --git a/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts index 5182db12b2e9f20996434ca41d9ca0bc15c9fae0..3f5ae734bd6dd91ec624cc42e52bacbb4e462934 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts +++ b/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts @@ -1,11 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { StoryObj } from '@storybook/vue3'; -import { rest } from 'msw'; +import { HttpResponse, http } from 'msw'; import { commonHandlers } from '../../.storybook/mocks.js'; import { userDetailed } from '../../.storybook/fakes.js'; import MkUserSetupDialog from './MkUserSetupDialog.vue'; @@ -38,17 +38,17 @@ export const Default = { msw: { handlers: [ ...commonHandlers, - rest.post('/api/users', (req, res, ctx) => { - return res(ctx.json([ + http.post('/api/users', () => { + return HttpResponse.json([ userDetailed('44'), userDetailed('49'), - ])); + ]); }), - rest.post('/api/pinned-users', (req, res, ctx) => { - return res(ctx.json([ + http.post('/api/pinned-users', () => { + return HttpResponse.json([ userDetailed('44'), userDetailed('49'), - ])); + ]); }), ], }, diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue index be945c10665ad8574b51fd8b88633be11e6447e9..bd8949890c04241a990743cdfb6c1cdbd39fcb09 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -93,7 +93,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps" style="text-align: center;"> <i class="ph-bell-ringing ph-bold ph-lg" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i> <div style="font-size: 120%;">{{ i18n.ts.pushNotification }}</div> - <div style="padding: 0 16px;">{{ i18n.t('_initialAccountSetting.pushNotificationDescription', { name: instance.name ?? host }) }}</div> + <div style="padding: 0 16px;">{{ i18n.tsx._initialAccountSetting.pushNotificationDescription({ name: instance.name ?? host }) }}</div> <MkPushNotificationAllowButton primary showOnlyToRegister style="margin: 0 auto;"/> <div class="_buttonsCenter" style="margin-top: 16px;"> <MkButton rounded data-cy-user-setup-back @click="page--"><i class="ph-arrow-left ph-bold ph-lg"></i> {{ i18n.ts.goBack }}</MkButton> @@ -110,7 +110,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps" style="text-align: center;"> <i class="ph-check ph-bold ph-lg" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i> <div style="font-size: 120%;">{{ i18n.ts._initialAccountSetting.initialAccountSettingCompleted }}</div> - <div>{{ i18n.t('_initialAccountSetting.youCanContinueTutorial', { name: instance.name ?? host }) }}</div> + <div>{{ i18n.tsx._initialAccountSetting.youCanContinueTutorial({ name: instance.name ?? host }) }}</div> <div class="_buttonsCenter" style="margin-top: 16px;"> <MkButton rounded primary gradate data-cy-user-setup-continue @click="launchTutorial()">{{ i18n.ts._initialAccountSetting.startTutorial }} <i class="ph-arrow-right ph-bold ph-lg"></i></MkButton> </div> diff --git a/packages/frontend/src/components/MkUsersTooltip.vue b/packages/frontend/src/components/MkUsersTooltip.vue index 37548952b6bd50eb8bd1cfbb3a564d17f0e6b7b3..054a5032575d1801e773b434d9ab99d931b81cf9 100644 --- a/packages/frontend/src/components/MkUsersTooltip.vue +++ b/packages/frontend/src/components/MkUsersTooltip.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkVisibilityPicker.vue b/packages/frontend/src/components/MkVisibilityPicker.vue index 61edc345a9edcae4e167aa07901c7987bb0641e4..bd6edad663231a18e152db5125644747c2ff72f1 100644 --- a/packages/frontend/src/components/MkVisibilityPicker.vue +++ b/packages/frontend/src/components/MkVisibilityPicker.vue @@ -1,29 +1,29 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkModal ref="modal" v-slot="{ type }" :zPriority="'high'" :src="src" @click="modal.close()" @closed="emit('closed')"> +<MkModal ref="modal" v-slot="{ type }" :zPriority="'high'" :src="src" @click="modal?.close()" @closed="emit('closed')"> <div class="_popup" :class="{ [$style.root]: true, [$style.asDrawer]: type === 'drawer' }"> <div :class="[$style.label, $style.item]"> {{ i18n.ts.visibility }} </div> - <button key="public" :disabled="isSilenced" class="_button" :class="[$style.item, { [$style.active]: v === 'public' }]" data-index="1" @click="choose('public')"> + <button key="public" :disabled="isSilenced || isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'public' }]" data-index="1" @click="choose('public')"> <div :class="$style.icon"><i class="ph-globe-hemisphere-west ph-bold ph-lg"></i></div> <div :class="$style.body"> <span :class="$style.itemTitle">{{ i18n.ts._visibility.public }}</span> <span :class="$style.itemDescription">{{ i18n.ts._visibility.publicDescription }}</span> </div> </button> - <button key="home" class="_button" :class="[$style.item, { [$style.active]: v === 'home' }]" data-index="2" @click="choose('home')"> + <button key="home" :disabled="isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'home' }]" data-index="2" @click="choose('home')"> <div :class="$style.icon"><i class="ph-house ph-bold ph-lg"></i></div> <div :class="$style.body"> <span :class="$style.itemTitle">{{ i18n.ts._visibility.home }}</span> <span :class="$style.itemDescription">{{ i18n.ts._visibility.homeDescription }}</span> </div> </button> - <button key="followers" class="_button" :class="[$style.item, { [$style.active]: v === 'followers' }]" data-index="3" @click="choose('followers')"> + <button key="followers" :disabled="isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'followers' }]" data-index="3" @click="choose('followers')"> <div :class="$style.icon"><i class="ph-lock ph-bold ph-lg"></i></div> <div :class="$style.body"> <span :class="$style.itemTitle">{{ i18n.ts._visibility.followers }}</span> @@ -54,6 +54,7 @@ const props = withDefaults(defineProps<{ isSilenced: boolean; localOnly: boolean; src?: HTMLElement; + isReplyVisibilitySpecified?: boolean; }>(), { }); diff --git a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue index 746ed3e0de01501a671504d8d2549784c7896bb9..cab42cd59d2ec9579755195d120cc06a5393904c 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -13,11 +13,11 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, shallowRef, ref } from 'vue'; +import { onMounted, shallowRef, ref, nextTick } from 'vue'; import { Chart } from 'chart.js'; import gradient from 'chartjs-plugin-gradient'; import tinycolor from 'tinycolor2'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import { chartVLine } from '@/scripts/chart-vline.js'; @@ -25,9 +25,9 @@ import { initChart } from '@/scripts/init-chart.js'; initChart(); -const chartEl = shallowRef<HTMLCanvasElement>(null); +const chartEl = shallowRef<HTMLCanvasElement | null>(null); const now = new Date(); -let chartInstance: Chart = null; +let chartInstance: Chart | null = null; const chartLimit = 30; const fetching = ref(true); @@ -53,7 +53,11 @@ async function renderChart() { })); }; - const raw = await os.api('charts/active-users', { limit: chartLimit, span: 'day' }); + const raw = await misskeyApi('charts/active-users', { limit: chartLimit, span: 'day' }); + + fetching.value = false; + + await nextTick(); const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; @@ -65,6 +69,8 @@ async function renderChart() { const max = Math.max(...raw.read); + if (chartEl.value == null) return; + chartInstance = new Chart(chartEl.value, { type: 'bar', data: { @@ -97,7 +103,6 @@ async function renderChart() { type: 'time', offset: true, time: { - stepSize: 1, unit: 'day', displayFormats: { day: 'M/d', @@ -108,6 +113,7 @@ async function renderChart() { display: false, }, ticks: { + stepSize: 1, display: true, maxRotation: 0, autoSkipPadding: 8, @@ -141,13 +147,10 @@ async function renderChart() { }, external: externalTooltipHandler, }, - gradient, }, }, plugins: [chartVLine(vLineColor)], }); - - fetching.value = false; } onMounted(async () => { diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue index 862a38bd543be83153f64bfe1b8a45f6d50dccf3..d8e6ba9a09cca499906112242102a40d96dd4ec2 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.vue @@ -1,12 +1,12 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <div v-if="meta" :class="$style.root"> <div :class="[$style.main, $style.panel]"> - <img :src="instance.iconUrl || instance.faviconUrl || '/apple-touch-icon.png'" alt="" :class="$style.mainIcon"/> + <img :src="instance.iconUrl || '/apple-touch-icon.png'" alt="" :class="$style.mainIcon"/> <button class="_button _acrylic" :class="$style.mainMenu" @click="showMenu"><i class="ph-dots-three ph-bold ph-lg"></i></button> <div :class="$style.mainFg"> <h1 :class="$style.mainTitle"> @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only </h1> <div :class="$style.mainAbout"> <!-- eslint-disable-next-line vue/no-v-html --> - <div v-html="meta.description || i18n.ts.headlineMisskey"></div> + <div v-html="sanitizeHtml(meta.description) || i18n.ts.headlineMisskey"></div> </div> <div v-if="instance.disableRegistration" :class="$style.mainWarn"> <MkInfo warn>{{ i18n.ts.invitationRequiredToRegister }}</MkInfo> @@ -56,6 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; import * as Misskey from 'misskey-js'; +import sanitizeHtml from 'sanitize-html'; import XSigninDialog from '@/components/MkSigninDialog.vue'; import XSignupDialog from '@/components/MkSignupDialog.vue'; import MkButton from '@/components/MkButton.vue'; @@ -63,6 +64,7 @@ import MkTimeline from '@/components/MkTimeline.vue'; import MkInfo from '@/components/MkInfo.vue'; import { instanceName } from '@/config.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { instance } from '@/instance.js'; import MkNumber from '@/components/MkNumber.vue'; @@ -71,11 +73,11 @@ import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart. const meta = ref<Misskey.entities.MetaResponse | null>(null); const stats = ref<Misskey.entities.StatsResponse | null>(null); -os.api('meta', { detail: true }).then(_meta => { +misskeyApi('meta', { detail: true }).then(_meta => { meta.value = _meta; }); -os.api('stats', {}).then((res) => { +misskeyApi('stats', {}).then((res) => { stats.value = res; }); @@ -108,21 +110,27 @@ function showMenu(ev) { text: i18n.ts.impressum, icon: 'ph-newspaper-clipping ph-bold ph-lg', action: () => { - window.open(instance.impressumUrl, '_blank', 'noopener'); + window.open(instance.impressumUrl!, '_blank', 'noopener'); }, } : undefined, (instance.tosUrl) ? { text: i18n.ts.termsOfService, icon: 'ph-notebook ph-bold ph-lg', action: () => { - window.open(instance.tosUrl, '_blank', 'noopener'); + window.open(instance.tosUrl!, '_blank', 'noopener'); }, } : undefined, (instance.privacyPolicyUrl) ? { text: i18n.ts.privacyPolicy, icon: 'ph-shield ph-bold ph-lg', action: () => { - window.open(instance.privacyPolicyUrl, '_blank', 'noopener'); + window.open(instance.privacyPolicyUrl!, '_blank', 'noopener'); }, - } : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl) ? undefined : { type: 'divider' }, { + } : undefined, (instance.donationUrl) ? { + text: i18n.ts.donation, + icon: 'ph-hand-coins ph-bold ph-lg', + action: () => { + window.open(instance.donationUrl, '_blank', 'noopener'); + }, + } : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl && !instance.donationUrl) ? undefined : { type: 'divider' }, { text: i18n.ts.help, icon: 'ph-question ph-bold ph-lg', action: () => { diff --git a/packages/frontend/src/components/MkWaitingDialog.vue b/packages/frontend/src/components/MkWaitingDialog.vue index 28943efd1a9b3bee4d7767a54fbb5dba679e6f39..ad2105cc0bfaf9c0f8bd7d2f383b796c9dc39fb2 100644 --- a/packages/frontend/src/components/MkWaitingDialog.vue +++ b/packages/frontend/src/components/MkWaitingDialog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -32,7 +32,7 @@ const emit = defineEmits<{ function done() { emit('done'); - modal.value.close(); + modal.value?.close(); } watch(() => props.showing, () => { diff --git a/packages/frontend/src/components/MkWidgets.vue b/packages/frontend/src/components/MkWidgets.vue index bc1f33c43ec9c0860a5a434b25dec5e1f84f05b0..05a0f6e04eb0f19125254c1aa8e89834b46fb733 100644 --- a/packages/frontend/src/components/MkWidgets.vue +++ b/packages/frontend/src/components/MkWidgets.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <header :class="$style.editHeader"> <MkSelect v-model="widgetAdderSelected" style="margin-bottom: var(--margin)" data-cy-widget-select> <template #label>{{ i18n.ts.selectWidget }}</template> - <option v-for="widget in widgetDefs" :key="widget" :value="widget">{{ i18n.t(`_widgets.${widget}`) }}</option> + <option v-for="widget in widgetDefs" :key="widget" :value="widget">{{ i18n.ts._widgets[widget] }}</option> </MkSelect> <MkButton inline primary data-cy-widget-add @click="addWidget"><i class="ph-plus ph-bold ph-lg"></i> {{ i18n.ts.add }}</MkButton> <MkButton inline @click="$emit('exit')">{{ i18n.ts.close }}</MkButton> @@ -104,19 +104,21 @@ const updateWidget = (id, data) => { }; function onContextmenu(widget: Widget, ev: MouseEvent) { - const isLink = (el: HTMLElement) => { + const element = ev.target as HTMLElement | null; + const isLink = (el: HTMLElement): boolean => { if (el.tagName === 'A') return true; if (el.parentElement) { return isLink(el.parentElement); } + return false; }; - if (isLink(ev.target)) return; - if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(ev.target.tagName) || ev.target.attributes['contenteditable']) return; + if (element && isLink(element)) return; + if (element && (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(element.tagName) || element.attributes['contenteditable'])) return; if (window.getSelection()?.toString() !== '') return; os.contextMenu([{ type: 'label', - text: i18n.t(`_widgets.${widget.name}`), + text: i18n.ts._widgets[widget.name], }, { icon: 'ph-gear ph-bold ph-lg', text: i18n.ts.settings, diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue index e5b8bd9b15c7f6b8ff264f8d564c0192a356bdcf..f13b53b005ec6c248bbc4570fe98ccdfaef9e8ad 100644 --- a/packages/frontend/src/components/MkWindow.vue +++ b/packages/frontend/src/components/MkWindow.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -63,7 +63,7 @@ import { defaultStore } from '@/store.js'; const minHeight = 50; const minWidth = 250; -function dragListen(fn: (ev: MouseEvent) => void) { +function dragListen(fn: (ev: MouseEvent | TouchEvent) => void) { window.addEventListener('mousemove', fn); window.addEventListener('touchmove', fn); window.addEventListener('mouseleave', dragClear.bind(null, fn)); @@ -138,11 +138,12 @@ function onContextmenu(ev: MouseEvent) { // 最å‰é¢ã¸ç§»å‹• function top() { if (rootEl.value) { - rootEl.value.style.zIndex = os.claimZIndex(props.front ? 'middle' : 'low'); + rootEl.value.style.zIndex = os.claimZIndex(props.front ? 'middle' : 'low').toString(); } } function maximize() { + if (rootEl.value == null) return; maximized.value = true; unResizedTop = rootEl.value.style.top; unResizedLeft = rootEl.value.style.left; @@ -155,6 +156,7 @@ function maximize() { } function unMaximize() { + if (rootEl.value == null) return; maximized.value = false; rootEl.value.style.top = unResizedTop; rootEl.value.style.left = unResizedLeft; @@ -163,6 +165,7 @@ function unMaximize() { } function minimize() { + if (rootEl.value == null) return; minimized.value = true; unResizedWidth = rootEl.value.style.width; unResizedHeight = rootEl.value.style.height; @@ -171,8 +174,8 @@ function minimize() { } function unMinimize() { + if (rootEl.value == null) return; const main = rootEl.value; - if (main == null) return; minimized.value = false; rootEl.value.style.width = unResizedWidth; @@ -199,9 +202,17 @@ function onDblClick() { } } -function onHeaderMousedown(evt: MouseEvent) { +function getPositionX(event: MouseEvent | TouchEvent) { + return 'touches' in event && event.touches.length > 0 ? event.touches[0].clientX : 'clientX' in event ? event.clientX : 0; +} + +function getPositionY(event: MouseEvent | TouchEvent) { + return 'touches' in event && event.touches.length > 0 ? event.touches[0].clientY : 'clientY' in event ? event.clientY : 0; +} + +function onHeaderMousedown(evt: MouseEvent | TouchEvent) { // å³ã‚¯ãƒªãƒƒã‚¯ã¯ã‚³ãƒ³ãƒ†ã‚ストメニューを開ã“ã†ã¨ã—ãŸå¯èƒ½æ€§ãŒé«˜ã„ãŸã‚無視 - if (evt.button === 2) return; + if ('button' in evt && evt.button === 2) return; let beforeMaximized = false; @@ -226,8 +237,8 @@ function onHeaderMousedown(evt: MouseEvent) { const position = main.getBoundingClientRect(); - const clickX = evt.touches && evt.touches.length > 0 ? evt.touches[0].clientX : evt.clientX; - const clickY = evt.touches && evt.touches.length > 0 ? evt.touches[0].clientY : evt.clientY; + const clickX = getPositionX(evt); + const clickY = getPositionY(evt); const moveBaseX = beforeMaximized ? parseInt(unResizedWidth, 10) / 2 : clickX - position.left; // TODO: parseIntã‚„ã‚ã‚‹ const moveBaseY = beforeMaximized ? 20 : clickY - position.top; const browserWidth = window.innerWidth; @@ -251,8 +262,10 @@ function onHeaderMousedown(evt: MouseEvent) { // å³ã¯ã¿å‡ºã— if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth; - rootEl.value.style.left = moveLeft + 'px'; - rootEl.value.style.top = moveTop + 'px'; + if (rootEl.value) { + rootEl.value.style.left = moveLeft + 'px'; + rootEl.value.style.top = moveTop + 'px'; + } } if (beforeMaximized) { @@ -261,26 +274,26 @@ function onHeaderMousedown(evt: MouseEvent) { // å‹•ã‹ã—ãŸæ™‚ dragListen(me => { - const x = me.touches && me.touches.length > 0 ? me.touches[0].clientX : me.clientX; - const y = me.touches && me.touches.length > 0 ? me.touches[0].clientY : me.clientY; + const x = getPositionX(me); + const y = getPositionY(me); move(x, y); }); } // 上ãƒãƒ³ãƒ‰ãƒ«æŽ´ã¿æ™‚ -function onTopHandleMousedown(evt) { +function onTopHandleMousedown(evt: MouseEvent | TouchEvent) { const main = rootEl.value; // ã©ã†ã„ã†ã‚ã‘ã‹nullã«ãªã‚‹ã“ã¨ãŒã‚ã‚‹ if (main == null) return; - const base = evt.clientY; + const base = getPositionY(evt); const height = parseInt(getComputedStyle(main, '').height, 10); const top = parseInt(getComputedStyle(main, '').top, 10); // å‹•ã‹ã—ãŸæ™‚ dragListen(me => { - const move = me.clientY - base; + const move = getPositionY(me) - base; if (top + move > 0) { if (height + -move > minHeight) { applyTransformHeight(height + -move); @@ -297,18 +310,18 @@ function onTopHandleMousedown(evt) { } // å³ãƒãƒ³ãƒ‰ãƒ«æŽ´ã¿æ™‚ -function onRightHandleMousedown(evt) { +function onRightHandleMousedown(evt: MouseEvent | TouchEvent) { const main = rootEl.value; if (main == null) return; - const base = evt.clientX; + const base = getPositionX(evt); const width = parseInt(getComputedStyle(main, '').width, 10); const left = parseInt(getComputedStyle(main, '').left, 10); const browserWidth = window.innerWidth; // å‹•ã‹ã—ãŸæ™‚ dragListen(me => { - const move = me.clientX - base; + const move = getPositionX(me) - base; if (left + width + move < browserWidth) { if (width + move > minWidth) { applyTransformWidth(width + move); @@ -322,18 +335,18 @@ function onRightHandleMousedown(evt) { } // 下ãƒãƒ³ãƒ‰ãƒ«æŽ´ã¿æ™‚ -function onBottomHandleMousedown(evt) { +function onBottomHandleMousedown(evt: MouseEvent | TouchEvent) { const main = rootEl.value; if (main == null) return; - const base = evt.clientY; + const base = getPositionY(evt); const height = parseInt(getComputedStyle(main, '').height, 10); const top = parseInt(getComputedStyle(main, '').top, 10); const browserHeight = window.innerHeight; // å‹•ã‹ã—ãŸæ™‚ dragListen(me => { - const move = me.clientY - base; + const move = getPositionY(me) - base; if (top + height + move < browserHeight) { if (height + move > minHeight) { applyTransformHeight(height + move); @@ -347,17 +360,17 @@ function onBottomHandleMousedown(evt) { } // å·¦ãƒãƒ³ãƒ‰ãƒ«æŽ´ã¿æ™‚ -function onLeftHandleMousedown(evt) { +function onLeftHandleMousedown(evt: MouseEvent | TouchEvent) { const main = rootEl.value; if (main == null) return; - const base = evt.clientX; + const base = getPositionX(evt); const width = parseInt(getComputedStyle(main, '').width, 10); const left = parseInt(getComputedStyle(main, '').left, 10); // å‹•ã‹ã—ãŸæ™‚ dragListen(me => { - const move = me.clientX - base; + const move = getPositionX(me) - base; if (left + move > 0) { if (width + -move > minWidth) { applyTransformWidth(width + -move); @@ -374,25 +387,25 @@ function onLeftHandleMousedown(evt) { } // 左上ãƒãƒ³ãƒ‰ãƒ«æŽ´ã¿æ™‚ -function onTopLeftHandleMousedown(evt) { +function onTopLeftHandleMousedown(evt: MouseEvent | TouchEvent) { onTopHandleMousedown(evt); onLeftHandleMousedown(evt); } // å³ä¸Šãƒãƒ³ãƒ‰ãƒ«æŽ´ã¿æ™‚ -function onTopRightHandleMousedown(evt) { +function onTopRightHandleMousedown(evt: MouseEvent | TouchEvent) { onTopHandleMousedown(evt); onRightHandleMousedown(evt); } // å³ä¸‹ãƒãƒ³ãƒ‰ãƒ«æŽ´ã¿æ™‚ -function onBottomRightHandleMousedown(evt) { +function onBottomRightHandleMousedown(evt: MouseEvent | TouchEvent) { onBottomHandleMousedown(evt); onRightHandleMousedown(evt); } // 左下ãƒãƒ³ãƒ‰ãƒ«æŽ´ã¿æ™‚ -function onBottomLeftHandleMousedown(evt) { +function onBottomLeftHandleMousedown(evt: MouseEvent | TouchEvent) { onBottomHandleMousedown(evt); onLeftHandleMousedown(evt); } @@ -400,23 +413,23 @@ function onBottomLeftHandleMousedown(evt) { // 高ã•ã‚’é©ç”¨ function applyTransformHeight(height) { if (height > window.innerHeight) height = window.innerHeight; - rootEl.value.style.height = height + 'px'; + if (rootEl.value) rootEl.value.style.height = height + 'px'; } // å¹…ã‚’é©ç”¨ function applyTransformWidth(width) { if (width > window.innerWidth) width = window.innerWidth; - rootEl.value.style.width = width + 'px'; + if (rootEl.value) rootEl.value.style.width = width + 'px'; } // Y座標をé©ç”¨ function applyTransformTop(top) { - rootEl.value.style.top = top + 'px'; + if (rootEl.value) rootEl.value.style.top = top + 'px'; } // X座標をé©ç”¨ function applyTransformLeft(left) { - rootEl.value.style.left = left + 'px'; + if (rootEl.value) rootEl.value.style.left = left + 'px'; } function onBrowserResize() { @@ -438,8 +451,10 @@ onMounted(() => { applyTransformWidth(props.initialWidth); if (props.initialHeight) applyTransformHeight(props.initialHeight); - applyTransformTop((window.innerHeight / 2) - (rootEl.value.offsetHeight / 2)); - applyTransformLeft((window.innerWidth / 2) - (rootEl.value.offsetWidth / 2)); + if (rootEl.value) { + applyTransformTop((window.innerHeight / 2) - (rootEl.value.offsetHeight / 2)); + applyTransformLeft((window.innerWidth / 2) - (rootEl.value.offsetWidth / 2)); + } // ä»–ã®ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦å†…ã®ãƒœã‚¿ãƒ³ãªã©ã‚’押ã—ã¦ã“ã®ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ãŒé–‹ã‹ã‚ŒãŸå ´åˆã€è¦ªãŒæœ€å‰é¢ã«ãªã‚ã†ã¨ã™ã‚‹ã®ã§ãã‚Œã«éš ã•ã‚Œãªã„よã†ã«ã™ã‚‹ top(); diff --git a/packages/frontend/src/components/MkYouTubePlayer.vue b/packages/frontend/src/components/MkYouTubePlayer.vue index a9b2e8a00d23df8c061be48764a6674ad9fbec2b..3ad2a95bc3c7ca888c8cfdb0dcb3b824409efccf 100644 --- a/packages/frontend/src/components/MkYouTubePlayer.vue +++ b/packages/frontend/src/components/MkYouTubePlayer.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -39,7 +39,7 @@ if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid const fetching = ref(true); const title = ref<string | null>(null); const player = ref({ - url: null, + url: null as string | null, width: null, height: null, }); diff --git a/packages/frontend/src/components/SkApprovalUser.vue b/packages/frontend/src/components/SkApprovalUser.vue index 2bf6361ac8aff1c0b59bfe89145d342860689870..f85944cd04375986bdb3db40c8056cf0da030d9f 100644 --- a/packages/frontend/src/components/SkApprovalUser.vue +++ b/packages/frontend/src/components/SkApprovalUser.vue @@ -33,6 +33,7 @@ import MkFolder from '@/components/MkFolder.vue'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const props = defineProps<{ user: Misskey.entities.User; @@ -42,7 +43,7 @@ let reason = ref(''); let email = ref(''); function getReason() { - return os.api('admin/show-user', { + return misskeyApi('admin/show-user', { userId: props.user.id, }).then(info => { reason.value = info?.signupReason; @@ -87,7 +88,7 @@ async function approveAccount() { text: i18n.ts.approveConfirm, }); if (confirm.canceled) return; - await os.api('admin/approve-user', { userId: props.user.id }); + await misskeyApi('admin/approve-user', { userId: props.user.id }); emits('deleted', props.user.id); } </script> diff --git a/packages/frontend/src/components/SkInstanceTicker.vue b/packages/frontend/src/components/SkInstanceTicker.vue index d69e5fecec94acabe24e83d5365fbbd245b281b9..9cfc3326981fb58722b1f5c3bd99116b09a87c1f 100644 --- a/packages/frontend/src/components/SkInstanceTicker.vue +++ b/packages/frontend/src/components/SkInstanceTicker.vue @@ -46,11 +46,22 @@ const bg = { align-items: center; height: 1.5ex; border-radius: var(--radius-xl); - margin-top: 5px; padding: 4px; overflow: clip; color: #fff; - text-shadow: -1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000; + text-shadow: /* .866 ≈ sin(60deg) */ + 1px 0 1px #000, + .866px .5px 1px #000, + .5px .866px 1px #000, + 0 1px 1px #000, + -.5px .866px 1px #000, + -.866px .5px 1px #000, + -1px 0 1px #000, + -.866px -.5px 1px #000, + -.5px -.866px 1px #000, + 0 -1px 1px #000, + .5px -.866px 1px #000, + .866px -.5px 1px #000; } .icon { @@ -59,7 +70,9 @@ const bg = { } .name { - margin-left: 4px; + padding: 0.5ex; + margin: -0.5ex; + margin-left: calc(4px - 0.5ex); line-height: 1; font-size: 0.8em; font-weight: bold; diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue index 83909654c733a33efe45170843cff266bd77b1a8..09decad1a23159f4d7a1e39d6983c440327af492 100644 --- a/packages/frontend/src/components/SkNote.vue +++ b/packages/frontend/src/components/SkNote.vue @@ -5,9 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div - v-if="!hardMuted && !muted" + v-if="!hardMuted && muted === false" v-show="!isDeleted" - ref="el" + ref="rootEl" v-hotkey="keymap" :class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover }]" :tabindex="!isDeleted ? '-1' : undefined" @@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only </span> <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span> <span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ph-television ph-bold ph-lg"></i></span> - <span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span> + <span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil-simple ph-bold ph-lg"></i></span> </div> </div> <div v-if="renoteCollapsed" :class="$style.collapsedRenoteTarget"> @@ -54,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only <SkNoteHeader :note="appearNote" :mini="true"/> </div> </div> - <div :class="[{ [$style.clickToOpen]: defaultStore.state.clickToOpen }]" @click="defaultStore.state.clickToOpen ? noteclick(appearNote.id) : undefined"> + <div :class="[{ [$style.clickToOpen]: defaultStore.state.clickToOpen }]" @click.stop="defaultStore.state.clickToOpen ? noteclick(appearNote.id) : undefined"> <div style="container-type: inline-size;"> <p v-if="appearNote.cw != null" :class="$style.cw"> <Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/> @@ -76,18 +76,18 @@ SPDX-License-Identifier: AGPL-3.0-only /> <div v-if="translating || translation" :class="$style.translation"> <MkLoading v-if="translating" mini/> - <div v-else> - <b>{{ i18n.t('translatedFrom', { x: translation.sourceLang }) }}: </b> + <div v-else-if="translation"> + <b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b> <Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> </div> </div> <MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton> <MkButton v-else-if="!defaultStore.state.animatedMfm && allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton> </div> - <div v-if="appearNote.files.length > 0"> + <div v-if="appearNote.files && appearNote.files.length > 0"> <MkMediaList :mediaList="appearNote.files" @click.stop/> </div> - <MkPoll v-if="appearNote.poll" :note="appearNote" :class="$style.poll" @click.stop/> + <MkPoll v-if="appearNote.poll" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll" @click.stop/> <MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :class="$style.urlPreview" @click.stop/> <div v-if="appearNote.renote" :class="$style.quote"><SkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div> <button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click.stop @click="collapsed = false"> @@ -147,7 +147,7 @@ SPDX-License-Identifier: AGPL-3.0-only <button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown="clip()"> <i class="ph-paperclip ph-bold ph-lg"></i> </button> - <button ref="menuButton" :class="$style.footerButton" class="_button" @mousedown="menu()"> + <button ref="menuButton" :class="$style.footerButton" class="_button" @mousedown="showMenu()"> <i class="ph-dots-three ph-bold ph-lg"></i> </button> </footer> @@ -155,7 +155,14 @@ SPDX-License-Identifier: AGPL-3.0-only </article> </div> <div v-else-if="!hardMuted" :class="$style.muted" @click="muted = false"> - <I18n :src="i18n.ts.userSaysSomething" tag="small"> + <I18n v-if="muted === 'sensitiveMute'" :src="i18n.ts.userSaysSomethingSensitive" tag="small"> + <template #name> + <MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)"> + <MkUserName :user="appearNote.user"/> + </MkA> + </template> + </I18n> + <I18n v-else :src="i18n.ts.userSaysSomething" tag="small"> <template #name> <MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)"> <MkUserName :user="appearNote.user"/> @@ -173,7 +180,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { computed, inject, onMounted, ref, shallowRef, Ref, watch, provide } from 'vue'; -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; import * as Misskey from 'misskey-js'; import SkNoteSub from '@/components/SkNoteSub.vue'; import SkNoteHeader from '@/components/SkNoteHeader.vue'; @@ -190,6 +197,7 @@ import { focusPrev, focusNext } from '@/scripts/focus.js'; import { checkWordMute } from '@/scripts/check-word-mute.js'; import { userPage } from '@/filters/user.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import * as sound from '@/scripts/sound.js'; import { defaultStore, noteViewInterruptors } from '@/store.js'; import { reactionPicker } from '@/scripts/reaction-picker.js'; @@ -208,7 +216,8 @@ import { MenuItem } from '@/types/menu.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; import { shouldCollapsed } from '@/scripts/collapsed.js'; -import { useRouter } from '@/router.js'; +import { useRouter } from '@/router/supplier.js'; +import { boostMenuItems, type Visibility } from '@/scripts/boost-quote.js'; const props = withDefaults(defineProps<{ note: Misskey.entities.Note; @@ -228,6 +237,7 @@ const emit = defineEmits<{ const router = useRouter(); +const inTimeline = inject<boolean>('inTimeline', false); const inChannel = inject('inChannel', null); const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null); @@ -246,7 +256,7 @@ if (noteViewInterruptors.length > 0) { let result: Misskey.entities.Note | null = deepClone(note.value); for (const interruptor of noteViewInterruptors) { try { - result = await interruptor.handler(result); + result = await interruptor.handler(result!) as Misskey.entities.Note | null; if (result === null) { isDeleted.value = true; return; @@ -255,7 +265,7 @@ if (noteViewInterruptors.length > 0) { console.error(err); } } - note.value = result; + note.value = result as Misskey.entities.Note; }); } @@ -263,11 +273,11 @@ const isRenote = ( note.value.renote != null && note.value.text == null && note.value.cw == null && - note.value.fileIds.length === 0 && + note.value.fileIds && note.value.fileIds.length === 0 && note.value.poll == null ); -const el = shallowRef<HTMLElement>(); +const rootEl = shallowRef<HTMLElement>(); const menuButton = shallowRef<HTMLElement>(); const menuVersionsButton = shallowRef<HTMLElement>(); const renoteButton = shallowRef<HTMLElement>(); @@ -277,50 +287,61 @@ const quoteButton = shallowRef<HTMLElement>(); const clipButton = shallowRef<HTMLElement>(); const likeButton = shallowRef<HTMLElement>(); const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value); -const renoteUrl = appearNote.value.renote ? appearNote.value.renote.url : null; -const renoteUri = appearNote.value.renote ? appearNote.value.renote.uri : null; const isMyRenote = $i && ($i.id === note.value.userId); const showContent = ref(defaultStore.state.uncollapseCW); -const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text).filter(u => u !== renoteUrl && u !== renoteUri) : null); -const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value) : null); +const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null); +const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null); const isLong = shouldCollapsed(appearNote.value, urls.value ?? []); -const collapsed = defaultStore.state.expandLongNote && appearNote.value.cw == null ? false : ref(appearNote.value.cw == null && isLong); +const collapsed = ref(defaultStore.state.expandLongNote && appearNote.value.cw == null && isLong ? false : appearNote.value.cw == null && isLong); const isDeleted = ref(false); const renoted = ref(false); const muted = ref(checkMute(appearNote.value, $i?.mutedWords)); -const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords)); -const translation = ref<any>(null); +const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords, true)); +const translation = ref<Misskey.entities.NotesTranslateResponse | null>(null); const translating = ref(false); const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance); -const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i.id)); -const renoteCollapsed = ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || (appearNote.value.myReaction != null))); +const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i?.id)); +const renoteCollapsed = ref( + defaultStore.state.collapseRenotes && isRenote && ( + ($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || // `||` must be `||`! See https://github.com/misskey-dev/misskey/issues/13131 + (appearNote.value.myReaction != null) + ) +); const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null); const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null); const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false); -function checkMute(note: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null): boolean { +/* Overload Functionã«LintãŒå¯¾å¿œã—ã¦ã„ãªã„ã®ã§ã‚³ãƒ¡ãƒ³ãƒˆã‚¢ã‚¦ãƒˆ +function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: true): boolean; +function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: false): boolean | 'sensitiveMute'; +*/ +function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly = false): boolean | 'sensitiveMute' { if (mutedWords == null) return false; - if (checkWordMute(note, $i, mutedWords)) return true; - if (note.reply && checkWordMute(note.reply, $i, mutedWords)) return true; - if (note.renote && checkWordMute(note.renote, $i, mutedWords)) return true; + if (checkWordMute(noteToCheck, $i, mutedWords)) return true; + if (noteToCheck.reply && checkWordMute(noteToCheck.reply, $i, mutedWords)) return true; + if (noteToCheck.renote && checkWordMute(noteToCheck.renote, $i, mutedWords)) return true; + + if (checkOnly) return false; + + if (inTimeline && !defaultStore.state.tl.filter.withSensitive && noteToCheck.files?.some((v) => v.isSensitive)) return 'sensitiveMute'; return false; } const keymap = { 'r': () => reply(true), 'e|a|plus': () => react(true), - 'q': () => renoteButton.value.renote(true), + 'q': () => renote(appearNote.value.visibility), 'up|k|shift+tab': focusBefore, 'down|j|tab': focusAfter, 'esc': blur, - 'm|o': () => menu(true), + 'm|o': () => showMenu(true), 's': () => showContent.value !== showContent.value, }; provide('react', (reaction: string) => { - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, reaction: reaction, }); @@ -332,7 +353,7 @@ if (props.mock) { }, { deep: true }); } else { useNoteCapture({ - rootEl: el, + rootEl: rootEl, note: appearNote, pureNote: note, isDeletedRef: isDeleted, @@ -341,7 +362,7 @@ if (props.mock) { if (!props.mock) { useTooltip(renoteButton, async (showing) => { - const renotes = await os.api('notes/renotes', { + const renotes = await misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 11, }); @@ -359,7 +380,7 @@ if (!props.mock) { }); useTooltip(quoteButton, async (showing) => { - const renotes = await os.api('notes/renotes', { + const renotes = await misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 11, quote: true, @@ -378,7 +399,7 @@ if (!props.mock) { }); if ($i) { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -388,54 +409,15 @@ if (!props.mock) { } } -type Visibility = 'public' | 'home' | 'followers' | 'specified'; - -// defaultStore.state.visibilityãŒstringãªãŸã‚stringã‚‚å—ã‘付ã‘ã¦ã„ã‚‹ -function smallerVisibility(a: Visibility | string, b: Visibility | string): Visibility { - if (a === 'specified' || b === 'specified') return 'specified'; - if (a === 'followers' || b === 'followers') return 'followers'; - if (a === 'home' || b === 'home') return 'home'; - // if (a === 'public' || b === 'public') - return 'public'; -} - function boostVisibility() { - os.popupMenu([ - { - type: 'button', - icon: 'ph-globe-hemisphere-west ph-bold ph-lg', - text: i18n.ts._visibility['public'], - action: () => { - renote('public'); - }, - }, - { - type: 'button', - icon: 'ph-house ph-bold ph-lg', - text: i18n.ts._visibility['home'], - action: () => { - renote('home'); - }, - }, - { - type: 'button', - icon: 'ph-lock ph-bold ph-lg', - text: i18n.ts._visibility['followers'], - action: () => { - renote('followers'); - }, - }, - { - type: 'button', - icon: 'ph-planet ph-bold ph-lg', - text: i18n.ts._timelines.local, - action: () => { - renote('local'); - }, - }], renoteButton.value); + if (!defaultStore.state.showVisibilitySelectorOnBoost) { + renote(defaultStore.state.visibilityOnBoost); + } else { + os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value); + } } -function renote(visibility: Visibility | 'local') { +function renote(visibility: Visibility, localOnly: boolean = false) { pleaseLogin(); showMovedDialog(); @@ -449,7 +431,7 @@ function renote(visibility: Visibility | 'local') { } if (!props.mock) { - os.api('notes/create', { + misskeyApi('notes/create', { renoteId: appearNote.value.id, channelId: appearNote.value.channelId, }).then(() => { @@ -457,7 +439,7 @@ function renote(visibility: Visibility | 'local') { renoted.value = true; }); } - } else if (!appearNote.value.channel || appearNote.value.channel?.allowRenoteToExternal) { + } else if (!appearNote.value.channel || appearNote.value.channel.allowRenoteToExternal) { const el = renoteButton.value as HTMLElement | null | undefined; if (el) { const rect = el.getBoundingClientRect(); @@ -466,18 +448,10 @@ function renote(visibility: Visibility | 'local') { os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility; - const localOnlySetting = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly; - - let noteVisibility = visibility === 'local' || visibility === 'specified' ? smallerVisibility(appearNote.value.visibility, configuredVisibility) : smallerVisibility(visibility, configuredVisibility); - if (appearNote.value.channel?.isSensitive) { - noteVisibility = smallerVisibility(visibility === 'local' || visibility === 'specified' ? appearNote.value.visibility : visibility, 'home'); - } - if (!props.mock) { - os.api('notes/create', { - localOnly: visibility === 'local' ? true : localOnlySetting, - visibility: noteVisibility, + misskeyApi('notes/create', { + localOnly: localOnly, + visibility: visibility, renoteId: appearNote.value.id, }).then(() => { os.toast(i18n.ts.renoted); @@ -499,9 +473,9 @@ function quote() { renote: appearNote.value, channel: appearNote.value.channel, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, - userId: $i.id, + userId: $i?.id, limit: 1, quote: true, }).then((res) => { @@ -521,9 +495,9 @@ function quote() { os.post({ renote: appearNote.value, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, - userId: $i.id, + userId: $i?.id, limit: 1, quote: true, }).then((res) => { @@ -551,7 +525,7 @@ function reply(viaKeyboard = false): void { reply: appearNote.value, channel: appearNote.value.channel, animation: !viaKeyboard, - }, () => { + }).then(() => { focus(); }); } @@ -559,10 +533,11 @@ function reply(viaKeyboard = false): void { function like(): void { pleaseLogin(); showMovedDialog(); + sound.playMisskeySfx('reaction'); if (props.mock) { return; } - os.api('notes/like', { + misskeyApi('notes/like', { noteId: appearNote.value.id, override: defaultLike.value, }); @@ -579,17 +554,17 @@ function react(viaKeyboard = false): void { pleaseLogin(); showMovedDialog(); if (appearNote.value.reactionAcceptance === 'likeOnly') { - sound.play('reaction'); + sound.playMisskeySfx('reaction'); if (props.mock) { return; } - os.api('notes/like', { + misskeyApi('notes/like', { noteId: appearNote.value.id, override: defaultLike.value, }); - const el = reactButton.value as HTMLElement | null | undefined; + const el = reactButton.value; if (el) { const rect = el.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); @@ -598,15 +573,15 @@ function react(viaKeyboard = false): void { } } else { blur(); - reactionPicker.show(reactButton.value, reaction => { - sound.play('reaction'); + reactionPicker.show(reactButton.value ?? null, note.value, reaction => { + sound.playMisskeySfx('reaction'); if (props.mock) { emit('reaction', reaction); return; } - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, reaction: reaction, }); @@ -619,8 +594,8 @@ function react(viaKeyboard = false): void { } } -function undoReact(note): void { - const oldReaction = note.myReaction; +function undoReact(targetNote: Misskey.entities.Note): void { + const oldReaction = targetNote.myReaction; if (!oldReaction) return; if (props.mock) { @@ -628,8 +603,8 @@ function undoReact(note): void { return; } - os.api('notes/reactions/delete', { - noteId: note.id, + misskeyApi('notes/reactions/delete', { + noteId: targetNote.id, }); } @@ -637,7 +612,7 @@ function undoRenote(note) : void { if (props.mock) { return; } - os.api('notes/unrenote', { + misskeyApi('notes/unrenote', { noteId: note.id, }); os.toast(i18n.ts.rmboost); @@ -657,32 +632,34 @@ function onContextmenu(ev: MouseEvent): void { return; } - const isLink = (el: HTMLElement) => { + const isLink = (el: HTMLElement): boolean => { if (el.tagName === 'A') return true; // å†ç”Ÿé€Ÿåº¦ã®é¸æŠžãªã©ã®ãŸã‚ã«ã€Audioè¦ç´ ã®ã‚³ãƒ³ãƒ†ã‚ストメニューã¯ãƒ–ラウザデフォルトã¨ã™ã‚‹ã€‚ if (el.tagName === 'AUDIO') return true; if (el.parentElement) { return isLink(el.parentElement); } + return false; }; - if (isLink(ev.target)) return; - if (window.getSelection().toString() !== '') return; + + if (ev.target && isLink(ev.target as HTMLElement)) return; + if (window.getSelection()?.toString() !== '') return; if (defaultStore.state.useReactionPickerForContextMenu) { ev.preventDefault(); react(); } else { - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value }); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); os.contextMenu(menu, ev).then(focus).finally(cleanup); } } -function menu(viaKeyboard = false): void { +function showMenu(viaKeyboard = false): void { if (props.mock) { return; } - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value }); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); os.popupMenu(menu, menuButton.value, { viaKeyboard, }).then(focus).finally(cleanup); @@ -714,7 +691,7 @@ function showRenoteMenu(viaKeyboard = false): void { icon: 'ph-trash ph-bold ph-lg', danger: true, action: () => { - os.api('notes/delete', { + misskeyApi('notes/delete', { noteId: note.value.id, }); isDeleted.value = true; @@ -736,7 +713,7 @@ function showRenoteMenu(viaKeyboard = false): void { getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote), { type: 'divider' }, getAbuseNoteMenu(note.value, i18n.ts.reportAbuseRenote), - $i.isModerator || $i.isAdmin ? getUnrenote() : undefined, + ($i?.isModerator || $i?.isAdmin) ? getUnrenote() : undefined, ], renoteTime.value, { viaKeyboard: viaKeyboard, }); @@ -756,23 +733,23 @@ function animatedMFM() { } function focus() { - el.value.focus(); + rootEl.value?.focus(); } function blur() { - el.value.blur(); + rootEl.value?.blur(); } function focusBefore() { - focusPrev(el.value); + focusPrev(rootEl.value ?? null); } function focusAfter() { - focusNext(el.value); + focusNext(rootEl.value ?? null); } function readPromo() { - os.api('promo/read', { + misskeyApi('promo/read', { noteId: appearNote.value.id, }); isDeleted.value = true; @@ -819,19 +796,20 @@ function emitUpdReaction(emoji: string, delta: number) { margin: auto; width: calc(100% - 8px); height: calc(100% - 8px); - border: dashed 1px var(--focus); + border: solid 1px var(--focus); border-radius: var(--radius); box-sizing: border-box; } } .footer { + display: flex; + align-items: center; + justify-content: space-between; position: relative; z-index: 1; margin-top: 0.4em; - width: max-content; - min-width: min-content; - max-width: fit-content; + max-width: 400px; } &:hover > .article > .main > .footer > .footerButton { @@ -882,7 +860,6 @@ function emitUpdReaction(emoji: string, delta: number) { } .replyTo { - opacity: 0.7; padding-bottom: 0; } @@ -890,11 +867,28 @@ function emitUpdReaction(emoji: string, delta: number) { position: relative; display: flex; align-items: center; - padding: 24px 38px 16px; + padding: 24px 32px 0 calc(32px + var(--avatar) + 14px); line-height: 28px; white-space: pre; color: var(--renote); + &::before { + content: ''; + position: absolute; + top: 0; + left: calc(32px + .5 * var(--avatar)); + bottom: -8px; + border-left: var(--thread-width) solid var(--thread); + } + + &:first-child { + padding-left: 32px; + + &::before { + display: none; + } + } + & + .article { padding-top: 8px; } @@ -906,7 +900,7 @@ function emitUpdReaction(emoji: string, delta: number) { .renoteAvatar { flex-shrink: 0; - display: inline-block; + display: none; /* same as Firefish, but keeping the element around in case someone wants to add it back via CSS override */ width: 28px; height: 28px; margin: 0 8px 0 0; @@ -987,8 +981,8 @@ function emitUpdReaction(emoji: string, delta: number) { display: block !important; position: sticky !important; margin: 0 14px 0 0; - width: 58px; - height: 58px; + width: var(--avatar); + height: var(--avatar); position: sticky !important; top: calc(22px + var(--stickyTop, 0px)); left: 0; @@ -1130,24 +1124,24 @@ function emitUpdReaction(emoji: string, delta: number) { @container (max-width: 580px) { .root { font-size: 0.95em; + --avatar: 46px; } .renote { - padding: 24px 28px 16px; + padding: 24px 26px 0 calc(26px + var(--avatar) + 14px); + + &::before { + left: calc(26px + .5 * var(--avatar)); + } } .collapsedRenoteTarget { - padding: 8px 28px 24px; + padding: 8px 26px 24px; } .article { padding: 24px 26px; } - - .avatar { - width: 50px; - height: 50px; - } } @container (max-width: 500px) { @@ -1164,9 +1158,23 @@ function emitUpdReaction(emoji: string, delta: number) { } } +@container (max-width: 500px) { + .renote { + padding: 23px 25px 0 calc(25px + var(--avatar) + 14px); + + &::before { + left: calc(25px + .5 * var(--avatar)); + } + } +} + @container (max-width: 480px) { .renote { - padding: 20px 24px 8px; + padding: 22px 24px 0 calc(24px + var(--avatar) + 14px); + + &::before { + left: calc(24px + .5 * var(--avatar)); + } } .tip { @@ -1184,10 +1192,12 @@ function emitUpdReaction(emoji: string, delta: number) { } @container (max-width: 450px) { + .root { + --avatar: 44px; + } + .avatar { margin: 0 10px 0 0; - width: 46px; - height: 46px; top: calc(14px + var(--stickyTop, 0px)); } } @@ -1220,11 +1230,6 @@ function emitUpdReaction(emoji: string, delta: number) { } @container (max-width: 300px) { - .avatar { - width: 44px; - height: 44px; - } - .root:not(.showActionsOnlyHover) { .footerButton { &:not(:last-child) { @@ -1256,5 +1261,6 @@ function emitUpdReaction(emoji: string, delta: number) { .clickToOpen { cursor: pointer; + -webkit-tap-highlight-color: transparent; } </style> diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue index f850adba1b27aa42df77ee0e9891f983aa4de4de..ced7e7a1760a490ae29c20a19e134fed8a660416 100644 --- a/packages/frontend/src/components/SkNoteDetailed.vue +++ b/packages/frontend/src/components/SkNoteDetailed.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="!muted" v-show="!isDeleted" - ref="el" + ref="rootEl" v-hotkey="keymap" :class="$style.root" > @@ -40,10 +40,10 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> <template v-if="appearNote.reply && appearNote.reply.replyId"> - <SkNoteSub v-for="note in conversation" :key="note.id" :class="$style.replyToMore" :note="note" :expandAllCws="props.expandAllCws"/> + <SkNoteSub v-for="note in conversation" :key="note.id" :note="note" :expandAllCws="props.expandAllCws" detailed/> </template> - <SkNoteSub v-if="appearNote.reply" :note="appearNote.reply" :class="$style.replyTo" :expandAllCws="props.expandAllCws"/> - <article :class="$style.note" @contextmenu.stop="onContextmenu"> + <SkNoteSub v-if="appearNote.reply" :note="appearNote.reply" :class="$style.replyTo" :expandAllCws="props.expandAllCws" detailed/> + <article :id="appearNote.id" ref="noteEl" :class="$style.note" tabindex="-1" @contextmenu.stop="onContextmenu"> <header :class="$style.noteHeader"> <MkAvatar :class="$style.noteHeaderAvatar" :user="appearNote.user" indicator link preview/> <div style="display: flex; align-items: center; white-space: nowrap; overflow: hidden;"> @@ -68,7 +68,7 @@ SPDX-License-Identifier: AGPL-3.0-only <i v-else-if="appearNote.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i> <i v-else-if="appearNote.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i> </span> - <span v-if="appearNote.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span> + <span v-if="appearNote.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil-simple ph-bold ph-lg"></i></span> <span v-if="appearNote.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span> </div> <SkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/> @@ -96,32 +96,32 @@ SPDX-License-Identifier: AGPL-3.0-only <a v-if="appearNote.renote != null" :class="$style.rn">RN:</a> <div v-if="translating || translation" :class="$style.translation"> <MkLoading v-if="translating" mini/> - <div v-else> - <b>{{ i18n.t('translatedFrom', { x: translation.sourceLang }) }}: </b> + <div v-else-if="translation"> + <b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b> <Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> </div> </div> <MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton> <MkButton v-else-if="!defaultStore.state.animatedMfm && allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton> - <div v-if="appearNote.files.length > 0"> + <div v-if="appearNote.files && appearNote.files.length > 0"> <MkMediaList :mediaList="appearNote.files"/> </div> - <MkPoll v-if="appearNote.poll" ref="pollViewer" :note="appearNote" :class="$style.poll"/> + <MkPoll v-if="appearNote.poll" ref="pollViewer" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll"/> <MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="true" style="margin-top: 6px;"/> <div v-if="appearNote.renote" :class="$style.quote"><SkNoteSimple :note="appearNote.renote" :class="$style.quoteNote" :expandAllCws="props.expandAllCws"/></div> </div> <MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ph-television ph-bold ph-lg"></i> {{ appearNote.channel.name }}</MkA> </div> - <footer :class="$style.footer"> - <div :class="$style.noteFooterInfo"> - <div v-if="appearNote.updatedAt"> - {{ i18n.ts.edited }}: <MkTime :time="appearNote.updatedAt" mode="detail"/> - </div> - <MkA :to="notePage(appearNote)"> - <MkTime :time="appearNote.createdAt" mode="detail" colored/> - </MkA> + <div :class="$style.noteFooterInfo"> + <div v-if="appearNote.updatedAt"> + {{ i18n.ts.edited }}: <MkTime :time="appearNote.updatedAt" mode="detail"/> </div> - <MkReactionsViewer ref="reactionsViewer" :note="appearNote"/> + <MkA :to="notePage(appearNote)"> + <MkTime :time="appearNote.createdAt" mode="detail" colored/> + </MkA> + </div> + <MkReactionsViewer ref="reactionsViewer" :note="appearNote"/> + <footer :class="$style.footer"> <button class="_button" :class="$style.noteFooterButton" @click="reply()"> <i class="ph-arrow-u-up-left ph-bold ph-lg"></i> <p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ appearNote.repliesCount }}</p> @@ -162,7 +162,7 @@ SPDX-License-Identifier: AGPL-3.0-only <button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown="clip()"> <i class="ph-paperclip ph-bold ph-lg"></i> </button> - <button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown="menu()"> + <button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown="showMenu()"> <i class="ph-dots-three ph-bold ph-lg"></i> </button> </footer> @@ -174,11 +174,11 @@ SPDX-License-Identifier: AGPL-3.0-only <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'reactions' }]" @click="tab = 'reactions'"><i class="ph-smiley ph-bold ph-lg"></i> {{ i18n.ts.reactions }}</button> </div> <div> - <div v-if="tab === 'replies'" :class="$style.tab_replies"> + <div v-if="tab === 'replies'"> <div v-if="!repliesLoaded" style="padding: 16px"> <MkButton style="margin: 0 auto;" primary rounded @click="loadReplies">{{ i18n.ts.loadReplies }}</MkButton> </div> - <SkNoteSub v-for="note in replies" :key="note.id" :note="note" :class="$style.reply" :detail="true" :expandAllCws="props.expandAllCws" :onDeleteCallback="removeReply" /> + <SkNoteSub v-for="note in replies" :key="note.id" :note="note" :class="$style.reply" :detail="true" :expandAllCws="props.expandAllCws" :onDeleteCallback="removeReply" :isReply="true"/> </div> <div v-else-if="tab === 'renotes'" :class="$style.tab_renotes"> <MkPagination :pagination="renotesPagination" :disableAutoLoad="true"> @@ -191,11 +191,11 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </MkPagination> </div> - <div v-if="tab === 'quotes'" :class="$style.tab_replies"> + <div v-if="tab === 'quotes'"> <div v-if="!quotesLoaded" style="padding: 16px"> <MkButton style="margin: 0 auto;" primary rounded @click="loadQuotes">{{ i18n.ts.loadReplies }}</MkButton> </div> - <SkNoteSub v-for="note in quotes" :key="note.id" :note="note" :class="$style.reply" :detail="true" :expandAllCws="props.expandAllCws"/> + <SkNoteSub v-for="note in quotes" :key="note.id" :note="note" :class="$style.reply" :detail="true" :expandAllCws="props.expandAllCws" :reply="true"/> </div> <div v-else-if="tab === 'reactions'" :class="$style.tab_reactions"> <div :class="$style.reactionTabs"> @@ -228,8 +228,8 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, inject, onMounted, provide, ref, shallowRef, watch } from 'vue'; -import * as mfm from '@sharkey/sfm-js'; +import { computed, inject, onMounted, onUnmounted, onUpdated, provide, ref, shallowRef, watch } from 'vue'; +import * as mfm from '@transfem-org/sfm-js'; import * as Misskey from 'misskey-js'; import SkNoteSub from '@/components/SkNoteSub.vue'; import SkNoteSimple from '@/components/SkNoteSimple.vue'; @@ -245,6 +245,7 @@ import { checkWordMute } from '@/scripts/check-word-mute.js'; import { userPage } from '@/filters/user.js'; import { notePage } from '@/filters/note.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import * as sound from '@/scripts/sound.js'; import { defaultStore, noteViewInterruptors } from '@/store.js'; import { reactionPicker } from '@/scripts/reaction-picker.js'; @@ -261,9 +262,10 @@ import { checkAnimationFromMfm } from '@/scripts/check-animated-mfm.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; -import MkPagination from '@/components/MkPagination.vue'; +import MkPagination, { type Paging } from '@/components/MkPagination.vue'; import MkReactionIcon from '@/components/MkReactionIcon.vue'; import MkButton from '@/components/MkButton.vue'; +import { boostMenuItems, type Visibility } from '@/scripts/boost-quote.js'; const props = defineProps<{ note: Misskey.entities.Note; @@ -280,7 +282,7 @@ if (noteViewInterruptors.length > 0) { let result: Misskey.entities.Note | null = deepClone(note.value); for (const interruptor of noteViewInterruptors) { try { - result = await interruptor.handler(result); + result = await interruptor.handler(result!) as Misskey.entities.Note | null; if (result === null) { isDeleted.value = true; return; @@ -289,18 +291,19 @@ if (noteViewInterruptors.length > 0) { console.error(err); } } - note.value = result; + note.value = result as Misskey.entities.Note; }); } const isRenote = ( note.value.renote != null && note.value.text == null && - note.value.fileIds.length === 0 && + note.value.fileIds && note.value.fileIds.length === 0 && note.value.poll == null ); -const el = shallowRef<HTMLElement>(); +const rootEl = shallowRef<HTMLElement>(); +const noteEl = shallowRef<HTMLElement>(); const menuButton = shallowRef<HTMLElement>(); const menuVersionsButton = shallowRef<HTMLElement>(); const renoteButton = shallowRef<HTMLElement>(); @@ -310,24 +313,22 @@ const quoteButton = shallowRef<HTMLElement>(); const clipButton = shallowRef<HTMLElement>(); const likeButton = shallowRef<HTMLElement>(); const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value); -const renoteUrl = appearNote.value.renote ? appearNote.value.renote.url : null; -const renoteUri = appearNote.value.renote ? appearNote.value.renote.uri : null; const isMyRenote = $i && ($i.id === note.value.userId); const showContent = ref(defaultStore.state.uncollapseCW); const isDeleted = ref(false); const renoted = ref(false); const muted = ref($i ? checkWordMute(appearNote.value, $i, $i.mutedWords) : false); -const translation = ref(null); +const translation = ref<Misskey.entities.NotesTranslateResponse | null>(null); const translating = ref(false); const parsed = appearNote.value.text ? mfm.parse(appearNote.value.text) : null; -const urls = parsed ? extractUrlFromMfm(parsed).filter(u => u !== renoteUrl && u !== renoteUri) : null; +const urls = parsed ? extractUrlFromMfm(parsed).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null; const animated = computed(() => parsed ? checkAnimationFromMfm(parsed) : null); const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false); const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance); const conversation = ref<Misskey.entities.Note[]>([]); const replies = ref<Misskey.entities.Note[]>([]); const quotes = ref<Misskey.entities.Note[]>([]); -const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || appearNote.value.userId === $i.id); +const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i?.id)); const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null); watch(() => props.expandAllCws, (expandAllCws) => { @@ -335,7 +336,7 @@ watch(() => props.expandAllCws, (expandAllCws) => { }); if ($i) { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -347,23 +348,23 @@ if ($i) { const keymap = { 'r': () => reply(true), 'e|a|plus': () => react(true), - 'q': () => renoteButton.value.renote(true), + 'q': () => renote(appearNote.value.visibility), 'esc': blur, - 'm|o': () => menu(true), + 'm|o': () => showMenu(true), 's': () => showContent.value !== showContent.value, }; provide('react', (reaction: string) => { - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, reaction: reaction, }); }); const tab = ref('replies'); -const reactionTabType = ref(null); +const reactionTabType = ref<string | null>(null); -const renotesPagination = computed(() => ({ +const renotesPagination = computed<Paging>(() => ({ endpoint: 'notes/renotes', limit: 10, params: { @@ -371,7 +372,7 @@ const renotesPagination = computed(() => ({ }, })); -const reactionsPagination = computed(() => ({ +const reactionsPagination = computed<Paging>(() => ({ endpoint: 'notes/reactions', limit: 10, params: { @@ -381,20 +382,20 @@ const reactionsPagination = computed(() => ({ })); async function addReplyTo(replyNote: Misskey.entities.Note) { - replies.value.unshift(replyNote); - appearNote.value.repliesCount += 1; + replies.value.unshift(replyNote); + appearNote.value.repliesCount += 1; } async function removeReply(id: Misskey.entities.Note['id']) { - const replyIdx = replies.value.findIndex(note => note.id === id); - if (replyIdx >= 0) { - replies.value.splice(replyIdx, 1); - appearNote.value.repliesCount -= 1; - } + const replyIdx = replies.value.findIndex(note => note.id === id); + if (replyIdx >= 0) { + replies.value.splice(replyIdx, 1); + appearNote.value.repliesCount -= 1; + } } useNoteCapture({ - rootEl: el, + rootEl: rootEl, note: appearNote, pureNote: note, isDeletedRef: isDeleted, @@ -402,7 +403,7 @@ useNoteCapture({ }); useTooltip(renoteButton, async (showing) => { - const renotes = await os.api('notes/renotes', { + const renotes = await misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 11, }); @@ -420,7 +421,7 @@ useTooltip(renoteButton, async (showing) => { }); useTooltip(quoteButton, async (showing) => { - const renotes = await os.api('notes/renotes', { + const renotes = await misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 11, quote: true, @@ -438,53 +439,15 @@ useTooltip(quoteButton, async (showing) => { }, {}, 'closed'); }); -type Visibility = 'public' | 'home' | 'followers' | 'specified'; - -function smallerVisibility(a: Visibility | string, b: Visibility | string): Visibility { - if (a === 'specified' || b === 'specified') return 'specified'; - if (a === 'followers' || b === 'followers') return 'followers'; - if (a === 'home' || b === 'home') return 'home'; - // if (a === 'public' || b === 'public') - return 'public'; -} - function boostVisibility() { - os.popupMenu([ - { - type: 'button', - icon: 'ph-globe-hemisphere-west ph-bold ph-lg', - text: i18n.ts._visibility['public'], - action: () => { - renote('public'); - }, - }, - { - type: 'button', - icon: 'ph-house ph-bold ph-lg', - text: i18n.ts._visibility['home'], - action: () => { - renote('home'); - }, - }, - { - type: 'button', - icon: 'ph-lock ph-bold ph-lg', - text: i18n.ts._visibility['followers'], - action: () => { - renote('followers'); - }, - }, - { - type: 'button', - icon: 'ph-planet ph-bold ph-lg', - text: i18n.ts._timelines.local, - action: () => { - renote('local'); - }, - }], renoteButton.value); + if (!defaultStore.state.showVisibilitySelectorOnBoost) { + renote(defaultStore.state.visibilityOnBoost); + } else { + os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value); + } } -function renote(visibility: Visibility | 'local') { +function renote(visibility: Visibility, localOnly: boolean = false) { pleaseLogin(); showMovedDialog(); @@ -497,14 +460,14 @@ function renote(visibility: Visibility | 'local') { os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - os.api('notes/create', { + misskeyApi('notes/create', { renoteId: appearNote.value.id, channelId: appearNote.value.channelId, }).then(() => { os.toast(i18n.ts.renoted); renoted.value = true; }); - } else if (!appearNote.value.channel || appearNote.value.channel?.allowRenoteToExternal) { + } else if (!appearNote.value.channel || appearNote.value.channel.allowRenoteToExternal) { const el = renoteButton.value as HTMLElement | null | undefined; if (el) { const rect = el.getBoundingClientRect(); @@ -513,17 +476,9 @@ function renote(visibility: Visibility | 'local') { os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility; - const localOnlySetting = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly; - - let noteVisibility = visibility === 'local' || visibility === 'specified' ? smallerVisibility(appearNote.value.visibility, configuredVisibility) : smallerVisibility(visibility, configuredVisibility); - if (appearNote.value.channel?.isSensitive) { - noteVisibility = smallerVisibility(visibility === 'local' || visibility === 'specified' ? appearNote.value.visibility : visibility, 'home'); - } - - os.api('notes/create', { - localOnly: visibility === 'local' ? true : localOnlySetting, - visibility: noteVisibility, + misskeyApi('notes/create', { + localOnly: localOnly, + visibility: visibility, renoteId: appearNote.value.id, }).then(() => { os.toast(i18n.ts.renoted); @@ -541,9 +496,9 @@ function quote() { renote: appearNote.value, channel: appearNote.value.channel, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, - userId: $i.id, + userId: $i?.id, limit: 1, quote: true, }).then((res) => { @@ -563,9 +518,9 @@ function quote() { os.post({ renote: appearNote.value, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, - userId: $i.id, + userId: $i?.id, limit: 1, quote: true, }).then((res) => { @@ -591,7 +546,7 @@ function reply(viaKeyboard = false): void { reply: appearNote.value, channel: appearNote.value.channel, animation: !viaKeyboard, - }, () => { + }).then(() => { focus(); }); } @@ -600,7 +555,9 @@ function react(viaKeyboard = false): void { pleaseLogin(); showMovedDialog(); if (appearNote.value.reactionAcceptance === 'likeOnly') { - os.api('notes/like', { + sound.playMisskeySfx('reaction'); + + misskeyApi('notes/like', { noteId: appearNote.value.id, override: defaultLike.value, }); @@ -613,10 +570,10 @@ function react(viaKeyboard = false): void { } } else { blur(); - reactionPicker.show(reactButton.value, reaction => { - sound.play('reaction'); + reactionPicker.show(reactButton.value ?? null, note.value, reaction => { + sound.playMisskeySfx('reaction'); - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, reaction: reaction, }); @@ -632,7 +589,8 @@ function react(viaKeyboard = false): void { function like(): void { pleaseLogin(); showMovedDialog(); - os.api('notes/like', { + sound.playMisskeySfx('reaction'); + misskeyApi('notes/like', { noteId: appearNote.value.id, override: defaultLike.value, }); @@ -648,14 +606,14 @@ function like(): void { function undoReact(note): void { const oldReaction = note.myReaction; if (!oldReaction) return; - os.api('notes/reactions/delete', { + misskeyApi('notes/reactions/delete', { noteId: note.id, }); } function undoRenote() : void { if (!renoted.value) return; - os.api('notes/unrenote', { + misskeyApi('notes/unrenote', { noteId: appearNote.value.id, }); os.toast(i18n.ts.rmboost); @@ -671,26 +629,28 @@ function undoRenote() : void { } function onContextmenu(ev: MouseEvent): void { - const isLink = (el: HTMLElement) => { + const isLink = (el: HTMLElement): boolean => { if (el.tagName === 'A') return true; if (el.parentElement) { return isLink(el.parentElement); } + return false; }; - if (isLink(ev.target)) return; - if (window.getSelection().toString() !== '') return; + + if (ev.target && isLink(ev.target as HTMLElement)) return; + if (window.getSelection()?.toString() !== '') return; if (defaultStore.state.useReactionPickerForContextMenu) { ev.preventDefault(); react(); } else { - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted }); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted }); os.contextMenu(menu, ev).then(focus).finally(cleanup); } } -function menu(viaKeyboard = false): void { - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted }); +function showMenu(viaKeyboard = false): void { + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted }); os.popupMenu(menu, menuButton.value, { viaKeyboard, }).then(focus).finally(cleanup); @@ -715,7 +675,7 @@ function showRenoteMenu(viaKeyboard = false): void { icon: 'ph-trash ph-bold ph-lg', danger: true, action: () => { - os.api('notes/delete', { + misskeyApi('notes/delete', { noteId: note.value.id, }); isDeleted.value = true; @@ -726,18 +686,18 @@ function showRenoteMenu(viaKeyboard = false): void { } function focus() { - el.value.focus(); + noteEl.value?.focus(); } function blur() { - el.value.blur(); + noteEl.value?.blur(); } const repliesLoaded = ref(false); function loadReplies() { repliesLoaded.value = true; - os.api('notes/children', { + misskeyApi('notes/children', { noteId: appearNote.value.id, limit: 30, showQuotes: false, @@ -752,7 +712,7 @@ const quotesLoaded = ref(false); function loadQuotes() { quotesLoaded.value = true; - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 30, quote: true, @@ -767,10 +727,12 @@ const conversationLoaded = ref(false); function loadConversation() { conversationLoaded.value = true; - os.api('notes/conversation', { + if (appearNote.value.replyId == null) return; + misskeyApi('notes/conversation', { noteId: appearNote.value.replyId, }).then(res => { conversation.value = res.reverse(); + focus(); }); } @@ -787,6 +749,31 @@ function animatedMFM() { }).then((res) => { if (!res.canceled) allowAnim.value = true; }); } } + +let isScrolling = false; + +function setScrolling() { + isScrolling = true; +} + +onMounted(() => { + document.addEventListener('wheel', setScrolling); + isScrolling = false; + noteEl.value?.scrollIntoView({ block: 'center' }); +}); + +onUpdated(() => { + if (!isScrolling) { + noteEl.value?.scrollIntoView({ block: 'center' }); + if (location.hash) { + location.replace(location.hash); // Jump to highlighted reply + } + } +}); + +onUnmounted(() => { + document.removeEventListener('wheel', setScrolling); +}); </script> <style lang="scss" module> @@ -798,23 +785,19 @@ function animatedMFM() { } .footer { + display: flex; + align-items: center; + justify-content: space-between; position: relative; z-index: 1; margin-top: 0.4em; - width: max-content; - min-width: min-content; - max-width: fit-content; + max-width: 400px; } .replyTo { - opacity: 0.7; padding-bottom: 0; } -.replyToMore { - opacity: 0.7; -} - .renote { display: flex; align-items: center; @@ -859,6 +842,7 @@ function animatedMFM() { } .note { + position: relative; padding: 32px; font-size: 1.2em; overflow: hidden; @@ -866,6 +850,28 @@ function animatedMFM() { &:hover > .main > .footer > .button { opacity: 1; } + + &:focus-visible { + outline: none; + + &:after { + content: ""; + pointer-events: none; + display: block; + position: absolute; + z-index: 10; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: auto; + width: calc(100% - 8px); + height: calc(100% - 8px); + border: solid 1px var(--focus); + border-radius: var(--radius); + box-sizing: border-box; + } + } } .noteHeader { @@ -879,8 +885,8 @@ function animatedMFM() { .noteHeaderAvatar { display: block; flex-shrink: 0; - width: 58px; - height: 58px; + width: var(--avatar); + height: var(--avatar); } .noteHeaderBody { @@ -1021,10 +1027,17 @@ function animatedMFM() { } .tab { + display: flex; + align-items: center; + justify-content: center; flex: 1; padding: 12px 8px; border-top: solid 2px transparent; border-bottom: solid 2px transparent; + + > i { + margin-right: 8px; + } } .tabActive { diff --git a/packages/frontend/src/components/SkNoteHeader.vue b/packages/frontend/src/components/SkNoteHeader.vue index d3ecdf17bb2afe0945de7685260d37f40ae94c67..7dc4c8f0191bf0a359ef4a3ae05b96ffd0105b59 100644 --- a/packages/frontend/src/components/SkNoteHeader.vue +++ b/packages/frontend/src/components/SkNoteHeader.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkA> <div v-if="note.user.isBot" :class="$style.isBot">bot</div> <div v-if="note.user.badgeRoles" :class="$style.badgeRoles"> - <img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/> + <img v-for="(role, i) in note.user.badgeRoles" :key="i" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl!"/> </div> </div> <div :class="$style.username"><MkAcct :user="note.user"/></div> @@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only <i v-else-if="note.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i> <i v-else-if="note.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i> </span> - <span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em; cursor: pointer;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span> + <span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em; cursor: pointer;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil-simple ph-bold ph-lg"></i></span> <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span> <span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ph-television ph-bold ph-lg"></i></span> </div> @@ -65,7 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only <i v-else-if="note.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i> <i v-else-if="note.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i> </span> - <span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em; cursor: pointer;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span> + <span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em; cursor: pointer;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil-simple ph-bold ph-lg"></i></span> <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span> <span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ph-television ph-bold ph-lg"></i></span> </div> @@ -82,7 +82,7 @@ import { getNoteVersionsMenu } from '@/scripts/get-note-versions-menu.js'; import SkInstanceTicker from '@/components/SkInstanceTicker.vue'; import { popupMenu } from '@/os.js'; import { defaultStore } from '@/store.js'; -import { useRouter } from '@/router.js'; +import { useRouter } from '@/router/supplier.js'; import { deviceKind } from '@/scripts/device-kind.js'; const props = defineProps<{ @@ -116,6 +116,8 @@ const mock = inject<boolean>('mock', false); .root { display: flex; cursor: auto; /* not clickToOpen-able */ + min-height: 100%; + align-items: center; } .classicRoot { @@ -135,6 +137,7 @@ const mock = inject<boolean>('mock', false); display: flex; align-items: flex-end; margin-left: auto; + margin-bottom: auto; padding-left: 10px; overflow: clip; } @@ -143,10 +146,9 @@ const mock = inject<boolean>('mock', false); .name { flex-shrink: 1; display: block; - // note, these margin top values were done by hand may need futher checking if it actualy aligns pixel perfect - margin: 3px .5em 0 0; + margin: 0 .5em 0 0; padding: 0; - overflow: scroll; + overflow: hidden; overflow-wrap: anywhere; font-size: 1em; font-weight: bold; @@ -192,8 +194,7 @@ const mock = inject<boolean>('mock', false); .username { flex-shrink: 9999999; - // note these top margins were made to align with the instance ticker - margin: 4px .5em 0 0; + margin: 0 .5em 0 0; overflow: hidden; text-overflow: ellipsis; font-size: .95em; diff --git a/packages/frontend/src/components/SkNoteSimple.vue b/packages/frontend/src/components/SkNoteSimple.vue index fe12baedebef01190e35c1f9666ee78876d2f7ba..533aa609614e9a3bda9ebea198bb7c12c6d1a45d 100644 --- a/packages/frontend/src/components/SkNoteSimple.vue +++ b/packages/frontend/src/components/SkNoteSimple.vue @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkCwButton v-model="showContent" :text="note.text" :files="note.files" :poll="note.poll" @click.stop/> </p> <div v-show="note.cw == null || showContent"> - <MkSubNoteContent :hideFiles="hideFiles" :class="$style.text" :note="note"/> + <MkSubNoteContent :hideFiles="hideFiles" :class="$style.text" :note="note" :expandAllCws="props.expandAllCws"/> </div> </div> </div> @@ -48,6 +48,11 @@ watch(() => props.expandAllCws, (expandAllCws) => { margin: 0; padding: 0; font-size: 0.95em; + + &:hover, &:focus-within { + background: var(--panelHighlight); + transition: background .2s; + } } .avatar { diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue index bc482294b40d94859bd863210206d3d896e59e16..1cffd8dd66d1df4fb3122dca2fc8e742916f5fe0 100644 --- a/packages/frontend/src/components/SkNoteSub.vue +++ b/packages/frontend/src/components/SkNoteSub.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div v-show="!isDeleted" v-if="!muted" ref="el" :class="[$style.root, { [$style.children]: depth > 1 }]"> +<div v-show="!isDeleted" v-if="!muted" ref="el" :class="[$style.root, { [$style.children]: depth > 1, [$style.isReply]: props.isReply, [$style.detailed]: props.detailed }]"> <div v-if="!hideLine" :class="$style.line"></div> <div :class="$style.main"> <div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div> @@ -24,11 +24,11 @@ SPDX-License-Identifier: AGPL-3.0-only <MkCwButton v-model="showContent" :text="note.text" :files="note.files" :poll="note.poll"/> </p> <div v-show="note.cw == null || showContent"> - <MkSubNoteContent :class="$style.text" :note="note" :translating="translating" :translation="translation"/> + <MkSubNoteContent :class="$style.text" :note="note" :translating="translating" :translation="translation" :expandAllCws="props.expandAllCws"/> </div> </div> + <MkReactionsViewer ref="reactionsViewer" :note="note"/> <footer :class="$style.footer"> - <MkReactionsViewer ref="reactionsViewer" :note="note"/> <button class="_button" :class="$style.noteFooterButton" @click="reply()"> <i class="ph-arrow-u-up-left ph-bold ph-lg"></i> <p v-if="note.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ note.repliesCount }}</p> @@ -73,7 +73,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> <template v-if="depth < numberOfReplies"> - <SkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="[$style.reply, { [$style.single]: replies.length === 1 }]" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws" :onDeleteCallback="removeReply"/> + <SkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="[$style.reply, { [$style.single]: replies.length === 1 }]" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws" :onDeleteCallback="removeReply" :isReply="props.isReply"/> </template> <div v-else :class="$style.more"> <MkA class="_link" :to="notePage(note)">{{ i18n.ts.continueThread }} <i class="ph-caret-double-right ph-bold ph-lg"></i></MkA> @@ -99,6 +99,8 @@ import MkSubNoteContent from '@/components/MkSubNoteContent.vue'; import MkCwButton from '@/components/MkCwButton.vue'; import { notePage } from '@/filters/note.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; +import * as sound from '@/scripts/sound.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; import { userPage } from '@/filters/user.js'; @@ -111,6 +113,7 @@ import { reactionPicker } from '@/scripts/reaction-picker.js'; import { claimAchievement } from '@/scripts/achievements.js'; import { getNoteMenu } from '@/scripts/get-note-menu.js'; import { useNoteCapture } from '@/scripts/use-note-capture.js'; +import { boostMenuItems, type Visibility } from '@/scripts/boost-quote.js'; const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id); const hideLine = computed(() => { return props.detail ? true : false; }); @@ -123,8 +126,13 @@ const props = withDefaults(defineProps<{ // how many notes are in between this one and the note being viewed in detail depth?: number; + + isReply?: boolean; + detailed?: boolean; }>(), { depth: 1, + isReply: false, + detailed: false, }); const el = shallowRef<HTMLElement>(); @@ -147,7 +155,7 @@ const replies = ref<Misskey.entities.Note[]>([]); const isRenote = ( props.note.renote != null && props.note.text == null && - props.note.fileIds.length === 0 && + props.note.fileIds && props.note.fileIds.length === 0 && props.note.poll == null ); @@ -174,7 +182,7 @@ useNoteCapture({ }); if ($i) { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -202,8 +210,9 @@ function reply(viaKeyboard = false): void { function react(viaKeyboard = false): void { pleaseLogin(); showMovedDialog(); + sound.playMisskeySfx('reaction'); if (props.note.reactionAcceptance === 'likeOnly') { - os.api('notes/like', { + misskeyApi('notes/like', { noteId: props.note.id, override: defaultLike.value, }); @@ -216,8 +225,8 @@ function react(viaKeyboard = false): void { } } else { blur(); - reactionPicker.show(reactButton.value, reaction => { - os.api('notes/reactions/create', { + reactionPicker.show(reactButton.value ?? null, props.note, reaction => { + misskeyApi('notes/reactions/create', { noteId: props.note.id, reaction: reaction, }); @@ -233,7 +242,8 @@ function react(viaKeyboard = false): void { function like(): void { pleaseLogin(); showMovedDialog(); - os.api('notes/like', { + sound.playMisskeySfx('reaction'); + misskeyApi('notes/like', { noteId: props.note.id, override: defaultLike.value, }); @@ -249,14 +259,14 @@ function like(): void { function undoReact(note): void { const oldReaction = note.myReaction; if (!oldReaction) return; - os.api('notes/reactions/delete', { + misskeyApi('notes/reactions/delete', { noteId: note.id, }); } function undoRenote() : void { if (!renoted.value) return; - os.api('notes/unrenote', { + misskeyApi('notes/unrenote', { noteId: appearNote.value.id, }); os.toast(i18n.ts.rmboost); @@ -278,42 +288,14 @@ watch(() => props.expandAllCws, (expandAllCws) => { }); function boostVisibility() { - os.popupMenu([ - { - type: 'button', - icon: 'ph-globe-hemisphere-west ph-bold ph-lg', - text: i18n.ts._visibility['public'], - action: () => { - renote('public'); - }, - }, - { - type: 'button', - icon: 'ph-house ph-bold ph-lg', - text: i18n.ts._visibility['home'], - action: () => { - renote('home'); - }, - }, - { - type: 'button', - icon: 'ph-lock ph-bold ph-lg', - text: i18n.ts._visibility['followers'], - action: () => { - renote('followers'); - }, - }, - { - type: 'button', - icon: 'ph-planet ph-bold ph-lg', - text: i18n.ts._timelines.local, - action: () => { - renote('local'); - }, - }], renoteButton.value); -} - -function renote(visibility: 'public' | 'home' | 'followers' | 'specified' | 'local') { + if (!defaultStore.state.showVisibilitySelectorOnBoost) { + renote(defaultStore.state.visibilityOnBoost); + } else { + os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value); + } +} + +function renote(visibility: Visibility, localOnly: boolean = false) { pleaseLogin(); showMovedDialog(); @@ -326,9 +308,9 @@ function renote(visibility: 'public' | 'home' | 'followers' | 'specified' | 'loc os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - os.api('notes/create', { - renoteId: props.note.id, - channelId: props.note.channelId, + misskeyApi('notes/create', { + renoteId: appearNote.value.id, + channelId: appearNote.value.channelId, }).then(() => { os.toast(i18n.ts.renoted); renoted.value = true; @@ -342,10 +324,10 @@ function renote(visibility: 'public' | 'home' | 'followers' | 'specified' | 'loc os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - os.api('notes/create', { - renoteId: props.note.id, - localOnly: visibility === 'local' ? true : false, - visibility: visibility === 'local' || visibility === 'specified' ? props.note.visibility : visibility, + misskeyApi('notes/create', { + renoteId: appearNote.value.id, + localOnly: localOnly, + visibility: visibility, }).then(() => { os.toast(i18n.ts.renoted); renoted.value = true; @@ -362,7 +344,7 @@ function quote() { renote: appearNote.value, channel: appearNote.value.channel, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: props.note.id, userId: $i.id, limit: 1, @@ -384,7 +366,7 @@ function quote() { os.post({ renote: appearNote.value, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: props.note.id, userId: $i.id, limit: 1, @@ -413,7 +395,7 @@ function menu(viaKeyboard = false): void { } if (props.detail) { - os.api('notes/children', { + misskeyApi('notes/children', { noteId: props.note.id, limit: numberOfReplies.value, showQuotes: false, @@ -426,35 +408,61 @@ if (props.detail) { <style lang="scss" module> .root { padding: 28px 32px; - font-size: 0.9em; position: relative; + --reply-indent: calc(.5 * var(--avatar)); + &.children { - padding: 10px 0 0 16px; - font-size: 1em; + padding: 10px 0 0 8px; + } + + &.isReply { + /* @link https://utopia.fyi/clamp/calculator?a=450,580,26—36 */ + --avatar: clamp(26px, -8.6154px + 7.6923cqi, 36px); } } .line { position: absolute; - height: calc(100% - 58px); // 58px of avatar height (see SkNote) - left: 60px; + left: calc(32px + .5 * var(--avatar)); // using solid instead of dotted, stylelistic choice - border-left: 2.5px solid rgb(174, 174, 174); - top: 86px; // 28px of .root padding, plus 58px of avatar height (see SkNote) + border-left: var(--thread-width) solid var(--thread); + top: calc(28px + var(--avatar)); // 28px of .root padding, plus 58px of avatar height (see SkNote) + bottom: -28px; } .footer { + display: flex; + align-items: center; + justify-content: space-between; position: relative; z-index: 1; margin-top: 0.4em; - width: max-content; - min-width: min-content; - max-width: fit-content; + max-width: 400px; } .main { - display: flex; + position: relative; + display: flex; + + :is(.detailed, .isReply) &::after { + content: ""; + position: absolute; + top: -12px; + right: -12px; + left: -12px; + bottom: -12px; + background: var(--panelHighlight); + border-radius: var(--radius); + opacity: 0; + transition: opacity .2s, background .2s; + z-index: -1; + } + + :is(.detailed, .isReply) &:hover::after, + :is(.detailed, .isReply) &:focus-within::after { + opacity: 1; + } } .colorBar { @@ -471,8 +479,8 @@ if (props.detail) { flex-shrink: 0; display: block; margin: 0 14px 0 0; - width: 58px; - height: 58px; + width: var(--avatar); + height: var(--avatar); border-radius: var(--radius-sm); } @@ -500,10 +508,6 @@ if (props.detail) { padding-top: 10px; opacity: 0.7; - &:not(:last-child) { - margin-right: 1.5em; - } - &:hover { color: var(--fgHighlighted); } @@ -521,15 +525,11 @@ if (props.detail) { @container (max-width: 580px) { .root { padding: 28px 26px 0; + --avatar: 46px; } .line { - left: 50.5px; - } - - .avatar { - width: 50px; - height: 50px; + left: calc(26px + .5 * var(--avatar)); } } @@ -537,6 +537,11 @@ if (props.detail) { .root { padding: 23px 25px; } + + .line { + top: calc(23px + var(--avatar)); + left: calc(25px + .5 * var(--avatar)); + } } @container (max-width: 400px) { @@ -581,21 +586,17 @@ if (props.detail) { @container (max-width: 480px) { .root { padding: 22px 24px; - - &.children { - padding: 10px 0 0 8px; - } } -} -@container (max-width: 450px) { .line { - left: 46px; + top: calc(22px + var(--avatar)); + left: calc(24px + .5 * var(--avatar)); } +} - .avatar { - width: 46px; - height: 46px; +@container (max-width: 450px) { + .root { + --avatar: 44px; } } @@ -616,19 +617,19 @@ if (props.detail) { .threadLine { width: 0; flex-grow: 1; - border-left: 2.5px solid rgb(174, 174, 174); - margin-left: 29px; + border-left: var(--thread-width) solid var(--thread); + margin-left: var(--reply-indent); } .reply { - margin-left: 29px; + margin-left: var(--reply-indent); } .reply:not(:last-child) { - border-left: 2.5px solid rgb(174, 174, 174); + border-left: var(--thread-width) solid var(--thread); &::before { - left: -2px; + left: calc(-1 * var(--thread-width)); } } @@ -637,10 +638,10 @@ if (props.detail) { content: ''; left: 0px; top: -10px; - height: 49px; + height: calc(10px + 10px + .5 * var(--avatar)); width: 15px; - border-left: 2.5px solid rgb(174, 174, 174); - border-bottom: 2.5px solid rgb(174, 174, 174); + border-left: var(--thread-width) solid var(--thread); + border-bottom: var(--thread-width) solid var(--thread); border-bottom-left-radius: 15px; } @@ -649,40 +650,9 @@ if (props.detail) { padding-left: 0 !important; &::before { - left: 29px; + left: var(--reply-indent); width: 0; border-bottom: unset; } } - -@container (max-width: 580px) { - .threadLine, .reply { - margin-left: 25px; - } - .reply::before { - height: 45px; - } - .single::before { - left: 25px; - } - .single { - margin-left: 0; - } -} - -@container (max-width: 450px) { - .threadLine, .reply { - margin-left: 23px; - } - .reply::before { - height: 43px; - } - .single::before { - left: 23px; - width: 9px; - } - .single { - margin-left: 0; - } -} </style> diff --git a/packages/frontend/src/components/SkOldNoteWindow.vue b/packages/frontend/src/components/SkOldNoteWindow.vue index f8de28e346d20b09d657fb1dfa49da98c515e1d5..bed44bbb08f1bbee83d3b405ac6b4a27bf71f0e4 100644 --- a/packages/frontend/src/components/SkOldNoteWindow.vue +++ b/packages/frontend/src/components/SkOldNoteWindow.vue @@ -77,7 +77,7 @@ <script lang="ts" setup> import { inject, onMounted, ref, shallowRef, computed } from 'vue'; -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; import * as Misskey from 'misskey-js'; import MkNoteSimple from '@/components/MkNoteSimple.vue'; import MkMediaList from '@/components/MkMediaList.vue'; @@ -177,8 +177,8 @@ const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultS .noteHeaderAvatar { display: block; flex-shrink: 0; - width: 58px; - height: 58px; + width: var(--avatar); + height: var(--avatar); } .noteHeaderBody { diff --git a/packages/frontend/src/components/SkOneko.vue b/packages/frontend/src/components/SkOneko.vue new file mode 100644 index 0000000000000000000000000000000000000000..fbf50067a9d5f73c81e0b6e3c7068819b806c757 --- /dev/null +++ b/packages/frontend/src/components/SkOneko.vue @@ -0,0 +1,240 @@ +<template> +<div ref="nekoEl" :class="$style.oneko" aria-hidden="true"></div> +</template> + +<script lang="ts" setup> +// oneko.js: https://github.com/adryd325/oneko.js +// modified to be a vue component by ShittyKopper :3 + +import { shallowRef, onMounted } from 'vue'; + +const nekoEl = shallowRef<HTMLDivElement>(); + +let nekoPosX = 32; +let nekoPosY = 32; + +let mousePosX = 0; +let mousePosY = 0; + +let frameCount = 0; +let idleTime = 0; +let idleAnimation: string|null = null; +let idleAnimationFrame = 0; +let lastFrameTimestamp; + +const nekoSpeed = 10; +const spriteSets = { + idle: [[-3, -3]], + alert: [[-7, -3]], + scratchSelf: [ + [-5, 0], + [-6, 0], + [-7, 0], + ], + scratchWallN: [ + [0, 0], + [0, -1], + ], + scratchWallS: [ + [-7, -1], + [-6, -2], + ], + scratchWallE: [ + [-2, -2], + [-2, -3], + ], + scratchWallW: [ + [-4, 0], + [-4, -1], + ], + tired: [[-3, -2]], + sleeping: [ + [-2, 0], + [-2, -1], + ], + N: [ + [-1, -2], + [-1, -3], + ], + NE: [ + [0, -2], + [0, -3], + ], + E: [ + [-3, 0], + [-3, -1], + ], + SE: [ + [-5, -1], + [-5, -2], + ], + S: [ + [-6, -3], + [-7, -2], + ], + SW: [ + [-5, -3], + [-6, -1], + ], + W: [ + [-4, -2], + [-4, -3], + ], + NW: [ + [-1, 0], + [-1, -1], + ], +}; + +function init() { + if (!nekoEl.value) return; + + nekoEl.value.style.left = `${nekoPosX - 16}px`; + nekoEl.value.style.top = `${nekoPosY - 16}px`; + + document.addEventListener('mousemove', (event) => { + mousePosX = event.clientX; + mousePosY = event.clientY; + }); + + window.requestAnimationFrame(onAnimationFrame); +} + +function onAnimationFrame(timestamp) { + // Stops execution if the neko element is removed from DOM + if (!nekoEl.value?.isConnected) { + return; + } + if (!lastFrameTimestamp) { + lastFrameTimestamp = timestamp; + } + if (timestamp - lastFrameTimestamp > 100) { + lastFrameTimestamp = timestamp; + frame(); + } + window.requestAnimationFrame(onAnimationFrame); +} + +// eslint-disable-next-line no-shadow +function setSprite(name, frame) { + if (!nekoEl.value) return; + + const sprite = spriteSets[name][frame % spriteSets[name].length]; + nekoEl.value.style.backgroundPosition = `${sprite[0] * 32}px ${sprite[1] * 32}px`; +} + +function resetIdleAnimation() { + idleAnimation = null; + idleAnimationFrame = 0; +} + +function idle() { + idleTime += 1; + + // every ~ 20 seconds + if ( + idleTime > 10 && + Math.floor(Math.random() * 200) === 0 && + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + idleAnimation == null + ) { + let avalibleIdleAnimations = ['sleeping', 'scratchSelf']; + if (nekoPosX < 32) { + avalibleIdleAnimations.push('scratchWallW'); + } + if (nekoPosY < 32) { + avalibleIdleAnimations.push('scratchWallN'); + } + if (nekoPosX > window.innerWidth - 32) { + avalibleIdleAnimations.push('scratchWallE'); + } + if (nekoPosY > window.innerHeight - 32) { + avalibleIdleAnimations.push('scratchWallS'); + } + idleAnimation = + avalibleIdleAnimations[Math.floor(Math.random() * avalibleIdleAnimations.length)]; + } + + switch (idleAnimation) { + case 'sleeping': + if (idleAnimationFrame < 8) { + setSprite('tired', 0); + break; + } + setSprite('sleeping', Math.floor(idleAnimationFrame / 4)); + if (idleAnimationFrame > 192) { + resetIdleAnimation(); + } + break; + case 'scratchWallN': + case 'scratchWallS': + case 'scratchWallE': + case 'scratchWallW': + case 'scratchSelf': + setSprite(idleAnimation, idleAnimationFrame); + if (idleAnimationFrame > 9) { + resetIdleAnimation(); + } + break; + default: + setSprite('idle', 0); + return; + } + idleAnimationFrame += 1; +} + +function frame() { + if (!nekoEl.value) return; + + frameCount += 1; + const diffX = nekoPosX - mousePosX; + const diffY = nekoPosY - mousePosY; + const distance = Math.sqrt(diffX ** 2 + diffY ** 2); + + if (distance < nekoSpeed || distance < 48) { + idle(); + return; + } + + idleAnimation = null; + idleAnimationFrame = 0; + + if (idleTime > 1) { + setSprite('alert', 0); + // count down after being alerted before moving + idleTime = Math.min(idleTime, 7); + idleTime -= 1; + return; + } + + let direction; + direction = diffY / distance > 0.5 ? 'N' : ''; + direction += diffY / distance < -0.5 ? 'S' : ''; + direction += diffX / distance > 0.5 ? 'W' : ''; + direction += diffX / distance < -0.5 ? 'E' : ''; + setSprite(direction, frameCount); + + nekoPosX -= (diffX / distance) * nekoSpeed; + nekoPosY -= (diffY / distance) * nekoSpeed; + + nekoPosX = Math.min(Math.max(16, nekoPosX), window.innerWidth - 16); + nekoPosY = Math.min(Math.max(16, nekoPosY), window.innerHeight - 16); + + nekoEl.value.style.left = `${nekoPosX - 16}px`; + nekoEl.value.style.top = `${nekoPosY - 16}px`; +} + +onMounted(init); +</script> + +<style module> +.oneko { + width: 32px; + height: 32px; + position: fixed; + pointer-events: none; + image-rendering: pixelated; + z-index: 2147483647; + background-image: url(/client-assets/oneko.gif); +} +</style> diff --git a/packages/frontend/src/components/form/link.vue b/packages/frontend/src/components/form/link.vue index 88602a007ca0a923ce727b3443bb08c4eae864fe..8c8a34301013ba26d0c1a31b55da65ca76c7f094 100644 --- a/packages/frontend/src/components/form/link.vue +++ b/packages/frontend/src/components/form/link.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/form/section.vue b/packages/frontend/src/components/form/section.vue index 6af63d1ec6d49de8ad3c005a035ab328b2a83bc5..ad37daa2657a94e0cd8ef1d348f8a725d1542a3b 100644 --- a/packages/frontend/src/components/form/section.vue +++ b/packages/frontend/src/components/form/section.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/form/slot.vue b/packages/frontend/src/components/form/slot.vue index dc4d19750706fe38d3181a09b1febef32e1debd4..f54db0ca823dadcf559c276b8b93d7bf957e125b 100644 --- a/packages/frontend/src/components/form/slot.vue +++ b/packages/frontend/src/components/form/slot.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/form/split.vue b/packages/frontend/src/components/form/split.vue index 8cb24b479ee53a2c33b0662d2e6b88d8cc9faff2..2a015c95204262256350bd96e14e0a8196a08b4a 100644 --- a/packages/frontend/src/components/form/split.vue +++ b/packages/frontend/src/components/form/split.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/form/suspense.vue b/packages/frontend/src/components/form/suspense.vue index 933f00b081b21182f3b0f538ce41673886a08960..54566dc13514d460729eff2f9c88ba1ed471f2e7 100644 --- a/packages/frontend/src/components/form/suspense.vue +++ b/packages/frontend/src/components/form/suspense.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/global/I18n.vue b/packages/frontend/src/components/global/I18n.vue new file mode 100644 index 0000000000000000000000000000000000000000..162aa2bcf8111281bdd3497c8775fc5b2fc260d5 --- /dev/null +++ b/packages/frontend/src/components/global/I18n.vue @@ -0,0 +1,46 @@ +<template> +<render/> +</template> + +<script setup lang="ts" generic="T extends string | ParameterizedString"> +import { computed, h } from 'vue'; +import type { ParameterizedString } from '../../../../../locales/index.js'; + +const props = withDefaults(defineProps<{ + src: T; + tag?: string; + // eslint-disable-next-line vue/require-default-prop + textTag?: string; +}>(), { + tag: 'span', +}); + +const slots = defineSlots<T extends ParameterizedString<infer R> ? { [K in R]: () => unknown } : NonNullable<unknown>>(); + +const parsed = computed(() => { + let str = props.src as string; + const value: (string | { arg: string; })[] = []; + for (;;) { + const nextBracketOpen = str.indexOf('{'); + const nextBracketClose = str.indexOf('}'); + + if (nextBracketOpen === -1) { + value.push(str); + break; + } else { + if (nextBracketOpen > 0) value.push(str.substring(0, nextBracketOpen)); + value.push({ + arg: str.substring(nextBracketOpen + 1, nextBracketClose), + }); + } + + str = str.substring(nextBracketClose + 1); + } + + return value; +}); + +const render = () => { + return h(props.tag, parsed.value.map(x => typeof x === 'string' ? (props.textTag ? h(props.textTag, x) : x) : slots[x.arg]())); +}; +</script> diff --git a/packages/frontend/src/components/global/MkA.stories.impl.ts b/packages/frontend/src/components/global/MkA.stories.impl.ts index 62f4805a11bb1e4123cda97d5f50499c3411c4c7..c1d8cf0ca6b2fbcf02b5da9a1c284791e02a8b3b 100644 --- a/packages/frontend/src/components/global/MkA.stories.impl.ts +++ b/packages/frontend/src/components/global/MkA.stories.impl.ts @@ -1,11 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { expect } from '@storybook/jest'; -import { userEvent, within } from '@storybook/testing-library'; +import { expect, userEvent, within } from '@storybook/test'; import { StoryObj } from '@storybook/vue3'; import MkA from './MkA.vue'; import { tick } from '@/scripts/test-utils.js'; @@ -33,7 +32,8 @@ export const Default = { async play({ canvasElement }) { const canvas = within(canvasElement); const a = canvas.getByRole<HTMLAnchorElement>('link'); - await expect(a.href).toMatch(/^https?:\/\/.*#test$/); + // FIXME: 通るã‘ã©ãã®å¾Œè½ã¡ã‚‹ã®ã§ã‚³ãƒ¡ãƒ³ãƒˆã‚¢ã‚¦ãƒˆ + // await expect(a.href).toMatch(/^https?:\/\/.*#test$/); await userEvent.pointer({ keys: '[MouseRight]', target: a }); await tick(); const menu = canvas.getByRole('menu'); @@ -45,6 +45,7 @@ export const Default = { }, args: { to: '#test', + behavior: 'browser', }, parameters: { layout: 'centered', diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue index e2b59869a4660864c509f4d9d11901d2a8d5c04e..b3c58cf235a639a75c842347b801110b128cfed0 100644 --- a/packages/frontend/src/components/global/MkA.vue +++ b/packages/frontend/src/components/global/MkA.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -15,7 +15,7 @@ import * as os from '@/os.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { url } from '@/config.js'; import { i18n } from '@/i18n.js'; -import { useRouter } from '@/router.js'; +import { useRouter } from '@/router/supplier.js'; const props = withDefaults(defineProps<{ to: string; diff --git a/packages/frontend/src/components/global/MkAcct.stories.impl.ts b/packages/frontend/src/components/global/MkAcct.stories.impl.ts index 49ec61211cdf559dc3f609e813279dfbaf5d71d0..04960ec60caeabd016543f2874d98085cfdb260a 100644 --- a/packages/frontend/src/components/global/MkAcct.stories.impl.ts +++ b/packages/frontend/src/components/global/MkAcct.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/global/MkAcct.vue b/packages/frontend/src/components/global/MkAcct.vue index 594494f3c8336692aa52efc6241267cb9d25952d..8cb082585bc8895a7ce837e78573c8b8aedaf1f9 100644 --- a/packages/frontend/src/components/global/MkAcct.vue +++ b/packages/frontend/src/components/global/MkAcct.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -21,7 +21,7 @@ import { host as hostRaw } from '@/config.js'; import { defaultStore } from '@/store.js'; defineProps<{ - user: Misskey.entities.UserDetailed; + user: Misskey.entities.User; detail?: boolean; }>(); diff --git a/packages/frontend/src/components/global/MkAd.stories.impl.ts b/packages/frontend/src/components/global/MkAd.stories.impl.ts index 5ae45ec58f64af0ccde030003e6fecd77dbcb907..f6cdc2bf23d4b41613f0141d6afd05cdd58c7ee8 100644 --- a/packages/frontend/src/components/global/MkAd.stories.impl.ts +++ b/packages/frontend/src/components/global/MkAd.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/global/MkAd.vue b/packages/frontend/src/components/global/MkAd.vue index b3eb6d681f64c8e877a108ebc6e0f7884e171b0c..f13a161ae85b7f48cb1731ef78c851df00bcc9d0 100644 --- a/packages/frontend/src/components/global/MkAd.vue +++ b/packages/frontend/src/components/global/MkAd.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/global/MkAvatar.stories.impl.ts b/packages/frontend/src/components/global/MkAvatar.stories.impl.ts index 515d7eab1876e69225f724e073bcccd5d2396321..933754ec4c46554ec7769accdba4d0f11bb065bd 100644 --- a/packages/frontend/src/components/global/MkAvatar.stories.impl.ts +++ b/packages/frontend/src/components/global/MkAvatar.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue index 4a876931c3108993edb67c0481bb01d143d0987e..de62fe12a97a4ce03c5013c52857327235e3f377 100644 --- a/packages/frontend/src/components/global/MkAvatar.vue +++ b/packages/frontend/src/components/global/MkAvatar.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only <img v-for="decoration in decorations ?? user.avatarDecorations" :class="[$style.decoration]" - :src="decoration.url" + :src="getDecorationUrl(decoration)" :style="{ rotate: getDecorationAngle(decoration), scale: getDecorationScale(decoration), @@ -81,15 +81,22 @@ const bound = computed(() => props.link ? { to: userPage(props.user), target: props.target } : {}); -const url = computed(() => (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar) - ? getStaticImageUrl(props.user.avatarUrl) - : props.user.avatarUrl); +const url = computed(() => { + if (props.user.avatarUrl == null) return null; + if (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar) return getStaticImageUrl(props.user.avatarUrl); + return props.user.avatarUrl; +}); function onClick(ev: MouseEvent): void { if (props.link) return; emit('click', ev); } +function getDecorationUrl(decoration: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>) { + if (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar) return getStaticImageUrl(decoration.url); + return decoration.url; +} + function getDecorationAngle(decoration: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>) { const angle = decoration.angle ?? 0; return angle === 0 ? undefined : `${angle * 360}deg`; @@ -109,6 +116,7 @@ function getDecorationOffset(decoration: Omit<Misskey.entities.UserDetailed['ava const color = ref<string | undefined>(); watch(() => props.user.avatarBlurhash, () => { + if (props.user.avatarBlurhash == null) return; color.value = extractAvgColorFromBlurhash(props.user.avatarBlurhash); }, { immediate: true, diff --git a/packages/frontend/src/components/global/MkCondensedLine.stories.impl.ts b/packages/frontend/src/components/global/MkCondensedLine.stories.impl.ts index 7df49a2066d081e4cb70db02a44a6c412453d40c..e4e90cddd521e02a2e4831ea7dc92e239263bdf3 100644 --- a/packages/frontend/src/components/global/MkCondensedLine.stories.impl.ts +++ b/packages/frontend/src/components/global/MkCondensedLine.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/global/MkCondensedLine.vue b/packages/frontend/src/components/global/MkCondensedLine.vue index 2ed615f5ff41d8b8cc62810b25bb710ecb9bd915..7c4957d77fac1c7261335b7eb7d087143b42ccc3 100644 --- a/packages/frontend/src/components/global/MkCondensedLine.vue +++ b/packages/frontend/src/components/global/MkCondensedLine.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/global/MkCustomEmoji.stories.impl.ts b/packages/frontend/src/components/global/MkCustomEmoji.stories.impl.ts index f50217b70dd040a78155851e84646d7465bccede..9e6177045d3eddfae6b8c6ec59ce250787273020 100644 --- a/packages/frontend/src/components/global/MkCustomEmoji.stories.impl.ts +++ b/packages/frontend/src/components/global/MkCustomEmoji.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -48,3 +48,18 @@ export const Missing = { name: Default.args.name, }, } satisfies StoryObj<typeof MkCustomEmoji>; +export const ErrorToText = { + ...Default, + args: { + url: 'https://example.com/404', + name: Default.args.name, + }, +} satisfies StoryObj<typeof MkCustomEmoji>; +export const ErrorToImage = { + ...Default, + args: { + url: 'https://example.com/404', + name: Default.args.name, + fallbackToImage: true, + }, +} satisfies StoryObj<typeof MkCustomEmoji>; diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue index e8732d1b16a30c8c0c5518345a9745eac81cfb31..b57a311c0c1b68036373f25fc1ef589eb5e3b9b9 100644 --- a/packages/frontend/src/components/global/MkCustomEmoji.vue +++ b/packages/frontend/src/components/global/MkCustomEmoji.vue @@ -1,10 +1,16 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> -<span v-if="errored">:{{ customEmojiName }}:</span> +<img + v-if="errored && fallbackToImage" + :class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]" + src="/client-assets/dummy.png" + :title="alt" +/> +<span v-else-if="errored">:{{ customEmojiName }}:</span> <img v-else :class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]" @@ -24,9 +30,11 @@ import { getProxiedImageUrl, getStaticImageUrl } from '@/scripts/media-proxy.js' import { defaultStore } from '@/store.js'; import { customEmojisMap } from '@/custom-emojis.js'; import * as os from '@/os.js'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import * as sound from '@/scripts/sound.js'; import { i18n } from '@/i18n.js'; +import MkCustomEmojiDetailedDialog from '@/components/MkCustomEmojiDetailedDialog.vue'; const props = defineProps<{ name: string; @@ -37,6 +45,7 @@ const props = defineProps<{ useOriginalSize?: boolean; menu?: boolean; menuReaction?: boolean; + fallbackToImage?: boolean; }>(); const react = inject<((name: string) => void) | null>('react', null); @@ -55,7 +64,7 @@ const rawUrl = computed(() => { }); const url = computed(() => { - if (rawUrl.value == null) return null; + if (rawUrl.value == null) return undefined; const proxied = (rawUrl.value.startsWith('/emoji/') || (props.useOriginalSize && isLocal.value)) @@ -91,9 +100,21 @@ function onClick(ev: MouseEvent) { icon: 'ph-smiley ph-bold ph-lg', action: () => { react(`:${props.name}:`); - sound.play('reaction'); + sound.playMisskeySfx('reaction'); + }, + }] : []), { + text: i18n.ts.info, + icon: 'ph-info ph-bold ph-lg', + action: async () => { + os.popup(MkCustomEmojiDetailedDialog, { + emoji: await misskeyApiGet('emoji', { + name: customEmojiName.value, + }), + }, { + anchor: ev.target, + }); }, - }] : [])], ev.currentTarget ?? ev.target); + }], ev.currentTarget ?? ev.target); } } </script> diff --git a/packages/frontend/src/components/global/MkEllipsis.stories.impl.ts b/packages/frontend/src/components/global/MkEllipsis.stories.impl.ts index 32deaae8e27d8c98138bcf0f9644abedabd39842..6a8fcf4fe307e12621ded3ed87134b3418769e85 100644 --- a/packages/frontend/src/components/global/MkEllipsis.stories.impl.ts +++ b/packages/frontend/src/components/global/MkEllipsis.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/global/MkEllipsis.vue b/packages/frontend/src/components/global/MkEllipsis.vue index 5cc07f7040d74b9599050704a22b5e0ea799fb8e..4ba6be10fedc3258d1033fee9baef9b175577f54 100644 --- a/packages/frontend/src/components/global/MkEllipsis.vue +++ b/packages/frontend/src/components/global/MkEllipsis.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/global/MkEmoji.stories.impl.ts b/packages/frontend/src/components/global/MkEmoji.stories.impl.ts index c8beec7e8f42e77d28d299f2ec3c573287ab3a08..309c015757b101848699ca0282e3d1697e1496a2 100644 --- a/packages/frontend/src/components/global/MkEmoji.stories.impl.ts +++ b/packages/frontend/src/components/global/MkEmoji.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue index b1d62db33cb0e6e9339596bdcd7ef06d8e45fcd8..2e7a0c5bb737aad24890c4d999704f5aa5160c28 100644 --- a/packages/frontend/src/components/global/MkEmoji.vue +++ b/packages/frontend/src/components/global/MkEmoji.vue @@ -1,19 +1,18 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <img v-if="!useOsNativeEmojis" :class="$style.root" :src="url" :alt="props.emoji" decoding="async" @pointerenter="computeTitle" @click="onClick" v-on:click.stop/> -<span v-else-if="useOsNativeEmojis" :alt="props.emoji" @pointerenter="computeTitle" @click="onClick" v-on:click.stop>{{ props.emoji }}</span> -<span v-else>{{ emoji }}</span> +<span v-else :alt="props.emoji" @pointerenter="computeTitle" @click="onClick" v-on:click.stop>{{ colorizedNativeEmoji }}</span> </template> <script lang="ts" setup> import { computed, inject } from 'vue'; -import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base.js'; +import { char2fluentEmojiFilePath, char2twemojiFilePath, char2tossfaceFilePath } from '@/scripts/emoji-base.js'; import { defaultStore } from '@/store.js'; -import { getEmojiName } from '@/scripts/emojilist.js'; +import { colorizeEmoji, getEmojiName } from '@/scripts/emojilist.js'; import * as os from '@/os.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import * as sound from '@/scripts/sound.js'; @@ -27,17 +26,15 @@ const props = defineProps<{ const react = inject<((name: string) => void) | null>('react', null); -const char2path = defaultStore.state.emojiStyle === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath; +const char2path = defaultStore.state.emojiStyle === 'twemoji' ? char2twemojiFilePath : defaultStore.state.emojiStyle === 'tossface' ? char2tossfaceFilePath : char2fluentEmojiFilePath; const useOsNativeEmojis = computed(() => defaultStore.state.emojiStyle === 'native'); -const url = computed(() => { - return char2path(props.emoji); -}); +const url = computed(() => char2path(props.emoji)); +const colorizedNativeEmoji = computed(() => colorizeEmoji(props.emoji)); // Searching from an array with 2000 items for every emoji felt like too energy-consuming, so I decided to do it lazily on pointerenter function computeTitle(event: PointerEvent): void { - const title = getEmojiName(props.emoji as string) ?? props.emoji as string; - (event.target as HTMLElement).title = title; + (event.target as HTMLElement).title = getEmojiName(props.emoji); } function onClick(ev: MouseEvent) { @@ -57,7 +54,7 @@ function onClick(ev: MouseEvent) { icon: 'ph-smiley ph-bold ph-lg', action: () => { react(props.emoji); - sound.play('reaction'); + sound.playMisskeySfx('reaction'); }, }] : [])], ev.currentTarget ?? ev.target); } diff --git a/packages/frontend/src/components/global/MkError.stories.impl.ts b/packages/frontend/src/components/global/MkError.stories.impl.ts index cf0a1dbb5f90639ce0a0bc1fc3e1ec12206036f1..daef04cd877485c26ccf26dcd1ce8edea5cbf6bb 100644 --- a/packages/frontend/src/components/global/MkError.stories.impl.ts +++ b/packages/frontend/src/components/global/MkError.stories.impl.ts @@ -1,12 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { action } from '@storybook/addon-actions'; -import { expect } from '@storybook/jest'; -import { waitFor } from '@storybook/testing-library'; +import { expect, waitFor } from '@storybook/test'; import { StoryObj } from '@storybook/vue3'; import MkError from './MkError.vue'; export const Default = { diff --git a/packages/frontend/src/components/global/MkError.stories.meta.ts b/packages/frontend/src/components/global/MkError.stories.meta.ts index a3955c57860e2f227a891c30fdc2443f6f0bc41d..1abbc56f509d2e2948ee4b1283094e031c340212 100644 --- a/packages/frontend/src/components/global/MkError.stories.meta.ts +++ b/packages/frontend/src/components/global/MkError.stories.meta.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/global/MkError.vue b/packages/frontend/src/components/global/MkError.vue index 47b42467d627d9ae978c8226b9f68e326560cea1..2976cd7be8229f877d2c6780f2445630925def99 100644 --- a/packages/frontend/src/components/global/MkError.vue +++ b/packages/frontend/src/components/global/MkError.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/global/MkFooterSpacer.vue b/packages/frontend/src/components/global/MkFooterSpacer.vue index e78df6b8d978db6efe6df93dde32b01423cdf9d7..1a75855fa137dc3218e485633af3615a6b2dd7bb 100644 --- a/packages/frontend/src/components/global/MkFooterSpacer.vue +++ b/packages/frontend/src/components/global/MkFooterSpacer.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/global/MkLazy.vue b/packages/frontend/src/components/global/MkLazy.vue index 6d7ff4ca49f1d5681f1eb47000d366ce2d9e7227..f35932ae772ffc09a11afd0e2cf8d8210f3d9079 100644 --- a/packages/frontend/src/components/global/MkLazy.vue +++ b/packages/frontend/src/components/global/MkLazy.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/global/MkLoading.stories.impl.ts b/packages/frontend/src/components/global/MkLoading.stories.impl.ts index 9cedd68fd8085c473c47d340a4b7d614fa7c4482..c781ad0479f1a3c0b84fbca48a00a27c3efb8cc1 100644 --- a/packages/frontend/src/components/global/MkLoading.stories.impl.ts +++ b/packages/frontend/src/components/global/MkLoading.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/global/MkLoading.vue b/packages/frontend/src/components/global/MkLoading.vue index 3f34e83f5877b8c6fc09bec29c1f55b31632c374..49d8ace37bee32d60c09783c03bfd738c12bbc03 100644 --- a/packages/frontend/src/components/global/MkLoading.vue +++ b/packages/frontend/src/components/global/MkLoading.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts index 9cdb490e4b31cb55ad871fd0c979664521101228..730351f79534b9f2e5531b0e6ea461f7ad27c49a 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts @@ -1,12 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { StoryObj } from '@storybook/vue3'; -import { within } from '@storybook/testing-library'; -import { expect } from '@storybook/jest'; +import { expect, within } from '@storybook/test'; import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.js'; export const Default = { render(args) { diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index a3bfdf0bb4c65942bf58919bb5677ddbd3137a7a..f8b5fcfedccc0a5c31112fbc9383dcada877b328 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -1,10 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { VNode, h, defineAsyncComponent, SetupContext } from 'vue'; -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; import * as Misskey from 'misskey-js'; import MkUrl from '@/components/global/MkUrl.vue'; import MkTime from '@/components/global/MkTime.vue'; @@ -13,12 +13,14 @@ import MkMention from '@/components/MkMention.vue'; import MkEmoji from '@/components/global/MkEmoji.vue'; import MkCustomEmoji from '@/components/global/MkCustomEmoji.vue'; import MkCode from '@/components/MkCode.vue'; +import MkCodeInline from '@/components/MkCodeInline.vue'; import MkGoogle from '@/components/MkGoogle.vue'; import MkSparkle from '@/components/MkSparkle.vue'; import MkA from '@/components/global/MkA.vue'; import { host } from '@/config.js'; import { defaultStore } from '@/store.js'; import { nyaize as doNyaize } from '@/scripts/nyaize.js'; +import { safeParseFloat } from '@/scripts/safe-parse.js'; const QUOTE_STYLE = ` display: block; @@ -35,7 +37,7 @@ type MfmProps = { nowrap?: boolean; author?: Misskey.entities.UserLite; isNote?: boolean; - emojiUrls?: string[]; + emojiUrls?: Record<string, string>; rootScale?: number; nyaize?: boolean | 'respect'; parsedNodes?: mfm.MfmNode[] | null; @@ -49,22 +51,28 @@ type MfmEvents = { }; // eslint-disable-next-line import/no-default-export -export default function(props: MfmProps, context: SetupContext<MfmEvents>) { +export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEvents>['emit'] }) { const isNote = props.isNote ?? true; - const shouldNyaize = props.nyaize ? props.nyaize === 'respect' ? props.author?.isCat ? props.author?.speakAsCat : false : false : false; + const shouldNyaize = props.nyaize ? props.nyaize === 'respect' ? props.author?.isCat ? props.author.speakAsCat : false : false : false; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (props.text == null || props.text === '') return; const rootAst = props.parsedNodes ?? (props.plain ? mfm.parseSimple : mfm.parse)(props.text); - const validTime = (t: string | null | undefined) => { + const validTime = (t: string | boolean | null | undefined) => { if (t == null) return null; + if (typeof t === 'boolean') return null; return t.match(/^[0-9.]+s$/) ? t : null; }; const useAnim = defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : props.isAnim ? true : false; + const validColor = (c: unknown): string | null => { + if (typeof c !== 'string') return null; + return c.match(/^[0-9a-f]{3,6}$/i) ? c : null; + }; + const MkFormula = defineAsyncComponent(() => import('@/components/MkFormula.vue')); /** @@ -115,7 +123,7 @@ export default function(props: MfmProps, context: SetupContext<MfmEvents>) { case 'tada': { const speed = validTime(token.props.args.speed) ?? '1s'; const delay = validTime(token.props.args.delay) ?? '0s'; - style = 'font-size: 150%;' + (useAnim ? `animation: tada ${speed} linear infinite both; animation-delay: ${delay};` : ''); + style = 'font-size: 150%;' + (useAnim ? `animation: global-tada ${speed} linear infinite both; animation-delay: ${delay};` : ''); break; } case 'jelly': { @@ -220,14 +228,14 @@ export default function(props: MfmProps, context: SetupContext<MfmEvents>) { return h(MkSparkle, {}, genEl(token.children, scale)); } case 'rotate': { - const degrees = parseFloat(token.props.args.deg ?? '90'); + const degrees = safeParseFloat(token.props.args.deg) ?? 90; style = `transform: rotate(${degrees}deg); transform-origin: center center;`; break; } case 'position': { if (!defaultStore.state.advancedMfm) break; - const x = parseFloat(token.props.args.x ?? '0'); - const y = parseFloat(token.props.args.y ?? '0'); + const x = safeParseFloat(token.props.args.x) ?? 0; + const y = safeParseFloat(token.props.args.y) ?? 0; style = `transform: translateX(${x}em) translateY(${y}em);`; break; } @@ -236,24 +244,38 @@ export default function(props: MfmProps, context: SetupContext<MfmEvents>) { style = ''; break; } - const x = Math.min(parseFloat(token.props.args.x ?? '1'), 5); - const y = Math.min(parseFloat(token.props.args.y ?? '1'), 5); + const x = Math.min(safeParseFloat(token.props.args.x) ?? 1, 5); + const y = Math.min(safeParseFloat(token.props.args.y) ?? 1, 5); style = `transform: scale(${x}, ${y});`; scale = scale * Math.max(x, y); break; } case 'fg': { - let color = token.props.args.color; - if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00'; + let color = validColor(token.props.args.color); + color = color ?? 'f00'; style = `color: #${color}; overflow-wrap: anywhere;`; break; } case 'bg': { - let color = token.props.args.color; - if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00'; + let color = validColor(token.props.args.color); + color = color ?? 'f00'; style = `background-color: #${color}; overflow-wrap: anywhere;`; break; } + case 'border': { + let color = validColor(token.props.args.color); + color = color ? `#${color}` : 'var(--accent)'; + let b_style = token.props.args.style; + if ( + typeof b_style !== 'string' || + !['hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'] + .includes(b_style) + ) b_style = 'solid'; + const width = safeParseFloat(token.props.args.width) ?? 1; + const radius = safeParseFloat(token.props.args.radius) ?? 0; + style = `border: ${width}px ${b_style} ${color}; border-radius: ${radius}px;${token.props.args.noclip ? '' : ' overflow: clip;'}`; + break; + } case 'ruby': { if (token.children.length === 1) { const child = token.children[0]; @@ -292,7 +314,8 @@ export default function(props: MfmProps, context: SetupContext<MfmEvents>) { return h('span', { onClick(ev: MouseEvent): void { ev.stopPropagation(); ev.preventDefault(); - context.emit('clickEv', token.props.args.ev ?? ''); + const clickEv = typeof token.props.args.ev === 'string' ? token.props.args.ev : ''; + emit('clickEv', clickEv); } }, genEl(token.children, scale)); } } @@ -353,15 +376,14 @@ export default function(props: MfmProps, context: SetupContext<MfmEvents>) { return [h(MkCode, { key: Math.random(), code: token.props.code, - lang: token.props.lang, + lang: token.props.lang ?? undefined, })]; } case 'inlineCode': { - return [h(MkCode, { + return [h(MkCodeInline, { key: Math.random(), code: token.props.code, - inline: true, })]; } @@ -388,6 +410,7 @@ export default function(props: MfmProps, context: SetupContext<MfmEvents>) { useOriginalSize: scale >= 2.5, menu: props.enableEmojiMenu, menuReaction: props.enableEmojiMenuReaction, + fallbackToImage: false, })]; } else { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition @@ -397,8 +420,7 @@ export default function(props: MfmProps, context: SetupContext<MfmEvents>) { return [h(MkCustomEmoji, { key: Math.random(), name: token.props.name, - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - url: props.emojiUrls ? props.emojiUrls[token.props.name] : null, + url: props.emojiUrls && props.emojiUrls[token.props.name], normal: props.plain, host: props.author.host, useOriginalSize: scale >= 2.5, diff --git a/packages/frontend/src/components/global/MkPageHeader.stories.impl.ts b/packages/frontend/src/components/global/MkPageHeader.stories.impl.ts index 05d2872e9154d6d4b08a4efda24bd3e177d2a2de..d4327e14637fc0b041ae450700ff9431d7dc196c 100644 --- a/packages/frontend/src/components/global/MkPageHeader.stories.impl.ts +++ b/packages/frontend/src/components/global/MkPageHeader.stories.impl.ts @@ -1,10 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { waitFor } from '@storybook/testing-library'; +import { waitFor } from '@storybook/test'; import { StoryObj } from '@storybook/vue3'; import MkPageHeader from './MkPageHeader.vue'; export const Empty = { diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.stories.impl.ts b/packages/frontend/src/components/global/MkPageHeader.tabs.stories.impl.ts index 130dde63afd1acc4da2b4fb7d3b9899115a44cba..5d2126435efc7613a79512b6924a06010b57f7fa 100644 --- a/packages/frontend/src/components/global/MkPageHeader.tabs.stories.impl.ts +++ b/packages/frontend/src/components/global/MkPageHeader.tabs.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue index 75c8e735823168c84688339c2e4fb93b5124a36d..53bb5472dc6a9f7b35e4171da0361329fde81db9 100644 --- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue +++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -38,6 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts"> export type Tab = { key: string; + title: string; onClick?: (ev: MouseEvent) => void; } & ( | { @@ -120,8 +121,9 @@ function onTabWheel(ev: WheelEvent) { let entering = false; -async function enter(el: HTMLElement) { +async function enter(element: Element) { entering = true; + const el = element as HTMLElement; const elementWidth = el.getBoundingClientRect().width; el.style.width = '0'; el.style.paddingLeft = '0'; @@ -135,11 +137,12 @@ async function enter(el: HTMLElement) { setTimeout(renderTab, 170); } -function afterEnter(el: HTMLElement) { +function afterEnter(element: Element) { //el.style.width = ''; } -async function leave(el: HTMLElement) { +async function leave(element: Element) { + const el = element as HTMLElement; const elementWidth = el.getBoundingClientRect().width; el.style.width = elementWidth + 'px'; el.style.paddingLeft = ''; @@ -148,7 +151,8 @@ async function leave(el: HTMLElement) { el.style.paddingLeft = '0'; } -function afterLeave(el: HTMLElement) { +function afterLeave(element: Element) { + const el = element as HTMLElement; el.style.width = ''; } diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index a36d9517cd896efa94335409704f6a8b761a480c..95ac1020132afe4118cdf84fdd23ef18550eb744 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -15,23 +15,23 @@ SPDX-License-Identifier: AGPL-3.0-only </button> </div> - <template v-if="metadata"> + <template v-if="pageMetadata"> <div v-if="displayBackButton && !narrow" style="margin: 0 -45px 0 0;" :class="$style.buttonsLeft"> <button class="_button" :class="$style.button" style="left: 5px;" @click.stop="goBack()" @touchstart="preventDrag"> <i class="ph-caret-left ph-bold ph-lg"></i> </button> </div> <div v-if="!hideTitle" :class="$style.titleContainer" @click="top"> - <div v-if="metadata.avatar" :class="$style.titleAvatarContainer"> - <MkAvatar :class="$style.titleAvatar" :user="metadata.avatar" indicator/> + <div v-if="pageMetadata.avatar" :class="$style.titleAvatarContainer"> + <MkAvatar :class="$style.titleAvatar" :user="pageMetadata.avatar" indicator/> </div> - <i v-else-if="metadata.icon" :class="[$style.titleIcon, metadata.icon]"></i> + <i v-else-if="pageMetadata.icon" :class="[$style.titleIcon, pageMetadata.icon]"></i> <div :class="$style.title"> - <MkUserName v-if="metadata.userName" :user="metadata.userName" :nowrap="false"/> - <div v-else-if="metadata.title">{{ metadata.title }}</div> - <div v-if="metadata.subtitle" :class="$style.subtitle"> - {{ metadata.subtitle }} + <MkUserName v-if="pageMetadata.userName" :user="pageMetadata.userName" :nowrap="true"/> + <div v-else-if="pageMetadata.title">{{ pageMetadata.title }}</div> + <div v-if="pageMetadata.subtitle" :class="$style.subtitle"> + {{ pageMetadata.subtitle }} </div> </div> </div> @@ -55,7 +55,7 @@ import tinycolor from 'tinycolor2'; import XTabs, { Tab } from './MkPageHeader.tabs.vue'; import { scrollToTop } from '@/scripts/scroll.js'; import { globalEvents } from '@/events.js'; -import { injectPageMetadata } from '@/scripts/page-metadata.js'; +import { injectReactiveMetadata } from '@/scripts/page-metadata.js'; import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js'; import { PageHeaderItem } from '@/types/page-header.js'; @@ -76,7 +76,7 @@ const emit = defineEmits<{ const displayBackButton = props.displayBackButton && history.state.key !== 'index' && history.length > 1 && inject('shouldBackButton', true); -const metadata = injectPageMetadata(); +const pageMetadata = injectReactiveMetadata(); const hideTitle = inject('shouldOmitHeaderTitle', false); const thin_ = props.thin || inject('shouldHeaderThin', false); diff --git a/packages/frontend/src/components/global/MkSpacer.vue b/packages/frontend/src/components/global/MkSpacer.vue index a384e06f772feda5f6ce912849d9edbfb313e09b..db01c10eb074e4f5069a5fb58b2509e5ba665dbb 100644 --- a/packages/frontend/src/components/global/MkSpacer.vue +++ b/packages/frontend/src/components/global/MkSpacer.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/global/MkStickyContainer.stories.impl.ts b/packages/frontend/src/components/global/MkStickyContainer.stories.impl.ts index 16c62ce03d77cd11b78a77e1c3b66b0218391d07..186048991e9e8b41a506f1f621737720c4c98a2a 100644 --- a/packages/frontend/src/components/global/MkStickyContainer.stories.impl.ts +++ b/packages/frontend/src/components/global/MkStickyContainer.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/global/MkStickyContainer.vue b/packages/frontend/src/components/global/MkStickyContainer.vue index 70cc68b14ca063b4fe7d38eee71b2ca18821319a..89993e1b8ea716aee5bfe3c5391d5688168f697e 100644 --- a/packages/frontend/src/components/global/MkStickyContainer.vue +++ b/packages/frontend/src/components/global/MkStickyContainer.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -63,27 +63,32 @@ onMounted(() => { watch([parentStickyTop, parentStickyBottom], calc); watch(childStickyTop, () => { + if (bodyEl.value == null) return; bodyEl.value.style.setProperty('--stickyTop', `${childStickyTop.value}px`); }, { immediate: true, }); watch(childStickyBottom, () => { + if (bodyEl.value == null) return; bodyEl.value.style.setProperty('--stickyBottom', `${childStickyBottom.value}px`); }, { immediate: true, }); - headerEl.value.style.position = 'sticky'; - headerEl.value.style.top = 'var(--stickyTop, 0)'; - headerEl.value.style.zIndex = '1000'; - - footerEl.value.style.position = 'sticky'; - footerEl.value.style.bottom = 'var(--stickyBottom, 0)'; - footerEl.value.style.zIndex = '1000'; + if (headerEl.value != null) { + headerEl.value.style.position = 'sticky'; + headerEl.value.style.top = 'var(--stickyTop, 0)'; + headerEl.value.style.zIndex = '1000'; + observer.observe(headerEl.value); + } - observer.observe(headerEl.value); - observer.observe(footerEl.value); + if (footerEl.value != null) { + footerEl.value.style.position = 'sticky'; + footerEl.value.style.bottom = 'var(--stickyBottom, 0)'; + footerEl.value.style.zIndex = '1000'; + observer.observe(footerEl.value); + } }); onUnmounted(() => { diff --git a/packages/frontend/src/components/global/MkTime.stories.impl.ts b/packages/frontend/src/components/global/MkTime.stories.impl.ts index 0eeefa48597ac59dc20c9b0f69243086dd9b6196..355c83911399cfe8444e0b3028ca34b8d7114df8 100644 --- a/packages/frontend/src/components/global/MkTime.stories.impl.ts +++ b/packages/frontend/src/components/global/MkTime.stories.impl.ts @@ -1,16 +1,16 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { expect } from '@storybook/jest'; +import { expect } from '@storybook/test'; import { StoryObj } from '@storybook/vue3'; import MkTime from './MkTime.vue'; import { i18n } from '@/i18n.js'; import { dateTimeFormat } from '@/scripts/intl-const.js'; const now = new Date('2023-04-01T00:00:00.000Z'); -const future = new Date(8640000000000000); +const future = new Date('2024-04-01T00:00:00.000Z'); const oneHourAgo = new Date(now.getTime() - 3600000); const oneDayAgo = new Date(now.getTime() - 86400000); const oneWeekAgo = new Date(now.getTime() - 604800000); @@ -49,11 +49,12 @@ export const Empty = { export const RelativeFuture = { ...Empty, async play({ canvasElement }) { - await expect(canvasElement).toHaveTextContent(i18n.ts._ago.future); + await expect(canvasElement).toHaveTextContent(i18n.tsx._timeIn.years({ n: 1 })); // n (1) = future (2024) - now (2023) }, args: { ...Empty.args, time: future, + origin: now, }, } satisfies StoryObj<typeof MkTime>; export const AbsoluteFuture = { @@ -123,7 +124,7 @@ export const DetailNow = { export const RelativeOneHourAgo = { ...Empty, async play({ canvasElement }) { - await expect(canvasElement).toHaveTextContent(i18n.t('_ago.hoursAgo', { n: 1 })); + await expect(canvasElement).toHaveTextContent(i18n.tsx._ago.hoursAgo({ n: 1 })); }, args: { ...Empty.args, @@ -162,7 +163,7 @@ export const DetailOneHourAgo = { export const RelativeOneDayAgo = { ...Empty, async play({ canvasElement }) { - await expect(canvasElement).toHaveTextContent(i18n.t('_ago.daysAgo', { n: 1 })); + await expect(canvasElement).toHaveTextContent(i18n.tsx._ago.daysAgo({ n: 1 })); }, args: { ...Empty.args, @@ -201,7 +202,7 @@ export const DetailOneDayAgo = { export const RelativeOneWeekAgo = { ...Empty, async play({ canvasElement }) { - await expect(canvasElement).toHaveTextContent(i18n.t('_ago.weeksAgo', { n: 1 })); + await expect(canvasElement).toHaveTextContent(i18n.tsx._ago.weeksAgo({ n: 1 })); }, args: { ...Empty.args, @@ -240,7 +241,7 @@ export const DetailOneWeekAgo = { export const RelativeOneMonthAgo = { ...Empty, async play({ canvasElement }) { - await expect(canvasElement).toHaveTextContent(i18n.t('_ago.monthsAgo', { n: 1 })); + await expect(canvasElement).toHaveTextContent(i18n.tsx._ago.monthsAgo({ n: 1 })); }, args: { ...Empty.args, @@ -279,7 +280,7 @@ export const DetailOneMonthAgo = { export const RelativeOneYearAgo = { ...Empty, async play({ canvasElement }) { - await expect(canvasElement).toHaveTextContent(i18n.t('_ago.yearsAgo', { n: 1 })); + await expect(canvasElement).toHaveTextContent(i18n.tsx._ago.yearsAgo({ n: 1 })); }, args: { ...Empty.args, diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue index e11db9dc311b33554d1c9b903b38964ba2bb6656..67532268d3bec52b4433521812ae1572f9e9aba8 100644 --- a/packages/frontend/src/components/global/MkTime.vue +++ b/packages/frontend/src/components/global/MkTime.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -24,7 +24,7 @@ const props = withDefaults(defineProps<{ mode?: 'relative' | 'absolute' | 'detail'; colored?: boolean; }>(), { - origin: isChromatic() ? new Date('2023-04-01T00:00:00Z') : null, + origin: isChromatic() ? () => new Date('2023-04-01T00:00:00Z') : null, mode: 'relative', }); @@ -55,21 +55,21 @@ const relative = computed<string>(() => { if (invalid) return i18n.ts._ago.invalid; return ( - ago.value >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago.value / 31536000).toString() }) : - ago.value >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago.value / 2592000).toString() }) : - ago.value >= 604800 ? i18n.t('_ago.weeksAgo', { n: Math.round(ago.value / 604800).toString() }) : - ago.value >= 86400 ? i18n.t('_ago.daysAgo', { n: Math.round(ago.value / 86400).toString() }) : - ago.value >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago.value / 3600).toString() }) : - ago.value >= 60 ? i18n.t('_ago.minutesAgo', { n: (~~(ago.value / 60)).toString() }) : - ago.value >= 10 ? i18n.t('_ago.secondsAgo', { n: (~~(ago.value % 60)).toString() }) : + ago.value >= 31536000 ? i18n.tsx._ago.yearsAgo({ n: Math.round(ago.value / 31536000).toString() }) : + ago.value >= 2592000 ? i18n.tsx._ago.monthsAgo({ n: Math.round(ago.value / 2592000).toString() }) : + ago.value >= 604800 ? i18n.tsx._ago.weeksAgo({ n: Math.round(ago.value / 604800).toString() }) : + ago.value >= 86400 ? i18n.tsx._ago.daysAgo({ n: Math.round(ago.value / 86400).toString() }) : + ago.value >= 3600 ? i18n.tsx._ago.hoursAgo({ n: Math.round(ago.value / 3600).toString() }) : + ago.value >= 60 ? i18n.tsx._ago.minutesAgo({ n: (~~(ago.value / 60)).toString() }) : + ago.value >= 10 ? i18n.tsx._ago.secondsAgo({ n: (~~(ago.value % 60)).toString() }) : ago.value >= -3 ? i18n.ts._ago.justNow : - ago.value < -31536000 ? i18n.t('_timeIn.years', { n: Math.round(-ago.value / 31536000).toString() }) : - ago.value < -2592000 ? i18n.t('_timeIn.months', { n: Math.round(-ago.value / 2592000).toString() }) : - ago.value < -604800 ? i18n.t('_timeIn.weeks', { n: Math.round(-ago.value / 604800).toString() }) : - ago.value < -86400 ? i18n.t('_timeIn.days', { n: Math.round(-ago.value / 86400).toString() }) : - ago.value < -3600 ? i18n.t('_timeIn.hours', { n: Math.round(-ago.value / 3600).toString() }) : - ago.value < -60 ? i18n.t('_timeIn.minutes', { n: (~~(-ago.value / 60)).toString() }) : - i18n.t('_timeIn.seconds', { n: (~~(-ago.value % 60)).toString() }) + ago.value < -31536000 ? i18n.tsx._timeIn.years({ n: Math.round(-ago.value / 31536000).toString() }) : + ago.value < -2592000 ? i18n.tsx._timeIn.months({ n: Math.round(-ago.value / 2592000).toString() }) : + ago.value < -604800 ? i18n.tsx._timeIn.weeks({ n: Math.round(-ago.value / 604800).toString() }) : + ago.value < -86400 ? i18n.tsx._timeIn.days({ n: Math.round(-ago.value / 86400).toString() }) : + ago.value < -3600 ? i18n.tsx._timeIn.hours({ n: Math.round(-ago.value / 3600).toString() }) : + ago.value < -60 ? i18n.tsx._timeIn.minutes({ n: (~~(-ago.value / 60)).toString() }) : + i18n.tsx._timeIn.seconds({ n: (~~(-ago.value % 60)).toString() }) ); }); diff --git a/packages/frontend/src/components/global/MkUrl.stories.impl.ts b/packages/frontend/src/components/global/MkUrl.stories.impl.ts index b35b6114fdd4e3e4af4050a687720a562ae04495..34a4adfe49d483880f0c8378c340773ebb24b448 100644 --- a/packages/frontend/src/components/global/MkUrl.stories.impl.ts +++ b/packages/frontend/src/components/global/MkUrl.stories.impl.ts @@ -1,13 +1,12 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { expect } from '@storybook/jest'; -import { userEvent, waitFor, within } from '@storybook/testing-library'; +import { expect, userEvent, waitFor, within } from '@storybook/test'; import { StoryObj } from '@storybook/vue3'; -import { rest } from 'msw'; +import { HttpResponse, http } from 'msw'; import { commonHandlers } from '../../../.storybook/mocks.js'; import MkUrl from './MkUrl.vue'; export const Default = { @@ -59,8 +58,8 @@ export const Default = { msw: { handlers: [ ...commonHandlers, - rest.get('/url', (req, res, ctx) => { - return res(ctx.json({ + http.get('/url', () => { + return HttpResponse.json({ title: 'Misskey Hub', icon: 'https://misskey-hub.net/favicon.ico', description: 'Misskeyã¯ã‚ªãƒ¼ãƒ—ンソースã®åˆ†æ•£åž‹ã‚½ãƒ¼ã‚·ãƒ£ãƒ«ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚ングプラットフォームã§ã™ã€‚', @@ -74,7 +73,7 @@ export const Default = { sitename: 'misskey-hub.net', sensitive: false, url: 'https://misskey-hub.net/', - })); + }); }), ], }, diff --git a/packages/frontend/src/components/global/MkUrl.vue b/packages/frontend/src/components/global/MkUrl.vue index 667a113432de5e3b150e44e5610384c8f19fb506..b810840b69a8dd148b33f4033ec8e9a78d79b7ad 100644 --- a/packages/frontend/src/components/global/MkUrl.vue +++ b/packages/frontend/src/components/global/MkUrl.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <component :is="self ? 'MkA' : 'a'" ref="el" :class="$style.root" class="_link" :[attr]="self ? props.url.substring(local.length) : props.url" :rel="rel ?? 'nofollow noopener'" :target="target" @contextmenu.stop="() => {}" + @click.stop > <template v-if="!self"> <span :class="$style.schema">{{ schema }}//</span> diff --git a/packages/frontend/src/components/global/MkUserName.stories.impl.ts b/packages/frontend/src/components/global/MkUserName.stories.impl.ts index 8f47a6c1ab174c0b64d4821277fc9420d2f9ccfe..88bf4f4e6c1d3325ade78e2f1a7ad6079670ab9d 100644 --- a/packages/frontend/src/components/global/MkUserName.stories.impl.ts +++ b/packages/frontend/src/components/global/MkUserName.stories.impl.ts @@ -1,10 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { expect } from '@storybook/jest'; +import { expect } from '@storybook/test'; import { StoryObj } from '@storybook/vue3'; import { userDetailed } from '../../../.storybook/fakes.js'; import MkUserName from './MkUserName.vue'; diff --git a/packages/frontend/src/components/global/MkUserName.vue b/packages/frontend/src/components/global/MkUserName.vue index be283ea922ff9e434ac708b6bcba08a0a681090f..c5bcf53102166ee3ae4c79ff4d4b10eda10ba97c 100644 --- a/packages/frontend/src/components/global/MkUserName.vue +++ b/packages/frontend/src/components/global/MkUserName.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/global/RouterView.stories.impl.ts b/packages/frontend/src/components/global/RouterView.stories.impl.ts index 2fe4c53e78ce43fe54a5e0c3ac74df3317c96360..5dfe12b0c99b6b9346d8b35106b58c798b0c96d5 100644 --- a/packages/frontend/src/components/global/RouterView.stories.impl.ts +++ b/packages/frontend/src/components/global/RouterView.stories.impl.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue index 99ed8adbefd5bc7be2595146ed29e9a528269c87..06cb30eff1b1d4e2cf938e478d0de0eba3feb12a 100644 --- a/packages/frontend/src/components/global/RouterView.vue +++ b/packages/frontend/src/components/global/RouterView.vue @@ -1,10 +1,13 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> -<KeepAlive :max="defaultStore.state.numberOfPageCache"> +<KeepAlive + :max="defaultStore.state.numberOfPageCache" + :exclude="pageCacheController" +> <Suspense :timeout="0"> <component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/> @@ -16,12 +19,14 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { inject, onBeforeUnmount, provide, shallowRef, ref } from 'vue'; -import { Resolved, Router } from '@/nirax.js'; +import { inject, onBeforeUnmount, provide, ref, shallowRef, computed, nextTick } from 'vue'; +import { IRouter, Resolved, RouteDef } from '@/nirax.js'; import { defaultStore } from '@/store.js'; +import { globalEvents } from '@/events.js'; +import MkLoadingPage from '@/pages/_loading_.vue'; const props = defineProps<{ - router?: Router; + router?: IRouter; }>(); const router = props.router ?? inject('router'); @@ -46,20 +51,47 @@ function resolveNested(current: Resolved, d = 0): Resolved | null { } const current = resolveNested(router.current)!; -const currentPageComponent = shallowRef(current.route.component); +const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage); const currentPageProps = ref(current.props); const key = ref(current.route.path + JSON.stringify(Object.fromEntries(current.props))); function onChange({ resolved, key: newKey }) { const current = resolveNested(resolved); - if (current == null) return; + if (current == null || 'redirect' in current.route) return; currentPageComponent.value = current.route.component; currentPageProps.value = current.props; key.value = current.route.path + JSON.stringify(Object.fromEntries(current.props)); + + nextTick(() => { + // ページé·ç§»å®Œäº†å¾Œã«å†ã³ã‚ャッシュを有効化 + if (clearCacheRequested.value) { + clearCacheRequested.value = false; + } + }); } router.addListener('change', onChange); +// #region ã‚ャッシュ制御 + +/** + * ã‚ャッシュクリアãŒæœ‰åŠ¹ã«ãªã£ãŸã‚‰ã€å…¨ã‚ャッシュをクリアã™ã‚‹ + * + * keepAliveå´ã«watcherãŒã‚ã‚‹ã®ã§ã™ã消ãˆã‚‹ã¨ã¯ãŠã‚‚ã†ã‘ã©ã€å¿µã®ãŸã‚ページé·ç§»å®Œäº†ã¾ã§ã¯ã‚ャッシュを無効化ã—ã¦ãŠã。 + * ã‚ャッシュ有効時å‘ã‘ã«excludeを使ã„ãŸã„å ´åˆã¯ã€pageCacheControllerã«ä¸¦åˆ—ã«çªã£è¾¼ã‚€ã®ã§ã¯ãªãã€ä¸‹ã«è¿½è¨˜ã™ã‚‹ã“㨠+ */ +const pageCacheController = computed(() => clearCacheRequested.value ? /.*/ : undefined); +const clearCacheRequested = ref(false); + +globalEvents.on('requestClearPageCache', () => { + if (_DEV_) console.log('clear page cache requested'); + if (!clearCacheRequested.value) { + clearCacheRequested.value = true; + } +}); + +// #endregion + onBeforeUnmount(() => { router.removeListener('change', onChange); }); diff --git a/packages/frontend/src/components/global/i18n.ts b/packages/frontend/src/components/global/i18n.ts deleted file mode 100644 index 2f4d7edabd9600bfd15cca0b6c61829950dbef6e..0000000000000000000000000000000000000000 --- a/packages/frontend/src/components/global/i18n.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and other misskey contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { h } from 'vue'; - -export default function(props: { src: string; tag?: string; textTag?: string; }, { slots }) { - let str = props.src; - const parsed = [] as (string | { arg: string; })[]; - while (true) { - const nextBracketOpen = str.indexOf('{'); - const nextBracketClose = str.indexOf('}'); - - if (nextBracketOpen === -1) { - parsed.push(str); - break; - } else { - if (nextBracketOpen > 0) parsed.push(str.substring(0, nextBracketOpen)); - parsed.push({ - arg: str.substring(nextBracketOpen + 1, nextBracketClose), - }); - } - - str = str.substring(nextBracketClose + 1); - } - - return h(props.tag ?? 'span', parsed.map(x => typeof x === 'string' ? (props.textTag ? h(props.textTag, x) : x) : slots[x.arg]())); -} diff --git a/packages/frontend/src/components/index.ts b/packages/frontend/src/components/index.ts index a3e13c3a506703a0d52ab41c97c3d0663f6b11ef..44d8d59941b3fac3379c28c2422f4e9cd747ee48 100644 --- a/packages/frontend/src/components/index.ts +++ b/packages/frontend/src/components/index.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -16,7 +16,7 @@ import MkUserName from './global/MkUserName.vue'; import MkEllipsis from './global/MkEllipsis.vue'; import MkTime from './global/MkTime.vue'; import MkUrl from './global/MkUrl.vue'; -import I18n from './global/i18n.js'; +import I18n from './global/I18n.vue'; import RouterView from './global/RouterView.vue'; import MkLoading from './global/MkLoading.vue'; import MkError from './global/MkError.vue'; diff --git a/packages/frontend/src/components/page/block.type.ts b/packages/frontend/src/components/page/block.type.ts deleted file mode 100644 index cdd39339e698448f1fb2a2f88c4173070f8ef641..0000000000000000000000000000000000000000 --- a/packages/frontend/src/components/page/block.type.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and other misskey contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - -export type BlockBase = { - id: string; - type: string; -}; - -export type TextBlock = BlockBase & { - type: 'text'; - text: string; -}; - -export type SectionBlock = BlockBase & { - type: 'section'; - title: string; - children: Block[]; -}; - -export type ImageBlock = BlockBase & { - type: 'image'; - fileId: string | null; -}; - -export type NoteBlock = BlockBase & { - type: 'note'; - detailed: boolean; - note: string | null; -}; - -export type Block = - TextBlock | SectionBlock | ImageBlock | NoteBlock; diff --git a/packages/frontend/src/components/page/page.block.vue b/packages/frontend/src/components/page/page.block.vue index 7dbbaa03b4828167f067fa4acac37e229304f4cc..164720ac6bd0d874c78ab48ac44d90ce0bdb65e3 100644 --- a/packages/frontend/src/components/page/page.block.vue +++ b/packages/frontend/src/components/page/page.block.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -14,7 +14,6 @@ import XText from './page.text.vue'; import XSection from './page.section.vue'; import XImage from './page.image.vue'; import XNote from './page.note.vue'; -import { Block } from './block.type.js'; function getComponent(type: string) { switch (type) { @@ -27,7 +26,7 @@ function getComponent(type: string) { } defineProps<{ - block: Block, + block: Misskey.entities.PageBlock, h: number, page: Misskey.entities.Page, }>(); diff --git a/packages/frontend/src/components/page/page.image.vue b/packages/frontend/src/components/page/page.image.vue index 29aebf63e548810760e571a793acc833769790d3..ced02943db136688df0fecc1bf73e261aa58cbab 100644 --- a/packages/frontend/src/components/page/page.image.vue +++ b/packages/frontend/src/components/page/page.image.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -14,15 +14,19 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref } from 'vue'; +import { onMounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; -import { ImageBlock } from './block.type.js'; import MediaImage from '@/components/MkMediaImage.vue'; const props = defineProps<{ - block: ImageBlock, + block: Misskey.entities.PageBlock, page: Misskey.entities.Page, }>(); -const image = ref<Misskey.entities.DriveFile>(props.page.attachedFiles.find(x => x.id === props.block.fileId)); +const image = ref<Misskey.entities.DriveFile | null>(null); + +onMounted(() => { + image.value = props.page.attachedFiles.find(x => x.id === props.block.fileId) ?? null; +}); + </script> diff --git a/packages/frontend/src/components/page/page.note.vue b/packages/frontend/src/components/page/page.note.vue index d885ebb1d6c5dcae54682d239976120e11a21ced..7b56494a6e8a1d97193246b5bc629cd752b7e4b5 100644 --- a/packages/frontend/src/components/page/page.note.vue +++ b/packages/frontend/src/components/page/page.note.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -13,20 +13,20 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onMounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; -import { NoteBlock } from './block.type.js'; import MkNote from '@/components/MkNote.vue'; import MkNoteDetailed from '@/components/MkNoteDetailed.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const props = defineProps<{ - block: NoteBlock, + block: Misskey.entities.PageBlock, page: Misskey.entities.Page, }>(); const note = ref<Misskey.entities.Note | null>(null); onMounted(() => { - os.api('notes/show', { noteId: props.block.note }) + if (props.block.note == null) return; + misskeyApi('notes/show', { noteId: props.block.note }) .then(result => { note.value = result; }); diff --git a/packages/frontend/src/components/page/page.section.vue b/packages/frontend/src/components/page/page.section.vue index e4e5a43b594bca67c44e4de093a61860b51206e3..e3d26d924f94d330c97a5f5608c2281170124a37 100644 --- a/packages/frontend/src/components/page/page.section.vue +++ b/packages/frontend/src/components/page/page.section.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -25,12 +25,11 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { defineAsyncComponent } from 'vue'; import * as Misskey from 'misskey-js'; -import { SectionBlock } from './block.type.js'; const XBlock = defineAsyncComponent(() => import('./page.block.vue')); defineProps<{ - block: SectionBlock, + block: Misskey.entities.PageBlock, h: number, page: Misskey.entities.Page, }>(); diff --git a/packages/frontend/src/components/page/page.text.vue b/packages/frontend/src/components/page/page.text.vue index c0849a6d427212b31de0fcd796bf0bad0a84d4d8..6a9415e1370b9f564145deb183daf9820f0ccd2d 100644 --- a/packages/frontend/src/components/page/page.text.vue +++ b/packages/frontend/src/components/page/page.text.vue @@ -1,26 +1,25 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <div class="_gaps"> - <Mfm :text="block.text" :isNote="false"/> + <Mfm :text="block.text ?? ''" :isNote="false"/> <MkUrlPreview v-for="url in urls" :key="url" :url="url"/> </div> </template> <script lang="ts" setup> import { defineAsyncComponent } from 'vue'; -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; import * as Misskey from 'misskey-js'; -import { TextBlock } from './block.type.js'; import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js'; const MkUrlPreview = defineAsyncComponent(() => import('@/components/MkUrlPreview.vue')); const props = defineProps<{ - block: TextBlock, + block: Misskey.entities.PageBlock, page: Misskey.entities.Page, }>(); diff --git a/packages/frontend/src/components/page/page.vue b/packages/frontend/src/components/page/page.vue index 94ca7bdf04bb070c654f76ce2f5ced26d830487d..53c70b01f4abf9313aca61c806f62d1a09bb4fa2 100644 --- a/packages/frontend/src/components/page/page.vue +++ b/packages/frontend/src/components/page/page.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/config.ts b/packages/frontend/src/config.ts index 636e51c3749eb3b351c796bd16ed41aaaaa0d338..e3922a0cd5a7f1f609d572420231aa7950ed726c 100644 --- a/packages/frontend/src/config.ts +++ b/packages/frontend/src/config.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -18,7 +18,7 @@ export const langs = _LANGS_; const preParseLocale = miLocalStorage.getItem('locale'); export let locale = preParseLocale ? JSON.parse(preParseLocale) : null; export const version = _VERSION_; -export const instanceName = siteName === 'Sharkey' ? host : siteName; +export const instanceName = siteName === 'Sharkey' || siteName == null ? host : siteName; export const ui = miLocalStorage.getItem('ui'); export const debug = miLocalStorage.getItem('debug') === 'true'; diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index cdd67312699a7708c9a8f6998bb2e369889ca78e..ad798067b3fbe4ff8691cbfe8e39f1e64e0f781f 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -128,6 +128,7 @@ export const ROLE_POLICIES = [ 'btlAvailable', 'canPublicNote', 'canImportNotes', + 'mentionLimit', 'canInvite', 'inviteLimit', 'inviteLimitCycle', @@ -161,4 +162,28 @@ export const DEFAULT_SERVER_ERROR_IMAGE_URL = 'https://launcher.moe/error.png'; export const DEFAULT_NOT_FOUND_IMAGE_URL = 'https://launcher.moe/missingpage.webp'; export const DEFAULT_INFO_IMAGE_URL = 'https://launcher.moe/nothinghere.png'; -export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime']; +export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime']; +export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = { + tada: ['speed=', 'delay='], + jelly: ['speed=', 'delay='], + twitch: ['speed=', 'delay='], + shake: ['speed=', 'delay='], + spin: ['speed=', 'delay=', 'left', 'alternate', 'x', 'y'], + jump: ['speed=', 'delay='], + bounce: ['speed=', 'delay='], + flip: ['h', 'v'], + x2: [], + x3: [], + x4: [], + scale: ['x=', 'y='], + position: ['x=', 'y='], + fg: ['color='], + bg: ['color='], + border: ['width=', 'style=', 'color=', 'radius=', 'noclip'], + font: ['serif', 'monospace', 'cursive', 'fantasy', 'emoji', 'math'], + blur: [], + rainbow: ['speed=', 'delay='], + rotate: ['deg='], + ruby: [], + unixtime: [], +}; diff --git a/packages/frontend/src/custom-emojis.ts b/packages/frontend/src/custom-emojis.ts index 6a48159f13b941cd54faa8ace9e952ed9608bd9a..9da3582e1a616dc4fbb34b197a82a9fed2658007 100644 --- a/packages/frontend/src/custom-emojis.ts +++ b/packages/frontend/src/custom-emojis.ts @@ -1,11 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { shallowRef, computed, markRaw, watch } from 'vue'; import * as Misskey from 'misskey-js'; -import { api, apiGet } from '@/os.js'; +import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js'; import { useStream } from '@/stream.js'; import { get, set } from '@/scripts/idb-proxy.js'; @@ -52,11 +52,11 @@ export async function fetchCustomEmojis(force = false) { let res; if (force) { - res = await api('emojis', {}); + res = await misskeyApi('emojis', {}); } else { const lastFetchedAt = await get('lastEmojisFetchedAt'); if (lastFetchedAt && (now - lastFetchedAt) < 1000 * 60 * 60) return; - res = await apiGet('emojis', {}); + res = await misskeyApiGet('emojis', {}); } customEmojis.value = res.emojis; diff --git a/packages/frontend/src/debug.ts b/packages/frontend/src/debug.ts index 6df65bb763536bfda22f4a05b057e071da5b3a89..8bb8012ae36340b50bb03355744c2884798007f1 100644 --- a/packages/frontend/src/debug.ts +++ b/packages/frontend/src/debug.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/directives/adaptive-bg.ts b/packages/frontend/src/directives/adaptive-bg.ts index dd9691d9e26b6fa217c9a4bae2088c2203869eef..23fd1bddf4a25f46a38d648cd8d5a35ae1ea8a1d 100644 --- a/packages/frontend/src/directives/adaptive-bg.ts +++ b/packages/frontend/src/directives/adaptive-bg.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/directives/adaptive-border.ts b/packages/frontend/src/directives/adaptive-border.ts index 220cf4b9a6a6432c2babc0ebdf4534c3045700a4..b436075fcd4ae0404e7df620cf72b0adb7db9ec8 100644 --- a/packages/frontend/src/directives/adaptive-border.ts +++ b/packages/frontend/src/directives/adaptive-border.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/directives/anim.ts b/packages/frontend/src/directives/anim.ts index cf49799ef593a85608f9626dcc4314a91d5fd2fd..d5b6ae4287999bca287e372d3fbdd1316fab3169 100644 --- a/packages/frontend/src/directives/anim.ts +++ b/packages/frontend/src/directives/anim.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/directives/appear.ts b/packages/frontend/src/directives/appear.ts index 3fcff4d978f9a248a4ddadbb00f85b06acb5a920..706d4a9ee4b66a3556f602a1a01ec62efe801994 100644 --- a/packages/frontend/src/directives/appear.ts +++ b/packages/frontend/src/directives/appear.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/directives/click-anime.ts b/packages/frontend/src/directives/click-anime.ts index 2b3cdb27a508f4dbaf09cb9407671ac1aabb8385..5bb48bbcdd486b3717e77b7828c0e5c6170f958f 100644 --- a/packages/frontend/src/directives/click-anime.ts +++ b/packages/frontend/src/directives/click-anime.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/directives/follow-append.ts b/packages/frontend/src/directives/follow-append.ts index ae3e31e291aaa4cc92709b6d50676406100f1bcf..f200f242ed23621d44a5d91a697b4039269762b0 100644 --- a/packages/frontend/src/directives/follow-append.ts +++ b/packages/frontend/src/directives/follow-append.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/directives/get-size.ts b/packages/frontend/src/directives/get-size.ts index 56ff64035fcec19684095091226f82a82bc2e38f..2655c76c482e3cf839d825534ab5d5ddcd23007d 100644 --- a/packages/frontend/src/directives/get-size.ts +++ b/packages/frontend/src/directives/get-size.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/directives/hotkey.ts b/packages/frontend/src/directives/hotkey.ts index 13e548299fcf35ea99f2beb1b8de16ebfe3fbe5e..b082b6edf2d4fd6316cfc8f428b03c2e4bea2fe5 100644 --- a/packages/frontend/src/directives/hotkey.ts +++ b/packages/frontend/src/directives/hotkey.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/directives/index.ts b/packages/frontend/src/directives/index.ts index fcd7c3091e2ed333b2e5d64873326bea63a95232..bda7738ccdaa5a56057ef7fc8cd23168203d677c 100644 --- a/packages/frontend/src/directives/index.ts +++ b/packages/frontend/src/directives/index.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/directives/panel.ts b/packages/frontend/src/directives/panel.ts index 4916fcbd8d1faca5149bcb9e9a5cfb4dfbbe568e..bbcc220e094121c86b8894f848f83172f7dee1bd 100644 --- a/packages/frontend/src/directives/panel.ts +++ b/packages/frontend/src/directives/panel.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/directives/ripple.ts b/packages/frontend/src/directives/ripple.ts index cabd155c8709584bdbc123a09e4d1882c6bc0f47..2d724f771e402ec7f3b9171818da8ecef23f1dda 100644 --- a/packages/frontend/src/directives/ripple.ts +++ b/packages/frontend/src/directives/ripple.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/directives/tooltip.ts b/packages/frontend/src/directives/tooltip.ts index 5d6ec2928b1e6cb9043d4542d7f37dc0c793f883..b1c1b19907d6950bfb9339db9bc241a4e68193ed 100644 --- a/packages/frontend/src/directives/tooltip.ts +++ b/packages/frontend/src/directives/tooltip.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/directives/user-preview.ts b/packages/frontend/src/directives/user-preview.ts index e0fd10047a6d78bb089acaaf667f5defbbb49330..7a008a44865eb32842f65cc1e6578b254a081783 100644 --- a/packages/frontend/src/directives/user-preview.ts +++ b/packages/frontend/src/directives/user-preview.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -99,7 +99,6 @@ export class UserPreview { this.el.removeEventListener('mouseover', this.onMouseover); this.el.removeEventListener('mouseleave', this.onMouseleave); this.el.removeEventListener('click', this.onClick); - window.clearInterval(this.checkTimer); } } diff --git a/packages/frontend/src/events.ts b/packages/frontend/src/events.ts index 90d5f6eede42d9f2da81da34c7b2587e43d274bb..d476aec04a52a80be36bc9f906ff791da4f3c6a8 100644 --- a/packages/frontend/src/events.ts +++ b/packages/frontend/src/events.ts @@ -1,9 +1,13 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { EventEmitter } from 'eventemitter3'; +import * as Misskey from 'misskey-js'; -// TODO: 型付㑠-export const globalEvents = new EventEmitter(); +export const globalEvents = new EventEmitter<{ + themeChanged: () => void; + clientNotification: (notification: Misskey.entities.Notification) => void; + requestClearPageCache: () => void; +}>(); diff --git a/packages/frontend/src/filters/bytes.ts b/packages/frontend/src/filters/bytes.ts index d40b020a9eeac208fa6ec562008af2203454a6b9..49b44167d41e42f390787da65c590d08a6677407 100644 --- a/packages/frontend/src/filters/bytes.ts +++ b/packages/frontend/src/filters/bytes.ts @@ -1,14 +1,14 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ export default (v, digits = 0) => { if (v == null) return '?'; - const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; + const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'RB', 'QB']; if (v === 0) return '0'; const isMinus = v < 0; if (isMinus) v = -v; const i = Math.floor(Math.log(v) / Math.log(1024)); - return (isMinus ? '-' : '') + (v / Math.pow(1024, i)).toFixed(digits).replace(/\.0+$/, '') + sizes[i]; + return (isMinus ? '-' : '') + (v / Math.pow(1024, i)).toFixed(digits).replace(/(\.[1-9]*)0+$/, '$1').replace(/\.$/, '') + (sizes[i] ?? `e+${ i * 3 }B`); }; diff --git a/packages/frontend/src/filters/date.ts b/packages/frontend/src/filters/date.ts index 23541f1094264d1ce9d43ad36919085962b53470..2ffe93e868a2cc2905a34411e366cdee2c4640cc 100644 --- a/packages/frontend/src/filters/date.ts +++ b/packages/frontend/src/filters/date.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/filters/hms.ts b/packages/frontend/src/filters/hms.ts new file mode 100644 index 0000000000000000000000000000000000000000..7f90c92e9998277427a5bc86583ba5eb3352267c --- /dev/null +++ b/packages/frontend/src/filters/hms.ts @@ -0,0 +1,65 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { i18n } from '@/i18n.js'; + +export function hms(ms: number, options?: { + textFormat?: 'colon' | 'locale'; + enableSeconds?: boolean; + enableMs?: boolean; +}) { + const _options = { + textFormat: 'colon', + enableSeconds: true, + enableMs: false, + ...options, + }; + + const res: { + h?: string; + m?: string; + s?: string; + ms?: string; + } = {}; + + // ミリ秒を秒ã«å¤‰æ› + let seconds = Math.floor(ms / 1000); + + // å°æ•°ç‚¹ä»¥ä¸‹ã®å€¤ï¼ˆï¼’ä½ã¾ã§ï¼‰ + const mili = ms - seconds * 1000; + + // 時間を計算 + const hours = Math.floor(seconds / 3600); + res.h = format(hours); + seconds %= 3600; + + // 分を計算 + const minutes = Math.floor(seconds / 60); + res.m = format(minutes); + seconds %= 60; + + // 残ã£ãŸç§’æ•°ã‚’å–å¾— + seconds = seconds % 60; + res.s = format(seconds); + + // ミリ秒をå–å¾— + res.ms = format(Math.floor(mili / 10)); + + // çµæžœã‚’返㙠+ if (_options.textFormat === 'locale') { + res.h += i18n.ts._time.hour; + res.m += i18n.ts._time.minute; + res.s += i18n.ts._time.second; + } + return [ + res.h.startsWith('00') ? undefined : res.h, + res.m, + (_options.enableSeconds ? res.s : undefined), + ].filter(v => v !== undefined).join(_options.textFormat === 'colon' ? ':' : ' ') + (_options.enableMs ? _options.textFormat === 'colon' ? `.${res.ms}` : ` ${res.ms}` : ''); +} + +function format(n: number) { + return n.toString().padStart(2, '0'); +} diff --git a/packages/frontend/src/filters/kmg.ts b/packages/frontend/src/filters/kmg.ts new file mode 100644 index 0000000000000000000000000000000000000000..4dcb5c580027c20260def99091e7170859fa2bd5 --- /dev/null +++ b/packages/frontend/src/filters/kmg.ts @@ -0,0 +1,9 @@ +export default (v, fractionDigits = 0) => { + if (v == null) return 'N/A'; + if (v === 0) return '0'; + const sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'R', 'Q']; + const isMinus = v < 0; + if (isMinus) v = -v; + const i = Math.floor(Math.log(v) / Math.log(1000)); + return (isMinus ? '-' : '') + (v / Math.pow(1000, i)).toFixed(fractionDigits).replace(/(\.[1-9]*)0+$/, '$1').replace(/\.$/, '') + (sizes[i] ?? `e+${ i * 3 }`); +}; diff --git a/packages/frontend/src/filters/note.ts b/packages/frontend/src/filters/note.ts index 626d03a096442936574543918d4e776589873a6a..ce31021469561fe147f0e788dd1ecf8c896ecea6 100644 --- a/packages/frontend/src/filters/note.ts +++ b/packages/frontend/src/filters/note.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/filters/number.ts b/packages/frontend/src/filters/number.ts index d0e4f4991f277fb921306bf3c5100cc1b74cc71e..2e7cc60ff43dc4dff5937087a30bc6633fab65a9 100644 --- a/packages/frontend/src/filters/number.ts +++ b/packages/frontend/src/filters/number.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/filters/user.ts b/packages/frontend/src/filters/user.ts index 8d206037257b2ab074ff3fffbf36a0fa008b4df4..b713d4178954e0b299684b6e2acd56e6054dc7ae 100644 --- a/packages/frontend/src/filters/user.ts +++ b/packages/frontend/src/filters/user.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/i18n.ts b/packages/frontend/src/i18n.ts index 858db74dacf990412f673cc1b524e84cb8c880a7..cc9faddb2098ad4bf4c2495a0e0a30650e244f13 100644 --- a/packages/frontend/src/i18n.ts +++ b/packages/frontend/src/i18n.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -10,6 +10,7 @@ import { I18n } from '@/scripts/i18n.js'; export const i18n = markRaw(new I18n<Locale>(locale)); -export function updateI18n(newLocale) { - i18n.ts = newLocale; +export function updateI18n(newLocale: Locale) { + // @ts-expect-error -- private field + i18n.locale = newLocale; } diff --git a/packages/frontend/src/index.html b/packages/frontend/src/index.html index 8de01e4802d389783bf6285e8d719476e3efdf3a..ecd4f4471355ebec05e6607618944e3b430d0c52 100644 --- a/packages/frontend/src/index.html +++ b/packages/frontend/src/index.html @@ -1,5 +1,5 @@ <!-- - SPDX-FileCopyrightText: syuilo and other misskey contributors + SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -16,20 +16,22 @@ <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --> <meta http-equiv="Content-Security-Policy" - content="default-src 'self'; + content="default-src 'self' https://newassets.hcaptcha.com/ https://challenges.cloudflare.com/ http://localhost:7493/; worker-src 'self'; - script-src 'self' 'unsafe-eval'; + script-src 'self' 'unsafe-eval' https://*.hcaptcha.com https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; - img-src 'self' data: www.google.com xn--931a.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000; + img-src 'self' data: blob: www.google.com xn--931a.moe launcher.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000; media-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000; - connect-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;" + connect-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000 https://newassets.hcaptcha.com; + frame-src *;" /> <meta property="og:site_name" content="[DEV BUILD] Misskey" /> <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="theme-color-orig" content="#86b300"> </head> <body> -<div id="misskey_app"></div> +<div id="sharkey_app"></div> <script type="module" src="./_dev_boot_.ts"></script> </body> </html> diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts index b09264dabbeb552c5d05a2160c9792cf5864b52c..4232cbcd78d54c70d061cbc6d1913bd805084342 100644 --- a/packages/frontend/src/instance.ts +++ b/packages/frontend/src/instance.ts @@ -1,23 +1,34 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { computed, reactive } from 'vue'; import * as Misskey from 'misskey-js'; -import { api } from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { miLocalStorage } from '@/local-storage.js'; import { DEFAULT_INFO_IMAGE_URL, DEFAULT_NOT_FOUND_IMAGE_URL, DEFAULT_SERVER_ERROR_IMAGE_URL } from '@/const.js'; // TODO: ä»–ã®ã‚¿ãƒ–ã¨æ°¸ç¶šåŒ–ã•ã‚ŒãŸstateã‚’åŒæœŸ -const cached = miLocalStorage.getItem('instance'); +//#region loader +const providedMetaEl = document.getElementById('misskey_meta'); + +let cachedMeta = miLocalStorage.getItem('instance') ? JSON.parse(miLocalStorage.getItem('instance')!) : null; +let cachedAt = miLocalStorage.getItem('instanceCachedAt') ? parseInt(miLocalStorage.getItem('instanceCachedAt')!) : 0; +const providedMeta = providedMetaEl && providedMetaEl.textContent ? JSON.parse(providedMetaEl.textContent) : null; +const providedAt = providedMetaEl && providedMetaEl.dataset.generatedAt ? parseInt(providedMetaEl.dataset.generatedAt) : 0; +if (providedAt > cachedAt) { + miLocalStorage.setItem('instance', JSON.stringify(providedMeta)); + miLocalStorage.setItem('instanceCachedAt', providedAt.toString()); + cachedMeta = providedMeta; + cachedAt = providedAt; +} +//#endregion // TODO: instanceをリアクティブã«ã™ã‚‹ã‹ã¯å†è€ƒã®ä½™åœ°ã‚ã‚Š -export const instance: Misskey.entities.MetaResponse = reactive(cached ? JSON.parse(cached) : { - // TODO: set default values -}); +export const instance: Misskey.entities.MetaResponse = reactive(cachedMeta ?? {}); export const serverErrorImageUrl = computed(() => instance.serverErrorImageUrl ?? DEFAULT_SERVER_ERROR_IMAGE_URL); @@ -25,8 +36,16 @@ export const infoImageUrl = computed(() => instance.infoImageUrl ?? DEFAULT_INFO export const notFoundImageUrl = computed(() => instance.notFoundImageUrl ?? DEFAULT_NOT_FOUND_IMAGE_URL); -export async function fetchInstance() { - const meta = await api('meta', { +export async function fetchInstance(force = false): Promise<void> { + if (!force) { + const cachedAt = miLocalStorage.getItem('instanceCachedAt') ? parseInt(miLocalStorage.getItem('instanceCachedAt')!) : 0; + + if (Date.now() - cachedAt < 1000 * 60 * 60) { + return; + } + } + + const meta = await misskeyApi('meta', { detail: false, }); @@ -35,4 +54,5 @@ export async function fetchInstance() { } miLocalStorage.setItem('instance', JSON.stringify(instance)); + miLocalStorage.setItem('instanceCachedAt', Date.now().toString()); } diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts index d95ff2119ca23537325b24a22491d65559db0bd7..2b2f59edb35c08971da4a9014174267966c9d0d5 100644 --- a/packages/frontend/src/local-storage.ts +++ b/packages/frontend/src/local-storage.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -7,11 +7,13 @@ type Keys = 'v' | 'lastVersion' | 'instance' | + 'instanceCachedAt' | 'account' | 'accounts' | 'latestDonationInfoShownAt' | 'neverShowDonationInfo' | 'neverShowLocalOnlyInfo' | + 'modifiedVersionMustProminentlyOfferInAgplV3Section13Read' | 'lastUsed' | 'lang' | 'drafts' | diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index d615c751eecd2e40c623437984d5b0e777d64ac4..b71c15d19f15a05d18fdf8601844306b482b3817 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -118,6 +118,11 @@ export const navbarItemDef = reactive({ show: computed(() => $i != null && instance.enableAchievements), to: '/my/achievements', }, + games: { + title: 'Games', + icon: 'ph-game-controller ph-bold ph-lg', + to: '/games', + }, ui: { title: i18n.ts.switchUi, icon: 'ph-devices ph-bold ph-lg', diff --git a/packages/frontend/src/nirax.ts b/packages/frontend/src/nirax.ts index 9755bdcb18fefb559c672be258e6a4aaa2874ca4..616fb104e6c030c7a2a7f6a049309de1f1f9c33a 100644 --- a/packages/frontend/src/nirax.ts +++ b/packages/frontend/src/nirax.ts @@ -1,24 +1,33 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ // NIRAX --- A lightweight router -import { EventEmitter } from 'eventemitter3'; import { Component, onMounted, shallowRef, ShallowRef } from 'vue'; +import { EventEmitter } from 'eventemitter3'; import { safeURIDecode } from '@/scripts/safe-uri-decode.js'; -type RouteDef = { +interface RouteDefBase { path: string; - component: Component; query?: Record<string, string>; loginRequired?: boolean; name?: string; hash?: string; globalCacheKey?: string; children?: RouteDef[]; -}; +} + +interface RouteDefWithComponent extends RouteDefBase { + component: Component, +} + +interface RouteDefWithRedirect extends RouteDefBase { + redirect: string | ((props: Map<string, string | boolean>) => string); +} + +export type RouteDef = RouteDefWithComponent | RouteDefWithRedirect; type ParsedPath = (string | { name: string; @@ -27,7 +36,40 @@ type ParsedPath = (string | { optional?: boolean; })[]; -export type Resolved = { route: RouteDef; props: Map<string, string | boolean>; child?: Resolved; }; +export type RouterEvent = { + change: (ctx: { + beforePath: string; + path: string; + resolved: Resolved; + key: string; + }) => void; + replace: (ctx: { + path: string; + key: string; + }) => void; + push: (ctx: { + beforePath: string; + path: string; + route: RouteDef | null; + props: Map<string, string> | null; + key: string; + }) => void; + same: () => void; +} + +export type Resolved = { + route: RouteDef; + props: Map<string, string | boolean>; + child?: Resolved; + redirected?: boolean; + + /** @internal */ + _parsedRoute: { + fullPath: string; + queryString: string | null; + hash: string | null; + }; +}; function parsePath(path: string): ParsedPath { const res = [] as ParsedPath; @@ -54,34 +96,99 @@ function parsePath(path: string): ParsedPath { return res; } -export class Router extends EventEmitter<{ - change: (ctx: { - beforePath: string; - path: string; - resolved: Resolved; - key: string; - }) => void; - replace: (ctx: { - path: string; - key: string; - }) => void; - push: (ctx: { - beforePath: string; - path: string; - route: RouteDef | null; - props: Map<string, string> | null; - key: string; - }) => void; - same: () => void; -}> { +export interface IRouter extends EventEmitter<RouterEvent> { + current: Resolved; + currentRef: ShallowRef<Resolved>; + currentRoute: ShallowRef<RouteDef>; + navHook: ((path: string, flag?: any) => boolean) | null; + + /** + * ルートã®åˆæœŸåŒ–(eventListenerã®å®šç¾©å¾Œã«å¿…ãšå‘¼ã³å‡ºã™ã“ã¨ï¼‰ + */ + init(): void; + + resolve(path: string): Resolved | null; + + getCurrentPath(): any; + + getCurrentKey(): string; + + push(path: string, flag?: any): void; + + replace(path: string, key?: string | null): void; + + /** @see EventEmitter */ + eventNames(): Array<EventEmitter.EventNames<RouterEvent>>; + + /** @see EventEmitter */ + listeners<T extends EventEmitter.EventNames<RouterEvent>>( + event: T + ): Array<EventEmitter.EventListener<RouterEvent, T>>; + + /** @see EventEmitter */ + listenerCount( + event: EventEmitter.EventNames<RouterEvent> + ): number; + + /** @see EventEmitter */ + emit<T extends EventEmitter.EventNames<RouterEvent>>( + event: T, + ...args: EventEmitter.EventArgs<RouterEvent, T> + ): boolean; + + /** @see EventEmitter */ + on<T extends EventEmitter.EventNames<RouterEvent>>( + event: T, + fn: EventEmitter.EventListener<RouterEvent, T>, + context?: any + ): this; + + /** @see EventEmitter */ + addListener<T extends EventEmitter.EventNames<RouterEvent>>( + event: T, + fn: EventEmitter.EventListener<RouterEvent, T>, + context?: any + ): this; + + /** @see EventEmitter */ + once<T extends EventEmitter.EventNames<RouterEvent>>( + event: T, + fn: EventEmitter.EventListener<RouterEvent, T>, + context?: any + ): this; + + /** @see EventEmitter */ + removeListener<T extends EventEmitter.EventNames<RouterEvent>>( + event: T, + fn?: EventEmitter.EventListener<RouterEvent, T>, + context?: any, + once?: boolean | undefined + ): this; + + /** @see EventEmitter */ + off<T extends EventEmitter.EventNames<RouterEvent>>( + event: T, + fn?: EventEmitter.EventListener<RouterEvent, T>, + context?: any, + once?: boolean | undefined + ): this; + + /** @see EventEmitter */ + removeAllListeners( + event?: EventEmitter.EventNames<RouterEvent> + ): this; +} + +export class Router extends EventEmitter<RouterEvent> implements IRouter { private routes: RouteDef[]; public current: Resolved; - public currentRef: ShallowRef<Resolved> = shallowRef(); - public currentRoute: ShallowRef<RouteDef> = shallowRef(); + public currentRef: ShallowRef<Resolved>; + public currentRoute: ShallowRef<RouteDef>; private currentPath: string; private isLoggedIn: boolean; private notFoundPageComponent: Component; private currentKey = Date.now().toString(); + private redirectCount = 0; public navHook: ((path: string, flag?: any) => boolean) | null = null; @@ -89,13 +196,24 @@ export class Router extends EventEmitter<{ super(); this.routes = routes; + this.current = this.resolve(currentPath)!; + this.currentRef = shallowRef(this.current); + this.currentRoute = shallowRef(this.current.route); this.currentPath = currentPath; this.isLoggedIn = isLoggedIn; this.notFoundPageComponent = notFoundPageComponent; - this.navigate(currentPath, null, false); + } + + public init() { + const res = this.navigate(this.currentPath, null, false); + this.emit('replace', { + path: res._parsedRoute.fullPath, + key: this.currentKey, + }); } public resolve(path: string): Resolved | null { + const fullPath = path; let queryString: string | null = null; let hash: string | null = null; if (path[0] === '/') path = path.substring(1); @@ -108,6 +226,12 @@ export class Router extends EventEmitter<{ path = path.substring(0, path.indexOf('?')); } + const _parsedRoute = { + fullPath, + queryString, + hash, + }; + if (_DEV_) console.log('Routing: ', path, queryString); function check(routes: RouteDef[], _parts: string[]): Resolved | null { @@ -158,6 +282,7 @@ export class Router extends EventEmitter<{ route, props, child, + _parsedRoute, }; } else { continue forEachRouteLoop; @@ -183,6 +308,7 @@ export class Router extends EventEmitter<{ return { route, props, + _parsedRoute, }; } else { if (route.children) { @@ -192,6 +318,7 @@ export class Router extends EventEmitter<{ route, props, child, + _parsedRoute, }; } else { continue forEachRouteLoop; @@ -210,7 +337,7 @@ export class Router extends EventEmitter<{ return check(this.routes, _parts); } - private navigate(path: string, key: string | null | undefined, emitChange = true) { + private navigate(path: string, key: string | null | undefined, emitChange = true, _redirected = false): Resolved { const beforePath = this.currentPath; this.currentPath = path; @@ -220,6 +347,20 @@ export class Router extends EventEmitter<{ throw new Error('no route found for: ' + path); } + if ('redirect' in res.route) { + let redirectPath: string; + if (typeof res.route.redirect === 'function') { + redirectPath = res.route.redirect(res.props); + } else { + redirectPath = res.route.redirect + (res._parsedRoute.queryString ? '?' + res._parsedRoute.queryString : '') + (res._parsedRoute.hash ? '#' + res._parsedRoute.hash : ''); + } + if (_DEV_) console.log('Redirecting to: ', redirectPath); + if (_redirected && this.redirectCount++ > 10) { + throw new Error('redirect loop detected'); + } + return this.navigate(redirectPath, null, emitChange, true); + } + if (res.route.loginRequired && !this.isLoggedIn) { res.route.component = this.notFoundPageComponent; res.props.set('showLoginPopup', true); @@ -241,7 +382,11 @@ export class Router extends EventEmitter<{ }); } - return res; + this.redirectCount = 0; + return { + ...res, + redirected: _redirected, + }; } public getCurrentPath() { @@ -265,7 +410,7 @@ export class Router extends EventEmitter<{ const res = this.navigate(path, null); this.emit('push', { beforePath, - path, + path: res._parsedRoute.fullPath, route: res.route, props: res.props, key: this.currentKey, @@ -273,15 +418,20 @@ export class Router extends EventEmitter<{ } public replace(path: string, key?: string | null) { - this.navigate(path, key); + const res = this.navigate(path, key); + this.emit('replace', { + path: res._parsedRoute.fullPath, + key: this.currentKey, + }); } } -export function useScrollPositionManager(getScrollContainer: () => HTMLElement, router: Router) { +export function useScrollPositionManager(getScrollContainer: () => HTMLElement | null, router: IRouter) { const scrollPosStore = new Map<string, number>(); onMounted(() => { const scrollContainer = getScrollContainer(); + if (scrollContainer == null) return; scrollContainer.addEventListener('scroll', () => { scrollPosStore.set(router.getCurrentKey(), scrollContainer.scrollTop); diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index b02f6aa6403d114d5cf83241533308a37e88d967..fc73622d6b9cd65215f655f538cd98867ef87d54 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -1,16 +1,16 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ // TODO: ãªã‚“ã§ã‚‚ã‹ã‚“ã§ã‚‚os.tsã«çªã£è¾¼ã‚€ã®ã‚„ã‚ãŸã„ã®ã§ã‚ˆã—ãªã«åˆ†å‰²ã™ã‚‹ -import { pendingApiRequestsCount, api, apiGet } from '@/scripts/api.js'; -export { pendingApiRequestsCount, api, apiGet }; import { Component, markRaw, Ref, ref, defineAsyncComponent } from 'vue'; import { EventEmitter } from 'eventemitter3'; -import insertTextAtCursor from 'insert-text-at-cursor'; import * as Misskey from 'misskey-js'; +import type { ComponentProps as CP } from 'vue-component-type-helpers'; +import type { Form, GetFormResultType } from '@/scripts/form.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import MkPostFormDialog from '@/components/MkPostFormDialog.vue'; import MkWaitingDialog from '@/components/MkWaitingDialog.vue'; @@ -19,7 +19,6 @@ import MkToast from '@/components/MkToast.vue'; import MkDialog from '@/components/MkDialog.vue'; import MkPasswordDialog from '@/components/MkPasswordDialog.vue'; import MkEmojiPickerDialog from '@/components/MkEmojiPickerDialog.vue'; -import MkEmojiPickerWindow from '@/components/MkEmojiPickerWindow.vue'; import MkPopupMenu from '@/components/MkPopupMenu.vue'; import MkContextMenu from '@/components/MkContextMenu.vue'; import { MenuItem } from '@/types/menu.js'; @@ -28,15 +27,15 @@ import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; export const openingWindowsCount = ref(0); -export const apiWithDialog = (( - endpoint: string, - data: Record<string, any> = {}, +export const apiWithDialog = (<E extends keyof Misskey.Endpoints = keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req'] = Misskey.Endpoints[E]['req']>( + endpoint: E, + data: P = {} as any, token?: string | null | undefined, ) => { - const promise = api(endpoint, data, token); + const promise = misskeyApi(endpoint, data, token); promiseDialog(promise, null, async (err) => { - let title = null; - let text = err.message + '\n' + (err as any).id; + let title: string | undefined; + let text = err.message + '\n' + err.id; if (err.code === 'INTERNAL_ERROR') { title = i18n.ts.internalServerError; text = i18n.ts.internalServerErrorDescription; @@ -83,12 +82,12 @@ export const apiWithDialog = (( }); return promise; -}) as typeof api; +}) as typeof misskeyApi; export function promiseDialog<T extends Promise<any>>( promise: T, onSuccess?: ((res: any) => void) | null, - onFailure?: ((err: Error) => void) | null, + onFailure?: ((err: Misskey.api.APIError) => void) | null, text?: string, ): T { const showing = ref(true); @@ -109,10 +108,17 @@ export function promiseDialog<T extends Promise<any>>( if (onFailure) { onFailure(err); } else { - alert({ - type: 'error', - text: err, - }); + if (err.message) { + alert({ + type: 'error', + text: err.message, + }); + } else { + alert({ + type: 'error', + text: err, + }); + } } }); @@ -128,9 +134,10 @@ export function promiseDialog<T extends Promise<any>>( let popupIdCount = 0; export const popups = ref([]) as Ref<{ - id: any; - component: any; + id: number; + component: Component; props: Record<string, any>; + events: Record<string, any>; }[]>; const zIndexes = { @@ -144,7 +151,34 @@ export function claimZIndex(priority: keyof typeof zIndexes = 'low'): number { return zIndexes[priority]; } -export async function popup(component: Component, props: Record<string, any>, events = {}, disposeEvent?: string) { +// InstanceType<typeof Component>['$emit'] ã ã¨ã‚¤ãƒ³ã‚¿ãƒ¼ã‚»ã‚¯ã‚·ãƒ§ãƒ³åž‹ãŒè¿”ã£ã¦ã㦠+// 使ã„物ã«ãªã‚‰ãªã„ã®ã§ã€ä»£ã‚ã‚Šã« ['$props'] ã‹ã‚‰è‰²ã€…çœãã“ã¨ã§ emit ã®åž‹ã‚’生æˆã™ã‚‹ +// FIXME: 何故㋠*.ts ファイルã‹ã‚‰ã ã¨åž‹ãŒã†ã¾ãå–ã‚Œãªã„?ã“ã¨ãŒã‚ã‚‹ã®ã‚’ãªã‚“ã¨ã‹ã—ãŸã„ +type ComponentEmit<T> = T extends new () => { $props: infer Props } + ? [keyof Pick<T, Extract<keyof T, `on${string}`>>] extends [never] + ? Record<string, unknown> // *.ts ファイルã‹ã‚‰åž‹ãŒã†ã¾ãå–ã‚Œãªã„ã¨ã用(ã“ã‚ŒãŒãªã„㨠{} ã«ãªã£ã¦åž‹ã‚¨ãƒ©ãƒ¼ãŒã†ã‚‹ã•ã„) + : EmitsExtractor<Props> + : T extends (...args: any) => any + ? ReturnType<T> extends { [x: string]: any; __ctx?: { [x: string]: any; props: infer Props } } + ? [keyof Pick<T, Extract<keyof T, `on${string}`>>] extends [never] + ? Record<string, unknown> + : EmitsExtractor<Props> + : never + : never; + +// props ã« ref を許å¯ã™ã‚‹ã‚ˆã†ã«ã™ã‚‹ +type ComponentProps<T extends Component> = { [K in keyof CP<T>]: CP<T>[K] | Ref<CP<T>[K]> }; + +type EmitsExtractor<T> = { + [K in keyof T as K extends `onVnode${string}` ? never : K extends `on${infer E}` ? Uncapitalize<E> : K extends string ? never : K]: T[K]; +}; + +export async function popup<T extends Component>( + component: T, + props: ComponentProps<T>, + events: ComponentEmit<T> = {} as ComponentEmit<T>, + disposeEvent?: keyof ComponentEmit<T>, +): Promise<{ dispose: () => void }> { markRaw(component); const id = ++popupIdCount; @@ -185,12 +219,12 @@ export function toast(message: string) { export function alert(props: { type?: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question'; - title?: string | null; - text?: string | null; + title?: string; + text?: string; }): Promise<void> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(MkDialog, props, { - done: result => { + done: () => { resolve(); }, }, 'closed'); @@ -199,12 +233,12 @@ export function alert(props: { export function confirm(props: { type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question'; - title?: string | null; - text?: string | null; + title?: string; + text?: string; okText?: string; cancelText?: string; }): Promise<{ canceled: boolean }> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(MkDialog, { ...props, showCancelButton: true, @@ -225,13 +259,15 @@ export function actions<T extends { danger?: boolean, }[]>(props: { type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question'; - title?: string | null; - text?: string | null; + title?: string; + text?: string; actions: T; -}): Promise<{ canceled: true; result: undefined; } | { +}): Promise<{ + canceled: true; result: undefined; +} | { canceled: false; result: T[number]['value']; }> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(MkDialog, { ...props, actions: props.actions.map(a => ({ @@ -250,19 +286,50 @@ export function actions<T extends { }); } +// default ãŒæŒ‡å®šã•ã‚Œã¦ã„ãŸã‚‰ result 㯠null ã«ãªã‚Šå¾—ãªã„ã“ã¨ã‚’ä¿è¨¼ã™ã‚‹ overload function export function inputText(props: { type?: 'text' | 'email' | 'password' | 'url'; - title?: string | null; - text?: string | null; + title?: string; + text?: string; placeholder?: string | null; autocomplete?: string; - default?: string | null; + default: string; minLength?: number; maxLength?: number; -}): Promise<{ canceled: true; result: undefined; } | { +}): Promise<{ + canceled: true; result: undefined; +} | { canceled: false; result: string; +}>; +export function inputText(props: { + type?: 'text' | 'email' | 'password' | 'url'; + title?: string; + text?: string; + placeholder?: string | null; + autocomplete?: string; + default?: string | null; + minLength?: number; + maxLength?: number; +}): Promise<{ + canceled: true; result: undefined; +} | { + canceled: false; result: string | null; +}>; +export function inputText(props: { + type?: 'text' | 'email' | 'password' | 'url'; + title?: string; + text?: string; + placeholder?: string | null; + autocomplete?: string; + default?: string | null; + minLength?: number; + maxLength?: number; +}): Promise<{ + canceled: true; result: undefined; +} | { + canceled: false; result: string | null; }> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(MkDialog, { title: props.title, text: props.text, @@ -270,7 +337,7 @@ export function inputText(props: { type: props.type, placeholder: props.placeholder, autocomplete: props.autocomplete, - default: props.default, + default: props.default ?? null, minLength: props.minLength, maxLength: props.maxLength, }, @@ -282,16 +349,41 @@ export function inputText(props: { }); } +// default ãŒæŒ‡å®šã•ã‚Œã¦ã„ãŸã‚‰ result 㯠null ã«ãªã‚Šå¾—ãªã„ã“ã¨ã‚’ä¿è¨¼ã™ã‚‹ overload function export function inputNumber(props: { - title?: string | null; - text?: string | null; + title?: string; + text?: string; placeholder?: string | null; autocomplete?: string; - default?: number | null; -}): Promise<{ canceled: true; result: undefined; } | { + default: number; +}): Promise<{ + canceled: true; result: undefined; +} | { canceled: false; result: number; +}>; +export function inputNumber(props: { + title?: string; + text?: string; + placeholder?: string | null; + autocomplete?: string; + default?: number | null; +}): Promise<{ + canceled: true; result: undefined; +} | { + canceled: false; result: number | null; +}>; +export function inputNumber(props: { + title?: string; + text?: string; + placeholder?: string | null; + autocomplete?: string; + default?: number | null; +}): Promise<{ + canceled: true; result: undefined; +} | { + canceled: false; result: number | null; }> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(MkDialog, { title: props.title, text: props.text, @@ -299,7 +391,7 @@ export function inputNumber(props: { type: 'number', placeholder: props.placeholder, autocomplete: props.autocomplete, - default: props.default, + default: props.default ?? null, }, }, { done: result => { @@ -310,34 +402,38 @@ export function inputNumber(props: { } export function inputDate(props: { - title?: string | null; - text?: string | null; + title?: string; + text?: string; placeholder?: string | null; - default?: Date | null; -}): Promise<{ canceled: true; result: undefined; } | { + default?: string | null; +}): Promise<{ + canceled: true; result: undefined; +} | { canceled: false; result: Date; }> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(MkDialog, { title: props.title, text: props.text, input: { type: 'date', placeholder: props.placeholder, - default: props.default, + default: props.default ?? null, }, }, { done: result => { - resolve(result ? { result: new Date(result.result), canceled: false } : { canceled: true }); + resolve(result ? { result: new Date(result.result), canceled: false } : { result: undefined, canceled: true }); }, }, 'closed'); }); } -export function authenticateDialog(): Promise<{ canceled: true; result: undefined; } | { +export function authenticateDialog(): Promise<{ + canceled: true; result: undefined; +} | { canceled: false; result: { password: string; token: string | null; }; }> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(MkPasswordDialog, {}, { done: result => { resolve(result ? { canceled: false, result } : { canceled: true, result: undefined }); @@ -346,34 +442,53 @@ export function authenticateDialog(): Promise<{ canceled: true; result: undefine }); } +// default ãŒæŒ‡å®šã•ã‚Œã¦ã„ãŸã‚‰ result 㯠null ã«ãªã‚Šå¾—ãªã„ã“ã¨ã‚’ä¿è¨¼ã™ã‚‹ overload function export function select<C = any>(props: { - title?: string | null; - text?: string | null; + title?: string; + text?: string; + default: string; + items: { + value: C; + text: string; + }[]; +}): Promise<{ + canceled: true; result: undefined; +} | { + canceled: false; result: C; +}>; +export function select<C = any>(props: { + title?: string; + text?: string; default?: string | null; -} & ({ items: { value: C; text: string; }[]; +}): Promise<{ + canceled: true; result: undefined; } | { - groupedItems: { - label: string; - items: { - value: C; - text: string; - }[]; + canceled: false; result: C | null; +}>; +export function select<C = any>(props: { + title?: string; + text?: string; + default?: string | null; + items: { + value: C; + text: string; }[]; -})): Promise<{ canceled: true; result: undefined; } | { - canceled: false; result: C; +}): Promise<{ + canceled: true; result: undefined; +} | { + canceled: false; result: C | null; }> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(MkDialog, { title: props.title, text: props.text, select: { items: props.items, - groupedItems: props.groupedItems, - default: props.default, + default: props.default ?? null, }, }, { done: result => { @@ -384,7 +499,7 @@ export function select<C = any>(props: { } export function success(): Promise<void> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { const showing = ref(true); window.setTimeout(() => { showing.value = false; @@ -399,7 +514,7 @@ export function success(): Promise<void> { } export function waiting(): Promise<void> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { const showing = ref(true); popup(MkWaitingDialog, { success: false, @@ -410,9 +525,9 @@ export function waiting(): Promise<void> { }); } -export function form(title, form) { - return new Promise((resolve, reject) => { - popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form }, { +export function form<F extends Form>(title: string, f: F): Promise<{ canceled: true } | { result: GetFormResultType<F> }> { + return new Promise(resolve => { + popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form: f }, { done: result => { resolve(result); }, @@ -420,10 +535,11 @@ export function form(title, form) { }); } -export async function selectUser(opts: { includeSelf?: boolean } = {}) { - return new Promise((resolve, reject) => { +export async function selectUser(opts: { includeSelf?: boolean; localOnly?: boolean; } = {}): Promise<Misskey.entities.UserDetailed> { + return new Promise(resolve => { popup(defineAsyncComponent(() => import('@/components/MkUserSelectDialog.vue')), { includeSelf: opts.includeSelf, + localOnly: opts.localOnly, }, { ok: user => { resolve(user); @@ -433,7 +549,7 @@ export async function selectUser(opts: { includeSelf?: boolean } = {}) { } export async function selectDriveFile(multiple: boolean): Promise<Misskey.entities.DriveFile[]> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), { type: 'file', multiple, @@ -447,23 +563,23 @@ export async function selectDriveFile(multiple: boolean): Promise<Misskey.entiti }); } -export async function selectDriveFolder(multiple: boolean) { - return new Promise((resolve, reject) => { +export async function selectDriveFolder(multiple: boolean): Promise<Misskey.entities.DriveFolder[]> { + return new Promise(resolve => { popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), { type: 'folder', multiple, }, { done: folders => { if (folders) { - resolve(multiple ? folders : folders[0]); + resolve(folders); } }, }, 'closed'); }); } -export async function pickEmoji(src: HTMLElement | null, opts) { - return new Promise((resolve, reject) => { +export async function pickEmoji(src: HTMLElement, opts: ComponentProps<typeof MkEmojiPickerDialog>): Promise<string> { + return new Promise(resolve => { popup(MkEmojiPickerDialog, { src, ...opts, @@ -479,7 +595,7 @@ export async function cropImage(image: Misskey.entities.DriveFile, options: { aspectRatio: number; uploadFolder?: string | null; }): Promise<Misskey.entities.DriveFile> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { popup(defineAsyncComponent(() => import('@/components/MkCropperDialog.vue')), { file: image, aspectRatio: options.aspectRatio, @@ -492,67 +608,13 @@ export async function cropImage(image: Misskey.entities.DriveFile, options: { }); } -type AwaitType<T> = - T extends Promise<infer U> ? U : - T extends (...args: any[]) => Promise<infer V> ? V : - T; -let openingEmojiPicker: AwaitType<ReturnType<typeof popup>> | null = null; -let activeTextarea: HTMLTextAreaElement | HTMLInputElement | null = null; -export async function openEmojiPicker(src?: HTMLElement, opts, initialTextarea: typeof activeTextarea) { - if (openingEmojiPicker) return; - - activeTextarea = initialTextarea; - - const textareas = document.querySelectorAll('textarea, input'); - for (const textarea of Array.from(textareas)) { - textarea.addEventListener('focus', () => { - activeTextarea = textarea; - }); - } - - const observer = new MutationObserver(records => { - for (const record of records) { - for (const node of Array.from(record.addedNodes).filter(node => node instanceof HTMLElement) as HTMLElement[]) { - const textareas = node.querySelectorAll('textarea, input') as NodeListOf<NonNullable<typeof activeTextarea>>; - for (const textarea of Array.from(textareas).filter(textarea => textarea.dataset.preventEmojiInsert == null)) { - if (document.activeElement === textarea) activeTextarea = textarea; - textarea.addEventListener('focus', () => { - activeTextarea = textarea; - }); - } - } - } - }); - - observer.observe(document.body, { - childList: true, - subtree: true, - attributes: false, - characterData: false, - }); - - openingEmojiPicker = await popup(MkEmojiPickerWindow, { - src, - ...opts, - }, { - chosen: emoji => { - insertTextAtCursor(activeTextarea, emoji); - }, - closed: () => { - openingEmojiPicker!.dispose(); - openingEmojiPicker = null; - observer.disconnect(); - }, - }); -} - -export function popupMenu(items: MenuItem[] | Ref<MenuItem[]>, src?: HTMLElement | EventTarget | null, options?: { +export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | null, options?: { align?: string; width?: number; viaKeyboard?: boolean; onClosing?: () => void; }): Promise<void> { - return new Promise((resolve, reject) => { + return new Promise(resolve => { let dispose; popup(MkPopupMenu, { items, @@ -574,9 +636,9 @@ export function popupMenu(items: MenuItem[] | Ref<MenuItem[]>, src?: HTMLElement }); } -export function contextMenu(items: MenuItem[] | Ref<MenuItem[]>, ev: MouseEvent): Promise<void> { +export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> { ev.preventDefault(); - return new Promise((resolve, reject) => { + return new Promise(resolve => { let dispose; popup(MkContextMenu, { items, @@ -595,7 +657,7 @@ export function contextMenu(items: MenuItem[] | Ref<MenuItem[]>, ev: MouseEvent) export function post(props: Record<string, any> = {}): Promise<void> { showMovedDialog(); - return new Promise((resolve, reject) => { + return new Promise(resolve => { // NOTE: MkPostFormDialogã‚’dynamic importã™ã‚‹ã¨iOSã§ãƒ†ã‚ストエリアã«è‡ªå‹•ãƒ•ã‚©ãƒ¼ã‚«ã‚¹ã§ããªã„ // NOTE: ãŸã ã€dynamic importã—ãªã„å ´åˆã€MkPostFormDialogインスタンスãŒä½¿ã„ã¾ã‚ã•ã‚Œã€ // VueãŒæ¸¡ã•ã‚ŒãŸã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆã«å†…部的ã«__propsã¨ã„ã†ãƒ—ãƒãƒ‘ティを生やã™å½±éŸ¿ã§ã€ @@ -621,7 +683,7 @@ export function checkExistence(fileData: ArrayBuffer): Promise<any> { const data = new FormData(); data.append('md5', getMD5(fileData)); - os.api('drive/files/find-by-hash', { + api('drive/files/find-by-hash', { md5: getMD5(fileData) }).then(resp => { resolve(resp.length > 0 ? resp[0] : null); diff --git a/packages/frontend/src/pages/_empty_.vue b/packages/frontend/src/pages/_empty_.vue index 9403d862c28a06aa385b4813076a50522249ea18..236d3fa14d5f51afd8a95ddd8ae5e246b4ea35e9 100644 --- a/packages/frontend/src/pages/_empty_.vue +++ b/packages/frontend/src/pages/_empty_.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/_error_.vue b/packages/frontend/src/pages/_error_.vue index 2cdf8f2e8c03fbefb57f768b6dcf6336d1623bda..6b1eff5bcb61f439e64df1f3c4c2e5c49c4199f8 100644 --- a/packages/frontend/src/pages/_error_.vue +++ b/packages/frontend/src/pages/_error_.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div>{{ i18n.ts.youShouldUpgradeClient }}</div> <MkButton style="margin: 8px auto;" @click="reload">{{ i18n.ts.reload }}</MkButton> </template> - <div><MkA to="/docs/general/troubleshooting" class="_link">{{ i18n.ts.troubleshooting }}</MkA></div> + <div><MkLink url="https://misskey-hub.net/docs/for-users/resources/troubleshooting/" target="_blank">{{ i18n.ts.troubleshooting }}</MkLink></div> <div v-if="error" style="opacity: 0.7;">ERROR: {{ error }}</div> </div> </div> @@ -28,8 +28,9 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref, computed } from 'vue'; import * as Misskey from 'misskey-js'; import MkButton from '@/components/MkButton.vue'; +import MkLink from '@/components/MkLink.vue'; import { version } from '@/config.js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { unisonReload } from '@/scripts/unison-reload.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -46,7 +47,7 @@ const loaded = ref(false); const serverIsDead = ref(false); const meta = ref<Misskey.entities.MetaResponse | null>(null); -os.api('meta', { +misskeyApi('meta', { detail: false, }).then(res => { loaded.value = true; @@ -66,10 +67,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.error, icon: 'ph-warning ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/_loading_.vue b/packages/frontend/src/pages/_loading_.vue index 9f3c9fd3552919bef9895cb52d8430cbc58b3a6c..5175979642c4990540ab3ae42ae7a01113e548c8 100644 --- a/packages/frontend/src/pages/_loading_.vue +++ b/packages/frontend/src/pages/_loading_.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/about-sharkey.vue b/packages/frontend/src/pages/about-sharkey.vue index 2e4ff5d041e1e8291e242cb4fdf0a18253ac9099..30788e24ceb9516dbc9016263150347343755448 100644 --- a/packages/frontend/src/pages/about-sharkey.vue +++ b/packages/frontend/src/pages/about-sharkey.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="misskey">Sharkey</div> <div class="version">v{{ version }}</div> <span v-for="emoji in easterEggEmojis" :key="emoji.id" class="emoji" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"> - <MkCustomEmoji v-if="emoji.emoji[0] === ':'" class="emoji" :name="emoji.emoji" :normal="true" :noStyle="true"/> + <MkCustomEmoji v-if="emoji.emoji[0] === ':'" class="emoji" :name="emoji.emoji" :normal="true" :noStyle="true" :fallbackToImage="true"/> <MkEmoji v-else class="emoji" :emoji="emoji.emoji" :normal="true" :noStyle="true"/> </span> </div> @@ -27,33 +27,66 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="$i != null" style="text-align: center;"> <MkButton primary rounded inline @click="iLoveMisskey">I <Mfm text="$[jelly â¤]"/> #Sharkey</MkButton> </div> - <FormSection> + <FormSection v-if="instance.repositoryUrl !== 'https://activitypub.software/TransFem-org/Sharkey/'"> <div class="_gaps_s"> - <FormLink to="https://git.joinsharkey.org/Sharkey/Sharkey" external> + <MkInfo> + {{ i18n.tsx._aboutMisskey.thisIsModifiedVersion({ name: instance.name }) }} + </MkInfo> + <FormLink v-if="instance.repositoryUrl" :to="instance.repositoryUrl" external> <template #icon><i class="ph-code ph-bold ph-lg"></i></template> {{ i18n.ts._aboutMisskey.source }} - <template #suffix>Forgejo</template> + </FormLink> + <FormLink v-if="instance.providesTarball" :to="`/tarball/sharkey-${version}.tar.gz`" external> + <template #icon><i class="ph-download ph-bold ph-lg"></i></template> + {{ i18n.ts._aboutMisskey.source }} + <template #suffix>Tarball</template> + </FormLink> + <MkInfo v-if="!instance.repositoryUrl && !instance.providesTarball" warn> + {{ i18n.ts.sourceCodeIsNotYetProvided }} + </MkInfo> + </div> + </FormSection> + <FormSection> + <div class="_gaps_s"> + <FormLink to="https://activitypub.software/TransFem-org/Sharkey/" external> + <template #icon><i class="ph-code ph-bold ph-lg"></i></template> + {{ i18n.ts._aboutMisskey.source }} ({{ i18n.ts._aboutMisskey.original_sharkey }}) + <template #suffix>GitLab</template> </FormLink> <FormLink to="https://ko-fi.com/transfem" external> <template #icon><i class="ph-piggy-bank ph-bold ph-lg"></i></template> - {{ i18n.ts._aboutMisskey.donate }} + {{ i18n.ts._aboutMisskey.donate_sharkey }} <template #suffix>Ko-Fi</template> </FormLink> </div> </FormSection> + <FormSection> + <div class="_gaps_s"> + <FormLink to="https://github.com/misskey-dev/misskey" external> + <template #icon><i class="ph-code ph-bold ph-lg"></i></template> + {{ i18n.ts._aboutMisskey.source }} ({{ i18n.ts._aboutMisskey.original }}) + <template #suffix>GitHub</template> + </FormLink> + <FormLink to="https://www.patreon.com/syuilo" external> + <template #icon><i class="ph-piggy-bank ph-bold ph-lg"></i></template> + {{ i18n.ts._aboutMisskey.donate }} + <template #suffix>Patreon</template> + </FormLink> + </div> + </FormSection> <FormSection> <template #label>{{ i18n.ts._aboutMisskey.projectMembers }}</template> <div :class="$style.contributors" style="margin-bottom: 8px;"> - <a href="https://git.joinsharkey.org/Marie" target="_blank" :class="$style.contributor"> - <img src="https://git.joinsharkey.org/avatar/0d57abf583f5ed6cf37f47055a1e1aa4?size=512" :class="$style.contributorAvatar"> + <a href="https://activitypub.software/Marie" target="_blank" :class="$style.contributor"> + <img src="https://activitypub.software/uploads/-/system/user/avatar/2/avatar.png?width=128" :class="$style.contributorAvatar"> <span :class="$style.contributorUsername">@Marie</span> </a> - <a href="https://git.joinsharkey.org/Amelia" target="_blank" :class="$style.contributor"> - <img src="https://git.joinsharkey.org/avatars/0634b661b89d6e45137074b6ddcd0b9ffc4cf467f2188ec12416ec6f91bb9d42?size=512" :class="$style.contributorAvatar"> + <a href="https://activitypub.software/Amelia" target="_blank" :class="$style.contributor"> + <img src="https://activitypub.software/uploads/-/system/user/avatar/1/avatar.png?width=128" :class="$style.contributorAvatar"> <span :class="$style.contributorUsername">@Amelia</span> </a> </div> - <template #caption><MkLink url="https://git.joinsharkey.org/Sharkey/Sharkey/graph">{{ i18n.ts._aboutMisskey.allContributors }}</MkLink></template> + <template #caption><MkLink url="https://activitypub.software/TransFem-org/Sharkey/-/graphs/develop">{{ i18n.ts._aboutMisskey.allContributors }}</MkLink></template> </FormSection> <FormSection> <template #label>Misskey Contributors</template> @@ -88,7 +121,7 @@ SPDX-License-Identifier: AGPL-3.0-only </a> </div> </FormSection> - <FormSection> + <FormSection v-if="sponsors[0].length > 0"> <template #label>Our lovely Sponsors</template> <div :class="$style.contributors"> <span @@ -116,10 +149,13 @@ import FormLink from '@/components/form/link.vue'; import FormSection from '@/components/form/section.vue'; import MkButton from '@/components/MkButton.vue'; import MkLink from '@/components/MkLink.vue'; +import MkInfo from '@/components/MkInfo.vue'; import { physics } from '@/scripts/physics.js'; import { i18n } from '@/i18n.js'; +import { instance } from '@/instance.js'; import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { claimAchievement, claimedAchievements } from '@/scripts/achievements.js'; import { $i } from '@/account.js'; @@ -132,7 +168,7 @@ const easterEggEngine = ref(null); const sponsors = ref([]); const containerEl = shallowRef<HTMLElement>(); -await os.api('sponsors', { forceUpdate: true }).then((res) => sponsors.value.push(res.sponsor_data)); +await misskeyApi('sponsors', { forceUpdate: true }).then((res) => sponsors.value.push(res.sponsor_data)); function iconLoaded() { const emojis = defaultStore.state.reactions; @@ -179,10 +215,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.aboutMisskey, icon: null, -}); +})); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index eda6455fd6e5869a7bb89ec5e573f5883da62a1f..f37e9dbf9670d2b95aa928e3c45ab30c68b08366 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/about.federation.vue b/packages/frontend/src/pages/about.federation.vue index 0de000ee3efbff978bb8d0de94f7dafd3cf2fe5e..c7f2315faa6a5636da20c1f922aba07f21654873 100644 --- a/packages/frontend/src/pages/about.federation.vue +++ b/packages/frontend/src/pages/about.federation.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -17,10 +17,11 @@ SPDX-License-Identifier: AGPL-3.0-only <option value="federating">{{ i18n.ts.federating }}</option> <option value="subscribing">{{ i18n.ts.subscribing }}</option> <option value="publishing">{{ i18n.ts.publishing }}</option> + <option value="bubble">Bubble</option> <option value="nsfw">NSFW</option> - <option value="suspended">{{ i18n.ts.suspended }}</option> - <option value="silenced">{{ i18n.ts.silence }}</option> - <option value="blocked">{{ i18n.ts.blocked }}</option> + <option v-if="$i" value="suspended">{{ i18n.ts.suspended }}</option> + <option v-if="$i" value="silenced">{{ i18n.ts.silence }}</option> + <option v-if="$i" value="blocked">{{ i18n.ts.blocked }}</option> <option value="notResponding">{{ i18n.ts.notResponding }}</option> </MkSelect> <MkSelect v-model="sort"> @@ -59,6 +60,7 @@ import MkPagination, { Paging } from '@/components/MkPagination.vue'; import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue'; import FormSplit from '@/components/form/split.vue'; import { i18n } from '@/i18n.js'; +import { $i } from '@/account.js'; const host = ref(''); const state = ref('federating'); @@ -80,6 +82,7 @@ const pagination = { state.value === 'silenced' ? { silenced: true } : state.value === 'notResponding' ? { notResponding: true } : state.value === 'nsfw' ? { nsfw: true } : + state.value === 'bubble' ? { bubble: true } : {}), })), } as Paging; diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index b532314745bc1b4acab63a2d17594451cd458baf..f2aceada7dffd059ad18bedcfed180cd14841249 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -1,107 +1,136 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> - <MkSpacer v-if="tab === 'overview'" :contentMax="600" :marginMin="20"> - <div class="_gaps_m"> - <div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }"> - <div style="overflow: clip;"> - <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.bannerIcon"/> - <div :class="$style.bannerName"> - <b>{{ instance.name ?? host }}</b> + <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSpacer v-if="tab === 'overview'" :contentMax="600" :marginMin="20"> + <div class="_gaps_m"> + <div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }"> + <div style="overflow: clip;"> + <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.bannerIcon"/> + <div :class="$style.bannerName"> + <b>{{ instance.name ?? host }}</b> + </div> </div> </div> - </div> - <MkKeyValue> - <template #key>{{ i18n.ts.description }}</template> - <template #value><div v-html="instance.description"></div></template> - </MkKeyValue> - - <FormSection> - <div class="_gaps_m"> - <MkKeyValue :copy="version"> - <template #key>Sharkey</template> - <template #value>{{ version }}</template> - </MkKeyValue> - <div v-html="i18n.t('poweredByMisskeyDescription', { name: instance.name ?? host })"> - </div> - <FormLink to="/about-sharkey">{{ i18n.ts.aboutMisskey }}</FormLink> - </div> - </FormSection> - - <FormSection> - <div class="_gaps_m"> - <FormSplit> - <MkKeyValue> - <template #key>{{ i18n.ts.administrator }}</template> - <template #value>{{ instance.maintainerName }}</template> - </MkKeyValue> - <MkKeyValue> - <template #key>{{ i18n.ts.contact }}</template> - <template #value>{{ instance.maintainerEmail }}</template> + <MkKeyValue> + <template #key>{{ i18n.ts.description }}</template> + <template #value><div v-html="sanitizeHtml(instance.description)"></div></template> + </MkKeyValue> + + <FormSection> + <div class="_gaps_m"> + <MkKeyValue :copy="version"> + <template #key>Sharkey</template> + <template #value>{{ version }}</template> </MkKeyValue> - </FormSplit> - <FormLink v-if="instance.impressumUrl" :to="instance.impressumUrl" external>{{ i18n.ts.impressum }}</FormLink> - <div class="_gaps_s"> - <MkFolder v-if="instance.serverRules.length > 0"> - <template #label>{{ i18n.ts.serverRules }}</template> - - <ol class="_gaps_s" :class="$style.rules"> - <li v-for="item, index in instance.serverRules" :key="index" :class="$style.rule"><div :class="$style.ruleText" v-html="item"></div></li> - </ol> - </MkFolder> - <FormLink v-if="instance.tosUrl" :to="instance.tosUrl" external>{{ i18n.ts.termsOfService }}</FormLink> - <FormLink v-if="instance.privacyPolicyUrl" :to="instance.privacyPolicyUrl" external>{{ i18n.ts.privacyPolicy }}</FormLink> + <div v-html="i18n.tsx.poweredByMisskeyDescription({ name: instance.name ?? host })"> + </div> + <FormLink to="/about-sharkey"> + <template #icon><i class="ph-info ph-bold ph-lg"></i></template> + {{ i18n.ts.aboutMisskey }} + </FormLink> + <FormLink v-if="instance.repositoryUrl || instance.providesTarball" :to="instance.repositoryUrl || `/tarball/sharkey-${version}.tar.gz`" external> + <template #icon><i class="ph-code ph-bold ph-lg"></i></template> + {{ i18n.ts.sourceCode }} + </FormLink> + <MkInfo v-else warn> + {{ i18n.ts.sourceCodeIsNotYetProvided }} + </MkInfo> </div> - </div> - </FormSection> + </FormSection> - <FormSuspense :p="initStats"> <FormSection> - <template #label>{{ i18n.ts.statistics }}</template> - <FormSplit> - <MkKeyValue> - <template #key>{{ i18n.ts.users }}</template> - <template #value>{{ number(stats.originalUsersCount) }}</template> - </MkKeyValue> - <MkKeyValue> - <template #key>{{ i18n.ts.notes }}</template> - <template #value>{{ number(stats.originalNotesCount) }}</template> - </MkKeyValue> - </FormSplit> + <div class="_gaps_m"> + <FormSplit> + <MkKeyValue> + <template #key>{{ i18n.ts.administrator }}</template> + <template #value>{{ instance.maintainerName }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>{{ i18n.ts.contact }}</template> + <template #value>{{ instance.maintainerEmail }}</template> + </MkKeyValue> + </FormSplit> + <FormLink v-if="instance.impressumUrl" :to="instance.impressumUrl" external> + <template #icon><i class="ph-newspaper-clipping ph-bold ph-lg"></i></template> + {{ i18n.ts.impressum }} + </FormLink> + <div class="_gaps_s"> + <MkFolder v-if="instance.serverRules.length > 0"> + <template #label> + <i class="ph-list-checks ph-bold ph-lg"></i> + {{ i18n.ts.serverRules }} + </template> + + <ol class="_gaps_s" :class="$style.rules"> + <li v-for="(item, index) in instance.serverRules" :key="index" :class="$style.rule"><div :class="$style.ruleText" v-html="sanitizeHtml(item)"></div></li> + </ol> + </MkFolder> + <FormLink v-if="instance.tosUrl" :to="instance.tosUrl" external> + <template #icon><i class="ph-notebook ph-bold ph-lg"></i></template> + {{ i18n.ts.termsOfService }} + </FormLink> + <FormLink v-if="instance.privacyPolicyUrl" :to="instance.privacyPolicyUrl" external> + <template #icon><i class="ph-shield ph-bold ph-lg"></i></template> + {{ i18n.ts.privacyPolicy }} + </FormLink> + <FormLink v-if="instance.feedbackUrl" :to="instance.feedbackUrl" external> + <template #icon><i class="ph-envelope ph-bold ph-lg"></i></template> + {{ i18n.ts.feedback }} + </FormLink> + </div> + </div> </FormSection> - </FormSuspense> - - <FormSection> - <template #label>Well-known resources</template> - <div class="_gaps_s"> - <FormLink :to="`/.well-known/host-meta`" external>host-meta</FormLink> - <FormLink :to="`/.well-known/host-meta.json`" external>host-meta.json</FormLink> - <FormLink :to="`/.well-known/nodeinfo`" external>nodeinfo</FormLink> - <FormLink :to="`/robots.txt`" external>robots.txt</FormLink> - <FormLink :to="`/manifest.json`" external>manifest.json</FormLink> - </div> - </FormSection> - </div> - </MkSpacer> - <MkSpacer v-else-if="tab === 'emojis'" :contentMax="1000" :marginMin="20"> - <XEmojis/> - </MkSpacer> - <MkSpacer v-else-if="tab === 'federation'" :contentMax="1000" :marginMin="20"> - <XFederation/> - </MkSpacer> - <MkSpacer v-else-if="tab === 'charts'" :contentMax="1000" :marginMin="20"> - <MkInstanceStats/> - </MkSpacer> + + <FormSuspense :p="initStats"> + <FormSection> + <template #label>{{ i18n.ts.statistics }}</template> + <FormSplit> + <MkKeyValue> + <template #key>{{ i18n.ts.users }}</template> + <template #value>{{ number(stats.originalUsersCount) }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>{{ i18n.ts.notes }}</template> + <template #value>{{ number(stats.originalNotesCount) }}</template> + </MkKeyValue> + </FormSplit> + </FormSection> + </FormSuspense> + + <FormSection> + <template #label>Well-known resources</template> + <div class="_gaps_s"> + <FormLink :to="`/.well-known/host-meta`" external>host-meta</FormLink> + <FormLink :to="`/.well-known/host-meta.json`" external>host-meta.json</FormLink> + <FormLink :to="`/.well-known/nodeinfo`" external>nodeinfo</FormLink> + <FormLink :to="`/robots.txt`" external>robots.txt</FormLink> + <FormLink :to="`/manifest.json`" external>manifest.json</FormLink> + </div> + </FormSection> + </div> + </MkSpacer> + <MkSpacer v-else-if="tab === 'emojis'" :contentMax="1000" :marginMin="20"> + <XEmojis/> + </MkSpacer> + <MkSpacer v-else-if="tab === 'federation'" :contentMax="1000" :marginMin="20"> + <XFederation/> + </MkSpacer> + <MkSpacer v-else-if="tab === 'charts'" :contentMax="1000" :marginMin="20"> + <MkInstanceStats/> + </MkSpacer> + </MkHorizontalSwipe> </MkStickyContainer> </template> <script lang="ts" setup> +import sanitizeHtml from 'sanitize-html'; import { computed, watch, ref } from 'vue'; import * as Misskey from 'misskey-js'; import XEmojis from './about.emojis.vue'; @@ -113,8 +142,10 @@ import FormSuspense from '@/components/form/suspense.vue'; import FormSplit from '@/components/form/split.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; +import MkInfo from '@/components/MkInfo.vue'; import MkInstanceStats from '@/components/MkInstanceStats.vue'; -import * as os from '@/os.js'; +import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import number from '@/filters/number.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -136,7 +167,7 @@ watch(tab, () => { } }); -const initStats = () => os.api('stats', { +const initStats = () => misskeyApi('stats', { }).then((res) => { stats.value = res; }); @@ -160,10 +191,10 @@ const headerTabs = computed(() => [{ icon: 'ph-chart-line ph-bold ph-lg', }]); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: i18n.ts.instanceInfo, icon: 'ph-info ph-bold ph-lg', -}))); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/achievements.vue b/packages/frontend/src/pages/achievements.vue index f735da7e6706264728a3e560a7f2c3d8acb71f13..4e496c3c6cf42acbaf7ac5a922d3d737d5a08a6e 100644 --- a/packages/frontend/src/pages/achievements.vue +++ b/packages/frontend/src/pages/achievements.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -48,10 +48,10 @@ onDeactivated(() => { } }); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.achievements, icon: 'ph-trophy ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue index 845beebbafb0ab684993a47434452fe12494b4f8..b8f7e2c163df855478ed2652e0e6022446cbf7d2 100644 --- a/packages/frontend/src/pages/admin-file.vue +++ b/packages/frontend/src/pages/admin-file.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -79,6 +79,7 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkInfo from '@/components/MkInfo.vue'; import bytes from '@/filters/bytes.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { iAmAdmin, iAmModerator } from '@/account.js'; @@ -93,8 +94,8 @@ const props = defineProps<{ }>(); async function fetch() { - file.value = await os.api('drive/files/show', { fileId: props.fileId }); - info.value = await os.api('admin/drive/show-file', { fileId: props.fileId }); + file.value = await misskeyApi('drive/files/show', { fileId: props.fileId }); + info.value = await misskeyApi('admin/drive/show-file', { fileId: props.fileId }); isSensitive.value = file.value.isSensitive; } @@ -103,7 +104,7 @@ fetch(); async function del() { const { canceled } = await os.confirm({ type: 'warning', - text: i18n.t('removeAreYouSure', { x: file.value.name }), + text: i18n.tsx.removeAreYouSure({ x: file.value.name }), }); if (canceled) return; @@ -113,7 +114,7 @@ async function del() { } async function toggleIsSensitive(v) { - await os.api('drive/files/update', { fileId: props.fileId, isSensitive: v }); + await misskeyApi('drive/files/update', { fileId: props.fileId, isSensitive: v }); isSensitive.value = v; } @@ -139,10 +140,10 @@ const headerTabs = computed(() => [{ icon: 'ph-code ph-bold ph-lg', }]); -definePageMetadata(computed(() => ({ - title: file.value ? i18n.ts.file + ': ' + file.value.name : i18n.ts.file, +definePageMetadata(() => ({ + title: file.value ? `${i18n.ts.file}: ${file.value.name}` : i18n.ts.file, icon: 'ph-file ph-bold ph-lg', -}))); +})); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue index 741897b5f0580298d2e30bd5513a7fc6533a0b22..3beaf5d08bc0db1e1b6a370f09974cb4f53c0ab7 100644 --- a/packages/frontend/src/pages/admin-user.vue +++ b/packages/frontend/src/pages/admin-user.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -124,7 +124,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-for="role in info.roles" :key="role.id"> <div :class="$style.roleItemMain"> <MkRolePreview :class="$style.role" :role="role" :forModeration="true"/> - <button class="_button" :class="$style.roleToggle" @click="toggleRoleItem(role)"><i class="ph-caret-down ph-bold ph-lg"></i></button> + <button class="_button" @click="toggleRoleItem(role)"><i class="ph-caret-down ph-bold ph-lg"></i></button> <button v-if="role.target === 'manual'" class="_button" :class="$style.roleUnassign" @click="unassignRole(role, $event)"><i class="ph-x ph-bold ph-lg"></i></button> <button v-else class="_button" :class="$style.roleUnassign" disabled><i class="ph-prohibit ph-bold ph-lg"></i></button> </div> @@ -169,9 +169,9 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSelect> </div> <div class="charts"> - <div class="label">{{ i18n.t('recentNHours', { n: 90 }) }}</div> + <div class="label">{{ i18n.tsx.recentNHours({ n: 90 }) }}</div> <MkChart class="chart" :src="chartSrc" span="hour" :limit="90" :args="{ user, withoutAll: true }" :detailed="true"></MkChart> - <div class="label">{{ i18n.t('recentNDays', { n: 90 }) }}</div> + <div class="label">{{ i18n.tsx.recentNDays({ n: 90 }) }}</div> <MkChart class="chart" :src="chartSrc" span="day" :limit="90" :args="{ user, withoutAll: true }" :detailed="true"></MkChart> </div> </div> @@ -206,11 +206,12 @@ import FormSuspense from '@/components/form/suspense.vue'; import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue'; import MkInfo from '@/components/MkInfo.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { url } from '@/config.js'; import { acct } from '@/filters/user.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; -import { iAmAdmin, $i } from '@/account.js'; +import { iAmAdmin, $i, iAmModerator } from '@/account.js'; import MkRolePreview from '@/components/MkRolePreview.vue'; import MkPagination from '@/components/MkPagination.vue'; @@ -251,11 +252,11 @@ const announcementsPagination = { const expandedRoles = ref([]); function createFetcher() { - return () => Promise.all([os.api('users/show', { + return () => Promise.all([misskeyApi('users/show', { userId: props.userId, - }), os.api('admin/show-user', { + }), misskeyApi('admin/show-user', { userId: props.userId, - }), iAmAdmin ? os.api('admin/get-user-ips', { + }), iAmAdmin ? misskeyApi('admin/get-user-ips', { userId: props.userId, }) : Promise.resolve(null)]).then(([_user, _info, _ips]) => { user.value = _user; @@ -268,7 +269,7 @@ function createFetcher() { moderationNote.value = info.value.moderationNote; watch(moderationNote, async () => { - await os.api('admin/update-user-note', { userId: user.value.id, text: moderationNote.value }); + await misskeyApi('admin/update-user-note', { userId: user.value.id, text: moderationNote.value }); await refreshUser(); }); }); @@ -291,12 +292,12 @@ async function resetPassword() { if (confirm.canceled) { return; } else { - const { password } = await os.api('admin/reset-password', { + const { password } = await misskeyApi('admin/reset-password', { userId: user.value.id, }); os.alert({ type: 'success', - text: i18n.t('newPasswordIs', { password }), + text: i18n.tsx.newPasswordIs({ password }), }); } } @@ -309,7 +310,7 @@ async function toggleNSFW(v) { if (confirm.canceled) { markedAsNSFW.value = !v; } else { - await os.api(v ? 'admin/nsfw-user' : 'admin/unnsfw-user', { userId: user.value.id }); + await misskeyApi(v ? 'admin/nsfw-user' : 'admin/unnsfw-user', { userId: user.value.id }); await refreshUser(); } } @@ -322,7 +323,7 @@ async function toggleSilence(v) { if (confirm.canceled) { silenced.value = !v; } else { - await os.api(v ? 'admin/silence-user' : 'admin/unsilence-user', { userId: user.value.id }); + await misskeyApi(v ? 'admin/silence-user' : 'admin/unsilence-user', { userId: user.value.id }); await refreshUser(); } } @@ -335,7 +336,7 @@ async function toggleSuspend(v) { if (confirm.canceled) { suspended.value = !v; } else { - await os.api(v ? 'admin/suspend-user' : 'admin/unsuspend-user', { userId: user.value.id }); + await misskeyApi(v ? 'admin/suspend-user' : 'admin/unsuspend-user', { userId: user.value.id }); await refreshUser(); } } @@ -347,7 +348,7 @@ async function unsetUserAvatar() { }); if (confirm.canceled) return; const process = async () => { - await os.api('admin/unset-user-avatar', { userId: user.value.id }); + await misskeyApi('admin/unset-user-avatar', { userId: user.value.id }); os.success(); }; await process().catch(err => { @@ -366,7 +367,7 @@ async function unsetUserBanner() { }); if (confirm.canceled) return; const process = async () => { - await os.api('admin/unset-user-banner', { userId: user.value.id }); + await misskeyApi('admin/unset-user-banner', { userId: user.value.id }); os.success(); }; await process().catch(err => { @@ -385,7 +386,7 @@ async function deleteAllFiles() { }); if (confirm.canceled) return; const process = async () => { - await os.api('admin/delete-all-files-of-a-user', { userId: user.value.id }); + await misskeyApi('admin/delete-all-files-of-a-user', { userId: user.value.id }); os.success(); }; await process().catch(err => { @@ -405,7 +406,7 @@ async function deleteAccount() { if (confirm.canceled) return; const typed = await os.inputText({ - text: i18n.t('typeToConfirm', { x: user.value?.username }), + text: i18n.tsx.typeToConfirm({ x: user.value?.username }), }); if (typed.canceled) return; @@ -422,7 +423,7 @@ async function deleteAccount() { } async function assignRole() { - const roles = await os.api('admin/roles/list'); + const roles = await misskeyApi('admin/roles/list'); const { canceled, result: roleId } = await os.select({ title: i18n.ts._role.chooseRoleToAssign, @@ -498,7 +499,7 @@ watch(() => props.userId, () => { }); watch(user, () => { - os.api('ap/get', { + misskeyApi('ap/get', { uri: user.value.uri ?? `${url}/users/${user.value.id}`, }).then(res => { ap.value = res; @@ -533,10 +534,10 @@ const headerTabs = computed(() => [{ icon: 'ph-code ph-bold ph-lg', }]); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: user.value ? acct(user.value) : i18n.ts.userInfo, icon: 'ph-warning-circle ph-bold ph-lg', -}))); +})); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/pages/admin/RolesEditorFormula.vue b/packages/frontend/src/pages/admin/RolesEditorFormula.vue index 92010f771c73635ba026f8160bf9a13848051d07..1dbcc867a1017a9d79ae9c84fbe5b38c559124ca 100644 --- a/packages/frontend/src/pages/admin/RolesEditorFormula.vue +++ b/packages/frontend/src/pages/admin/RolesEditorFormula.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSelect v-model="type" :class="$style.typeSelect"> <option value="isLocal">{{ i18n.ts._role._condition.isLocal }}</option> <option value="isRemote">{{ i18n.ts._role._condition.isRemote }}</option> + <option value="roleAssignedTo">{{ i18n.ts._role._condition.roleAssignedTo }}</option> <option value="createdLessThan">{{ i18n.ts._role._condition.createdLessThan }}</option> <option value="createdMoreThan">{{ i18n.ts._role._condition.createdMoreThan }}</option> <option value="followersLessThanOrEq">{{ i18n.ts._role._condition.followersLessThanOrEq }}</option> @@ -51,6 +52,10 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInput v-else-if="['followersLessThanOrEq', 'followersMoreThanOrEq', 'followingLessThanOrEq', 'followingMoreThanOrEq', 'notesLessThanOrEq', 'notesMoreThanOrEq'].includes(type)" v-model="v.value" type="number"> </MkInput> + + <MkSelect v-else-if="type === 'roleAssignedTo'" v-model="v.roleId"> + <option v-for="role in roles.filter(r => r.target === 'manual')" :key="role.id" :value="role.id">{{ role.name }}</option> + </MkSelect> </div> </template> @@ -62,6 +67,7 @@ import MkSelect from '@/components/MkSelect.vue'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; import { deepClone } from '@/scripts/clone.js'; +import { rolesCache } from '@/cache.js'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); @@ -77,6 +83,8 @@ const props = defineProps<{ const v = ref(deepClone(props.modelValue)); +const roles = await rolesCache.fetch(); + watch(() => props.modelValue, () => { if (JSON.stringify(props.modelValue) === JSON.stringify(v.value)) return; v.value = deepClone(props.modelValue); @@ -92,6 +100,7 @@ const type = computed({ if (t === 'and') v.value.values = []; if (t === 'or') v.value.values = []; if (t === 'not') v.value.value = { id: uuid(), type: 'isRemote' }; + if (t === 'roleAssignedTo') v.value.roleId = ''; if (t === 'createdLessThan') v.value.sec = 86400; if (t === 'createdMoreThan') v.value.sec = 86400; if (t === 'followersLessThanOrEq') v.value.value = 10; diff --git a/packages/frontend/src/pages/admin/_header_.vue b/packages/frontend/src/pages/admin/_header_.vue index 6f1a31616a414d3beca3a62b6837c8fc45d77ffe..5c9c32c964fce73d4efc1e35c2e0f40899bbc256 100644 --- a/packages/frontend/src/pages/admin/_header_.vue +++ b/packages/frontend/src/pages/admin/_header_.vue @@ -1,16 +1,16 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <div ref="el" class="fdidabkc" :style="{ background: bg }" @click="onClick"> - <template v-if="metadata"> + <template v-if="pageMetadata"> <div class="titleContainer" @click="showTabsPopup"> - <i v-if="metadata.icon" class="icon" :class="metadata.icon"></i> + <i v-if="pageMetadata.icon" class="icon" :class="pageMetadata.icon"></i> <div class="title"> - <div class="title">{{ metadata.title }}</div> + <div class="title">{{ pageMetadata.title }}</div> </div> </div> <div class="tabs"> @@ -39,7 +39,7 @@ import { popupMenu } from '@/os.js'; import { scrollToTop } from '@/scripts/scroll.js'; import MkButton from '@/components/MkButton.vue'; import { globalEvents } from '@/events.js'; -import { injectPageMetadata } from '@/scripts/page-metadata.js'; +import { injectReactiveMetadata } from '@/scripts/page-metadata.js'; type Tab = { key?: string | null; @@ -65,7 +65,7 @@ const emit = defineEmits<{ (ev: 'update:tab', key: string); }>(); -const metadata = injectPageMetadata(); +const pageMetadata = injectReactiveMetadata(); const el = shallowRef<HTMLElement>(null); const tabRefs = {}; @@ -118,7 +118,7 @@ function onTabClick(tab: Tab, ev: MouseEvent): void { } const calcBg = () => { - const rawBg = metadata?.bg ?? 'var(--bg)'; + const rawBg = pageMetadata.value?.bg ?? 'var(--bg)'; const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); tinyBg.setAlpha(0.85); bg.value = tinyBg.toRgbString(); diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue index 92688989d2fc765f58de41e7365a92654af5eee9..42fcc3a5982771d6085a3d5572c61501b1599116 100644 --- a/packages/frontend/src/pages/admin/abuses.vue +++ b/packages/frontend/src/pages/admin/abuses.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -87,8 +87,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.abuseReports, icon: 'ph-warning-circle ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue index 8a1e03c30d19da8f92d546762be591e0d2fe9bbb..6ec5abd2f2eef83ee53427f0dbc6b595a32c5d50 100644 --- a/packages/frontend/src/pages/admin/ads.vue +++ b/packages/frontend/src/pages/admin/ads.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -96,6 +96,7 @@ import MkFolder from '@/components/MkFolder.vue'; import MkSelect from '@/components/MkSelect.vue'; import FormSplit from '@/components/form/split.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -108,7 +109,7 @@ const daysOfWeek: string[] = [i18n.ts._weekday.sunday, i18n.ts._weekday.monday, const filterType = ref('all'); let publishing: boolean | null = null; -os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => { +misskeyApi('admin/ad/list', { publishing: publishing }).then(adsResponse => { if (adsResponse != null) { ads.value = adsResponse.map(r => { const exdate = new Date(r.expiresAt); @@ -159,7 +160,7 @@ function add() { function remove(ad) { os.confirm({ type: 'warning', - text: i18n.t('removeAreYouSure', { x: ad.url }), + text: i18n.tsx.removeAreYouSure({ x: ad.url }), }).then(({ canceled }) => { if (canceled) return; ads.value = ads.value.filter(x => x !== ad); @@ -174,7 +175,7 @@ function remove(ad) { function save(ad) { if (ad.id == null) { - os.api('admin/ad/create', { + misskeyApi('admin/ad/create', { ...ad, expiresAt: new Date(ad.expiresAt).getTime(), startsAt: new Date(ad.startsAt).getTime(), @@ -191,7 +192,7 @@ function save(ad) { }); }); } else { - os.api('admin/ad/update', { + misskeyApi('admin/ad/update', { ...ad, expiresAt: new Date(ad.expiresAt).getTime(), startsAt: new Date(ad.startsAt).getTime(), @@ -210,7 +211,7 @@ function save(ad) { } function more() { - os.api('admin/ad/list', { untilId: ads.value.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => { + misskeyApi('admin/ad/list', { untilId: ads.value.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => { if (adsResponse == null) return; ads.value = ads.value.concat(adsResponse.map(r => { const exdate = new Date(r.expiresAt); @@ -227,7 +228,7 @@ function more() { } function refresh() { - os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => { + misskeyApi('admin/ad/list', { publishing: publishing }).then(adsResponse => { if (adsResponse == null) return; ads.value = adsResponse.map(r => { const exdate = new Date(r.expiresAt); @@ -254,10 +255,10 @@ const headerActions = computed(() => [{ const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.ads, icon: 'ph-flag ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue index 931bd9bbc85a79c9ebb1a1313f9e033fdcd800cf..a8832b99fd4892b062c954c1e109d6f5a394e267 100644 --- a/packages/frontend/src/pages/admin/announcements.vue +++ b/packages/frontend/src/pages/admin/announcements.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -54,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="announcement.needConfirmationToRead" :helpText="i18n.ts._announcement.needConfirmationToReadDescription"> {{ i18n.ts._announcement.needConfirmationToRead }} </MkSwitch> - <p v-if="announcement.reads">{{ i18n.t('nUsersRead', { n: announcement.reads }) }}</p> + <p v-if="announcement.reads">{{ i18n.tsx.nUsersRead({ n: announcement.reads }) }}</p> <div class="buttons _buttons"> <MkButton class="button" inline primary @click="save(announcement)"><i class="ph-floppy-disk ph-bold ph-lg"></i> {{ i18n.ts.save }}</MkButton> <MkButton v-if="announcement.id != null" class="button" inline @click="archive(announcement)"><i class="ph-check ph-bold ph-lg"></i> {{ i18n.ts._announcement.end }} ({{ i18n.ts.archive }})</MkButton> @@ -79,6 +79,7 @@ import MkSwitch from '@/components/MkSwitch.vue'; import MkRadios from '@/components/MkRadios.vue'; import MkInfo from '@/components/MkInfo.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkFolder from '@/components/MkFolder.vue'; @@ -86,7 +87,7 @@ import MkTextarea from '@/components/MkTextarea.vue'; const announcements = ref<any[]>([]); -os.api('admin/announcements/list').then(announcementResponse => { +misskeyApi('admin/announcements/list').then(announcementResponse => { announcements.value = announcementResponse; }); @@ -108,11 +109,11 @@ function add() { function del(announcement) { os.confirm({ type: 'warning', - text: i18n.t('deleteAreYouSure', { x: announcement.title }), + text: i18n.tsx.deleteAreYouSure({ x: announcement.title }), }).then(({ canceled }) => { if (canceled) return; announcements.value = announcements.value.filter(x => x !== announcement); - os.api('admin/announcements/delete', announcement); + misskeyApi('admin/announcements/delete', announcement); }); } @@ -134,13 +135,13 @@ async function save(announcement) { } function more() { - os.api('admin/announcements/list', { untilId: announcements.value.reduce((acc, announcement) => announcement.id != null ? announcement : acc).id }).then(announcementResponse => { + misskeyApi('admin/announcements/list', { untilId: announcements.value.reduce((acc, announcement) => announcement.id != null ? announcement : acc).id }).then(announcementResponse => { announcements.value = announcements.value.concat(announcementResponse); }); } function refresh() { - os.api('admin/announcements/list').then(announcementResponse => { + misskeyApi('admin/announcements/list').then(announcementResponse => { announcements.value = announcementResponse; }); } @@ -156,8 +157,8 @@ const headerActions = computed(() => [{ const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.announcements, icon: 'ph-megaphone ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/admin/approvals.vue b/packages/frontend/src/pages/admin/approvals.vue index 7d0535bd7f8a052595a281dede20fe3762c93108..998e16681affe0cb919edc21cb933e25d3c596f8 100644 --- a/packages/frontend/src/pages/admin/approvals.vue +++ b/packages/frontend/src/pages/admin/approvals.vue @@ -48,10 +48,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: i18n.ts.approvals, icon: 'ph-chalkboard-teacher ph-bold ph-lg', -}))); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue index eebea51bf10ea56097e49ccc201b5975f6e5d022..052d2f0bc266d3bd169adbafe0079e5bc05e1935 100644 --- a/packages/frontend/src/pages/admin/bot-protection.vue +++ b/packages/frontend/src/pages/admin/bot-protection.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkRadios v-model="provider"> <option :value="null">{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</option> <option value="hcaptcha">hCaptcha</option> + <option value="mcaptcha">mCaptcha</option> <option value="recaptcha">reCAPTCHA</option> <option value="turnstile">Turnstile</option> </MkRadios> @@ -28,6 +29,24 @@ SPDX-License-Identifier: AGPL-3.0-only <MkCaptcha provider="hcaptcha" :sitekey="hcaptchaSiteKey || '10000000-ffff-ffff-ffff-000000000001'"/> </FormSlot> </template> + <template v-else-if="provider === 'mcaptcha'"> + <MkInput v-model="mcaptchaSiteKey"> + <template #prefix><i class="ph-key ph-bold ph-lg"></i></template> + <template #label>{{ i18n.ts.mcaptchaSiteKey }}</template> + </MkInput> + <MkInput v-model="mcaptchaSecretKey"> + <template #prefix><i class="ph-key ph-bold ph-lg"></i></template> + <template #label>{{ i18n.ts.mcaptchaSecretKey }}</template> + </MkInput> + <MkInput v-model="mcaptchaInstanceUrl"> + <template #prefix><i class="ph-globe-simple ph-bold ph-lg"></i></template> + <template #label>{{ i18n.ts.mcaptchaInstanceUrl }}</template> + </MkInput> + <FormSlot v-if="mcaptchaSiteKey && mcaptchaInstanceUrl"> + <template #label>{{ i18n.ts.preview }}</template> + <MkCaptcha provider="mcaptcha" :sitekey="mcaptchaSiteKey" :instanceUrl="mcaptchaInstanceUrl"/> + </FormSlot> + </template> <template v-else-if="provider === 'recaptcha'"> <MkInput v-model="recaptchaSiteKey"> <template #prefix><i class="ph-key ph-bold ph-lg"></i></template> @@ -72,6 +91,7 @@ import MkButton from '@/components/MkButton.vue'; import FormSuspense from '@/components/form/suspense.vue'; import FormSlot from '@/components/form/slot.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { fetchInstance } from '@/instance.js'; import { i18n } from '@/i18n.js'; @@ -80,21 +100,30 @@ const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue' const provider = ref<CaptchaProvider | null>(null); const hcaptchaSiteKey = ref<string | null>(null); const hcaptchaSecretKey = ref<string | null>(null); +const mcaptchaSiteKey = ref<string | null>(null); +const mcaptchaSecretKey = ref<string | null>(null); +const mcaptchaInstanceUrl = ref<string | null>(null); const recaptchaSiteKey = ref<string | null>(null); const recaptchaSecretKey = ref<string | null>(null); const turnstileSiteKey = ref<string | null>(null); const turnstileSecretKey = ref<string | null>(null); async function init() { - const meta = await os.api('admin/meta'); + const meta = await misskeyApi('admin/meta'); hcaptchaSiteKey.value = meta.hcaptchaSiteKey; hcaptchaSecretKey.value = meta.hcaptchaSecretKey; + mcaptchaSiteKey.value = meta.mcaptchaSiteKey; + mcaptchaSecretKey.value = meta.mcaptchaSecretKey; + mcaptchaInstanceUrl.value = meta.mcaptchaInstanceUrl; recaptchaSiteKey.value = meta.recaptchaSiteKey; recaptchaSecretKey.value = meta.recaptchaSecretKey; turnstileSiteKey.value = meta.turnstileSiteKey; turnstileSecretKey.value = meta.turnstileSecretKey; - provider.value = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : meta.enableTurnstile ? 'turnstile' : null; + provider.value = meta.enableHcaptcha ? 'hcaptcha' : + meta.enableRecaptcha ? 'recaptcha' : + meta.enableTurnstile ? 'turnstile' : + meta.enableMcaptcha ? 'mcaptcha' : null; } function save() { @@ -102,6 +131,10 @@ function save() { enableHcaptcha: provider.value === 'hcaptcha', hcaptchaSiteKey: hcaptchaSiteKey.value, hcaptchaSecretKey: hcaptchaSecretKey.value, + enableMcaptcha: provider.value === 'mcaptcha', + mcaptchaSiteKey: mcaptchaSiteKey.value, + mcaptchaSecretKey: mcaptchaSecretKey.value, + mcaptchaInstanceUrl: mcaptchaInstanceUrl.value, enableRecaptcha: provider.value === 'recaptcha', recaptchaSiteKey: recaptchaSiteKey.value, recaptchaSecretKey: recaptchaSecretKey.value, @@ -109,7 +142,7 @@ function save() { turnstileSiteKey: turnstileSiteKey.value, turnstileSecretKey: turnstileSecretKey.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } </script> diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue index fc6a9e0d674faf1eab009edcd50540c8947c58ce..9310f52bfb94ba74139ee501109598b612e01126 100644 --- a/packages/frontend/src/pages/admin/branding.vue +++ b/packages/frontend/src/pages/admin/branding.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -19,10 +19,10 @@ SPDX-License-Identifier: AGPL-3.0-only <template #prefix><i class="ph-link ph-bold ph-lg"></i></template> <template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/192px)</template> <template #caption> - <div>{{ i18n.t('_serverSettings.appIconDescription', { host: instance.name ?? host }) }}</div> + <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div> <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div> <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div> - <div><strong>{{ i18n.t('_serverSettings.appIconResolutionMustBe', { resolution: '192x192px' }) }}</strong></div> + <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '192x192px' }) }}</strong></div> </template> </MkInput> @@ -30,10 +30,10 @@ SPDX-License-Identifier: AGPL-3.0-only <template #prefix><i class="ph-link ph-bold ph-lg"></i></template> <template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/512px)</template> <template #caption> - <div>{{ i18n.t('_serverSettings.appIconDescription', { host: instance.name ?? host }) }}</div> + <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div> <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div> <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div> - <div><strong>{{ i18n.t('_serverSettings.appIconResolutionMustBe', { resolution: '512x512px' }) }}</strong></div> + <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '512x512px' }) }}</strong></div> </template> </MkInput> @@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only <FromSlot> <template #label>{{ i18n.ts.defaultLike }}</template> - <MkCustomEmoji v-if="defaultLike.startsWith(':')" style="max-height: 3em; font-size: 1.1em;" :useOriginalSize="false" :class="$style.reaction" :name="defaultLike" :normal="true" :noStyle="true"/> + <MkCustomEmoji v-if="defaultLike.startsWith(':')" style="max-height: 3em; font-size: 1.1em;" :useOriginalSize="false" :name="defaultLike" :normal="true" :noStyle="true"/> <MkEmoji v-else :emoji="defaultLike" style="max-height: 3em; font-size: 1.1em;" :normal="true" :noStyle="true"/> <MkButton rounded :small="true" @click="chooseNewLike"><i class="ph-smiley ph-bold ph-lg"></i> Change</MkButton> </FromSlot> @@ -83,6 +83,16 @@ SPDX-License-Identifier: AGPL-3.0-only <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> </MkTextarea> + <MkInput v-model="repositoryUrl" type="url"> + <template #prefix><i class="ph-link ph-bold ph-lg"></i></template> + <template #label>{{ i18n.ts.repositoryUrl }}</template> + </MkInput> + + <MkInput v-model="feedbackUrl" type="url"> + <template #prefix><i class="ph-link ph-bold ph-lg"></i></template> + <template #label>{{ i18n.ts.feedbackUrl }}</template> + </MkInput> + <MkTextarea v-model="manifestJsonOverride"> <template #label>{{ i18n.ts._serverSettings.manifestJsonOverride }}</template> </MkTextarea> @@ -109,6 +119,7 @@ import MkTextarea from '@/components/MkTextarea.vue'; import FromSlot from '@/components/form/slot.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { instance, fetchInstance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -128,10 +139,12 @@ const defaultLike = ref<string>(''); const serverErrorImageUrl = ref<string | null>(null); const infoImageUrl = ref<string | null>(null); const notFoundImageUrl = ref<string | null>(null); +const repositoryUrl = ref<string | null>(null); +const feedbackUrl = ref<string | null>(null); const manifestJsonOverride = ref<string>('{}'); async function init() { - const meta = await os.api('admin/meta'); + const meta = await misskeyApi('admin/meta'); iconUrl.value = meta.iconUrl; app192IconUrl.value = meta.app192IconUrl; app512IconUrl.value = meta.app512IconUrl; @@ -144,6 +157,8 @@ async function init() { serverErrorImageUrl.value = meta.serverErrorImageUrl; infoImageUrl.value = meta.infoImageUrl; notFoundImageUrl.value = meta.notFoundImageUrl; + repositoryUrl.value = meta.repositoryUrl; + feedbackUrl.value = meta.feedbackUrl; manifestJsonOverride.value = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t'); } @@ -157,12 +172,14 @@ function save() { themeColor: themeColor.value === '' ? null : themeColor.value, defaultLightTheme: defaultLightTheme.value === '' ? null : defaultLightTheme.value, defaultDarkTheme: defaultDarkTheme.value === '' ? null : defaultDarkTheme.value, - infoImageUrl: infoImageUrl.value, - notFoundImageUrl: notFoundImageUrl.value, - serverErrorImageUrl: serverErrorImageUrl.value, + infoImageUrl: infoImageUrl.value === '' ? null : infoImageUrl.value, + notFoundImageUrl: notFoundImageUrl.value === '' ? null : notFoundImageUrl.value, + serverErrorImageUrl: serverErrorImageUrl.value === '' ? null : serverErrorImageUrl.value, + repositoryUrl: repositoryUrl.value === '' ? null : repositoryUrl.value, + feedbackUrl: feedbackUrl.value === '' ? null : feedbackUrl.value, manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)), }).then(() => { - fetchInstance(); + fetchInstance(true); }); } @@ -181,10 +198,10 @@ function chooseNewLike(ev: MouseEvent) { const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.branding, icon: 'ph-paint-roller ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin/database.vue b/packages/frontend/src/pages/admin/database.vue index d9fc672fbf625f166226028cd6f7552a0c082c37..a64e07b4c771745f162eb0d00ff9d86b83c9f4e0 100644 --- a/packages/frontend/src/pages/admin/database.vue +++ b/packages/frontend/src/pages/admin/database.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -21,20 +21,20 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed } from 'vue'; import FormSuspense from '@/components/form/suspense.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import bytes from '@/filters/bytes.js'; import number from '@/filters/number.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -const databasePromiseFactory = () => os.api('admin/get-table-stats').then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size)); +const databasePromiseFactory = () => misskeyApi('admin/get-table-stats').then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size)); const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.database, icon: 'ph-database ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue index 819619df901e38f3287b250738640d36018bbab4..1cbfdab094affa7af2d6df7a32d6e48e42386d22 100644 --- a/packages/frontend/src/pages/admin/email-settings.vue +++ b/packages/frontend/src/pages/admin/email-settings.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -73,6 +73,7 @@ import FormSuspense from '@/components/form/suspense.vue'; import FormSplit from '@/components/form/split.vue'; import FormSection from '@/components/form/section.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { fetchInstance, instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -87,7 +88,7 @@ const smtpUser = ref<string>(''); const smtpPass = ref<string>(''); async function init() { - const meta = await os.api('admin/meta'); + const meta = await misskeyApi('admin/meta'); enableEmail.value = meta.enableEmail; email.value = meta.email; smtpSecure.value = meta.smtpSecure; @@ -123,16 +124,16 @@ function save() { smtpUser: smtpUser.value, smtpPass: smtpPass.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.emailServer, icon: 'ph-envelope ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue index f4359270b684e39b08abd4f1c321aa5d9a71d1b3..728cdc60b037bfc1949942f1420cddd913fec8ca 100644 --- a/packages/frontend/src/pages/admin/external-services.vue +++ b/packages/frontend/src/pages/admin/external-services.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -19,6 +19,14 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="deeplIsPro"> <template #label>Pro account</template> </MkSwitch> + <MkSwitch v-model="deeplFreeMode"> + <template #label>{{ i18n.ts.deeplFreeMode }}</template> + </MkSwitch> + <MkInput v-if="deeplFreeMode" v-model="deeplFreeInstance" :placeholder="'example.com/translate'"> + <template #prefix><i class="ph-globe-simple ph-bold ph-lg"></i></template> + <template #label>DeepLX-JS URL</template> + <template #caption>{{ i18n.ts.deeplFreeModeDescription }}</template> + </MkInput> </div> </FormSection> </FormSuspense> @@ -42,25 +50,32 @@ import MkSwitch from '@/components/MkSwitch.vue'; import FormSuspense from '@/components/form/suspense.vue'; import FormSection from '@/components/form/section.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { fetchInstance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; const deeplAuthKey = ref<string>(''); const deeplIsPro = ref<boolean>(false); +const deeplFreeMode = ref<boolean>(false); +const deeplFreeInstance = ref<string>(''); async function init() { - const meta = await os.api('admin/meta'); + const meta = await misskeyApi('admin/meta'); deeplAuthKey.value = meta.deeplAuthKey; deeplIsPro.value = meta.deeplIsPro; + deeplFreeMode.value = meta.deeplFreeMode; + deeplFreeInstance.value = meta.deeplFreeInstance; } function save() { os.apiWithDialog('admin/update-meta', { deeplAuthKey: deeplAuthKey.value, deeplIsPro: deeplIsPro.value, + deeplFreeMode: deeplFreeMode.value, + deeplFreeInstance: deeplFreeInstance.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } @@ -68,10 +83,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.externalServices, icon: 'ph-arrow-square-out ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue index 1888a0eb166f53ce0daad8fda6739969112f5cef..f8c4a3b272be9582048508fa2ff74cab042bb9e3 100644 --- a/packages/frontend/src/pages/admin/federation.vue +++ b/packages/frontend/src/pages/admin/federation.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -105,10 +105,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: i18n.ts.federation, icon: 'ph-globe-hemisphere-west ph-bold ph-lg', -}))); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin/files.vue b/packages/frontend/src/pages/admin/files.vue index 6808da6088e700eb67109431190b63d3737cdb4a..2a70f1c4ec90f21eac5136cb399095a956e3724a 100644 --- a/packages/frontend/src/pages/admin/files.vue +++ b/packages/frontend/src/pages/admin/files.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -42,6 +42,7 @@ import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -79,11 +80,11 @@ function show(file) { async function find() { const { canceled, result: q } = await os.inputText({ title: i18n.ts.fileIdOrUrl, - allowEmpty: false, + minLength: 1, }); if (canceled) return; - os.api('admin/drive/show-file', q.startsWith('http://') || q.startsWith('https://') ? { url: q.trim() } : { fileId: q.trim() }).then(file => { + misskeyApi('admin/drive/show-file', q.startsWith('http://') || q.startsWith('https://') ? { url: q.trim() } : { fileId: q.trim() }).then(file => { show(file); }).catch(err => { if (err.code === 'NO_SUCH_FILE') { @@ -107,8 +108,8 @@ const headerActions = computed(() => [{ const headerTabs = computed(() => []); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: i18n.ts.files, icon: 'ph-cloud ph-bold ph-lg', -}))); +})); </script> diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index 1b41a48cb42346511c4a3cc0bf960957337e4549..0fd073dd0d75dd49ec25b32e7a4c70cc8e5873ca 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -29,15 +29,16 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ComputedRef, Ref, onActivated, onMounted, onUnmounted, provide, watch, ref, computed } from 'vue'; +import { onActivated, onMounted, onUnmounted, provide, watch, ref, computed } from 'vue'; import { i18n } from '@/i18n.js'; import MkSuperMenu from '@/components/MkSuperMenu.vue'; import MkInfo from '@/components/MkInfo.vue'; import { instance } from '@/instance.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { lookupUser, lookupUserByEmail } from '@/scripts/lookup-user.js'; -import { useRouter } from '@/router.js'; -import { PageMetadata, definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js'; +import { PageMetadata, definePageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; +import { useRouter } from '@/router/supplier.js'; const isEmpty = (x: string | null) => x == null || x === ''; @@ -52,26 +53,26 @@ const indexInfo = { provide('shouldOmitHeaderTitle', false); const INFO = ref(indexInfo); -const childInfo: Ref<ComputedRef<PageMetadata> | null> = ref(null); +const childInfo = ref<null | PageMetadata>(null); const narrow = ref(false); const view = ref(null); const el = ref<HTMLDivElement | null>(null); const pageProps = ref({}); let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail); -let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha && !instance.enableTurnstile; +let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha && !instance.enableMcaptcha && !instance.enableTurnstile; let noEmailServer = !instance.enableEmail; const thereIsUnresolvedAbuseReport = ref(false); const pendingUserApprovals = ref(false); const currentPage = computed(() => router.currentRef.value.child); -os.api('admin/abuse-user-reports', { +misskeyApi('admin/abuse-user-reports', { state: 'unresolved', limit: 1, }).then(reports => { if (reports.length > 0) thereIsUnresolvedAbuseReport.value = true; }); -os.api('admin/show-users', { +misskeyApi('admin/show-users', { state: 'approved', origin: 'local', limit: 1, @@ -271,17 +272,19 @@ watch(router.currentRef, (to) => { } }); -provideMetadataReceiver((info) => { +provideMetadataReceiver((metadataGetter) => { + const info = metadataGetter(); if (info == null) { childInfo.value = null; } else { childInfo.value = info; - INFO.value.needWideArea = info.value.needWideArea ?? undefined; + INFO.value.needWideArea = info.needWideArea ?? undefined; } }); +provideReactiveMetadata(INFO); function invite() { - os.api('admin/invite/create').then(x => { + misskeyApi('admin/invite/create').then(x => { os.alert({ type: 'info', text: x[0].code, @@ -309,7 +312,7 @@ function lookup(ev: MouseEvent) { }, }, { text: i18n.ts.note, - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', action: () => { alert('TODO'); }, @@ -332,7 +335,7 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(INFO.value); +definePageMetadata(() => INFO.value); defineExpose({ header: { diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue index e54f6dc065db93b95edbea739fa40418c11da675..fcb67633f62405cdf79a09a29b28ec93b27be110 100644 --- a/packages/frontend/src/pages/admin/instance-block.vue +++ b/packages/frontend/src/pages/admin/instance-block.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -29,6 +29,7 @@ import MkButton from '@/components/MkButton.vue'; import MkTextarea from '@/components/MkTextarea.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { fetchInstance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -38,7 +39,7 @@ const silencedHosts = ref<string>(''); const tab = ref('block'); async function init() { - const meta = await os.api('admin/meta'); + const meta = await misskeyApi('admin/meta'); blockedHosts.value = meta.blockedHosts.join('\n'); silencedHosts.value = meta.silencedHosts.join('\n'); } @@ -49,7 +50,7 @@ function save() { silencedHosts: silencedHosts.value.split('\n') || [], }).then(() => { - fetchInstance(); + fetchInstance(true); }); } @@ -65,8 +66,8 @@ const headerTabs = computed(() => [{ icon: 'ph-eye-closed ph-bold ph-lg', }]); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.instanceBlocking, icon: 'ph-prohibit ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/admin/invites.vue b/packages/frontend/src/pages/admin/invites.vue index 6314d0ce4ef5f86fed97085f4adbe58cf966d8a4..7b8a1e1d4e30f4b8023a45c3271c469629b50595 100644 --- a/packages/frontend/src/pages/admin/invites.vue +++ b/packages/frontend/src/pages/admin/invites.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -59,6 +59,7 @@ import { computed, ref, shallowRef } from 'vue'; import XHeader from './_header_.vue'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import MkButton from '@/components/MkButton.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkSelect from '@/components/MkSelect.vue'; @@ -93,14 +94,14 @@ async function createWithOptions() { count: createCount.value, }; - const tickets = await os.api('admin/invite/create', options); + const tickets = await misskeyApi('admin/invite/create', options); os.alert({ type: 'success', title: i18n.ts.inviteCodeCreated, - text: tickets?.map(x => x.code).join('\n'), + text: tickets.map(x => x.code).join('\n'), }); - tickets?.forEach(ticket => pagingComponent.value?.prepend(ticket)); + tickets.forEach(ticket => pagingComponent.value?.prepend(ticket)); } function deleted(id: string) { @@ -112,10 +113,10 @@ function deleted(id: string) { const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.invite, icon: 'ph-user-plus ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue index 9539611f7608a291e1f04498675c5b17def34d26..13af28b659457b02ed2e89e96a4601c80fc41bf2 100644 --- a/packages/frontend/src/pages/admin/moderation.vue +++ b/packages/frontend/src/pages/admin/moderation.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -49,6 +49,11 @@ SPDX-License-Identifier: AGPL-3.0-only <template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template> </MkTextarea> + <MkTextarea v-model="prohibitedWords"> + <template #label>{{ i18n.ts.prohibitedWords }}</template> + <template #caption>{{ i18n.ts.prohibitedWordsDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template> + </MkTextarea> + <MkTextarea v-model="hiddenTags"> <template #label>{{ i18n.ts.hiddenTags }}</template> <template #caption>{{ i18n.ts.hiddenTagsDescription }}</template> @@ -75,6 +80,7 @@ import MkInput from '@/components/MkInput.vue'; import MkTextarea from '@/components/MkTextarea.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { fetchInstance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -86,6 +92,7 @@ const emailRequiredForSignup = ref<boolean>(false); const approvalRequiredForSignup = ref<boolean>(false); const bubbleTimelineEnabled = ref<boolean>(false); const sensitiveWords = ref<string>(''); +const prohibitedWords = ref<string>(''); const hiddenTags = ref<string>(''); const preservedUsernames = ref<string>(''); const bubbleTimeline = ref<string>(''); @@ -93,11 +100,12 @@ const tosUrl = ref<string | null>(null); const privacyPolicyUrl = ref<string | null>(null); async function init() { - const meta = await os.api('admin/meta'); + const meta = await misskeyApi('admin/meta'); enableRegistration.value = !meta.disableRegistration; emailRequiredForSignup.value = meta.emailRequiredForSignup; approvalRequiredForSignup.value = meta.approvalRequiredForSignup; sensitiveWords.value = meta.sensitiveWords.join('\n'); + prohibitedWords.value = meta.prohibitedWords.join('\n'); hiddenTags.value = meta.hiddenTags.join('\n'); preservedUsernames.value = meta.preservedUsernames.join('\n'); tosUrl.value = meta.tosUrl; @@ -114,20 +122,21 @@ function save() { tosUrl: tosUrl.value, privacyPolicyUrl: privacyPolicyUrl.value, sensitiveWords: sensitiveWords.value.split('\n'), + prohibitedWords: prohibitedWords.value.split('\n'), hiddenTags: hiddenTags.value.split('\n'), preservedUsernames: preservedUsernames.value.split('\n'), bubbleInstances: bubbleTimeline.value.split('\n'), }).then(() => { - fetchInstance(); + fetchInstance(true); }); } const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.moderation, icon: 'ph-shield ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue index 322d2d531c81350f5eb7f2609455f44f7f51bd55..03d5d6ece127c89cf1e2fdfa545dee658feea9aa 100644 --- a/packages/frontend/src/pages/admin/modlog.ModLog.vue +++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -114,6 +114,12 @@ SPDX-License-Identifier: AGPL-3.0-only <CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/> </div> </template> + <template v-else-if="log.type === 'updateRemoteInstanceNote'"> + <div>{{ i18n.ts.user }}: {{ log.info.userId }}</div> + <div :class="$style.diff"> + <CodeDiff :context="5" :hideHeader="true" :oldString="log.info.before ?? ''" :newString="log.info.after ?? ''" maxHeight="300px"/> + </div> + </template> <details> <summary>raw</summary> diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue index acb0336491a7e665b65f5cb9dd48bf4fe9497d9f..4651bb4516a4902e59410979868f9bd4db35a959 100644 --- a/packages/frontend/src/pages/admin/modlog.vue +++ b/packages/frontend/src/pages/admin/modlog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -54,14 +54,12 @@ const pagination = { })), }; -console.log(Misskey); - const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.moderationLogs, icon: 'ph-list ph-bold ph-lg-search', -}); +})); </script> diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue index e71e53c942fa964ef4c5a2ed46e3cbf01352b340..4f362e1814de09e507f902494dc39ad6d17a7bf1 100644 --- a/packages/frontend/src/pages/admin/object-storage.vue +++ b/packages/frontend/src/pages/admin/object-storage.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -90,6 +90,7 @@ import MkInput from '@/components/MkInput.vue'; import FormSuspense from '@/components/form/suspense.vue'; import FormSplit from '@/components/form/split.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { fetchInstance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -110,7 +111,7 @@ const objectStorageSetPublicRead = ref<boolean>(false); const objectStorageS3ForcePathStyle = ref<boolean>(true); async function init() { - const meta = await os.api('admin/meta'); + const meta = await misskeyApi('admin/meta'); useObjectStorage.value = meta.useObjectStorage; objectStorageBaseUrl.value = meta.objectStorageBaseUrl; objectStorageBucket.value = meta.objectStorageBucket; @@ -142,16 +143,16 @@ function save() { objectStorageSetPublicRead: objectStorageSetPublicRead.value, objectStorageS3ForcePathStyle: objectStorageS3ForcePathStyle.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.objectStorage, icon: 'ph-cloud ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin/other-settings.vue b/packages/frontend/src/pages/admin/other-settings.vue index 6523676a18f752ebb59da0f48d97dd8bc6a67b6c..20e0d6e5786189eee7a6dcfda6a4434e60ec53ec 100644 --- a/packages/frontend/src/pages/admin/other-settings.vue +++ b/packages/frontend/src/pages/admin/other-settings.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -61,6 +61,7 @@ import { ref, computed } from 'vue'; import XHeader from './_header_.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { fetchInstance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -74,7 +75,7 @@ const enableChartsForRemoteUser = ref<boolean>(false); const enableChartsForFederatedInstances = ref<boolean>(false); async function init() { - const meta = await os.api('admin/meta'); + const meta = await misskeyApi('admin/meta'); enableServerMachineStats.value = meta.enableServerMachineStats; enableAchievements.value = meta.enableAchievements; enableBotTrending.value = meta.enableBotTrending; @@ -92,7 +93,7 @@ function save() { enableChartsForRemoteUser: enableChartsForRemoteUser.value, enableChartsForFederatedInstances: enableChartsForFederatedInstances.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } @@ -105,8 +106,8 @@ const headerActions = computed(() => [{ const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.other, icon: 'ph-faders ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/admin/overview.active-users.vue b/packages/frontend/src/pages/admin/overview.active-users.vue index 5e67370c2bac0f1367323a2de95c005fd338e7df..79dd6fd5fdc0f32f4d17a790365bd12da14e3b64 100644 --- a/packages/frontend/src/pages/admin/overview.active-users.vue +++ b/packages/frontend/src/pages/admin/overview.active-users.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { onMounted, shallowRef, ref } from 'vue'; import { Chart } from 'chart.js'; import gradient from 'chartjs-plugin-gradient'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import { chartVLine } from '@/scripts/chart-vline.js'; @@ -52,7 +52,7 @@ async function renderChart() { })); }; - const raw = await os.api('charts/active-users', { limit: chartLimit, span: 'day' }); + const raw = await misskeyApi('charts/active-users', { limit: chartLimit, span: 'day' }); const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; diff --git a/packages/frontend/src/pages/admin/overview.ap-requests.vue b/packages/frontend/src/pages/admin/overview.ap-requests.vue index 0de62fadea1015f807a198b51c55f3c960a816ab..d4c83f21b68571001245fb69034b25b9f4b774be 100644 --- a/packages/frontend/src/pages/admin/overview.ap-requests.vue +++ b/packages/frontend/src/pages/admin/overview.ap-requests.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { onMounted, shallowRef, ref } from 'vue'; import { Chart } from 'chart.js'; import gradient from 'chartjs-plugin-gradient'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import { chartVLine } from '@/scripts/chart-vline.js'; import { defaultStore } from '@/store.js'; @@ -65,7 +65,7 @@ onMounted(async () => { })); }; - const raw = await os.api('charts/ap-request', { limit: chartLimit, span: 'day' }); + const raw = await misskeyApi('charts/ap-request', { limit: chartLimit, span: 'day' }); const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; const succColor = '#87e000'; diff --git a/packages/frontend/src/pages/admin/overview.federation.vue b/packages/frontend/src/pages/admin/overview.federation.vue index 2fad222bda49af1e5e8e6d3886b168bbdbf1d715..3a3550c6c08706f6f1db678a7e6aae32e0cfdb9d 100644 --- a/packages/frontend/src/pages/admin/overview.federation.vue +++ b/packages/frontend/src/pages/admin/overview.federation.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -49,6 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { onMounted, ref } from 'vue'; import XPie, { type InstanceForPie } from './overview.pie.vue'; import * as os from '@/os.js'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; import number from '@/filters/number.js'; import MkNumberDiff from '@/components/MkNumberDiff.vue'; import { i18n } from '@/i18n.js'; @@ -65,13 +66,13 @@ const fetching = ref(true); const { handler: externalTooltipHandler } = useChartTooltip(); onMounted(async () => { - const chart = await os.apiGet('charts/federation', { limit: 2, span: 'day' }); + const chart = await misskeyApiGet('charts/federation', { limit: 2, span: 'day' }); federationPubActive.value = chart.pubActive[0]; federationPubActiveDiff.value = chart.pubActive[0] - chart.pubActive[1]; federationSubActive.value = chart.subActive[0]; federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1]; - os.apiGet('federation/stats', { limit: 10 }).then(res => { + misskeyApiGet('federation/stats', { limit: 10 }).then(res => { topSubInstancesForPie.value = [ ...res.topSubInstances.map(x => ({ name: x.host, diff --git a/packages/frontend/src/pages/admin/overview.heatmap.vue b/packages/frontend/src/pages/admin/overview.heatmap.vue index 8e3c809353ee571020739941fa54ad0e62b43423..7b2b142b163143545d0169c4f68d147d4cb1a7fa 100644 --- a/packages/frontend/src/pages/admin/overview.heatmap.vue +++ b/packages/frontend/src/pages/admin/overview.heatmap.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/admin/overview.instances.vue b/packages/frontend/src/pages/admin/overview.instances.vue index de34f0c09b23e9a6bb254ef48d51f4941855bb4f..a09db2a6d52805ca1e54a26a4ae9b16850f401cd 100644 --- a/packages/frontend/src/pages/admin/overview.instances.vue +++ b/packages/frontend/src/pages/admin/overview.instances.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -18,8 +18,8 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import * as Misskey from 'misskey-js'; -import * as os from '@/os.js'; import { useInterval } from '@/scripts/use-interval.js'; import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue'; import { defaultStore } from '@/store.js'; @@ -28,7 +28,7 @@ const instances = ref<Misskey.entities.FederationInstance[]>([]); const fetching = ref(true); const fetch = async () => { - const fetchedInstances = await os.api('federation/instances', { + const fetchedInstances = await misskeyApi('federation/instances', { sort: '+latestRequestReceivedAt', limit: 6, }); diff --git a/packages/frontend/src/pages/admin/overview.moderators.vue b/packages/frontend/src/pages/admin/overview.moderators.vue index 3034bdd57e1a6f4f07426ab9caaf7479dee5614e..f0691534c8321f1be7c6f37307de5fb377cdceca 100644 --- a/packages/frontend/src/pages/admin/overview.moderators.vue +++ b/packages/frontend/src/pages/admin/overview.moderators.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -18,15 +18,15 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onMounted, ref } from 'vue'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import * as Misskey from 'misskey-js'; -import * as os from '@/os.js'; import { defaultStore } from '@/store.js'; const moderators = ref<Misskey.entities.UserDetailed[] | null>(null); const fetching = ref(true); onMounted(async () => { - moderators.value = await os.api('admin/show-users', { + moderators.value = await misskeyApi('admin/show-users', { sort: '+lastActiveDate', state: 'adminOrModerator', limit: 30, diff --git a/packages/frontend/src/pages/admin/overview.pie.vue b/packages/frontend/src/pages/admin/overview.pie.vue index 95c1f57b29263c3b17f04aaebfe8c802a8a4219c..c7a9f2a7027fd2faa2fa5ba23ce45692cc3b8037 100644 --- a/packages/frontend/src/pages/admin/overview.pie.vue +++ b/packages/frontend/src/pages/admin/overview.pie.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/admin/overview.queue.chart.vue b/packages/frontend/src/pages/admin/overview.queue.chart.vue index 38309e351a898c28ec7f51956de2d84127b46c0e..2efc17c888e76acc0b3902ccfd5157f585748ab6 100644 --- a/packages/frontend/src/pages/admin/overview.queue.chart.vue +++ b/packages/frontend/src/pages/admin/overview.queue.chart.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/admin/overview.queue.vue b/packages/frontend/src/pages/admin/overview.queue.vue index b6b3bf194ad86b18383714b7b6c21d137b500bc4..c7478f252a269ccc1028aeed4d96fba7e4b09786 100644 --- a/packages/frontend/src/pages/admin/overview.queue.vue +++ b/packages/frontend/src/pages/admin/overview.queue.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/admin/overview.retention.vue b/packages/frontend/src/pages/admin/overview.retention.vue index 514db663ab26fddcd132a60d280548a4a9eeb813..adcb9d594812e02f3e6fe3ba1c06a007f6320cfe 100644 --- a/packages/frontend/src/pages/admin/overview.retention.vue +++ b/packages/frontend/src/pages/admin/overview.retention.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/admin/overview.stats.vue b/packages/frontend/src/pages/admin/overview.stats.vue index adbfe3f9e2da70ec4f3e1797745a183a2c1a5c1b..27ae52c32c1fbdf83aca06dc6cb050ddc35e59a2 100644 --- a/packages/frontend/src/pages/admin/overview.stats.vue +++ b/packages/frontend/src/pages/admin/overview.stats.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> <div class="item _panel notes"> - <div class="icon"><i class="ph-pencil ph-bold ph-lg"></i></div> + <div class="icon"><i class="ph-pencil-simple ph-bold ph-lg"></i></div> <div class="body"> <div class="value"> <MkNumber :value="stats.originalNotesCount" style="margin-right: 0.5em;"/> @@ -63,7 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onMounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; -import * as os from '@/os.js'; +import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js'; import MkNumberDiff from '@/components/MkNumberDiff.vue'; import MkNumber from '@/components/MkNumber.vue'; import { i18n } from '@/i18n.js'; @@ -78,17 +78,17 @@ const fetching = ref(true); onMounted(async () => { const [_stats, _onlineUsersCount] = await Promise.all([ - os.api('stats', {}), - os.apiGet('get-online-users-count').then(res => res.count), + misskeyApi('stats', {}), + misskeyApiGet('get-online-users-count').then(res => res.count), ]); stats.value = _stats; onlineUsersCount.value = _onlineUsersCount; - os.apiGet('charts/users', { limit: 2, span: 'day' }).then(chart => { + misskeyApiGet('charts/users', { limit: 2, span: 'day' }).then(chart => { usersComparedToThePrevDay.value = stats.value.originalUsersCount - chart.local.total[1]; }); - os.apiGet('charts/notes', { limit: 2, span: 'day' }).then(chart => { + misskeyApiGet('charts/notes', { limit: 2, span: 'day' }).then(chart => { notesComparedToThePrevDay.value = stats.value.originalNotesCount - chart.local.total[1]; }); diff --git a/packages/frontend/src/pages/admin/overview.users.vue b/packages/frontend/src/pages/admin/overview.users.vue index 79579367c16a0e32a44b2c47001dab9490664e5f..408be88d4792282d93439bcebe11b047dfb0644c 100644 --- a/packages/frontend/src/pages/admin/overview.users.vue +++ b/packages/frontend/src/pages/admin/overview.users.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -18,8 +18,8 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import * as Misskey from 'misskey-js'; -import * as os from '@/os.js'; import { useInterval } from '@/scripts/use-interval.js'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; import { defaultStore } from '@/store.js'; @@ -28,7 +28,7 @@ const newUsers = ref<Misskey.entities.UserDetailed[] | null>(null); const fetching = ref(true); const fetch = async () => { - const _newUsers = await os.api('admin/show-users', { + const _newUsers = await misskeyApi('admin/show-users', { limit: 5, sort: '+createdAt', origin: 'local', diff --git a/packages/frontend/src/pages/admin/overview.vue b/packages/frontend/src/pages/admin/overview.vue index 9f2920ee0cf1562408b9dfd279765cb2d3eab336..bf766600e5e4afba9711c7ba6936d64850ff6b2b 100644 --- a/packages/frontend/src/pages/admin/overview.vue +++ b/packages/frontend/src/pages/admin/overview.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -79,6 +79,7 @@ import XModerators from './overview.moderators.vue'; import XHeatmap from './overview.heatmap.vue'; import type { InstanceForPie } from './overview.pie.vue'; import * as os from '@/os.js'; +import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js'; import { useStream } from '@/stream.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -117,14 +118,14 @@ onMounted(async () => { magicGrid.listen(); */ - os.apiGet('charts/federation', { limit: 2, span: 'day' }).then(chart => { + misskeyApiGet('charts/federation', { limit: 2, span: 'day' }).then(chart => { federationPubActive.value = chart.pubActive[0]; federationPubActiveDiff.value = chart.pubActive[0] - chart.pubActive[1]; federationSubActive.value = chart.subActive[0]; federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1]; }); - os.apiGet('federation/stats', { limit: 10 }).then(res => { + misskeyApiGet('federation/stats', { limit: 10 }).then(res => { topSubInstancesForPie.value = [ ...res.topSubInstances.map(x => ({ name: x.host, @@ -149,18 +150,18 @@ onMounted(async () => { ]; }); - os.api('admin/server-info').then(serverInfoResponse => { + misskeyApi('admin/server-info').then(serverInfoResponse => { serverInfo.value = serverInfoResponse; }); - os.api('admin/show-users', { + misskeyApi('admin/show-users', { limit: 5, sort: '+createdAt', }).then(res => { newUsers.value = res; }); - os.api('federation/instances', { + misskeyApi('federation/instances', { sort: '+latestRequestReceivedAt', limit: 25, }).then(res => { @@ -183,10 +184,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.dashboard, icon: 'ph-gauge ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin/proxy-account.vue b/packages/frontend/src/pages/admin/proxy-account.vue index 1425749bd437cb49b0975e2d2f8b149e3285e321..59fd5911d477b7b234769dd688248e81c39e8409 100644 --- a/packages/frontend/src/pages/admin/proxy-account.vue +++ b/packages/frontend/src/pages/admin/proxy-account.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -28,6 +28,7 @@ import MkButton from '@/components/MkButton.vue'; import MkInfo from '@/components/MkInfo.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { fetchInstance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -36,15 +37,15 @@ const proxyAccount = ref<Misskey.entities.UserDetailed | null>(null); const proxyAccountId = ref<string | null>(null); async function init() { - const meta = await os.api('admin/meta'); + const meta = await misskeyApi('admin/meta'); proxyAccountId.value = meta.proxyAccountId; if (proxyAccountId.value) { - proxyAccount.value = await os.api('users/show', { userId: proxyAccountId.value }); + proxyAccount.value = await misskeyApi('users/show', { userId: proxyAccountId.value }); } } function chooseProxyAccount() { - os.selectUser().then(user => { + os.selectUser({ localOnly: true }).then(user => { proxyAccount.value = user; proxyAccountId.value = user.id; save(); @@ -55,7 +56,7 @@ function save() { os.apiWithDialog('admin/update-meta', { proxyAccountId: proxyAccountId.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } @@ -63,8 +64,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.proxyAccount, icon: 'ph-ghost ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/admin/queue.chart.chart.vue b/packages/frontend/src/pages/admin/queue.chart.chart.vue index 566670c8430cc62e05f1151c7b388f9b41caa9fe..cc188981720cfef6a3b4b59ba18535766a4c88c3 100644 --- a/packages/frontend/src/pages/admin/queue.chart.chart.vue +++ b/packages/frontend/src/pages/admin/queue.chart.chart.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/admin/queue.chart.vue b/packages/frontend/src/pages/admin/queue.chart.vue index b829dd57382e20a69c7b46a8da3838c5a447e75b..f7b4b27a68c356352d19501b7991c30f03c5159a 100644 --- a/packages/frontend/src/pages/admin/queue.chart.vue +++ b/packages/frontend/src/pages/admin/queue.chart.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { markRaw, onMounted, onUnmounted, ref, shallowRef } from 'vue'; import XChart from './queue.chart.chart.vue'; import number from '@/filters/number.js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { useStream } from '@/stream.js'; import { i18n } from '@/i18n.js'; import MkFolder from '@/components/MkFolder.vue'; @@ -105,7 +105,7 @@ const onStatsLog = (statsLog) => { onMounted(() => { if (props.domain === 'inbox' || props.domain === 'deliver') { - os.api(`admin/queue/${props.domain}-delayed`).then(result => { + misskeyApi(`admin/queue/${props.domain}-delayed`).then(result => { jobs.value = result; }); } diff --git a/packages/frontend/src/pages/admin/queue.vue b/packages/frontend/src/pages/admin/queue.vue index 245c55f6e7f1255086ef50f129c3c86fc6a22145..ba6911a943d85ef4cd68b03536cecf7271cc6e13 100644 --- a/packages/frontend/src/pages/admin/queue.vue +++ b/packages/frontend/src/pages/admin/queue.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -68,8 +68,8 @@ const headerTabs = computed(() => [{ title: 'Inbox', }]); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.jobQueue, icon: 'ph-clock ph-bold ph-lg-play', -}); +})); </script> diff --git a/packages/frontend/src/pages/admin/relays.vue b/packages/frontend/src/pages/admin/relays.vue index 578c29ee6c1d950e298f021c114176b4180c83c7..6ff0d8bd225aebf033ee83ae33d13833a617aebf 100644 --- a/packages/frontend/src/pages/admin/relays.vue +++ b/packages/frontend/src/pages/admin/relays.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <i v-if="relay.status === 'accepted'" class="ph-check ph-bold ph-lg" :class="$style.icon" style="color: var(--success);"></i> <i v-else-if="relay.status === 'rejected'" class="ph-prohibit ph-bold ph-lg" :class="$style.icon" style="color: var(--error);"></i> <i v-else class="ph-clock ph-bold ph-lg" :class="$style.icon"></i> - <span>{{ i18n.t(`_relayStatus.${relay.status}`) }}</span> + <span>{{ i18n.ts._relayStatus[relay.status] }}</span> </div> <MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="ph-trash ph-bold ph-lg"></i> {{ i18n.ts.remove }}</MkButton> </div> @@ -29,6 +29,7 @@ import * as Misskey from 'misskey-js'; import XHeader from './_header_.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -41,7 +42,7 @@ async function addRelay() { placeholder: i18n.ts.inboxUrl, }); if (canceled) return; - os.api('admin/relays/add', { + misskeyApi('admin/relays/add', { inbox, }).then((relay: any) => { refresh(); @@ -54,7 +55,7 @@ async function addRelay() { } function remove(inbox: string) { - os.api('admin/relays/remove', { + misskeyApi('admin/relays/remove', { inbox, }).then(() => { refresh(); @@ -67,7 +68,7 @@ function remove(inbox: string) { } function refresh() { - os.api('admin/relays/list').then(relayList => { + misskeyApi('admin/relays/list').then(relayList => { relays.value = relayList; }); } @@ -83,10 +84,10 @@ const headerActions = computed(() => [{ const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.relays, icon: 'ph-planet ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue index 980c3111563ed272599dcb00400dbd107450ea23..e6023d2f2ada9f8a90f71e33dd46f7ee2c4973d6 100644 --- a/packages/frontend/src/pages/admin/roles.edit.vue +++ b/packages/frontend/src/pages/admin/roles.edit.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -28,11 +28,12 @@ import { v4 as uuid } from 'uuid'; import XHeader from './_header_.vue'; import XEditor from './roles.editor.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { useRouter } from '@/router.js'; import MkButton from '@/components/MkButton.vue'; import { rolesCache } from '@/cache.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -44,7 +45,7 @@ const role = ref<Misskey.entities.Role | null>(null); const data = ref<any>(null); if (props.id) { - role.value = await os.api('admin/roles/show', { + role.value = await misskeyApi('admin/roles/show', { roleId: props.id, }); @@ -86,11 +87,8 @@ async function save() { const headerTabs = computed(() => []); -definePageMetadata(computed(() => role.value ? { - title: i18n.ts._role.edit + ': ' + role.value.name, - icon: 'ph-seal-check ph-bold ph-lg', -} : { - title: i18n.ts._role.new, +definePageMetadata(() => ({ + title: role.value ? `${i18n.ts._role.edit}: ${role.value.name}` : i18n.ts._role.new, icon: 'ph-seal-check ph-bold ph-lg', })); </script> diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 164510fd24834b15cadafb5241322a52273bdb01..99a31d5157418bb13e83553098b423f299ed6fe7 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -200,6 +200,25 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])"> + <template #label>{{ i18n.ts._role._options.mentionMax }}</template> + <template #suffix> + <span v-if="role.policies.mentionLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.mentionLimit.value }}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.mentionLimit)"></i></span> + </template> + <div class="_gaps"> + <MkSwitch v-model="role.policies.mentionLimit.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts._role.useBaseValue }}</template> + </MkSwitch> + <MkInput v-model="role.policies.mentionLimit.value" :disabled="role.policies.mentionLimit.useDefault" type="number" :readonly="readonly"> + </MkInput> + <MkRange v-model="role.policies.mentionLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <template #label>{{ i18n.ts._role.priority }}</template> + </MkRange> + </div> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])"> <template #label>{{ i18n.ts._role._options.canInvite }}</template> <template #suffix> diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue index 92818cc3def0be25db64feb8b48e6e560b63d4aa..cda524f78762a45789dfa0db403d978735d59454 100644 --- a/packages/frontend/src/pages/admin/roles.role.vue +++ b/packages/frontend/src/pages/admin/roles.role.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSpacer :contentMax="700"> <div class="_gaps"> <div class="_buttons"> - <MkButton primary rounded @click="edit"><i class="ph-pencil ph-bold ph-lg"></i> {{ i18n.ts.edit }}</MkButton> + <MkButton primary rounded @click="edit"><i class="ph-pencil-simple ph-bold ph-lg"></i> {{ i18n.ts.edit }}</MkButton> <MkButton danger rounded @click="del"><i class="ph-trash ph-bold ph-lg"></i> {{ i18n.ts.delete }}</MkButton> </div> <MkFolder> @@ -67,14 +67,15 @@ import XHeader from './_header_.vue'; import XEditor from './roles.editor.vue'; import MkFolder from '@/components/MkFolder.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { useRouter } from '@/router.js'; import MkButton from '@/components/MkButton.vue'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkPagination from '@/components/MkPagination.vue'; import { infoImageUrl } from '@/instance.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -92,7 +93,7 @@ const usersPagination = { const expandedItems = ref([]); -const role = reactive(await os.api('admin/roles/show', { +const role = reactive(await misskeyApi('admin/roles/show', { roleId: props.id, })); @@ -103,7 +104,7 @@ function edit() { async function del() { const { canceled } = await os.confirm({ type: 'warning', - text: i18n.t('deleteAreYouSure', { x: role.name }), + text: i18n.tsx.deleteAreYouSure({ x: role.name }), }); if (canceled) return; @@ -115,9 +116,7 @@ async function del() { } async function assign() { - const user = await os.selectUser({ - includeSelf: true, - }); + const user = await os.selectUser({ includeSelf: true }); const { canceled: canceled2, result: period } = await os.select({ title: i18n.ts.period, @@ -171,10 +170,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => ({ - title: i18n.ts.role + ': ' + role.name, +definePageMetadata(() => ({ + title: `${i18n.ts.role}: ${role.name}`, icon: 'ph-seal-check ph-bold ph-lg', -}))); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 9cb48130d8cd518b348b8c407eb0114628980b00..f104f9a00d08f2ec92cef4c0f47128606acfe15b 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -67,6 +67,13 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSwitch> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])"> + <template #label>{{ i18n.ts._role._options.mentionMax }}</template> + <template #suffix>{{ policies.mentionLimit }}</template> + <MkInput v-model="policies.mentionLimit" type="number"> + </MkInput> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])"> <template #label>{{ i18n.ts._role._options.canInvite }}</template> <template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template> @@ -253,17 +260,18 @@ import MkRange from '@/components/MkRange.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkRolePreview from '@/components/MkRolePreview.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { instance } from '@/instance.js'; -import { useRouter } from '@/router.js'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import { ROLE_POLICIES } from '@/const.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); const baseRoleQ = ref(''); -const roles = await os.api('admin/roles/list'); +const roles = await misskeyApi('admin/roles/list'); const policies = reactive<Record<typeof ROLE_POLICIES[number], any>>({}); for (const ROLE_POLICY of ROLE_POLICIES) { @@ -289,10 +297,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: i18n.ts.roles, icon: 'ph-seal-check ph-bold ph-lg', -}))); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue index 8ed3e20af39e50d6cf8f9e7f8840589b89ec48e5..8e75975209bf4e473b64cea8a0c42fd3afab30fe 100644 --- a/packages/frontend/src/pages/admin/security.vue +++ b/packages/frontend/src/pages/admin/security.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -13,6 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #icon><i class="ph-shield ph-bold ph-lg"></i></template> <template #label>{{ i18n.ts.botProtection }}</template> <template v-if="enableHcaptcha" #suffix>hCaptcha</template> + <template v-else-if="enableMcaptcha" #suffix>mCaptcha</template> <template v-else-if="enableRecaptcha" #suffix>reCAPTCHA</template> <template v-else-if="enableTurnstile" #suffix>Turnstile</template> <template v-else #suffix>{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</template> @@ -27,16 +28,28 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps_m"> <span>{{ i18n.ts.activeEmailValidationDescription }}</span> - <MkSwitch v-model="enableActiveEmailValidation" @update:modelValue="save"> + <MkSwitch v-model="enableActiveEmailValidation"> <template #label>Enable</template> </MkSwitch> - <MkSwitch v-model="enableVerifymailApi" @update:modelValue="save"> + <MkSwitch v-model="enableVerifymailApi"> <template #label>Use Verifymail.io API</template> </MkSwitch> - <MkInput v-model="verifymailAuthKey" @update:modelValue="save"> + <MkInput v-model="verifymailAuthKey"> <template #prefix><i class="ph-key ph-bold ph-lg"></i></template> <template #label>Verifymail.io API Auth Key</template> </MkInput> + <MkSwitch v-model="enableTruemailApi"> + <template #label>Use TrueMail API</template> + </MkSwitch> + <MkInput v-model="truemailInstance"> + <template #prefix><i class="ph-key ph-bold ph-lg"></i></template> + <template #label>TrueMail API Instance</template> + </MkInput> + <MkInput v-model="truemailAuthKey"> + <template #prefix><i class="ph-key ph-bold ph-lg"></i></template> + <template #label>TrueMail API Auth Key</template> + </MkInput> + <MkButton primary @click="save"><i class="ph-floppy-disk ph-bold ph-lg"></i> {{ i18n.ts.save }}</MkButton> </div> </MkFolder> @@ -94,31 +107,40 @@ import MkInput from '@/components/MkInput.vue'; import MkButton from '@/components/MkButton.vue'; import MkTextarea from '@/components/MkTextarea.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { fetchInstance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; const summalyProxy = ref<string>(''); const enableHcaptcha = ref<boolean>(false); +const enableMcaptcha = ref<boolean>(false); const enableRecaptcha = ref<boolean>(false); const enableTurnstile = ref<boolean>(false); const enableIpLogging = ref<boolean>(false); const enableActiveEmailValidation = ref<boolean>(false); const enableVerifymailApi = ref<boolean>(false); const verifymailAuthKey = ref<string | null>(null); +const enableTruemailApi = ref<boolean>(false); +const truemailInstance = ref<string | null>(null); +const truemailAuthKey = ref<string | null>(null); const bannedEmailDomains = ref<string>(''); async function init() { - const meta = await os.api('admin/meta'); + const meta = await misskeyApi('admin/meta'); summalyProxy.value = meta.summalyProxy; enableHcaptcha.value = meta.enableHcaptcha; + enableMcaptcha.value = meta.enableMcaptcha; enableRecaptcha.value = meta.enableRecaptcha; enableTurnstile.value = meta.enableTurnstile; enableIpLogging.value = meta.enableIpLogging; enableActiveEmailValidation.value = meta.enableActiveEmailValidation; enableVerifymailApi.value = meta.enableVerifymailApi; verifymailAuthKey.value = meta.verifymailAuthKey; - bannedEmailDomains.value = meta.bannedEmailDomains.join('\n'); + enableTruemailApi.value = meta.enableTruemailApi; + truemailInstance.value = meta.truemailInstance; + truemailAuthKey.value = meta.truemailAuthKey; + bannedEmailDomains.value = meta.bannedEmailDomains?.join('\n') || ''; } function save() { @@ -128,9 +150,12 @@ function save() { enableActiveEmailValidation: enableActiveEmailValidation.value, enableVerifymailApi: enableVerifymailApi.value, verifymailAuthKey: verifymailAuthKey.value, + enableTruemailApi: enableTruemailApi.value, + truemailInstance: truemailInstance.value, + truemailAuthKey: truemailAuthKey.value, bannedEmailDomains: bannedEmailDomains.value.split('\n'), }).then(() => { - fetchInstance(); + fetchInstance(true); }); } @@ -138,8 +163,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.security, icon: 'ph-lock ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue index 6aecb4339914c4d18e111e0f08cfb46a061c6db2..db2bb56eac413eef5d65636a462c03c89d8fbe40 100644 --- a/packages/frontend/src/pages/admin/server-rules.vue +++ b/packages/frontend/src/pages/admin/server-rules.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -58,7 +58,7 @@ const save = async () => { await os.apiWithDialog('admin/update-meta', { serverRules: serverRules.value, }); - fetchInstance(); + fetchInstance(true); }; const remove = (index: number): void => { @@ -67,10 +67,10 @@ const remove = (index: number): void => { const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.serverRules, - icon: 'ph-check ph-bold ph-lgbox', -}); + icon: 'ph-check ph-bold ph-lg', +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue index 649f22e64439cbc112656ab1b0b5d17f0cd0cd82..887ac6fb4c30a7bd1848fcdc84c458b81e8a8639 100644 --- a/packages/frontend/src/pages/admin/settings.vue +++ b/packages/frontend/src/pages/admin/settings.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -34,12 +34,27 @@ SPDX-License-Identifier: AGPL-3.0-only </MkInput> </FormSplit> + <MkInput v-model="repositoryUrl" type="url"> + <template #label>{{ i18n.ts.repositoryUrl }}</template> + <template #prefix><i class="ph-link ph-bold ph-lg"></i></template> + <template #caption>{{ i18n.ts.repositoryUrlDescription }}</template> + </MkInput> + + <MkInfo v-if="!instance.providesTarball && !repositoryUrl" warn> + {{ i18n.ts.repositoryUrlOrTarballRequired }} + </MkInfo> + <MkInput v-model="impressumUrl" type="url"> <template #label>{{ i18n.ts.impressumUrl }}</template> <template #prefix><i class="ph-link ph-bold ph-lg"></i></template> <template #caption>{{ i18n.ts.impressumDescription }}</template> </MkInput> + <MkInput v-model="donationUrl" type="url"> + <template #label>{{ i18n.ts.donationUrl }}</template> + <template #prefix><i class="ph-link ph-bold ph-lg"></i></template> + </MkInput> + <MkTextarea v-model="pinnedUsers"> <template #label>{{ i18n.ts.pinnedUsers }}</template> <template #caption>{{ i18n.ts.pinnedUsersDescription }}</template> @@ -158,7 +173,8 @@ import FormSection from '@/components/form/section.vue'; import FormSplit from '@/components/form/split.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os.js'; -import { fetchInstance } from '@/instance.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; +import { fetchInstance, instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkButton from '@/components/MkButton.vue'; @@ -168,7 +184,9 @@ const shortName = ref<string | null>(null); const description = ref<string | null>(null); const maintainerName = ref<string | null>(null); const maintainerEmail = ref<string | null>(null); +const repositoryUrl = ref<string | null>(null); const impressumUrl = ref<string | null>(null); +const donationUrl = ref<string | null>(null); const pinnedUsers = ref<string>(''); const cacheRemoteFiles = ref<boolean>(false); const cacheRemoteSensitiveFiles = ref<boolean>(false); @@ -184,13 +202,15 @@ const perUserListTimelineCacheMax = ref<number>(0); const notesPerOneAd = ref<number>(0); async function init(): Promise<void> { - const meta = await os.api('admin/meta'); + const meta = await misskeyApi('admin/meta'); name.value = meta.name; shortName.value = meta.shortName; description.value = meta.description; maintainerName.value = meta.maintainerName; maintainerEmail.value = meta.maintainerEmail; + repositoryUrl.value = meta.repositoryUrl; impressumUrl.value = meta.impressumUrl; + donationUrl.value = meta.donationUrl; pinnedUsers.value = meta.pinnedUsers.join('\n'); cacheRemoteFiles.value = meta.cacheRemoteFiles; cacheRemoteSensitiveFiles.value = meta.cacheRemoteSensitiveFiles; @@ -213,7 +233,9 @@ async function save(): void { description: description.value, maintainerName: maintainerName.value, maintainerEmail: maintainerEmail.value, + repositoryUrl: repositoryUrl.value, impressumUrl: impressumUrl.value, + donationUrl: donationUrl.value, pinnedUsers: pinnedUsers.value.split('\n'), cacheRemoteFiles: cacheRemoteFiles.value, cacheRemoteSensitiveFiles: cacheRemoteSensitiveFiles.value, @@ -229,15 +251,15 @@ async function save(): void { notesPerOneAd: notesPerOneAd.value, }); - fetchInstance(); + fetchInstance(true); } const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.general, icon: 'ph-gear ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin/users.vue b/packages/frontend/src/pages/admin/users.vue index 1bc4eb4089dbf899745fc54cca9ff025b7792925..626346a998c7ff2ef26da6f7c2789df3b7a5aab4 100644 --- a/packages/frontend/src/pages/admin/users.vue +++ b/packages/frontend/src/pages/admin/users.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -91,7 +91,7 @@ const pagination = { }; function searchUser() { - os.selectUser().then(user => { + os.selectUser({ includeSelf: true }).then(user => { show(user); }); } @@ -138,10 +138,10 @@ const headerActions = computed(() => [{ const headerTabs = computed(() => []); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: i18n.ts.users, icon: 'ph-users ph-bold ph-lg', -}))); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/ads.vue b/packages/frontend/src/pages/ads.vue index 9d508937af0ace45ec724f455ad2152cf6899044..c6373e8d6048fe5b8dd152f97c8d12274e41f9e9 100644 --- a/packages/frontend/src/pages/ads.vue +++ b/packages/frontend/src/pages/ads.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -20,9 +20,9 @@ import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; import { instance } from '@/instance.js'; -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.ads, icon: 'ph-flag ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue index 705115abb08bc629eb9f053ab1a96a5676ce37a1..4f5abdb3850ec760ce61b00e50f8b3b43dd9d015 100644 --- a/packages/frontend/src/pages/announcements.vue +++ b/packages/frontend/src/pages/announcements.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,34 +7,36 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :contentMax="800"> - <div class="_gaps"> - <MkInfo v-if="$i && $i.hasUnreadAnnouncement && tab === 'current'" warn>{{ i18n.ts.youHaveUnreadAnnouncements }}</MkInfo> - <MkPagination ref="paginationEl" :key="tab" v-slot="{items}" :pagination="tab === 'current' ? paginationCurrent : paginationPast" class="_gaps"> - <section v-for="announcement in items" :key="announcement.id" class="_panel" :class="$style.announcement"> - <div v-if="announcement.forYou" :class="$style.forYou"><i class="ph-push-pin ph-bold ph-lg"></i> {{ i18n.ts.forYou }}</div> - <div :class="$style.header"> - <span v-if="$i && !announcement.silence && !announcement.isRead" style="margin-right: 0.5em;">🆕</span> - <span style="margin-right: 0.5em;"> - <i v-if="announcement.icon === 'info'" class="ph-info ph-bold ph-lg"></i> - <i v-else-if="announcement.icon === 'warning'" class="ph-warning ph-bold ph-lg" style="color: var(--warn);"></i> - <i v-else-if="announcement.icon === 'error'" class="ph-x-circle ph-bold ph-lg" style="color: var(--error);"></i> - <i v-else-if="announcement.icon === 'success'" class="ph-check ph-bold ph-lg" style="color: var(--success);"></i> - </span> - <span>{{ announcement.title }}</span> - </div> - <div :class="$style.content"> - <Mfm :text="announcement.text"/> - <img v-if="announcement.imageUrl" :src="announcement.imageUrl"/> - <div style="opacity: 0.7; font-size: 85%;"> - <MkTime :time="announcement.updatedAt ?? announcement.createdAt" mode="detail"/> + <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <div :key="tab" class="_gaps"> + <MkInfo v-if="$i && $i.hasUnreadAnnouncement && tab === 'current'" warn>{{ i18n.ts.youHaveUnreadAnnouncements }}</MkInfo> + <MkPagination ref="paginationEl" :key="tab" v-slot="{items}" :pagination="tab === 'current' ? paginationCurrent : paginationPast" class="_gaps"> + <section v-for="announcement in items" :key="announcement.id" class="_panel" :class="$style.announcement"> + <div v-if="announcement.forYou" :class="$style.forYou"><i class="ph-push-pin ph-bold ph-lg"></i> {{ i18n.ts.forYou }}</div> + <div :class="$style.header"> + <span v-if="$i && !announcement.silence && !announcement.isRead" style="margin-right: 0.5em;">🆕</span> + <span style="margin-right: 0.5em;"> + <i v-if="announcement.icon === 'info'" class="ph-info ph-bold ph-lg"></i> + <i v-else-if="announcement.icon === 'warning'" class="ph-warning ph-bold ph-lg" style="color: var(--warn);"></i> + <i v-else-if="announcement.icon === 'error'" class="ph-x-circle ph-bold ph-lg" style="color: var(--error);"></i> + <i v-else-if="announcement.icon === 'success'" class="ph-check ph-bold ph-lg" style="color: var(--success);"></i> + </span> + <span>{{ announcement.title }}</span> </div> - </div> - <div v-if="tab !== 'past' && $i && !announcement.silence && !announcement.isRead" :class="$style.footer"> - <MkButton primary @click="read(announcement)"><i class="ph-check ph-bold ph-lg"></i> {{ i18n.ts.gotIt }}</MkButton> - </div> - </section> - </MkPagination> - </div> + <div :class="$style.content"> + <Mfm :text="announcement.text"/> + <img v-if="announcement.imageUrl" :src="announcement.imageUrl"/> + <div style="opacity: 0.7; font-size: 85%;"> + <MkTime :time="announcement.updatedAt ?? announcement.createdAt" mode="detail"/> + </div> + </div> + <div v-if="tab !== 'past' && $i && !announcement.silence && !announcement.isRead" :class="$style.footer"> + <MkButton primary @click="read(announcement)"><i class="ph-check ph-bold ph-lg"></i> {{ i18n.ts.gotIt }}</MkButton> + </div> + </section> + </MkPagination> + </div> + </MkHorizontalSwipe> </MkSpacer> </MkStickyContainer> </template> @@ -44,7 +46,9 @@ import { ref, computed } from 'vue'; import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; import MkInfo from '@/components/MkInfo.vue'; +import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { $i, updateAccount } from '@/account.js'; @@ -74,7 +78,7 @@ async function read(announcement) { const confirm = await os.confirm({ type: 'question', title: i18n.ts._announcement.readConfirmTitle, - text: i18n.t('_announcement.readConfirmText', { title: announcement.title }), + text: i18n.tsx._announcement.readConfirmText({ title: announcement.title }), }); if (confirm.canceled) return; } @@ -84,7 +88,7 @@ async function read(announcement) { a.isRead = true; return a; }); - os.api('i/read-announcement', { announcementId: announcement.id }); + misskeyApi('i/read-announcement', { announcementId: announcement.id }); updateAccount({ unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== announcement.id), }); @@ -102,10 +106,10 @@ const headerTabs = computed(() => [{ icon: 'ph-circle ph-bold ph-lg', }]); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.announcements, icon: 'ph-megaphone ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue index 9abf0b9776c6f096f3097cd3ef63939302622e46..3e8deff71183ebc807349c7811abd7a8c90e14d4 100644 --- a/packages/frontend/src/pages/antenna-timeline.vue +++ b/packages/frontend/src/pages/antenna-timeline.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -29,9 +29,10 @@ import * as Misskey from 'misskey-js'; import MkTimeline from '@/components/MkTimeline.vue'; import { scroll } from '@/scripts/scroll.js'; import * as os from '@/os.js'; -import { useRouter } from '@/router.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -73,7 +74,7 @@ function focus() { } watch(() => props.antennaId, async () => { - antenna.value = await os.api('antennas/show', { + antenna.value = await misskeyApi('antennas/show', { antennaId: props.antennaId, }); }, { immediate: true }); @@ -90,10 +91,10 @@ const headerActions = computed(() => antenna.value ? [{ const headerTabs = computed(() => []); -definePageMetadata(computed(() => antenna.value ? { - title: antenna.value.name, +definePageMetadata(() => ({ + title: antenna.value ? antenna.value.name : i18n.ts.antennas, icon: 'ph-flying-saucer ph-bold ph-lg', -} : null)); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/api-console.vue b/packages/frontend/src/pages/api-console.vue index dcdb5b8fe3f4eff5b6274fbe12b0d141b352e0e7..4d0cb2897f261a37a16ea23fb729e9a7851f228c 100644 --- a/packages/frontend/src/pages/api-console.vue +++ b/packages/frontend/src/pages/api-console.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -41,7 +41,7 @@ import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkTextarea from '@/components/MkTextarea.vue'; import MkSwitch from '@/components/MkSwitch.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; const body = ref('{}'); @@ -51,14 +51,14 @@ const sending = ref(false); const res = ref(''); const withCredential = ref(true); -os.api('endpoints').then(endpointResponse => { +misskeyApi('endpoints').then(endpointResponse => { endpoints.value = endpointResponse; }); function send() { sending.value = true; const requestBody = JSON5.parse(body.value); - os.api(endpoint.value as keyof Endpoints, requestBody, requestBody.i || (withCredential.value ? undefined : null)).then(resp => { + misskeyApi(endpoint.value as keyof Endpoints, requestBody, requestBody.i || (withCredential.value ? undefined : null)).then(resp => { sending.value = false; res.value = JSON5.stringify(resp, null, 2); }, err => { @@ -68,7 +68,7 @@ function send() { } function onEndpointChange() { - os.api('endpoint', { endpoint: endpoint.value }, withCredential.value ? undefined : null).then(resp => { + misskeyApi('endpoint', { endpoint: endpoint.value }, withCredential.value ? undefined : null).then(resp => { const endpointBody = {}; for (const p of resp.params) { endpointBody[p.name] = @@ -87,8 +87,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: 'API console', icon: 'ph-terminal-window ph-bold ph-lg-2', -}); +})); </script> diff --git a/packages/frontend/src/pages/auth.form.vue b/packages/frontend/src/pages/auth.form.vue index 8a17e5895df2161dfc4b8d5fce52012d5c5f2c5f..f4fb2ef4d5c20c2261e9dac2a61c016cb06b0465 100644 --- a/packages/frontend/src/pages/auth.form.vue +++ b/packages/frontend/src/pages/auth.form.vue @@ -1,17 +1,17 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <section> <div v-if="app.permission.length > 0"> - <p>{{ i18n.t('_auth.permission', { name }) }}</p> + <p>{{ i18n.tsx._auth.permission({ name }) }}</p> <ul> - <li v-for="p in app.permission" :key="p">{{ i18n.t(`_permissions.${p}`) }}</li> + <li v-for="p in app.permission" :key="p">{{ i18n.ts._permissions[p] }}</li> </ul> </div> - <div>{{ i18n.t('_auth.shareAccess', { name: `${name} (${app.id})` }) }}</div> + <div>{{ i18n.tsx._auth.shareAccess({ name: `${name} (${app.id})` }) }}</div> <div :class="$style.buttons"> <MkButton inline @click="cancel">{{ i18n.ts.cancel }}</MkButton> <MkButton inline primary @click="accept">{{ i18n.ts.accept }}</MkButton> @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed } from 'vue'; import * as Misskey from 'misskey-js'; import MkButton from '@/components/MkButton.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ @@ -44,7 +44,7 @@ const name = computed(() => { }); function cancel() { - os.api('auth/deny', { + misskeyApi('auth/deny', { token: props.session.token, }).then(() => { emit('denied'); @@ -52,7 +52,7 @@ function cancel() { } function accept() { - os.api('auth/accept', { + misskeyApi('auth/accept', { token: props.session.token, }).then(() => { emit('accepted'); diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue index d97e89842d435555de94443d88dec638cbcb5a6f..cb735a26d803ed7723475d1e890581fadef73bd4 100644 --- a/packages/frontend/src/pages/auth.vue +++ b/packages/frontend/src/pages/auth.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only <h1>{{ i18n.ts._auth.denied }}</h1> </div> <div v-if="state == 'accepted' && session"> - <h1>{{ session.app.isAuthorized ? i18n.t('already-authorized') : i18n.ts.allowed }}</h1> + <h1>{{ session.app.isAuthorized ? i18n.ts['already-authorized'] : i18n.ts.allowed }}</h1> <p v-if="session.app.callbackUrl"> {{ i18n.ts._auth.callback }} <MkEllipsis/> @@ -46,7 +46,7 @@ import { onMounted, ref, computed } from 'vue'; import * as Misskey from 'misskey-js'; import XForm from './auth.form.vue'; import MkSignin from '@/components/MkSignin.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { $i, login } from '@/account.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; @@ -96,13 +96,13 @@ onMounted(async () => { if (!$i) return; try { - session.value = await os.api('auth/session/show', { + session.value = await misskeyApi('auth/session/show', { token: props.token, }); // æ—¢ã«é€£æºã—ã¦ã„ãŸå ´åˆ if (session.value.app.isAuthorized) { - await os.api('auth/accept', { + await misskeyApi('auth/accept', { token: session.value.token, }); accepted(); @@ -118,10 +118,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts._auth.shareAccessTitle, icon: 'ph-squares-four ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue index 30b100a7fb3efa2e0e0310bc985904b1e7daaa26..41c87d362560eff87d8321705077035e0cbacc50 100644 --- a/packages/frontend/src/pages/avatar-decorations.vue +++ b/packages/frontend/src/pages/avatar-decorations.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -40,6 +40,7 @@ import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkTextarea from '@/components/MkTextarea.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkFolder from '@/components/MkFolder.vue'; @@ -59,11 +60,11 @@ function add() { function del(avatarDecoration) { os.confirm({ type: 'warning', - text: i18n.t('deleteAreYouSure', { x: avatarDecoration.name }), + text: i18n.tsx.deleteAreYouSure({ x: avatarDecoration.name }), }).then(({ canceled }) => { if (canceled) return; avatarDecorations.value = avatarDecorations.value.filter(x => x !== avatarDecoration); - os.api('admin/avatar-decorations/delete', avatarDecoration); + misskeyApi('admin/avatar-decorations/delete', avatarDecoration); }); } @@ -77,7 +78,7 @@ async function save(avatarDecoration) { } function load() { - os.api('admin/avatar-decorations/list').then(_avatarDecorations => { + misskeyApi('admin/avatar-decorations/list').then(_avatarDecorations => { avatarDecorations.value = _avatarDecorations; }); } @@ -93,8 +94,8 @@ const headerActions = computed(() => [{ const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.avatarDecorations, icon: 'ph-sparkle ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue index 912d02c7fc8f9b03123eaf6b7abff89e9f6a6ef5..ea4374dd3c5351a395edae1b993fa590cf1a8862 100644 --- a/packages/frontend/src/pages/channel-editor.vue +++ b/packages/frontend/src/pages/channel-editor.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -76,12 +76,13 @@ import MkInput from '@/components/MkInput.vue'; import MkColorInput from '@/components/MkColorInput.vue'; import { selectFile } from '@/scripts/select-file.js'; import * as os from '@/os.js'; -import { useRouter } from '@/router.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; import MkFolder from '@/components/MkFolder.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkTextarea from '@/components/MkTextarea.vue'; +import { useRouter } from '@/router/supplier.js'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); @@ -105,7 +106,7 @@ watch(() => bannerId.value, async () => { if (bannerId.value == null) { bannerUrl.value = null; } else { - bannerUrl.value = (await os.api('drive/files/show', { + bannerUrl.value = (await misskeyApi('drive/files/show', { fileId: bannerId.value, })).url; } @@ -114,7 +115,7 @@ watch(() => bannerId.value, async () => { async function fetchChannel() { if (props.channelId == null) return; - channel.value = await os.api('channels/show', { + channel.value = await misskeyApi('channels/show', { channelId: props.channelId, }); @@ -173,13 +174,13 @@ function save() { async function archive() { const { canceled } = await os.confirm({ type: 'warning', - title: i18n.t('channelArchiveConfirmTitle', { name: name.value }), + title: i18n.tsx.channelArchiveConfirmTitle({ name: name.value }), text: i18n.ts.channelArchiveConfirmDescription, }); if (canceled) return; - os.api('channels/update', { + misskeyApi('channels/update', { channelId: props.channelId, isArchived: true, }).then(() => { @@ -201,11 +202,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => props.channelId ? { - title: i18n.ts._channel.edit, - icon: 'ph-television ph-bold ph-lg', -} : { - title: i18n.ts._channel.create, +definePageMetadata(() => ({ + title: props.channelId ? i18n.ts._channel.edit : i18n.ts._channel.create, icon: 'ph-television ph-bold ph-lg', })); </script> diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue index b0873ea33615aef346eb8765f04e9849e45c8151..881acd01973ad5b4470678d875675e7c6eb71510 100644 --- a/packages/frontend/src/pages/channel.vue +++ b/packages/frontend/src/pages/channel.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,59 +7,61 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :contentMax="700" :class="$style.main"> - <div v-if="channel && tab === 'overview'" class="_gaps"> - <div class="_panel" :class="$style.bannerContainer"> - <XChannelFollowButton :channel="channel" :full="true" :class="$style.subscribe"/> - <MkButton v-if="favorited" v-tooltip="i18n.ts.unfavorite" asLike class="button" rounded primary :class="$style.favorite" @click="unfavorite()"><i class="ph-star ph-bold ph-lg"></i></MkButton> - <MkButton v-else v-tooltip="i18n.ts.favorite" asLike class="button" rounded :class="$style.favorite" @click="favorite()"><i class="ph-star ph-bold ph-lg"></i></MkButton> - <div :style="{ backgroundImage: channel.bannerUrl ? `url(${channel.bannerUrl})` : undefined }" :class="$style.banner"> - <div :class="$style.bannerStatus"> - <div><i class="ph-users ph-bold ph-lg ti-fw"></i><I18n :src="i18n.ts._channel.usersCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.usersCount }}</b></template></I18n></div> - <div><i class="ph-pencil ph-bold ph-lg ti-fw"></i><I18n :src="i18n.ts._channel.notesCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.notesCount }}</b></template></I18n></div> + <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <div v-if="channel && tab === 'overview'" key="overview" class="_gaps"> + <div class="_panel" :class="$style.bannerContainer"> + <XChannelFollowButton :channel="channel" :full="true" :class="$style.subscribe"/> + <MkButton v-if="favorited" v-tooltip="i18n.ts.unfavorite" asLike class="button" rounded primary :class="$style.favorite" @click="unfavorite()"><i class="ph-star ph-bold ph-lg"></i></MkButton> + <MkButton v-else v-tooltip="i18n.ts.favorite" asLike class="button" rounded :class="$style.favorite" @click="favorite()"><i class="ph-star ph-bold ph-lg"></i></MkButton> + <div :style="{ backgroundImage: channel.bannerUrl ? `url(${channel.bannerUrl})` : undefined }" :class="$style.banner"> + <div :class="$style.bannerStatus"> + <div><i class="ph-users ph-bold ph-lg"></i><I18n :src="i18n.ts._channel.usersCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.usersCount }}</b></template></I18n></div> + <div><i class="ph-pencil-simple ph-bold ph-lg"></i><I18n :src="i18n.ts._channel.notesCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.notesCount }}</b></template></I18n></div> + </div> + <div v-if="channel.isSensitive" :class="$style.sensitiveIndicator">{{ i18n.ts.sensitive }}</div> + <div :class="$style.bannerFade"></div> + </div> + <div v-if="channel.description" :class="$style.description"> + <Mfm :text="channel.description" :isNote="false"/> </div> - <div v-if="channel.isSensitive" :class="$style.sensitiveIndicator">{{ i18n.ts.sensitive }}</div> - <div :class="$style.bannerFade"></div> - </div> - <div v-if="channel.description" :class="$style.description"> - <Mfm :text="channel.description" :isNote="false"/> </div> - </div> - <MkFoldableSection> - <template #header><i class="ph-push-pin ph-bold ph-lg ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.pinnedNotes }}</template> - <div v-if="channel.pinnedNotes && channel.pinnedNotes.length > 0" class="_gaps"> - <MkNote v-for="note in channel.pinnedNotes" :key="note.id" class="_panel" :note="note"/> - </div> - </MkFoldableSection> - </div> - <div v-if="channel && tab === 'timeline'" class="_gaps"> - <MkInfo v-if="channel.isArchived" warn>{{ i18n.ts.thisChannelArchived }}</MkInfo> + <MkFoldableSection> + <template #header><i class="ph-push-pin ph-bold ph-lg" style="margin-right: 0.5em;"></i>{{ i18n.ts.pinnedNotes }}</template> + <div v-if="channel.pinnedNotes && channel.pinnedNotes.length > 0" class="_gaps"> + <MkNote v-for="note in channel.pinnedNotes" :key="note.id" class="_panel" :note="note"/> + </div> + </MkFoldableSection> + </div> + <div v-if="channel && tab === 'timeline'" key="timeline" class="_gaps"> + <MkInfo v-if="channel.isArchived" warn>{{ i18n.ts.thisChannelArchived }}</MkInfo> - <!-- スマホ・タブレットã®å ´åˆã€ã‚ーボードãŒè¡¨ç¤ºã•ã‚Œã‚‹ã¨æŠ•ç¨¿ãŒè¦‹ã¥ã‚‰ããªã‚‹ã®ã§ã€ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—å ´åˆã®ã¿è‡ªå‹•ã§ãƒ•ã‚©ãƒ¼ã‚«ã‚¹ã‚’当ã¦ã‚‹ --> - <MkPostForm v-if="$i && defaultStore.reactiveState.showFixedPostFormInChannel.value" :channel="channel" class="post-form _panel" fixed :autofocus="deviceKind === 'desktop'"/> + <!-- スマホ・タブレットã®å ´åˆã€ã‚ーボードãŒè¡¨ç¤ºã•ã‚Œã‚‹ã¨æŠ•ç¨¿ãŒè¦‹ã¥ã‚‰ããªã‚‹ã®ã§ã€ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—å ´åˆã®ã¿è‡ªå‹•ã§ãƒ•ã‚©ãƒ¼ã‚«ã‚¹ã‚’当ã¦ã‚‹ --> + <MkPostForm v-if="$i && defaultStore.reactiveState.showFixedPostFormInChannel.value" :channel="channel" class="post-form _panel" fixed :autofocus="deviceKind === 'desktop'"/> - <MkTimeline :key="channelId" src="channel" :channel="channelId" @before="before" @after="after" @note="miLocalStorage.setItemAsJson(`channelLastReadedAt:${channel.id}`, Date.now())"/> - </div> - <div v-else-if="tab === 'featured'"> - <MkNotes :pagination="featuredPagination"/> - </div> - <div v-else-if="tab === 'search'"> - <div class="_gaps"> - <div> - <MkInput v-model="searchQuery" @enter="search()"> - <template #prefix><i class="ph-magnifying-glass ph-bold ph-lg"></i></template> - </MkInput> - <MkButton primary rounded style="margin-top: 8px;" @click="search()">{{ i18n.ts.search }}</MkButton> + <MkTimeline :key="channelId" src="channel" :channel="channelId" @before="before" @after="after" @note="miLocalStorage.setItemAsJson(`channelLastReadedAt:${channel.id}`, Date.now())"/> + </div> + <div v-else-if="tab === 'featured'" key="featured"> + <MkNotes :pagination="featuredPagination"/> + </div> + <div v-else-if="tab === 'search'" key="search"> + <div class="_gaps"> + <div> + <MkInput v-model="searchQuery" @enter="search()"> + <template #prefix><i class="ph-magnifying-glass ph-bold ph-lg"></i></template> + </MkInput> + <MkButton primary rounded style="margin-top: 8px;" @click="search()">{{ i18n.ts.search }}</MkButton> + </div> + <MkNotes v-if="searchPagination" :key="searchKey" :pagination="searchPagination"/> </div> - <MkNotes v-if="searchPagination" :key="searchKey" :pagination="searchPagination"/> </div> - </div> + </MkHorizontalSwipe> </MkSpacer> <template #footer> <div :class="$style.footer"> <MkSpacer :contentMax="700" :marginMin="16" :marginMax="16"> <div class="_buttonsCenter"> - <MkButton inline rounded primary gradate @click="openPostForm()"><i class="ph-pencil ph-bold ph-lg"></i> {{ i18n.ts.postToTheChannel }}</MkButton> + <MkButton inline rounded primary gradate @click="openPostForm()"><i class="ph-pencil-simple ph-bold ph-lg"></i> {{ i18n.ts.postToTheChannel }}</MkButton> </div> </MkSpacer> </div> @@ -74,7 +76,7 @@ import MkPostForm from '@/components/MkPostForm.vue'; import MkTimeline from '@/components/MkTimeline.vue'; import XChannelFollowButton from '@/components/MkChannelFollowButton.vue'; import * as os from '@/os.js'; -import { useRouter } from '@/router.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { $i, iAmModerator } from '@/account.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -87,10 +89,12 @@ import { defaultStore } from '@/store.js'; import MkNote from '@/components/MkNote.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; +import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; import { PageHeaderItem } from '@/types/page-header.js'; import { isSupportShare } from '@/scripts/navigator.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { miLocalStorage } from '@/local-storage.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -99,6 +103,7 @@ const props = defineProps<{ }>(); const tab = ref('overview'); + const channel = ref<Misskey.entities.Channel | null>(null); const favorited = ref(false); const searchQuery = ref(''); @@ -113,7 +118,7 @@ const featuredPagination = computed(() => ({ })); watch(() => props.channelId, async () => { - channel.value = await os.api('channels/show', { + channel.value = await misskeyApi('channels/show', { channelId: props.channelId, }); favorited.value = channel.value.isFavorited ?? false; @@ -253,10 +258,10 @@ const headerTabs = computed(() => [{ icon: 'ph-magnifying-glass ph-bold ph-lg', }]); -definePageMetadata(computed(() => channel.value ? { - title: channel.value.name, +definePageMetadata(() => ({ + title: channel.value ? channel.value.name : i18n.ts.channel, icon: 'ph-television ph-bold ph-lg', -} : null)); +})); </script> <style lang="scss" module> @@ -267,6 +272,7 @@ definePageMetadata(computed(() => channel.value ? { .footer { -webkit-backdrop-filter: var(--blur, blur(15px)); backdrop-filter: var(--blur, blur(15px)); + background: var(--acrylicBg); border-top: solid 0.5px var(--divider); } diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue index 63d1e454a271b6cd814d5b5269646cf9f8fba66e..253b272d2a481c525872763e897565ed465abb21 100644 --- a/packages/frontend/src/pages/channels.vue +++ b/packages/frontend/src/pages/channels.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,44 +7,46 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :contentMax="700"> - <div v-if="tab === 'search'"> - <div class="_gaps"> - <MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search"> - <template #prefix><i class="ph-magnifying-glass ph-bold ph-lg"></i></template> - </MkInput> - <MkRadios v-model="searchType" @update:modelValue="search()"> - <option value="nameAndDescription">{{ i18n.ts._channel.nameAndDescription }}</option> - <option value="nameOnly">{{ i18n.ts._channel.nameOnly }}</option> - </MkRadios> - <MkButton large primary gradate rounded @click="search">{{ i18n.ts.search }}</MkButton> + <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <div v-if="tab === 'search'" key="search"> + <div class="_gaps"> + <MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search"> + <template #prefix><i class="ph-magnifying-glass ph-bold ph-lg"></i></template> + </MkInput> + <MkRadios v-model="searchType" @update:modelValue="search()"> + <option value="nameAndDescription">{{ i18n.ts._channel.nameAndDescription }}</option> + <option value="nameOnly">{{ i18n.ts._channel.nameOnly }}</option> + </MkRadios> + <MkButton large primary gradate rounded @click="search">{{ i18n.ts.search }}</MkButton> + </div> + + <MkFoldableSection v-if="channelPagination"> + <template #header>{{ i18n.ts.searchResult }}</template> + <MkChannelList :key="key" :pagination="channelPagination"/> + </MkFoldableSection> </div> - - <MkFoldableSection v-if="channelPagination"> - <template #header>{{ i18n.ts.searchResult }}</template> - <MkChannelList :key="key" :pagination="channelPagination"/> - </MkFoldableSection> - </div> - <div v-if="tab === 'featured'"> - <MkPagination v-slot="{items}" :pagination="featuredPagination"> - <MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> - </MkPagination> - </div> - <div v-else-if="tab === 'favorites'"> - <MkPagination v-slot="{items}" :pagination="favoritesPagination"> - <MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> - </MkPagination> - </div> - <div v-else-if="tab === 'following'"> - <MkPagination v-slot="{items}" :pagination="followingPagination"> - <MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> - </MkPagination> - </div> - <div v-else-if="tab === 'owned'"> - <MkButton class="new" @click="create()"><i class="ph-plus ph-bold ph-lg"></i></MkButton> - <MkPagination v-slot="{items}" :pagination="ownedPagination"> - <MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> - </MkPagination> - </div> + <div v-if="tab === 'featured'" key="featured"> + <MkPagination v-slot="{items}" :pagination="featuredPagination"> + <MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> + </MkPagination> + </div> + <div v-else-if="tab === 'favorites'" key="favorites"> + <MkPagination v-slot="{items}" :pagination="favoritesPagination"> + <MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> + </MkPagination> + </div> + <div v-else-if="tab === 'following'" key="following"> + <MkPagination v-slot="{items}" :pagination="followingPagination"> + <MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> + </MkPagination> + </div> + <div v-else-if="tab === 'owned'" key="owned"> + <MkButton class="new" @click="create()"><i class="ph-plus ph-bold ph-lg"></i></MkButton> + <MkPagination v-slot="{items}" :pagination="ownedPagination"> + <MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> + </MkPagination> + </div> + </MkHorizontalSwipe> </MkSpacer> </MkStickyContainer> </template> @@ -58,9 +60,10 @@ import MkInput from '@/components/MkInput.vue'; import MkRadios from '@/components/MkRadios.vue'; import MkButton from '@/components/MkButton.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; -import { useRouter } from '@/router.js'; +import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -146,11 +149,11 @@ const headerTabs = computed(() => [{ }, { key: 'owned', title: i18n.ts._channel.owned, - icon: 'ph-pencil-line ph-bold ph-lg', + icon: 'ph-pencil-simple-line ph-bold ph-lg', }]); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: i18n.ts.channel, icon: 'ph-television ph-bold ph-lg', -}))); +})); </script> diff --git a/packages/frontend/src/pages/clicker.vue b/packages/frontend/src/pages/clicker.vue index 8c1322d73231a84a9724502dea6df00a4eca6c79..679fb67d257f0ac5a2535b3a4a553a53dbe99411 100644 --- a/packages/frontend/src/pages/clicker.vue +++ b/packages/frontend/src/pages/clicker.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -16,10 +16,10 @@ SPDX-License-Identifier: AGPL-3.0-only import MkClickerGame from '@/components/MkClickerGame.vue'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -definePageMetadata({ +definePageMetadata(() => ({ title: 'ðŸªðŸ‘ˆ', icon: 'ph-cookie ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue index 9b5f0224ccbdd421e2d879acc951f3c713b22ff2..5c646889fd4257e63c5f3c5bec90309defbc4205 100644 --- a/packages/frontend/src/pages/clip.vue +++ b/packages/frontend/src/pages/clip.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -32,6 +32,7 @@ import MkNotes from '@/components/MkNotes.vue'; import { $i } from '@/account.js'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { url } from '@/config.js'; import MkButton from '@/components/MkButton.vue'; @@ -56,7 +57,7 @@ const pagination = { const isOwned = computed<boolean | null>(() => $i && clip.value && ($i.id === clip.value.userId)); watch(() => props.clipId, async () => { - clip.value = await os.api('clips/show', { + clip.value = await misskeyApi('clips/show', { clipId: props.clipId, }); favorited.value = clip.value.isFavorited; @@ -88,7 +89,7 @@ async function unfavorite() { } const headerActions = computed(() => clip.value && isOwned.value ? [{ - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', text: i18n.ts.edit, handler: async (): Promise<void> => { const { canceled, result } = await os.form(clip.value.name, { @@ -144,7 +145,7 @@ const headerActions = computed(() => clip.value && isOwned.value ? [{ handler: async (): Promise<void> => { const { canceled } = await os.confirm({ type: 'warning', - text: i18n.t('deleteAreYouSure', { x: clip.value.name }), + text: i18n.tsx.deleteAreYouSure({ x: clip.value.name }), }); if (canceled) return; @@ -156,10 +157,10 @@ const headerActions = computed(() => clip.value && isOwned.value ? [{ }, }] : null); -definePageMetadata(computed(() => clip.value ? { - title: clip.value.name, +definePageMetadata(() => ({ + title: clip.value ? clip.value.name : i18n.ts.clip, icon: 'ph-paperclip ph-bold ph-lg', -} : null)); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index bc2a268f34e2003c03751ab690a6bcbd203c0b80..1a745d6626422b9d332d865f581694b06763618c 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -82,6 +82,7 @@ import MkSwitch from '@/components/MkSwitch.vue'; import FormSplit from '@/components/form/split.vue'; import { selectFile } from '@/scripts/select-file.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -187,7 +188,7 @@ const menu = (ev: MouseEvent) => { icon: 'ph-download ph-bold ph-lg', text: i18n.ts.export, action: async () => { - os.api('export-custom-emojis', { + misskeyApi('export-custom-emojis', { }) .then(() => { os.alert({ @@ -206,7 +207,7 @@ const menu = (ev: MouseEvent) => { text: i18n.ts.import, action: async () => { const file = await selectFile(ev.currentTarget ?? ev.target); - os.api('admin/emoji/import-zip', { + misskeyApi('admin/emoji/import-zip', { fileId: file.id, }) .then(() => { @@ -314,10 +315,10 @@ const headerTabs = computed(() => [{ title: i18n.ts.remote, }]); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: i18n.ts.customEmojis, icon: 'ph-smiley ph-bold ph-lg', -}))); +})); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue index 69309b37f3ee7bc3ad7854ec6cbf57cc5c92b97a..872dd4d5cfe4b07677ca6bdb849aec0ec438e5df 100644 --- a/packages/frontend/src/pages/drive.file.info.vue +++ b/packages/frontend/src/pages/drive.file.info.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -14,11 +14,11 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.fileQuickActionsRoot"> <button class="_button" :class="$style.fileNameEditBtn" @click="rename()"> <h2 class="_nowrap" :class="$style.fileName">{{ file.name }}</h2> - <i class="ph-pencil ph-bold ph-lg" :class="$style.fileNameEditIcon"></i> + <i class="ph-pencil-simple ph-bold ph-lg" :class="$style.fileNameEditIcon"></i> </button> <div :class="$style.fileQuickActionsOthers"> <button v-tooltip="i18n.ts.createNoteFromTheFile" class="_button" :class="$style.fileQuickActionsOthersButton" @click="postThis()"> - <i class="ph-pencil ph-bold ph-lg"></i> + <i class="ph-pencil-simple ph-bold ph-lg"></i> </button> <button v-if="isImage" v-tooltip="i18n.ts.cropImage" class="_button" :class="$style.fileQuickActionsOthersButton" @click="crop()"> <i class="ph-crop ph-bold ph-lg"></i> @@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only <button class="_button" :class="$style.fileAltEditBtn" @click="describe()"> <MkKeyValue> <template #key>{{ i18n.ts.description }}</template> - <template #value>{{ file.comment ? file.comment : `(${i18n.ts.none})` }}<i class="ph-pencil ph-bold ph-lg" :class="$style.fileAltEditIcon"></i></template> + <template #value>{{ file.comment ? file.comment : `(${i18n.ts.none})` }}<i class="ph-pencil-simple ph-bold ph-lg" :class="$style.fileAltEditIcon"></i></template> </MkKeyValue> </button> <MkKeyValue :class="$style.fileMetaDataChildren"> @@ -79,7 +79,8 @@ import bytes from '@/filters/bytes.js'; import { infoImageUrl } from '@/instance.js'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; -import { useRouter } from '@/router.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -94,7 +95,7 @@ const isImage = computed(() => file.value?.type.startsWith('image/')); async function fetch() { fetching.value = true; - file.value = await os.api('drive/files/show', { + file.value = await misskeyApi('drive/files/show', { fileId: props.fileId, }).catch((err) => { console.error(err); @@ -179,7 +180,7 @@ async function deleteFile() { const { canceled } = await os.confirm({ type: 'warning', - text: i18n.t('driveFileDeleteConfirm', { name: file.value.name }), + text: i18n.tsx.driveFileDeleteConfirm({ name: file.value.name }), }); if (canceled) return; diff --git a/packages/frontend/src/pages/drive.file.notes.vue b/packages/frontend/src/pages/drive.file.notes.vue index ee1a0ee9b0e5fc96867c15d97da94ed9396da9d4..ca63d43747f03e9244bb8f4790542f731d37e14e 100644 --- a/packages/frontend/src/pages/drive.file.notes.vue +++ b/packages/frontend/src/pages/drive.file.notes.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/drive.file.vue b/packages/frontend/src/pages/drive.file.vue index b1bb84b48877da94d10c04581714301a7ebd7445..7cb2976e76628d0dd585a1cb4a9457f27c542942 100644 --- a/packages/frontend/src/pages/drive.file.vue +++ b/packages/frontend/src/pages/drive.file.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -9,13 +9,15 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/> </template> - <MkSpacer v-if="tab === 'info'" :contentMax="800"> - <XFileInfo :fileId="fileId"/> - </MkSpacer> + <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSpacer v-if="tab === 'info'" key="info" :contentMax="800"> + <XFileInfo :fileId="fileId"/> + </MkSpacer> - <MkSpacer v-else-if="tab === 'notes'" :contentMax="800"> - <XNotes :fileId="fileId"/> - </MkSpacer> + <MkSpacer v-else-if="tab === 'notes'" key="notes" :contentMax="800"> + <XNotes :fileId="fileId"/> + </MkSpacer> + </MkHorizontalSwipe> </MkStickyContainer> </template> @@ -23,6 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed, ref, defineAsyncComponent } from 'vue'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; +import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; const props = defineProps<{ fileId: string; @@ -42,11 +45,11 @@ const headerTabs = computed(() => [{ }, { key: 'notes', title: i18n.ts._fileViewer.attachedNotes, - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', }]); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: i18n.ts._fileViewer.title, icon: 'ph-file-text ph-bold ph-lg', -}))); +})); </script> diff --git a/packages/frontend/src/pages/drive.vue b/packages/frontend/src/pages/drive.vue index f3a3af677f4e900a3ace74360435bc034b07376a..7403986061b77a1ecd1eefa52336336c0ab25b86 100644 --- a/packages/frontend/src/pages/drive.vue +++ b/packages/frontend/src/pages/drive.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -22,9 +22,9 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: folder.value ? folder.value.name : i18n.ts.drive, icon: 'ph-cloud ph-bold ph-lg', hideHeader: true, -}))); +})); </script> diff --git a/packages/frontend/src/pages/drop-and-fusion.game.vue b/packages/frontend/src/pages/drop-and-fusion.game.vue new file mode 100644 index 0000000000000000000000000000000000000000..f1fa580c36004f41105a63b022aa2490a9657490 --- /dev/null +++ b/packages/frontend/src/pages/drop-and-fusion.game.vue @@ -0,0 +1,1499 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkSpacer :contentMax="800"> + <div :class="$style.root"> + <div v-if="!gameLoaded" :class="$style.loadingScreen"> + <div>{{ i18n.ts.loading }}<MkEllipsis/></div> + </div> + <!-- ↓ã«å¯¾ã—ã¦Transitionコンãƒãƒ¼ãƒãƒ³ãƒˆã‚’使ã†ã¨ä½•æ•…ã‹keyを指定ã—ã¦ã„ã¦ã‚‚ã‚ャッシュãŒåŠ¹ã‹ãšæ§˜ã€…ãªã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆãŒéƒ½åº¦å†è©•ä¾¡ã•ã‚Œã¦ãƒ‘フォーマンスãŒä½Žä¸‹ã™ã‚‹ --> + <div v-show="gameLoaded" class="_gaps_s"> + <div v-if="readyGo === 'ready'" :class="$style.readyGo_bg"> + </div> + <Transition + :enterActiveClass="$style.transition_zoom_enterActive" + :leaveActiveClass="$style.transition_zoom_leaveActive" + :enterFromClass="$style.transition_zoom_enterFrom" + :leaveToClass="$style.transition_zoom_leaveTo" + :moveClass="$style.transition_zoom_move" + mode="default" + > + <div v-if="readyGo === 'ready'" :class="$style.readyGo_ready"> + <img src="/client-assets/drop-and-fusion/ready.png" :class="$style.readyGo_img"/> + </div> + <div v-else-if="readyGo === 'go'" :class="$style.readyGo_go"> + <img src="/client-assets/drop-and-fusion/go.png" :class="$style.readyGo_img"/> + </div> + </Transition> + + <div :class="$style.header"> + <div class="_woodenFrame" :class="[$style.headerTitle]"> + <div class="_woodenFrameInner"> + <b>{{ i18n.ts.bubbleGame }}</b> + <div>- {{ gameMode.toUpperCase() }} -</div> + </div> + </div> + <div class="_woodenFrame _woodenFrameH"> + <div class="_woodenFrameInner"> + <MkButton inline small @click="hold">{{ i18n.ts._bubbleGame.hold }}</MkButton> + <img v-if="holdingStock" :src="getTextureImageUrl(holdingStock.mono)" style="width: 32px; margin-left: 8px; vertical-align: bottom;"/> + </div> + <div class="_woodenFrameInner" :class="$style.stock" style="text-align: center;"> + <TransitionGroup + :enterActiveClass="$style.transition_stock_enterActive" + :leaveActiveClass="$style.transition_stock_leaveActive" + :enterFromClass="$style.transition_stock_enterFrom" + :leaveToClass="$style.transition_stock_leaveTo" + :moveClass="$style.transition_stock_move" + > + <img v-for="x in stock" :key="x.id" :src="getTextureImageUrl(x.mono)" style="width: 32px; vertical-align: bottom;"/> + </TransitionGroup> + </div> + </div> + </div> + + <div ref="containerEl" :class="[$style.gameContainer, { [$style.gameOver]: isGameOver && !replaying }]" @contextmenu.stop.prevent @click.stop.prevent="onClick" @touchmove.stop.prevent="onTouchmove" @touchend="onTouchend" @mousemove="onMousemove"> + <img v-if="defaultStore.state.darkMode" src="/client-assets/drop-and-fusion/frame-dark.svg" :class="$style.mainFrameImg"/> + <img v-else src="/client-assets/drop-and-fusion/frame-light.svg" :class="$style.mainFrameImg"/> + <canvas ref="canvasEl" :class="$style.canvas"/> + <Transition + :enterActiveClass="$style.transition_combo_enterActive" + :leaveActiveClass="$style.transition_combo_leaveActive" + :enterFromClass="$style.transition_combo_enterFrom" + :leaveToClass="$style.transition_combo_leaveTo" + :moveClass="$style.transition_combo_move" + > + <div v-show="combo > 1" :class="$style.combo" :style="{ fontSize: `${100 + ((comboPrev - 2) * 15)}%` }">{{ comboPrev }} Chain!</div> + </Transition> + <div v-if="!isGameOver && !replaying && readyGo !== 'ready'" :class="$style.dropperContainer" :style="{ left: dropperX + 'px' }"> + <!--<img v-if="currentPick" src="/client-assets/drop-and-fusion/dropper.png" :class="$style.dropper" :style="{ left: dropperX + 'px' }"/>--> + <Transition + :enterActiveClass="$style.transition_picked_enterActive" + :leaveActiveClass="$style.transition_picked_leaveActive" + :enterFromClass="$style.transition_picked_enterFrom" + :leaveToClass="$style.transition_picked_leaveTo" + :moveClass="$style.transition_picked_move" + mode="out-in" + > + <img v-if="currentPick" :key="currentPick.id" :src="getTextureImageUrl(currentPick.mono)" :class="$style.currentMono" :style="{ marginBottom: -((currentPick?.mono.sizeY * viewScale) / 2) + 'px', left: -((currentPick?.mono.sizeX * viewScale) / 2) + 'px', width: `${currentPick?.mono.sizeX * viewScale}px` }"/> + </Transition> + <template v-if="dropReady && currentPick"> + <img src="/client-assets/drop-and-fusion/drop-arrow.svg" :class="$style.currentMonoArrow"/> + <div :class="$style.dropGuide"/> + </template> + </div> + <div v-if="isGameOver && !replaying" :class="$style.gameOverLabel"> + <div class="_gaps_s"> + <img src="/client-assets/drop-and-fusion/gameover.png" style="width: 200px; max-width: 100%; display: block; margin: auto; margin-bottom: -5px;"/> + <div>{{ i18n.ts._bubbleGame._score.score }}: <MkNumber :value="score"/>{{ getScoreUnit(gameMode) }}</div> + <div>{{ i18n.ts._bubbleGame._score.maxChain }}: <MkNumber :value="maxCombo"/></div> + <div v-if="gameMode === 'yen'"> + {{ i18n.ts._bubbleGame._score.scoreYen }}: + <I18n :src="i18n.ts._bubbleGame._score.yen" tag="b"> + <template #yen><MkNumber :value="yenTotal ?? score"/></template> + </I18n> + </div> + <I18n v-if="gameMode === 'sweets'" :src="i18n.ts._bubbleGame._score.scoreSweets" tag="div"> + <template #onigiriQtyWithUnit> + <I18n :src="i18n.ts._bubbleGame._score.estimatedQty" tag="b"> + <template #qty><MkNumber :value="score / 130"/></template> + </I18n> + </template> + </I18n> + </div> + </div> + <div v-if="replaying" :class="$style.replayIndicator"><span :class="$style.replayIndicatorText"><i class="ph-play ph-bold ph-lg"></i> {{ i18n.ts.replaying }}</span></div> + </div> + + <div v-if="replaying" class="_woodenFrame"> + <div class="_woodenFrameInner"> + <div style="background: #0004;"> + <div style="height: 10px; background: var(--accent); will-change: width;" :style="{ width: `${(currentFrame / endedAtFrame) * 100}%` }"></div> + </div> + </div> + <div class="_woodenFrameInner"> + <div class="_buttonsCenter"> + <MkButton @click="endReplay"><i class="ph-stop ph-bold ph-lg"></i> {{ i18n.ts.endReplay }}</MkButton> + <MkButton :primary="replayPlaybackRate === 4" @click="replayPlaybackRate = replayPlaybackRate === 4 ? 1 : 4"><i class="ph-skip-forward ph-bold ph-lg"></i> x4</MkButton> + <MkButton :primary="replayPlaybackRate === 16" @click="replayPlaybackRate = replayPlaybackRate === 16 ? 1 : 16"><i class="ph-skip-forward ph-bold ph-lg"></i> x16</MkButton> + </div> + </div> + </div> + + <div v-if="isGameOver" class="_woodenFrame"> + <div class="_woodenFrameInner"> + <div class="_buttonsCenter"> + <MkButton primary rounded @click="backToTitle">{{ i18n.ts.backToTitle }}</MkButton> + <MkButton primary rounded @click="replay">{{ i18n.ts.showReplay }}</MkButton> + <MkButton primary rounded @click="share">{{ i18n.ts.share }}</MkButton> + <MkButton rounded @click="exportLog">{{ i18n.ts.copyReplayData }}</MkButton> + </div> + </div> + </div> + + <div style="display: flex;"> + <div class="_woodenFrame" style="flex: 1; margin-right: 10px;"> + <div class="_woodenFrameInner"> + <div>{{ i18n.ts._bubbleGame._score.score }}: <MkNumber :value="score"/>{{ getScoreUnit(gameMode) }}</div> + <div>{{ i18n.ts._bubbleGame._score.highScore }}: <b v-if="highScore"><MkNumber :value="highScore"/>{{ getScoreUnit(gameMode) }}</b><b v-else>-</b></div> + <div v-if="gameMode === 'yen'"> + {{ i18n.ts._bubbleGame._score.scoreYen }}: + <I18n :src="i18n.ts._bubbleGame._score.yen" tag="b"> + <template #yen><MkNumber :value="yenTotal ?? score"/></template> + </I18n> + </div> + </div> + </div> + <div class="_woodenFrame" style="margin-left: auto;"> + <div class="_woodenFrameInner" style="text-align: center;"> + <div @click="showConfig = !showConfig"><i class="ph-gear ph-bold ph-lg"></i></div> + </div> + </div> + </div> + + <div v-if="showConfig" class="_woodenFrame"> + <div class="_woodenFrameInner"> + <div class="_gaps"> + <MkRange v-model="bgmVolume" :min="0" :max="1" :step="0.01" :textConverter="(v) => `${Math.floor(v * 100)}%`" :continuousUpdate="true" @dragEnded="(v) => updateSettings('bgmVolume', v)"> + <template #label>BGM {{ i18n.ts.volume }}</template> + </MkRange> + <MkRange v-model="sfxVolume" :min="0" :max="1" :step="0.01" :textConverter="(v) => `${Math.floor(v * 100)}%`" :continuousUpdate="true" @dragEnded="(v) => updateSettings('sfxVolume', v)"> + <template #label>{{ i18n.ts.sfx }} {{ i18n.ts.volume }}</template> + </MkRange> + </div> + </div> + </div> + + <div class="_woodenFrame"> + <div class="_woodenFrameInner"> + <div>FUSION RECIPE</div> + <div> + <div v-for="(mono, i) in game.monoDefinitions.sort((a, b) => a.level - b.level)" :key="mono.id" style="display: inline-block;"> + <img :src="getTextureImageUrl(mono)" style="width: 32px; vertical-align: bottom;"/> + <div v-if="i < game.monoDefinitions.length - 1" style="display: inline-block; margin-left: 4px; vertical-align: bottom;"><i class="ph-arrow-fat-right ph-bold ph-lg"></i></div> + </div> + </div> + </div> + </div> + + <div class="_woodenFrame"> + <div class="_woodenFrameInner"> + <MkButton v-if="!isGameOver && !replaying" full danger @click="surrender">{{ i18n.ts.surrender }}</MkButton> + <MkButton v-else full @click="restart">{{ i18n.ts.gameRetry }}</MkButton> + </div> + </div> + </div> + </div> +</MkSpacer> +</template> + +<script lang="ts" setup> +import { computed, onDeactivated, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'; +import * as Matter from 'matter-js'; +import * as Misskey from 'misskey-js'; +import { DropAndFusionGame, Mono } from 'misskey-bubble-game'; +import { definePageMetadata } from '@/scripts/page-metadata.js'; +import MkRippleEffect from '@/components/MkRippleEffect.vue'; +import * as os from '@/os.js'; +import MkNumber from '@/components/MkNumber.vue'; +import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue'; +import MkButton from '@/components/MkButton.vue'; +import { claimAchievement } from '@/scripts/achievements.js'; +import { defaultStore } from '@/store.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; +import { i18n } from '@/i18n.js'; +import { useInterval } from '@/scripts/use-interval.js'; +import { apiUrl } from '@/config.js'; +import { $i } from '@/account.js'; +import * as sound from '@/scripts/sound.js'; +import MkRange from '@/components/MkRange.vue'; +import copyToClipboard from '@/scripts/copy-to-clipboard.js'; + +type FrontendMonoDefinition = { + id: string; + img: string; + imgSizeX: number; + imgSizeY: number; + spriteScale: number; + sfxPitch: number; +}; + +const NORAML_MONOS: FrontendMonoDefinition[] = [{ + id: '9377076d-c980-4d83-bdaf-175bc58275b7', + sfxPitch: 0.25, + img: '/client-assets/drop-and-fusion/normal_monos/exploding_head.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: 'be9f38d2-b267-4b1a-b420-904e22e80568', + sfxPitch: 0.5, + img: '/client-assets/drop-and-fusion/normal_monos/face_with_symbols_on_mouth.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: 'beb30459-b064-4888-926b-f572e4e72e0c', + sfxPitch: 0.75, + img: '/client-assets/drop-and-fusion/normal_monos/cold_face.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: 'feab6426-d9d8-49ae-849c-048cdbb6cdf0', + sfxPitch: 1, + img: '/client-assets/drop-and-fusion/normal_monos/zany_face.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: 'd6d8fed6-6d18-4726-81a1-6cf2c974df8a', + sfxPitch: 1.5, + img: '/client-assets/drop-and-fusion/normal_monos/pleading_face.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: '249c728e-230f-4332-bbbf-281c271c75b2', + sfxPitch: 2, + img: '/client-assets/drop-and-fusion/normal_monos/face_with_open_mouth.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: '23d67613-d484-4a93-b71e-3e81b19d6186', + sfxPitch: 2.5, + img: '/client-assets/drop-and-fusion/normal_monos/smiling_face_with_sunglasses.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: '3cbd0add-ad7d-4685-bad0-29f6dddc0b99', + sfxPitch: 3, + img: '/client-assets/drop-and-fusion/normal_monos/grinning_squinting_face.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: '8f86d4f4-ee02-41bf-ad38-1ce0ae457fb5', + sfxPitch: 3.5, + img: '/client-assets/drop-and-fusion/normal_monos/smiling_face_with_hearts.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: '64ec4add-ce39-42b4-96cb-33908f3f118d', + sfxPitch: 4, + img: '/client-assets/drop-and-fusion/normal_monos/heart_suit.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}]; + +const YEN_MONOS: FrontendMonoDefinition[] = [{ + id: '880f9bd9-802f-4135-a7e1-fd0e0331f726', + sfxPitch: 0.25, + img: '/client-assets/drop-and-fusion/yen_monos/10000yen.png', + imgSizeX: 512, + imgSizeY: 256, + spriteScale: 0.97, +}, { + id: 'e807beb6-374a-4314-9cc2-aa5f17d96b6b', + sfxPitch: 0.5, + img: '/client-assets/drop-and-fusion/yen_monos/5000yen.png', + imgSizeX: 512, + imgSizeY: 256, + spriteScale: 0.97, +}, { + id: '033445b7-8f90-4fc9-beca-71a9e87cb530', + sfxPitch: 0.75, + img: '/client-assets/drop-and-fusion/yen_monos/2000yen.png', + imgSizeX: 512, + imgSizeY: 256, + spriteScale: 0.97, +}, { + id: '410a09ec-5f7f-46f6-b26f-cbca4ccbd091', + sfxPitch: 1, + img: '/client-assets/drop-and-fusion/yen_monos/1000yen.png', + imgSizeX: 512, + imgSizeY: 256, + spriteScale: 0.97, +}, { + id: '2aae82bc-3fa4-49ad-a6b5-94d888e809f5', + sfxPitch: 1.5, + img: '/client-assets/drop-and-fusion/yen_monos/500yen.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 0.97, +}, { + id: 'a619bd67-d08f-4cc0-8c7e-c8072a4950cd', + sfxPitch: 2, + img: '/client-assets/drop-and-fusion/yen_monos/100yen.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 0.97, +}, { + id: 'c1c5d8e4-17d6-4455-befd-12154d731faa', + sfxPitch: 2.5, + img: '/client-assets/drop-and-fusion/yen_monos/50yen.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 0.97, +}, { + id: '7082648c-e428-44c4-887a-25c07a8ebdd5', + sfxPitch: 3, + img: '/client-assets/drop-and-fusion/yen_monos/10yen.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 0.97, +}, { + id: '0d8d40d5-e6e0-4d26-8a95-b8d842363379', + sfxPitch: 3.5, + img: '/client-assets/drop-and-fusion/yen_monos/5yen.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 0.97, +}, { + id: '9dec1b38-d99d-40de-8288-37367b983d0d', + sfxPitch: 4, + img: '/client-assets/drop-and-fusion/yen_monos/1yen.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 0.97, +}]; + +const SQUARE_MONOS: FrontendMonoDefinition[] = [{ + id: 'f75fd0ba-d3d4-40a4-9712-b470e45b0525', + sfxPitch: 0.25, + img: '/client-assets/drop-and-fusion/square_monos/keycap_10.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: '7b70f4af-1c01-45fd-af72-61b1f01e03d1', + sfxPitch: 0.5, + img: '/client-assets/drop-and-fusion/square_monos/keycap_9.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: '41607ef3-b6d6-4829-95b6-3737bf8bb956', + sfxPitch: 0.75, + img: '/client-assets/drop-and-fusion/square_monos/keycap_8.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: '8a8310d2-0374-460f-bb50-ca9cd3ee3416', + sfxPitch: 1, + img: '/client-assets/drop-and-fusion/square_monos/keycap_7.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: '1092e069-fe1a-450b-be97-b5d477ec398c', + sfxPitch: 1.5, + img: '/client-assets/drop-and-fusion/square_monos/keycap_6.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: '2294734d-7bb8-4781-bb7b-ef3820abf3d0', + sfxPitch: 2, + img: '/client-assets/drop-and-fusion/square_monos/keycap_5.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: 'ea8a61af-e350-45f7-ba6a-366fcd65692a', + sfxPitch: 2.5, + img: '/client-assets/drop-and-fusion/square_monos/keycap_4.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: 'd0c74815-fc1c-4fbe-9953-c92e4b20f919', + sfxPitch: 3, + img: '/client-assets/drop-and-fusion/square_monos/keycap_3.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: 'd8fbd70e-611d-402d-87da-1a7fd8cd2c8d', + sfxPitch: 3.5, + img: '/client-assets/drop-and-fusion/square_monos/keycap_2.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}, { + id: '35e476ee-44bd-4711-ad42-87be245d3efd', + sfxPitch: 4, + img: '/client-assets/drop-and-fusion/square_monos/keycap_1.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 1.12, +}]; + +const SWEETS_MONOS: FrontendMonoDefinition[] = [{ + id: '77f724c0-88be-4aeb-8e1a-a00ed18e3844', + sfxPitch: 0.25, + img: '/client-assets/drop-and-fusion/sweets_monos/shortcake_color.svg', + imgSizeX: 32, + imgSizeY: 32, + spriteScale: 1, +}, { + id: 'f3468ef4-2e1e-4906-8795-f147f39f7e1f', + sfxPitch: 0.5, + img: '/client-assets/drop-and-fusion/sweets_monos/pancakes_color.svg', + imgSizeX: 32, + imgSizeY: 32, + spriteScale: 1, +}, { + id: 'bcb41129-6f2d-44ee-89d3-86eb2df564ba', + sfxPitch: 0.75, + img: '/client-assets/drop-and-fusion/sweets_monos/shaved_ice_color.svg', + imgSizeX: 32, + imgSizeY: 32, + spriteScale: 1, +}, { + id: 'f058e1ad-1981-409b-b3a7-302de0a43744', + sfxPitch: 1, + img: '/client-assets/drop-and-fusion/sweets_monos/soft_ice_cream_color.svg', + imgSizeX: 32, + imgSizeY: 32, + spriteScale: 1, +}, { + id: 'd22cfe38-5a3b-4b9c-a1a6-907930a3d732', + sfxPitch: 1.5, + img: '/client-assets/drop-and-fusion/sweets_monos/doughnut_color.svg', + imgSizeX: 32, + imgSizeY: 32, + spriteScale: 1, +}, { + id: '79867083-a073-427e-ae82-07a70d9f3b4f', + sfxPitch: 2, + img: '/client-assets/drop-and-fusion/sweets_monos/custard_color.svg', + imgSizeX: 32, + imgSizeY: 32, + spriteScale: 1, +}, { + id: '2e152a12-a567-4100-b4d4-d15d81ba47b1', + sfxPitch: 2.5, + img: '/client-assets/drop-and-fusion/sweets_monos/chocolate_bar_color.svg', + imgSizeX: 32, + imgSizeY: 32, + spriteScale: 1, +}, { + id: '12250376-2258-4716-8eec-b3a7239461fc', + sfxPitch: 3, + img: '/client-assets/drop-and-fusion/sweets_monos/lollipop_color.svg', + imgSizeX: 32, + imgSizeY: 32, + spriteScale: 1, +}, { + id: '4d4f2668-4be7-44a3-aa3a-856df6e25aa6', + sfxPitch: 3.5, + img: '/client-assets/drop-and-fusion/sweets_monos/candy_color.svg', + imgSizeX: 32, + imgSizeY: 32, + spriteScale: 1, +}, { + id: 'c9984b40-4045-44c3-b260-d47b7b4625b2', + sfxPitch: 4, + img: '/client-assets/drop-and-fusion/sweets_monos/cookie_color.svg', + imgSizeX: 32, + imgSizeY: 32, + spriteScale: 1, +}]; + +const props = defineProps<{ + gameMode: 'normal' | 'square' | 'yen' | 'sweets' | 'space'; + mute: boolean; +}>(); + +const emit = defineEmits<{ + (ev: 'end'): void; +}>(); + +const monoDefinitions = computed(() => { + return props.gameMode === 'normal' ? NORAML_MONOS : + props.gameMode === 'square' ? SQUARE_MONOS : + props.gameMode === 'yen' ? YEN_MONOS : + props.gameMode === 'sweets' ? SWEETS_MONOS : + props.gameMode === 'space' ? NORAML_MONOS : + [] as never; +}); + +function getScoreUnit(gameMode: string) { + return gameMode === 'normal' ? 'pt' : + gameMode === 'square' ? 'pt' : + gameMode === 'yen' ? '円' : + gameMode === 'sweets' ? 'kcal' : + '' as never; +} + +function getMonoRenderOptions(mono: Mono) { + const def = monoDefinitions.value.find(x => x.id === mono.id)!; + return { + + sprite: { + texture: def.img, + xScale: (mono.sizeX / def.imgSizeX) * def.spriteScale, + yScale: (mono.sizeY / def.imgSizeY) * def.spriteScale, + }, + + }; +} + +let viewScale = 1; +let seed: string = Date.now().toString(); +let containerElRect: DOMRect | null = null; +let logs: ReturnType<DropAndFusionGame['getLogs']> | null = null; +let endedAtFrame = 0; +let bgmNodes: ReturnType<typeof sound.createSourceNode> | null = null; +let renderer: Matter.Render | null = null; +let monoTextures: Record<string, Blob> = {}; +let monoTextureUrls: Record<string, string> = {}; +let tickRaf: number | null = null; +let game = new DropAndFusionGame({ + seed: seed, + gameMode: props.gameMode, + getMonoRenderOptions, +}); +attachGameEvents(); + +const containerEl = shallowRef<HTMLElement>(); +const canvasEl = shallowRef<HTMLCanvasElement>(); +const dropperX = ref(0); +const currentPick = shallowRef<{ id: string; mono: Mono } | null>(null); +const stock = shallowRef<{ id: string; mono: Mono }[]>([]); +const holdingStock = shallowRef<{ id: string; mono: Mono } | null>(null); +const score = ref(0); +const combo = ref(0); +const comboPrev = ref(0); +const maxCombo = ref(0); +const dropReady = ref(true); +const isGameOver = ref(false); +const gameLoaded = ref(false); +const readyGo = ref<'ready' | 'go' | null>('ready'); +const highScore = ref<number | null>(null); +const yenTotal = ref<number | null>(null); +const showConfig = ref(false); +const replaying = ref(false); +const replayPlaybackRate = ref(1); +const currentFrame = ref(0); +const bgmVolume = ref(defaultStore.state.dropAndFusion.bgmVolume); +const sfxVolume = ref(defaultStore.state.dropAndFusion.sfxVolume); + +watch(replayPlaybackRate, (newValue) => { + game.replayPlaybackRate = newValue; +}); + +watch(bgmVolume, (newValue) => { + if (bgmNodes) { + bgmNodes.gainNode.gain.value = props.mute ? 0 : newValue; + } +}); + +function createRendererInstance(game: DropAndFusionGame) { + return Matter.Render.create({ + engine: game.engine, + canvas: canvasEl.value!, + options: { + width: game.GAME_WIDTH, + height: game.GAME_HEIGHT, + background: 'transparent', // transparent to hide + wireframeBackground: 'transparent', // transparent to hide + wireframes: false, + showSleeping: false, + pixelRatio: Math.max(2, window.devicePixelRatio), + }, + }); +} + +function loadMonoTextures() { + async function loadSingleMonoTexture(mono: FrontendMonoDefinition) { + if (renderer == null) return; + + // Matter-js内ã«ã‚ャッシュãŒã‚ã‚‹å ´åˆã¯ã‚¹ã‚ップ + if (renderer.textures[mono.img]) return; + + let src = mono.img; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (monoTextureUrls[mono.img]) { + src = monoTextureUrls[mono.img]; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + } else if (monoTextures[mono.img]) { + src = URL.createObjectURL(monoTextures[mono.img]); + monoTextureUrls[mono.img] = src; + } else { + const res = await fetch(mono.img); + const blob = await res.blob(); + monoTextures[mono.img] = blob; + src = URL.createObjectURL(blob); + monoTextureUrls[mono.img] = src; + } + + const image = new Image(); + image.src = src; + renderer.textures[mono.img] = image; + } + + return Promise.all(monoDefinitions.value.map(x => loadSingleMonoTexture(x))); +} + +function getTextureImageUrl(mono: Mono) { + const def = monoDefinitions.value.find(x => x.id === mono.id)!; + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (monoTextureUrls[def.img]) { + return monoTextureUrls[def.img]; + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + } else if (monoTextures[def.img]) { + // Gameクラス内ã«ã‚ャッシュãŒã‚ã‚‹å ´åˆã¯ãれを使ㆠ+ const out = URL.createObjectURL(monoTextures[def.img]); + monoTextureUrls[def.img] = out; + return out; + } else { + return def.img; + } +} + +function tick() { + const hasNextTick = game.tick(); + if (hasNextTick) { + tickRaf = window.requestAnimationFrame(tick); + } else { + tickRaf = null; + } +} + +function tickReplay() { + let hasNextTick; + for (let i = 0; i < replayPlaybackRate.value; i++) { + const log = logs!.find(x => x.frame === game.frame); + if (log) { + switch (log.operation) { + case 'drop': { + game.drop(log.x); + break; + } + case 'hold': { + game.hold(); + break; + } + case 'surrender': { + game.surrender(); + break; + } + default: + break; + } + } + + hasNextTick = game.tick(); + currentFrame.value = game.frame; + if (!hasNextTick) break; + } + + if (hasNextTick) { + tickRaf = window.requestAnimationFrame(tickReplay); + } else { + tickRaf = null; + } +} + +async function start() { + renderer = createRendererInstance(game); + await loadMonoTextures(); + Matter.Render.lookAt(renderer, { + min: { x: 0, y: 0 }, + max: { x: game.GAME_WIDTH, y: game.GAME_HEIGHT }, + }); + Matter.Render.run(renderer); + game.start(); + window.requestAnimationFrame(tick); + + gameLoaded.value = true; + + window.setTimeout(() => { + readyGo.value = 'go'; + window.setTimeout(() => { + readyGo.value = null; + }, 1000); + }, 1500); +} + +function onClick(ev: MouseEvent) { + if (!containerElRect) return; + if (replaying.value) return; + const x = (ev.clientX - containerElRect.left) / viewScale; + game.drop(x); +} + +function onTouchend(ev: TouchEvent) { + if (!containerElRect) return; + if (replaying.value) return; + const x = (ev.changedTouches[0].clientX - containerElRect.left) / viewScale; + game.drop(x); +} + +function onMousemove(ev: MouseEvent) { + if (!containerElRect) return; + const x = (ev.clientX - containerElRect.left); + moveDropper(containerElRect, x); +} + +function onTouchmove(ev: TouchEvent) { + if (!containerElRect) return; + const x = (ev.touches[0].clientX - containerElRect.left); + moveDropper(containerElRect, x); +} + +function moveDropper(rect: DOMRect, x: number) { + dropperX.value = Math.min(rect.width * ((game.GAME_WIDTH - game.PLAYAREA_MARGIN) / game.GAME_WIDTH), Math.max(rect.width * (game.PLAYAREA_MARGIN / game.GAME_WIDTH), x)); +} + +function hold() { + game.hold(); +} + +async function surrender() { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.ts.areYouSure, + }); + if (canceled) return; + game.surrender(); +} + +async function restart() { + reset(); + game = new DropAndFusionGame({ + seed: seed, + gameMode: props.gameMode, + getMonoRenderOptions, + }); + attachGameEvents(); + await start(); +} + +function reset() { + dispose(); + seed = Date.now().toString(); + isGameOver.value = false; + replaying.value = false; + replayPlaybackRate.value = 1; + currentPick.value = null; + dropReady.value = true; + stock.value = []; + holdingStock.value = null; + score.value = 0; + combo.value = 0; + comboPrev.value = 0; + maxCombo.value = 0; + gameLoaded.value = false; + readyGo.value = null; +} + +function dispose() { + game.dispose(); + if (renderer) Matter.Render.stop(renderer); + if (tickRaf) { + window.cancelAnimationFrame(tickRaf); + } +} + +function backToTitle() { + emit('end'); +} + +function replay() { + replaying.value = true; + dispose(); + game = new DropAndFusionGame({ + seed: seed, + gameMode: props.gameMode, + getMonoRenderOptions, + }); + attachGameEvents(); + os.promiseDialog(loadMonoTextures(), async () => { + renderer = createRendererInstance(game); + Matter.Render.lookAt(renderer, { + min: { x: 0, y: 0 }, + max: { x: game.GAME_WIDTH, y: game.GAME_HEIGHT }, + }); + Matter.Render.run(renderer); + game.start(); + window.requestAnimationFrame(tickReplay); + }); +} + +function endReplay() { + replaying.value = false; + dispose(); +} + +function exportLog() { + if (!logs) return; + const data = JSON.stringify({ + v: game.GAME_VERSION, + m: props.gameMode, + s: seed, + d: new Date().toISOString(), + l: DropAndFusionGame.serializeLogs(logs), + }); + copyToClipboard(data); + os.success(); +} + +function updateSettings< + K extends keyof typeof defaultStore.state.dropAndFusion, + V extends typeof defaultStore.state.dropAndFusion[K], +>(key: K, value: V) { + const changes: { [P in K]?: V } = {}; + changes[key] = value; + defaultStore.set('dropAndFusion', { + ...defaultStore.state.dropAndFusion, + ...changes, + }); +} + +function loadImage(url: string) { + return new Promise<HTMLImageElement>(res => { + const img = new Image(); + img.src = url; + img.addEventListener('load', () => { + res(img); + }); + }); +} + +function getGameImageDriveFile() { + return new Promise<Misskey.entities.DriveFile | null>(res => { + const dcanvas = document.createElement('canvas'); + dcanvas.width = game.GAME_WIDTH; + dcanvas.height = game.GAME_HEIGHT; + const ctx = dcanvas.getContext('2d'); + if (!ctx || !canvasEl.value) return res(null); + Promise.all([ + loadImage('/client-assets/drop-and-fusion/frame-light.svg'), + loadImage('/client-assets/drop-and-fusion/logo.png'), + ]).then((images) => { + const [frame, logo] = images; + ctx.fillStyle = '#fff'; + ctx.fillRect(0, 0, game.GAME_WIDTH, game.GAME_HEIGHT); + + ctx.drawImage(frame, 0, 0, game.GAME_WIDTH, game.GAME_HEIGHT); + ctx.drawImage(canvasEl.value!, 0, 0, game.GAME_WIDTH, game.GAME_HEIGHT); + + ctx.fillStyle = '#000'; + ctx.font = '16px bold sans-serif'; + ctx.textBaseline = 'top'; + ctx.fillText(`SCORE: ${score.value.toLocaleString()}${getScoreUnit(props.gameMode)}`, 10, 10); + + ctx.globalAlpha = 0.7; + ctx.drawImage(logo, game.GAME_WIDTH * 0.55, 6, game.GAME_WIDTH * 0.45, game.GAME_WIDTH * 0.45 * (logo.height / logo.width)); + ctx.globalAlpha = 1; + + dcanvas.toBlob(blob => { + if (!blob) return res(null); + if ($i == null) return res(null); + const formData = new FormData(); + formData.append('file', blob); + formData.append('name', `bubble-game-${Date.now()}.png`); + formData.append('isSensitive', 'false'); + formData.append('i', $i.token); + if (defaultStore.state.uploadFolder) { + formData.append('folderId', defaultStore.state.uploadFolder); + } + + window.fetch(apiUrl + '/drive/files/create', { + method: 'POST', + body: formData, + }) + .then(response => response.json()) + .then(f => { + res(f); + }); + }, 'image/png'); + + dcanvas.remove(); + }); + }); +} + +async function share() { + const uploading = getGameImageDriveFile(); + os.promiseDialog(uploading); + const file = await uploading; + if (!file) return; + os.post({ + initialText: `#BubbleGame (${props.gameMode}) +SCORE: ${score.value.toLocaleString()}${getScoreUnit(props.gameMode)}`, + initialFiles: [file], + instant: true, + }); +} + +function attachGameEvents() { + game.addListener('changeScore', value => { + score.value = value; + }); + + game.addListener('changeCombo', value => { + if (value === 0) { + comboPrev.value = combo.value; + } else { + comboPrev.value = value; + } + maxCombo.value = Math.max(maxCombo.value, value); + combo.value = value; + }); + + game.addListener('changeStock', value => { + currentPick.value = JSON.parse(JSON.stringify(value[0])); + stock.value = JSON.parse(JSON.stringify(value.slice(1))); + }); + + game.addListener('changeHolding', value => { + holdingStock.value = value; + + if (!props.mute) { + sound.playUrl('/client-assets/drop-and-fusion/hold.mp3', { + volume: 0.5 * sfxVolume.value, + playbackRate: replayPlaybackRate.value, + }); + } + }); + + game.addListener('dropped', (x) => { + if (!props.mute) { + const panV = x - game.PLAYAREA_MARGIN; + const panW = game.GAME_WIDTH - game.PLAYAREA_MARGIN - game.PLAYAREA_MARGIN; + const pan = ((panV / panW) - 0.5) * 2; + if (props.gameMode === 'yen') { + sound.playUrl('/client-assets/drop-and-fusion/drop_yen.mp3', { + volume: sfxVolume.value, + pan, + playbackRate: replayPlaybackRate.value, + }); + } else { + sound.playUrl('/client-assets/drop-and-fusion/drop.mp3', { + volume: sfxVolume.value, + pan, + playbackRate: replayPlaybackRate.value, + }); + } + } + + if (replaying.value) return; + + dropReady.value = false; + window.setTimeout(() => { + if (!isGameOver.value) { + dropReady.value = true; + } + }, game.frameToMs(game.DROP_COOLTIME)); + }); + + game.addListener('fusioned', (x, y, nextMono, scoreDelta) => { + if (!canvasEl.value) return; + + const rect = canvasEl.value.getBoundingClientRect(); + const domX = rect.left + (x * viewScale); + const domY = rect.top + (y * viewScale); + const scoreUnit = getScoreUnit(props.gameMode); + os.popup(MkRippleEffect, { x: domX, y: domY }, {}, 'end'); + os.popup(MkPlusOneEffect, { x: domX, y: domY, value: scoreDelta + (scoreUnit === 'pt' ? '' : scoreUnit) }, {}, 'end'); + + if (nextMono) { + const def = monoDefinitions.value.find(x => x.id === nextMono.id)!; + if (!props.mute) { + const panV = x - game.PLAYAREA_MARGIN; + const panW = game.GAME_WIDTH - game.PLAYAREA_MARGIN - game.PLAYAREA_MARGIN; + const pan = ((panV / panW) - 0.5) * 2; + const pitch = def.sfxPitch; + if (props.gameMode === 'yen') { + sound.playUrl('/client-assets/drop-and-fusion/fusion_yen.mp3', { + volume: 0.25 * sfxVolume.value, + pan: pan, + playbackRate: (pitch / 4) * replayPlaybackRate.value, + }); + } else { + sound.playUrl('/client-assets/drop-and-fusion/fusion.mp3', { + volume: sfxVolume.value, + pan: pan, + playbackRate: pitch * replayPlaybackRate.value, + }); + } + } + } else { + if (!props.mute) { + // TODO: èžåˆå¾Œã®ãƒ¢ãƒŽãŒãªã„å ´åˆã§ã‚‚何らã‹ã®åŠ¹æžœéŸ³ã‚’å†ç”Ÿ + } + } + }); + + const minCollisionEnergyForSound = 2.5; + const maxCollisionEnergyForSound = 9; + const soundPitchMax = 4; + const soundPitchMin = 0.5; + + game.addListener('collision', (energy, bodyA, bodyB) => { + if (!props.mute && (energy > minCollisionEnergyForSound)) { + const volume = (Math.min(maxCollisionEnergyForSound, energy - minCollisionEnergyForSound) / maxCollisionEnergyForSound) / 4; + const panV = + bodyA.label === '_wall_' ? bodyB.position.x - game.PLAYAREA_MARGIN : + bodyB.label === '_wall_' ? bodyA.position.x - game.PLAYAREA_MARGIN : + ((bodyA.position.x + bodyB.position.x) / 2) - game.PLAYAREA_MARGIN; + const panW = game.GAME_WIDTH - game.PLAYAREA_MARGIN - game.PLAYAREA_MARGIN; + const pan = ((panV / panW) - 0.5) * 2; + const pitch = soundPitchMin + ((soundPitchMax - soundPitchMin) * (1 - (Math.min(10, energy) / 10))); + + if (props.gameMode === 'yen') { + sound.playUrl('/client-assets/drop-and-fusion/collision_yen.mp3', { + volume: volume * sfxVolume.value, + pan: pan, + playbackRate: Math.max(1, pitch) * replayPlaybackRate.value, + }); + } else { + sound.playUrl('/client-assets/drop-and-fusion/collision.mp3', { + volume: volume * sfxVolume.value, + pan: pan, + playbackRate: pitch * replayPlaybackRate.value, + }); + } + } + }); + + game.addListener('monoAdded', (mono) => { + if (replaying.value) return; + + // 実績関連 + if (mono.level === 10) { + claimAchievement('bubbleGameExplodingHead'); + + const monos = game.getActiveMonos(); + if (monos.filter(x => x.level === 10).length >= 2) { + claimAchievement('bubbleGameDoubleExplodingHead'); + } + } + }); + + game.addListener('gameOver', () => { + if (!props.mute) { + if (props.gameMode === 'yen') { + sound.playUrl('/client-assets/drop-and-fusion/gameover_yen.mp3', { + volume: 0.5 * sfxVolume.value, + }); + } else { + sound.playUrl('/client-assets/drop-and-fusion/gameover.mp3', { + volume: sfxVolume.value, + }); + } + } + + if (replaying.value) { + endReplay(); + return; + } + + logs = game.getLogs(); + endedAtFrame = game.frame; + currentPick.value = null; + dropReady.value = false; + isGameOver.value = true; + + misskeyApi('bubble-game/register', { + seed, + score: score.value, + gameMode: props.gameMode, + gameVersion: game.GAME_VERSION, + logs: DropAndFusionGame.serializeLogs(logs), + }); + + if (props.gameMode === 'yen') { + yenTotal.value = (yenTotal.value ?? 0) + score.value; + misskeyApi('i/registry/set', { + scope: ['dropAndFusionGame'], + key: 'yenTotal', + value: yenTotal.value, + }); + } + + if (score.value > (highScore.value ?? 0)) { + highScore.value = score.value; + + misskeyApi('i/registry/set', { + scope: ['dropAndFusionGame'], + key: 'highScore:' + props.gameMode, + value: highScore.value, + }); + } + }); +} + +useInterval(() => { + if (!canvasEl.value) return; + const actualCanvasWidth = canvasEl.value.getBoundingClientRect().width; + if (actualCanvasWidth === 0) return; + viewScale = actualCanvasWidth / game.GAME_WIDTH; + containerElRect = containerEl.value?.getBoundingClientRect() ?? null; +}, 1000, { immediate: false, afterMounted: true }); + +onMounted(async () => { + try { + highScore.value = await misskeyApi('i/registry/get', { + scope: ['dropAndFusionGame'], + key: 'highScore:' + props.gameMode, + }); + } catch (err) { + highScore.value = null; + } + + if (props.gameMode === 'yen') { + try { + yenTotal.value = await misskeyApi('i/registry/get', { + scope: ['dropAndFusionGame'], + key: 'yenTotal', + }); + } catch (err: any) { + if (err.code === 'NO_SUCH_KEY') { + // nop + } else { + os.alert({ + type: 'error', + text: i18n.ts.cannotLoad, + }); + return; + } + } + } + + /* + const getVerticesFromSvg = async (path: string) => { + const svgDoc = await fetch(path) + .then((response) => response.text()) + .then((svgString) => { + const parser = new DOMParser(); + return parser.parseFromString(svgString, 'image/svg+xml'); + }); + const pathDatas = svgDoc.querySelectorAll('path'); + if (!pathDatas) return; + const vertices = Array.from(pathDatas).map((pathData) => { + return Matter.Svg.pathToVertices(pathData); + }); + return vertices; + }; + + getVerticesFromSvg('/client-assets/drop-and-fusion/sweets_monos/verts/doughnut_color.svg').then((vertices) => { + console.log('doughnut_color', vertices); + }); + */ + + await start(); + + const bgmBuffer = await sound.loadAudio('/client-assets/drop-and-fusion/bgm_1.mp3'); + if (!bgmBuffer) return; + bgmNodes = sound.createSourceNode(bgmBuffer, { + volume: props.mute ? 0 : bgmVolume.value, + }); + if (!bgmNodes) return; + bgmNodes.soundSource.loop = true; + bgmNodes.soundSource.start(); +}); + +onUnmounted(() => { + dispose(); + bgmNodes?.soundSource.stop(); +}); + +onDeactivated(() => { + dispose(); + bgmNodes?.soundSource.stop(); +}); + +definePageMetadata(() => ({ + title: i18n.ts.bubbleGame, + icon: 'ph-orange-slice ph-bold ph-lg', +})); +</script> + +<style lang="scss" module> +.transition_zoom_move, +.transition_zoom_enterActive, +.transition_zoom_leaveActive { + transition: opacity 0.5s cubic-bezier(0,.5,.5,1), transform 0.5s cubic-bezier(0,.5,.5,1) !important; +} +.transition_zoom_enterFrom, +.transition_zoom_leaveTo { + opacity: 0; + transform: scale(0.8); +} + +.transition_stock_move, +.transition_stock_enterActive, +.transition_stock_leaveActive { + transition: opacity 0.4s cubic-bezier(0,.5,.5,1), transform 0.4s cubic-bezier(0,.5,.5,1) !important; +} +.transition_stock_enterFrom, +.transition_stock_leaveTo { + opacity: 0; + transform: scale(0.7); +} +.transition_stock_leaveActive { + position: absolute; +} + +.transition_picked_move, +.transition_picked_enterActive { + transition: opacity 0.5s cubic-bezier(0,.5,.5,1), transform 0.5s cubic-bezier(0,.5,.5,1) !important; +} +.transition_picked_leaveActive { + transition: all 0s !important; +} +.transition_picked_enterFrom, +.transition_picked_leaveTo { + opacity: 0; + transform: translateY(-50px); +} +.transition_picked_leaveActive { + position: absolute; +} + +.transition_combo_move, +.transition_combo_enterActive { + transition: all 0s !important; +} +.transition_combo_leaveActive { + transition: opacity 0.4s cubic-bezier(0,.5,.5,1), transform 0.4s cubic-bezier(0,.5,.5,1) !important; +} +.transition_combo_enterFrom, +.transition_combo_leaveTo { + opacity: 0; + transform: scale(0.7); +} +.transition_combo_leaveActive { + position: absolute; +} + +.root { + margin: 0 auto; + max-width: 600px; + user-select: none; + + * { + user-select: none; + } +} + +.loadingScreen { + text-align: center; + padding: 32px; +} + +.readyGo_bg { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 100; + backdrop-filter: blur(4px); +} + +.readyGo_ready, +.readyGo_go { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; + z-index: 101; + pointer-events: none; +} + +.readyGo_img { + display: block; + width: 250px; + max-width: 100%; +} + +.header { + position: relative; + z-index: 10; + display: grid; + grid-template-columns: 1fr; + grid-template-rows: auto auto; + gap: 8px; + + > .headerTitle { + text-align: center; + } + + @media (min-width: 500px) { + grid-template-columns: 1fr auto; + grid-template-rows: auto; + + > .headerTitle { + text-align: start; + } + } +} + +.mainFrameImg { + position: absolute; + top: 0; + left: 0; + width: 100%; + // ãªã‚“ã‹iOSã§ã¡ã‚‰ã¤ã + //filter: drop-shadow(0 6px 16px #0007); + pointer-events: none; + user-select: none; +} + +.canvas { + position: relative; + display: block; + z-index: 1; + width: 100% !important; + height: auto !important; + pointer-events: none; + user-select: none; +} + +.gameContainer { + position: relative; + margin-top: -20px; +} + +.stock { + pointer-events: none; + user-select: none; +} + +.combo { + position: absolute; + z-index: 3; + top: 50%; + width: 100%; + text-align: center; + font-weight: bold; + font-style: oblique; + color: #fff; + -webkit-text-stroke: 1px rgb(255, 145, 0); + text-shadow: 0 0 6px #0005; + pointer-events: none; + user-select: none; +} + +.dropperContainer { + position: absolute; + top: 0; + height: 100%; + z-index: 2; + pointer-events: none; + user-select: none; + will-change: left; +} + +.currentMono { + position: absolute; + display: block; + bottom: 88%; + z-index: 2; + filter: drop-shadow(0 6px 16px #0007); +} + +.dropper { + position: relative; + top: 0; + width: 70px; + margin-top: -10px; + margin-left: -30px; + z-index: 2; + filter: drop-shadow(0 6px 16px #0007); +} + +.currentMonoArrow { + position: absolute; + width: 20px; + bottom: 80%; + left: -10px; + z-index: 3; + animation: currentMonoArrow 2s ease infinite; +} + +.dropGuide { + position: absolute; + z-index: 3; + bottom: 0; + width: 3px; + margin-left: -2px; + height: 85%; + background: #f002; +} + +.gameOverLabel { + position: absolute; + z-index: 10; + top: 50%; + left: 0; + right: 0; + margin: auto; + width: calc(100% - 50px); + max-width: 320px; + padding: 16px; + box-sizing: border-box; + background: #0007; + border-radius: 16px; + color: #fff; + text-align: center; + font-weight: bold; +} + +.gameOver { + .canvas { + filter: grayscale(1); + } +} + +.replayIndicator { + position: absolute; + z-index: 10; + left: 10px; + bottom: 10px; + padding: 6px 8px; + color: #f00; + font-weight: bold; + background: #0008; + border-radius: 6px; + pointer-events: none; +} + +.replayIndicatorText { + animation: replayIndicator-blink 2s infinite; +} + +@keyframes replayIndicator-blink { + 0% { opacity: 1; } + 50% { opacity: 0; } + 100% { opacity: 1; } +} + +@keyframes currentMonoArrow { + 0% { transform: translateY(0); } + 25% { transform: translateY(-8px); } + 50% { transform: translateY(0); } + 75% { transform: translateY(-8px); } + 100% { transform: translateY(0); } +} +</style> diff --git a/packages/frontend/src/pages/drop-and-fusion.vue b/packages/frontend/src/pages/drop-and-fusion.vue new file mode 100644 index 0000000000000000000000000000000000000000..3ece281468f4742d3004517daea6fabfa8ea209c --- /dev/null +++ b/packages/frontend/src/pages/drop-and-fusion.vue @@ -0,0 +1,160 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<Transition + :enterActiveClass="$style.transition_zoom_enterActive" + :leaveActiveClass="$style.transition_zoom_leaveActive" + :enterFromClass="$style.transition_zoom_enterFrom" + :leaveToClass="$style.transition_zoom_leaveTo" + :moveClass="$style.transition_zoom_move" + mode="out-in" +> + <MkSpacer v-if="!gameStarted" :contentMax="800"> + <div :class="$style.root"> + <div class="_gaps"> + <div class="_woodenFrame" style="text-align: center;"> + <div class="_woodenFrameInner"> + <img src="/client-assets/drop-and-fusion/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/> + </div> + </div> + <div class="_woodenFrame" style="text-align: center;"> + <div class="_woodenFrameInner"> + <div class="_gaps" style="padding: 16px;"> + <MkSelect v-model="gameMode"> + <option value="normal">NORMAL</option> + <option value="square">SQUARE</option> + <option value="yen">YEN</option> + <option value="sweets">SWEETS</option> + <!--<option value="space">SPACE</option>--> + </MkSelect> + <MkButton primary gradate large rounded inline @click="start">{{ i18n.ts.start }}</MkButton> + </div> + </div> + <div class="_woodenFrameInner"> + <div class="_gaps" style="padding: 16px;"> + <div style="font-size: 90%;"><i class="ph-music-notes ph-bold ph-lg"></i> {{ i18n.ts.soundWillBePlayed }}</div> + <MkSwitch v-model="mute"> + <template #label>{{ i18n.ts.mute }}</template> + </MkSwitch> + </div> + </div> + </div> + <div class="_woodenFrame"> + <div class="_woodenFrameInner"> + <div class="_gaps_s" style="padding: 16px;"> + <div><b>{{ i18n.tsx.lastNDays({ n: 7 }) }} {{ i18n.ts.ranking }}</b> ({{ gameMode.toUpperCase() }})</div> + <div v-if="ranking" class="_gaps_s"> + <div v-for="r in ranking" :key="r.id" :class="$style.rankingRecord"> + <MkAvatar :link="true" style="width: 24px; height: 24px; margin-right: 4px;" :user="r.user"/> + <MkUserName :user="r.user" :nowrap="true"/> + <b style="margin-left: auto;">{{ r.score.toLocaleString() }} {{ getScoreUnit(gameMode) }}</b> + </div> + </div> + <div v-else>{{ i18n.ts.loading }}</div> + </div> + </div> + </div> + <div class="_woodenFrame"> + <div class="_woodenFrameInner" style="padding: 16px;"> + <div style="font-weight: bold;">{{ i18n.ts._bubbleGame.howToPlay }}</div> + <ol> + <li>{{ i18n.ts._bubbleGame._howToPlay.section1 }}</li> + <li>{{ i18n.ts._bubbleGame._howToPlay.section2 }}</li> + <li>{{ i18n.ts._bubbleGame._howToPlay.section3 }}</li> + </ol> + </div> + </div> + <div class="_woodenFrame"> + <div class="_woodenFrameInner"> + <div class="_gaps_s" style="padding: 16px;"> + <div><b>Credit</b></div> + <div> + <div>Ai-chan illustration: @poteriri@misskey.io</div> + <div>BGM: @ys@misskey.design</div> + </div> + </div> + </div> + </div> + </div> + </div> + </MkSpacer> + <XGame v-else :gameMode="gameMode" :mute="mute" @end="onGameEnd"/> +</Transition> +</template> + +<script lang="ts" setup> +import { computed, ref, watch } from 'vue'; +import XGame from './drop-and-fusion.game.vue'; +import { definePageMetadata } from '@/scripts/page-metadata.js'; +import MkButton from '@/components/MkButton.vue'; +import { i18n } from '@/i18n.js'; +import MkSelect from '@/components/MkSelect.vue'; +import MkSwitch from '@/components/MkSwitch.vue'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; + +const gameMode = ref<'normal' | 'square' | 'yen' | 'sweets' | 'space'>('normal'); +const gameStarted = ref(false); +const mute = ref(false); +const ranking = ref(null); + +watch(gameMode, async () => { + ranking.value = await misskeyApiGet('bubble-game/ranking', { gameMode: gameMode.value }); +}, { immediate: true }); + +function getScoreUnit(gameMode: string) { + return gameMode === 'normal' ? 'pt' : + gameMode === 'square' ? 'pt' : + gameMode === 'yen' ? '円' : + gameMode === 'sweets' ? 'kcal' : + gameMode === 'space' ? 'pt' : + '' as never; +} + +async function start() { + gameStarted.value = true; +} + +function onGameEnd() { + gameStarted.value = false; +} + +definePageMetadata(() => ({ + title: i18n.ts.bubbleGame, + icon: 'ph-game-controller ph-bold ph-lg', +})); +</script> + +<style lang="scss" module> +.transition_zoom_move, +.transition_zoom_enterActive, +.transition_zoom_leaveActive { + transition: opacity 0.5s cubic-bezier(0,.5,.5,1), transform 0.5s cubic-bezier(0,.5,.5,1) !important; +} +.transition_zoom_enterFrom, +.transition_zoom_leaveTo { + opacity: 0; + transform: scale(0.8); +} + +.root { + margin: 0 auto; + max-width: 600px; + user-select: none; + + * { + user-select: none; + } +} + +.rankingRecord { + display: flex; + line-height: 24px; + padding-top: 4px; + white-space: nowrap; + overflow: visible; + text-overflow: ellipsis; +} +</style> diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue index 82cfa92f6aa0240271567a48c3c50f9b749a2d41..64960fd063815dd15173c3f2ff0215267b011e62 100644 --- a/packages/frontend/src/pages/emoji-edit-dialog.vue +++ b/packages/frontend/src/pages/emoji-edit-dialog.vue @@ -1,13 +1,15 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkModalWindow - ref="dialog" - :width="400" - @close="dialog.close()" +<MkWindow + ref="windowEl" + :initialWidth="400" + :initialHeight="500" + :canResize="false" + @close="windowEl.close()" @closed="$emit('closed')" > <template v-if="emoji" #header>:{{ emoji.name }}:</template> @@ -39,9 +41,12 @@ SPDX-License-Identifier: AGPL-3.0-only </MkInput> <MkInput v-model="aliases" autocapitalize="off"> <template #label>{{ i18n.ts.tags }}</template> - <template #caption>{{ i18n.ts.setMultipleBySeparatingWithSpace }}</template> + <template #caption> + {{ i18n.ts.theKeywordWhenSearchingForCustomEmoji }}<br/> + {{ i18n.ts.setMultipleBySeparatingWithSpace }} + </template> </MkInput> - <MkInput v-model="license"> + <MkInput v-model="license" :mfmAutocomplete="true"> <template #label>{{ i18n.ts.license }}</template> </MkInput> <MkFolder> @@ -70,18 +75,19 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton primary rounded style="margin: 0 auto;" @click="done"><i class="ph-check ph-bold ph-lg"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> </div> </div> -</MkModalWindow> +</MkWindow> </template> <script lang="ts" setup> import { computed, watch, ref } from 'vue'; import * as Misskey from 'misskey-js'; -import MkModalWindow from '@/components/MkModalWindow.vue'; +import MkWindow from '@/components/MkWindow.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkFolder from '@/components/MkFolder.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { customEmojiCategories } from '@/custom-emojis.js'; import MkSwitch from '@/components/MkSwitch.vue'; @@ -92,7 +98,7 @@ const props = defineProps<{ emoji?: any, }>(); -const dialog = ref<InstanceType<typeof MkModalWindow> | null>(null); +const windowEl = ref<InstanceType<typeof MkWindow> | null>(null); const name = ref<string>(props.emoji ? props.emoji.name : ''); const category = ref<string>(props.emoji ? props.emoji.category : ''); const aliases = ref<string>(props.emoji ? props.emoji.aliases.join(' ') : ''); @@ -104,7 +110,7 @@ const rolesThatCanBeUsedThisEmojiAsReaction = ref<Misskey.entities.Role[]>([]); const file = ref<Misskey.entities.DriveFile>(); watch(roleIdsThatCanBeUsedThisEmojiAsReaction, async () => { - rolesThatCanBeUsedThisEmojiAsReaction.value = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.value.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null); + rolesThatCanBeUsedThisEmojiAsReaction.value = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.value.map((id) => misskeyApi('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null); }, { immediate: true }); const imgUrl = computed(() => file.value ? file.value.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null); @@ -123,13 +129,13 @@ async function changeImage(ev) { } async function addRole() { - const roles = await os.api('admin/roles/list'); + const roles = await misskeyApi('admin/roles/list'); const currentRoleIds = rolesThatCanBeUsedThisEmojiAsReaction.value.map(x => x.id); const { canceled, result: role } = await os.select({ items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ text: r.name, value: r })), }); - if (canceled) return; + if (canceled || role == null) return; rolesThatCanBeUsedThisEmojiAsReaction.value.push(role); } @@ -166,7 +172,7 @@ async function done() { }, }); - dialog.value.close(); + windowEl.value.close(); } else { const created = await os.apiWithDialog('admin/emoji/add', params); @@ -174,24 +180,24 @@ async function done() { created: created, }); - dialog.value.close(); + windowEl.value.close(); } } async function del() { const { canceled } = await os.confirm({ type: 'warning', - text: i18n.t('removeAreYouSure', { x: name.value }), + text: i18n.tsx.removeAreYouSure({ x: name.value }), }); if (canceled) return; - os.api('admin/emoji/delete', { + misskeyApi('admin/emoji/delete', { id: props.emoji.id, }).then(() => { emit('done', { deleted: true, }); - dialog.value.close(); + windowEl.value.close(); }); } </script> diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue index d94fe96fa2043d460229c76b4065ed70ed4eb66b..c9805af51b77d1654bdbb69974e2b668dbbce6b0 100644 --- a/packages/frontend/src/pages/emojis.emoji.vue +++ b/packages/frontend/src/pages/emojis.emoji.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -14,18 +14,15 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; import * as os from '@/os.js'; +import * as Misskey from 'misskey-js'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { i18n } from '@/i18n.js'; +import MkCustomEmojiDetailedDialog from '@/components/MkCustomEmojiDetailedDialog.vue'; const props = defineProps<{ - emoji: { - name: string; - aliases: string[]; - category: string; - url: string; - }; + emoji: Misskey.entities.EmojiSimple; }>(); function menu(ev) { @@ -42,12 +39,13 @@ function menu(ev) { }, { text: i18n.ts.info, icon: 'ph-info ph-bold ph-lg', - action: () => { - os.apiGet('emoji', { name: props.emoji.name }).then(res => { - os.alert({ - type: 'info', - text: `Name: ${res.name}\nAliases: ${res.aliases.join(' ')}\nCategory: ${res.category}\nisSensitive: ${res.isSensitive}\nlocalOnly: ${res.localOnly}\nLicense: ${res.license}\nURL: ${res.url}`, - }); + action: async () => { + os.popup(MkCustomEmojiDetailedDialog, { + emoji: await misskeyApiGet('emoji', { + name: props.emoji.name, + }) + }, { + anchor: ev.target, }); }, }], ev.currentTarget ?? ev.target); diff --git a/packages/frontend/src/pages/explore.featured.vue b/packages/frontend/src/pages/explore.featured.vue index 000371528efca3af26966d4f36132efa5f72be9b..b5c8e70166862aa1a3d2da81d44d84054ddce038 100644 --- a/packages/frontend/src/pages/explore.featured.vue +++ b/packages/frontend/src/pages/explore.featured.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/explore.roles.vue b/packages/frontend/src/pages/explore.roles.vue index d30e107e97c94ccd960e83bdb9a92cadab71aa4f..389cd23ad258fa4afcc5d2fddf704797c1ae6431 100644 --- a/packages/frontend/src/pages/explore.roles.vue +++ b/packages/frontend/src/pages/explore.roles.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -15,11 +15,11 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkRolePreview from '@/components/MkRolePreview.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const roles = ref<Misskey.entities.Role[] | null>(null); -os.api('roles/list').then(res => { +misskeyApi('roles/list').then(res => { roles.value = res.filter(x => x.target === 'manual').sort((a, b) => b.displayOrder - a.displayOrder); }); </script> diff --git a/packages/frontend/src/pages/explore.users.vue b/packages/frontend/src/pages/explore.users.vue index fbca2b8ede3e5536cc322be0dd8ef7bf2615cc61..c9ab5443b69bbe2292092dd3952a9f135d2a1dd1 100644 --- a/packages/frontend/src/pages/explore.users.vue +++ b/packages/frontend/src/pages/explore.users.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -68,7 +68,7 @@ import * as Misskey from 'misskey-js'; import MkUserList from '@/components/MkUserList.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkTab from '@/components/MkTab.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ @@ -123,14 +123,14 @@ const recentlyRegisteredUsersF = { endpoint: 'users', limit: 10, noPaging: true, sort: '+createdAt', } }; -os.api('hashtags/list', { +misskeyApi('hashtags/list', { sort: '+attachedLocalUsers', attachedToLocalUserOnly: true, limit: 30, }).then(tags => { tagsLocal.value = tags; }); -os.api('hashtags/list', { +misskeyApi('hashtags/list', { sort: '+attachedRemoteUsers', attachedToRemoteUserOnly: true, limit: 30, diff --git a/packages/frontend/src/pages/explore.vue b/packages/frontend/src/pages/explore.vue index 9693e2659804fad7da58d404bb7e4c1213feecc3..c599a290f77699e8ef225aeece624ee3ba760e34 100644 --- a/packages/frontend/src/pages/explore.vue +++ b/packages/frontend/src/pages/explore.vue @@ -1,22 +1,22 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> - <div> - <div v-if="tab === 'featured'"> + <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <div v-if="tab === 'featured'" key="featured"> <XFeatured/> </div> - <div v-else-if="tab === 'users'"> + <div v-else-if="tab === 'users'" key="users"> <XUsers/> </div> - <div v-else-if="tab === 'roles'"> + <div v-else-if="tab === 'roles'" key="roles"> <XRoles/> </div> - </div> + </MkHorizontalSwipe> </MkStickyContainer> </template> @@ -26,6 +26,7 @@ import XFeatured from './explore.featured.vue'; import XUsers from './explore.users.vue'; import XRoles from './explore.roles.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; +import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; @@ -59,8 +60,8 @@ const headerTabs = computed(() => [{ title: i18n.ts.roles, }]); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: i18n.ts.explore, icon: 'ph-hash ph-bold ph-lg', -}))); +})); </script> diff --git a/packages/frontend/src/pages/favorites.vue b/packages/frontend/src/pages/favorites.vue index 10f4a96a98fe731ccd129b373827635d3e052913..28278981943dcaf62b8c06a871a3ab1983789fef 100644 --- a/packages/frontend/src/pages/favorites.vue +++ b/packages/frontend/src/pages/favorites.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -38,10 +38,10 @@ const pagination = { limit: 10, }; -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.favorites, icon: 'ph-star ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue index 21e6d006137bd5e3a156b5d12e11f0bbbf2378de..53c8c789141c35093c43c65e86f642204d3ba075 100644 --- a/packages/frontend/src/pages/flash/flash-edit.vue +++ b/packages/frontend/src/pages/flash/flash-edit.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInput v-model="title"> <template #label>{{ i18n.ts._play.title }}</template> </MkInput> - <MkTextarea v-model="summary"> + <MkTextarea v-model="summary" :mfmAutocomplete="true" :mfmPreview="true"> <template #label>{{ i18n.ts._play.summary }}</template> </MkTextarea> <MkButton primary @click="selectPreset">{{ i18n.ts.selectFromPresets }}<i class="ph-caret-down ph-bold ph-lg"></i></MkButton> @@ -38,13 +38,14 @@ import { computed, ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkTextarea from '@/components/MkTextarea.vue'; import MkCodeEditor from '@/components/MkCodeEditor.vue'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; -import { useRouter } from '@/router.js'; +import { useRouter } from '@/router/supplier.js'; const PRESET_DEFAULT = `/// @ 0.16.0 @@ -369,7 +370,7 @@ const flash = ref<Misskey.entities.Flash | null>(null); const visibility = ref<Misskey.entities.FlashUpdateRequest['visibility']>('public'); if (props.id) { - flash.value = await os.api('flash/show', { + flash.value = await misskeyApi('flash/show', { flashId: props.id, }); } @@ -437,7 +438,7 @@ function show() { async function del() { const { canceled } = await os.confirm({ type: 'warning', - text: i18n.t('deleteAreYouSure', { x: flash.value.title }), + text: i18n.tsx.deleteAreYouSure({ x: flash.value.title }), }); if (canceled) return; @@ -451,9 +452,7 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => flash.value ? { - title: i18n.ts._play.edit + ': ' + flash.value.title, -} : { - title: i18n.ts._play.new, +definePageMetadata(() => ({ + title: flash.value ? `${i18n.ts._play.edit}: ${flash.value.title}` : i18n.ts._play.new, })); </script> diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue index 2b9346fcac20a4287d1bbc7eabc8ee5aa90e3cb0..7e56d3f51b22e953ff7db8a5400ba0d502771ee3 100644 --- a/packages/frontend/src/pages/flash/flash-index.vue +++ b/packages/frontend/src/pages/flash/flash-index.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,32 +7,34 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :contentMax="700"> - <div v-if="tab === 'featured'"> - <MkPagination v-slot="{items}" :pagination="featuredFlashsPagination"> - <div class="_gaps_s"> - <MkFlashPreview v-for="flash in items" :key="flash.id" :flash="flash"/> - </div> - </MkPagination> - </div> - - <div v-else-if="tab === 'my'"> - <div class="_gaps"> - <MkButton gradate rounded style="margin: 0 auto;" @click="create()"><i class="ph-plus ph-bold ph-lg"></i></MkButton> - <MkPagination v-slot="{items}" :pagination="myFlashsPagination"> + <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <div v-if="tab === 'featured'" key="featured"> + <MkPagination v-slot="{items}" :pagination="featuredFlashsPagination"> <div class="_gaps_s"> <MkFlashPreview v-for="flash in items" :key="flash.id" :flash="flash"/> </div> </MkPagination> </div> - </div> - <div v-else-if="tab === 'liked'"> - <MkPagination v-slot="{items}" :pagination="likedFlashsPagination"> - <div class="_gaps_s"> - <MkFlashPreview v-for="like in items" :key="like.flash.id" :flash="like.flash"/> + <div v-else-if="tab === 'my'" key="my"> + <div class="_gaps"> + <MkButton gradate rounded style="margin: 0 auto;" @click="create()"><i class="ph-plus ph-bold ph-lg"></i></MkButton> + <MkPagination v-slot="{items}" :pagination="myFlashsPagination"> + <div class="_gaps_s"> + <MkFlashPreview v-for="flash in items" :key="flash.id" :flash="flash"/> + </div> + </MkPagination> </div> - </MkPagination> - </div> + </div> + + <div v-else-if="tab === 'liked'" key="liked"> + <MkPagination v-slot="{items}" :pagination="likedFlashsPagination"> + <div class="_gaps_s"> + <MkFlashPreview v-for="like in items" :key="like.flash.id" :flash="like.flash"/> + </div> + </MkPagination> + </div> + </MkHorizontalSwipe> </MkSpacer> </MkStickyContainer> </template> @@ -42,9 +44,10 @@ import { computed, ref } from 'vue'; import MkFlashPreview from '@/components/MkFlashPreview.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; -import { useRouter } from '@/router.js'; +import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -80,15 +83,15 @@ const headerTabs = computed(() => [{ }, { key: 'my', title: i18n.ts._play.my, - icon: 'ph-pencil-line ph-bold ph-lg', + icon: 'ph-pencil-simple-line ph-bold ph-lg', }, { key: 'liked', title: i18n.ts._play.liked, icon: 'ph-heart ph-bold ph-lg', }]); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: 'Play', icon: 'ph-play ph-bold ph-lg', -}))); +})); </script> diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue index 5fae1248e9cbd0020f208ca2c10273c8f16a546a..cbb52a2e2315017336ee05b5d8bbf721a439eb01 100644 --- a/packages/frontend/src/pages/flash/flash.vue +++ b/packages/frontend/src/pages/flash/flash.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-else :class="$style.ready"> <div class="_panel main"> <div class="title">{{ flash.title }}</div> - <div class="summary">{{ flash.summary }}</div> + <div class="summary"><Mfm :text="flash.summary"/></div> <MkButton class="start" gradate rounded large @click="start">Play</MkButton> <div class="info"> <span v-tooltip="i18n.ts.numberOfLikes"><i class="ph-heart ph-bold ph-lg"></i> {{ flash.likedCount }}</span> @@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #icon><i class="ph-code ph-bold ph-lg"></i></template> <template #label>{{ i18n.ts._play.viewSource }}</template> - <MkCode :code="flash.script" lang="is" :inline="false" class="_monospace"/> + <MkCode :code="flash.script" lang="is" class="_monospace"/> </MkFolder> <div :class="$style.footer"> <Mfm :text="`By @${flash.user.username}`"/> @@ -62,12 +62,13 @@ import * as Misskey from 'misskey-js'; import { Interpreter, Parser, values } from '@syuilo/aiscript'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { url } from '@/config.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkAsUi from '@/components/MkAsUi.vue'; import { AsUiComponent, AsUiRoot, registerAsUiLib } from '@/scripts/aiscript/ui.js'; -import { createAiScriptEnv } from '@/scripts/aiscript/api.js'; +import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js'; import MkFolder from '@/components/MkFolder.vue'; import MkCode from '@/components/MkCode.vue'; import { defaultStore } from '@/store.js'; @@ -84,7 +85,7 @@ const error = ref<any>(null); function fetchFlash() { flash.value = null; - os.api('flash/show', { + misskeyApi('flash/show', { flashId: props.id, }).then(_flash => { flash.value = _flash; @@ -162,15 +163,7 @@ async function run() { THIS_ID: values.STR(flash.value.id), THIS_URL: values.STR(`${url}/play/${flash.value.id}`), }, { - in: (q) => { - return new Promise(ok => { - os.inputText({ - title: q, - }).then(({ result: a }) => { - ok(a ?? ''); - }); - }); - }, + in: aiScriptReadline, out: (value) => { // nop }, @@ -212,15 +205,17 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => flash.value ? { - title: flash.value.title, - avatar: flash.value.user, - path: `/play/${flash.value.id}`, - share: { - title: flash.value.title, - text: flash.value.summary, - }, -} : null)); +definePageMetadata(() => ({ + title: flash.value ? flash.value.title : 'Play', + ...flash.value ? { + avatar: flash.value.user, + path: `/play/${flash.value.id}`, + share: { + title: flash.value.title, + text: flash.value.summary, + }, + } : {}, +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue index d7506642212bf761e234269046ee05ee53b72d91..4cdfe28916c457b58e930eb00bbf6031ed69b2d5 100644 --- a/packages/frontend/src/pages/follow-requests.vue +++ b/packages/frontend/src/pages/follow-requests.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -41,7 +41,7 @@ import { shallowRef, computed } from 'vue'; import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; import { userPage, acct } from '@/filters/user.js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { infoImageUrl } from '@/instance.js'; @@ -54,13 +54,13 @@ const pagination = { }; function accept(user) { - os.api('following/requests/accept', { userId: user.id }).then(() => { + misskeyApi('following/requests/accept', { userId: user.id }).then(() => { paginationComponent.value.reload(); }); } function reject(user) { - os.api('following/requests/reject', { userId: user.id }).then(() => { + misskeyApi('following/requests/reject', { userId: user.id }).then(() => { paginationComponent.value.reload(); }); } @@ -69,10 +69,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: i18n.ts.followRequests, icon: 'ph-user-plus ph-bold ph-lg', -}))); +})); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/pages/follow.vue b/packages/frontend/src/pages/follow.vue index a0a4a480b577d9ef48a55941433af5fc164a992d..247b0ac639a0a797dfc4d239af344ad4bda26a8b 100644 --- a/packages/frontend/src/pages/follow.vue +++ b/packages/frontend/src/pages/follow.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -12,14 +12,15 @@ SPDX-License-Identifier: AGPL-3.0-only import { } from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; -import { mainRouter } from '@/router.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; -import { defaultStore } from "@/store.js"; +import { defaultStore } from '@/store.js'; +import { mainRouter } from '@/router/main.js'; async function follow(user): Promise<void> { const { canceled } = await os.confirm({ type: 'question', - text: i18n.t('followConfirm', { name: user.name || user.username }), + text: i18n.tsx.followConfirm({ name: user.name || user.username }), }); if (canceled) { @@ -42,7 +43,7 @@ if (acct == null) { let promise; if (acct.startsWith('https://')) { - promise = os.api('ap/show', { + promise = misskeyApi('ap/show', { uri: acct, }); promise.then(res => { @@ -60,7 +61,7 @@ if (acct.startsWith('https://')) { } }); } else { - promise = os.api('users/show', Misskey.acct.parse(acct)); + promise = misskeyApi('users/show', Misskey.acct.parse(acct)); promise.then(user => { follow(user); }); diff --git a/packages/frontend/src/pages/gallery/edit.vue b/packages/frontend/src/pages/gallery/edit.vue index 857317c48fefb69da9e662f8214cf480a7fbd55b..d2fe271b0f6ab08c4c4d63df74679486658eb510 100644 --- a/packages/frontend/src/pages/gallery/edit.vue +++ b/packages/frontend/src/pages/gallery/edit.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -47,9 +47,10 @@ import MkSwitch from '@/components/MkSwitch.vue'; import FormSuspense from '@/components/form/suspense.vue'; import { selectFiles } from '@/scripts/select-file.js'; import * as os from '@/os.js'; -import { useRouter } from '@/router.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -107,7 +108,7 @@ async function del() { } watch(() => props.postId, () => { - init.value = () => props.postId ? os.api('gallery/posts/show', { + init.value = () => props.postId ? misskeyApi('gallery/posts/show', { postId: props.postId, }).then(post => { files.value = post.files ?? []; @@ -121,12 +122,9 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => props.postId ? { - title: i18n.ts.edit, - icon: 'ph-pencil ph-bold ph-lg', -} : { - title: i18n.ts.postToGallery, - icon: 'ph-pencil ph-bold ph-lg', +definePageMetadata(() => ({ + title: props.postId ? i18n.ts.edit : i18n.ts.postToGallery, + icon: 'ph-pencil-simple ph-bold ph-lg', })); </script> diff --git a/packages/frontend/src/pages/gallery/index.vue b/packages/frontend/src/pages/gallery/index.vue index 936d9b839307b6eb2ec935cb55192f46cc37a3a8..96979250bd40c4c06990fa268c30db6475b57ffb 100644 --- a/packages/frontend/src/pages/gallery/index.vue +++ b/packages/frontend/src/pages/gallery/index.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,8 +7,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :contentMax="1400"> - <div class="_root"> - <div v-if="tab === 'explore'"> + <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <div v-if="tab === 'explore'" key="explore"> <MkFoldableSection class="_margin"> <template #header><i class="ph-clock ph-bold ph-lg"></i>{{ i18n.ts.recentPosts }}</template> <MkPagination v-slot="{items}" :pagination="recentPostsPagination" :disableAutoLoad="true"> @@ -26,14 +26,14 @@ SPDX-License-Identifier: AGPL-3.0-only </MkPagination> </MkFoldableSection> </div> - <div v-else-if="tab === 'liked'"> + <div v-else-if="tab === 'liked'" key="liked"> <MkPagination v-slot="{items}" :pagination="likedPostsPagination"> <div :class="$style.items"> <MkGalleryPostPreview v-for="like in items" :key="like.id" :post="like.post" class="post"/> </div> </MkPagination> </div> - <div v-else-if="tab === 'my'"> + <div v-else-if="tab === 'my'" key="my"> <MkA to="/gallery/new" class="_link" style="margin: 16px;"><i class="ph-plus ph-bold ph-lg"></i> {{ i18n.ts.postToGallery }}</MkA> <MkPagination v-slot="{items}" :pagination="myPostsPagination"> <div :class="$style.items"> @@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkPagination> </div> - </div> + </MkHorizontalSwipe> </MkSpacer> </MkStickyContainer> </template> @@ -51,9 +51,10 @@ import { watch, ref, computed } from 'vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue'; +import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; -import { useRouter } from '@/router.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -115,13 +116,13 @@ const headerTabs = computed(() => [{ }, { key: 'my', title: i18n.ts._gallery.my, - icon: 'ph-pencil-line ph-bold ph-lg', + icon: 'ph-pencil-simple-line ph-bold ph-lg', }]); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.gallery, icon: 'ph-images-square ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue index 54a8790ef90f0310fe271796236bd02df0f40a17..1511928d55009c72235aa63173bcb84713c3ef97 100644 --- a/packages/frontend/src/pages/gallery/post.vue +++ b/packages/frontend/src/pages/gallery/post.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton v-else v-tooltip="i18n.ts._gallery.like" class="button" @click="like()"><i class="ph-heart ph-bold ph-lg"></i><span v-if="post.likedCount > 0" class="count">{{ post.likedCount }}</span></MkButton> </div> <div class="other"> - <button v-if="$i && $i.id === post.user.id" v-tooltip="i18n.ts.edit" v-click-anime class="_button" @click="edit"><i class="ph-pencil ph-bold ph-lg ti-fw"></i></button> + <button v-if="$i && $i.id === post.user.id" v-tooltip="i18n.ts.edit" v-click-anime class="_button" @click="edit"><i class="ph-pencil-simple ph-bold ph-lg ti-fw"></i></button> <button v-tooltip="i18n.ts.shareWithNote" v-click-anime class="_button" @click="shareWithNote"><i class="ph-repeat ph-bold ph-lg ti-fw"></i></button> <button v-tooltip="i18n.ts.copyLink" v-click-anime class="_button" @click="copyLink"><i class="ph-share-network ph-bold ph-lg ti-fw"></i></button> <button v-if="isSupportShare()" v-tooltip="i18n.ts.share" v-click-anime class="_button" @click="share"><i class="ph-share-network ph-bold ph-lg ti-fw"></i></button> @@ -66,18 +66,19 @@ import { computed, watch, ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import MkContainer from '@/components/MkContainer.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue'; import MkFollowButton from '@/components/MkFollowButton.vue'; import { url } from '@/config.js'; -import { useRouter } from '@/router.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { defaultStore } from '@/store.js'; import { $i } from '@/account.js'; import { isSupportShare } from '@/scripts/navigator.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -97,7 +98,7 @@ const otherPostsPagination = { function fetchPost() { post.value = null; - os.api('gallery/posts/show', { + misskeyApi('gallery/posts/show', { postId: props.postId, }).then(_post => { post.value = _post; @@ -155,17 +156,19 @@ function edit() { watch(() => props.postId, fetchPost, { immediate: true }); const headerActions = computed(() => [{ - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', text: i18n.ts.edit, handler: edit, }]); const headerTabs = computed(() => []); -definePageMetadata(computed(() => post.value ? { - title: post.value.title, - avatar: post.value.user, -} : null)); +definePageMetadata(() => ({ + title: post.value ? post.value.title : i18n.ts.gallery, + ...post.value ? { + avatar: post.value.user, + } : {}, +})); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/pages/games.vue b/packages/frontend/src/pages/games.vue new file mode 100644 index 0000000000000000000000000000000000000000..2822fbde89d6d44444bea78872f4f9f449ab3036 --- /dev/null +++ b/packages/frontend/src/pages/games.vue @@ -0,0 +1,34 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkStickyContainer> + <template #header><MkPageHeader/></template> + <MkSpacer :contentMax="800"> + <div class="_gaps"> + <div class="_panel"> + <MkA to="/bubble-game"> + <img src="/client-assets/drop-and-fusion/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/> + </MkA> + </div> + <div class="_panel"> + <MkA to="/reversi"> + <img src="/client-assets/reversi/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/> + </MkA> + </div> + </div> + </MkSpacer> +</MkStickyContainer> +</template> + +<script lang="ts" setup> +import { i18n } from '@/i18n.js'; +import { definePageMetadata } from '@/scripts/page-metadata.js'; + +definePageMetadata(() => ({ + title: 'Misskey Games', + icon: 'ph-game-controller ph-bold ph-lg', +})); +</script> diff --git a/packages/frontend/src/pages/install-extentions.vue b/packages/frontend/src/pages/install-extensions.vue similarity index 98% rename from packages/frontend/src/pages/install-extentions.vue rename to packages/frontend/src/pages/install-extensions.vue index 7e6c75ac9975fdf7404d9681914d25d058be1ff6..32f6fbd1853b6d2a2680d61ab65f6de5e0cdee4f 100644 --- a/packages/frontend/src/pages/install-extentions.vue +++ b/packages/frontend/src/pages/install-extensions.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -105,6 +105,7 @@ import MkInfo from '@/components/MkInfo.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { AiScriptPluginMeta, parsePluginMeta, installPlugin } from '@/scripts/install-plugin.js'; import { parseThemeCode, installTheme } from '@/scripts/install-theme.js'; import { unisonReload } from '@/scripts/unison-reload.js'; @@ -159,7 +160,7 @@ async function fetch() { uiPhase.value = 'error'; return; } - const res = await os.api('fetch-external-resources', { + const res = await misskeyApi('fetch-external-resources', { url: url.value, hash: hash.value, }).catch((err) => { @@ -311,10 +312,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts._externalResourceInstaller.title, icon: 'ph-download ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index 683a31c36dbd7820463928f85d0ed5a6094d513f..4099e2bac945555442149e6d3003b484a2608ef8 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,118 +7,123 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer v-if="instance" :contentMax="600" :marginMin="16" :marginMax="32"> - <div v-if="tab === 'overview'" class="_gaps_m"> - <div class="fnfelxur"> - <img :src="faviconUrl" alt="" class="icon"/> - <span class="name">{{ instance.name || `(${i18n.ts.unknown})` }}</span> - </div> - <div style="display: flex; flex-direction: column; gap: 1em;"> - <MkKeyValue :copy="host" oneline> - <template #key>Host</template> - <template #value><span class="_monospace"><MkLink :url="`https://${host}`">{{ host }}</MkLink></span></template> - </MkKeyValue> - <MkKeyValue oneline> - <template #key>{{ i18n.ts.software }}</template> - <template #value><span class="_monospace">{{ instance.softwareName || `(${i18n.ts.unknown})` }} / {{ instance.softwareVersion || `(${i18n.ts.unknown})` }}</span></template> - </MkKeyValue> - <MkKeyValue oneline> - <template #key>{{ i18n.ts.administrator }}</template> - <template #value>{{ instance.maintainerName || `(${i18n.ts.unknown})` }} ({{ instance.maintainerEmail || `(${i18n.ts.unknown})` }})</template> + <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <div v-if="tab === 'overview'" key="overview" class="_gaps_m"> + <div class="fnfelxur"> + <img :src="faviconUrl" alt="" class="icon"/> + <span class="name">{{ instance.name || `(${i18n.ts.unknown})` }}</span> + </div> + <div style="display: flex; flex-direction: column; gap: 1em;"> + <MkKeyValue :copy="host" oneline> + <template #key>Host</template> + <template #value><span class="_monospace"><MkLink :url="`https://${host}`">{{ host }}</MkLink></span></template> + </MkKeyValue> + <MkKeyValue oneline> + <template #key>{{ i18n.ts.software }}</template> + <template #value><span class="_monospace">{{ instance.softwareName || `(${i18n.ts.unknown})` }} / {{ instance.softwareVersion || `(${i18n.ts.unknown})` }}</span></template> + </MkKeyValue> + <MkKeyValue oneline> + <template #key>{{ i18n.ts.administrator }}</template> + <template #value>{{ instance.maintainerName || `(${i18n.ts.unknown})` }} ({{ instance.maintainerEmail || `(${i18n.ts.unknown})` }})</template> + </MkKeyValue> + </div> + <MkKeyValue> + <template #key>{{ i18n.ts.description }}</template> + <template #value>{{ instance.description }}</template> </MkKeyValue> - </div> - <MkKeyValue> - <template #key>{{ i18n.ts.description }}</template> - <template #value>{{ instance.description }}</template> - </MkKeyValue> - <FormSection v-if="iAmModerator"> - <template #label>Moderation</template> - <div class="_gaps_s"> - <MkSwitch v-model="suspended" :disabled="!instance" @update:modelValue="toggleSuspend">{{ i18n.ts.stopActivityDelivery }}</MkSwitch> - <MkSwitch v-model="isBlocked" :disabled="!meta || !instance" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch> - <MkSwitch v-model="isSilenced" :disabled="!meta || !instance" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch> - <MkSwitch v-model="isNSFW" :disabled="!instance" @update:modelValue="toggleNSFW">Mark as NSFW</MkSwitch> - <MkButton @click="refreshMetadata"><i class="ph-arrows-counter-clockwise ph-bold ph-lg"></i> Refresh metadata</MkButton> - </div> - </FormSection> + <FormSection v-if="iAmModerator"> + <template #label>Moderation</template> + <div class="_gaps_s"> + <MkSwitch v-model="suspended" :disabled="!instance" @update:modelValue="toggleSuspend">{{ i18n.ts.stopActivityDelivery }}</MkSwitch> + <MkSwitch v-model="isBlocked" :disabled="!meta || !instance" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch> + <MkSwitch v-model="isSilenced" :disabled="!meta || !instance" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch> + <MkSwitch v-model="isNSFW" :disabled="!instance" @update:modelValue="toggleNSFW">Mark as NSFW</MkSwitch> + <MkButton @click="refreshMetadata"><i class="ph-arrows-clockwise ph-bold ph-lg"></i> Refresh metadata</MkButton> + <MkTextarea v-model="moderationNote" manualSave> + <template #label>{{ i18n.ts.moderationNote }}</template> + </MkTextarea> + </div> + </FormSection> - <FormSection> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.registeredAt }}</template> - <template #value><MkTime mode="detail" :time="instance.firstRetrievedAt"/></template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.updatedAt }}</template> - <template #value><MkTime mode="detail" :time="instance.infoUpdatedAt"/></template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.latestRequestReceivedAt }}</template> - <template #value><MkTime v-if="instance.latestRequestReceivedAt" mode="detail" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></template> - </MkKeyValue> - </FormSection> + <FormSection> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.registeredAt }}</template> + <template #value><MkTime mode="detail" :time="instance.firstRetrievedAt"/></template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.updatedAt }}</template> + <template #value><MkTime mode="detail" :time="instance.infoUpdatedAt"/></template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.latestRequestReceivedAt }}</template> + <template #value><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></template> + </MkKeyValue> + </FormSection> - <FormSection> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>Following (Pub)</template> - <template #value>{{ number(instance.followingCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>Followers (Sub)</template> - <template #value>{{ number(instance.followersCount) }}</template> - </MkKeyValue> - </FormSection> + <FormSection> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>Following (Pub)</template> + <template #value>{{ number(instance.followingCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>Followers (Sub)</template> + <template #value>{{ number(instance.followersCount) }}</template> + </MkKeyValue> + </FormSection> - <FormSection> - <template #label>Well-known resources</template> - <FormLink :to="`https://${host}/.well-known/host-meta`" external style="margin-bottom: 8px;">host-meta</FormLink> - <FormLink :to="`https://${host}/.well-known/host-meta.json`" external style="margin-bottom: 8px;">host-meta.json</FormLink> - <FormLink :to="`https://${host}/.well-known/nodeinfo`" external style="margin-bottom: 8px;">nodeinfo</FormLink> - <FormLink :to="`https://${host}/robots.txt`" external style="margin-bottom: 8px;">robots.txt</FormLink> - <FormLink :to="`https://${host}/manifest.json`" external style="margin-bottom: 8px;">manifest.json</FormLink> - </FormSection> - </div> - <div v-else-if="tab === 'chart'" class="_gaps_m"> - <div class="cmhjzshl"> - <div class="selects"> - <MkSelect v-model="chartSrc" style="margin: 0 10px 0 0; flex: 1;"> - <option value="instance-requests">{{ i18n.ts._instanceCharts.requests }}</option> - <option value="instance-users">{{ i18n.ts._instanceCharts.users }}</option> - <option value="instance-users-total">{{ i18n.ts._instanceCharts.usersTotal }}</option> - <option value="instance-notes">{{ i18n.ts._instanceCharts.notes }}</option> - <option value="instance-notes-total">{{ i18n.ts._instanceCharts.notesTotal }}</option> - <option value="instance-ff">{{ i18n.ts._instanceCharts.ff }}</option> - <option value="instance-ff-total">{{ i18n.ts._instanceCharts.ffTotal }}</option> - <option value="instance-drive-usage">{{ i18n.ts._instanceCharts.cacheSize }}</option> - <option value="instance-drive-usage-total">{{ i18n.ts._instanceCharts.cacheSizeTotal }}</option> - <option value="instance-drive-files">{{ i18n.ts._instanceCharts.files }}</option> - <option value="instance-drive-files-total">{{ i18n.ts._instanceCharts.filesTotal }}</option> - </MkSelect> - </div> - <div class="charts"> - <div class="label">{{ i18n.t('recentNHours', { n: 90 }) }}</div> - <MkChart class="chart" :src="chartSrc" span="hour" :limit="90" :args="{ host: host }" :detailed="true"></MkChart> - <div class="label">{{ i18n.t('recentNDays', { n: 90 }) }}</div> - <MkChart class="chart" :src="chartSrc" span="day" :limit="90" :args="{ host: host }" :detailed="true"></MkChart> + <FormSection> + <template #label>Well-known resources</template> + <FormLink :to="`https://${host}/.well-known/host-meta`" external style="margin-bottom: 8px;">host-meta</FormLink> + <FormLink :to="`https://${host}/.well-known/host-meta.json`" external style="margin-bottom: 8px;">host-meta.json</FormLink> + <FormLink :to="`https://${host}/.well-known/nodeinfo`" external style="margin-bottom: 8px;">nodeinfo</FormLink> + <FormLink :to="`https://${host}/robots.txt`" external style="margin-bottom: 8px;">robots.txt</FormLink> + <FormLink :to="`https://${host}/manifest.json`" external style="margin-bottom: 8px;">manifest.json</FormLink> + </FormSection> + </div> + <div v-else-if="tab === 'chart'" key="chart" class="_gaps_m"> + <div class="cmhjzshl"> + <div class="selects"> + <MkSelect v-model="chartSrc" style="margin: 0 10px 0 0; flex: 1;"> + <option value="instance-requests">{{ i18n.ts._instanceCharts.requests }}</option> + <option value="instance-users">{{ i18n.ts._instanceCharts.users }}</option> + <option value="instance-users-total">{{ i18n.ts._instanceCharts.usersTotal }}</option> + <option value="instance-notes">{{ i18n.ts._instanceCharts.notes }}</option> + <option value="instance-notes-total">{{ i18n.ts._instanceCharts.notesTotal }}</option> + <option value="instance-ff">{{ i18n.ts._instanceCharts.ff }}</option> + <option value="instance-ff-total">{{ i18n.ts._instanceCharts.ffTotal }}</option> + <option value="instance-drive-usage">{{ i18n.ts._instanceCharts.cacheSize }}</option> + <option value="instance-drive-usage-total">{{ i18n.ts._instanceCharts.cacheSizeTotal }}</option> + <option value="instance-drive-files">{{ i18n.ts._instanceCharts.files }}</option> + <option value="instance-drive-files-total">{{ i18n.ts._instanceCharts.filesTotal }}</option> + </MkSelect> + </div> + <div class="charts"> + <div class="label">{{ i18n.tsx.recentNHours({ n: 90 }) }}</div> + <MkChart class="chart" :src="chartSrc" span="hour" :limit="90" :args="{ host: host }" :detailed="true"></MkChart> + <div class="label">{{ i18n.tsx.recentNDays({ n: 90 }) }}</div> + <MkChart class="chart" :src="chartSrc" span="day" :limit="90" :args="{ host: host }" :detailed="true"></MkChart> + </div> </div> </div> - </div> - <div v-else-if="tab === 'users'" class="_gaps_m"> - <MkPagination v-slot="{items}" :pagination="usersPagination" style="display: grid; grid-template-columns: repeat(auto-fill,minmax(270px,1fr)); grid-gap: 12px;"> - <MkA v-for="user in items" :key="user.id" v-tooltip.mfm="`Last posted: ${dateString(user.updatedAt)}`" class="user" :to="`/admin/user/${user.id}`"> - <MkUserCardMini :user="user"/> - </MkA> - </MkPagination> - </div> - <div v-else-if="tab === 'raw'" class="_gaps_m"> - <MkObjectView tall :value="instance"> - </MkObjectView> - </div> + <div v-else-if="tab === 'users'" key="users" class="_gaps_m"> + <MkPagination v-slot="{items}" :pagination="usersPagination" style="display: grid; grid-template-columns: repeat(auto-fill,minmax(270px,1fr)); grid-gap: 12px;"> + <MkA v-for="user in items" :key="user.id" v-tooltip.mfm="`Last posted: ${dateString(user.updatedAt)}`" class="user" :to="`/admin/user/${user.id}`"> + <MkUserCardMini :user="user"/> + </MkA> + </MkPagination> + </div> + <div v-else-if="tab === 'raw'" key="raw" class="_gaps_m"> + <MkObjectView tall :value="instance"> + </MkObjectView> + </div> + </MkHorizontalSwipe> </MkSpacer> </MkStickyContainer> </template> <script lang="ts" setup> -import { ref, computed } from 'vue'; +import { ref, computed, watch } from 'vue'; import * as Misskey from 'misskey-js'; import MkChart from '@/components/MkChart.vue'; import MkObjectView from '@/components/MkObjectView.vue'; @@ -130,20 +135,24 @@ import MkKeyValue from '@/components/MkKeyValue.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import number from '@/filters/number.js'; import { iAmModerator, iAmAdmin } from '@/account.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkPagination from '@/components/MkPagination.vue'; +import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; import { dateString } from '@/filters/date.js'; +import MkTextarea from '@/components/MkTextarea.vue'; const props = defineProps<{ host: string; }>(); const tab = ref('overview'); + const chartSrc = ref('instance-requests'); const meta = ref<Misskey.entities.AdminMetaResponse | null>(null); const instance = ref<Misskey.entities.FederationInstance | null>(null); @@ -152,6 +161,7 @@ const isBlocked = ref(false); const isSilenced = ref(false); const isNSFW = ref(false); const faviconUrl = ref<string | null>(null); +const moderationNote = ref(''); const usersPagination = { endpoint: iAmModerator ? 'admin/show-users' : 'users' as const, @@ -164,11 +174,15 @@ const usersPagination = { offsetMode: true, }; +watch(moderationNote, async () => { + await misskeyApi('admin/federation/update-instance', { host: instance.value.host, moderationNote: moderationNote.value }); +}); + async function fetch(): Promise<void> { if (iAmAdmin) { - meta.value = await os.api('admin/meta'); + meta.value = await misskeyApi('admin/meta'); } - instance.value = await os.api('federation/show-instance', { + instance.value = await misskeyApi('federation/show-instance', { host: props.host, }); suspended.value = instance.value?.isSuspended ?? false; @@ -176,13 +190,14 @@ async function fetch(): Promise<void> { isSilenced.value = instance.value?.isSilenced ?? false; isNSFW.value = instance.value?.isNSFW ?? false; faviconUrl.value = getProxiedImageUrlNullable(instance.value?.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.value?.iconUrl, 'preview'); + moderationNote.value = instance.value?.moderationNote; } async function toggleBlock(): Promise<void> { if (!meta.value) throw new Error('No meta?'); if (!instance.value) throw new Error('No instance?'); const { host } = instance.value; - await os.api('admin/update-meta', { + await misskeyApi('admin/update-meta', { blockedHosts: isBlocked.value ? meta.value.blockedHosts.concat([host]) : meta.value.blockedHosts.filter(x => x !== host), }); } @@ -192,14 +207,14 @@ async function toggleSilenced(): Promise<void> { if (!instance.value) throw new Error('No instance?'); const { host } = instance.value; const silencedHosts = meta.value.silencedHosts ?? []; - await os.api('admin/update-meta', { + await misskeyApi('admin/update-meta', { silencedHosts: isSilenced.value ? silencedHosts.concat([host]) : silencedHosts.filter(x => x !== host), }); } async function toggleSuspend(): Promise<void> { if (!instance.value) throw new Error('No instance?'); - await os.api('admin/federation/update-instance', { + await misskeyApi('admin/federation/update-instance', { host: instance.value.host, isSuspended: suspended.value, }); @@ -207,7 +222,7 @@ async function toggleSuspend(): Promise<void> { async function toggleNSFW(): Promise<void> { if (!instance.value) throw new Error('No instance?'); - await os.api('admin/federation/update-instance', { + await misskeyApi('admin/federation/update-instance', { host: instance.value.host, isNSFW: isNSFW.value, }); @@ -215,7 +230,7 @@ async function toggleNSFW(): Promise<void> { function refreshMetadata(): void { if (!instance.value) throw new Error('No instance?'); - os.api('admin/federation/refresh-remote-instance-metadata', { + misskeyApi('admin/federation/refresh-remote-instance-metadata', { host: instance.value.host, }); os.alert({ @@ -251,10 +266,10 @@ const headerTabs = computed(() => [{ icon: 'ph-code ph-bold ph-lg', }]); -definePageMetadata({ +definePageMetadata(() => ({ title: props.host, icon: 'ph-hard-drives ph-bold ph-lg', -}); +})); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/pages/invite.vue b/packages/frontend/src/pages/invite.vue index 6ac78a206836f1adcc23435d2b497eb4e0acf76b..b8c006eb77839e57c56ee2967a9c1c058a835c80 100644 --- a/packages/frontend/src/pages/invite.vue +++ b/packages/frontend/src/pages/invite.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -19,9 +19,9 @@ SPDX-License-Identifier: AGPL-3.0-only </MKSpacer> <MkSpacer v-else :contentMax="800"> <div class="_gaps_m" style="text-align: center;"> - <div v-if="resetCycle && inviteLimit">{{ i18n.t('inviteLimitResetCycle', { time: resetCycle, limit: inviteLimit }) }}</div> + <div v-if="resetCycle && inviteLimit">{{ i18n.tsx.inviteLimitResetCycle({ time: resetCycle, limit: inviteLimit }) }}</div> <MkButton inline primary rounded :disabled="currentInviteLimit !== null && currentInviteLimit <= 0" @click="create"><i class="ph-user-plus ph-bold ph-lg"></i> {{ i18n.ts.createInviteCode }}</MkButton> - <div v-if="currentInviteLimit !== null">{{ i18n.t('createLimitRemaining', { limit: currentInviteLimit }) }}</div> + <div v-if="currentInviteLimit !== null">{{ i18n.tsx.createLimitRemaining({ limit: currentInviteLimit }) }}</div> <MkPagination ref="pagingComponent" :pagination="pagination"> <template #default="{ items }"> @@ -40,6 +40,7 @@ import { computed, ref, shallowRef } from 'vue'; import type * as Misskey from 'misskey-js'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import MkButton from '@/components/MkButton.vue'; import MkPagination, { Paging } from '@/components/MkPagination.vue'; import MkInviteCode from '@/components/MkInviteCode.vue'; @@ -68,7 +69,7 @@ const resetCycle = computed<null | string>(() => { }); async function create() { - const ticket = await os.api('invite/create'); + const ticket = await misskeyApi('invite/create'); os.alert({ type: 'success', title: i18n.ts.inviteCodeCreated, @@ -87,15 +88,15 @@ function deleted(id: string) { } async function update() { - currentInviteLimit.value = (await os.api('invite/limit')).remaining; + currentInviteLimit.value = (await misskeyApi('invite/limit')).remaining; } update(); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.invite, icon: 'ph-user-plus ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/list.vue b/packages/frontend/src/pages/list.vue index d6c6685a7904d54dd2e59ca16c85a23a3b622d66..87070d9167cc51d47f382b4559ce2752a767cf07 100644 --- a/packages/frontend/src/pages/list.vue +++ b/packages/frontend/src/pages/list.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -37,6 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { watch, computed, ref } from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { userPage } from '@/filters/user.js'; import { i18n } from '@/i18n.js'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; @@ -53,12 +54,12 @@ const error = ref(); const users = ref<Misskey.entities.UserDetailed[]>([]); function fetchList(): void { - os.api('users/lists/show', { + misskeyApi('users/lists/show', { listId: props.listId, forPublic: true, }).then(_list => { list.value = _list; - os.api('users/show', { + misskeyApi('users/show', { userIds: list.value.userIds, }).then(_users => { users.value = _users; @@ -100,10 +101,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => list.value ? { - title: list.value.name, +definePageMetadata(() => ({ + title: list.value ? list.value.name : i18n.ts.lists, icon: 'ph-list ph-bold ph-lg', -} : null)); +})); </script> <style lang="scss" module> .main { diff --git a/packages/frontend/src/pages/miauth.vue b/packages/frontend/src/pages/miauth.vue index 2b53b67ab374285420279ef1f810a8bae0dafa59..4812bfe70faa820d768d41c0d8741805e84e24dc 100644 --- a/packages/frontend/src/pages/miauth.vue +++ b/packages/frontend/src/pages/miauth.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -20,13 +20,13 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div v-else> <div v-if="_permissions.length > 0"> - <p v-if="name">{{ i18n.t('_auth.permission', { name }) }}</p> + <p v-if="name">{{ i18n.tsx._auth.permission({ name }) }}</p> <p v-else>{{ i18n.ts._auth.permissionAsk }}</p> <ul> - <li v-for="p in _permissions" :key="p">{{ i18n.t(`_permissions.${p}`) }}</li> + <li v-for="p in _permissions" :key="p">{{ i18n.ts._permissions[p] }}</li> </ul> </div> - <div v-if="name">{{ i18n.t('_auth.shareAccess', { name }) }}</div> + <div v-if="name">{{ i18n.tsx._auth.shareAccess({ name }) }}</div> <div v-else>{{ i18n.ts._auth.shareAccessAsk }}</div> <div :class="$style.buttons"> <MkButton inline @click="deny">{{ i18n.ts.cancel }}</MkButton> @@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref, computed } from 'vue'; import MkSignin from '@/components/MkSignin.vue'; import MkButton from '@/components/MkButton.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { $i, login } from '@/account.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -65,7 +65,7 @@ const state = ref<string | null>(null); async function accept(): Promise<void> { state.value = 'waiting'; - await os.api('miauth/gen-token', { + await misskeyApi('miauth/gen-token', { session: props.session, name: props.name, iconUrl: props.icon, @@ -93,10 +93,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: 'MiAuth', icon: 'ph-squares-four ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/my-antennas/create.vue b/packages/frontend/src/pages/my-antennas/create.vue index 79b592dada129e13bf3058f1c671d38c6221dc6e..f511c48a06f7e812b322ebb9cb17e6dadeecdec9 100644 --- a/packages/frontend/src/pages/my-antennas/create.vue +++ b/packages/frontend/src/pages/my-antennas/create.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -14,8 +14,8 @@ import { ref } from 'vue'; import XAntenna from './editor.vue'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { useRouter } from '@/router.js'; import { antennasCache } from '@/cache.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -38,8 +38,8 @@ function onAntennaCreated() { router.push('/my/antennas'); } -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.manageAntennas, icon: 'ph-flying-saucer ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/my-antennas/edit.vue b/packages/frontend/src/pages/my-antennas/edit.vue index 851b32527c038af0e614dd7d62e9bb3d7d981970..a262e932f306ef085cf5b18c9598fdd3ecce1605 100644 --- a/packages/frontend/src/pages/my-antennas/edit.vue +++ b/packages/frontend/src/pages/my-antennas/edit.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -13,11 +13,11 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import XAntenna from './editor.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; -import { useRouter } from '@/router.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { antennasCache } from '@/cache.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -32,12 +32,12 @@ function onAntennaUpdated() { router.push('/my/antennas'); } -os.api('antennas/show', { antennaId: props.antennaId }).then((antennaResponse) => { +misskeyApi('antennas/show', { antennaId: props.antennaId }).then((antennaResponse) => { antenna.value = antennaResponse; }); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.manageAntennas, icon: 'ph-flying-saucer ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/my-antennas/editor.vue b/packages/frontend/src/pages/my-antennas/editor.vue index 0fc7f862a31bca5e96bc77684967de286d8ff35a..2d29e2d37591e4309972362f1c4eb7d2491e2444 100644 --- a/packages/frontend/src/pages/my-antennas/editor.vue +++ b/packages/frontend/src/pages/my-antennas/editor.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -57,6 +57,7 @@ import MkTextarea from '@/components/MkTextarea.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ @@ -84,7 +85,7 @@ const userLists = ref<Misskey.entities.UserList[] | null>(null); watch(() => src.value, async () => { if (src.value === 'list' && userLists.value === null) { - userLists.value = await os.api('users/lists/list'); + userLists.value = await misskeyApi('users/lists/list'); } }); @@ -115,11 +116,11 @@ async function saveAntenna() { async function deleteAntenna() { const { canceled } = await os.confirm({ type: 'warning', - text: i18n.t('removeAreYouSure', { x: props.antenna.name }), + text: i18n.tsx.removeAreYouSure({ x: props.antenna.name }), }); if (canceled) return; - await os.api('antennas/delete', { + await misskeyApi('antennas/delete', { antennaId: props.antenna.id, }); @@ -128,7 +129,7 @@ async function deleteAntenna() { } function addUser() { - os.selectUser().then(user => { + os.selectUser({ includeSelf: true }).then(user => { users.value = users.value.trim(); users.value += '\n@' + Misskey.acct.toString(user as any); users.value = users.value.trim(); diff --git a/packages/frontend/src/pages/my-antennas/index.vue b/packages/frontend/src/pages/my-antennas/index.vue index b46fb7a5d79d404b2bfbf19d6773d49d4a2fffff..a312672f747c3f2bcb7f3db91a7badff4c544b26 100644 --- a/packages/frontend/src/pages/my-antennas/index.vue +++ b/packages/frontend/src/pages/my-antennas/index.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -55,10 +55,10 @@ const headerActions = computed(() => [{ const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.manageAntennas, icon: 'ph-flying-saucer ph-bold ph-lg', -}); +})); onActivated(() => { antennasCache.fetch(); diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue index d787e53bb0a5fed4dca6febdd483257e4d8857bb..f46ea0e0ea94eb4a676e83126369cb93b18f4288 100644 --- a/packages/frontend/src/pages/my-clips/index.vue +++ b/packages/frontend/src/pages/my-clips/index.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,20 +7,22 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :contentMax="700"> - <div v-if="tab === 'my'" class="_gaps"> - <MkButton primary rounded class="add" @click="create"><i class="ph-plus ph-bold ph-lg"></i> {{ i18n.ts.add }}</MkButton> - - <MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="_gaps"> - <MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`"> + <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <div v-if="tab === 'my'" key="my" class="_gaps"> + <MkButton primary rounded class="add" @click="create"><i class="ph-plus ph-bold ph-lg"></i> {{ i18n.ts.add }}</MkButton> + + <MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="_gaps"> + <MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`"> + <MkClipPreview :clip="item"/> + </MkA> + </MkPagination> + </div> + <div v-else-if="tab === 'favorites'" key="favorites" class="_gaps"> + <MkA v-for="item in favorites" :key="item.id" :to="`/clips/${item.id}`"> <MkClipPreview :clip="item"/> </MkA> - </MkPagination> - </div> - <div v-else-if="tab === 'favorites'" class="_gaps"> - <MkA v-for="item in favorites" :key="item.id" :to="`/clips/${item.id}`"> - <MkClipPreview :clip="item"/> - </MkA> - </div> + </div> + </MkHorizontalSwipe> </MkSpacer> </MkStickyContainer> </template> @@ -32,9 +34,11 @@ import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; import MkClipPreview from '@/components/MkClipPreview.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { clipsCache } from '@/cache.js'; +import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; const pagination = { endpoint: 'clips/list' as const, @@ -43,12 +47,13 @@ const pagination = { }; const tab = ref('my'); + const favorites = ref<Misskey.entities.Clip[] | null>(null); const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>(); watch(tab, async () => { - favorites.value = await os.api('clips/my-favorites'); + favorites.value = await misskeyApi('clips/my-favorites'); }); async function create() { @@ -99,14 +104,10 @@ const headerTabs = computed(() => [{ icon: 'ph-heart ph-bold ph-lg', }]); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.clip, icon: 'ph-paperclip ph-bold ph-lg', - action: { - icon: 'ph-plus ph-bold ph-lg', - handler: create, - }, -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue index 3379cf43d40a3590db1cd04c3c08d17502c0ac40..f2469be8dea402187e8b10642e49853912e8b357 100644 --- a/packages/frontend/src/pages/my-lists/index.vue +++ b/packages/frontend/src/pages/my-lists/index.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="items.length > 0" class="_gaps"> <MkA v-for="list in items" :key="list.id" class="_panel" :class="$style.list" :to="`/my/lists/${ list.id }`"> - <div style="margin-bottom: 4px;">{{ list.name }} <span :class="$style.nUsers">({{ i18n.t('nUsers', { n: `${list.userIds.length}/${$i?.policies['userEachUserListsLimit']}` }) }})</span></div> + <div style="margin-bottom: 4px;">{{ list.name }} <span :class="$style.nUsers">({{ i18n.tsx.nUsers({ n: `${list.userIds.length}/${$i.policies['userEachUserListsLimit']}` }) }})</span></div> <MkAvatars :userIds="list.userIds" :limit="10"/> </MkA> </div> @@ -37,7 +37,9 @@ import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { userListsCache } from '@/cache.js'; import { infoImageUrl } from '@/instance.js'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; + +const $i = signinRequired(); const items = computed(() => userListsCache.value.value ?? []); @@ -69,10 +71,10 @@ const headerActions = computed(() => [{ const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.manageLists, icon: 'ph-list ph-bold ph-lg', -}); +})); onActivated(() => { fetch(); diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue index df9cdb0fce94889dcfaacbc11f3ea6fae8eb8668..1b7aa3f93839387cbeede1ec93a5f3d787d157d2 100644 --- a/packages/frontend/src/pages/my-lists/list.vue +++ b/packages/frontend/src/pages/my-lists/list.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkFolder defaultOpen> <template #label>{{ i18n.ts.members }}</template> - <template #caption>{{ i18n.t('nUsers', { n: `${list.userIds.length}/${$i?.policies['userEachUserListsLimit']}` }) }}</template> + <template #caption>{{ i18n.tsx.nUsers({ n: `${list.userIds.length}/${$i.policies['userEachUserListsLimit']}` }) }}</template> <div class="_gaps_s"> <MkButton rounded primary style="margin: 0 auto;" @click="addUser()">{{ i18n.ts.addUser }}</MkButton> @@ -57,7 +57,7 @@ import { computed, ref, watch } from 'vue'; import * as Misskey from 'misskey-js'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; -import { mainRouter } from '@/router.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; import { userPage } from '@/filters/user.js'; @@ -66,9 +66,12 @@ import MkSwitch from '@/components/MkSwitch.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkInput from '@/components/MkInput.vue'; import { userListsCache } from '@/cache.js'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; import { defaultStore } from '@/store.js'; import MkPagination from '@/components/MkPagination.vue'; +import { mainRouter } from '@/router/main.js'; + +const $i = signinRequired(); const { enableInfiniteScroll, @@ -91,7 +94,7 @@ const membershipsPagination = { }; function fetchList() { - os.api('users/lists/show', { + misskeyApi('users/lists/show', { listId: props.listId, }).then(_list => { list.value = _list; @@ -119,7 +122,7 @@ async function removeUser(item, ev) { danger: true, action: async () => { if (!list.value) return; - os.api('users/lists/pull', { + misskeyApi('users/lists/pull', { listId: list.value.id, userId: item.userId, }).then(() => { @@ -134,7 +137,7 @@ async function showMembershipMenu(item, ev) { text: item.withReplies ? i18n.ts.hideRepliesToOthersInTimeline : i18n.ts.showRepliesToOthersInTimeline, icon: item.withReplies ? 'ph-envelope-open ph-bold ph-lg' : 'ph-envelope ph-bold ph-lg', action: async () => { - os.api('users/lists/update-membership', { + misskeyApi('users/lists/update-membership', { listId: list.value.id, userId: item.userId, withReplies: !item.withReplies, @@ -152,7 +155,7 @@ async function deleteList() { if (!list.value) return; const { canceled } = await os.confirm({ type: 'warning', - text: i18n.t('removeAreYouSure', { x: list.value.name }), + text: i18n.tsx.removeAreYouSure({ x: list.value.name }), }); if (canceled) return; @@ -183,10 +186,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => list.value ? { - title: list.value.name, +definePageMetadata(() => ({ + title: list.value ? list.value.name : i18n.ts.lists, icon: 'ph-list ph-bold ph-lg', -} : null)); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/not-found.vue b/packages/frontend/src/pages/not-found.vue index e8ba31395e882959a4e4566c3996f10b6275cb15..6f69f9285dfa12201a2931b09f5253c5b073799b 100644 --- a/packages/frontend/src/pages/not-found.vue +++ b/packages/frontend/src/pages/not-found.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -31,8 +31,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.notFound, icon: 'ph-warning ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue index a98a7bde2c08dbab781ade117d3ad5debe3a758b..6ccb5b61e5cf927468cdfe9131887bbe0e876194 100644 --- a/packages/frontend/src/pages/note.vue +++ b/packages/frontend/src/pages/note.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -11,11 +11,14 @@ SPDX-License-Identifier: AGPL-3.0-only <Transition :name="defaultStore.state.animation ? 'fade' : ''" mode="out-in"> <div v-if="note"> <div v-if="showNext" class="_margin"> - <MkNotes class="" :pagination="nextPagination" :noGap="true" :disableAutoLoad="true"/> + <MkNotes class="" :pagination="showNext === 'channel' ? nextChannelPagination : nextUserPagination" :noGap="true" :disableAutoLoad="true"/> </div> <div class="_margin"> - <MkButton v-if="!showNext" :class="$style.loadNext" @click="showNext = true"><i class="ph-caret-up ph-bold ph-lg"></i></MkButton> + <div v-if="!showNext" class="_buttons" :class="$style.loadNext"> + <MkButton v-if="note.channelId" rounded :class="$style.loadButton" @click="showNext = 'channel'"><i class="ph-caret-up ph-bold ph-lg"></i> <i class="ph-television-simple ph-bold ph-lg"></i></MkButton> + <MkButton rounded :class="$style.loadButton" @click="showNext = 'user'"><i class="ph-caret-up ph-bold ph-lg"></i> <i class="ph-user ph-bold ph-lg"></i></MkButton> + </div> <div v-if="defaultStore.state.noteDesign === 'misskey'" class="_margin _gaps_s"> <MkRemoteCaution v-if="note.user.host != null" :href="note.url ?? note.uri"/> <MkNoteDetailed :key="note.id" v-model:note="note" :class="$style.note" :expandAllCws="expandAllCws"/> @@ -32,11 +35,14 @@ SPDX-License-Identifier: AGPL-3.0-only </MkA> </div> </div> - <MkButton v-if="!showPrev" :class="$style.loadPrev" @click="showPrev = true"><i class="ph-caret-down ph-bold ph-lg"></i></MkButton> + <div v-if="!showPrev" class="_buttons" :class="$style.loadPrev"> + <MkButton v-if="note.channelId" rounded :class="$style.loadButton" @click="showPrev = 'channel'"><i class="ph-caret-down ph-bold ph-lg"></i> <i class="ph-television-simple ph-bold ph-lg"></i></MkButton> + <MkButton rounded :class="$style.loadButton" @click="showPrev = 'user'"><i class="ph-caret-down ph-bold ph-lg"></i> <i class="ph-user ph-bold ph-lg"></i></MkButton> + </div> </div> <div v-if="showPrev" class="_margin"> - <MkNotes class="" :pagination="prevPagination" :noGap="true"/> + <MkNotes class="" :pagination="showPrev === 'channel' ? prevChannelPagination : prevUserPagination" :noGap="true"/> </div> </div> <MkError v-else-if="error" @retry="fetchNote()"/> @@ -50,12 +56,13 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { computed, watch, ref } from 'vue'; import * as Misskey from 'misskey-js'; +import type { Paging } from '@/components/MkPagination.vue'; import MkNoteDetailed from '@/components/MkNoteDetailed.vue'; import MkNotes from '@/components/MkNotes.vue'; import SkNoteDetailed from '@/components/SkNoteDetailed.vue'; import MkRemoteCaution from '@/components/MkRemoteCaution.vue'; import MkButton from '@/components/MkButton.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; import { dateString } from '@/filters/date.js'; @@ -68,41 +75,60 @@ const props = defineProps<{ const note = ref<null | Misskey.entities.Note>(); const clips = ref<Misskey.entities.Clip[]>(); -const showPrev = ref(false); -const showNext = ref(false); +const showPrev = ref<'user' | 'channel' | false>(false); +const showNext = ref<'user' | 'channel' | false>(false); const expandAllCws = ref(false); const error = ref(); -const prevPagination = { - endpoint: 'users/notes' as const, +const prevUserPagination: Paging = { + endpoint: 'users/notes', limit: 10, params: computed(() => note.value ? ({ userId: note.value.userId, untilId: note.value.id, - }) : null), + }) : undefined), }; -const nextPagination = { +const nextUserPagination: Paging = { reversed: true, - endpoint: 'users/notes' as const, + endpoint: 'users/notes', limit: 10, params: computed(() => note.value ? ({ userId: note.value.userId, sinceId: note.value.id, - }) : null), + }) : undefined), +}; + +const prevChannelPagination: Paging = { + endpoint: 'channels/timeline', + limit: 10, + params: computed(() => note.value ? ({ + channelId: note.value.channelId, + untilId: note.value.id, + }) : undefined), +}; + +const nextChannelPagination: Paging = { + reversed: true, + endpoint: 'channels/timeline', + limit: 10, + params: computed(() => note.value ? ({ + channelId: note.value.channelId, + sinceId: note.value.id, + }) : undefined), }; function fetchNote() { showPrev.value = false; showNext.value = false; note.value = null; - os.api('notes/show', { + misskeyApi('notes/show', { noteId: props.noteId, }).then(res => { note.value = res; // å¤ã„ノートã¯è¢«ã‚¯ãƒªãƒƒãƒ—数をカウントã—ã¦ã„ãªã„ã®ã§ã€2023-10-01以å‰ã®ã‚‚ã®ã¯å¼·åˆ¶çš„ã«notes/clipsã‚’å©ã if (note.value.clippedCount > 0 || new Date(note.value.createdAt).getTime() < new Date('2023-10-01').getTime()) { - os.api('notes/clips', { + misskeyApi('notes/clips', { noteId: note.value.id, }).then((_clips) => { clips.value = _clips; @@ -127,16 +153,18 @@ const headerActions = computed(() => note.value ? [ const headerTabs = computed(() => []); -definePageMetadata(computed(() => note.value ? { +definePageMetadata(() => ({ title: i18n.ts.note, - subtitle: dateString(note.value.createdAt), - avatar: note.value.user, - path: `/notes/${note.value.id}`, - share: { - title: i18n.t('noteOf', { user: note.value.user.name }), - text: note.value.text, - }, -} : null)); + ...note.value ? { + subtitle: dateString(note.value.createdAt), + avatar: note.value.user, + path: `/notes/${note.value.id}`, + share: { + title: i18n.tsx.noteOf({ user: note.value.user.name }), + text: note.value.text, + }, + } : {}, +})); </script> <style lang="scss" module> @@ -151,9 +179,7 @@ definePageMetadata(computed(() => note.value ? { .loadNext, .loadPrev { - min-width: 0; - margin: 0 auto; - border-radius: var(--radius-ellipse); + justify-content: center; } .loadNext { @@ -164,6 +190,10 @@ definePageMetadata(computed(() => note.value ? { margin-top: var(--margin); } +.loadButton { + min-width: 0; +} + .note { border-radius: var(--radius); background: var(--panel); diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue index f3fadf5c8efe4015e81177bf9d035c65a8eea05f..9cbb6323a889fb398e5bd004abf0df73153ea48a 100644 --- a/packages/frontend/src/pages/notifications.vue +++ b/packages/frontend/src/pages/notifications.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,15 +7,17 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :contentMax="800"> - <div v-if="tab === 'all'"> - <XNotifications class="notifications" :excludeTypes="excludeTypes"/> - </div> - <div v-else-if="tab === 'mentions'"> - <MkNotes :pagination="mentionsPagination"/> - </div> - <div v-else-if="tab === 'directNotes'"> - <MkNotes :pagination="directNotesPagination"/> - </div> + <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <div v-if="tab === 'all'" key="all"> + <XNotifications :class="$style.notifications" :excludeTypes="excludeTypes"/> + </div> + <div v-else-if="tab === 'mentions'" key="mention"> + <MkNotes :pagination="mentionsPagination"/> + </div> + <div v-else-if="tab === 'directNotes'" key="directNotes"> + <MkNotes :pagination="directNotesPagination"/> + </div> + </MkHorizontalSwipe> </MkSpacer> </MkStickyContainer> </template> @@ -24,6 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed, ref } from 'vue'; import XNotifications from '@/components/MkNotifications.vue'; import MkNotes from '@/components/MkNotes.vue'; +import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -48,8 +51,8 @@ const directNotesPagination = { function setFilter(ev) { const typeItems = notificationTypes.map(t => ({ - text: i18n.t(`_notification._types.${t}`), - active: includeTypes.value && includeTypes.value.includes(t), + text: i18n.ts._notification._types[t], + active: (includeTypes.value && includeTypes.value.includes(t)) ?? false, action: () => { includeTypes.value = [t]; }, @@ -60,7 +63,7 @@ function setFilter(ev) { action: () => { includeTypes.value = null; }, - }, { type: 'divider' }, ...typeItems] : typeItems; + }, { type: 'divider' as const }, ...typeItems] : typeItems; os.popupMenu(items, ev.currentTarget ?? ev.target); } @@ -91,8 +94,15 @@ const headerTabs = computed(() => [{ icon: 'ph-envelope ph-bold ph-lg', }]); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: i18n.ts.notifications, icon: 'ph-bell ph-bold ph-lg', -}))); +})); </script> + +<style module lang="scss"> +.notifications { + border-radius: var(--radius); + overflow: clip; +} +</style> diff --git a/packages/frontend/src/pages/oauth.vue b/packages/frontend/src/pages/oauth.vue index 53b609e0bd35fa9d5a8e79857f1e362a4ae42371..80b6a237ea8e79939d0582fe73d28d4da8ec71c5 100644 --- a/packages/frontend/src/pages/oauth.vue +++ b/packages/frontend/src/pages/oauth.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -9,13 +9,13 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSpacer :contentMax="800"> <div v-if="$i"> <div v-if="permissions.length > 0"> - <p v-if="name">{{ i18n.t('_auth.permission', { name }) }}</p> + <p v-if="name">{{ i18n.tsx._auth.permission({ name }) }}</p> <p v-else>{{ i18n.ts._auth.permissionAsk }}</p> <ul> - <li v-for="p in permissions" :key="p">{{ i18n.t(`_permissions.${p}`) }}</li> + <li v-for="p in permissions" :key="p">{{ i18n.ts._permissions[p] }}</li> </ul> </div> - <div v-if="name">{{ i18n.t('_auth.shareAccess', { name }) }}</div> + <div v-if="name">{{ i18n.tsx._auth.shareAccess({ name }) }}</div> <div v-else>{{ i18n.ts._auth.shareAccessAsk }}</div> <form :class="$style.buttons" action="/oauth/decision" accept-charset="utf-8" method="post"> <input name="login_token" type="hidden" :value="$i.token"/> @@ -51,10 +51,10 @@ function onLogin(res): void { login(res.i); } -definePageMetadata({ +definePageMetadata(() => ({ title: 'OAuth', icon: 'ph-squares-four ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue index 459454a9bec4221add7022d627cb6e02b15e71db..2a55c083d1870700d1a5adfdf709de6a175be994 100644 --- a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue +++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -26,6 +26,7 @@ import * as Misskey from 'misskey-js'; import XContainer from '../page-editor.container.vue'; import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ @@ -52,7 +53,7 @@ onMounted(async () => { if (props.modelValue.fileId == null) { await choose(); } else { - os.api('drive/files/show', { + misskeyApi('drive/files/show', { fileId: props.modelValue.fileId, }).then(fileResponse => { file.value = fileResponse; diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue index 442558cc2ae2da012d9b1b58850291251d584552..978d03c1cddfc6de63615d04633a62ea5c9cfd22 100644 --- a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue +++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -30,7 +30,7 @@ import MkInput from '@/components/MkInput.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkNote from '@/components/MkNote.vue'; import MkNoteDetailed from '@/components/MkNoteDetailed.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ @@ -53,7 +53,7 @@ watch(id, async () => { ...props.modelValue, note: id.value, }); - note.value = await os.api('notes/show', { noteId: id.value }); + note.value = await misskeyApi('notes/show', { noteId: id.value }); }, { immediate: true, }); diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue index 885fa55bc9a337b9a0f9bdf93cdc342669eb18d6..2e4402085c7375255af32198d58f0d14d157dc98 100644 --- a/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue +++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #header><i class="ph-note ph-bold ph-lg"></i> {{ props.modelValue.title }}</template> <template #func> <button class="_button" @click="rename()"> - <i class="ph-pencil ph-bold ph-lg"></i> + <i class="ph-pencil-simple ph-bold ph-lg"></i> </button> </template> diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue index 2af4e4e36551e6e7767c4b4827a28408237b9ae1..e83ad058b446b03cdc5424501a3fa6c3d04b6a43 100644 --- a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue +++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/page-editor/page-editor.blocks.vue b/packages/frontend/src/pages/page-editor/page-editor.blocks.vue index 52220d36bb41ccd9bc61b616ac750ddba71bb242..4967e730008144f5f5772790983a05d19207813a 100644 --- a/packages/frontend/src/pages/page-editor/page-editor.blocks.vue +++ b/packages/frontend/src/pages/page-editor/page-editor.blocks.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/page-editor/page-editor.container.vue b/packages/frontend/src/pages/page-editor/page-editor.container.vue index 71fa890f636fa39f14a9c2bc56746eba9eee88f2..7ef66c1c0c9c2685de0dab9c74775527ec9f11c4 100644 --- a/packages/frontend/src/pages/page-editor/page-editor.container.vue +++ b/packages/frontend/src/pages/page-editor/page-editor.container.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue index 8c4696b04b6b898d1c43c102bc624173d32793e2..92de9d57a5ebdb322154cb7a5caf4253a07170ad 100644 --- a/packages/frontend/src/pages/page-editor/page-editor.vue +++ b/packages/frontend/src/pages/page-editor/page-editor.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -70,11 +70,12 @@ import MkSelect from '@/components/MkSelect.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkInput from '@/components/MkInput.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { selectFile } from '@/scripts/select-file.js'; -import { mainRouter } from '@/router.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { $i } from '@/account.js'; +import { mainRouter } from '@/router/main.js'; const props = defineProps<{ initPageId?: string; @@ -105,7 +106,7 @@ watch(eyeCatchingImageId, async () => { if (eyeCatchingImageId.value == null) { eyeCatchingImage.value = null; } else { - eyeCatchingImage.value = await os.api('drive/files/show', { + eyeCatchingImage.value = await misskeyApi('drive/files/show', { fileId: eyeCatchingImageId.value, }); } @@ -148,7 +149,7 @@ function save() { if (pageId.value) { options.pageId = pageId.value; - os.api('pages/update', options) + misskeyApi('pages/update', options) .then(page => { currentName.value = name.value.trim(); os.alert({ @@ -157,7 +158,7 @@ function save() { }); }).catch(onError); } else { - os.api('pages/create', options) + misskeyApi('pages/create', options) .then(created => { pageId.value = created.id; currentName.value = name.value.trim(); @@ -173,10 +174,10 @@ function save() { function del() { os.confirm({ type: 'warning', - text: i18n.t('removeAreYouSure', { x: title.value.trim() }), + text: i18n.tsx.removeAreYouSure({ x: title.value.trim() }), }).then(({ canceled }) => { if (canceled) return; - os.api('pages/delete', { + misskeyApi('pages/delete', { pageId: pageId.value, }).then(() => { os.alert({ @@ -191,7 +192,7 @@ function del() { function duplicate() { title.value = title.value + ' - copy'; name.value = name.value + '-copy'; - os.api('pages/create', getSaveOptions()).then(created => { + misskeyApi('pages/create', getSaveOptions()).then(created => { pageId.value = created.id; currentName.value = name.value.trim(); os.alert({ @@ -235,11 +236,11 @@ function removeEyeCatchingImage() { async function init() { if (props.initPageId) { - page.value = await os.api('pages/show', { + page.value = await misskeyApi('pages/show', { pageId: props.initPageId, }); } else if (props.initPageName && props.initUser) { - page.value = await os.api('pages/show', { + page.value = await misskeyApi('pages/show', { name: props.initPageName, username: props.initUser, }); @@ -282,17 +283,11 @@ const headerTabs = computed(() => [{ icon: 'ph-note ph-bold ph-lg', }]); -definePageMetadata(computed(() => { - let title = i18n.ts._pages.newPage; - if (props.initPageId) { - title = i18n.ts._pages.editPage; - } else if (props.initPageName && props.initUser) { - title = i18n.ts._pages.readPage; - } - return { - title: title, - icon: 'ph-pencil ph-bold ph-lg', - }; +definePageMetadata(() => ({ + title: props.initPageId ? i18n.ts._pages.editPage + : props.initPageName && props.initUser ? i18n.ts._pages.readPage + : i18n.ts._pages.newPage, + icon: 'ph-pencil-simple ph-bold ph-lg', })); </script> diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue index 6b06da9a2481827bc8c76234cdca2c8c2b1bb42c..dc47f20bee6e08912355055a71f3e6327cd09b4a 100644 --- a/packages/frontend/src/pages/page.vue +++ b/packages/frontend/src/pages/page.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -81,6 +81,7 @@ import * as Misskey from 'misskey-js'; import XPage from '@/components/page/page.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { url } from '@/config.js'; import MkMediaImage from '@/components/MkMediaImage.vue'; import MkFollowButton from '@/components/MkFollowButton.vue'; @@ -113,7 +114,7 @@ const path = computed(() => props.username + '/' + props.pageName); function fetchPage() { page.value = null; - os.api('pages/show', { + misskeyApi('pages/show', { name: props.pageName, username: props.username, }).then(async _page => { @@ -186,15 +187,17 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => page.value ? { - title: page.value.title || page.value.name, - avatar: page.value.user, - path: `/@${page.value.user.username}/pages/${page.value.name}`, - share: { - title: page.value.title || page.value.name, - text: page.value.summary, - }, -} : null)); +definePageMetadata(() => ({ + title: page.value ? page.value.title || page.value.name : i18n.ts.pages, + ...page.value ? { + avatar: page.value.user, + path: `/@${page.value.user.username}/pages/${page.value.name}`, + share: { + title: page.value.title || page.value.name, + text: page.value.summary, + }, + } : {}, +})); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/pages/pages.vue b/packages/frontend/src/pages/pages.vue index a7ca433ed3332e4b40ba3f0e3db85e0982b53670..7b4dd83068caf4b24a9572f4046c5ae772a35e62 100644 --- a/packages/frontend/src/pages/pages.vue +++ b/packages/frontend/src/pages/pages.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,30 +7,32 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :contentMax="700"> - <div v-if="tab === 'featured'"> - <MkPagination v-slot="{items}" :pagination="featuredPagesPagination"> - <div class="_gaps"> - <MkPagePreview v-for="page in items" :key="page.id" :page="page"/> - </div> - </MkPagination> - </div> + <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <div v-if="tab === 'featured'" key="featured"> + <MkPagination v-slot="{items}" :pagination="featuredPagesPagination"> + <div class="_gaps"> + <MkPagePreview v-for="page in items" :key="page.id" :page="page"/> + </div> + </MkPagination> + </div> - <div v-else-if="tab === 'my'" class="_gaps"> - <MkButton class="new" @click="create()"><i class="ph-plus ph-bold ph-lg"></i></MkButton> - <MkPagination v-slot="{items}" :pagination="myPagesPagination"> - <div class="_gaps"> - <MkPagePreview v-for="page in items" :key="page.id" :page="page"/> - </div> - </MkPagination> - </div> + <div v-else-if="tab === 'my'" key="my" class="_gaps"> + <MkButton class="new" @click="create()"><i class="ph-plus ph-bold ph-lg"></i></MkButton> + <MkPagination v-slot="{items}" :pagination="myPagesPagination"> + <div class="_gaps"> + <MkPagePreview v-for="page in items" :key="page.id" :page="page"/> + </div> + </MkPagination> + </div> - <div v-else-if="tab === 'liked'"> - <MkPagination v-slot="{items}" :pagination="likedPagesPagination"> - <div class="_gaps"> - <MkPagePreview v-for="like in items" :key="like.page.id" :page="like.page"/> - </div> - </MkPagination> - </div> + <div v-else-if="tab === 'liked'" key="liked"> + <MkPagination v-slot="{items}" :pagination="likedPagesPagination"> + <div class="_gaps"> + <MkPagePreview v-for="like in items" :key="like.page.id" :page="like.page"/> + </div> + </MkPagination> + </div> + </MkHorizontalSwipe> </MkSpacer> </MkStickyContainer> </template> @@ -40,9 +42,10 @@ import { computed, ref } from 'vue'; import MkPagePreview from '@/components/MkPagePreview.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; -import { useRouter } from '@/router.js'; +import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -78,15 +81,15 @@ const headerTabs = computed(() => [{ }, { key: 'my', title: i18n.ts._pages.my, - icon: 'ph-pencil-line ph-bold ph-lg', + icon: 'ph-pencil-simple-line ph-bold ph-lg', }, { key: 'liked', title: i18n.ts._pages.liked, icon: 'ph-heart ph-bold ph-lg', }]); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: i18n.ts.pages, icon: 'ph-note ph-bold ph-lg', -}))); +})); </script> diff --git a/packages/frontend/src/pages/registry.keys.vue b/packages/frontend/src/pages/registry.keys.vue index 95aa64f8d3848e0ea66844eb3a62a74965b7b4c6..350c4fea1d62a7387526719b98cc1248ecb98d6e 100644 --- a/packages/frontend/src/pages/registry.keys.vue +++ b/packages/frontend/src/pages/registry.keys.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -36,6 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { watch, computed, ref } from 'vue'; import JSON5 from 'json5'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import FormLink from '@/components/form/link.vue'; @@ -54,7 +55,7 @@ const scope = computed(() => props.path ? props.path.split('/') : []); const keys = ref<any>(null); function fetchKeys() { - os.api('i/registry/keys-with-type', { + misskeyApi('i/registry/keys-with-type', { scope: scope.value, domain: props.domain === '@' ? null : props.domain, }).then(res => { @@ -95,8 +96,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.registry, icon: 'ph-faders ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/registry.value.vue b/packages/frontend/src/pages/registry.value.vue index fb3cc4a556f1dc7b851f2b2bc0be1257e5dcc707..61bf5f4545ef809eff3c98d64aeeb25eb3e7b85a 100644 --- a/packages/frontend/src/pages/registry.value.vue +++ b/packages/frontend/src/pages/registry.value.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -48,6 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { watch, computed, ref } from 'vue'; import JSON5 from 'json5'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkButton from '@/components/MkButton.vue'; @@ -68,7 +69,7 @@ const value = ref<any>(null); const valueForEditor = ref<string | null>(null); function fetchValue() { - os.api('i/registry/get-detail', { + misskeyApi('i/registry/get-detail', { scope: scope.value, key: key.value, domain: props.domain === '@' ? null : props.domain, @@ -122,8 +123,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.registry, icon: 'ph-faders ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/registry.vue b/packages/frontend/src/pages/registry.vue index 7d1dd751ab180cdb3c41b15a0a5dad2d21ab9b64..de0c8981879e94ff44758d8cc6e0e021f7acc839 100644 --- a/packages/frontend/src/pages/registry.vue +++ b/packages/frontend/src/pages/registry.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -26,6 +26,7 @@ import { ref, computed } from 'vue'; import * as Misskey from 'misskey-js'; import JSON5 from 'json5'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import FormLink from '@/components/form/link.vue'; @@ -35,7 +36,7 @@ import MkButton from '@/components/MkButton.vue'; const scopesWithDomain = ref<Misskey.entities.IRegistryScopesWithDomainResponse | null>(null); function fetchScopes() { - os.api('i/registry/scopes-with-domain').then(res => { + misskeyApi('i/registry/scopes-with-domain').then(res => { scopesWithDomain.value = res; }); } @@ -72,8 +73,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.registry, icon: 'ph-faders ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/reset-password.vue b/packages/frontend/src/pages/reset-password.vue index 1aed57724ed1af6c6174bff2fd664ea3759963c5..8b0b4baa677354ff9014e1f4d2199b6e4656c8fe 100644 --- a/packages/frontend/src/pages/reset-password.vue +++ b/packages/frontend/src/pages/reset-password.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -25,8 +25,8 @@ import MkInput from '@/components/MkInput.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; -import { mainRouter } from '@/router.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; +import { mainRouter } from '@/router/main.js'; const props = defineProps<{ token?: string; @@ -53,8 +53,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.resetPassword, icon: 'ph-lock ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/reversi/game.board.vue b/packages/frontend/src/pages/reversi/game.board.vue new file mode 100644 index 0000000000000000000000000000000000000000..c5c3d491fcfd36782687b9ba5fe4c9e6df9d23a8 --- /dev/null +++ b/packages/frontend/src/pages/reversi/game.board.vue @@ -0,0 +1,622 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkSpacer :contentMax="500"> + <div :class="$style.root" class="_gaps"> + <div style="display: flex; align-items: center; justify-content: center; gap: 10px;"> + <span>({{ i18n.ts._reversi.black }})</span> + <MkAvatar style="width: 32px; height: 32px;" :user="blackUser" :showIndicator="true"/> + <span> vs </span> + <MkAvatar style="width: 32px; height: 32px;" :user="whiteUser" :showIndicator="true"/> + <span>({{ i18n.ts._reversi.white }})</span> + </div> + + <div style="overflow: clip; line-height: 28px;"> + <div v-if="!iAmPlayer && !game.isEnded && turnUser"> + <Mfm :key="'turn:' + turnUser.id" :text="i18n.tsx._reversi.turnOf({ name: turnUser.name ?? turnUser.username })" :plain="true" :customEmojis="turnUser.emojis"/> + <MkEllipsis/> + </div> + <div v-if="(logPos !== game.logs.length) && turnUser"> + <Mfm :key="'past-turn-of:' + turnUser.id" :text="i18n.tsx._reversi.pastTurnOf({ name: turnUser.name ?? turnUser.username })" :plain="true" :customEmojis="turnUser.emojis"/> + </div> + <div v-if="iAmPlayer && !game.isEnded && !isMyTurn">{{ i18n.ts._reversi.opponentTurn }}<MkEllipsis/><span style="margin-left: 1em; opacity: 0.7;">({{ i18n.tsx.remainingN({ n: opTurnTimerRmain }) }})</span></div> + <div v-if="iAmPlayer && !game.isEnded && isMyTurn"><span style="display: inline-block; font-weight: bold; animation: global-tada 1s linear infinite both;">{{ i18n.ts._reversi.myTurn }}</span><span style="margin-left: 1em; opacity: 0.7;">({{ i18n.tsx.remainingN({ n: myTurnTimerRmain }) }})</span></div> + <div v-if="game.isEnded && logPos == game.logs.length"> + <template v-if="game.winner"> + <Mfm :key="'won'" :text="i18n.tsx._reversi.won({ name: game.winner.name ?? game.winner.username })" :plain="true" :customEmojis="game.winner.emojis"/> + <span v-if="game.surrenderedUserId != null"> ({{ i18n.ts._reversi.surrendered }})</span> + <span v-if="game.timeoutUserId != null"> ({{ i18n.ts._reversi.timeout }})</span> + </template> + <template v-else>{{ i18n.ts._reversi.drawn }}</template> + </div> + </div> + + <div class="_woodenFrame"> + <div :class="$style.boardInner"> + <div v-if="showBoardLabels" :class="$style.labelsX"> + <span v-for="i in game.map[0].length" :key="i" :class="$style.labelsXLabel">{{ String.fromCharCode(64 + i) }}</span> + </div> + <div style="display: flex;"> + <div v-if="showBoardLabels" :class="$style.labelsY"> + <div v-for="i in game.map.length" :key="i" :class="$style.labelsYLabel">{{ i }}</div> + </div> + <div :class="$style.boardCells" :style="cellsStyle"> + <div + v-for="(stone, i) in engine.board" + :key="i" + v-tooltip="`${String.fromCharCode(65 + engine.posToXy(i)[0])}${engine.posToXy(i)[1] + 1}`" + :class="[$style.boardCell, { + [$style.boardCell_empty]: stone == null, + [$style.boardCell_none]: engine.map[i] === 'null', + [$style.boardCell_isEnded]: game.isEnded, + [$style.boardCell_myTurn]: !game.isEnded && isMyTurn, + [$style.boardCell_can]: turnUser ? engine.canPut(turnUser.id === blackUser.id, i) : null, + [$style.boardCell_prev]: engine.prevPos === i + }]" + @click="putStone(i)" + > + <Transition + :enterActiveClass="$style.transition_flip_enterActive" + :leaveActiveClass="$style.transition_flip_leaveActive" + :enterFromClass="$style.transition_flip_enterFrom" + :leaveToClass="$style.transition_flip_leaveTo" + mode="default" + > + <template v-if="useAvatarAsStone"> + <img v-if="stone === true" :class="$style.boardCellStone" :src="blackUser.avatarUrl ?? undefined"/> + <img v-else-if="stone === false" :class="$style.boardCellStone" :src="whiteUser.avatarUrl ?? undefined"/> + </template> + <template v-else> + <img v-if="stone === true" :class="$style.boardCellStone" src="/client-assets/reversi/stone_b.png"/> + <img v-else-if="stone === false" :class="$style.boardCellStone" src="/client-assets/reversi/stone_w.png"/> + </template> + </Transition> + </div> + </div> + <div v-if="showBoardLabels" :class="$style.labelsY"> + <div v-for="i in game.map.length" :key="i" :class="$style.labelsYLabel">{{ i }}</div> + </div> + </div> + <div v-if="showBoardLabels" :class="$style.labelsX"> + <span v-for="i in game.map[0].length" :key="i" :class="$style.labelsXLabel">{{ String.fromCharCode(64 + i) }}</span> + </div> + </div> + </div> + + <div v-if="game.isEnded" class="_panel _gaps_s" style="padding: 16px;"> + <div>{{ logPos }} / {{ game.logs.length }}</div> + <div v-if="!autoplaying" class="_buttonsCenter"> + <MkButton :disabled="logPos === 0" @click="logPos = 0"><i class="ph-caret-left ph-bold ph-lg"></i></MkButton> + <MkButton :disabled="logPos === 0" @click="logPos--"><i class="ph-caret-left ph-bold ph-lg"></i></MkButton> + <MkButton :disabled="logPos === game.logs.length" @click="logPos++"><i class="ph-caret-right ph-bold ph-lg"></i></MkButton> + <MkButton :disabled="logPos === game.logs.length" @click="logPos = game.logs.length"><i class="ph-caret-right ph-bold ph-lg"></i></MkButton> + </div> + <MkButton style="margin: auto;" :disabled="autoplaying" @click="autoplay()"><i class="ph-play ph-bold ph-lg"></i></MkButton> + </div> + + <div class="_panel" style="padding: 16px;"> + <div> + <b>{{ i18n.tsx._reversi.turnCount({ count: logPos }) }}</b> {{ i18n.ts._reversi.black }}:{{ engine.blackCount }} {{ i18n.ts._reversi.white }}:{{ engine.whiteCount }} {{ i18n.ts._reversi.total }}:{{ engine.blackCount + engine.whiteCount }} + </div> + <div> + <div style="display: flex; align-items: center;"> + <span style="margin-right: 8px;">({{ i18n.ts._reversi.black }})</span> + <MkAvatar style="width: 32px; height: 32px; margin-right: 8px;" :user="blackUser" :showIndicator="true"/> + <MkA :to="userPage(blackUser)"><MkUserName :user="blackUser"/></MkA> + </div> + <div> vs </div> + <div style="display: flex; align-items: center;"> + <span style="margin-right: 8px;">({{ i18n.ts._reversi.white }})</span> + <MkAvatar style="width: 32px; height: 32px; margin-right: 8px;" :user="whiteUser" :showIndicator="true"/> + <MkA :to="userPage(whiteUser)"><MkUserName :user="whiteUser"/></MkA> + </div> + </div> + <div> + <p v-if="game.isLlotheo">{{ i18n.ts._reversi.isLlotheo }}</p> + <p v-if="game.loopedBoard">{{ i18n.ts._reversi.loopedMap }}</p> + <p v-if="game.canPutEverywhere">{{ i18n.ts._reversi.canPutEverywhere }}</p> + </div> + </div> + + <MkFolder> + <template #label>{{ i18n.ts.options }}</template> + <div class="_gaps_s" style="text-align: left;"> + <MkSwitch v-model="showBoardLabels">{{ i18n.ts._reversi.showBoardLabels }}</MkSwitch> + <MkSwitch v-model="useAvatarAsStone">{{ i18n.ts._reversi.useAvatarAsStone }}</MkSwitch> + </div> + </MkFolder> + + <div class="_buttonsCenter"> + <MkButton v-if="!game.isEnded && iAmPlayer" danger @click="surrender">{{ i18n.ts._reversi.surrender }}</MkButton> + <MkButton @click="share">{{ i18n.ts.share }}</MkButton> + </div> + + <MkA v-if="game.isEnded" :to="`/reversi`"> + <img src="/client-assets/reversi/logo.png" style="display: block; max-width: 100%; width: 200px; margin: auto;"/> + </MkA> + </div> +</MkSpacer> +</template> + +<script lang="ts" setup> +import { computed, onActivated, onDeactivated, onMounted, onUnmounted, ref, shallowRef, triggerRef, watch } from 'vue'; +import * as Misskey from 'misskey-js'; +import * as Reversi from 'misskey-reversi'; +import MkButton from '@/components/MkButton.vue'; +import MkFolder from '@/components/MkFolder.vue'; +import MkSwitch from '@/components/MkSwitch.vue'; +import { deepClone } from '@/scripts/clone.js'; +import { useInterval } from '@/scripts/use-interval.js'; +import { signinRequired } from '@/account.js'; +import { i18n } from '@/i18n.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; +import { userPage } from '@/filters/user.js'; +import * as sound from '@/scripts/sound.js'; +import * as os from '@/os.js'; +import { confetti } from '@/scripts/confetti.js'; + +const $i = signinRequired(); + +const props = defineProps<{ + game: Misskey.entities.ReversiGameDetailed; + connection?: Misskey.ChannelConnection<Misskey.Channels['reversiGame']> | null; +}>(); + +const showBoardLabels = ref<boolean>(false); +const useAvatarAsStone = ref<boolean>(true); +const autoplaying = ref<boolean>(false); +// eslint-disable-next-line vue/no-setup-props-destructure +const game = ref<Misskey.entities.ReversiGameDetailed & { logs: Reversi.Serializer.SerializedLog[] }>(deepClone(props.game)); +const logPos = ref<number>(game.value.logs.length); +const engine = shallowRef<Reversi.Game>(Reversi.Serializer.restoreGame({ + map: game.value.map, + isLlotheo: game.value.isLlotheo, + canPutEverywhere: game.value.canPutEverywhere, + loopedBoard: game.value.loopedBoard, + logs: game.value.logs, +})); + +const iAmPlayer = computed(() => { + return game.value.user1Id === $i.id || game.value.user2Id === $i.id; +}); + +const myColor = computed(() => { + if (!iAmPlayer.value) return null; + if (game.value.user1Id === $i.id && game.value.black === 1) return true; + if (game.value.user2Id === $i.id && game.value.black === 2) return true; + return false; +}); + +const opColor = computed(() => { + if (!iAmPlayer.value) return null; + return !myColor.value; +}); + +const blackUser = computed(() => { + return game.value.black === 1 ? game.value.user1 : game.value.user2; +}); + +const whiteUser = computed(() => { + return game.value.black === 1 ? game.value.user2 : game.value.user1; +}); + +const turnUser = computed(() => { + if (engine.value.turn === true) { + return game.value.black === 1 ? game.value.user1 : game.value.user2; + } else if (engine.value.turn === false) { + return game.value.black === 1 ? game.value.user2 : game.value.user1; + } else { + return null; + } +}); + +const isMyTurn = computed(() => { + if (!iAmPlayer.value) return false; + const u = turnUser.value; + if (u == null) return false; + return u.id === $i.id; +}); + +const cellsStyle = computed(() => { + return { + 'grid-template-rows': `repeat(${game.value.map.length}, 1fr)`, + 'grid-template-columns': `repeat(${game.value.map[0].length}, 1fr)`, + }; +}); + +watch(logPos, (v) => { + if (!game.value.isEnded) return; + engine.value = Reversi.Serializer.restoreGame({ + map: game.value.map, + isLlotheo: game.value.isLlotheo, + canPutEverywhere: game.value.canPutEverywhere, + loopedBoard: game.value.loopedBoard, + logs: game.value.logs.slice(0, v), + }); +}); + +if (game.value.isStarted && !game.value.isEnded) { + useInterval(() => { + if (game.value.isEnded) return; + const crc32 = engine.value.calcCrc32(); + if (_DEV_) console.log('crc32', crc32); + misskeyApi('reversi/verify', { + gameId: game.value.id, + crc32: crc32.toString(), + }).then((res) => { + if (res.desynced) { + if (_DEV_) console.log('resynced'); + restoreGame(res.game!); + } + }); + }, 10000, { immediate: false, afterMounted: true }); +} + +const appliedOps: string[] = []; + +function putStone(pos: number) { + if (game.value.isEnded) return; + if (!iAmPlayer.value) return; + if (!isMyTurn.value) return; + if (!engine.value.canPut(myColor.value!, pos)) return; + + engine.value.putStone(pos); + + triggerRef(engine); + + sound.playUrl('/client-assets/reversi/put.mp3', { + volume: 1, + playbackRate: 1, + }); + + const id = Math.random().toString(36).slice(2); + props.connection!.send('putStone', { + pos: pos, + id, + }); + appliedOps.push(id); + + myTurnTimerRmain.value = game.value.timeLimitForEachTurn; + opTurnTimerRmain.value = game.value.timeLimitForEachTurn; + + checkEnd(); +} + +const myTurnTimerRmain = ref<number>(game.value.timeLimitForEachTurn); +const opTurnTimerRmain = ref<number>(game.value.timeLimitForEachTurn); + +const TIMER_INTERVAL_SEC = 3; +if (!props.game.isEnded) { + useInterval(() => { + if (myTurnTimerRmain.value > 0) { + myTurnTimerRmain.value = Math.max(0, myTurnTimerRmain.value - TIMER_INTERVAL_SEC); + } + if (opTurnTimerRmain.value > 0) { + opTurnTimerRmain.value = Math.max(0, opTurnTimerRmain.value - TIMER_INTERVAL_SEC); + } + + if (iAmPlayer.value) { + if ((isMyTurn.value && myTurnTimerRmain.value === 0) || (!isMyTurn.value && opTurnTimerRmain.value === 0)) { + props.connection!.send('claimTimeIsUp', {}); + } + } + }, TIMER_INTERVAL_SEC * 1000, { immediate: false, afterMounted: true }); +} + +async function onStreamLog(log) { + game.value.logs = Reversi.Serializer.serializeLogs([ + ...Reversi.Serializer.deserializeLogs(game.value.logs), + log, + ]); + + logPos.value++; + + if (log.id == null || !appliedOps.includes(log.id)) { + switch (log.operation) { + case 'put': { + sound.playUrl('/client-assets/reversi/put.mp3', { + volume: 1, + playbackRate: 1, + }); + + if (log.player !== engine.value.turn) { // = desyncãŒç™ºç”Ÿã—ã¦ã„ã‚‹ + const _game = await misskeyApi('reversi/show-game', { + gameId: props.game.id, + }); + restoreGame(_game); + return; + } + + engine.value.putStone(log.pos); + triggerRef(engine); + + myTurnTimerRmain.value = game.value.timeLimitForEachTurn; + opTurnTimerRmain.value = game.value.timeLimitForEachTurn; + + checkEnd(); + break; + } + + default: + break; + } + } +} + +function onStreamEnded(x) { + game.value = deepClone(x.game); + + if (game.value.winnerId === $i.id) { + confetti({ + duration: 1000 * 3, + }); + + sound.playUrl('/client-assets/reversi/win.mp3', { + volume: 1, + playbackRate: 1, + }); + } else { + sound.playUrl('/client-assets/reversi/lose.mp3', { + volume: 1, + playbackRate: 1, + }); + } +} + +function checkEnd() { + game.value.isEnded = engine.value.isEnded; + if (game.value.isEnded) { + if (engine.value.winner === true) { + game.value.winnerId = game.value.black === 1 ? game.value.user1Id : game.value.user2Id; + game.value.winner = game.value.black === 1 ? game.value.user1 : game.value.user2; + } else if (engine.value.winner === false) { + game.value.winnerId = game.value.black === 1 ? game.value.user2Id : game.value.user1Id; + game.value.winner = game.value.black === 1 ? game.value.user2 : game.value.user1; + } else { + game.value.winnerId = null; + game.value.winner = null; + } + } +} + +function restoreGame(_game) { + game.value = deepClone(_game); + + engine.value = Reversi.Serializer.restoreGame({ + map: game.value.map, + isLlotheo: game.value.isLlotheo, + canPutEverywhere: game.value.canPutEverywhere, + loopedBoard: game.value.loopedBoard, + logs: game.value.logs, + }); + + logPos.value = game.value.logs.length; + + checkEnd(); +} + +async function surrender() { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.ts.areYouSure, + }); + if (canceled) return; + + misskeyApi('reversi/surrender', { + gameId: game.value.id, + }); +} + +function autoplay() { + autoplaying.value = true; + logPos.value = 0; + const logs = Reversi.Serializer.deserializeLogs(game.value.logs); + + window.setTimeout(() => { + logPos.value = 1; + + let i = 1; + let previousLog = logs[0]; + const tick = () => { + const log = logs[i]; + const time = log.time - previousLog.time; + setTimeout(() => { + i++; + logPos.value++; + previousLog = log; + + if (i < logs.length) { + tick(); + } else { + autoplaying.value = false; + } + }, time); + }; + + tick(); + }, 1000); +} + +function share() { + os.post({ + initialText: `#MisskeyReversi ${location.href}`, + instant: true, + }); +} + +onMounted(() => { + if (props.connection != null) { + props.connection.on('log', onStreamLog); + props.connection.on('ended', onStreamEnded); + } +}); + +onActivated(() => { + if (props.connection != null) { + props.connection.on('log', onStreamLog); + props.connection.on('ended', onStreamEnded); + } +}); + +onDeactivated(() => { + if (props.connection != null) { + props.connection.off('log', onStreamLog); + props.connection.off('ended', onStreamEnded); + } +}); + +onUnmounted(() => { + if (props.connection != null) { + props.connection.off('log', onStreamLog); + props.connection.off('ended', onStreamEnded); + } +}); +</script> + +<style lang="scss" module> +@use "sass:math"; + +.transition_flip_enterActive, +.transition_flip_leaveActive { + backface-visibility: hidden; + transition: opacity 0.5s ease, transform 0.5s ease; +} +.transition_flip_enterFrom { + transform: rotateY(-180deg); + opacity: 0; +} +.transition_flip_leaveTo { + transform: rotateY(180deg); + opacity: 0; +} + +$label-size: 16px; +$gap: 4px; + +.root { + text-align: center; +} + +.boardInner { + padding: 32px; + + background: var(--panel); + box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410; + border-radius: 8px; +} + +@container (max-width: 400px) { + .boardInner { + padding: 16px; + } +} + +.labelsX { + height: $label-size; + padding: 0 $label-size; + display: flex; +} + +.labelsXLabel { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.8em; + + &:first-child { + margin-left: -(math.div($gap, 2)); + } + + &:last-child { + margin-right: -(math.div($gap, 2)); + } +} + +.labelsY { + width: $label-size; + display: flex; + flex-direction: column; +} + +.labelsYLabel { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + font-size: 12px; + + &:first-child { + margin-top: -(math.div($gap, 2)); + } + + &:last-child { + margin-bottom: -(math.div($gap, 2)); + } +} + +.boardCells { + flex: 1; + display: grid; + grid-gap: $gap; +} + +.boardCell { + background: transparent; + border-radius: 100%; + aspect-ratio: 1; + transform-style: preserve-3d; + perspective: 150px; + transition: border 0.25s ease, opacity 0.25s ease; + + &.boardCell_empty { + border: solid 2px var(--divider); + } + + &.boardCell_empty.boardCell_can { + border-color: var(--accent); + opacity: 0.5; + } + + &.boardCell_empty.boardCell_myTurn { + border-color: var(--divider); + opacity: 1; + + &.boardCell_can { + border-color: var(--accent); + cursor: pointer; + + &:hover { + background: var(--accent); + } + } + } + + &.boardCell_prev { + box-shadow: 0 0 0 4px var(--accent); + } + + &.boardCell_isEnded { + border-color: var(--divider); + } + + &.boardCell_none { + border-color: transparent !important; + } +} + +.boardCellStone { + position: absolute; + top: 0; + left: 0; + pointer-events: none; + user-select: none; + display: block; + width: 100%; + height: 100%; + border-radius: 100%; +} +</style> diff --git a/packages/frontend/src/pages/reversi/game.setting.vue b/packages/frontend/src/pages/reversi/game.setting.vue new file mode 100644 index 0000000000000000000000000000000000000000..93b0972e9cede2255430459eb8af572c794768e9 --- /dev/null +++ b/packages/frontend/src/pages/reversi/game.setting.vue @@ -0,0 +1,298 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkStickyContainer> + <MkSpacer :contentMax="600"> + <div style="text-align: center;"><b><MkUserName :user="game.user1"/></b> vs <b><MkUserName :user="game.user2"/></b></div> + + <div :class="{ [$style.disallow]: isReady }"> + <div class="_gaps" :class="{ [$style.disallowInner]: isReady }"> + <div style="font-size: 1.5em; text-align: center;">{{ i18n.ts._reversi.gameSettings }}</div> + + <template v-if="game.noIrregularRules"> + <div>{{ i18n.ts._reversi.disallowIrregularRules }}</div> + </template> + <template v-else> + <div class="_panel"> + <div style="display: flex; align-items: center; padding: 16px; border-bottom: solid 1px var(--divider);"> + <div>{{ mapName }}</div> + <MkButton style="margin-left: auto;" @click="chooseMap">{{ i18n.ts._reversi.chooseBoard }}</MkButton> + </div> + + <div style="padding: 16px;"> + <div v-if="game.map == null"><i class="ph-dice-five ph-bold ph-lg"></i></div> + <div v-else :class="$style.board" :style="{ 'grid-template-rows': `repeat(${ game.map.length }, 1fr)`, 'grid-template-columns': `repeat(${ game.map[0].length }, 1fr)` }"> + <div v-for="(x, i) in game.map.join('')" :class="[$style.boardCell, { [$style.boardCellNone]: x == ' ' }]" @click="onMapCellClick(i, x)"> + <i v-if="x === 'b' || x === 'w'" style="pointer-events: none; user-select: none;" :class="x === 'b' ? 'ph-circle-half ph-bold ph-lg' : 'ph-circle ph-bold ph-lg'"></i> + </div> + </div> + </div> + </div> + + <MkFolder :defaultOpen="true"> + <template #label>{{ i18n.ts._reversi.blackOrWhite }}</template> + + <MkRadios v-model="game.bw"> + <option value="random">{{ i18n.ts.random }}</option> + <option :value="'1'"> + <I18n :src="i18n.ts._reversi.blackIs" tag="span"> + <template #name> + <b><MkUserName :user="game.user1"/></b> + </template> + </I18n> + </option> + <option :value="'2'"> + <I18n :src="i18n.ts._reversi.blackIs" tag="span"> + <template #name> + <b><MkUserName :user="game.user2"/></b> + </template> + </I18n> + </option> + </MkRadios> + </MkFolder> + + <MkFolder :defaultOpen="true"> + <template #label>{{ i18n.ts._reversi.timeLimitForEachTurn }}</template> + <template #suffix>{{ game.timeLimitForEachTurn }}{{ i18n.ts._time.second }}</template> + + <MkRadios v-model="game.timeLimitForEachTurn"> + <option :value="5">5{{ i18n.ts._time.second }}</option> + <option :value="10">10{{ i18n.ts._time.second }}</option> + <option :value="30">30{{ i18n.ts._time.second }}</option> + <option :value="60">60{{ i18n.ts._time.second }}</option> + <option :value="90">90{{ i18n.ts._time.second }}</option> + <option :value="120">120{{ i18n.ts._time.second }}</option> + <option :value="180">180{{ i18n.ts._time.second }}</option> + <option :value="3600">3600{{ i18n.ts._time.second }}</option> + </MkRadios> + </MkFolder> + + <MkFolder :defaultOpen="true"> + <template #label>{{ i18n.ts._reversi.rules }}</template> + + <div class="_gaps_s"> + <MkSwitch v-model="game.isLlotheo" @update:modelValue="updateSettings('isLlotheo')">{{ i18n.ts._reversi.isLlotheo }}</MkSwitch> + <MkSwitch v-model="game.loopedBoard" @update:modelValue="updateSettings('loopedBoard')">{{ i18n.ts._reversi.loopedMap }}</MkSwitch> + <MkSwitch v-model="game.canPutEverywhere" @update:modelValue="updateSettings('canPutEverywhere')">{{ i18n.ts._reversi.canPutEverywhere }}</MkSwitch> + </div> + </MkFolder> + </template> + </div> + </div> + </MkSpacer> + <template #footer> + <div :class="$style.footer"> + <MkSpacer :contentMax="700" :marginMin="16" :marginMax="16"> + <div style="text-align: center;" class="_gaps_s"> + <div v-if="opponentHasSettingsChanged" style="color: var(--warn);">{{ i18n.ts._reversi.opponentHasSettingsChanged }}</div> + <div> + <template v-if="isReady && isOpReady">{{ i18n.ts._reversi.thisGameIsStartedSoon }}<MkEllipsis/></template> + <template v-if="isReady && !isOpReady">{{ i18n.ts._reversi.waitingForOther }}<MkEllipsis/></template> + <template v-if="!isReady && isOpReady">{{ i18n.ts._reversi.waitingForMe }}</template> + <template v-if="!isReady && !isOpReady">{{ i18n.ts._reversi.waitingBoth }}<MkEllipsis/></template> + </div> + <div class="_buttonsCenter"> + <MkButton rounded danger @click="cancel">{{ i18n.ts.cancel }}</MkButton> + <MkButton v-if="!isReady" rounded primary @click="ready">{{ i18n.ts._reversi.ready }}</MkButton> + <MkButton v-if="isReady" rounded @click="unready">{{ i18n.ts._reversi.cancelReady }}</MkButton> + </div> + <div> + <MkSwitch v-model="shareWhenStart">{{ i18n.ts._reversi.shareToTlTheGameWhenStart }}</MkSwitch> + </div> + </div> + </MkSpacer> + </div> + </template> +</MkStickyContainer> +</template> + +<script lang="ts" setup> +import { computed, watch, ref, onMounted, shallowRef, onUnmounted } from 'vue'; +import * as Misskey from 'misskey-js'; +import * as Reversi from 'misskey-reversi'; +import { i18n } from '@/i18n.js'; +import { signinRequired } from '@/account.js'; +import { deepClone } from '@/scripts/clone.js'; +import MkButton from '@/components/MkButton.vue'; +import MkRadios from '@/components/MkRadios.vue'; +import MkSwitch from '@/components/MkSwitch.vue'; +import MkFolder from '@/components/MkFolder.vue'; +import * as os from '@/os.js'; +import { MenuItem } from '@/types/menu.js'; +import { useRouter } from '@/router/supplier.js'; + +const $i = signinRequired(); + +const router = useRouter(); + +const mapCategories = Array.from(new Set(Object.values(Reversi.maps).map(x => x.category))); + +const props = defineProps<{ + game: Misskey.entities.ReversiGameDetailed; + connection: Misskey.ChannelConnection; +}>(); + +const shareWhenStart = defineModel<boolean>('shareWhenStart', { default: false }); + +const game = ref<Misskey.entities.ReversiGameDetailed>(deepClone(props.game)); + +const mapName = computed(() => { + if (game.value.map == null) return 'Random'; + const found = Object.values(Reversi.maps).find(x => x.data.join('') === game.value.map.join('')); + return found ? found.name! : '-Custom-'; +}); +const isReady = computed(() => { + if (game.value.user1Id === $i.id && game.value.user1Ready) return true; + if (game.value.user2Id === $i.id && game.value.user2Ready) return true; + return false; +}); +const isOpReady = computed(() => { + if (game.value.user1Id !== $i.id && game.value.user1Ready) return true; + if (game.value.user2Id !== $i.id && game.value.user2Ready) return true; + return false; +}); + +const opponentHasSettingsChanged = ref(false); + +watch(() => game.value.bw, () => { + updateSettings('bw'); +}); + +watch(() => game.value.timeLimitForEachTurn, () => { + updateSettings('timeLimitForEachTurn'); +}); + +function chooseMap(ev: MouseEvent) { + const menu: MenuItem[] = []; + + for (const c of mapCategories) { + const maps = Object.values(Reversi.maps).filter(x => x.category === c); + if (maps.length === 0) continue; + if (c != null) { + menu.push({ + type: 'label', + text: c, + }); + } + for (const m of maps) { + menu.push({ + text: m.name!, + action: () => { + game.value.map = m.data; + updateSettings('map'); + }, + }); + } + } + + os.popupMenu(menu, ev.currentTarget ?? ev.target); +} + +async function cancel() { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.ts.areYouSure, + }); + if (canceled) return; + + props.connection.send('cancel', {}); + + router.push('/reversi'); +} + +function ready() { + props.connection.send('ready', true); + opponentHasSettingsChanged.value = false; +} + +function unready() { + props.connection.send('ready', false); +} + +function onChangeReadyStates(states) { + game.value.user1Ready = states.user1; + game.value.user2Ready = states.user2; +} + +function updateSettings(key: keyof Misskey.entities.ReversiGameDetailed) { + props.connection.send('updateSettings', { + key: key, + value: game.value[key], + }); +} + +function onUpdateSettings({ userId, key, value }: { userId: string; key: keyof Misskey.entities.ReversiGameDetailed; value: any; }) { + if (userId === $i.id) return; + if (game.value[key] === value) return; + game.value[key] = value; + if (isReady.value) { + opponentHasSettingsChanged.value = true; + unready(); + } +} + +function onMapCellClick(pos: number, pixel: string) { + const x = pos % game.value.map[0].length; + const y = Math.floor(pos / game.value.map[0].length); + const newPixel = + pixel === ' ' ? '-' : + pixel === '-' ? 'b' : + pixel === 'b' ? 'w' : + ' '; + const line = game.value.map[y].split(''); + line[x] = newPixel; + game.value.map[y] = line.join(''); + updateSettings('map'); +} + +props.connection.on('changeReadyStates', onChangeReadyStates); +props.connection.on('updateSettings', onUpdateSettings); + +onUnmounted(() => { + props.connection.off('changeReadyStates', onChangeReadyStates); + props.connection.off('updateSettings', onUpdateSettings); +}); +</script> + +<style lang="scss" module> +.disallow { + cursor: not-allowed; +} +.disallowInner { + pointer-events: none; + user-select: none; + opacity: 0.7; +} + +.board { + display: grid; + grid-gap: 4px; + width: 300px; + height: 300px; + margin: 0 auto; + color: var(--fg); +} + +.boardCell { + display: grid; + place-items: center; + background: transparent; + border: solid 2px var(--divider); + border-radius: 6px; + overflow: clip; + cursor: pointer; +} +.boardCellNone { + border-color: transparent; +} + +.footer { + -webkit-backdrop-filter: var(--blur, blur(15px)); + backdrop-filter: var(--blur, blur(15px)); + background: var(--acrylicBg); + border-top: solid 0.5px var(--divider); +} +</style> diff --git a/packages/frontend/src/pages/reversi/game.vue b/packages/frontend/src/pages/reversi/game.vue new file mode 100644 index 0000000000000000000000000000000000000000..21b7797240dfb3af7cccea5fec9294dfe491b5c3 --- /dev/null +++ b/packages/frontend/src/pages/reversi/game.vue @@ -0,0 +1,120 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div v-if="game == null || (!game.isEnded && connection == null)"><MkLoading/></div> +<GameSetting v-else-if="!game.isStarted" v-model:shareWhenStart="shareWhenStart" :game="game" :connection="connection!"/> +<GameBoard v-else :game="game" :connection="connection"/> +</template> + +<script lang="ts" setup> +import { computed, watch, ref, onMounted, shallowRef, onUnmounted } from 'vue'; +import * as Misskey from 'misskey-js'; +import GameSetting from './game.setting.vue'; +import GameBoard from './game.board.vue'; +import { misskeyApi } from '@/scripts/misskey-api.js'; +import { definePageMetadata } from '@/scripts/page-metadata.js'; +import { useStream } from '@/stream.js'; +import { signinRequired } from '@/account.js'; +import { useRouter } from '@/router/supplier.js'; +import * as os from '@/os.js'; +import { i18n } from '@/i18n.js'; +import { useInterval } from '@/scripts/use-interval.js'; + +const $i = signinRequired(); + +const router = useRouter(); + +const props = defineProps<{ + gameId: string; +}>(); + +const game = shallowRef<Misskey.entities.ReversiGameDetailed | null>(null); +const connection = shallowRef<Misskey.ChannelConnection | null>(null); +const shareWhenStart = ref(false); + +watch(() => props.gameId, () => { + fetchGame(); +}); + +function start(_game: Misskey.entities.ReversiGameDetailed) { + if (game.value?.isStarted) return; + + if (shareWhenStart.value) { + misskeyApi('notes/create', { + text: i18n.ts._reversi.iStartedAGame + '\n' + location.href, + visibility: 'home', + }); + } + + game.value = _game; +} + +async function fetchGame() { + const _game = await misskeyApi('reversi/show-game', { + gameId: props.gameId, + }); + + game.value = _game; + shareWhenStart.value = false; + + if (connection.value) { + connection.value.dispose(); + } + if (!game.value.isEnded) { + connection.value = useStream().useChannel('reversiGame', { + gameId: game.value.id, + }); + connection.value.on('started', x => { + start(x.game); + }); + connection.value.on('canceled', x => { + connection.value?.dispose(); + + if (x.userId !== $i.id) { + os.alert({ + type: 'warning', + text: i18n.ts._reversi.gameCanceled, + }); + router.push('/reversi'); + } + }); + } +} + +// 通信をå–ã‚Šã“ã¼ã—ãŸå ´åˆã®æ•‘済 +useInterval(async () => { + if (game.value == null) return; + if (game.value.isStarted) return; + + const _game = await misskeyApi('reversi/show-game', { + gameId: props.gameId, + }); + + if (_game.isStarted) { + start(_game); + } else { + game.value = _game; + } +}, 1000 * 10, { + immediate: false, + afterMounted: true, +}); + +onMounted(() => { + fetchGame(); +}); + +onUnmounted(() => { + if (connection.value) { + connection.value.dispose(); + } +}); + +definePageMetadata(() => ({ + title: 'Reversi', + icon: 'ph-game-controller ph-bold ph-lg', +})); +</script> diff --git a/packages/frontend/src/pages/reversi/index.vue b/packages/frontend/src/pages/reversi/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..c863b918344b27a53cdbe8b19161846d712cb652 --- /dev/null +++ b/packages/frontend/src/pages/reversi/index.vue @@ -0,0 +1,353 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkSpacer v-if="!matchingAny && !matchingUser" :contentMax="600"> + <div class="_gaps"> + <div> + <img src="/client-assets/reversi/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/> + </div> + + <div class="_panel _gaps" style="padding: 16px;"> + <div class="_buttonsCenter"> + <MkButton primary gradate rounded @click="matchAny">{{ i18n.ts._reversi.freeMatch }}</MkButton> + <MkButton primary gradate rounded @click="matchUser">{{ i18n.ts.invite }}</MkButton> + </div> + <div style="font-size: 90%; opacity: 0.7; text-align: center;"><i class="ph-music-notes ph-bold ph-lg"></i> {{ i18n.ts.soundWillBePlayed }}</div> + </div> + + <MkFolder v-if="invitations.length > 0" :defaultOpen="true"> + <template #label>{{ i18n.ts.invitations }}</template> + <div class="_gaps_s"> + <button v-for="user in invitations" :key="user.id" v-panel :class="$style.invitation" class="_button" tabindex="-1" @click="accept(user)"> + <MkAvatar style="width: 32px; height: 32px; margin-right: 8px;" :user="user" :showIndicator="true"/> + <span style="margin-right: 8px;"><b><MkUserName :user="user"/></b></span> + <span>@{{ user.username }}</span> + </button> + </div> + </MkFolder> + + <MkFolder v-if="$i" :defaultOpen="true"> + <template #label>{{ i18n.ts._reversi.myGames }}</template> + <MkPagination :pagination="myGamesPagination" :disableAutoLoad="true"> + <template #default="{ items }"> + <div :class="$style.gamePreviews"> + <MkA v-for="g in items" :key="g.id" v-panel :class="[$style.gamePreview, !g.isStarted && !g.isEnded && $style.gamePreviewWaiting, g.isStarted && !g.isEnded && $style.gamePreviewActive]" tabindex="-1" :to="`/reversi/g/${g.id}`"> + <div :class="$style.gamePreviewPlayers"> + <span v-if="g.winnerId === g.user1Id" style="margin-right: 0.75em; color: var(--accent); font-weight: bold;"><i class="ph-trophy ph-bold ph-lg"></i></span> + <span v-if="g.winnerId === g.user2Id" style="margin-right: 0.75em; visibility: hidden;"><i class="ph-x ph-bold ph-lg"></i></span> + <MkAvatar :class="$style.gamePreviewPlayersAvatar" :user="g.user1"/> + <span style="margin: 0 1em;">vs</span> + <MkAvatar :class="$style.gamePreviewPlayersAvatar" :user="g.user2"/> + <span v-if="g.winnerId === g.user1Id" style="margin-left: 0.75em; visibility: hidden;"><i class="ph-x ph-bold ph-lg"></i></span> + <span v-if="g.winnerId === g.user2Id" style="margin-left: 0.75em; color: var(--accent); font-weight: bold;"><i class="ph-trophy ph-bold ph-lg"></i></span> + </div> + <div :class="$style.gamePreviewFooter"> + <span v-if="g.isStarted && !g.isEnded" :class="$style.gamePreviewStatusActive">{{ i18n.ts._reversi.playing }}</span> + <span v-else-if="!g.isEnded" :class="$style.gamePreviewStatusWaiting"><MkEllipsis/></span> + <span v-else>{{ i18n.ts._reversi.ended }}</span> + <MkTime style="margin-left: auto; opacity: 0.7;" :time="g.createdAt"/> + </div> + </MkA> + </div> + </template> + </MkPagination> + </MkFolder> + + <MkFolder :defaultOpen="true"> + <template #label>{{ i18n.ts._reversi.allGames }}</template> + <MkPagination :pagination="gamesPagination" :disableAutoLoad="true"> + <template #default="{ items }"> + <div :class="$style.gamePreviews"> + <MkA v-for="g in items" :key="g.id" v-panel :class="[$style.gamePreview, !g.isStarted && !g.isEnded && $style.gamePreviewWaiting, g.isStarted && !g.isEnded && $style.gamePreviewActive]" tabindex="-1" :to="`/reversi/g/${g.id}`"> + <div :class="$style.gamePreviewPlayers"> + <span v-if="g.winnerId === g.user1Id" style="margin-right: 0.75em; color: var(--accent); font-weight: bold;"><i class="ph-trophy ph-bold ph-lg"></i></span> + <span v-if="g.winnerId === g.user2Id" style="margin-right: 0.75em; visibility: hidden;"><i class="ph-x ph-bold ph-lg"></i></span> + <MkAvatar :class="$style.gamePreviewPlayersAvatar" :user="g.user1"/> + <span style="margin: 0 1em;">vs</span> + <MkAvatar :class="$style.gamePreviewPlayersAvatar" :user="g.user2"/> + <span v-if="g.winnerId === g.user1Id" style="margin-left: 0.75em; visibility: hidden;"><i class="ph-x ph-bold ph-lg"></i></span> + <span v-if="g.winnerId === g.user2Id" style="margin-left: 0.75em; color: var(--accent); font-weight: bold;"><i class="ph-trophy ph-bold ph-lg"></i></span> + </div> + <div :class="$style.gamePreviewFooter"> + <span v-if="g.isStarted && !g.isEnded" :class="$style.gamePreviewStatusActive">{{ i18n.ts._reversi.playing }}</span> + <span v-else-if="!g.isEnded" :class="$style.gamePreviewStatusWaiting"><MkEllipsis/></span> + <span v-else>{{ i18n.ts._reversi.ended }}</span> + <MkTime style="margin-left: auto; opacity: 0.7;" :time="g.createdAt"/> + </div> + </MkA> + </div> + </template> + </MkPagination> + </MkFolder> + </div> +</MkSpacer> +<MkSpacer v-else :contentMax="600"> + <div :class="$style.waitingScreen"> + <div v-if="matchingUser" :class="$style.waitingScreenTitle"> + <I18n :src="i18n.ts.waitingFor" tag="span"> + <template #x> + <b><MkUserName :user="matchingUser"/></b> + </template> + </I18n> + <MkEllipsis/> + </div> + <div v-else :class="$style.waitingScreenTitle"> + {{ i18n.ts._reversi.lookingForPlayer }}<MkEllipsis/> + </div> + <div class="cancel"> + <MkButton inline rounded @click="cancelMatching">{{ i18n.ts.cancel }}</MkButton> + </div> + </div> +</MkSpacer> +</template> + +<script lang="ts" setup> +import { onDeactivated, onMounted, onUnmounted, ref } from 'vue'; +import * as Misskey from 'misskey-js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; +import { definePageMetadata } from '@/scripts/page-metadata.js'; +import { useStream } from '@/stream.js'; +import MkButton from '@/components/MkButton.vue'; +import MkFolder from '@/components/MkFolder.vue'; +import { i18n } from '@/i18n.js'; +import { $i } from '@/account.js'; +import MkPagination from '@/components/MkPagination.vue'; +import { useRouter } from '@/router/supplier.js'; +import * as os from '@/os.js'; +import { useInterval } from '@/scripts/use-interval.js'; +import { pleaseLogin } from '@/scripts/please-login.js'; +import * as sound from '@/scripts/sound.js'; + +const myGamesPagination = { + endpoint: 'reversi/games' as const, + limit: 10, + params: { + my: true, + }, +}; + +const gamesPagination = { + endpoint: 'reversi/games' as const, + limit: 10, +}; + +const router = useRouter(); + +if ($i) { + const connection = useStream().useChannel('reversi'); + + connection.on('matched', x => { + if (matchingUser.value != null || matchingAny.value) { + startGame(x.game); + } + }); + + connection.on('invited', invitation => { + if (invitations.value.some(x => x.id === invitation.user.id)) return; + invitations.value.unshift(invitation.user); + }); + + onUnmounted(() => { + connection.dispose(); + }); +} + +const invitations = ref<Misskey.entities.UserLite[]>([]); +const matchingUser = ref<Misskey.entities.UserLite | null>(null); +const matchingAny = ref<boolean>(false); +const noIrregularRules = ref<boolean>(false); + +function startGame(game: Misskey.entities.ReversiGameDetailed) { + matchingUser.value = null; + matchingAny.value = false; + + sound.playUrl('/client-assets/reversi/matched.mp3', { + volume: 1, + playbackRate: 1, + }); + + router.push(`/reversi/g/${game.id}`); +} + +async function matchHeatbeat() { + if (matchingUser.value) { + const res = await misskeyApi('reversi/match', { + userId: matchingUser.value.id, + }); + + if (res != null) { + startGame(res); + } + } else if (matchingAny.value) { + const res = await misskeyApi('reversi/match', { + userId: null, + noIrregularRules: noIrregularRules.value, + }); + + if (res != null) { + startGame(res); + } + } +} + +async function matchUser() { + pleaseLogin(); + + const user = await os.selectUser({ includeSelf: false, localOnly: true }); + if (user == null) return; + + matchingUser.value = user; + + matchHeatbeat(); +} + +function matchAny(ev: MouseEvent) { + pleaseLogin(); + + os.popupMenu([{ + text: i18n.ts._reversi.allowIrregularRules, + action: () => { + noIrregularRules.value = false; + matchingAny.value = true; + matchHeatbeat(); + }, + }, { + text: i18n.ts._reversi.disallowIrregularRules, + action: () => { + noIrregularRules.value = true; + matchingAny.value = true; + matchHeatbeat(); + }, + }], ev.currentTarget ?? ev.target); +} + +function cancelMatching() { + if (matchingUser.value) { + misskeyApi('reversi/cancel-match', { userId: matchingUser.value.id }); + matchingUser.value = null; + } else if (matchingAny.value) { + misskeyApi('reversi/cancel-match', { userId: null }); + matchingAny.value = false; + } +} + +async function accept(user) { + const game = await misskeyApi('reversi/match', { + userId: user.id, + }); + if (game) { + startGame(game); + } +} + +useInterval(matchHeatbeat, 1000 * 5, { immediate: false, afterMounted: true }); + +onMounted(() => { + misskeyApi('reversi/invitations').then(_invitations => { + invitations.value = _invitations; + }); + + window.addEventListener('beforeunload', cancelMatching); +}); + +onDeactivated(() => { + cancelMatching(); +}); + +onUnmounted(() => { + cancelMatching(); +}); + +definePageMetadata(() => ({ + title: 'Reversi', + icon: 'ph-game-controller ph-bold ph-lg', +})); +</script> + +<style lang="scss" module> +@keyframes blink { + 0% { opacity: 1; } + 50% { opacity: 0.2; } +} + +.invitation { + display: flex; + box-sizing: border-box; + width: 100%; + padding: 16px; + line-height: 32px; + text-align: left; +} + +.gamePreviews { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); + grid-gap: var(--margin); +} + +.gamePreview { + font-size: 90%; + border-radius: 8px; + overflow: clip; +} + +.gamePreviewActive { + box-shadow: inset 0 0 8px 0px var(--accent); +} + +.gamePreviewWaiting { + box-shadow: inset 0 0 8px 0px var(--warn); +} + +.gamePreviewPlayers { + text-align: center; + padding: 16px; + line-height: 32px; +} + +.gamePreviewPlayersAvatar { + width: 32px; + height: 32px; + + &:first-child { + margin-right: 8px; + } + + &:last-child { + margin-left: 8px; + } +} + +.gamePreviewFooter { + display: flex; + align-items: baseline; + border-top: solid 0.5px var(--divider); + padding: 6px 10px; + font-size: 0.9em; +} + +.gamePreviewStatusActive { + color: var(--accent); + font-weight: bold; + animation: blink 2s infinite; +} + +.gamePreviewStatusWaiting { + color: var(--warn); + font-weight: bold; + animation: blink 2s infinite; +} + +.waitingScreen { + text-align: center; +} + +.waitingScreenTitle { + font-size: 1.5em; + margin-bottom: 16px; + margin-top: 32px; +} +</style> diff --git a/packages/frontend/src/pages/role.vue b/packages/frontend/src/pages/role.vue index 6dce4f187d3cc4fb3c4a376ec2e71694d50f8d2c..8621b61eeb417e9dd7d856e925829764fcf88fa3 100644 --- a/packages/frontend/src/pages/role.vue +++ b/packages/frontend/src/pages/role.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { computed, watch, ref } from 'vue'; import * as Misskey from 'misskey-js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import MkUserList from '@/components/MkUserList.vue'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; @@ -59,7 +59,7 @@ const error = ref(); const visible = ref(false); watch(() => props.role, () => { - os.api('roles/show', { + misskeyApi('roles/show', { roleId: props.role, }).then(res => { role.value = res; @@ -89,14 +89,14 @@ const headerTabs = computed(() => [{ title: i18n.ts.users, }, { key: 'timeline', - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', title: i18n.ts.timeline, }]); -definePageMetadata(computed(() => ({ - title: role.value?.name, +definePageMetadata(() => ({ + title: role.value ? role.value.name : i18n.ts.role, icon: 'ph-seal-check ph-bold ph-lg', -}))); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue index ccda5bb8acd8539cbf9543d8342aac0f7b88f11d..fb3657cdc9fcd9ef0ea0ccdb79f71fd04284e86a 100644 --- a/packages/frontend/src/pages/scratchpad.vue +++ b/packages/frontend/src/pages/scratchpad.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -44,7 +44,7 @@ import { Interpreter, Parser, utils } from '@syuilo/aiscript'; import MkContainer from '@/components/MkContainer.vue'; import MkButton from '@/components/MkButton.vue'; import MkCodeEditor from '@/components/MkCodeEditor.vue'; -import { createAiScriptEnv } from '@/scripts/aiscript/api.js'; +import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js'; import * as os from '@/os.js'; import { $i } from '@/account.js'; import { i18n } from '@/i18n.js'; @@ -86,19 +86,7 @@ async function run() { root.value = _root.value; }), }), { - in: (q) => { - return new Promise(ok => { - os.inputText({ - title: q, - }).then(({ canceled, result: a }) => { - if (canceled) { - ok(''); - } else { - ok(a); - } - }); - }); - }, + in: aiScriptReadline, out: (value) => { if (value.type === 'str' && value.value.toLowerCase().replace(',', '').includes('hello world')) { claimAchievement('outputHelloWorldOnScratchpad'); @@ -164,10 +152,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.scratchpad, icon: 'ph-terminal-window ph-bold ph-lg-2', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/search.note.vue b/packages/frontend/src/pages/search.note.vue index 405db06758badb56bf292be00e4759e98e61d098..33de0d72cf553bd0ce9b9e641fc216fd1ae61c0b 100644 --- a/packages/frontend/src/pages/search.note.vue +++ b/packages/frontend/src/pages/search.note.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only <option value="audio">Audio</option> </MkSelect> - <MkFolder> + <MkFolder :defaultOpen="true"> <template #label>{{ i18n.ts.specifyUser }}</template> <template v-if="user" #suffix>@{{ user.username }}</template> @@ -58,9 +58,10 @@ import MkSwitch from '@/components/MkSwitch.vue'; import MkSelect from '@/components/MkSelect.vue'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; -import { useRouter } from '@/router.js'; import MkFolder from '@/components/MkFolder.vue'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -74,7 +75,7 @@ const order = ref(false); const filetype = ref(null); function selectUser() { - os.selectUser().then(_user => { + os.selectUser({ includeSelf: true }).then(_user => { user.value = _user; }); } @@ -85,7 +86,7 @@ async function search() { if (query == null || query === '') return; if (query.startsWith('https://')) { - const promise = os.api('ap/show', { + const promise = misskeyApi('ap/show', { uri: query, }); diff --git a/packages/frontend/src/pages/search.user.vue b/packages/frontend/src/pages/search.user.vue index 596f4da711ab9aa5dfec1737fad6c4e1a18ed7ff..dad9cd910ac200715ccd45b9a74142427fec9ffd 100644 --- a/packages/frontend/src/pages/search.user.vue +++ b/packages/frontend/src/pages/search.user.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -33,7 +33,8 @@ import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; -import { useRouter } from '@/router.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -48,7 +49,7 @@ async function search() { if (query == null || query === '') return; if (query.startsWith('https://')) { - const promise = os.api('ap/show', { + const promise = misskeyApi('ap/show', { uri: query, }); diff --git a/packages/frontend/src/pages/search.vue b/packages/frontend/src/pages/search.vue index acc291c73e09a650e76e2af4a61dfe0598fadbf0..fe56297c65eb5323884f42c93f0dd8520ab6dd2e 100644 --- a/packages/frontend/src/pages/search.vue +++ b/packages/frontend/src/pages/search.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,18 +7,20 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> - <MkSpacer v-if="tab === 'note'" :contentMax="800"> - <div v-if="notesSearchAvailable"> - <XNote/> - </div> - <div v-else> - <MkInfo warn>{{ i18n.ts.notesSearchNotAvailable }}</MkInfo> - </div> - </MkSpacer> - - <MkSpacer v-else-if="tab === 'user'" :contentMax="800"> - <XUser/> - </MkSpacer> + <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <MkSpacer v-if="tab === 'note'" key="note" :contentMax="800"> + <div v-if="notesSearchAvailable"> + <XNote/> + </div> + <div v-else> + <MkInfo warn>{{ i18n.ts.notesSearchNotAvailable }}</MkInfo> + </div> + </MkSpacer> + + <MkSpacer v-else-if="tab === 'user'" key="user" :contentMax="800"> + <XUser/> + </MkSpacer> + </MkHorizontalSwipe> </MkStickyContainer> </template> @@ -29,6 +31,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js'; import { $i } from '@/account.js'; import { instance } from '@/instance.js'; import MkInfo from '@/components/MkInfo.vue'; +import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; const XNote = defineAsyncComponent(() => import('./search.note.vue')); const XUser = defineAsyncComponent(() => import('./search.user.vue')); @@ -42,15 +45,15 @@ const headerActions = computed(() => []); const headerTabs = computed(() => [{ key: 'note', title: i18n.ts.notes, - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', }, { key: 'user', title: i18n.ts.users, icon: 'ph-users ph-bold ph-lg', }]); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: i18n.ts.search, icon: 'ph-magnifying-glass ph-bold ph-lg', -}))); +})); </script> diff --git a/packages/frontend/src/pages/settings/2fa.qrdialog.vue b/packages/frontend/src/pages/settings/2fa.qrdialog.vue index 9a2a98ad8926430199163586a79d4d41c550780b..13f475c2f22f4a68d8c06dc72917f388261b419e 100644 --- a/packages/frontend/src/pages/settings/2fa.qrdialog.vue +++ b/packages/frontend/src/pages/settings/2fa.qrdialog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -110,7 +110,9 @@ import * as os from '@/os.js'; import MkFolder from '@/components/MkFolder.vue'; import MkInfo from '@/components/MkInfo.vue'; import { confetti } from '@/scripts/confetti.js'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; + +const $i = signinRequired(); defineProps<{ twoFactorData: { @@ -151,7 +153,7 @@ function downloadBackupCodes() { const txtBlob = new Blob([backupCodes.value.join('\n')], { type: 'text/plain' }); const dummya = document.createElement('a'); dummya.href = URL.createObjectURL(txtBlob); - dummya.download = `${$i?.username}-2fa-backup-codes.txt`; + dummya.download = `${$i.username}-2fa-backup-codes.txt`; dummya.click(); } } diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue index 09421ba2c2d60ac61c1d4cae53a09fc2a9086b8c..ba85a430845c4fb620ec1f720d7b7613cabad31c 100644 --- a/packages/frontend/src/pages/settings/2fa.vue +++ b/packages/frontend/src/pages/settings/2fa.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -80,9 +80,11 @@ import MkSwitch from '@/components/MkSwitch.vue'; import FormSection from '@/components/form/section.vue'; import MkFolder from '@/components/MkFolder.vue'; import * as os from '@/os.js'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; import { i18n } from '@/i18n.js'; +const $i = signinRequired(); + // メモ: å„エンドãƒã‚¤ãƒ³ãƒˆã¯meUpdatedを発行ã™ã‚‹ãŸã‚ã€refreshAccountã¯ä¸è¦ withDefaults(defineProps<{ @@ -91,7 +93,7 @@ withDefaults(defineProps<{ first: false, }); -const usePasswordLessLogin = computed(() => $i?.usePasswordLessLogin ?? false); +const usePasswordLessLogin = computed(() => $i.usePasswordLessLogin ?? false); async function registerTOTP(): Promise<void> { const auth = await os.authenticateDialog(); @@ -139,7 +141,7 @@ async function unregisterKey(key) { const confirm = await os.confirm({ type: 'question', title: i18n.ts._2fa.removeKey, - text: i18n.t('_2fa.removeKeyConfirm', { name: key.name }), + text: i18n.tsx._2fa.removeKeyConfirm({ name: key.name }), }); if (confirm.canceled) return; diff --git a/packages/frontend/src/pages/settings/accounts.vue b/packages/frontend/src/pages/settings/accounts.vue index 697ce27f2f14eee11b0c1c681ba60ff2755cc8a3..f5effbd68b1ca72f845b692ace386ef27d7a76e1 100644 --- a/packages/frontend/src/pages/settings/accounts.vue +++ b/packages/frontend/src/pages/settings/accounts.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -24,6 +24,7 @@ import type * as Misskey from 'misskey-js'; import FormSuspense from '@/components/form/suspense.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { getAccounts, addAccount as addAccounts, removeAccount as _removeAccount, login, $i } from '@/account.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -36,7 +37,7 @@ const init = async () => { getAccounts().then(accounts => { storedAccounts.value = accounts.filter(x => x.id !== $i!.id); - return os.api('users/show', { + return misskeyApi('users/show', { userIds: storedAccounts.value.map(x => x.id), }); }).then(response => { @@ -105,10 +106,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.accounts, icon: 'ph-users ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/settings/api.vue b/packages/frontend/src/pages/settings/api.vue index ca38bd2e3de50ca866376c94fc044d7ec1d68657..f8f340d602a1cd42b61d8208fd4ac22110ee2c00 100644 --- a/packages/frontend/src/pages/settings/api.vue +++ b/packages/frontend/src/pages/settings/api.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -16,6 +16,7 @@ import { defineAsyncComponent, ref, computed } from 'vue'; import FormLink from '@/components/form/link.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -25,7 +26,7 @@ function generateToken() { os.popup(defineAsyncComponent(() => import('@/components/MkTokenGenerateWindow.vue')), {}, { done: async result => { const { name, permissions } = result; - const { token } = await os.api('miauth/gen-token', { + const { token } = await misskeyApi('miauth/gen-token', { session: null, name: name, permission: permissions, @@ -44,8 +45,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: 'API', icon: 'ph-webhooks-logo ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue index f492dc6d3140a0570a5c7a8707c58364e51d8552..abdb5d1cdd24abe4522a26071344898b1672c956 100644 --- a/packages/frontend/src/pages/settings/apps.vue +++ b/packages/frontend/src/pages/settings/apps.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only <details> <summary>{{ i18n.ts.details }}</summary> <ul> - <li v-for="p in token.permission" :key="p">{{ i18n.t(`_permissions.${p}`) }}</li> + <li v-for="p in token.permission" :key="p">{{ i18n.ts._permissions[p] }}</li> </ul> </details> <div> @@ -47,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref, computed } from 'vue'; import FormPagination from '@/components/MkPagination.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkKeyValue from '@/components/MkKeyValue.vue'; @@ -66,7 +66,7 @@ const pagination = { }; function revoke(token) { - os.api('i/revoke-token', { tokenId: token.id }).then(() => { + misskeyApi('i/revoke-token', { tokenId: token.id }).then(() => { list.value.reload(); }); } @@ -75,10 +75,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.installedApps, icon: 'ph-plug ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue index 2bf261abd9309ee3743d44f1fc7e54f7a6d01dee..1b731ff6243e3f2b246741f5ade57791a3f18a4d 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -16,7 +16,9 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { } from 'vue'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; + +const $i = signinRequired(); const props = defineProps<{ active?: boolean; diff --git a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue index a46a92d1c6896873ba766038d2849513bc23f03b..327e0ef723c07a4617f175a2a0a4f9ecc5582bbe 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -51,7 +51,9 @@ import MkModalWindow from '@/components/MkModalWindow.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import { i18n } from '@/i18n.js'; import MkRange from '@/components/MkRange.vue'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; + +const $i = signinRequired(); const props = defineProps<{ usingIndex: number | null; diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue index 976f6aa68c02d04fa1428bbda3cae43bab09739a..a60d7209cfc7d1115c6ca26eb5c172f89b6be70b 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.vue @@ -1,12 +1,12 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <div> <div v-if="!loading" class="_gaps"> - <MkInfo>{{ i18n.t('_profile.avatarDecorationMax', { max: $i.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo> + <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/> @@ -50,15 +50,18 @@ import * as Misskey from 'misskey-js'; import XDecoration from './avatar-decoration.decoration.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; import MkInfo from '@/components/MkInfo.vue'; import { definePageMetadata } from '@/scripts/page-metadata.js'; +const $i = signinRequired(); + const loading = ref(true); const avatarDecorations = ref<Misskey.entities.GetAvatarDecorationsResponse>([]); -os.api('get-avatar-decorations').then(_avatarDecorations => { +misskeyApi('get-avatar-decorations').then(_avatarDecorations => { avatarDecorations.value = _avatarDecorations; loading.value = false; }); @@ -125,10 +128,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.avatarDecorations, icon: 'ph-sparkle ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/settings/custom-css.vue b/packages/frontend/src/pages/settings/custom-css.vue index 00a1fca8561dad14577739e95abfd2cf5c43a657..59733e896f28251eb297219b5705b9ed248aea6c 100644 --- a/packages/frontend/src/pages/settings/custom-css.vue +++ b/packages/frontend/src/pages/settings/custom-css.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -45,8 +45,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.customCss, icon: 'ph-code ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/settings/deck.vue b/packages/frontend/src/pages/settings/deck.vue index 32acd5e7a6fbb058a81d10b6126100c8261c7037..81ae9bc2f7127482a63f5a57e3aac2f8655bafda 100644 --- a/packages/frontend/src/pages/settings/deck.vue +++ b/packages/frontend/src/pages/settings/deck.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -36,8 +36,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.deck, icon: 'ph-text-columns ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/settings/drive-cleaner.vue b/packages/frontend/src/pages/settings/drive-cleaner.vue index 601479b73c451fd0036da40934ec78d7dddff1b2..fef12fee06a1a6df7969d779aac2fd8865f931f0 100644 --- a/packages/frontend/src/pages/settings/drive-cleaner.vue +++ b/packages/frontend/src/pages/settings/drive-cleaner.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -51,6 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed, ref, watch } from 'vue'; import tinycolor from 'tinycolor2'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import MkPagination from '@/components/MkPagination.vue'; import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; import { i18n } from '@/i18n.js'; @@ -94,7 +95,7 @@ watch(sortModeSelect, () => { function fetchDriveInfo(): void { fetching.value = true; - os.api('drive').then(info => { + misskeyApi('drive').then(info => { capacity.value = info.capacity; usage.value = info.usage; fetching.value = false; @@ -116,10 +117,10 @@ function onContextMenu(ev: MouseEvent, file): void { os.contextMenu(getDriveFileMenu(file), ev); } -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.drivecleaner, icon: 'ph-trash ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue index 166e49ac547ea5e3e04df862bcc4fc8037d034dc..4185a1c85512beabe8d0526d99fb5053a787501f 100644 --- a/packages/frontend/src/pages/settings/drive.vue +++ b/packages/frontend/src/pages/settings/drive.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -62,12 +62,15 @@ import FormSection from '@/components/form/section.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; import FormSplit from '@/components/form/split.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import bytes from '@/filters/bytes.js'; import { defaultStore } from '@/store.js'; import MkChart from '@/components/MkChart.vue'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; + +const $i = signinRequired(); const fetching = ref(true); const usage = ref<number | null>(null); @@ -76,6 +79,7 @@ const uploadFolder = ref<Misskey.entities.DriveFolder | null>(null); const alwaysMarkNsfw = ref($i.alwaysMarkNsfw); const meterStyle = computed(() => { + if (!capacity.value || !usage.value) return {}; return { width: `${usage.value / capacity.value * 100}%`, background: tinycolor({ @@ -88,14 +92,14 @@ const meterStyle = computed(() => { const keepOriginalUploading = computed(defaultStore.makeGetterSetter('keepOriginalUploading')); -os.api('drive').then(info => { +misskeyApi('drive').then(info => { capacity.value = info.capacity; usage.value = info.usage; fetching.value = false; }); if (defaultStore.state.uploadFolder) { - os.api('drive/folders/show', { + misskeyApi('drive/folders/show', { folderId: defaultStore.state.uploadFolder, }).then(response => { uploadFolder.value = response; @@ -104,10 +108,10 @@ if (defaultStore.state.uploadFolder) { function chooseUploadFolder() { os.selectDriveFolder(false).then(async folder => { - defaultStore.set('uploadFolder', folder ? folder.id : null); + defaultStore.set('uploadFolder', folder[0] ? folder[0].id : null); os.success(); if (defaultStore.state.uploadFolder) { - uploadFolder.value = await os.api('drive/folders/show', { + uploadFolder.value = await misskeyApi('drive/folders/show', { folderId: defaultStore.state.uploadFolder, }); } else { @@ -117,7 +121,7 @@ function chooseUploadFolder() { } function saveProfile() { - os.api('i/update', { + misskeyApi('i/update', { alwaysMarkNsfw: !!alwaysMarkNsfw.value, }).catch(err => { os.alert({ @@ -133,10 +137,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.drive, icon: 'ph-cloud ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue index 003501f45a339846c7f0cb94d9cf5c93b6fc3c3c..938abb0651acc8ce7614f78f90649c589125c860 100644 --- a/packages/frontend/src/pages/settings/email.vue +++ b/packages/frontend/src/pages/settings/email.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -54,15 +54,18 @@ import MkInfo from '@/components/MkInfo.vue'; import MkInput from '@/components/MkInput.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import * as os from '@/os.js'; -import { $i } from '@/account.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; +import { signinRequired } from '@/account.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { instance } from '@/instance.js'; -const emailAddress = ref($i!.email); +const $i = signinRequired(); + +const emailAddress = ref($i.email); const onChangeReceiveAnnouncementEmail = (v) => { - os.api('i/update', { + misskeyApi('i/update', { receiveAnnouncementEmail: v, }); }; @@ -78,14 +81,14 @@ async function saveEmailAddress() { }); } -const emailNotification_mention = ref($i!.emailNotificationTypes.includes('mention')); -const emailNotification_reply = ref($i!.emailNotificationTypes.includes('reply')); -const emailNotification_quote = ref($i!.emailNotificationTypes.includes('quote')); -const emailNotification_follow = ref($i!.emailNotificationTypes.includes('follow')); -const emailNotification_receiveFollowRequest = ref($i!.emailNotificationTypes.includes('receiveFollowRequest')); +const emailNotification_mention = ref($i.emailNotificationTypes.includes('mention')); +const emailNotification_reply = ref($i.emailNotificationTypes.includes('reply')); +const emailNotification_quote = ref($i.emailNotificationTypes.includes('quote')); +const emailNotification_follow = ref($i.emailNotificationTypes.includes('follow')); +const emailNotification_receiveFollowRequest = ref($i.emailNotificationTypes.includes('receiveFollowRequest')); const saveNotificationSettings = () => { - os.api('i/update', { + misskeyApi('i/update', { emailNotificationTypes: [ ...[emailNotification_mention.value ? 'mention' : null], ...[emailNotification_reply.value ? 'reply' : null], @@ -110,8 +113,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.email, icon: 'ph-envelope ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/settings/emoji-picker.vue b/packages/frontend/src/pages/settings/emoji-picker.vue index 40bb823ac65be67b6c01f1fadd92f898e1f5b5c3..e9936ca5f28aa61c877335bc44f8f275397dcb48 100644 --- a/packages/frontend/src/pages/settings/emoji-picker.vue +++ b/packages/frontend/src/pages/settings/emoji-picker.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only > <template #item="{element}"> <button class="_button" :class="$style.emojisItem" @click="removeReaction(element, $event)"> - <MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true"/> + <MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true" :fallbackToImage="true"/> <MkEmoji v-else :emoji="element" :normal="true"/> </button> </template> @@ -63,7 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only > <template #item="{element}"> <button class="_button" :class="$style.emojisItem" @click="removeEmoji(element, $event)"> - <MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true"/> + <MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true" :fallbackToImage="true"/> <MkEmoji v-else :emoji="element" :normal="true"/> </button> </template> @@ -87,7 +87,7 @@ SPDX-License-Identifier: AGPL-3.0-only <FromSlot> <template #label>{{ i18n.ts.defaultLike }}</template> - <MkCustomEmoji v-if="like && like.startsWith(':')" style="max-height: 3em; font-size: 1.1em;" :useOriginalSize="false" :class="$style.reaction" :name="like" :normal="true" :noStyle="true"/> + <MkCustomEmoji v-if="like && like.startsWith(':')" style="max-height: 3em; font-size: 1.1em;" :useOriginalSize="false" :name="like" :normal="true" :noStyle="true"/> <MkEmoji v-else-if="like && !like.startsWith(':')" :emoji="like" style="max-height: 3em; font-size: 1.1em;" :normal="true" :noStyle="true"/> <span v-else-if="!like">{{ i18n.ts.notSet }}</span> <div class="_buttons" style="padding-top: 8px;"> @@ -172,7 +172,7 @@ const chooseEmoji = (ev: MouseEvent) => pickEmoji(pinnedEmojis, ev); const setDefaultEmoji = () => setDefault(pinnedEmojis); function previewReaction(ev: MouseEvent) { - reactionPicker.show(getHTMLElement(ev)); + reactionPicker.show(getHTMLElement(ev), null); } function previewEmoji(ev: MouseEvent) { @@ -228,7 +228,7 @@ async function pickEmoji(itemsRef: Ref<string[]>, ev: MouseEvent) { os.pickEmoji(getHTMLElement(ev), { showPinned: false, }).then(it => { - const emoji = it as string; + const emoji = it; if (!itemsRef.value.includes(emoji)) { itemsRef.value.push(emoji); } @@ -276,10 +276,10 @@ watch(pinnedEmojis, () => { deep: true, }); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.emojiPicker, icon: 'ph-smiley ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 8eacdd32e6068be307c96327b723e5ff0c5ad646..1e4e815d5db1977035cc8a5b24a33885da363525 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -17,6 +17,13 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </MkSelect> + <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> + <MkRadios v-model="overridedDeviceKind"> <template #label>{{ i18n.ts.overridedDeviceKind }}</template> <option :value="null">{{ i18n.ts.auto }}</option> @@ -87,9 +94,9 @@ SPDX-License-Identifier: AGPL-3.0-only <MkRadios v-model="mediaListWithOneImageAppearance"> <template #label>{{ i18n.ts.mediaListWithOneImageAppearance }}</template> <option value="expand">{{ i18n.ts.default }}</option> - <option value="16_9">{{ i18n.t('limitTo', { x: '16:9' }) }}</option> - <option value="1_1">{{ i18n.t('limitTo', { x: '1:1' }) }}</option> - <option value="2_3">{{ i18n.t('limitTo', { x: '2:3' }) }}</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> <MkRange v-model="numberOfReplies" :min="2" :max="20" :step="1" easing> @@ -138,6 +145,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="useSystemFont">{{ i18n.ts.useSystemFont }}</MkSwitch> <MkSwitch v-model="disableDrawer">{{ i18n.ts.disableDrawer }}</MkSwitch> <MkSwitch v-model="forceShowAds">{{ i18n.ts.forceShowAds }}</MkSwitch> + <MkSwitch v-model="oneko">{{ i18n.ts.oneko }}</MkSwitch> <MkSwitch v-model="enableSeasonalScreenEffect">{{ i18n.ts.seasonalScreenEffect }}</MkSwitch> </div> <div> @@ -146,6 +154,7 @@ SPDX-License-Identifier: AGPL-3.0-only <option value="native">{{ i18n.ts.native }}</option> <option value="fluentEmoji">Fluent Emoji</option> <option value="twemoji">Twemoji</option> + <option value="tossface">Tossface</option> </MkRadios> <div style="margin: 8px 0 0 0; font-size: 1.5em;"><Mfm :key="emojiStyle" text="ðŸ®ðŸ¦ðŸðŸ©ðŸ°ðŸ«ðŸ¬ðŸ¥žðŸª"/></div> </div> @@ -171,6 +180,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps_m"> <div class="_gaps_s"> + <MkSwitch v-model="warnMissingAltText">{{ i18n.ts.warnForMissingAltText }}</MkSwitch> <MkSwitch v-model="imageNewTab">{{ i18n.ts.openImageInNewTab }}</MkSwitch> <MkSwitch v-model="useReactionPickerForContextMenu">{{ i18n.ts.useReactionPickerForContextMenu }}</MkSwitch> <MkSwitch v-model="enableInfiniteScroll">{{ i18n.ts.enableInfiniteScroll }}</MkSwitch> @@ -178,6 +188,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="clickToOpen">{{ i18n.ts.clickToOpen }}</MkSwitch> <MkSwitch v-model="showBots">{{ i18n.ts.showBots }}</MkSwitch> <MkSwitch v-model="disableStreamingTimeline">{{ i18n.ts.disableStreamingTimeline }}</MkSwitch> + <MkSwitch v-model="enableHorizontalSwipe">{{ i18n.ts.enableHorizontalSwipe }}</MkSwitch> </div> <MkSelect v-model="serverDisconnectedBehavior"> <template #label>{{ i18n.ts.whenServerDisconnected }}</template> @@ -190,6 +201,22 @@ SPDX-License-Identifier: AGPL-3.0-only <template #caption>{{ i18n.ts.numberOfPageCacheDescription }}</template> </MkRange> + <MkFolder> + <template #label>{{ i18n.ts.boostSettings }}</template> + <div class="_gaps_m"> + <MkSwitch v-model="showVisibilitySelectorOnBoost"> + {{ i18n.ts.showVisibilitySelectorOnBoost }} + <template #caption>{{ i18n.ts.showVisibilitySelectorOnBoostDescription }}</template> + </MkSwitch> + <MkSelect v-model="visibilityOnBoost"> + <template #label>{{ i18n.ts.visibilityOnBoost }}</template> + <option value="public">{{ i18n.ts._visibility['public'] }}</option> + <option value="home">{{ i18n.ts._visibility['home'] }}</option> + <option value="followers">{{ i18n.ts._visibility['followers'] }}</option> + </MkSelect> + </div> + </MkFolder> + <MkFolder> <template #label>{{ i18n.ts.dataSaver }}</template> @@ -229,9 +256,11 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps"> <MkFolder> <template #label>{{ i18n.ts.additionalEmojiDictionary }}</template> - <div v-for="lang in emojiIndexLangs" class="_buttons"> - <MkButton @click="downloadEmojiIndex(lang)"><i class="ph-download ph-bold ph-lg"></i> {{ lang }}{{ defaultStore.reactiveState.additionalUnicodeEmojiIndexes.value[lang] ? ` (${ i18n.ts.installed })` : '' }}</MkButton> - <MkButton v-if="defaultStore.reactiveState.additionalUnicodeEmojiIndexes.value[lang]" danger @click="removeEmojiIndex(lang)"><i class="ph-trash ph-bold ph-lg"></i> {{ i18n.ts.remove }}</MkButton> + <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="ph-trash ph-bold ph-lg"></i> {{ i18n.ts.remove }} ({{ getEmojiIndexLangName(lang) }})</MkButton> + <MkButton v-else @click="downloadEmojiIndex(lang)"><i class="ph-download ph-bold ph-lg"></i> {{ getEmojiIndexLangName(lang) }}{{ defaultStore.reactiveState.additionalUnicodeEmojiIndexes.value[lang] ? ` (${ i18n.ts.installed })` : '' }}</MkButton> + </template> </div> </MkFolder> <FormLink to="/settings/deck">{{ i18n.ts.deck }}</FormLink> @@ -257,6 +286,7 @@ import MkInfo from '@/components/MkInfo.vue'; import { langs } from '@/config.js'; import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { unisonReload } from '@/scripts/unison-reload.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -280,6 +310,7 @@ async function reloadAsk() { unisonReload(); } +const hemisphere = computed(defaultStore.makeGetterSetter('hemisphere')); const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind')); const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serverDisconnectedBehavior')); const showNoteActionsOnlyHover = computed(defaultStore.makeGetterSetter('showNoteActionsOnlyHover')); @@ -302,9 +333,11 @@ const emojiStyle = computed(defaultStore.makeGetterSetter('emojiStyle')); const disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer')); const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages')); const forceShowAds = computed(defaultStore.makeGetterSetter('forceShowAds')); +const oneko = computed(defaultStore.makeGetterSetter('oneko')); const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages')); const highlightSensitiveMedia = computed(defaultStore.makeGetterSetter('highlightSensitiveMedia')); const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab')); +const warnMissingAltText = computed(defaultStore.makeGetterSetter('warnMissingAltText')); const nsfw = computed(defaultStore.makeGetterSetter('nsfw')); const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm')); const showFixedPostFormInChannel = computed(defaultStore.makeGetterSetter('showFixedPostFormInChannel')); @@ -326,6 +359,9 @@ const noteDesign = computed(defaultStore.makeGetterSetter('noteDesign')); const uncollapseCW = computed(defaultStore.makeGetterSetter('uncollapseCW')); const expandLongNote = computed(defaultStore.makeGetterSetter('expandLongNote')); const enableSeasonalScreenEffect = computed(defaultStore.makeGetterSetter('enableSeasonalScreenEffect')); +const showVisibilitySelectorOnBoost = computed(defaultStore.makeGetterSetter('showVisibilitySelectorOnBoost')); +const visibilityOnBoost = computed(defaultStore.makeGetterSetter('visibilityOnBoost')); +const enableHorizontalSwipe = computed(defaultStore.makeGetterSetter('enableHorizontalSwipe')); watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); @@ -364,6 +400,7 @@ watch(noteDesign, async (newval) => { }); watch([ + hemisphere, lang, fontSize, cornerRadius, @@ -381,19 +418,35 @@ watch([ keepScreenOn, disableStreamingTimeline, enableSeasonalScreenEffect, + showVisibilitySelectorOnBoost, + visibilityOnBoost, ], async () => { await reloadAsk(); }); -const emojiIndexLangs = ['en-US']; +const emojiIndexLangs = ['en-US', 'ja-JP', 'ja-JP_hira'] as const; -function downloadEmojiIndex(lang: string) { +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); } } @@ -416,7 +469,7 @@ function removeEmojiIndex(lang: string) { } async function setPinnedList() { - const lists = await os.api('users/lists/list'); + const lists = await misskeyApi('users/lists/list'); const { canceled, result: list } = await os.select({ title: i18n.ts.selectList, items: lists.map(x => ({ @@ -485,8 +538,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.general, icon: 'ph-faders ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/settings/import-export.vue b/packages/frontend/src/pages/settings/import-export.vue index 7ca1faf40679073b8bd9c65147d21a645c8fdc64..87bde70fc2d914681261cb70134f5af7113391f6 100644 --- a/packages/frontend/src/pages/settings/import-export.vue +++ b/packages/frontend/src/pages/settings/import-export.vue @@ -1,12 +1,12 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <div class="_gaps_m"> <FormSection first> - <template #label><i class="ph-pencil ph-bold ph-lg"></i> {{ i18n.ts._exportOrImport.allNotes }}</template> + <template #label><i class="ph-pencil-simple ph-bold ph-lg"></i> {{ i18n.ts._exportOrImport.allNotes }}</template> <div class="_gaps_s"> <MkFolder> <template #label>{{ i18n.ts.export }}</template> @@ -36,6 +36,14 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton primary :class="$style.button" inline @click="exportFavorites()"><i class="ph-download ph-bold ph-lg"></i> {{ i18n.ts.export }}</MkButton> </MkFolder> </FormSection> + <FormSection> + <template #label><i class="ph-paperclip ph-bold ph-lg"></i> {{ i18n.ts._exportOrImport.clips }}</template> + <MkFolder> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ph-download ph-bold ph-lg"></i></template> + <MkButton primary :class="$style.button" inline @click="exportClips()"><i class="ph-download ph-bold ph-lg"></i> {{ i18n.ts.export }}</MkButton> + </MkFolder> + </FormSection> <FormSection> <template #label><i class="ph-users ph-bold ph-lg"></i> {{ i18n.ts._exportOrImport.followingList }}</template> <div class="_gaps_s"> @@ -133,11 +141,12 @@ import MkFolder from '@/components/MkFolder.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkRadios from '@/components/MkRadios.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { selectFile } from '@/scripts/select-file.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { $i } from '@/account.js'; -import { defaultStore } from "@/store.js"; +import { defaultStore } from '@/store.js'; const excludeMutingUsers = ref(false); const excludeInactiveUsers = ref(false); @@ -166,15 +175,19 @@ const onError = (ev) => { }; const exportNotes = () => { - os.api('i/export-notes', {}).then(onExportSuccess).catch(onError); + misskeyApi('i/export-notes', {}).then(onExportSuccess).catch(onError); }; const exportFavorites = () => { - os.api('i/export-favorites', {}).then(onExportSuccess).catch(onError); + misskeyApi('i/export-favorites', {}).then(onExportSuccess).catch(onError); +}; + +const exportClips = () => { + misskeyApi('i/export-clips', {}).then(onExportSuccess).catch(onError); }; const exportFollowing = () => { - os.api('i/export-following', { + misskeyApi('i/export-following', { excludeMuting: excludeMutingUsers.value, excludeInactive: excludeInactiveUsers.value, }) @@ -182,24 +195,24 @@ const exportFollowing = () => { }; const exportBlocking = () => { - os.api('i/export-blocking', {}).then(onExportSuccess).catch(onError); + misskeyApi('i/export-blocking', {}).then(onExportSuccess).catch(onError); }; const exportUserLists = () => { - os.api('i/export-user-lists', {}).then(onExportSuccess).catch(onError); + misskeyApi('i/export-user-lists', {}).then(onExportSuccess).catch(onError); }; const exportMuting = () => { - os.api('i/export-mute', {}).then(onExportSuccess).catch(onError); + misskeyApi('i/export-mute', {}).then(onExportSuccess).catch(onError); }; const exportAntennas = () => { - os.api('i/export-antennas', {}).then(onExportSuccess).catch(onError); + misskeyApi('i/export-antennas', {}).then(onExportSuccess).catch(onError); }; const importFollowing = async (ev) => { const file = await selectFile(ev.currentTarget ?? ev.target); - os.api('i/import-following', { + misskeyApi('i/import-following', { fileId: file.id, withReplies: withReplies.value, }).then(onImportSuccess).catch(onError); @@ -207,7 +220,7 @@ const importFollowing = async (ev) => { const importNotes = async (ev) => { const file = await selectFile(ev.currentTarget ?? ev.target); - os.api('i/import-notes', { + misskeyApi('i/import-notes', { fileId: file.id, type: noteType.value, }).then(onImportSuccess).catch(onError); @@ -215,32 +228,32 @@ const importNotes = async (ev) => { const importUserLists = async (ev) => { const file = await selectFile(ev.currentTarget ?? ev.target); - os.api('i/import-user-lists', { fileId: file.id }).then(onImportSuccess).catch(onError); + misskeyApi('i/import-user-lists', { fileId: file.id }).then(onImportSuccess).catch(onError); }; const importMuting = async (ev) => { const file = await selectFile(ev.currentTarget ?? ev.target); - os.api('i/import-muting', { fileId: file.id }).then(onImportSuccess).catch(onError); + misskeyApi('i/import-muting', { fileId: file.id }).then(onImportSuccess).catch(onError); }; const importBlocking = async (ev) => { const file = await selectFile(ev.currentTarget ?? ev.target); - os.api('i/import-blocking', { fileId: file.id }).then(onImportSuccess).catch(onError); + misskeyApi('i/import-blocking', { fileId: file.id }).then(onImportSuccess).catch(onError); }; const importAntennas = async (ev) => { const file = await selectFile(ev.currentTarget ?? ev.target); - os.api('i/import-antennas', { fileId: file.id }).then(onImportSuccess).catch(onError); + misskeyApi('i/import-antennas', { fileId: file.id }).then(onImportSuccess).catch(onError); }; const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.importAndExport, icon: 'ph-package ph-bold ph-lg', -}); +})); </script> <style module> diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index 96575e097b16e337e40c33a82e1b3252458a285b..35fb1a03f49fdcfff9c00363de001efd90f1cd7f 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -27,16 +27,16 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script setup lang="ts"> -import { ComputedRef, Ref, computed, onActivated, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'; +import { computed, onActivated, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'; import { i18n } from '@/i18n.js'; import MkInfo from '@/components/MkInfo.vue'; import MkSuperMenu from '@/components/MkSuperMenu.vue'; import { signout, $i } from '@/account.js'; import { clearCache } from '@/scripts/clear-cache.js'; import { instance } from '@/instance.js'; -import { useRouter } from '@/router.js'; -import { PageMetadata, definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js'; +import { PageMetadata, definePageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; import * as os from '@/os.js'; +import { useRouter } from '@/router/supplier.js'; const indexInfo = { title: i18n.ts.settings, @@ -45,7 +45,7 @@ const indexInfo = { }; const INFO = ref(indexInfo); const el = shallowRef<HTMLElement | null>(null); -const childInfo: Ref<ComputedRef<PageMetadata> | null> = ref(null); +const childInfo = ref<null | PageMetadata>(null); const router = useRouter(); @@ -230,20 +230,22 @@ watch(router.currentRef, (to) => { const emailNotConfigured = computed(() => instance.enableEmail && ($i.email == null || !$i.emailVerified)); -provideMetadataReceiver((info) => { +provideMetadataReceiver((metadataGetter) => { + const info = metadataGetter(); if (info == null) { childInfo.value = null; } else { childInfo.value = info; - INFO.value.needWideArea = info.value.needWideArea ?? undefined; + INFO.value.needWideArea = info.needWideArea ?? undefined; } }); +provideReactiveMetadata(INFO); const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(INFO); +definePageMetadata(() => INFO.value); // w 890 // h 700 </script> diff --git a/packages/frontend/src/pages/settings/migration.vue b/packages/frontend/src/pages/settings/migration.vue index 3b47189eb443f84124ffd42bd0bfd994dd19df8e..12f29e2ff8d871b5d7769dfe15d30b4cc0e767c5 100644 --- a/packages/frontend/src/pages/settings/migration.vue +++ b/packages/frontend/src/pages/settings/migration.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -21,13 +21,13 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps"> <MkInput v-for="(_, i) in accountAliases" v-model="accountAliases[i]"> <template #prefix><i class="ph-airplane-landing ph-bold ph-lg"></i></template> - <template #label>{{ i18n.t('_accountMigration.moveFromLabel', { n: i + 1 }) }}</template> + <template #label>{{ i18n.tsx._accountMigration.moveFromLabel({ n: i + 1 }) }}</template> </MkInput> </div> </div> </MkFolder> - <MkFolder :defaultOpen="!!$i?.movedTo"> + <MkFolder :defaultOpen="!!$i.movedTo"> <template #icon><i class="ph-airplane-takeoff ph-bold ph-lg"></i></template> <template #label>{{ i18n.ts._accountMigration.moveTo }}</template> @@ -66,24 +66,27 @@ import MkButton from '@/components/MkButton.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkUserInfo from '@/components/MkUserInfo.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; import { unisonReload } from '@/scripts/unison-reload.js'; +const $i = signinRequired(); + const moveToAccount = ref(''); const movedTo = ref<Misskey.entities.UserDetailed>(); const accountAliases = ref(['']); async function init() { - if ($i?.movedTo) { - movedTo.value = await os.api('users/show', { userId: $i.movedTo }); + if ($i.movedTo) { + movedTo.value = await misskeyApi('users/show', { userId: $i.movedTo }); } else { moveToAccount.value = ''; } - if ($i?.alsoKnownAs && $i.alsoKnownAs.length > 0) { - const alsoKnownAs = await os.api('users/show', { userIds: $i.alsoKnownAs }); + if ($i.alsoKnownAs && $i.alsoKnownAs.length > 0) { + const alsoKnownAs = await misskeyApi('users/show', { userIds: $i.alsoKnownAs }); accountAliases.value = (alsoKnownAs && alsoKnownAs.length > 0) ? alsoKnownAs.map(user => `@${Misskey.acct.toString(user)}`) : ['']; } else { accountAliases.value = ['']; @@ -94,7 +97,7 @@ async function move(): Promise<void> { const account = moveToAccount.value; const confirm = await os.confirm({ type: 'warning', - text: i18n.t('_accountMigration.migrationConfirm', { account }), + text: i18n.tsx._accountMigration.migrationConfirm({ account }), }); if (confirm.canceled) return; await os.apiWithDialog('i/move', { @@ -118,10 +121,10 @@ async function save(): Promise<void> { init(); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.accountMigration, icon: 'ph-airplane ph-bold ph-lg', -}); +})); </script> <style lang="scss"> diff --git a/packages/frontend/src/pages/settings/mute-block.instance-mute.vue b/packages/frontend/src/pages/settings/mute-block.instance-mute.vue index 0e149fd46178b9ba0ba46a82eaeb0087cdcb4a2e..3b3376a9a7313349a837e25398145dae77234b2f 100644 --- a/packages/frontend/src/pages/settings/mute-block.instance-mute.vue +++ b/packages/frontend/src/pages/settings/mute-block.instance-mute.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -19,11 +19,13 @@ import { ref, watch } from 'vue'; import MkTextarea from '@/components/MkTextarea.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkButton from '@/components/MkButton.vue'; -import * as os from '@/os.js'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; -const instanceMutes = ref($i!.mutedInstances.join('\n')); +const $i = signinRequired(); + +const instanceMutes = ref($i.mutedInstances.join('\n')); const changed = ref(false); async function save() { @@ -32,7 +34,7 @@ async function save() { .map(el => el.trim()) .filter(el => el); - await os.api('i/update', { + await misskeyApi('i/update', { mutedInstances: mutes, }); diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue index a996a03cced299fd38e2a1e00493670c82766b61..588184826d1c9a4d3be3a3c060a5d92206402712 100644 --- a/packages/frontend/src/pages/settings/mute-block.vue +++ b/packages/frontend/src/pages/settings/mute-block.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -9,14 +9,14 @@ SPDX-License-Identifier: AGPL-3.0-only <template #icon><i class="ph-envelope ph-bold ph-lg"></i></template> <template #label>{{ i18n.ts.wordMute }}</template> - <XWordMute :muted="$i!.mutedWords" @save="saveMutedWords"/> + <XWordMute :muted="$i.mutedWords" @save="saveMutedWords"/> </MkFolder> <MkFolder> <template #icon><i class="ph-x-square ph-bold ph-lg"></i></template> <template #label>{{ i18n.ts.hardWordMute }}</template> - <XWordMute :muted="$i!.hardMutedWords" @save="saveHardMutedWords"/> + <XWordMute :muted="$i.hardMutedWords" @save="saveHardMutedWords"/> </MkFolder> <MkFolder> @@ -136,9 +136,11 @@ import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; import * as os from '@/os.js'; import { infoImageUrl } from '@/instance.js'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; import MkFolder from '@/components/MkFolder.vue'; +const $i = signinRequired(); + const renoteMutingPagination = { endpoint: 'renote-mute/list' as const, limit: 10, @@ -227,10 +229,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.muteAndBlock, icon: 'ph-prohibit ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/settings/mute-block.word-mute.vue b/packages/frontend/src/pages/settings/mute-block.word-mute.vue index 96ee48cdba40f60d194a7b41c1d0ce42fe880bff..faf16ca368c4fa8f2d7c0c158d5e43d3c7d5c6bc 100644 --- a/packages/frontend/src/pages/settings/mute-block.word-mute.vue +++ b/packages/frontend/src/pages/settings/mute-block.word-mute.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -64,7 +64,7 @@ async function save() { os.alert({ type: 'error', title: i18n.ts.regexpError, - text: i18n.t('regexpErrorDescription', { tab: 'word mute', line: i + 1 }) + '\n' + err.toString(), + text: i18n.tsx.regexpErrorDescription({ tab: 'word mute', line: i + 1 }) + '\n' + err.toString(), }); // re-throw error so these invalid settings are not saved throw err; diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue index f3c9ec8926de5cbf2255e3b18fb334a88329fced..ae5f081e1c1f65f1e36a632878db0602bbf0d06c 100644 --- a/packages/frontend/src/pages/settings/navbar.vue +++ b/packages/frontend/src/pages/settings/navbar.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -118,10 +118,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.navbar, icon: 'ph-list ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/settings/notifications.notification-config.vue b/packages/frontend/src/pages/settings/notifications.notification-config.vue index 06686c3204f6480917fff9a237a0af4ffa6c281e..6dde006106f2023024de3b72be501bec96880ddb 100644 --- a/packages/frontend/src/pages/settings/notifications.notification-config.vue +++ b/packages/frontend/src/pages/settings/notifications.notification-config.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,10 +7,11 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps_m"> <MkSelect v-model="type"> <option value="all">{{ i18n.ts.all }}</option> - <option value="following">{{ i18n.ts.following }}</option> - <option value="follower">{{ i18n.ts.followers }}</option> - <option value="mutualFollow">{{ i18n.ts.mutualFollow }}</option> - <option value="list">{{ i18n.ts.userList }}</option> + <option value="following" v-if="hasSender">{{ i18n.ts.following }}</option> + <option value="follower" v-if="hasSender">{{ i18n.ts.followers }}</option> + <option value="mutualFollow" v-if="hasSender">{{ i18n.ts.mutualFollow }}</option> + <option value="followingOrFollower" v-if="hasSender">{{ i18n.ts.followingOrFollower }}</option> + <option value="list" v-if="hasSender">{{ i18n.ts.userList }}</option> <option value="never">{{ i18n.ts.none }}</option> </MkSelect> @@ -32,10 +33,13 @@ import MkSelect from '@/components/MkSelect.vue'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; -const props = defineProps<{ +const props = withDefaults(defineProps<{ value: any; userLists: Misskey.entities.UserList[]; -}>(); + hasSender: boolean; +}>(), { + hasSender: true, +}); const emit = defineEmits<{ (ev: 'update', result: any): void; diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index 0bdfbdf741e7480e5faa85d3d68378e6cc427879..36fe7df03e0e0fa2d20afc3b1cfd37434a4c1086 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -9,19 +9,20 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.notificationRecieveConfig }}</template> <div class="_gaps_s"> <MkFolder v-for="type in notificationTypes.filter(x => !nonConfigurableNotificationTypes.includes(x))" :key="type"> - <template #label>{{ i18n.t('_notification._types.' + type) }}</template> + <template #label>{{ i18n.ts._notification._types[type] }}</template> <template #suffix> {{ $i.notificationRecieveConfig[type]?.type === 'never' ? i18n.ts.none : $i.notificationRecieveConfig[type]?.type === 'following' ? i18n.ts.following : $i.notificationRecieveConfig[type]?.type === 'follower' ? i18n.ts.followers : $i.notificationRecieveConfig[type]?.type === 'mutualFollow' ? i18n.ts.mutualFollow : + $i.notificationRecieveConfig[type]?.type === 'followingOrFollower' ? i18n.ts.followingOrFollower : $i.notificationRecieveConfig[type]?.type === 'list' ? i18n.ts.userList : i18n.ts.all }} </template> - <XNotificationConfig :userLists="userLists" :value="$i.notificationRecieveConfig[type] ?? { type: 'all' }" @update="(res) => updateReceiveConfig(type, res)"/> + <XNotificationConfig :userLists="userLists" :value="$i.notificationRecieveConfig[type] ?? { type: 'all' }" :hasSender="!(notificationTypesWithoutSender.includes(type))" @update="(res) => updateReceiveConfig(type, res)"/> </MkFolder> </div> </FormSection> @@ -34,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only <FormSection> <div class="_gaps_m"> <FormLink @click="testNotification">{{ i18n.ts._notification.sendTestNotification }}</FormLink> + <FormLink @click="flushNotification">{{ i18n.ts._notification.flushNotification }}</FormLink> </div> </FormSection> <FormSection> @@ -62,18 +64,22 @@ import FormSection from '@/components/form/section.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import * as os from '@/os.js'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue'; import { notificationTypes } from '@/const.js'; -const nonConfigurableNotificationTypes = ['note', 'roleAssigned', 'followRequestAccepted', 'achievementEarned']; +const $i = signinRequired(); + +const nonConfigurableNotificationTypes = ['note', 'roleAssigned', 'followRequestAccepted']; +const notificationTypesWithoutSender = ['achievementEarned']; const allowButton = shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>(); const pushRegistrationInServer = computed(() => allowButton.value?.pushRegistrationInServer); const sendReadMessage = computed(() => pushRegistrationInServer.value?.sendReadMessage || false); -const userLists = await os.api('users/lists/list'); +const userLists = await misskeyApi('users/lists/list'); async function readAllUnreadNotes() { await os.apiWithDialog('i/read-all-unread-notes'); @@ -86,11 +92,11 @@ async function readAllNotifications() { async function updateReceiveConfig(type, value) { await os.apiWithDialog('i/update', { notificationRecieveConfig: { - ...$i!.notificationRecieveConfig, + ...$i.notificationRecieveConfig, [type]: value, }, }).then(i => { - $i!.notificationRecieveConfig = i.notificationRecieveConfig; + $i.notificationRecieveConfig = i.notificationRecieveConfig; }); } @@ -107,15 +113,26 @@ function onChangeSendReadMessage(v: boolean) { } function testNotification(): void { - os.api('notifications/test-notification'); + misskeyApi('notifications/test-notification'); +} + +async function flushNotification() { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.ts.resetAreYouSure, + }); + + if (canceled) return; + + os.apiWithDialog('notifications/flush'); } const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.notifications, icon: 'ph-bell ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue index efda0c00b374c834c1ea29c92d8f05d9a30e0569..683e5f0e30da153d623763cc0fb1378f034b4ed1 100644 --- a/packages/frontend/src/pages/settings/other.vue +++ b/packages/frontend/src/pages/settings/other.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -104,26 +104,21 @@ import FormInfo from '@/components/MkInfo.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; -import { signout, $i } from '@/account.js'; +import { signout, signinRequired } from '@/account.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { unisonReload } from '@/scripts/unison-reload.js'; import FormSection from '@/components/form/section.vue'; +const $i = signinRequired(); + const reportError = computed(defaultStore.makeGetterSetter('reportError')); const enableCondensedLineForAcct = computed(defaultStore.makeGetterSetter('enableCondensedLineForAcct')); const devMode = computed(defaultStore.makeGetterSetter('devMode')); const defaultWithReplies = computed(defaultStore.makeGetterSetter('defaultWithReplies')); -function onChangeInjectFeaturedNote(v) { - os.api('i/update', { - injectFeaturedNote: v, - }).then((i) => { - $i!.injectFeaturedNote = i.injectFeaturedNote; - }); -} - async function deleteAccount() { { const { canceled } = await os.confirm({ @@ -165,11 +160,11 @@ async function updateRepliesAll(withReplies: boolean) { }); if (canceled) return; - os.api('following/update-all', { withReplies }); + misskeyApi('following/update-all', { withReplies }); } const exportData = () => { - os.api('i/export-data', {}).then(() => { + misskeyApi('i/export-data', {}).then(() => { os.alert({ type: 'info', text: i18n.ts.exportRequested, @@ -192,8 +187,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.other, icon: 'ph-dots-three ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/settings/plugin.install.vue b/packages/frontend/src/pages/settings/plugin.install.vue index be8db548a497622bc420b27796f05923d4076c72..f3dd862bd11b08bce0452800b6d60fd2bec7a9c1 100644 --- a/packages/frontend/src/pages/settings/plugin.install.vue +++ b/packages/frontend/src/pages/settings/plugin.install.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -53,8 +53,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts._plugin.install, icon: 'ph-download ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/settings/plugin.vue b/packages/frontend/src/pages/settings/plugin.vue index 5b5c282f397d30e81bf43beaeedecbf83e1127b1..f1699f726e162866e2909ba7aea2bfba510ca199 100644 --- a/packages/frontend/src/pages/settings/plugin.vue +++ b/packages/frontend/src/pages/settings/plugin.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -125,8 +125,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.plugins, icon: 'ph-plug ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue index c7538f3a1b513434bdeb1c8b70b243b7ca73bef0..f180e0b72cea575c9d0465867efb8ba2c47d69fe 100644 --- a/packages/frontend/src/pages/settings/preferences-backups.vue +++ b/packages/frontend/src/pages/settings/preferences-backups.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -37,12 +37,13 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, onMounted, onUnmounted, ref } from 'vue'; +import { onMounted, onUnmounted, ref } from 'vue'; import { v4 as uuid } from 'uuid'; import FormSection from '@/components/form/section.vue'; import MkButton from '@/components/MkButton.vue'; import MkInfo from '@/components/MkInfo.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { ColdDeviceStorage, defaultStore } from '@/store.js'; import { unisonReload } from '@/scripts/unison-reload.js'; import { useStream } from '@/stream.js'; @@ -70,6 +71,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [ 'animatedMfm', 'advancedMfm', 'loadRawImages', + 'warnMissingAltText', 'imageNewTab', 'dataSaver', 'disableShowingAnimatedImages', @@ -97,6 +99,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [ 'showClipButtonInNoteFooter', 'reactionsDisplaySize', 'forceShowAds', + 'oneko', 'numberOfReplies', 'aiChanMode', 'devMode', @@ -146,7 +149,7 @@ const connection = $i && useStream().useChannel('main'); const profiles = ref<Record<string, Profile> | null>(null); -os.api('i/registry/get-all', { scope }) +misskeyApi('i/registry/get-all', { scope }) .then(res => { profiles.value = res || {}; }); @@ -205,6 +208,7 @@ async function saveNew(): Promise<void> { const { canceled, result: name } = await os.inputText({ title: ts._preferencesBackups.inputName, + default: '', }); if (canceled) return; @@ -380,6 +384,7 @@ async function rename(id: string): Promise<void> { const { canceled: cancel1, result: name } = await os.inputText({ title: ts._preferencesBackups.inputName, + default: '', }); if (cancel1 || profiles.value[id].name === name) return; @@ -446,10 +451,10 @@ onUnmounted(() => { connection?.off('registryUpdated'); }); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: ts.preferencesBackups, icon: 'ph-floppy-disk ph-bold ph-lg', -}))); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue index 62056ff8a6b4cb068b47993db580ce8101cb2687..86cf5ab241778ff078bb15c1881ba009d0e3683f 100644 --- a/packages/frontend/src/pages/settings/privacy.vue +++ b/packages/frontend/src/pages/settings/privacy.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -77,12 +77,14 @@ import MkSwitch from '@/components/MkSwitch.vue'; import MkSelect from '@/components/MkSelect.vue'; import FormSection from '@/components/form/section.vue'; import MkFolder from '@/components/MkFolder.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; +const $i = signinRequired(); + const isLocked = ref($i.isLocked); const autoAcceptFollowed = ref($i.autoAcceptFollowed); const noCrawle = ref($i.noCrawle); @@ -90,8 +92,8 @@ const noindex = ref($i.noindex); const isExplorable = ref($i.isExplorable); const hideOnlineStatus = ref($i.hideOnlineStatus); const publicReactions = ref($i.publicReactions); -const followingVisibility = ref($i?.followingVisibility); -const followersVisibility = ref($i?.followersVisibility); +const followingVisibility = ref($i.followingVisibility); +const followersVisibility = ref($i.followersVisibility); const defaultNoteVisibility = computed(defaultStore.makeGetterSetter('defaultNoteVisibility')); const defaultNoteLocalOnly = computed(defaultStore.makeGetterSetter('defaultNoteLocalOnly')); @@ -99,7 +101,7 @@ const rememberNoteVisibility = computed(defaultStore.makeGetterSetter('rememberN const keepCw = computed(defaultStore.makeGetterSetter('keepCw')); function save() { - os.api('i/update', { + misskeyApi('i/update', { isLocked: !!isLocked.value, autoAcceptFollowed: !!autoAcceptFollowed.value, noCrawle: !!noCrawle.value, @@ -116,8 +118,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.privacy, icon: 'ph-lock ph-bold ph-lg-open', -}); +})); </script> diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index 4bae635d05ce6f709cdf3e8cb2a13da94bf1727a..408cf4ed6792e6d744774ea91ab0abd60a3129c8 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -127,14 +127,17 @@ import FormSlot from '@/components/form/slot.vue'; import { selectFile } from '@/scripts/select-file.js'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; import { langmap } from '@/scripts/langmap.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { claimAchievement } from '@/scripts/achievements.js'; import { defaultStore } from '@/store.js'; +import { globalEvents } from '@/events.js'; import MkInfo from '@/components/MkInfo.vue'; import MkTextarea from '@/components/MkTextarea.vue'; +const $i = signinRequired(); + const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); const reactionAcceptance = computed(defaultStore.makeGetterSetter('reactionAcceptance')); @@ -152,11 +155,11 @@ const profile = reactive({ description: $i.description, location: $i.location, birthday: $i.birthday, - listenbrainz: $i?.listenbrainz, + listenbrainz: $i.listenbrainz, lang: $i.lang, - isBot: $i.isBot, - isCat: $i.isCat, - speakAsCat: $i.speakAsCat, + isBot: $i.isBot ?? false, + isCat: $i.isCat ?? false, + speakAsCat: $i.speakAsCat ?? false, }); watch(() => profile, () => { @@ -165,7 +168,7 @@ watch(() => profile, () => { deep: true, }); -const fields = ref($i?.fields.map(field => ({ id: Math.random().toString(), name: field.name, value: field.value })) ?? []); +const fields = ref($i.fields.map(field => ({ id: Math.random().toString(), name: field.name, value: field.value })) ?? []); const fieldEditMode = ref(false); function addField() { @@ -188,6 +191,7 @@ function saveFields() { os.apiWithDialog('i/update', { fields: fields.value.filter(field => field.name !== '' && field.value !== '').map(field => ({ name: field.name, value: field.value })), }); + globalEvents.emit('requestClearPageCache'); } function save() { @@ -215,6 +219,7 @@ function save() { isCat: !!profile.isCat, speakAsCat: !!profile.speakAsCat, }); + globalEvents.emit('requestClearPageCache'); claimAchievement('profileFilled'); if (profile.name === 'syuilo' || profile.name === 'ã—ã‚…ã„ã‚') { claimAchievement('setNameToSyuilo'); @@ -230,7 +235,7 @@ function changeAvatar(ev) { const { canceled } = await os.confirm({ type: 'question', - text: i18n.t('cropImageAsk'), + text: i18n.ts.cropImageAsk, okText: i18n.ts.cropYes, cancelText: i18n.ts.cropNo, }); @@ -246,68 +251,153 @@ function changeAvatar(ev) { }); $i.avatarId = i.avatarId; $i.avatarUrl = i.avatarUrl; + globalEvents.emit('requestClearPageCache'); claimAchievement('profileFilled'); }); } function changeBanner(ev) { - selectFile(ev.currentTarget ?? ev.target, i18n.ts.banner).then(async (file) => { - let originalOrCropped = file; + if ($i.bannerId) { + os.popupMenu([{ + text: i18n.ts._profile.updateBanner, + action: async () => { + selectFile(ev.currentTarget ?? ev.target, i18n.ts.banner).then(async (file) => { + let originalOrCropped = file; + + const { canceled } = await os.confirm({ + type: 'question', + text: i18n.ts.cropImageAsk, + okText: i18n.ts.cropYes, + cancelText: i18n.ts.cropNo, + }); + + if (!canceled) { + originalOrCropped = await os.cropImage(file, { + aspectRatio: 2, + }); + } + + const i = await os.apiWithDialog('i/update', { + bannerId: originalOrCropped.id, + }); + $i.bannerId = i.bannerId; + $i.bannerUrl = i.bannerUrl; + globalEvents.emit('requestClearPageCache'); + }); + }, + }, { + text: i18n.ts._profile.removeBanner, + action: async () => { + const i = await os.apiWithDialog('i/update', { + bannerId: null, + }); + $i.bannerId = i.bannerId; + $i.bannerUrl = i.bannerUrl; + globalEvents.emit('requestClearPageCache'); + }, + }], ev.currentTarget ?? ev.target); + } else { + selectFile(ev.currentTarget ?? ev.target, i18n.ts.banner).then(async (file) => { + let originalOrCropped = file; + + const { canceled } = await os.confirm({ + type: 'question', + text: i18n.ts.cropImageAsk, + okText: i18n.ts.cropYes, + cancelText: i18n.ts.cropNo, + }); - const { canceled } = await os.confirm({ - type: 'question', - text: i18n.t('cropImageAsk'), - okText: i18n.ts.cropYes, - cancelText: i18n.ts.cropNo, - }); + if (!canceled) { + originalOrCropped = await os.cropImage(file, { + aspectRatio: 2, + }); + } - if (!canceled) { - originalOrCropped = await os.cropImage(file, { - aspectRatio: 2, + const i = await os.apiWithDialog('i/update', { + bannerId: originalOrCropped.id, }); - } - - const i = await os.apiWithDialog('i/update', { - bannerId: originalOrCropped.id, + $i.bannerId = i.bannerId; + $i.bannerUrl = i.bannerUrl; + globalEvents.emit('requestClearPageCache'); }); - $i.bannerId = i.bannerId; - $i.bannerUrl = i.bannerUrl; - }); + } } function changeBackground(ev) { - selectFile(ev.currentTarget ?? ev.target, i18n.ts.background).then(async (file) => { - let originalOrCropped = file; + if ($i.backgroundId) { + os.popupMenu([{ + text: i18n.ts._profile.updateBackground, + action: async () => { + selectFile(ev.currentTarget ?? ev.target, i18n.ts.background).then(async (file) => { + let originalOrCropped = file; + + const { canceled } = await os.confirm({ + type: 'question', + text: i18n.ts.cropImageAsk, + okText: i18n.ts.cropYes, + cancelText: i18n.ts.cropNo, + }); + + if (!canceled) { + originalOrCropped = await os.cropImage(file, { + aspectRatio: 1, + }); + } + + const i = await os.apiWithDialog('i/update', { + backgroundId: originalOrCropped.id, + }); + $i.backgroundId = i.backgroundId; + $i.backgroundUrl = i.backgroundUrl; + globalEvents.emit('requestClearPageCache'); + }); + }, + }, { + text: i18n.ts._profile.removeBackground, + action: async () => { + const i = await os.apiWithDialog('i/update', { + backgroundId: null, + }); + $i.backgroundId = i.backgroundId; + $i.backgroundUrl = i.backgroundUrl; + globalEvents.emit('requestClearPageCache'); + }, + }], ev.currentTarget ?? ev.target); + } else { + selectFile(ev.currentTarget ?? ev.target, i18n.ts.background).then(async (file) => { + let originalOrCropped = file; + + const { canceled } = await os.confirm({ + type: 'question', + text: i18n.ts.cropImageAsk, + okText: i18n.ts.cropYes, + cancelText: i18n.ts.cropNo, + }); - const { canceled } = await os.confirm({ - type: 'question', - text: i18n.t('cropImageAsk'), - okText: i18n.ts.cropYes, - cancelText: i18n.ts.cropNo, - }); + if (!canceled) { + originalOrCropped = await os.cropImage(file, { + aspectRatio: 1, + }); + } - if (!canceled) { - originalOrCropped = await os.cropImage(file, { - aspectRatio: 1, + const i = await os.apiWithDialog('i/update', { + backgroundId: originalOrCropped.id, }); - } - - const i = await os.apiWithDialog('i/update', { - backgroundId: originalOrCropped.id, + $i.backgroundId = i.backgroundId; + $i.backgroundUrl = i.backgroundUrl; + globalEvents.emit('requestClearPageCache'); }); - $i.backgroundId = i.backgroundId; - $i.backgroundUrl = i.backgroundUrl; - }); + } } const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.profile, icon: 'ph-user ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/settings/roles.vue b/packages/frontend/src/pages/settings/roles.vue index 716b168c92ad4b52221b725be471987bb0914cd5..273cf013f08733b7374251d8d74ea6187e80bf16 100644 --- a/packages/frontend/src/pages/settings/roles.vue +++ b/packages/frontend/src/pages/settings/roles.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -27,24 +27,20 @@ import { computed } from 'vue'; import FormSection from '@/components/form/section.vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkRolePreview from '@/components/MkRolePreview.vue'; -function save() { - os.apiWithDialog('i/update', { - - }); -} +const $i = signinRequired(); const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.roles, icon: 'ph-seal-check ph-bold ph-lg', -}); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue index 9ae479e6e4c5a3610069dbb4b884410548219616..43e5104d71190e9312d352044c92ed561d4d2037 100644 --- a/packages/frontend/src/pages/settings/security.vue +++ b/packages/frontend/src/pages/settings/security.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -47,6 +47,7 @@ import FormSlot from '@/components/form/slot.vue'; import MkButton from '@/components/MkButton.vue'; import MkPagination from '@/components/MkPagination.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -92,7 +93,7 @@ async function regenerateToken() { const auth = await os.authenticateDialog(); if (auth.canceled) return; - os.api('i/regenerate-token', { + misskeyApi('i/regenerate-token', { password: auth.result.password, token: auth.result.token, }); @@ -102,10 +103,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.security, icon: 'ph-lock ph-bold ph-lg', -}); +})); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/pages/settings/sounds.sound.vue b/packages/frontend/src/pages/settings/sounds.sound.vue index a43ffb1f0b436df70b0888d721282bee344759d8..307c5eaae462721fdd7767849c9aaabe4356c035 100644 --- a/packages/frontend/src/pages/settings/sounds.sound.vue +++ b/packages/frontend/src/pages/settings/sounds.sound.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -32,7 +32,8 @@ import MkButton from '@/components/MkButton.vue'; import MkRange from '@/components/MkRange.vue'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; -import { playFile, soundsTypes, getSoundDuration } from '@/scripts/sound.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; +import { playMisskeySfxFile, soundsTypes, getSoundDuration } from '@/scripts/sound.js'; import { selectFile } from '@/scripts/select-file.js'; const props = defineProps<{ @@ -53,7 +54,7 @@ const fileName = ref<string>(''); const volume = ref(props.volume); if (type.value === '_driveFile_' && fileId.value) { - const apiRes = await os.api('drive/files/show', { + const apiRes = await misskeyApi('drive/files/show', { fileId: fileId.value, }); fileName.value = apiRes.name; @@ -118,7 +119,7 @@ function listen() { return; } - playFile(type.value === '_driveFile_' ? { + playMisskeySfxFile(type.value === '_driveFile_' ? { type: '_driveFile_', fileId: fileId.value as string, fileUrl: fileUrl.value as string, diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue index bec41a6cec731c0bdca9d376ccd5d221d22d3d83..bf398ac30304ea0d0a099231318c1c023843e23b 100644 --- a/packages/frontend/src/pages/settings/sounds.vue +++ b/packages/frontend/src/pages/settings/sounds.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.sounds }}</template> <div class="_gaps_s"> <MkFolder v-for="type in operationTypes" :key="type"> - <template #label>{{ i18n.t('_sfx.' + type) }}</template> + <template #label>{{ i18n.ts._sfx[type] }}</template> <template #suffix>{{ getSoundTypeName(sounds[type].type) }}</template> <XSound :type="sounds[type].type" :volume="sounds[type].volume" :fileId="sounds[type].fileId" :fileUrl="sounds[type].fileUrl" @update="(res) => updated(type, res)"/> @@ -33,9 +33,9 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { Ref, computed, ref } from 'vue'; +import XSound from './sounds.sound.vue'; import type { SoundType, OperationType } from '@/scripts/sound.js'; import type { SoundStore } from '@/store.js'; -import XSound from './sounds.sound.vue'; import MkRange from '@/components/MkRange.vue'; import MkButton from '@/components/MkButton.vue'; import FormSection from '@/components/form/section.vue'; @@ -94,8 +94,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.sounds, icon: 'ph-music-notes ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/settings/statusbar.statusbar.vue b/packages/frontend/src/pages/settings/statusbar.statusbar.vue index de5f1a3db9c89a21ce9b212bf8da5e6843a217f2..92e389a288f00e03ee93a74c915fb12de3b940b2 100644 --- a/packages/frontend/src/pages/settings/statusbar.statusbar.vue +++ b/packages/frontend/src/pages/settings/statusbar.statusbar.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/settings/statusbar.vue b/packages/frontend/src/pages/settings/statusbar.vue index c45e386ac52cc27d93b49a425b4192554527f2dd..fa924d13f0b44d8d49a8a7b99629784235827435 100644 --- a/packages/frontend/src/pages/settings/statusbar.vue +++ b/packages/frontend/src/pages/settings/statusbar.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -21,7 +21,7 @@ import { v4 as uuid } from 'uuid'; import XStatusbar from './statusbar.statusbar.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkButton from '@/components/MkButton.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -31,7 +31,7 @@ const statusbars = defaultStore.reactiveState.statusbars; const userLists = ref<Misskey.entities.UserList[] | null>(null); onMounted(() => { - os.api('users/lists/list').then(res => { + misskeyApi('users/lists/list').then(res => { userLists.value = res; }); }); @@ -50,8 +50,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.statusbar, icon: 'ph-list ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/settings/theme.install.vue b/packages/frontend/src/pages/settings/theme.install.vue index d377590b9d913b913b6f33830bb418579d0f4aa4..01ae5286b7e98966e2562507af514ce4f07db472 100644 --- a/packages/frontend/src/pages/settings/theme.install.vue +++ b/packages/frontend/src/pages/settings/theme.install.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -33,7 +33,7 @@ async function install(code: string): Promise<void> { await installTheme(code); os.alert({ type: 'success', - text: i18n.t('_theme.installed', { name: theme.name }), + text: i18n.tsx._theme.installed({ name: theme.name }), }); } catch (err) { switch (err.message.toLowerCase()) { @@ -59,8 +59,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts._theme.install, icon: 'ph-download ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/settings/theme.manage.vue b/packages/frontend/src/pages/settings/theme.manage.vue index f7856d122f996ce065f4822e3ce6698c08a87b06..43d76951c06f2718baf3bac0eca97b7cc8086052 100644 --- a/packages/frontend/src/pages/settings/theme.manage.vue +++ b/packages/frontend/src/pages/settings/theme.manage.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -76,8 +76,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts._theme.manage, icon: 'ph-wrench ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue index cb9c714441a55d21b56ecb98ffe913adb04b4da9..9b493f5ffeeadcfc2e7c96f467aa8ff638d0e8b2 100644 --- a/packages/frontend/src/pages/settings/theme.vue +++ b/packages/frontend/src/pages/settings/theme.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -88,6 +88,18 @@ import { uniqueBy } from '@/scripts/array.js'; import { fetchThemes, getThemes } from '@/theme-store.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { miLocalStorage } from '@/local-storage.js'; +import { unisonReload } from '@/scripts/unison-reload.js'; +import * as os from '@/os.js'; + +async function reloadAsk() { + const { canceled } = await os.confirm({ + type: 'info', + text: i18n.ts.reloadToApplySetting, + }); + if (canceled) return; + + unisonReload(); +} const installedThemes = ref(getThemes()); const builtinThemes = getBuiltinThemesRef(); @@ -124,6 +136,7 @@ const lightThemeId = computed({ } }, }); + const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); const syncDeviceDarkMode = computed(ColdDeviceStorage.makeGetterSetter('syncDeviceDarkMode')); const wallpaper = ref(miLocalStorage.getItem('wallpaper')); @@ -141,7 +154,7 @@ watch(wallpaper, () => { } else { miLocalStorage.setItem('wallpaper', wallpaper.value); } - location.reload(); + reloadAsk(); }); onActivated(() => { @@ -164,10 +177,10 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.theme, icon: 'ph-palette ph-bold ph-lg', -}); +})); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/pages/settings/webhook.edit.vue b/packages/frontend/src/pages/settings/webhook.edit.vue index f6e2f633179a3cc83c8e82ba3859fa7e436436c8..99326c867167269d982437c7da1bf8f72ebfdfeb 100644 --- a/packages/frontend/src/pages/settings/webhook.edit.vue +++ b/packages/frontend/src/pages/settings/webhook.edit.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -48,9 +48,10 @@ import FormSection from '@/components/form/section.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { useRouter } from '@/router.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -58,7 +59,7 @@ const props = defineProps<{ webhookId: string; }>(); -const webhook = await os.api('i/webhooks/show', { +const webhook = await misskeyApi('i/webhooks/show', { webhookId: props.webhookId, }); @@ -98,7 +99,7 @@ async function save(): Promise<void> { async function del(): Promise<void> { const { canceled } = await os.confirm({ type: 'warning', - text: i18n.t('deleteAreYouSure', { x: webhook.name }), + text: i18n.tsx.deleteAreYouSure({ x: webhook.name }), }); if (canceled) return; @@ -113,8 +114,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: 'Edit webhook', icon: 'ph-webhooks-logo ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/settings/webhook.new.vue b/packages/frontend/src/pages/settings/webhook.new.vue index 032796caf05e0e109f451f2e4aa4437aa465ed95..299386338ab728a80154b657fc85deec353299d3 100644 --- a/packages/frontend/src/pages/settings/webhook.new.vue +++ b/packages/frontend/src/pages/settings/webhook.new.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -82,8 +82,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: 'Create new webhook', icon: 'ph-webhooks-logo ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/settings/webhook.vue b/packages/frontend/src/pages/settings/webhook.vue index c391458274ac25bcf7e0a2052e4ba382568f5ffc..3717abb13ebac633074fc6f998e24509b3324f95 100644 --- a/packages/frontend/src/pages/settings/webhook.vue +++ b/packages/frontend/src/pages/settings/webhook.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -50,8 +50,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: 'Webhook', icon: 'ph-webhooks-logo ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/share.vue b/packages/frontend/src/pages/share.vue index a978be0ae5b4be4ac1c1ef85115ee970978c1235..1eeeb587eb8222a87d9cad185a78b3e7349ed3d1 100644 --- a/packages/frontend/src/pages/share.vue +++ b/packages/frontend/src/pages/share.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -37,6 +37,7 @@ import * as Misskey from 'misskey-js'; import MkButton from '@/components/MkButton.vue'; import MkPostForm from '@/components/MkPostForm.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { postMessageToParentWindow } from '@/scripts/post-message.js'; import { i18n } from '@/i18n.js'; @@ -55,7 +56,7 @@ const renote = ref<Misskey.entities.Note | undefined>(); const visibility = ref(Misskey.noteVisibilities.includes(visibilityQuery) ? visibilityQuery : undefined); const localOnly = ref(localOnlyQuery === '0' ? false : localOnlyQuery === '1' ? true : undefined); const files = ref([] as Misskey.entities.DriveFile[]); -const visibleUsers = ref([] as Misskey.entities.User[]); +const visibleUsers = ref([] as Misskey.entities.UserDetailed[]); async function init() { let noteText = ''; @@ -76,7 +77,7 @@ async function init() { ] // TypeScriptã®æŒ‡ç¤ºé€šã‚Šã«å¤‰æ›ã™ã‚‹ .map(q => 'username' in q ? { username: q.username, host: q.host === null ? undefined : q.host } : q) - .map(q => os.api('users/show', q) + .map(q => misskeyApi('users/show', q) .then(user => { visibleUsers.value.push(user); }, () => { @@ -91,11 +92,11 @@ async function init() { const replyId = urlParams.get('replyId'); const replyUri = urlParams.get('replyUri'); if (replyId) { - reply.value = await os.api('notes/show', { + reply.value = await misskeyApi('notes/show', { noteId: replyId, }); } else if (replyUri) { - const obj = await os.api('ap/show', { + const obj = await misskeyApi('ap/show', { uri: replyUri, }); if (obj.type === 'Note') { @@ -108,11 +109,11 @@ async function init() { const renoteId = urlParams.get('renoteId'); const renoteUri = urlParams.get('renoteUri'); if (renoteId) { - renote.value = await os.api('notes/show', { + renote.value = await misskeyApi('notes/show', { noteId: renoteId, }); } else if (renoteUri) { - const obj = await os.api('ap/show', { + const obj = await misskeyApi('ap/show', { uri: renoteUri, }); if (obj.type === 'Note') { @@ -126,7 +127,7 @@ async function init() { if (fileIds) { await Promise.all( fileIds.split(',') - .map(fileId => os.api('drive/files/show', { fileId }) + .map(fileId => misskeyApi('drive/files/show', { fileId }) .then(file => { files.value.push(file); }, () => { @@ -171,8 +172,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.share, icon: 'ph-share-network ph-bold ph-lg', -}); +})); </script> diff --git a/packages/frontend/src/pages/signup-complete.vue b/packages/frontend/src/pages/signup-complete.vue index 4009652bcfc668051829d8e25826c797a670c955..b08a304cfd6ca5afac56dfbef0b604e34d1e92ff 100644 --- a/packages/frontend/src/pages/signup-complete.vue +++ b/packages/frontend/src/pages/signup-complete.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only <i class="ph-check ph-bold ph-lg"></i> </div> <div class="_gaps_m" style="padding: 32px;"> - <div>{{ i18n.t('clickToFinishEmailVerification', { ok: i18n.ts.gotIt }) }}</div> + <div>{{ i18n.tsx.clickToFinishEmailVerification({ ok: i18n.ts.gotIt }) }}</div> <div> <MkButton gradate large rounded type="submit" :disabled="submitting" data-cy-admin-ok style="margin: 0 auto;"> {{ submitting ? i18n.ts.processing : i18n.ts.gotIt }}<MkEllipsis v-if="submitting"/> @@ -31,6 +31,7 @@ import MkAnimBg from '@/components/MkAnimBg.vue'; import { login } from '@/account.js'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const submitting = ref(false); @@ -42,7 +43,7 @@ function submit() { if (submitting.value) return; submitting.value = true; - os.api('signup-pending', { + misskeyApi('signup-pending', { code: props.code, }).then(res => { if (res.pendingApproval) { diff --git a/packages/frontend/src/pages/tag.vue b/packages/frontend/src/pages/tag.vue index 167816638c6565852f5e537279521544bd5f5bdd..d9c94569a72e7a5a4286b3eb46da2acb38bcdc78 100644 --- a/packages/frontend/src/pages/tag.vue +++ b/packages/frontend/src/pages/tag.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-if="$i" #footer> <div :class="$style.footer"> <MkSpacer :contentMax="800" :marginMin="16" :marginMax="16"> - <MkButton rounded primary :class="$style.button" @click="post()"><i class="ph-pencil ph-bold ph-lg"></i>{{ i18n.ts.postToHashtag }}</MkButton> + <MkButton rounded primary :class="$style.button" @click="post()"><i class="ph-pencil-simple ph-bold ph-lg"></i>{{ i18n.ts.postToHashtag }}</MkButton> </MkSpacer> </div> </template> @@ -55,21 +55,22 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: props.tag, icon: 'ph-hash ph-bold ph-lg', -}))); +})); </script> <style lang="scss" module> .footer { -webkit-backdrop-filter: var(--blur, blur(15px)); backdrop-filter: var(--blur, blur(15px)); + background: var(--acrylicBg); border-top: solid 0.5px var(--divider); display: flex; } .button { - margin: 0 auto var(--margin) auto; + margin: 0 auto; } </style> diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue index 4b4196d0a97260246727ba2b02bd219703415b53..d020320b44dabb2a9a5db691f5a7c880e08c00df 100644 --- a/packages/frontend/src/pages/theme-editor.vue +++ b/packages/frontend/src/pages/theme-editor.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -186,7 +186,7 @@ function applyThemeCode() { async function saveAs() { const { canceled, result: name } = await os.inputText({ title: i18n.ts.name, - allowEmpty: false, + minLength: 1, }); if (canceled) return; @@ -204,7 +204,7 @@ async function saveAs() { changed.value = false; os.alert({ type: 'success', - text: i18n.t('_theme.installed', { name: theme.value.name }), + text: i18n.tsx._theme.installed({ name: theme.value.name }), }); } @@ -219,10 +219,10 @@ const headerActions = computed(() => [{ const headerTabs = computed(() => []); -definePageMetadata({ +definePageMetadata(() => ({ title: i18n.ts.themeEditor, icon: 'ph-palette ph-bold ph-lg', -}); +})); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index f5cefeddb4c1e4d34868dc8bef932155d707abda..a9f7a163f67cd936c534749d2bb77c4fed2f1096 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,28 +7,29 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :displayMyAvatar="true"/></template> <MkSpacer :contentMax="800"> - <div ref="rootEl" v-hotkey.global="keymap"> - <MkInfo v-if="['home', 'local', 'social', 'global'].includes(src) && !defaultStore.reactiveState.timelineTutorials.value[src]" style="margin-bottom: var(--margin);" closable @close="closeTutorial()"> - {{ i18n.ts._timelineDescription[src] }} - </MkInfo> - <MkPostForm v-if="defaultStore.reactiveState.showFixedPostForm.value" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/> - - <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> - <div :class="$style.tl"> - <MkTimeline - ref="tlComponent" - :key="src + withRenotes + withReplies + onlyFiles" - :src="src.split(':')[0]" - :list="src.split(':')[1]" - :withRenotes="withRenotes" - :withReplies="withReplies" - :onlyFiles="onlyFiles" - :withBots="withBots" - :sound="true" - @queue="queueUpdated" - /> + <MkHorizontalSwipe v-model:tab="src" :tabs="$i ? headerTabs : headerTabsWhenNotLogin"> + <div :key="src" ref="rootEl" v-hotkey.global="keymap"> + <MkInfo v-if="['home', 'local', 'social', 'global'].includes(src) && !defaultStore.reactiveState.timelineTutorials.value[src]" style="margin-bottom: var(--margin);" closable @close="closeTutorial()"> + {{ i18n.ts._timelineDescription[src] }} + </MkInfo> + <MkPostForm v-if="defaultStore.reactiveState.showFixedPostForm.value" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/> + <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> + <div :class="$style.tl"> + <MkTimeline + ref="tlComponent" + :key="src + withRenotes + withReplies + onlyFiles" + :src="src.split(':')[0]" + :list="src.split(':')[1]" + :withRenotes="withRenotes" + :withReplies="withReplies" + :onlyFiles="onlyFiles" + :withBots="withBots" + :sound="true" + @queue="queueUpdated" + /> + </div> </div> - </div> + </MkHorizontalSwipe> </MkSpacer> </MkStickyContainer> </template> @@ -39,8 +40,10 @@ import type { Tab } from '@/components/global/MkPageHeader.tabs.vue'; import MkTimeline from '@/components/MkTimeline.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkPostForm from '@/components/MkPostForm.vue'; +import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; import { scroll } from '@/scripts/scroll.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import { instance } from '@/instance.js'; @@ -48,6 +51,7 @@ import { $i } from '@/account.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { antennasCache, userListsCache } from '@/cache.js'; import { deviceKind } from '@/scripts/device-kind.js'; +import { deepMerge } from '@/scripts/merge.js'; import { MenuItem } from '@/types/menu.js'; import { miLocalStorage } from '@/local-storage.js'; @@ -64,17 +68,68 @@ const tlComponent = shallowRef<InstanceType<typeof MkTimeline>>(); const rootEl = shallowRef<HTMLElement>(); const queue = ref(0); -const srcWhenNotSignin = ref(isLocalTimelineAvailable ? 'local' : 'global'); -const src = computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin.value), set: (x) => saveSrc(x) }); -const withRenotes = ref(true); -const withReplies = ref($i ? defaultStore.state.tlWithReplies : false); -const withBots = ref($i ? defaultStore.state.tlWithBots : true); -const onlyFiles = ref(false); +const srcWhenNotSignin = ref<'local' | 'global'>(isLocalTimelineAvailable ? 'local' : 'global'); +const src = computed<'home' | 'local' | 'social' | 'global' | 'bubble' | `list:${string}`>({ + get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin.value), + set: (x) => saveSrc(x), +}); +const withRenotes = computed<boolean>({ + get: () => defaultStore.reactiveState.tl.value.filter.withRenotes, + set: (x) => saveTlFilter('withRenotes', x), +}); + +// computed内ã§ã®ç„¡é™ãƒ«ãƒ¼ãƒ—を防ããŸã‚ã®ãƒ•ãƒ©ã‚° +const localSocialTLFilterSwitchStore = ref<'withReplies' | 'onlyFiles' | false>('withReplies'); -watch(src, () => queue.value = 0); +const withReplies = computed<boolean>({ + get: () => { + if (!$i) return false; + if (['local', 'social'].includes(src.value) && localSocialTLFilterSwitchStore.value === 'onlyFiles') { + return false; + } else { + return defaultStore.reactiveState.tl.value.filter.withReplies; + } + }, + set: (x) => saveTlFilter('withReplies', x), +}); +const withBots = computed<boolean>({ + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + get: () => (defaultStore.reactiveState.tl.value.filter?.withBots ?? saveTlFilter('withBots', true)), + set: (x) => saveTlFilter('withBots', x), +}); +const onlyFiles = computed<boolean>({ + get: () => { + if (['local', 'social'].includes(src.value) && localSocialTLFilterSwitchStore.value === 'withReplies') { + return false; + } else { + return defaultStore.reactiveState.tl.value.filter.onlyFiles; + } + }, + set: (x) => saveTlFilter('onlyFiles', x), +}); + +watch([withReplies, onlyFiles], ([withRepliesTo, onlyFilesTo]) => { + if (withRepliesTo) { + localSocialTLFilterSwitchStore.value = 'withReplies'; + } else if (onlyFilesTo) { + localSocialTLFilterSwitchStore.value = 'onlyFiles'; + } else { + localSocialTLFilterSwitchStore.value = false; + } +}); + +const withSensitive = computed<boolean>({ + get: () => defaultStore.reactiveState.tl.value.filter.withSensitive, + set: (x) => saveTlFilter('withSensitive', x), +}); -watch(withReplies, (x) => { - if ($i) defaultStore.set('tlWithReplies', x); +watch(src, () => { + queue.value = 0; +}); + +watch(withSensitive, () => { + // ã“ã‚Œã ã‘ã¯ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆå´ã§å®Œçµã™ã‚‹å‡¦ç†ãªã®ã§æ‰‹å‹•ã§ãƒªãƒãƒ¼ãƒ‰ + tlComponent.value?.reloadTimeline(); }); function queueUpdated(q: number): void { @@ -125,7 +180,7 @@ async function chooseAntenna(ev: MouseEvent): Promise<void> { } async function chooseChannel(ev: MouseEvent): Promise<void> { - const channels = await os.api('channels/my-favorites', { + const channels = await misskeyApi('channels/my-favorites', { limit: 100, }); const items: MenuItem[] = [ @@ -152,16 +207,24 @@ async function chooseChannel(ev: MouseEvent): Promise<void> { } function saveSrc(newSrc: 'home' | 'local' | 'social' | 'global' | 'bubble' | `list:${string}`): void { - let userList = null; + const out = deepMerge({ src: newSrc }, defaultStore.state.tl); + if (newSrc.startsWith('userList:')) { const id = newSrc.substring('userList:'.length); - userList = defaultStore.reactiveState.pinnedUserLists.value.find(l => l.id === id); + out.userList = defaultStore.reactiveState.pinnedUserLists.value.find(l => l.id === id) ?? null; + } + + defaultStore.set('tl', out); + if (['local', 'global'].includes(newSrc)) { + srcWhenNotSignin.value = newSrc as 'local' | 'global'; + } +} + +function saveTlFilter(key: keyof typeof defaultStore.state.tl.filter, newValue: boolean) { + if (key !== 'withReplies' || $i) { + const out = deepMerge({ filter: { [key]: newValue } }, defaultStore.state.tl); + defaultStore.set('tl', out); } - defaultStore.set('tl', { - src: newSrc, - userList, - }); - srcWhenNotSignin.value = newSrc; } async function timetravel(): Promise<void> { @@ -200,6 +263,10 @@ const headerActions = computed(() => { ref: withReplies, disabled: onlyFiles, } : undefined, { + type: 'switch', + text: i18n.ts.withSensitive, + ref: withSensitive, + }, { type: 'switch', text: i18n.ts.fileAttachedOnly, ref: onlyFiles, @@ -213,8 +280,7 @@ const headerActions = computed(() => { icon: 'ph-arrows-counter-clockwise ph-bold ph-lg', text: i18n.ts.reload, handler: (ev: Event) => { - console.log('called'); - tlComponent.value.reloadTimeline(); + tlComponent.value?.reloadTimeline(); }, }); } @@ -283,10 +349,10 @@ const headerTabsWhenNotLogin = computed(() => [ }] : []), ] as Tab[]); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: i18n.ts.timeline, icon: src.value === 'local' ? 'ph-planet ph-bold ph-lg' : src.value === 'social' ? 'ph-rocket-launch ph-bold ph-lg' : src.value === 'global' ? 'ph-globe-hemisphere-west ph-bold ph-lg' : src.value === 'bubble' ? 'ph-drop ph-bold ph-lg' : 'ph-house ph-bold ph-lg', -}))); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue index 3ec23df7b8faeb600c07d1bfa62c8a326d670367..dd0b7fb675458cac10b8ac46f6b8874f61042f70 100644 --- a/packages/frontend/src/pages/user-list-timeline.vue +++ b/packages/frontend/src/pages/user-list-timeline.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -28,10 +28,10 @@ import { computed, watch, ref, shallowRef } from 'vue'; import * as Misskey from 'misskey-js'; import MkTimeline from '@/components/MkTimeline.vue'; import { scroll } from '@/scripts/scroll.js'; -import * as os from '@/os.js'; -import { useRouter } from '@/router.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; +import { useRouter } from '@/router/supplier.js'; const router = useRouter(); @@ -45,7 +45,7 @@ const tlEl = shallowRef<InstanceType<typeof MkTimeline>>(); const rootEl = shallowRef<HTMLElement>(); watch(() => props.listId, async () => { - list.value = await os.api('users/lists/show', { + list.value = await misskeyApi('users/lists/show', { listId: props.listId, }); }, { immediate: true }); @@ -70,10 +70,10 @@ const headerActions = computed(() => list.value ? [{ const headerTabs = computed(() => []); -definePageMetadata(computed(() => list.value ? { - title: list.value.name, +definePageMetadata(() => ({ + title: list.value ? list.value.name : i18n.ts.lists, icon: 'ph-list ph-bold ph-lg', -} : null)); +})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/user-tag.vue b/packages/frontend/src/pages/user-tag.vue index 7e6757bba5e6296b3d35077a1343f34e1f605468..b6ebfb8abcd69c046c20cfb7d1d1574c3173441c 100644 --- a/packages/frontend/src/pages/user-tag.vue +++ b/packages/frontend/src/pages/user-tag.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -34,9 +34,9 @@ const tagUsers = computed(() => ({ }, })); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: props.tag, icon: 'ph-user-circle ph-bold ph-lg', -}))); +})); </script> diff --git a/packages/frontend/src/pages/user/achievements.vue b/packages/frontend/src/pages/user/achievements.vue index 4e1444307473d5176aba4aaec405164c18782cc3..403e74904c8bbb711297eefd1ae6ae6f601f5e8d 100644 --- a/packages/frontend/src/pages/user/achievements.vue +++ b/packages/frontend/src/pages/user/achievements.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/user/activity.following.vue b/packages/frontend/src/pages/user/activity.following.vue index bd1159cb3219156f09ffad72442efd442d59a270..aa2c791c76dc7108e362a28701830778d7ecff76 100644 --- a/packages/frontend/src/pages/user/activity.following.vue +++ b/packages/frontend/src/pages/user/activity.following.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -18,7 +18,7 @@ import { onMounted, shallowRef, ref } from 'vue'; import { Chart, ChartDataset } from 'chart.js'; import * as Misskey from 'misskey-js'; import gradient from 'chartjs-plugin-gradient'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import { chartVLine } from '@/scripts/chart-vline.js'; @@ -61,7 +61,7 @@ async function renderChart() { })); }; - const raw = await os.api('charts/user/following', { userId: props.user.id, limit: chartLimit, span: 'day' }); + const raw = await misskeyApi('charts/user/following', { userId: props.user.id, limit: chartLimit, span: 'day' }); const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; diff --git a/packages/frontend/src/pages/user/activity.heatmap.vue b/packages/frontend/src/pages/user/activity.heatmap.vue deleted file mode 100644 index ff46db9653e699833f50580719a01cb6d936edd1..0000000000000000000000000000000000000000 --- a/packages/frontend/src/pages/user/activity.heatmap.vue +++ /dev/null @@ -1,219 +0,0 @@ -<!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors -SPDX-License-Identifier: AGPL-3.0-only ---> - -<template> -<div ref="rootEl"> - <MkLoading v-if="fetching"/> - <div v-else :class="$style.root" class="_panel"> - <canvas ref="chartEl"></canvas> - </div> -</div> -</template> - -<script lang="ts" setup> -import { onMounted, nextTick, watch, shallowRef, ref } from 'vue'; -import { Chart } from 'chart.js'; -import * as Misskey from 'misskey-js'; -import * as os from '@/os.js'; -import { defaultStore } from '@/store.js'; -import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; -import { alpha } from '@/scripts/color.js'; -import { initChart } from '@/scripts/init-chart.js'; - -initChart(); - -const props = defineProps<{ - src: string; - user: Misskey.entities.User; -}>(); - -const rootEl = shallowRef<HTMLDivElement>(null); -const chartEl = shallowRef<HTMLCanvasElement>(null); -const now = new Date(); -let chartInstance: Chart = null; -const fetching = ref(true); - -const { handler: externalTooltipHandler } = useChartTooltip({ - position: 'middle', -}); - -async function renderChart() { - if (chartInstance) { - chartInstance.destroy(); - } - - const wide = rootEl.value.offsetWidth > 700; - const narrow = rootEl.value.offsetWidth < 400; - - const weeks = wide ? 50 : narrow ? 10 : 25; - const chartLimit = 7 * weeks; - - const getDate = (ago: number) => { - const y = now.getFullYear(); - const m = now.getMonth(); - const d = now.getDate(); - - return new Date(y, m, d - ago); - }; - - const format = (arr) => { - return arr.map((v, i) => { - const dt = getDate(i); - const iso = `${dt.getFullYear()}-${(dt.getMonth() + 1).toString().padStart(2, '0')}-${dt.getDate().toString().padStart(2, '0')}`; - return { - x: iso, - y: dt.getDay(), - d: iso, - v, - }; - }); - }; - - let values; - - if (props.src === 'notes') { - const raw = await os.api('charts/user/notes', { userId: props.user.id, limit: chartLimit, span: 'day' }); - values = raw.inc; - } - - fetching.value = false; - - await nextTick(); - - const color = defaultStore.state.darkMode ? '#b4e900' : '#86b300'; - - // 視覚上ã®åˆ†ã‹ã‚Šã‚„ã™ã•ã®ãŸã‚上ã‹ã‚‰æœ€ã‚‚大ãã„3ã¤ã®å€¤ã®å¹³å‡ã‚’最大値ã¨ã™ã‚‹ - const max = values.slice().sort((a, b) => b - a).slice(0, 3).reduce((a, b) => a + b, 0) / 3; - - const min = Math.max(0, Math.min(...values) - 1); - - const marginEachCell = 4; - - chartInstance = new Chart(chartEl.value, { - type: 'matrix', - data: { - datasets: [{ - label: '', - data: format(values), - pointRadius: 0, - borderWidth: 0, - borderJoinStyle: 'round', - borderRadius: 3, - backgroundColor(c) { - const value = c.dataset.data[c.dataIndex].v; - let a = (value - min) / max; - if (value !== 0) { // 0ã§ãªã„é™ã‚Šã¯å®Œå…¨ã«ä¸å¯è¦–ã«ã¯ã—ãªã„ - a = Math.max(a, 0.05); - } - return alpha(color, a); - }, - fill: true, - width(c) { - const a = c.chart.chartArea ?? {}; - return (a.right - a.left) / weeks - marginEachCell; - }, - height(c) { - const a = c.chart.chartArea ?? {}; - return (a.bottom - a.top) / 7 - marginEachCell; - }, - /* @see <https://github.com/misskey-dev/misskey/pull/10365#discussion_r1155511107> - }] satisfies ChartData[], - */ - }], - }, - options: { - aspectRatio: wide ? 6 : narrow ? 1.8 : 3.2, - layout: { - padding: { - left: 8, - right: 0, - top: 0, - bottom: 0, - }, - }, - scales: { - x: { - type: 'time', - offset: true, - position: 'bottom', - time: { - unit: 'week', - round: 'week', - isoWeekday: 0, - displayFormats: { - day: 'M/d', - month: 'Y/M', - week: 'M/d', - }, - }, - grid: { - display: false, - }, - ticks: { - display: true, - maxRotation: 0, - autoSkipPadding: 8, - }, - }, - y: { - offset: true, - reverse: true, - position: 'right', - grid: { - display: false, - }, - ticks: { - maxRotation: 0, - autoSkip: true, - padding: 1, - font: { - size: 9, - }, - callback: (value, index, values) => ['', 'Mon', '', 'Wed', '', 'Fri', ''][value], - }, - }, - }, - plugins: { - legend: { - display: false, - }, - tooltip: { - enabled: false, - callbacks: { - title(context) { - const v = context[0].dataset.data[context[0].dataIndex]; - return v.d; - }, - label(context) { - const v = context.dataset.data[context.dataIndex]; - return [v.v]; - }, - }, - //mode: 'index', - animation: { - duration: 0, - }, - external: externalTooltipHandler, - }, - }, - }, - }); -} - -watch(() => props.src, () => { - fetching.value = true; - renderChart(); -}); - -onMounted(async () => { - renderChart(); -}); -</script> - -<style lang="scss" module> -.root { - padding: 20px; -} -</style> diff --git a/packages/frontend/src/pages/user/activity.notes.vue b/packages/frontend/src/pages/user/activity.notes.vue index dd035641d8ba892e57b704681f7411fbbbd42702..64514716d6855a3cc77066c0d2a38d08b4eca5aa 100644 --- a/packages/frontend/src/pages/user/activity.notes.vue +++ b/packages/frontend/src/pages/user/activity.notes.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -18,7 +18,7 @@ import { onMounted, shallowRef, ref } from 'vue'; import { Chart, ChartDataset } from 'chart.js'; import * as Misskey from 'misskey-js'; import gradient from 'chartjs-plugin-gradient'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import { chartVLine } from '@/scripts/chart-vline.js'; @@ -61,7 +61,7 @@ async function renderChart() { })); }; - const raw = await os.api('charts/user/notes', { userId: props.user.id, limit: chartLimit, span: 'day' }); + const raw = await misskeyApi('charts/user/notes', { userId: props.user.id, limit: chartLimit, span: 'day' }); const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; diff --git a/packages/frontend/src/pages/user/activity.pv.vue b/packages/frontend/src/pages/user/activity.pv.vue index 2dd9a1570f268c12dc791746c116c3c5f2b231c7..ce24807f93b91e31d745e905a03faa0c5fc442bf 100644 --- a/packages/frontend/src/pages/user/activity.pv.vue +++ b/packages/frontend/src/pages/user/activity.pv.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -18,7 +18,7 @@ import { onMounted, shallowRef, ref } from 'vue'; import { Chart, ChartDataset } from 'chart.js'; import * as Misskey from 'misskey-js'; import gradient from 'chartjs-plugin-gradient'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import { chartVLine } from '@/scripts/chart-vline.js'; @@ -61,7 +61,7 @@ async function renderChart() { })); }; - const raw = await os.api('charts/user/pv', { userId: props.user.id, limit: chartLimit, span: 'day' }); + const raw = await misskeyApi('charts/user/pv', { userId: props.user.id, limit: chartLimit, span: 'day' }); const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; diff --git a/packages/frontend/src/pages/user/activity.vue b/packages/frontend/src/pages/user/activity.vue index 42035cc619a51defc4e77f6ff350ab0236e899cf..271631e8d12c8acf163d42651b4ed0eea9534caa 100644 --- a/packages/frontend/src/pages/user/activity.vue +++ b/packages/frontend/src/pages/user/activity.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -8,10 +8,10 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps"> <MkFoldableSection class="item"> <template #header><i class="ph-pulse ph-bold ph-lg"></i> Heatmap</template> - <XHeatmap :user="user" :src="'notes'"/> + <MkHeatmap :user="user" :src="'notes'"/> </MkFoldableSection> <MkFoldableSection class="item"> - <template #header><i class="ph-pencil ph-bold ph-lg"></i> Notes</template> + <template #header><i class="ph-pencil-simple ph-bold ph-lg"></i> Notes</template> <XNotes :user="user"/> </MkFoldableSection> <MkFoldableSection class="item"> @@ -28,11 +28,11 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import * as Misskey from 'misskey-js'; -import XHeatmap from './activity.heatmap.vue'; import XPv from './activity.pv.vue'; import XNotes from './activity.notes.vue'; import XFollowing from './activity.following.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; +import MkHeatmap from '@/components/MkHeatmap.vue'; const props = defineProps<{ user: Misskey.entities.User; diff --git a/packages/frontend/src/pages/user/clips.vue b/packages/frontend/src/pages/user/clips.vue index eaae4725165fb33dceae5f28221676e62d0b9d7f..ac01cff8cdb7cf3d4f3cf01518a08a2944f49991 100644 --- a/packages/frontend/src/pages/user/clips.vue +++ b/packages/frontend/src/pages/user/clips.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/user/flashs.vue b/packages/frontend/src/pages/user/flashs.vue index 5e93a0b04cc99b859f4295b8945a70abf3fa25f8..b3313476e117a07a4ae513304c2fc2649e118c91 100644 --- a/packages/frontend/src/pages/user/flashs.vue +++ b/packages/frontend/src/pages/user/flashs.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/user/follow-list.vue b/packages/frontend/src/pages/user/follow-list.vue index 19b729035352ec57753ba3ce3d4772bea7728f64..e60dccec17664d4cd6f22f68d8eff2c65df3f7f8 100644 --- a/packages/frontend/src/pages/user/follow-list.vue +++ b/packages/frontend/src/pages/user/follow-list.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/user/followers.vue b/packages/frontend/src/pages/user/followers.vue index 36f1b4543eff51d54badb3e744c66f0f4f5d1807..e8addf88b701487ea0174bb4b3af7152fc7f9588 100644 --- a/packages/frontend/src/pages/user/followers.vue +++ b/packages/frontend/src/pages/user/followers.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed, watch, ref } from 'vue'; import * as Misskey from 'misskey-js'; import XFollowList from './follow-list.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; @@ -37,7 +37,7 @@ const error = ref<any>(null); function fetchUser(): void { if (props.acct == null) return; user.value = null; - os.api('users/show', Misskey.acct.parse(props.acct)).then(u => { + misskeyApi('users/show', Misskey.acct.parse(props.acct)).then(u => { user.value = u; }).catch(err => { error.value = err; @@ -52,11 +52,14 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => user.value ? { +definePageMetadata(() => ({ + title: i18n.ts.user, icon: 'ph-user ph-bold ph-lg', - title: user.value.name ? `${user.value.name} (@${user.value.username})` : `@${user.value.username}`, - subtitle: i18n.ts.followers, - userName: user.value, - avatar: user.value, -} : null)); + ...user.value ? { + title: user.value.name ? `${user.value.name} (@${user.value.username})` : `@${user.value.username}`, + subtitle: i18n.ts.followers, + userName: user.value, + avatar: user.value, + } : {}, +})); </script> diff --git a/packages/frontend/src/pages/user/following.vue b/packages/frontend/src/pages/user/following.vue index 43876b77c0afd31812794f7d58aca391c335db2c..8e4da403839db64b6d0cbb27b794e3deab810d0f 100644 --- a/packages/frontend/src/pages/user/following.vue +++ b/packages/frontend/src/pages/user/following.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed, watch, ref } from 'vue'; import * as Misskey from 'misskey-js'; import XFollowList from './follow-list.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; @@ -37,7 +37,7 @@ const error = ref<any>(null); function fetchUser(): void { if (props.acct == null) return; user.value = null; - os.api('users/show', Misskey.acct.parse(props.acct)).then(u => { + misskeyApi('users/show', Misskey.acct.parse(props.acct)).then(u => { user.value = u; }).catch(err => { error.value = err; @@ -52,11 +52,14 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => user.value ? { +definePageMetadata(() => ({ + title: i18n.ts.user, icon: 'ph-user ph-bold ph-lg', - title: user.value.name ? `${user.value.name} (@${user.value.username})` : `@${user.value.username}`, - subtitle: i18n.ts.following, - userName: user.value, - avatar: user.value, -} : null)); + ...user.value ? { + title: user.value.name ? `${user.value.name} (@${user.value.username})` : `@${user.value.username}`, + subtitle: i18n.ts.following, + userName: user.value, + avatar: user.value, + } : {}, +})); </script> diff --git a/packages/frontend/src/pages/user/gallery.vue b/packages/frontend/src/pages/user/gallery.vue index 0d806100d99e140e3f9d79f90b4dab22bbf07385..9ba81322ba3b2c68519fdf157de073198becad67 100644 --- a/packages/frontend/src/pages/user/gallery.vue +++ b/packages/frontend/src/pages/user/gallery.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/user/home.stories.impl.ts b/packages/frontend/src/pages/user/home.stories.impl.ts index a2ef5d50d1ef8dd2e7c3c1150f612176e22c8db0..c623ef9ee48f5e39e40f9e6ba7c6d88e0b94f58f 100644 --- a/packages/frontend/src/pages/user/home.stories.impl.ts +++ b/packages/frontend/src/pages/user/home.stories.impl.ts @@ -1,11 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { StoryObj } from '@storybook/vue3'; -import { rest } from 'msw'; +import { HttpResponse, http } from 'msw'; import { userDetailed } from '../../../.storybook/fakes.js'; import { commonHandlers } from '../../../.storybook/mocks.js'; import home_ from './home.vue'; @@ -39,12 +39,13 @@ export const Default = { msw: { handlers: [ ...commonHandlers, - rest.post('/api/users/notes', (req, res, ctx) => { - return res(ctx.json([])); + http.post('/api/users/notes', () => { + return HttpResponse.json([]); }), - rest.get('/api/charts/user/notes', (req, res, ctx) => { - const length = Math.max(Math.min(parseInt(req.url.searchParams.get('limit') ?? '30', 10), 1), 300); - return res(ctx.json({ + http.get('/api/charts/user/notes', ({ request }) => { + const url = new URL(request.url); + const length = Math.max(Math.min(parseInt(url.searchParams.get('limit') ?? '30', 10), 1), 300); + return HttpResponse.json({ total: Array.from({ length }, () => 0), inc: Array.from({ length }, () => 0), dec: Array.from({ length }, () => 0), @@ -54,11 +55,12 @@ export const Default = { renote: Array.from({ length }, () => 0), withFile: Array.from({ length }, () => 0), }, - })); + }); }), - rest.get('/api/charts/user/pv', (req, res, ctx) => { - const length = Math.max(Math.min(parseInt(req.url.searchParams.get('limit') ?? '30', 10), 1), 300); - return res(ctx.json({ + http.get('/api/charts/user/pv', ({ request }) => { + const url = new URL(request.url); + const length = Math.max(Math.min(parseInt(url.searchParams.get('limit') ?? '30', 10), 1), 300); + return HttpResponse.json({ upv: { user: Array.from({ length }, () => 0), visitor: Array.from({ length }, () => 0), @@ -67,7 +69,7 @@ export const Default = { user: Array.from({ length }, () => 0), visitor: Array.from({ length }, () => 0), }, - })); + }); }), ], }, diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 44a8ca250b6a193a36d2dfedb6a0e3fc7824b16c..96ae4824f0c9d965a1ddfafdb18f03dd175e09b7 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -1,10 +1,10 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="narrow ? 800 : 1100" :style="background"> +<MkSpacer :contentMax="narrow ? 800 : 1100" :style="background" style="transform: none !important;"> <div ref="rootEl" class="ftskorzw" :class="{ wide: !narrow }" style="container-type: inline-size;"> <div class="main _gaps"> <MkInfo v-if="user.isSuspended" :warn="true">{{ i18n.ts.userSuspended }}</MkInfo> @@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="ph-lock ph-bold ph-lg"></i></span> <span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ph-robot ph-bold ph-lg"></i></span> <button v-if="$i && !isEditingMemo && !memoDraft" class="_button add-note-button" @click="showMemoTextarea"> - <i class="ph-pencil-line ph-bold ph-lg"/> {{ i18n.ts.addMemo }} + <i class="ph-pencil-simple-line ph-bold ph-lg"/> {{ i18n.ts.addMemo }} </button> </div> </div> @@ -84,7 +84,7 @@ SPDX-License-Identifier: AGPL-3.0-only </dl> <dl v-if="user.birthday" class="field"> <dt class="name"><i class="ph-cake ph-bold ph-lg ti-fw"></i> {{ i18n.ts.birthday }}</dt> - <dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ i18n.t('yearsOld', { age }) }})</dd> + <dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ i18n.tsx.yearsOld({ age }) }})</dd> </dl> <dl class="field"> <dt class="name"><i class="ph-calendar ph-bold ph-lg ti-fw"></i> {{ i18n.ts.registeredDate }}</dt> @@ -185,14 +185,14 @@ import { getUserMenu } from '@/scripts/get-user-menu.js'; import number from '@/filters/number.js'; import { userPage } from '@/filters/user.js'; import * as os from '@/os.js'; -import { useRouter } from '@/router.js'; import { i18n } from '@/i18n.js'; import { $i, iAmModerator } from '@/account.js'; import { dateString } from '@/filters/date.js'; import { confetti } from '@/scripts/confetti.js'; -import { api } from '@/os.js'; import { defaultStore } from '@/store.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js'; +import { useRouter } from '@/router/supplier.js'; function calcAge(birthdate: string): number { const date = new Date(birthdate); @@ -262,7 +262,7 @@ const background = computed(() => { }); watch(moderationNote, async () => { - await os.api('admin/update-user-note', { userId: props.user.id, text: moderationNote.value }); + await misskeyApi('admin/update-user-note', { userId: props.user.id, text: moderationNote.value }); }); const pagination = { @@ -279,7 +279,7 @@ const AllPagination = { params: computed(() => ({ userId: props.user.id, withRenotes: noteview.value === 'all', - withReplies: noteview.value === 'all' || noteview.value === 'files', + withReplies: noteview.value === 'all', withChannelNotes: noteview.value === 'all', withFiles: noteview.value === 'files', })), @@ -333,7 +333,7 @@ function adjustMemoTextarea() { } async function updateMemo() { - await api('users/update-memo', { + await misskeyApi('users/update-memo', { memo: memoDraft.value, userId: props.user.id, }); diff --git a/packages/frontend/src/pages/user/index.activity.vue b/packages/frontend/src/pages/user/index.activity.vue index f555486a6d9ff966d8cf18ad0dc147d17feb70eb..857cf996ff2414c31e1a0c119fe8b4465bc7a096 100644 --- a/packages/frontend/src/pages/user/index.activity.vue +++ b/packages/frontend/src/pages/user/index.activity.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/user/index.files.vue b/packages/frontend/src/pages/user/index.files.vue index 30817db77ce30676c1f0ef82b35f4bbfcf672957..be58cec24aaf3056090285669c7da3c43735961a 100644 --- a/packages/frontend/src/pages/user/index.files.vue +++ b/packages/frontend/src/pages/user/index.files.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -37,7 +37,7 @@ import { onMounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; import { getStaticImageUrl } from '@/scripts/media-proxy.js'; import { notePage } from '@/filters/note.js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import MkContainer from '@/components/MkContainer.vue'; import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue'; import { defaultStore } from '@/store.js'; @@ -61,7 +61,7 @@ function thumbnail(image: Misskey.entities.DriveFile): string { } onMounted(() => { - os.api('users/notes', { + misskeyApi('users/notes', { userId: props.user.id, withFiles: true, limit: 15, diff --git a/packages/frontend/src/pages/user/index.timeline.vue b/packages/frontend/src/pages/user/index.timeline.vue index e5a0f49e3d91f212af150818ab421dbbfa1c975e..8dbf90f344ca40cf3d624e47137983cbc377dbb3 100644 --- a/packages/frontend/src/pages/user/index.timeline.vue +++ b/packages/frontend/src/pages/user/index.timeline.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue index 44b4f84ca348ad522d1282a3cead14048e0aadb2..7f20e941d3c1fffb28d4c4cc9aecd56f7e95905a 100644 --- a/packages/frontend/src/pages/user/index.vue +++ b/packages/frontend/src/pages/user/index.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -8,19 +8,21 @@ SPDX-License-Identifier: AGPL-3.0-only <template #header><MkPageHeader v-model:tab="tab" :displayBackButton="true" :actions="headerActions" :tabs="headerTabs"/></template> <div> <div v-if="user"> - <XHome v-if="tab === 'home'" :user="user"/> - <MkSpacer v-else-if="tab === 'notes'" :contentMax="800" style="padding-top: 0"> - <XTimeline :user="user"/> - </MkSpacer> - <XActivity v-else-if="tab === 'activity'" :user="user"/> - <XAchievements v-else-if="tab === 'achievements'" :user="user"/> - <XReactions v-else-if="tab === 'reactions'" :user="user"/> - <XClips v-else-if="tab === 'clips'" :user="user"/> - <XLists v-else-if="tab === 'lists'" :user="user"/> - <XPages v-else-if="tab === 'pages'" :user="user"/> - <XFlashs v-else-if="tab === 'flashs'" :user="user"/> - <XGallery v-else-if="tab === 'gallery'" :user="user"/> - <XRaw v-else-if="tab === 'raw'" :user="user"/> + <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> + <XHome v-if="tab === 'home'" key="home" :user="user"/> + <MkSpacer v-else-if="tab === 'notes'" key="notes" :contentMax="800" style="padding-top: 0"> + <XTimeline :user="user"/> + </MkSpacer> + <XActivity v-else-if="tab === 'activity'" key="activity" :user="user"/> + <XAchievements v-else-if="tab === 'achievements'" key="achievements" :user="user"/> + <XReactions v-else-if="tab === 'reactions'" key="reactions" :user="user"/> + <XClips v-else-if="tab === 'clips'" key="clips" :user="user"/> + <XLists v-else-if="tab === 'lists'" key="lists" :user="user"/> + <XPages v-else-if="tab === 'pages'" key="pages" :user="user"/> + <XFlashs v-else-if="tab === 'flashs'" key="flashs" :user="user"/> + <XGallery v-else-if="tab === 'gallery'" key="gallery" :user="user"/> + <XRaw v-else-if="tab === 'raw'" key="raw" :user="user"/> + </MkHorizontalSwipe> </div> <MkError v-else-if="error" @retry="fetchUser()"/> <MkLoading v-else/> @@ -32,10 +34,11 @@ SPDX-License-Identifier: AGPL-3.0-only import { defineAsyncComponent, computed, watch, ref } from 'vue'; import * as Misskey from 'misskey-js'; import { acct as getAcct } from '@/filters/user.js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; +import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; const XHome = defineAsyncComponent(() => import('./home.vue')); const XTimeline = defineAsyncComponent(() => import('./index.timeline.vue')); @@ -57,13 +60,14 @@ const props = withDefaults(defineProps<{ }); const tab = ref(props.page); + const user = ref<null | Misskey.entities.UserDetailed>(null); const error = ref<any>(null); function fetchUser(): void { if (props.acct == null) return; user.value = null; - os.api('users/show', Misskey.acct.parse(props.acct)).then(u => { + misskeyApi('users/show', Misskey.acct.parse(props.acct)).then(u => { user.value = u; }).catch(err => { error.value = err; @@ -83,7 +87,7 @@ const headerTabs = computed(() => user.value ? [{ }, { key: 'notes', title: i18n.ts.notes, - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', }, { key: 'activity', title: i18n.ts.activity, @@ -92,7 +96,7 @@ const headerTabs = computed(() => user.value ? [{ key: 'achievements', title: i18n.ts.achievements, icon: 'ph-trophy ph-bold ph-lg', -}] : []), ...($i && ($i.id === user.value.id)) || user.value.publicReactions ? [{ +}] : []), ...($i && ($i.id === user.value.id || $i.isAdmin || $i.isModerator)) || user.value.publicReactions ? [{ key: 'reactions', title: i18n.ts.reaction, icon: 'ph-smiley ph-bold ph-lg', @@ -122,15 +126,18 @@ const headerTabs = computed(() => user.value ? [{ icon: 'ph-code ph-bold ph-lg', }] : []); -definePageMetadata(computed(() => user.value ? { +definePageMetadata(() => ({ + title: i18n.ts.user, icon: 'ph-user ph-bold ph-lg', - title: user.value.name ? `${user.value.name} (@${user.value.username})` : `@${user.value.username}`, - subtitle: `@${getAcct(user.value)}`, - userName: user.value, - avatar: user.value, - path: `/@${user.value.username}`, - share: { - title: user.value.name, - }, -} : null)); + ...user.value ? { + title: user.value.name ? `${user.value.name} (@${user.value.username})` : `@${user.value.username}`, + subtitle: `@${getAcct(user.value)}`, + userName: user.value, + avatar: user.value, + path: `/@${user.value.username}`, + share: { + title: user.value.name, + }, + } : {}, +})); </script> diff --git a/packages/frontend/src/pages/user/lists.vue b/packages/frontend/src/pages/user/lists.vue index c58a8abdfb24736f946b53edd21dbd18c796066f..8f95ce2dc9fec4d1b325417fb6daaf3dd64bc36b 100644 --- a/packages/frontend/src/pages/user/lists.vue +++ b/packages/frontend/src/pages/user/lists.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/user/pages.vue b/packages/frontend/src/pages/user/pages.vue index 94ec80d05ef06eb9f0619cf9a2bcd1deb10d7e36..6375bf7d74a13422e62a13e49d3648a0711c6b51 100644 --- a/packages/frontend/src/pages/user/pages.vue +++ b/packages/frontend/src/pages/user/pages.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/user/raw.vue b/packages/frontend/src/pages/user/raw.vue index ebe40d58608aa295d1e51127aa716afd4e93cba1..ac18ad9392dc905a2d077894218f48cd20ee0778 100644 --- a/packages/frontend/src/pages/user/raw.vue +++ b/packages/frontend/src/pages/user/raw.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/user/reactions.vue b/packages/frontend/src/pages/user/reactions.vue index 916b6615d583b9ec62c42d6da18f3bdf184a34da..3671decc1891ceb30dfb6d252bf5bad8e92de6a0 100644 --- a/packages/frontend/src/pages/user/reactions.vue +++ b/packages/frontend/src/pages/user/reactions.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue index 50f86a0ae29552fbc6f3c4e9a6a1d4f6ab4c82f9..255e07c3fa90c15a4846196b4ba1b8aa1bd0d0bd 100644 --- a/packages/frontend/src/pages/welcome.entrance.a.vue +++ b/packages/frontend/src/pages/welcome.entrance.a.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -39,7 +39,7 @@ import XTimeline from './welcome.timeline.vue'; import MarqueeText from '@/components/MkMarquee.vue'; import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue'; import misskeysvg from '/client-assets/sharkey.svg'; -import * as os from '@/os.js'; +import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js'; import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue'; import { getProxiedImageUrl } from '@/scripts/media-proxy.js'; @@ -53,11 +53,11 @@ function getInstanceIcon(instance: Misskey.entities.FederationInstance): string return getProxiedImageUrl(instance.iconUrl, 'preview'); } -os.api('meta', { detail: true }).then(_meta => { +misskeyApi('meta', { detail: true }).then(_meta => { meta.value = _meta; }); -os.apiGet('federation/instances', { +misskeyApiGet('federation/instances', { sort: '+pubSub', limit: 20, }).then(_instances => { diff --git a/packages/frontend/src/pages/welcome.setup.vue b/packages/frontend/src/pages/welcome.setup.vue index c2f9d4e5858ff130812d57659bc6754af3d534da..7d5861d2aea19edff6dc8613128161f08e0bcb60 100644 --- a/packages/frontend/src/pages/welcome.setup.vue +++ b/packages/frontend/src/pages/welcome.setup.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -40,6 +40,7 @@ import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import { host, version } from '@/config.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { login } from '@/account.js'; import { i18n } from '@/i18n.js'; import MkAnimBg from '@/components/MkAnimBg.vue'; @@ -52,7 +53,7 @@ function submit() { if (submitting.value) return; submitting.value = true; - os.api('admin/accounts/create', { + misskeyApi('admin/accounts/create', { username: username.value, password: password.value, }).then(res => { diff --git a/packages/frontend/src/pages/welcome.timeline.vue b/packages/frontend/src/pages/welcome.timeline.vue index 2cbe0ed9b1f42d7f8a2af8c79eaa7fca21511789..59f91e8b4cc3a9ce4b08f39a0d423cebd633e7c1 100644 --- a/packages/frontend/src/pages/welcome.timeline.vue +++ b/packages/frontend/src/pages/welcome.timeline.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkMediaList :mediaList="note.files"/> </div> <div v-if="note.poll"> - <MkPoll :note="note" :readOnly="true"/> + <MkPoll :noteId="note.id" :poll="note.poll" :readOnly="true"/> </div> </div> <MkReactionsViewer ref="reactionsViewer" :note="note"/> @@ -32,14 +32,14 @@ import { onUpdated, ref, shallowRef } from 'vue'; import MkReactionsViewer from '@/components/MkReactionsViewer.vue'; import MkMediaList from '@/components/MkMediaList.vue'; import MkPoll from '@/components/MkPoll.vue'; -import * as os from '@/os.js'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; import { getScrollContainer } from '@/scripts/scroll.js'; const notes = ref<Misskey.entities.Note[]>([]); const isScrolling = ref(false); const scrollEl = shallowRef<HTMLElement>(); -os.apiGet('notes/featured').then(_notes => { +misskeyApiGet('notes/featured').then(_notes => { notes.value = _notes; }); diff --git a/packages/frontend/src/pages/welcome.vue b/packages/frontend/src/pages/welcome.vue index 7f0af1b83eb25767ae6d2499a551343088dd6255..9ba6a5885e30b065c3aa4c19067dc0fc4a2b8227 100644 --- a/packages/frontend/src/pages/welcome.vue +++ b/packages/frontend/src/pages/welcome.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -16,12 +16,12 @@ import * as Misskey from 'misskey-js'; import XSetup from './welcome.setup.vue'; import XEntrance from './welcome.entrance.a.vue'; import { instanceName } from '@/config.js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; const meta = ref<Misskey.entities.MetaResponse | null>(null); -os.api('meta', { detail: true }).then(res => { +misskeyApi('meta', { detail: true }).then(res => { meta.value = res; }); @@ -29,8 +29,8 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); -definePageMetadata(computed(() => ({ +definePageMetadata(() => ({ title: instanceName, icon: null, -}))); +})); </script> diff --git a/packages/frontend/src/pizzax.ts b/packages/frontend/src/pizzax.ts index b2254a0611447ae64602e2075477b767e9c3cbda..ac325e923f36da2ce702808e590d14bd92ee492a 100644 --- a/packages/frontend/src/pizzax.ts +++ b/packages/frontend/src/pizzax.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -8,11 +8,12 @@ import { onUnmounted, Ref, ref, watch } from 'vue'; import { BroadcastChannel } from 'broadcast-channel'; import { $i } from '@/account.js'; -import { api } from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { get, set } from '@/scripts/idb-proxy.js'; import { defaultStore } from '@/store.js'; import { useStream } from '@/stream.js'; import { deepClone } from '@/scripts/clone.js'; +import { deepMerge } from '@/scripts/merge.js'; type StateDef = Record<string, { where: 'account' | 'device' | 'deviceAccount'; @@ -80,6 +81,21 @@ export class Storage<T extends StateDef> { this.loaded = this.ready.then(() => this.load()); } + private isPureObject(value: unknown): value is Record<string | number | symbol, unknown> { + return typeof value === 'object' && value !== null && !Array.isArray(value); + } + + private mergeState<X>(value: X, def: X): X { + if (this.isPureObject(value) && this.isPureObject(def)) { + const merged = deepMerge(value, def); + + if (_DEV_) console.log('Merging state. Incoming: ', value, ' Default: ', def, ' Result: ', merged); + + return merged as X; + } + return value; + } + private async init(): Promise<void> { await this.migrate(); @@ -89,11 +105,11 @@ export class Storage<T extends StateDef> { for (const [k, v] of Object.entries(this.def) as [keyof T, T[keyof T]['default']][]) { if (v.where === 'device' && Object.prototype.hasOwnProperty.call(deviceState, k)) { - this.reactiveState[k].value = this.state[k] = deviceState[k]; + this.reactiveState[k].value = this.state[k] = this.mergeState<T[keyof T]['default']>(deviceState[k], v.default); } else if (v.where === 'account' && $i && Object.prototype.hasOwnProperty.call(registryCache, k)) { - this.reactiveState[k].value = this.state[k] = registryCache[k]; + this.reactiveState[k].value = this.state[k] = this.mergeState<T[keyof T]['default']>(registryCache[k], v.default); } else if (v.where === 'deviceAccount' && Object.prototype.hasOwnProperty.call(deviceAccountState, k)) { - this.reactiveState[k].value = this.state[k] = deviceAccountState[k]; + this.reactiveState[k].value = this.state[k] = this.mergeState<T[keyof T]['default']>(deviceAccountState[k], v.default); } else { this.reactiveState[k].value = this.state[k] = v.default; if (_DEV_) console.log('Use default value', k, v.default); @@ -134,7 +150,7 @@ export class Storage<T extends StateDef> { window.setTimeout(async () => { await defaultStore.ready; - api('i/registry/get-all', { scope: ['client', this.key] }) + misskeyApi('i/registry/get-all', { scope: ['client', this.key] }) .then(kvs => { const cache: Partial<T> = {}; for (const [k, v] of Object.entries(this.def) as [keyof T, T[keyof T]['default']][]) { @@ -168,7 +184,7 @@ export class Storage<T extends StateDef> { this.reactiveState[key].value = this.state[key] = rawValue; return this.addIdbSetJob(async () => { - if (_DEV_) console.log(`set ${key} start`); + if (_DEV_) console.log(`set ${String(key)} start`); switch (this.def[key].where) { case 'device': { this.pizzaxChannel.postMessage({ @@ -199,7 +215,7 @@ export class Storage<T extends StateDef> { const cache = await get(this.registryCacheKeyName) || {}; cache[key] = rawValue; await set(this.registryCacheKeyName, cache); - await api('i/registry/set', { + await misskeyApi('i/registry/set', { scope: ['client', this.key], key: key.toString(), value: rawValue, @@ -207,7 +223,7 @@ export class Storage<T extends StateDef> { break; } } - if (_DEV_) console.log(`set ${key} complete`); + if (_DEV_) console.log(`set ${String(key)} complete`); }); } @@ -223,9 +239,12 @@ export class Storage<T extends StateDef> { /** * 特定ã®ã‚ーã®ã€ç°¡æ˜“çš„ãªgetter/setterを作りã¾ã™ - * 主ã«vueå ´ã§è¨å®šã‚³ãƒ³ãƒˆãƒãƒ¼ãƒ«ã®modelã¨ã—ã¦ä½¿ã†ç”¨ + * 主ã«vue上ã§è¨å®šã‚³ãƒ³ãƒˆãƒãƒ¼ãƒ«ã®modelã¨ã—ã¦ä½¿ã†ç”¨ */ - public makeGetterSetter<K extends keyof T>(key: K, getter?: (v: T[K]) => unknown, setter?: (v: unknown) => T[K]) { + public makeGetterSetter<K extends keyof T>(key: K, getter?: (v: T[K]) => unknown, setter?: (v: unknown) => T[K]): { + get: () => T[K]['default']; + set: (value: T[K]['default']) => void; + } { const valueRef = ref(this.state[key]); const stop = watch(this.reactiveState[key], val => { diff --git a/packages/frontend/src/plugin.ts b/packages/frontend/src/plugin.ts index 5e49af4858eb74680b86cc490a4b8c8750e0354a..743cadc36a82e03588bd6a2d9af2827ab565d2d5 100644 --- a/packages/frontend/src/plugin.ts +++ b/packages/frontend/src/plugin.ts @@ -1,10 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { Interpreter, Parser, utils, values } from '@syuilo/aiscript'; -import { createAiScriptEnv } from '@/scripts/aiscript/api.js'; +import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js'; import { inputText } from '@/os.js'; import { Plugin, noteActions, notePostInterruptors, noteViewInterruptors, postFormActions, userActions, pageViewInterruptors } from '@/store.js'; @@ -19,19 +19,7 @@ export async function install(plugin: Plugin): Promise<void> { plugin: plugin, storageKey: 'plugins:' + plugin.id, }), { - in: (q): Promise<string> => { - return new Promise(ok => { - inputText({ - title: q, - }).then(({ canceled, result: a }) => { - if (canceled) { - ok(''); - } else { - ok(a); - } - }); - }); - }, + in: aiScriptReadline, out: (value): void => { console.log(value); }, diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts deleted file mode 100644 index b861afa9a37fbba13513fe18e31028bc61e2dd2e..0000000000000000000000000000000000000000 --- a/packages/frontend/src/router.ts +++ /dev/null @@ -1,558 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and other misskey contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { AsyncComponentLoader, defineAsyncComponent, inject } from 'vue'; -import { Router } from '@/nirax.js'; -import { $i, iAmModerator } from '@/account.js'; -import MkLoading from '@/pages/_loading_.vue'; -import MkError from '@/pages/_error_.vue'; - -export const page = (loader: AsyncComponentLoader<any>) => defineAsyncComponent({ - loader: loader, - loadingComponent: MkLoading, - errorComponent: MkError, -}); - -export const routes = [{ - path: '/@:initUser/pages/:initPageName/view-source', - component: page(() => import('./pages/page-editor/page-editor.vue')), -}, { - path: '/@:username/pages/:pageName', - component: page(() => import('./pages/page.vue')), -}, { - path: '/@:acct/following', - component: page(() => import('./pages/user/following.vue')), -}, { - path: '/@:acct/followers', - component: page(() => import('./pages/user/followers.vue')), -}, { - name: 'user', - path: '/@:acct/:page?', - component: page(() => import('./pages/user/index.vue')), -}, { - name: 'note', - path: '/notes/:noteId', - component: page(() => import('./pages/note.vue')), -}, { - name: 'list', - path: '/list/:listId', - component: page(() => import('./pages/list.vue')), -}, { - path: '/clips/:clipId', - component: page(() => import('./pages/clip.vue')), -}, { - path: '/instance-info/:host', - component: page(() => import('./pages/instance-info.vue')), -}, { - name: 'settings', - path: '/settings', - component: page(() => import('./pages/settings/index.vue')), - loginRequired: true, - children: [{ - path: '/profile', - name: 'profile', - component: page(() => import('./pages/settings/profile.vue')), - }, { - path: '/avatar-decoration', - name: 'avatarDecoration', - component: page(() => import('./pages/settings/avatar-decoration.vue')), - }, { - path: '/roles', - name: 'roles', - component: page(() => import('./pages/settings/roles.vue')), - }, { - path: '/privacy', - name: 'privacy', - component: page(() => import('./pages/settings/privacy.vue')), - }, { - path: '/emoji-picker', - name: 'emojiPicker', - component: page(() => import('./pages/settings/emoji-picker.vue')), - }, { - path: '/drive', - name: 'drive', - component: page(() => import('./pages/settings/drive.vue')), - }, { - path: '/drive/cleaner', - name: 'drive', - component: page(() => import('./pages/settings/drive-cleaner.vue')), - }, { - path: '/notifications', - name: 'notifications', - component: page(() => import('./pages/settings/notifications.vue')), - }, { - path: '/email', - name: 'email', - component: page(() => import('./pages/settings/email.vue')), - }, { - path: '/security', - name: 'security', - component: page(() => import('./pages/settings/security.vue')), - }, { - path: '/general', - name: 'general', - component: page(() => import('./pages/settings/general.vue')), - }, { - path: '/theme/install', - name: 'theme', - component: page(() => import('./pages/settings/theme.install.vue')), - }, { - path: '/theme/manage', - name: 'theme', - component: page(() => import('./pages/settings/theme.manage.vue')), - }, { - path: '/theme', - name: 'theme', - component: page(() => import('./pages/settings/theme.vue')), - }, { - path: '/navbar', - name: 'navbar', - component: page(() => import('./pages/settings/navbar.vue')), - }, { - path: '/statusbar', - name: 'statusbar', - component: page(() => import('./pages/settings/statusbar.vue')), - }, { - path: '/sounds', - name: 'sounds', - component: page(() => import('./pages/settings/sounds.vue')), - }, { - path: '/plugin/install', - name: 'plugin', - component: page(() => import('./pages/settings/plugin.install.vue')), - }, { - path: '/plugin', - name: 'plugin', - component: page(() => import('./pages/settings/plugin.vue')), - }, { - path: '/import-export', - name: 'import-export', - component: page(() => import('./pages/settings/import-export.vue')), - }, { - path: '/mute-block', - name: 'mute-block', - component: page(() => import('./pages/settings/mute-block.vue')), - }, { - path: '/api', - name: 'api', - component: page(() => import('./pages/settings/api.vue')), - }, { - path: '/apps', - name: 'api', - component: page(() => import('./pages/settings/apps.vue')), - }, { - path: '/webhook/edit/:webhookId', - name: 'webhook', - component: page(() => import('./pages/settings/webhook.edit.vue')), - }, { - path: '/webhook/new', - name: 'webhook', - component: page(() => import('./pages/settings/webhook.new.vue')), - }, { - path: '/webhook', - name: 'webhook', - component: page(() => import('./pages/settings/webhook.vue')), - }, { - path: '/deck', - name: 'deck', - component: page(() => import('./pages/settings/deck.vue')), - }, { - path: '/preferences-backups', - name: 'preferences-backups', - component: page(() => import('./pages/settings/preferences-backups.vue')), - }, { - path: '/migration', - name: 'migration', - component: page(() => import('./pages/settings/migration.vue')), - }, { - path: '/custom-css', - name: 'general', - component: page(() => import('./pages/settings/custom-css.vue')), - }, { - path: '/accounts', - name: 'profile', - component: page(() => import('./pages/settings/accounts.vue')), - }, { - path: '/other', - name: 'other', - component: page(() => import('./pages/settings/other.vue')), - }, { - path: '/', - component: page(() => import('./pages/_empty_.vue')), - }], -}, { - path: '/reset-password/:token?', - component: page(() => import('./pages/reset-password.vue')), -}, { - path: '/signup-complete/:code', - component: page(() => import('./pages/signup-complete.vue')), -}, { - path: '/announcements', - component: page(() => import('./pages/announcements.vue')), -}, { - path: '/about', - component: page(() => import('./pages/about.vue')), - hash: 'initialTab', -}, { - path: '/about-sharkey', - component: page(() => import('./pages/about-sharkey.vue')), -}, { - path: '/invite', - name: 'invite', - component: page(() => import('./pages/invite.vue')), -}, { - path: '/ads', - component: page(() => import('./pages/ads.vue')), -}, { - path: '/theme-editor', - component: page(() => import('./pages/theme-editor.vue')), - loginRequired: true, -}, { - path: '/roles/:role', - component: page(() => import('./pages/role.vue')), -}, { - path: '/user-tags/:tag', - component: page(() => import('./pages/user-tag.vue')), -}, { - path: '/explore', - component: page(() => import('./pages/explore.vue')), - hash: 'initialTab', -}, { - path: '/search', - component: page(() => import('./pages/search.vue')), - query: { - q: 'query', - channel: 'channel', - type: 'type', - origin: 'origin', - }, -}, { - path: '/authorize-follow', - component: page(() => import('./pages/follow.vue')), - loginRequired: true, -}, { - path: '/share', - component: page(() => import('./pages/share.vue')), - loginRequired: true, -}, { - path: '/api-console', - component: page(() => import('./pages/api-console.vue')), - loginRequired: true, -}, { - path: '/scratchpad', - component: page(() => import('./pages/scratchpad.vue')), -}, { - path: '/auth/:token', - component: page(() => import('./pages/auth.vue')), -}, { - path: '/miauth/:session', - component: page(() => import('./pages/miauth.vue')), - query: { - callback: 'callback', - name: 'name', - icon: 'icon', - permission: 'permission', - }, -}, { - path: '/tags/:tag', - component: page(() => import('./pages/tag.vue')), -}, { - path: '/pages/new', - component: page(() => import('./pages/page-editor/page-editor.vue')), - loginRequired: true, -}, { - path: '/pages/edit/:initPageId', - component: page(() => import('./pages/page-editor/page-editor.vue')), - loginRequired: true, -}, { - path: '/pages', - component: page(() => import('./pages/pages.vue')), -}, { - path: '/play/:id/edit', - component: page(() => import('./pages/flash/flash-edit.vue')), - loginRequired: true, -}, { - path: '/play/new', - component: page(() => import('./pages/flash/flash-edit.vue')), - loginRequired: true, -}, { - path: '/play/:id', - component: page(() => import('./pages/flash/flash.vue')), -}, { - path: '/play', - component: page(() => import('./pages/flash/flash-index.vue')), -}, { - path: '/gallery/:postId/edit', - component: page(() => import('./pages/gallery/edit.vue')), - loginRequired: true, -}, { - path: '/gallery/new', - component: page(() => import('./pages/gallery/edit.vue')), - loginRequired: true, -}, { - path: '/gallery/:postId', - component: page(() => import('./pages/gallery/post.vue')), -}, { - path: '/gallery', - component: page(() => import('./pages/gallery/index.vue')), -}, { - path: '/channels/:channelId/edit', - component: page(() => import('./pages/channel-editor.vue')), - loginRequired: true, -}, { - path: '/channels/new', - component: page(() => import('./pages/channel-editor.vue')), - loginRequired: true, -}, { - path: '/channels/:channelId', - component: page(() => import('./pages/channel.vue')), -}, { - path: '/channels', - component: page(() => import('./pages/channels.vue')), -}, { - path: '/avatar-decorations', - name: 'avatarDecorations', - component: page(() => import('./pages/avatar-decorations.vue')), -}, { - path: '/custom-emojis-manager', - component: page(() => import('./pages/custom-emojis-manager.vue')), -}, { - path: '/registry/keys/:domain/:path(*)?', - component: page(() => import('./pages/registry.keys.vue')), -}, { - path: '/registry/value/:domain/:path(*)?', - component: page(() => import('./pages/registry.value.vue')), -}, { - path: '/registry', - component: page(() => import('./pages/registry.vue')), -}, { - path: '/install-extentions', - component: page(() => import('./pages/install-extentions.vue')), - loginRequired: true, -}, { - path: '/admin/user/:userId', - component: iAmModerator ? page(() => import('./pages/admin-user.vue')) : page(() => import('./pages/not-found.vue')), -}, { - path: '/admin/file/:fileId', - component: iAmModerator ? page(() => import('./pages/admin-file.vue')) : page(() => import('./pages/not-found.vue')), -}, { - path: '/admin', - component: iAmModerator ? page(() => import('./pages/admin/index.vue')) : page(() => import('./pages/not-found.vue')), - children: [{ - path: '/overview', - name: 'overview', - component: page(() => import('./pages/admin/overview.vue')), - }, { - path: '/users', - name: 'users', - component: page(() => import('./pages/admin/users.vue')), - }, { - path: '/emojis', - name: 'emojis', - component: page(() => import('./pages/custom-emojis-manager.vue')), - }, { - path: '/avatar-decorations', - name: 'avatarDecorations', - component: page(() => import('./pages/avatar-decorations.vue')), - }, { - path: '/queue', - name: 'queue', - component: page(() => import('./pages/admin/queue.vue')), - }, { - path: '/files', - name: 'files', - component: page(() => import('./pages/admin/files.vue')), - }, { - path: '/federation', - name: 'federation', - component: page(() => import('./pages/admin/federation.vue')), - }, { - path: '/announcements', - name: 'announcements', - component: page(() => import('./pages/admin/announcements.vue')), - }, { - path: '/ads', - name: 'ads', - component: page(() => import('./pages/admin/ads.vue')), - }, { - path: '/roles/:id/edit', - name: 'roles', - component: page(() => import('./pages/admin/roles.edit.vue')), - }, { - path: '/roles/new', - name: 'roles', - component: page(() => import('./pages/admin/roles.edit.vue')), - }, { - path: '/roles/:id', - name: 'roles', - component: page(() => import('./pages/admin/roles.role.vue')), - }, { - path: '/roles', - name: 'roles', - component: page(() => import('./pages/admin/roles.vue')), - }, { - path: '/database', - name: 'database', - component: page(() => import('./pages/admin/database.vue')), - }, { - path: '/abuses', - name: 'abuses', - component: page(() => import('./pages/admin/abuses.vue')), - }, { - path: '/modlog', - name: 'modlog', - component: page(() => import('./pages/admin/modlog.vue')), - }, { - path: '/settings', - name: 'settings', - component: page(() => import('./pages/admin/settings.vue')), - }, { - path: '/branding', - name: 'branding', - component: page(() => import('./pages/admin/branding.vue')), - }, { - path: '/moderation', - name: 'moderation', - component: page(() => import('./pages/admin/moderation.vue')), - }, { - path: '/email-settings', - name: 'email-settings', - component: page(() => import('./pages/admin/email-settings.vue')), - }, { - path: '/object-storage', - name: 'object-storage', - component: page(() => import('./pages/admin/object-storage.vue')), - }, { - path: '/security', - name: 'security', - component: page(() => import('./pages/admin/security.vue')), - }, { - path: '/relays', - name: 'relays', - component: page(() => import('./pages/admin/relays.vue')), - }, { - path: '/instance-block', - name: 'instance-block', - component: page(() => import('./pages/admin/instance-block.vue')), - }, { - path: '/proxy-account', - name: 'proxy-account', - component: page(() => import('./pages/admin/proxy-account.vue')), - }, { - path: '/external-services', - name: 'external-services', - component: page(() => import('./pages/admin/external-services.vue')), - }, { - path: '/other-settings', - name: 'other-settings', - component: page(() => import('./pages/admin/other-settings.vue')), - }, { - path: '/server-rules', - name: 'server-rules', - component: page(() => import('./pages/admin/server-rules.vue')), - }, { - path: '/invites', - name: 'invites', - component: page(() => import('./pages/admin/invites.vue')), - }, { - path: '/approvals', - name: 'approvals', - component: page(() => import('./pages/admin/approvals.vue')), - }, { - path: '/', - component: page(() => import('./pages/_empty_.vue')), - }], -}, { - path: '/my/notifications', - component: page(() => import('./pages/notifications.vue')), - loginRequired: true, -}, { - path: '/my/favorites', - component: page(() => import('./pages/favorites.vue')), - loginRequired: true, -}, { - path: '/my/achievements', - component: page(() => import('./pages/achievements.vue')), - loginRequired: true, -}, { - path: '/my/drive/folder/:folder', - component: page(() => import('./pages/drive.vue')), - loginRequired: true, -}, { - path: '/my/drive', - component: page(() => import('./pages/drive.vue')), - loginRequired: true, -}, { - path: '/my/drive/file/:fileId', - component: page(() => import('./pages/drive.file.vue')), - loginRequired: true, -}, { - path: '/my/follow-requests', - component: page(() => import('./pages/follow-requests.vue')), - loginRequired: true, -}, { - path: '/my/lists/:listId', - component: page(() => import('./pages/my-lists/list.vue')), - loginRequired: true, -}, { - path: '/my/lists', - component: page(() => import('./pages/my-lists/index.vue')), - loginRequired: true, -}, { - path: '/my/clips', - component: page(() => import('./pages/my-clips/index.vue')), - loginRequired: true, -}, { - path: '/my/antennas/create', - component: page(() => import('./pages/my-antennas/create.vue')), - loginRequired: true, -}, { - path: '/my/antennas/:antennaId', - component: page(() => import('./pages/my-antennas/edit.vue')), - loginRequired: true, -}, { - path: '/my/antennas', - component: page(() => import('./pages/my-antennas/index.vue')), - loginRequired: true, -}, { - path: '/timeline/list/:listId', - component: page(() => import('./pages/user-list-timeline.vue')), - loginRequired: true, -}, { - path: '/timeline/antenna/:antennaId', - component: page(() => import('./pages/antenna-timeline.vue')), - loginRequired: true, -}, { - path: '/clicker', - component: page(() => import('./pages/clicker.vue')), - loginRequired: true, -}, { - path: '/timeline', - component: page(() => import('./pages/timeline.vue')), -}, { - name: 'index', - path: '/', - component: $i ? page(() => import('./pages/timeline.vue')) : page(() => import('./pages/welcome.vue')), - globalCacheKey: 'index', -}, { - path: '/:(*)', - component: page(() => import('./pages/not-found.vue')), -}]; - -export const mainRouter = new Router(routes, location.pathname + location.search + location.hash, !!$i, page(() => import('@/pages/not-found.vue'))); - -window.history.replaceState({ key: mainRouter.getCurrentKey() }, '', location.href); - -mainRouter.addListener('push', ctx => { - window.history.pushState({ key: ctx.key }, '', ctx.path); -}); - -window.addEventListener('popstate', (event) => { - mainRouter.replace(location.pathname + location.search + location.hash, event.state?.key); -}); - -export function useRouter(): Router { - return inject<Router | null>('router', null) ?? mainRouter; -} diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts new file mode 100644 index 0000000000000000000000000000000000000000..c5fc28a345be22d44c9e214695b38b9df06583ea --- /dev/null +++ b/packages/frontend/src/router/definition.ts @@ -0,0 +1,606 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { App, AsyncComponentLoader, defineAsyncComponent, provide } from 'vue'; +import type { RouteDef } from '@/nirax.js'; +import { IRouter, Router } from '@/nirax.js'; +import { $i, iAmModerator } from '@/account.js'; +import MkLoading from '@/pages/_loading_.vue'; +import MkError from '@/pages/_error_.vue'; +import { setMainRouter } from '@/router/main.js'; + +const page = (loader: AsyncComponentLoader<any>) => defineAsyncComponent({ + loader: loader, + loadingComponent: MkLoading, + errorComponent: MkError, +}); + +const routes: RouteDef[] = [{ + path: '/@:initUser/pages/:initPageName/view-source', + component: page(() => import('@/pages/page-editor/page-editor.vue')), +}, { + path: '/@:username/pages/:pageName', + component: page(() => import('@/pages/page.vue')), +}, { + path: '/@:acct/following', + component: page(() => import('@/pages/user/following.vue')), +}, { + path: '/@:acct/followers', + component: page(() => import('@/pages/user/followers.vue')), +}, { + name: 'user', + path: '/@:acct/:page?', + component: page(() => import('@/pages/user/index.vue')), +}, { + name: 'note', + path: '/notes/:noteId', + component: page(() => import('@/pages/note.vue')), +}, { + name: 'list', + path: '/list/:listId', + component: page(() => import('@/pages/list.vue')), +}, { + path: '/clips/:clipId', + component: page(() => import('@/pages/clip.vue')), +}, { + path: '/instance-info/:host', + component: page(() => import('@/pages/instance-info.vue')), +}, { + name: 'settings', + path: '/settings', + component: page(() => import('@/pages/settings/index.vue')), + loginRequired: true, + children: [{ + path: '/profile', + name: 'profile', + component: page(() => import('@/pages/settings/profile.vue')), + }, { + path: '/avatar-decoration', + name: 'avatarDecoration', + component: page(() => import('@/pages/settings/avatar-decoration.vue')), + }, { + path: '/roles', + name: 'roles', + component: page(() => import('@/pages/settings/roles.vue')), + }, { + path: '/privacy', + name: 'privacy', + component: page(() => import('@/pages/settings/privacy.vue')), + }, { + path: '/emoji-picker', + name: 'emojiPicker', + component: page(() => import('@/pages/settings/emoji-picker.vue')), + }, { + path: '/drive', + name: 'drive', + component: page(() => import('@/pages/settings/drive.vue')), + }, { + path: '/drive/cleaner', + name: 'drive', + component: page(() => import('@/pages/settings/drive-cleaner.vue')), + }, { + path: '/notifications', + name: 'notifications', + component: page(() => import('@/pages/settings/notifications.vue')), + }, { + path: '/email', + name: 'email', + component: page(() => import('@/pages/settings/email.vue')), + }, { + path: '/security', + name: 'security', + component: page(() => import('@/pages/settings/security.vue')), + }, { + path: '/general', + name: 'general', + component: page(() => import('@/pages/settings/general.vue')), + }, { + path: '/theme/install', + name: 'theme', + component: page(() => import('@/pages/settings/theme.install.vue')), + }, { + path: '/theme/manage', + name: 'theme', + component: page(() => import('@/pages/settings/theme.manage.vue')), + }, { + path: '/theme', + name: 'theme', + component: page(() => import('@/pages/settings/theme.vue')), + }, { + path: '/navbar', + name: 'navbar', + component: page(() => import('@/pages/settings/navbar.vue')), + }, { + path: '/statusbar', + name: 'statusbar', + component: page(() => import('@/pages/settings/statusbar.vue')), + }, { + path: '/sounds', + name: 'sounds', + component: page(() => import('@/pages/settings/sounds.vue')), + }, { + path: '/plugin/install', + name: 'plugin', + component: page(() => import('@/pages/settings/plugin.install.vue')), + }, { + path: '/plugin', + name: 'plugin', + component: page(() => import('@/pages/settings/plugin.vue')), + }, { + path: '/import-export', + name: 'import-export', + component: page(() => import('@/pages/settings/import-export.vue')), + }, { + path: '/mute-block', + name: 'mute-block', + component: page(() => import('@/pages/settings/mute-block.vue')), + }, { + path: '/api', + name: 'api', + component: page(() => import('@/pages/settings/api.vue')), + }, { + path: '/apps', + name: 'api', + component: page(() => import('@/pages/settings/apps.vue')), + }, { + path: '/webhook/edit/:webhookId', + name: 'webhook', + component: page(() => import('@/pages/settings/webhook.edit.vue')), + }, { + path: '/webhook/new', + name: 'webhook', + component: page(() => import('@/pages/settings/webhook.new.vue')), + }, { + path: '/webhook', + name: 'webhook', + component: page(() => import('@/pages/settings/webhook.vue')), + }, { + path: '/deck', + name: 'deck', + component: page(() => import('@/pages/settings/deck.vue')), + }, { + path: '/preferences-backups', + name: 'preferences-backups', + component: page(() => import('@/pages/settings/preferences-backups.vue')), + }, { + path: '/migration', + name: 'migration', + component: page(() => import('@/pages/settings/migration.vue')), + }, { + path: '/custom-css', + name: 'general', + component: page(() => import('@/pages/settings/custom-css.vue')), + }, { + path: '/accounts', + name: 'profile', + component: page(() => import('@/pages/settings/accounts.vue')), + }, { + path: '/other', + name: 'other', + component: page(() => import('@/pages/settings/other.vue')), + }, { + path: '/', + component: page(() => import('@/pages/_empty_.vue')), + }], +}, { + path: '/reset-password/:token?', + component: page(() => import('@/pages/reset-password.vue')), +}, { + path: '/signup-complete/:code', + component: page(() => import('@/pages/signup-complete.vue')), +}, { + path: '/announcements', + component: page(() => import('@/pages/announcements.vue')), +}, { + path: '/about', + component: page(() => import('@/pages/about.vue')), + hash: 'initialTab', +}, { + path: '/about-sharkey', + component: page(() => import('@/pages/about-sharkey.vue')), +}, { + path: '/invite', + name: 'invite', + component: page(() => import('@/pages/invite.vue')), +}, { + path: '/ads', + component: page(() => import('@/pages/ads.vue')), +}, { + path: '/theme-editor', + component: page(() => import('@/pages/theme-editor.vue')), + loginRequired: true, +}, { + path: '/roles/:role', + component: page(() => import('@/pages/role.vue')), +}, { + path: '/user-tags/:tag', + component: page(() => import('@/pages/user-tag.vue')), +}, { + path: '/explore', + component: page(() => import('@/pages/explore.vue')), + hash: 'initialTab', +}, { + path: '/search', + component: page(() => import('@/pages/search.vue')), + query: { + q: 'query', + channel: 'channel', + type: 'type', + origin: 'origin', + }, +}, { + path: '/authorize-follow', + component: page(() => import('@/pages/follow.vue')), + loginRequired: true, +}, { + path: '/share', + component: page(() => import('@/pages/share.vue')), + loginRequired: true, +}, { + path: '/api-console', + component: page(() => import('@/pages/api-console.vue')), + loginRequired: true, +}, { + path: '/scratchpad', + component: page(() => import('@/pages/scratchpad.vue')), +}, { + path: '/auth/:token', + component: page(() => import('@/pages/auth.vue')), +}, { + path: '/miauth/:session', + component: page(() => import('@/pages/miauth.vue')), + query: { + callback: 'callback', + name: 'name', + icon: 'icon', + permission: 'permission', + }, +}, { + path: '/oauth/authorize', + component: page(() => import('@/pages/oauth.vue')), +}, { + path: '/tags/:tag', + component: page(() => import('@/pages/tag.vue')), +}, { + path: '/pages/new', + component: page(() => import('@/pages/page-editor/page-editor.vue')), + loginRequired: true, +}, { + path: '/pages/edit/:initPageId', + component: page(() => import('@/pages/page-editor/page-editor.vue')), + loginRequired: true, +}, { + path: '/pages', + component: page(() => import('@/pages/pages.vue')), +}, { + path: '/play/:id/edit', + component: page(() => import('@/pages/flash/flash-edit.vue')), + loginRequired: true, +}, { + path: '/play/new', + component: page(() => import('@/pages/flash/flash-edit.vue')), + loginRequired: true, +}, { + path: '/play/:id', + component: page(() => import('@/pages/flash/flash.vue')), +}, { + path: '/play', + component: page(() => import('@/pages/flash/flash-index.vue')), +}, { + path: '/gallery/:postId/edit', + component: page(() => import('@/pages/gallery/edit.vue')), + loginRequired: true, +}, { + path: '/gallery/new', + component: page(() => import('@/pages/gallery/edit.vue')), + loginRequired: true, +}, { + path: '/gallery/:postId', + component: page(() => import('@/pages/gallery/post.vue')), +}, { + path: '/gallery', + component: page(() => import('@/pages/gallery/index.vue')), +}, { + path: '/channels/:channelId/edit', + component: page(() => import('@/pages/channel-editor.vue')), + loginRequired: true, +}, { + path: '/channels/new', + component: page(() => import('@/pages/channel-editor.vue')), + loginRequired: true, +}, { + path: '/channels/:channelId', + component: page(() => import('@/pages/channel.vue')), +}, { + path: '/channels', + component: page(() => import('@/pages/channels.vue')), +}, { + path: '/custom-emojis-manager', + component: page(() => import('@/pages/custom-emojis-manager.vue')), +}, { + path: '/avatar-decorations', + name: 'avatarDecorations', + component: page(() => import('@/pages/avatar-decorations.vue')), +}, { + path: '/registry/keys/:domain/:path(*)?', + component: page(() => import('@/pages/registry.keys.vue')), +}, { + path: '/registry/value/:domain/:path(*)?', + component: page(() => import('@/pages/registry.value.vue')), +}, { + path: '/registry', + component: page(() => import('@/pages/registry.vue')), +}, { + path: '/install-extentions', + redirect: '/install-extensions', + loginRequired: true, +}, { + path: '/install-extensions', + component: page(() => import('@/pages/install-extensions.vue')), + loginRequired: true, +}, { + path: '/admin/user/:userId', + component: iAmModerator ? page(() => import('@/pages/admin-user.vue')) : page(() => import('@/pages/not-found.vue')), +}, { + path: '/admin/file/:fileId', + component: iAmModerator ? page(() => import('@/pages/admin-file.vue')) : page(() => import('@/pages/not-found.vue')), +}, { + path: '/admin', + component: iAmModerator ? page(() => import('@/pages/admin/index.vue')) : page(() => import('@/pages/not-found.vue')), + children: [{ + path: '/overview', + name: 'overview', + component: page(() => import('@/pages/admin/overview.vue')), + }, { + path: '/users', + name: 'users', + component: page(() => import('@/pages/admin/users.vue')), + }, { + path: '/emojis', + name: 'emojis', + component: page(() => import('@/pages/custom-emojis-manager.vue')), + }, { + path: '/avatar-decorations', + name: 'avatarDecorations', + component: page(() => import('@/pages/avatar-decorations.vue')), + }, { + path: '/queue', + name: 'queue', + component: page(() => import('@/pages/admin/queue.vue')), + }, { + path: '/files', + name: 'files', + component: page(() => import('@/pages/admin/files.vue')), + }, { + path: '/federation', + name: 'federation', + component: page(() => import('@/pages/admin/federation.vue')), + }, { + path: '/announcements', + name: 'announcements', + component: page(() => import('@/pages/admin/announcements.vue')), + }, { + path: '/ads', + name: 'ads', + component: page(() => import('@/pages/admin/ads.vue')), + }, { + path: '/roles/:id/edit', + name: 'roles', + component: page(() => import('@/pages/admin/roles.edit.vue')), + }, { + path: '/roles/new', + name: 'roles', + component: page(() => import('@/pages/admin/roles.edit.vue')), + }, { + path: '/roles/:id', + name: 'roles', + component: page(() => import('@/pages/admin/roles.role.vue')), + }, { + path: '/roles', + name: 'roles', + component: page(() => import('@/pages/admin/roles.vue')), + }, { + path: '/database', + name: 'database', + component: page(() => import('@/pages/admin/database.vue')), + }, { + path: '/abuses', + name: 'abuses', + component: page(() => import('@/pages/admin/abuses.vue')), + }, { + path: '/modlog', + name: 'modlog', + component: page(() => import('@/pages/admin/modlog.vue')), + }, { + path: '/settings', + name: 'settings', + component: page(() => import('@/pages/admin/settings.vue')), + }, { + path: '/branding', + name: 'branding', + component: page(() => import('@/pages/admin/branding.vue')), + }, { + path: '/moderation', + name: 'moderation', + component: page(() => import('@/pages/admin/moderation.vue')), + }, { + path: '/email-settings', + name: 'email-settings', + component: page(() => import('@/pages/admin/email-settings.vue')), + }, { + path: '/object-storage', + name: 'object-storage', + component: page(() => import('@/pages/admin/object-storage.vue')), + }, { + path: '/security', + name: 'security', + component: page(() => import('@/pages/admin/security.vue')), + }, { + path: '/relays', + name: 'relays', + component: page(() => import('@/pages/admin/relays.vue')), + }, { + path: '/instance-block', + name: 'instance-block', + component: page(() => import('@/pages/admin/instance-block.vue')), + }, { + path: '/proxy-account', + name: 'proxy-account', + component: page(() => import('@/pages/admin/proxy-account.vue')), + }, { + path: '/external-services', + name: 'external-services', + component: page(() => import('@/pages/admin/external-services.vue')), + }, { + path: '/other-settings', + name: 'other-settings', + component: page(() => import('@/pages/admin/other-settings.vue')), + }, { + path: '/server-rules', + name: 'server-rules', + component: page(() => import('@/pages/admin/server-rules.vue')), + }, { + path: '/invites', + name: 'invites', + component: page(() => import('@/pages/admin/invites.vue')), + }, { + path: '/approvals', + name: 'approvals', + component: page(() => import('@/pages/admin/approvals.vue')), + }, { + path: '/', + component: page(() => import('@/pages/_empty_.vue')), + }], +}, { + path: '/my/notifications', + component: page(() => import('@/pages/notifications.vue')), + loginRequired: true, +}, { + path: '/my/favorites', + component: page(() => import('@/pages/favorites.vue')), + loginRequired: true, +}, { + path: '/my/achievements', + component: page(() => import('@/pages/achievements.vue')), + loginRequired: true, +}, { + path: '/my/drive/folder/:folder', + component: page(() => import('@/pages/drive.vue')), + loginRequired: true, +}, { + path: '/my/drive', + component: page(() => import('@/pages/drive.vue')), + loginRequired: true, +}, { + path: '/my/drive/file/:fileId', + component: page(() => import('@/pages/drive.file.vue')), + loginRequired: true, +}, { + path: '/my/follow-requests', + component: page(() => import('@/pages/follow-requests.vue')), + loginRequired: true, +}, { + path: '/my/lists/:listId', + component: page(() => import('@/pages/my-lists/list.vue')), + loginRequired: true, +}, { + path: '/my/lists', + component: page(() => import('@/pages/my-lists/index.vue')), + loginRequired: true, +}, { + path: '/my/clips', + component: page(() => import('@/pages/my-clips/index.vue')), + loginRequired: true, +}, { + path: '/my/antennas/create', + component: page(() => import('@/pages/my-antennas/create.vue')), + loginRequired: true, +}, { + path: '/my/antennas/:antennaId', + component: page(() => import('@/pages/my-antennas/edit.vue')), + loginRequired: true, +}, { + path: '/my/antennas', + component: page(() => import('@/pages/my-antennas/index.vue')), + loginRequired: true, +}, { + path: '/timeline/list/:listId', + component: page(() => import('@/pages/user-list-timeline.vue')), + loginRequired: true, +}, { + path: '/timeline/antenna/:antennaId', + component: page(() => import('@/pages/antenna-timeline.vue')), + loginRequired: true, +}, { + path: '/clicker', + component: page(() => import('@/pages/clicker.vue')), + loginRequired: true, +}, { + path: '/games', + component: page(() => import('@/pages/games.vue')), + loginRequired: false, +}, { + path: '/bubble-game', + component: page(() => import('@/pages/drop-and-fusion.vue')), + loginRequired: true, +}, { + path: '/reversi', + component: page(() => import('@/pages/reversi/index.vue')), + loginRequired: false, +}, { + path: '/reversi/g/:gameId', + component: page(() => import('@/pages/reversi/game.vue')), + loginRequired: false, +}, { + path: '/timeline', + component: page(() => import('@/pages/timeline.vue')), +}, { + name: 'index', + path: '/', + component: $i ? page(() => import('@/pages/timeline.vue')) : page(() => import('@/pages/welcome.vue')), + globalCacheKey: 'index', +}, { + // テスト用リダイレクトè¨å®šã€‚ãƒã‚°ã‚¤ãƒ³ä¸ãƒ¦ãƒ¼ã‚¶ã®ãƒ—ãƒãƒ•ã‚£ãƒ¼ãƒ«ã«ãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆã™ã‚‹ + path: '/redirect-test', + redirect: $i ? `@${$i.username}` : '/', + loginRequired: true, +}, { + path: '/:(*)', + component: page(() => import('@/pages/not-found.vue')), +}]; + +function createRouterImpl(path: string): IRouter { + return new Router(routes, path, !!$i, page(() => import('@/pages/not-found.vue'))); +} + +/** + * {@link Router}ã«ã‚ˆã‚‹ç”»é¢é·ç§»ã‚’å¯èƒ½ã¨ã™ã‚‹ãŸã‚ã«{@link mainRouter}をセットアップã™ã‚‹ã€‚ + * ã¾ãŸã€{@link Router}ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã‚’作æˆã™ã‚‹ãŸã‚ã®ãƒ•ã‚¡ã‚¯ãƒˆãƒªã‚‚{@link provide}経由ã§å…¬é–‹ã™ã‚‹ï¼ˆ`routerFactory`ã¨ã„ã†ã‚ーã§å–å¾—å¯èƒ½ï¼‰ + */ +export function setupRouter(app: App) { + app.provide('routerFactory', createRouterImpl); + + const mainRouter = createRouterImpl(location.pathname + location.search + location.hash); + + window.addEventListener('popstate', (event) => { + mainRouter.replace(location.pathname + location.search + location.hash, event.state?.key); + }); + + mainRouter.addListener('push', ctx => { + window.history.pushState({ key: ctx.key }, '', ctx.path); + }); + + mainRouter.addListener('same', () => { + window.scroll({ top: 0, behavior: 'smooth' }); + }); + + mainRouter.addListener('replace', ctx => { + window.history.replaceState({ key: ctx.key }, '', ctx.path); + }); + + mainRouter.init(); + + setMainRouter(mainRouter); +} diff --git a/packages/frontend/src/router/main.ts b/packages/frontend/src/router/main.ts new file mode 100644 index 0000000000000000000000000000000000000000..7a3fde131ed840139b459b4ad1148a7bb8e367d8 --- /dev/null +++ b/packages/frontend/src/router/main.ts @@ -0,0 +1,167 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ShallowRef } from 'vue'; +import { EventEmitter } from 'eventemitter3'; +import { IRouter, Resolved, RouteDef, RouterEvent } from '@/nirax.js'; + +function getMainRouter(): IRouter { + const router = mainRouterHolder; + if (!router) { + throw new Error('mainRouter is not found.'); + } + + return router; +} + +/** + * メインルータをè¨å®šã™ã‚‹ã€‚一度è¨å®šã™ã‚‹ã¨ã€ãれ以é™ã¯å¤‰æ›´ã§ããªã„。 + * {@link setupRouter}ã‹ã‚‰å‘¼ã³å‡ºã•ã‚Œã‚‹ã“ã¨ã®ã¿ã‚’想定ã—ã¦ã„る。 + */ +export function setMainRouter(router: IRouter) { + if (mainRouterHolder) { + throw new Error('mainRouter is already exists.'); + } + + mainRouterHolder = router; +} + +/** + * {@link mainRouter}用ã®ãƒ—ãƒã‚シ実装。 + * {@link mainRouter}ã¯èµ·å‹•ã‚·ãƒ¼ã‚±ãƒ³ã‚¹ã®ä¸€éƒ¨ã«ã¦åˆæœŸåŒ–ã•ã‚Œã‚‹ãŸã‚ã€åƒ…ã‹ã«undefinedã«ãªã‚‹æœŸé–“ãŒã‚る。 + * ãã®åƒ…ã‹ãªæœŸé–“ã®ãŸã‚ã ã‘ã«åž‹ã‚’undefinedè¾¼ã¿ã«ã—ãŸããªã„ã®ã§ã“ã®ã‚¯ãƒ©ã‚¹ã‚’ç·©è¡æã¨ã—ã¦ä½¿ç”¨ã™ã‚‹ã€‚ + */ +class MainRouterProxy implements IRouter { + private supplier: () => IRouter; + + constructor(supplier: () => IRouter) { + this.supplier = supplier; + } + + get current(): Resolved { + return this.supplier().current; + } + + get currentRef(): ShallowRef<Resolved> { + return this.supplier().currentRef; + } + + get currentRoute(): ShallowRef<RouteDef> { + return this.supplier().currentRoute; + } + + get navHook(): ((path: string, flag?: any) => boolean) | null { + return this.supplier().navHook; + } + + set navHook(value) { + this.supplier().navHook = value; + } + + getCurrentKey(): string { + return this.supplier().getCurrentKey(); + } + + getCurrentPath(): any { + return this.supplier().getCurrentPath(); + } + + push(path: string, flag?: any): void { + this.supplier().push(path, flag); + } + + replace(path: string, key?: string | null): void { + this.supplier().replace(path, key); + } + + resolve(path: string): Resolved | null { + return this.supplier().resolve(path); + } + + init(): void { + this.supplier().init(); + } + + eventNames(): Array<EventEmitter.EventNames<RouterEvent>> { + return this.supplier().eventNames(); + } + + listeners<T extends EventEmitter.EventNames<RouterEvent>>( + event: T, + ): Array<EventEmitter.EventListener<RouterEvent, T>> { + return this.supplier().listeners(event); + } + + listenerCount( + event: EventEmitter.EventNames<RouterEvent>, + ): number { + return this.supplier().listenerCount(event); + } + + emit<T extends EventEmitter.EventNames<RouterEvent>>( + event: T, + ...args: EventEmitter.EventArgs<RouterEvent, T> + ): boolean { + return this.supplier().emit(event, ...args); + } + + on<T extends EventEmitter.EventNames<RouterEvent>>( + event: T, + fn: EventEmitter.EventListener<RouterEvent, T>, + context?: any, + ): this { + this.supplier().on(event, fn, context); + return this; + } + + addListener<T extends EventEmitter.EventNames<RouterEvent>>( + event: T, + fn: EventEmitter.EventListener<RouterEvent, T>, + context?: any, + ): this { + this.supplier().addListener(event, fn, context); + return this; + } + + once<T extends EventEmitter.EventNames<RouterEvent>>( + event: T, + fn: EventEmitter.EventListener<RouterEvent, T>, + context?: any, + ): this { + this.supplier().once(event, fn, context); + return this; + } + + removeListener<T extends EventEmitter.EventNames<RouterEvent>>( + event: T, + fn?: EventEmitter.EventListener<RouterEvent, T>, + context?: any, + once?: boolean, + ): this { + this.supplier().removeListener(event, fn, context, once); + return this; + } + + off<T extends EventEmitter.EventNames<RouterEvent>>( + event: T, + fn?: EventEmitter.EventListener<RouterEvent, T>, + context?: any, + once?: boolean, + ): this { + this.supplier().off(event, fn, context, once); + return this; + } + + removeAllListeners( + event?: EventEmitter.EventNames<RouterEvent>, + ): this { + this.supplier().removeAllListeners(event); + return this; + } +} + +let mainRouterHolder: IRouter | null = null; + +export const mainRouter: IRouter = new MainRouterProxy(getMainRouter); diff --git a/packages/frontend/src/router/supplier.ts b/packages/frontend/src/router/supplier.ts new file mode 100644 index 0000000000000000000000000000000000000000..7da236f4e73d57878bc0b225be071303b68f3983 --- /dev/null +++ b/packages/frontend/src/router/supplier.ts @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { inject } from 'vue'; +import { IRouter, Router } from '@/nirax.js'; +import { mainRouter } from '@/router/main.js'; + +/** + * メインã®{@link Router}ã‚’å–å¾—ã™ã‚‹ã€‚ + * ã‚らã‹ã˜ã‚{@link setupRouter}を実行ã—ã¦ãŠãå¿…è¦ãŒã‚る({@link provide}ã«ã‚ˆã‚Š{@link IRouter}ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã‚’注入å¯èƒ½ã§ã‚ã‚‹ãªã‚‰ã°ã“ã®é™ã‚Šã§ã¯ãªã„) + */ +export function useRouter(): IRouter { + return inject<Router | null>('router', null) ?? mainRouter; +} + +/** + * ä»»æ„ã®{@link Router}ã‚’å–å¾—ã™ã‚‹ãŸã‚ã®ãƒ•ã‚¡ã‚¯ãƒˆãƒªã‚’å–å¾—ã™ã‚‹ã€‚ + * ã‚らã‹ã˜ã‚{@link setupRouter}を実行ã—ã¦ãŠãå¿…è¦ãŒã‚る。 + */ +export function useRouterFactory(): (path: string) => IRouter { + const factory = inject<(path: string) => IRouter>('routerFactory'); + if (!factory) { + console.error('routerFactory is not defined.'); + throw new Error('routerFactory is not defined.'); + } + + return factory; +} diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts index e7585fcf81e0a3fbff0f9e119c08031ca0a20364..f5d0ab559fd9c83151782d77fe1b5288ffade7b7 100644 --- a/packages/frontend/src/scripts/achievements.ts +++ b/packages/frontend/src/scripts/achievements.ts @@ -1,9 +1,9 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { $i } from '@/account.js'; export const ACHIEVEMENT_TYPES = [ @@ -83,6 +83,8 @@ export const ACHIEVEMENT_TYPES = [ 'brainDiver', 'smashTestNotificationButton', 'tutorialCompleted', + 'bubbleGameExplodingHead', + 'bubbleGameDoubleExplodingHead', ] as const; export const ACHIEVEMENT_BADGES = { @@ -466,6 +468,16 @@ export const ACHIEVEMENT_BADGES = { bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))', frame: 'bronze', }, + 'bubbleGameExplodingHead': { + img: '/fluent-emoji/1f92f.png', + bg: 'linear-gradient(0deg, rgb(255 77 77), rgb(247 155 214))', + frame: 'bronze', + }, + 'bubbleGameDoubleExplodingHead': { + img: '/fluent-emoji/1f92f.png', + bg: 'linear-gradient(0deg, rgb(255 77 77), rgb(247 155 214))', + frame: 'silver', + }, /* @see <https://github.com/misskey-dev/misskey/pull/10365#discussion_r1155511107> } as const satisfies Record<typeof ACHIEVEMENT_TYPES[number], { img: string; @@ -489,7 +501,7 @@ export async function claimAchievement(type: typeof ACHIEVEMENT_TYPES[number]) { window.setTimeout(() => { claimingQueue.delete(type); }, 500); - os.api('i/claim-achievement', { name: type }); + misskeyApi('i/claim-achievement', { name: type }); } if (_DEV_) { diff --git a/packages/frontend/src/scripts/aiscript/api.ts b/packages/frontend/src/scripts/aiscript/api.ts index 038ae231091228c6080b0896cb932490e6588f9b..98a0c61752cb66c446dd5b552f18795e218e4901 100644 --- a/packages/frontend/src/scripts/aiscript/api.ts +++ b/packages/frontend/src/scripts/aiscript/api.ts @@ -1,16 +1,27 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { utils, values } from '@syuilo/aiscript'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { $i } from '@/account.js'; import { miLocalStorage } from '@/local-storage.js'; import { customEmojis } from '@/custom-emojis.js'; import { url, lang } from '@/config.js'; import { nyaize } from '@/scripts/nyaize.js'; +export function aiScriptReadline(q: string): Promise<string> { + return new Promise(ok => { + os.inputText({ + title: q, + }).then(({ result: a }) => { + ok(a ?? ''); + }); + }); +} + export function createAiScriptEnv(opts) { return { USER_ID: $i ? values.STR($i.id) : values.NULL, @@ -44,7 +55,7 @@ export function createAiScriptEnv(opts) { if (typeof token.value !== 'string') throw new Error('invalid token'); } const actualToken: string|null = token?.value ?? opts.token ?? null; - return os.api(ep.value, utils.valToJs(param), actualToken).then(res => { + return misskeyApi(ep.value, utils.valToJs(param), actualToken).then(res => { return utils.jsToVal(res); }, err => { return values.ERROR('request_failed', utils.jsToVal(err)); diff --git a/packages/frontend/src/scripts/aiscript/ui.ts b/packages/frontend/src/scripts/aiscript/ui.ts index 08ba1e6d9bab33d8d65c7b14f8e1af4b8906d808..f2493264d37a90706ac70a29a31c173aa0d1bf38 100644 --- a/packages/frontend/src/scripts/aiscript/ui.ts +++ b/packages/frontend/src/scripts/aiscript/ui.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -218,7 +218,7 @@ function getTextOptions(def: values.Value | undefined): Omit<AsUiText, 'id' | 't }; } -function getMfmOptions(def: values.Value | undefined): Omit<AsUiMfm, 'id' | 'type'> { +function getMfmOptions(def: values.Value | undefined, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>): Omit<AsUiMfm, 'id' | 'type'> { utils.assertObject(def); const text = def.value.get('text'); @@ -241,7 +241,7 @@ function getMfmOptions(def: values.Value | undefined): Omit<AsUiMfm, 'id' | 'typ color: color?.value, font: font?.value, onClickEv: (evId: string) => { - if (onClickEv) call(onClickEv, values.STR(evId)); + if (onClickEv) call(onClickEv, [values.STR(evId)]); }, }; } diff --git a/packages/frontend/src/scripts/array.ts b/packages/frontend/src/scripts/array.ts index 082703a450e614afe5db366f48e9ce9912e46e7e..b3d76e149f4357e228b8811a4fbb1b4838619598 100644 --- a/packages/frontend/src/scripts/array.ts +++ b/packages/frontend/src/scripts/array.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/autocomplete.ts b/packages/frontend/src/scripts/autocomplete.ts index 2a9a42ace5a8004f5e258797d3e0b23235871496..9fc8f7843ed5affab66580c54972c205ee23bdbe 100644 --- a/packages/frontend/src/scripts/autocomplete.ts +++ b/packages/frontend/src/scripts/autocomplete.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -8,18 +8,18 @@ import getCaretCoordinates from 'textarea-caret'; import { toASCII } from 'punycode/'; import { popup } from '@/os.js'; -export type SuggestionType = 'user' | 'hashtag' | 'emoji' | 'mfmTag'; +export type SuggestionType = 'user' | 'hashtag' | 'emoji' | 'mfmTag' | 'mfmParam'; export class Autocomplete { private suggestion: { x: Ref<number>; y: Ref<number>; - q: Ref<string | null>; + q: Ref<any>; close: () => void; } | null; private textarea: HTMLInputElement | HTMLTextAreaElement; private currentType: string; - private textRef: Ref<string>; + private textRef: Ref<string | number | null>; private opening: boolean; private onlyType: SuggestionType[]; @@ -38,7 +38,7 @@ export class Autocomplete { /** * 対象ã®ãƒ†ã‚ストエリアを与ãˆã¦ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã‚’åˆæœŸåŒ–ã—ã¾ã™ã€‚ */ - constructor(textarea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>, onlyType?: SuggestionType[]) { + constructor(textarea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string | number | null>, onlyType?: SuggestionType[]) { //#region BIND this.onInput = this.onInput.bind(this); this.complete = this.complete.bind(this); @@ -49,7 +49,7 @@ export class Autocomplete { this.textarea = textarea; this.textRef = textRef; this.opening = false; - this.onlyType = onlyType ?? ['user', 'hashtag', 'emoji', 'mfmTag']; + this.onlyType = onlyType ?? ['user', 'hashtag', 'emoji', 'mfmTag', 'mfmParam']; this.attach(); } @@ -80,6 +80,7 @@ export class Autocomplete { const hashtagIndex = text.lastIndexOf('#'); const emojiIndex = text.lastIndexOf(':'); const mfmTagIndex = text.lastIndexOf('$'); + const mfmParamIndex = text.lastIndexOf('.'); const max = Math.max( mentionIndex, @@ -92,9 +93,12 @@ export class Autocomplete { return; } + const afterLastMfmParam = text.split(/\$\[[a-zA-Z]+/).pop(); + const isMention = mentionIndex !== -1; const isHashtag = hashtagIndex !== -1; - const isMfmTag = mfmTagIndex !== -1; + const isMfmParam = mfmParamIndex !== -1 && afterLastMfmParam?.includes('.') && !afterLastMfmParam?.includes(' '); + const isMfmTag = mfmTagIndex !== -1 && !isMfmParam; const isEmoji = emojiIndex !== -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':'); let opened = false; @@ -134,6 +138,17 @@ export class Autocomplete { } } + if (isMfmParam && !opened && this.onlyType.includes('mfmParam')) { + const mfmParam = text.substring(mfmParamIndex + 1); + if (!mfmParam.includes(' ')) { + this.open('mfmParam', { + tag: text.substring(mfmTagIndex + 2, mfmParamIndex), + params: mfmParam.split(','), + }); + opened = true; + } + } + if (!opened) { this.close(); } @@ -142,7 +157,7 @@ export class Autocomplete { /** * サジェストをæ示ã—ã¾ã™ã€‚ */ - private async open(type: string, q: string | null) { + private async open(type: string, q: any) { if (type !== this.currentType) { this.close(); } @@ -280,6 +295,22 @@ export class Autocomplete { const pos = trimmedBefore.length + (value.length + 3); this.textarea.setSelectionRange(pos, pos); }); + } else if (type === 'mfmParam') { + const source = this.text; + + const before = source.substring(0, caret); + const trimmedBefore = before.substring(0, before.lastIndexOf('.')); + const after = source.substring(caret); + + // 挿入 + this.text = `${trimmedBefore}.${value}${after}`; + + // ã‚ャレットを戻㙠+ nextTick(() => { + this.textarea.focus(); + const pos = trimmedBefore.length + (value.length + 1); + this.textarea.setSelectionRange(pos, pos); + }); } } } diff --git a/packages/frontend/src/scripts/boost-quote.ts b/packages/frontend/src/scripts/boost-quote.ts new file mode 100644 index 0000000000000000000000000000000000000000..4e025f5d4f22b752ffbdb531c6af0a27f8d5cba8 --- /dev/null +++ b/packages/frontend/src/scripts/boost-quote.ts @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: dakkar and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only +*/ + +import { ref, Ref } from 'vue'; +import * as Misskey from 'misskey-js'; +import { i18n } from '@/i18n.js'; +import { defaultStore } from '@/store.js'; +import { MenuItem } from '@/types/menu.js'; + +/* + this script should eventually contain all Sharkey-specific bits of + boosting and quoting that we would otherwise have to replicate in + `{M,S}kNote{,Detailed,Sub}.vue` + */ + +export type Visibility = 'public' | 'home' | 'followers' | 'specified'; + +export function smallerVisibility(a: Visibility | string, b: Visibility | string): Visibility { + if (a === 'specified' || b === 'specified') return 'specified'; + if (a === 'followers' || b === 'followers') return 'followers'; + if (a === 'home' || b === 'home') return 'home'; + // if (a === 'public' || b === 'public') + return 'public'; +} + +export function visibilityIsAtLeast(a: Visibility | string, b: Visibility | string): boolean { + return smallerVisibility(a, b) === b; +} + +export function boostMenuItems(appearNote: Ref<Misskey.entities.Note>, renote: (v: Visibility, l: boolean) => void): MenuItem[] { + const localOnly = ref(defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly); + const effectiveVisibility = ( + appearNote.value.channel?.isSensitive + ? smallerVisibility(appearNote.value.visibility, 'home') + : appearNote.value.visibility + ); + + const menuItems: MenuItem[] = []; + if (visibilityIsAtLeast(effectiveVisibility, 'public')) { + menuItems.push({ + type: 'button', + icon: 'ph-globe-hemisphere-west ph-bold ph-lg', + text: i18n.ts._visibility['public'], + action: () => { + renote('public', localOnly.value); + }, + } as MenuItem); + } + if (visibilityIsAtLeast(effectiveVisibility, 'home')) { + menuItems.push({ + type: 'button', + icon: 'ph-house ph-bold ph-lg', + text: i18n.ts._visibility['home'], + action: () => { + renote('home', localOnly.value); + }, + } as MenuItem); + } + if (visibilityIsAtLeast(effectiveVisibility, 'followers')) { + menuItems.push({ + type: 'button', + icon: 'ph-lock ph-bold ph-lg', + text: i18n.ts._visibility['followers'], + action: () => { + renote('followers', localOnly.value); + }, + } as MenuItem); + } + + return [ + ...menuItems, + { + type: 'switch', + icon: 'ph-planet ph-bold ph-lg', + text: i18n.ts._timelines.local, + ref: localOnly, + } as MenuItem, + ]; +} diff --git a/packages/frontend/src/scripts/cache.ts b/packages/frontend/src/scripts/cache.ts index 12347cf4b10f0866602f6f04b4599686fd1ebe72..0fbdf34d5d70cd3862baa5ed0dc1bbfa06ab9d3c 100644 --- a/packages/frontend/src/scripts/cache.ts +++ b/packages/frontend/src/scripts/cache.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/chart-legend.ts b/packages/frontend/src/scripts/chart-legend.ts index e91908e0cb7afd6a3e229bba1c5cc967fd03e107..2d534f60c1e8a124122f9acdf6f8001ba8cb6f53 100644 --- a/packages/frontend/src/scripts/chart-legend.ts +++ b/packages/frontend/src/scripts/chart-legend.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/chart-vline.ts b/packages/frontend/src/scripts/chart-vline.ts index 336ec6cfbbe1674d8e53ff992d0ca6bf10888f08..24e41245e72c86c3aaceb20ff47838925ae13175 100644 --- a/packages/frontend/src/scripts/chart-vline.ts +++ b/packages/frontend/src/scripts/chart-vline.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/check-animated-mfm.ts b/packages/frontend/src/scripts/check-animated-mfm.ts index 8e3ef7ee46b0a0bd6b00ec048a6b373a02da4a38..eac18738ee8484e078c61cb90d6e4c83ea7f940a 100644 --- a/packages/frontend/src/scripts/check-animated-mfm.ts +++ b/packages/frontend/src/scripts/check-animated-mfm.ts @@ -1,4 +1,4 @@ -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; export function checkAnimationFromMfm(nodes: mfm.MfmNode[]): boolean { const animatedNodes = mfm.extract(nodes, (node) => { diff --git a/packages/frontend/src/scripts/check-reaction-permissions.ts b/packages/frontend/src/scripts/check-reaction-permissions.ts new file mode 100644 index 0000000000000000000000000000000000000000..e7b473dd7588caa73a42a262ec28c8405a8336fe --- /dev/null +++ b/packages/frontend/src/scripts/check-reaction-permissions.ts @@ -0,0 +1,12 @@ +import * as Misskey from 'misskey-js'; +import { UnicodeEmojiDef } from './emojilist.js'; + +export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef | string): boolean { + if (typeof emoji === 'string') return true; // UnicodeEmojiDefã«ã‚‚ç„¡ã„絵文å—ã§ã‚ã‚Œã°æ–‡å—列ã§æ¥ã‚‹ã€‚Unicode絵文å—ã§ã‚ã‚‹ã“ã¨ã«ã¯å¤‰ã‚ã‚Šãªã„ã®ã§å¸¸ã«ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³å¯èƒ½ã¨ã™ã‚‹; + if ('char' in emoji) return true; // UnicodeEmojiDefãªã‚‰å¸¸ã«ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³å¯èƒ½ + + const roleIdsThatCanBeUsedThisEmojiAsReaction = emoji.roleIdsThatCanBeUsedThisEmojiAsReaction ?? []; + return !(emoji.localOnly && note.user.host !== me.host) + && !(emoji.isSensitive && (note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote')) + && (roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0 || me.roles.some(role => roleIdsThatCanBeUsedThisEmojiAsReaction.includes(role.id))); +} diff --git a/packages/frontend/src/scripts/check-word-mute.ts b/packages/frontend/src/scripts/check-word-mute.ts index 5ac19c8d5b67e7415e5ed1b6a7dcf36e4aabf26c..67e896b4b9d0746c23b36f7db6dc7a20a5484818 100644 --- a/packages/frontend/src/scripts/check-word-mute.ts +++ b/packages/frontend/src/scripts/check-word-mute.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/chiptune2.ts b/packages/frontend/src/scripts/chiptune2.ts index e52adb07d1c39f568a88654751e6cddaa424e338..5a5b1d6c244b75741386a9f1570f9181f5223b4f 100644 --- a/packages/frontend/src/scripts/chiptune2.ts +++ b/packages/frontend/src/scripts/chiptune2.ts @@ -150,6 +150,27 @@ ChiptuneJsPlayer.prototype.getRow = function () { return 0; }; +ChiptuneJsPlayer.prototype.getNumPatterns = function () { + if (this.currentPlayingNode && this.currentPlayingNode.modulePtr) { + return libopenmpt._openmpt_module_get_num_patterns(this.currentPlayingNode.modulePtr); + } + return 0; +}; + +ChiptuneJsPlayer.prototype.getCurrentSpeed = function () { + if (this.currentPlayingNode && this.currentPlayingNode.modulePtr) { + return libopenmpt._openmpt_module_get_current_speed(this.currentPlayingNode.modulePtr); + } + return 0; +}; + +ChiptuneJsPlayer.prototype.getCurrentTempo = function () { + if (this.currentPlayingNode && this.currentPlayingNode.modulePtr) { + return libopenmpt._openmpt_module_get_current_tempo(this.currentPlayingNode.modulePtr); + } + return 0; +}; + ChiptuneJsPlayer.prototype.getPatternNumRows = function (pattern: number) { if (this.currentPlayingNode && this.currentPlayingNode.modulePtr) { return libopenmpt._openmpt_module_get_pattern_num_rows(this.currentPlayingNode.modulePtr, pattern); @@ -164,6 +185,20 @@ ChiptuneJsPlayer.prototype.getPatternRowChannel = function (pattern: number, row return ''; }; +ChiptuneJsPlayer.prototype.getCtls = function () { + if (this.currentPlayingNode && this.currentPlayingNode.modulePtr) { + return libopenmpt._openmpt_module_get_ctls(this.currentPlayingNode.modulePtr); + } + return 0; +}; + +ChiptuneJsPlayer.prototype.version = function () { + if (this.currentPlayingNode && this.currentPlayingNode.modulePtr) { + return libopenmpt._openmpt_get_library_version(); + } + return 0; +}; + ChiptuneJsPlayer.prototype.createLibopenmptNode = function (buffer, config: object) { const maxFramesPerChunk = 4096; const processNode = this.audioContext.createScriptProcessor(2048, 0, 2); @@ -178,6 +213,7 @@ ChiptuneJsPlayer.prototype.createLibopenmptNode = function (buffer, config: obje processNode.paused = false; processNode.leftBufferPtr = libopenmpt._malloc(4 * maxFramesPerChunk); processNode.rightBufferPtr = libopenmpt._malloc(4 * maxFramesPerChunk); + processNode.perf = { 'current': 0, 'max': 0 }; processNode.cleanup = function () { if (this.modulePtr !== 0) { libopenmpt._openmpt_module_destroy(this.modulePtr); @@ -205,7 +241,13 @@ ChiptuneJsPlayer.prototype.createLibopenmptNode = function (buffer, config: obje processNode.togglePause = function () { this.paused = !this.paused; }; + processNode.getProcessTime = function() { + const max = this.perf.max; + this.perf.max = 0; + return { 'current': this.perf.current, 'max': max }; + }; processNode.onaudioprocess = function (e) { + let startTimeP1 = performance.now(); const outputL = e.outputBuffer.getChannelData(0); const outputR = e.outputBuffer.getChannelData(1); let framesToRender = outputL.length; @@ -231,11 +273,13 @@ ChiptuneJsPlayer.prototype.createLibopenmptNode = function (buffer, config: obje const currentPattern = libopenmpt._openmpt_module_get_current_pattern(this.modulePtr); const currentRow = libopenmpt._openmpt_module_get_current_row(this.modulePtr); + startTimeP1 = startTimeP1 - performance.now(); if (currentPattern !== this.patternIndex) { processNode.player.fireEvent('onPatternChange'); } processNode.player.fireEvent('onRowChange', { index: currentRow }); + const startTimeP2 = performance.now(); while (framesToRender > 0) { const framesPerChunk = Math.min(framesToRender, maxFramesPerChunk); const actualFramesPerChunk = libopenmpt._openmpt_module_read_float_stereo(this.modulePtr, this.context.sampleRate, framesPerChunk, this.leftBufferPtr, this.rightBufferPtr); @@ -262,6 +306,8 @@ ChiptuneJsPlayer.prototype.createLibopenmptNode = function (buffer, config: obje this.cleanup(); error ? processNode.player.fireEvent('onError', { type: 'openmpt' }) : processNode.player.fireEvent('onEnded'); } + this.perf.current = performance.now() - startTimeP2 + startTimeP1; + if (this.perf.current > this.perf.max) this.perf.max = this.perf.current; }; return processNode; }; diff --git a/packages/frontend/src/scripts/clear-cache.ts b/packages/frontend/src/scripts/clear-cache.ts index f2db87c4fb06bf572d8ec3f4404cbbb2252230b4..b20109ec72bf96fb5a1fee52101c99020ad5b8ef 100644 --- a/packages/frontend/src/scripts/clear-cache.ts +++ b/packages/frontend/src/scripts/clear-cache.ts @@ -2,14 +2,18 @@ import { unisonReload } from '@/scripts/unison-reload.js'; import * as os from '@/os.js'; import { miLocalStorage } from '@/local-storage.js'; import { fetchCustomEmojis } from '@/custom-emojis.js'; +import { fetchInstance } from '@/instance.js'; export async function clearCache() { os.waiting(); + miLocalStorage.removeItem('instance'); + miLocalStorage.removeItem('instanceCachedAt'); miLocalStorage.removeItem('locale'); miLocalStorage.removeItem('localeVersion'); miLocalStorage.removeItem('theme'); miLocalStorage.removeItem('emojis'); miLocalStorage.removeItem('lastEmojisFetchedAt'); + await fetchInstance(true); await fetchCustomEmojis(true); unisonReload(); } diff --git a/packages/frontend/src/scripts/clicker-game.ts b/packages/frontend/src/scripts/clicker-game.ts index 5ad076e5ef17c962a768276aa60c7d159edda496..f9c4bc182957c09e4b243613b9e6931144ab65b3 100644 --- a/packages/frontend/src/scripts/clicker-game.ts +++ b/packages/frontend/src/scripts/clicker-game.ts @@ -1,10 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { ref, computed } from 'vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; type SaveData = { gameVersion: number; @@ -23,7 +23,7 @@ let prev = ''; export async function load() { try { - saveData.value = await os.api('i/registry/get', { + saveData.value = await misskeyApi('i/registry/get', { scope: ['clickerGame'], key: 'saveData', }); @@ -63,7 +63,7 @@ export async function save() { const current = JSON.stringify(saveData.value); if (current === prev) return; - await os.api('i/registry/set', { + await misskeyApi('i/registry/set', { scope: ['clickerGame'], key: 'saveData', value: saveData.value, diff --git a/packages/frontend/src/scripts/clone.ts b/packages/frontend/src/scripts/clone.ts index 96b53684f3110fdbba83ae2e5341738adfb05878..ea8eea14b57244aea16836ad175c95e2527dea87 100644 --- a/packages/frontend/src/scripts/clone.ts +++ b/packages/frontend/src/scripts/clone.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -8,15 +8,15 @@ // ã‚ã¨ã€Vue Refã‚’IndexedDBã«ä¿å˜ã—よã†ã¨ã—ã¦structredCloneを使ã£ãŸã‚‰ã‚¨ãƒ©ãƒ¼ã«ãªã£ãŸ // https://github.com/misskey-dev/misskey/pull/8098#issuecomment-1114144045 -type Cloneable = string | number | boolean | null | { [key: string]: Cloneable } | Cloneable[]; +export type Cloneable = string | number | boolean | null | undefined | { [key: string]: Cloneable } | { [key: number]: Cloneable } | { [key: symbol]: Cloneable } | Cloneable[]; export function deepClone<T extends Cloneable>(x: T): T { if (typeof x === 'object') { if (x === null) return x; if (Array.isArray(x)) return x.map(deepClone) as T; - const obj = {} as Record<string, Cloneable>; + const obj = {} as Record<string | number | symbol, Cloneable>; for (const [k, v] of Object.entries(x)) { - obj[k] = deepClone(v); + obj[k] = v === undefined ? undefined : deepClone(v); } return obj as T; } else { diff --git a/packages/frontend/src/scripts/code-highlighter.ts b/packages/frontend/src/scripts/code-highlighter.ts index 957669122ef6d7c2d516f61ce60f50fa8ff53dc0..2733897bab109620214a26f4757e31f4af418a70 100644 --- a/packages/frontend/src/scripts/code-highlighter.ts +++ b/packages/frontend/src/scripts/code-highlighter.ts @@ -1,10 +1,51 @@ -import { setWasm, setCDN, Highlighter, getHighlighter as _getHighlighter } from 'shiki'; - -setWasm('/assets/shiki/dist/onig.wasm'); -setCDN('/assets/shiki/'); +import { bundledThemesInfo } from 'shiki'; +import { getHighlighterCore, loadWasm } from 'shiki/core'; +import darkPlus from 'shiki/themes/dark-plus.mjs'; +import { unique } from './array.js'; +import { deepClone } from './clone.js'; +import { deepMerge } from './merge.js'; +import type { Highlighter, LanguageRegistration, ThemeRegistration, ThemeRegistrationRaw } from 'shiki'; +import { ColdDeviceStorage } from '@/store.js'; +import lightTheme from '@/themes/_light.json5'; +import darkTheme from '@/themes/_dark.json5'; let _highlighter: Highlighter | null = null; +export async function getTheme(mode: 'light' | 'dark', getName: true): Promise<string>; +export async function getTheme(mode: 'light' | 'dark', getName?: false): Promise<ThemeRegistration | ThemeRegistrationRaw>; +export async function getTheme(mode: 'light' | 'dark', getName = false): Promise<ThemeRegistration | ThemeRegistrationRaw | string | null> { + const theme = deepClone(ColdDeviceStorage.get(mode === 'light' ? 'lightTheme' : 'darkTheme')); + + if (theme.base) { + const base = [lightTheme, darkTheme].find(x => x.id === theme.base); + if (base && base.codeHighlighter) theme.codeHighlighter = Object.assign({}, base.codeHighlighter, theme.codeHighlighter); + } + + if (theme.codeHighlighter) { + let _res: ThemeRegistration = {}; + if (theme.codeHighlighter.base === '_none_') { + _res = deepClone(theme.codeHighlighter.overrides); + } else { + const base = await bundledThemesInfo.find(t => t.id === theme.codeHighlighter!.base)?.import() ?? darkPlus; + _res = deepMerge(theme.codeHighlighter.overrides ?? {}, 'default' in base ? base.default : base); + } + if (_res.name == null) { + _res.name = theme.id; + } + _res.type = mode; + + if (getName) { + return _res.name; + } + return _res; + } + + if (getName) { + return 'dark-plus'; + } + return darkPlus; +} + export async function getHighlighter(): Promise<Highlighter> { if (!_highlighter) { return await initHighlighter(); @@ -13,16 +54,36 @@ export async function getHighlighter(): Promise<Highlighter> { } export async function initHighlighter() { - const highlighter = await _getHighlighter({ - theme: 'dark-plus', - langs: ['js'], + const aiScriptGrammar = await import('aiscript-vscode/aiscript/syntaxes/aiscript.tmLanguage.json'); + + await loadWasm(import('shiki/onig.wasm?init')); + + // テーマã®é‡è¤‡ã‚’消㙠+ const themes = unique([ + darkPlus, + ...(await Promise.all([getTheme('light'), getTheme('dark')])), + ]); + + const highlighter = await getHighlighterCore({ + themes, + langs: [ + import('shiki/langs/javascript.mjs'), + aiScriptGrammar.default as unknown as LanguageRegistration, + ], + }); + + ColdDeviceStorage.watch('lightTheme', async () => { + const newTheme = await getTheme('light'); + if (newTheme.name && !highlighter.getLoadedThemes().includes(newTheme.name)) { + highlighter.loadTheme(newTheme); + } }); - await highlighter.loadLanguage({ - path: 'languages/aiscript.tmLanguage.json', - id: 'aiscript', - scopeName: 'source.aiscript', - aliases: ['is', 'ais'], + ColdDeviceStorage.watch('darkTheme', async () => { + const newTheme = await getTheme('dark'); + if (newTheme.name && !highlighter.getLoadedThemes().includes(newTheme.name)) { + highlighter.loadTheme(newTheme); + } }); _highlighter = highlighter; diff --git a/packages/frontend/src/scripts/collapsed.ts b/packages/frontend/src/scripts/collapsed.ts index 57e6ecf5b5ac450d8014c0783ca5f8d3a740c744..237bd37c7a0225b3424cc00eb891be14147b1fbb 100644 --- a/packages/frontend/src/scripts/collapsed.ts +++ b/packages/frontend/src/scripts/collapsed.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/collect-page-vars.ts b/packages/frontend/src/scripts/collect-page-vars.ts index 79356e60ebe8c1e697de84bd5f731fde91ddfe91..5096c0669ee123027e4958ed1578311234be028b 100644 --- a/packages/frontend/src/scripts/collect-page-vars.ts +++ b/packages/frontend/src/scripts/collect-page-vars.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/color.ts b/packages/frontend/src/scripts/color.ts index 25ef41d9b754cc0975a9e4f8db3b8a61c8b6bea1..a11255ffd1d074261681fbf5644fb97a2c30165d 100644 --- a/packages/frontend/src/scripts/color.ts +++ b/packages/frontend/src/scripts/color.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/confetti.ts b/packages/frontend/src/scripts/confetti.ts index b394ba3e2a9edd98447ee21c9c5387a496825130..8e53a6ceeb005d0ae0c90b116823f98b3b3a05bb 100644 --- a/packages/frontend/src/scripts/confetti.ts +++ b/packages/frontend/src/scripts/confetti.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/contains.ts b/packages/frontend/src/scripts/contains.ts index b50ce4128c53c23f3f243ccb068a0a58e019b335..6137c06e85747c6300152895537f87efa272e37d 100644 --- a/packages/frontend/src/scripts/contains.ts +++ b/packages/frontend/src/scripts/contains.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/copy-to-clipboard.ts b/packages/frontend/src/scripts/copy-to-clipboard.ts index 3884d4a20a1dc69e1c9a17f3b7102719e7844ea3..216c0464b3d5a0d300debef9bd70020853418ca9 100644 --- a/packages/frontend/src/scripts/copy-to-clipboard.ts +++ b/packages/frontend/src/scripts/copy-to-clipboard.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/device-kind.ts b/packages/frontend/src/scripts/device-kind.ts index 3843052a2430745d301c478208d2d8c840f6d9bf..7c33f8ccee274f497b99a9110e21ac330759af23 100644 --- a/packages/frontend/src/scripts/device-kind.ts +++ b/packages/frontend/src/scripts/device-kind.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -11,6 +11,13 @@ const ua = navigator.userAgent.toLowerCase(); const isTablet = /ipad/.test(ua) || (/mobile|iphone|android/.test(ua) && window.innerWidth > 700); const isSmartphone = !isTablet && /mobile|iphone|android/.test(ua); +const isIPhone = /iphone|ipod/gi.test(ua) && navigator.maxTouchPoints > 1; +// navigator.platform may be deprecated but this check is still required +const isIPadOS = navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1; +const isIos = /ipad|iphone|ipod/gi.test(ua) && navigator.maxTouchPoints > 1; + +export const isFullscreenNotSupported = isIPhone || isIos; + export const deviceKind: 'smartphone' | 'tablet' | 'desktop' = defaultStore.state.overridedDeviceKind ? defaultStore.state.overridedDeviceKind : isSmartphone ? 'smartphone' : isTablet ? 'tablet' diff --git a/packages/frontend/src/scripts/emoji-base.ts b/packages/frontend/src/scripts/emoji-base.ts index 46a13462a1edb4c7b40b8481c92ce846489ae6eb..16a5a6aa5b3fa6739f1f4c4ff55fea3853495c71 100644 --- a/packages/frontend/src/scripts/emoji-base.ts +++ b/packages/frontend/src/scripts/emoji-base.ts @@ -1,10 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ const twemojiSvgBase = '/twemoji'; const fluentEmojiPngBase = '/fluent-emoji'; +const tossfaceSvgBase = '/tossface'; export function char2twemojiFilePath(char: string): string { let codes = Array.from(char, x => x.codePointAt(0)?.toString(16)); @@ -23,3 +24,14 @@ export function char2fluentEmojiFilePath(char: string): string { const fileName = codes.map(x => x!.padStart(4, '0')).join('-'); return `${fluentEmojiPngBase}/${fileName}.png`; } + +export function char2tossfaceFilePath(char: string): string { + let codes = Array.from(char, x => x.codePointAt(0)?.toString(16)); + // Twemoji is the only emoji font which still supports the shibuya 50 emoji to this day + if (codes[0]?.startsWith('e50a')) return char2twemojiFilePath(char); + // Tossface does not use the fe0f modifier + codes = codes.filter(x => x !== 'fe0f'); + codes = codes.filter(x => x && x.length); + const fileName = codes.join('-'); + return `${tossfaceSvgBase}/${fileName}.svg`; +} diff --git a/packages/frontend/src/scripts/emoji-picker.ts b/packages/frontend/src/scripts/emoji-picker.ts index f87c3f6fb2aab31d0478ff57e299f5d0697f2ffb..14b5cbf35e0e3f2f28b4ca6b5faec911facebd79 100644 --- a/packages/frontend/src/scripts/emoji-picker.ts +++ b/packages/frontend/src/scripts/emoji-picker.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/emojilist.ts b/packages/frontend/src/scripts/emojilist.ts index 8885bf4b7f5f90db36f2eb6c21e5489edc296642..6565feba97d2d1e1b3831cd5fabc882b9d49d149 100644 --- a/packages/frontend/src/scripts/emojilist.ts +++ b/packages/frontend/src/scripts/emojilist.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -20,6 +20,10 @@ export const emojilist: UnicodeEmojiDef[] = _emojilist.map(x => ({ category: unicodeEmojiCategories[x[2]], })); +const unicodeEmojisMap = new Map<string, UnicodeEmojiDef>( + emojilist.map(x => [x.char, x]), +); + const _indexByChar = new Map<string, number>(); const _charGroupByCategory = new Map<string, string[]>(); for (let i = 0; i < emojilist.length; i++) { @@ -35,15 +39,33 @@ for (let i = 0; i < emojilist.length; i++) { export const emojiCharByCategory = _charGroupByCategory; -export function getEmojiName(char: string): string | null { - const idx = _indexByChar.get(char); - if (idx == null) { - return null; +export function getUnicodeEmoji(char: string): UnicodeEmojiDef | string { + // Colorize it because emojilist.json assumes that + return unicodeEmojisMap.get(colorizeEmoji(char)) + // カラースタイル絵文å—ãŒjsonã«ç„¡ã„å ´åˆã¯ãƒ†ã‚ストスタイル絵文å—ã«ãƒ•ã‚©ãƒ¼ãƒ«ãƒãƒƒã‚¯ã™ã‚‹ + ?? unicodeEmojisMap.get(char) + // ãã‚Œã§ã‚‚見ã¤ã‹ã‚‰ãªã„å ´åˆã¯ãã®ã¾ã¾è¿”ã™ï¼ˆçµµæ–‡å—æƒ…å ±ãŒjsonã«ç„¡ã„å ´åˆã€ã“ã®ãƒ•ã‚©ãƒ¼ãƒ«ãƒãƒƒã‚¯ãŒç„¡ã„ã¨ãƒ¬ãƒ³ãƒ€ãƒªãƒ³ã‚°ã«å¤±æ•—ã™ã‚‹ï¼‰ + ?? char; +} + +export function getEmojiName(char: string): string { + // Colorize it because emojilist.json assumes that + const idx = _indexByChar.get(colorizeEmoji(char)) ?? _indexByChar.get(char); + if (idx === undefined) { + // 絵文å—æƒ…å ±ãŒjsonã«ç„¡ã„å ´åˆã¯åå‰ã®å–å¾—ãŒå‡ºæ¥ãªã„ã®ã§ãã®ã¾ã¾è¿”ã™ã—ã‹ç„¡ã„ + return char; } else { return emojilist[idx].name; } } +/** + * テã‚ストスタイル絵文å—(U+260Eãªã©ã®1æ–‡å—ã§è¡¨ç¾ã•ã‚Œã‚‹çµµæ–‡å—)をカラースタイル絵文å—ã«å¤‰æ›ã—ã¾ã™ï¼ˆVS16:U+FE0Fを付与)。 + */ +export function colorizeEmoji(char: string) { + return char.length === 1 ? `${char}\uFE0F` : char; +} + export interface CustomEmojiFolderTree { value: string; category: string; diff --git a/packages/frontend/src/scripts/extract-avg-color-from-blurhash.ts b/packages/frontend/src/scripts/extract-avg-color-from-blurhash.ts index 57b296ab2af9be4d28175bb6b1c46b41709878d2..992f6e9a16de373928a82de50ff368ecdceeb797 100644 --- a/packages/frontend/src/scripts/extract-avg-color-from-blurhash.ts +++ b/packages/frontend/src/scripts/extract-avg-color-from-blurhash.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/extract-mentions.ts b/packages/frontend/src/scripts/extract-mentions.ts index fe60f9a85128df42aacd52945a7ad6e9492796d7..89a5ce1df8bed2e96db85a3a1a1f11a9e8ba801b 100644 --- a/packages/frontend/src/scripts/extract-mentions.ts +++ b/packages/frontend/src/scripts/extract-mentions.ts @@ -1,11 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ // test is located in test/extract-mentions -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; export function extractMentions(nodes: mfm.MfmNode[]): mfm.MfmMention['props'][] { // TODO: é‡è¤‡ã‚’削除 diff --git a/packages/frontend/src/scripts/extract-url-from-mfm.ts b/packages/frontend/src/scripts/extract-url-from-mfm.ts index d2fbfbcc0073547a2f6228889cfb487ebebbdb6d..a4c84aa7409c307ff999883652a561437170927b 100644 --- a/packages/frontend/src/scripts/extract-url-from-mfm.ts +++ b/packages/frontend/src/scripts/extract-url-from-mfm.ts @@ -1,9 +1,9 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import * as mfm from '@sharkey/sfm-js'; +import * as mfm from '@transfem-org/sfm-js'; import { unique } from '@/scripts/array.js'; // unique without hash diff --git a/packages/frontend/src/scripts/focus.ts b/packages/frontend/src/scripts/focus.ts index 6a31ebd43157361981e58c01afea261ffad516bb..ea6ee61c8838196abe8fed8f1323392bef5da0b2 100644 --- a/packages/frontend/src/scripts/focus.ts +++ b/packages/frontend/src/scripts/focus.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/form.ts b/packages/frontend/src/scripts/form.ts index 222fd9b0b7fd7beeed1defc245b7bc4f191d756e..b0db404f28183f9bccecdd87f689cb155b0b0890 100644 --- a/packages/frontend/src/scripts/form.ts +++ b/packages/frontend/src/scripts/form.ts @@ -1,51 +1,77 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -type EnumItem = string | {label: string; value: string;}; +type EnumItem = string | { + label: string; + value: string; +}; + export type FormItem = { label?: string; type: 'string'; default: string | null; + description?: string; + required?: boolean; hidden?: boolean; multiline?: boolean; + treatAsMfm?: boolean; } | { label?: string; type: 'number'; default: number | null; + description?: string; + required?: boolean; hidden?: boolean; step?: number; } | { label?: string; type: 'boolean'; default: boolean | null; + description?: string; hidden?: boolean; } | { label?: string; type: 'enum'; default: string | null; + required?: boolean; hidden?: boolean; enum: EnumItem[]; } | { label?: string; type: 'radio'; default: unknown | null; + required?: boolean; hidden?: boolean; options: { label: string; value: unknown; }[]; +} | { + label?: string; + type: 'range'; + default: number | null; + description?: string; + required?: boolean; + step?: number; + min: number; + max: number; + textConverter?: (value: number) => string; } | { label?: string; type: 'object'; default: Record<string, unknown> | null; - hidden: true; + hidden: boolean; } | { label?: string; type: 'array'; default: unknown[] | null; - hidden: true; + hidden: boolean; +} | { + type: 'button'; + content?: string; + action: (ev: MouseEvent, v: any) => void; }; export type Form = Record<string, FormItem>; @@ -55,6 +81,7 @@ type GetItemType<Item extends FormItem> = Item['type'] extends 'number' ? number : Item['type'] extends 'boolean' ? boolean : Item['type'] extends 'radio' ? unknown : + Item['type'] extends 'range' ? number : Item['type'] extends 'enum' ? string : Item['type'] extends 'array' ? unknown[] : Item['type'] extends 'object' ? Record<string, unknown> diff --git a/packages/frontend/src/scripts/format-time-string.ts b/packages/frontend/src/scripts/format-time-string.ts index 918996dd107e81139261cf7bb983b1836cb6f362..35ad77d98240d176eb8b2ca74c5cebdeeb0ee424 100644 --- a/packages/frontend/src/scripts/format-time-string.ts +++ b/packages/frontend/src/scripts/format-time-string.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/gen-search-query.ts b/packages/frontend/src/scripts/gen-search-query.ts index 54654980f2e24e25e876426f525778d029cb93ab..60884d08d39b933a0eb83e8b82cd7e1568543fbb 100644 --- a/packages/frontend/src/scripts/gen-search-query.ts +++ b/packages/frontend/src/scripts/gen-search-query.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -18,7 +18,7 @@ export async function genSearchQuery(v: any, q: string) { host = at; } } else { - const user = await v.os.api('users/show', Misskey.acct.parse(at)).catch(x => null); + const user = await v.api('users/show', Misskey.acct.parse(at)).catch(x => null); if (user) { userId = user.id; } else { diff --git a/packages/frontend/src/scripts/get-account-from-id.ts b/packages/frontend/src/scripts/get-account-from-id.ts index 346d283572ee1d65624a3bd1f3a98df4d23ccac4..40afa10f2dca749dc955b559e038a379e8425e70 100644 --- a/packages/frontend/src/scripts/get-account-from-id.ts +++ b/packages/frontend/src/scripts/get-account-from-id.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/get-drive-file-menu.ts b/packages/frontend/src/scripts/get-drive-file-menu.ts index d6a5b00c0bd5a96125ab382ecb125191885150b5..a883404307120561cc02162508241047fb8f2c30 100644 --- a/packages/frontend/src/scripts/get-drive-file-menu.ts +++ b/packages/frontend/src/scripts/get-drive-file-menu.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -8,6 +8,7 @@ import { defineAsyncComponent } from 'vue'; import { i18n } from '@/i18n.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { MenuItem } from '@/types/menu.js'; import { defaultStore } from '@/store.js'; @@ -18,7 +19,7 @@ function rename(file: Misskey.entities.DriveFile) { default: file.name, }).then(({ canceled, result: name }) => { if (canceled) return; - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: file.id, name: name, }); @@ -31,7 +32,7 @@ function describe(file: Misskey.entities.DriveFile) { file: file, }, { done: caption => { - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: file.id, comment: caption.length === 0 ? null : caption, }); @@ -40,7 +41,7 @@ function describe(file: Misskey.entities.DriveFile) { } function toggleSensitive(file: Misskey.entities.DriveFile) { - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: file.id, isSensitive: !file.isSensitive, }).catch(err => { @@ -65,11 +66,11 @@ function addApp() { async function deleteFile(file: Misskey.entities.DriveFile) { const { canceled } = await os.confirm({ type: 'warning', - text: i18n.t('driveFileDeleteConfirm', { name: file.name }), + text: i18n.tsx.driveFileDeleteConfirm({ name: file.name }), }); if (canceled) return; - os.api('drive/files/delete', { + misskeyApi('drive/files/delete', { fileId: file.id, }); } @@ -103,7 +104,7 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss }), }] : [], { type: 'divider' }, { text: i18n.ts.createNoteFromTheFile, - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', action: () => os.post({ initialFiles: [file], }), diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index a409f1b775496e5dcf4e2b88f87c21f652f297e0..40359a88bb0545ed375c575083067444a7b98a31 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -1,15 +1,16 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import { defineAsyncComponent, Ref } from 'vue'; +import { defineAsyncComponent, Ref, ShallowRef } from 'vue'; import * as Misskey from 'misskey-js'; import { claimAchievement } from './achievements.js'; import { $i } from '@/account.js'; import { i18n } from '@/i18n.js'; import { instance } from '@/instance.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { url } from '@/config.js'; import { defaultStore, noteActions } from '@/store.js'; @@ -35,18 +36,18 @@ export async function getNoteClipMenu(props: { const appearNote = isRenote ? props.note.renote as Misskey.entities.Note : props.note; const clips = await clipsCache.fetch(); - return [...clips.map(clip => ({ + const menu: MenuItem[] = [...clips.map(clip => ({ text: clip.name, action: () => { claimAchievement('noteClipped1'); os.promiseDialog( - os.api('clips/add-note', { clipId: clip.id, noteId: appearNote.id }), + misskeyApi('clips/add-note', { clipId: clip.id, noteId: appearNote.id }), null, async (err) => { if (err.id === '734806c4-542c-463a-9311-15c512803965') { const confirm = await os.confirm({ type: 'warning', - text: i18n.t('confirmToUnclipAlreadyClippedNote', { name: clip.name }), + text: i18n.tsx.confirmToUnclipAlreadyClippedNote({ name: clip.name }), }); if (!confirm.canceled) { os.apiWithDialog('clips/remove-note', { clipId: clip.id, noteId: appearNote.id }); @@ -92,6 +93,8 @@ export async function getNoteClipMenu(props: { os.apiWithDialog('clips/add-note', { clipId: clip.id, noteId: appearNote.id }); }, }]; + + return menu; } export function getAbuseNoteMenu(note: Misskey.entities.Note, text: string): MenuItem { @@ -99,10 +102,13 @@ export function getAbuseNoteMenu(note: Misskey.entities.Note, text: string): Men icon: 'ph-warning-circle ph-bold ph-lg', text, action: (): void => { - const u = note.url ?? note.uri ?? `${url}/notes/${note.id}`; + const localUrl = `${url}/notes/${note.id}`; + let noteInfo = ''; + if (note.url ?? note.uri != null) noteInfo = `Note: ${note.url ?? note.uri}\n`; + noteInfo += `Local Note: ${localUrl}\n`; os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { user: note.user, - initialComment: `Note: ${u}\n-----\n`, + initialComment: `${noteInfo}-----\n`, }, {}, 'closed'); }, }; @@ -132,7 +138,6 @@ export function getCopyNoteOriginLinkMenu(note: misskey.entities.Note, text: str export function getNoteMenu(props: { note: Misskey.entities.Note; - menuButton: Ref<HTMLElement>; translation: Ref<Misskey.entities.NotesTranslateResponse | null>; translating: Ref<boolean>; isDeleted: Ref<boolean>; @@ -156,7 +161,7 @@ export function getNoteMenu(props: { }).then(({ canceled }) => { if (canceled) return; - os.api('notes/delete', { + misskeyApi('notes/delete', { noteId: appearNote.id, }); @@ -173,7 +178,7 @@ export function getNoteMenu(props: { }).then(({ canceled }) => { if (canceled) return; - os.api('notes/delete', { + misskeyApi('notes/delete', { noteId: appearNote.id, }); @@ -252,7 +257,7 @@ export function getNoteMenu(props: { function share(): void { navigator.share({ - title: i18n.t('noteOf', { user: appearNote.user.name }), + title: i18n.tsx.noteOf({ user: appearNote.user.name }), text: appearNote.text, url: `${url}/notes/${appearNote.id}`, }); @@ -265,7 +270,7 @@ export function getNoteMenu(props: { async function translate(): Promise<void> { if (props.translation.value != null) return; props.translating.value = true; - const res = await os.api('notes/translate', { + const res = await misskeyApi('notes/translate', { noteId: appearNote.id, targetLang: miLocalStorage.getItem('lang') ?? navigator.language, }); @@ -275,7 +280,7 @@ export function getNoteMenu(props: { let menu: MenuItem[]; if ($i) { - const statePromise = os.api('notes/state', { + const statePromise = misskeyApi('notes/state', { noteId: appearNote.id, }); @@ -296,7 +301,7 @@ export function getNoteMenu(props: { text: i18n.ts.copyContent, action: copyContent, }, getCopyNoteLinkMenu(appearNote, i18n.ts.copyLink) - , (appearNote.url || appearNote.uri) ? + , (appearNote.url || appearNote.uri) ? getCopyNoteOriginLinkMenu(appearNote, 'Copy link (Origin)') : undefined, (appearNote.url || appearNote.uri) ? { @@ -355,7 +360,7 @@ export function getNoteMenu(props: { icon: 'ph-user ph-bold ph-lg', text: i18n.ts.user, children: async () => { - const user = appearNote.userId === $i?.id ? $i : await os.api('users/show', { userId: appearNote.userId }); + const user = appearNote.userId === $i?.id ? $i : await misskeyApi('users/show', { userId: appearNote.userId }); const { menu, cleanup } = getUserMenu(user); cleanups.push(cleanup); return menu; @@ -377,19 +382,55 @@ export function getNoteMenu(props: { ] : [] ), + ...(appearNote.channel && (appearNote.channel.userId === $i.id || $i.isModerator || $i.isAdmin) ? [ + { type: 'divider' }, + { + type: 'parent' as const, + icon: 'ti ti-device-tv', + text: i18n.ts.channel, + children: async () => { + const channelChildMenu = [] as MenuItem[]; + + const channel = await misskeyApi('channels/show', { channelId: appearNote.channel!.id }); + + if (channel.pinnedNoteIds.includes(appearNote.id)) { + channelChildMenu.push({ + icon: 'ti ti-pinned-off', + text: i18n.ts.unpin, + action: () => os.apiWithDialog('channels/update', { + channelId: appearNote.channel!.id, + pinnedNoteIds: channel.pinnedNoteIds.filter(id => id !== appearNote.id), + }), + }); + } else { + channelChildMenu.push({ + icon: 'ti ti-pin', + text: i18n.ts.pin, + action: () => os.apiWithDialog('channels/update', { + channelId: appearNote.channel!.id, + pinnedNoteIds: [...channel.pinnedNoteIds, appearNote.id], + }), + }); + } + return channelChildMenu; + }, + }, + ] + : [] + ), ...(appearNote.userId === $i.id || $i.isModerator || $i.isAdmin ? [ { type: 'divider' }, appearNote.userId === $i.id ? { - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', text: i18n.ts.edit, action: edit, } : undefined, { - icon: 'ph-pencil-line ph-bold ph-lg', + icon: 'ph-pencil-simple-line ph-bold ph-lg', text: i18n.ts.deleteAndEdit, danger: true, action: delEdit, - }, + }, { icon: 'ph-trash ph-bold ph-lg', text: i18n.ts.delete, @@ -409,7 +450,7 @@ export function getNoteMenu(props: { text: i18n.ts.copyContent, action: copyContent, }, getCopyNoteLinkMenu(appearNote, i18n.ts.copyLink) - , (appearNote.url || appearNote.uri) ? + , (appearNote.url || appearNote.uri) ? getCopyNoteOriginLinkMenu(appearNote, 'Copy link (Origin)') : undefined, (appearNote.url || appearNote.uri) ? { @@ -468,7 +509,7 @@ function smallerVisibility(a: Visibility | string, b: Visibility | string): Visi export function getRenoteMenu(props: { note: Misskey.entities.Note; - renoteButton: Ref<HTMLElement>; + renoteButton: ShallowRef<HTMLElement | undefined>; mock?: boolean; }) { const isRenote = ( @@ -488,7 +529,7 @@ export function getRenoteMenu(props: { text: i18n.ts.inChannelRenote, icon: 'ti ti-repeat', action: () => { - const el = props.renoteButton.value as HTMLElement | null | undefined; + const el = props.renoteButton.value; if (el) { const rect = el.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); @@ -497,7 +538,7 @@ export function getRenoteMenu(props: { } if (!props.mock) { - os.api('notes/create', { + misskeyApi('notes/create', { renoteId: appearNote.id, channelId: appearNote.channelId, }).then(() => { @@ -524,7 +565,7 @@ export function getRenoteMenu(props: { text: i18n.ts.renote, icon: 'ti ti-repeat', action: () => { - const el = props.renoteButton.value as HTMLElement | null | undefined; + const el = props.renoteButton.value; if (el) { const rect = el.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); @@ -542,7 +583,7 @@ export function getRenoteMenu(props: { } if (!props.mock) { - os.api('notes/create', { + misskeyApi('notes/create', { localOnly, visibility, renoteId: appearNote.id, @@ -564,7 +605,7 @@ export function getRenoteMenu(props: { const renoteItems = [ ...normalRenoteItems, - ...(channelRenoteItems.length > 0 && normalRenoteItems.length > 0) ? [{ type: 'divider' }] : [], + ...(channelRenoteItems.length > 0 && normalRenoteItems.length > 0) ? [{ type: 'divider' }] as MenuItem[] : [], ...channelRenoteItems, ]; diff --git a/packages/frontend/src/scripts/get-note-summary.ts b/packages/frontend/src/scripts/get-note-summary.ts index 1fd9f04d4616be0fce66fdfc8d62b4087c3f7592..6fd9947ac1e75eff85eb7790d926f3c88955b432 100644 --- a/packages/frontend/src/scripts/get-note-summary.ts +++ b/packages/frontend/src/scripts/get-note-summary.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -10,7 +10,11 @@ import { i18n } from '@/i18n.js'; * 投稿を表ã™æ–‡å—列をå–å¾—ã—ã¾ã™ã€‚ * @param {*} note (packã•ã‚ŒãŸ)投稿 */ -export const getNoteSummary = (note: Misskey.entities.Note): string => { +export const getNoteSummary = (note?: Misskey.entities.Note | null): string => { + if (note == null) { + return ''; + } + if (note.deletedAt) { return `(${i18n.ts.deletedNote})`; } @@ -30,7 +34,7 @@ export const getNoteSummary = (note: Misskey.entities.Note): string => { // ファイルãŒæ·»ä»˜ã•ã‚Œã¦ã„ã‚‹ã¨ã if ((note.files || []).length !== 0) { - summary += ` (${i18n.t('withNFiles', { n: note.files.length })})`; + summary += ` (${i18n.tsx.withNFiles({ n: note.files.length })})`; } // 投票ãŒæ·»ä»˜ã•ã‚Œã¦ã„ã‚‹ã¨ã diff --git a/packages/frontend/src/scripts/get-note-versions-menu.ts b/packages/frontend/src/scripts/get-note-versions-menu.ts index 46e3bab3a75c0cfd2836d7c20f4813825abb7ac3..84292f1277e65ba05a29f4d65875ae38bd31d1e8 100644 --- a/packages/frontend/src/scripts/get-note-versions-menu.ts +++ b/packages/frontend/src/scripts/get-note-versions-menu.ts @@ -2,6 +2,7 @@ import { Ref, defineAsyncComponent } from 'vue'; import * as Misskey from 'misskey-js'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; +import { misskeyApi } from './misskey-api.js'; import { MenuItem } from '@/types/menu.js'; import { dateTimeFormat } from './intl-const.js'; @@ -30,7 +31,7 @@ export async function getNoteVersionsMenu(props: { } const menu: MenuItem[] = []; - const statePromise = os.api('notes/versions', { + const statePromise = misskeyApi('notes/versions', { noteId: appearNote.id, }); @@ -39,9 +40,9 @@ export async function getNoteVersionsMenu(props: { const _time = edit.oldDate == null ? NaN : typeof edit.oldDate === 'number' ? edit.oldDate : (edit.oldDate instanceof Date ? edit.oldDate : new Date(edit.oldDate)).getTime(); - + menu.push({ - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', text: _time ? dateTimeFormat.format(_time) : dateTimeFormat.format(new Date(edit.updatedAt)), action: () => openVersion(edit), }); diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index 67bc781aef83ff2fdf747ae589bc701fc38d0a1b..61f9a453dc4882afbb44c04262ae71ee7cbf04d1 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -10,13 +10,14 @@ import { i18n } from '@/i18n.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { host, url } from '@/config.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore, userActions } from '@/store.js'; import { $i, iAmModerator } from '@/account.js'; -import { mainRouter } from '@/router.js'; -import { Router } from '@/nirax.js'; +import { IRouter } from '@/nirax.js'; import { antennasCache, rolesCache, userListsCache } from '@/cache.js'; +import { mainRouter } from '@/router/main.js'; -export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router = mainRouter) { +export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter = mainRouter) { const meId = $i ? $i.id : null; const cleanups = [] as (() => void)[]; @@ -131,7 +132,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router } async function editMemo(): Promise<void> { - const userDetailed = await os.api('users/show', { + const userDetailed = await misskeyApi('users/show', { userId: user.id, }); const { canceled, result } = await os.form(i18n.ts.editMemo, { @@ -169,20 +170,21 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router action: () => { copyToClipboard(`${user.host ?? host}/@${user.username}.atom`); }, - }, { + }, ...(user.host != null && user.url != null ? [{ + icon: 'ph-share ph-bold ph-lg', + text: i18n.ts.showOnRemote, + action: () => { + if (user.url == null) return; + window.open(user.url, '_blank', 'noopener'); + }, + }] : []), { icon: 'ph-share-network ph-bold ph-lg', text: i18n.ts.copyProfileUrl, action: () => { const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${toUnicode(user.host)}`; copyToClipboard(`${url}/${canonical}`); }, - }, ...(user.host ? [{ - icon: 'ph-share ph-bold ph-lg', - text: i18n.ts.openRemoteProfile, - action: () => { - open(`${user.uri}`, '_blank'); - }, - }] : []), { + }, { icon: 'ph-envelope ph-bold ph-lg', text: i18n.ts.sendMessage, action: () => { @@ -190,7 +192,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router os.post({ specified: user, initialText: `${canonical} ` }); }, }, { type: 'divider' }, { - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', text: i18n.ts.editMemo, action: () => { editMemo(); @@ -362,7 +364,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router if ($i && meId === user.id) { menu = menu.concat([{ type: 'divider' }, { - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', text: i18n.ts.editProfile, action: () => { router.push('/settings/profile'); diff --git a/packages/frontend/src/scripts/get-user-name.ts b/packages/frontend/src/scripts/get-user-name.ts index 3ae80d7fc391b9ea47a5b6dce9a87129af39f1b9..56e91abba029cc1863829c20e3a69046d67a6bb9 100644 --- a/packages/frontend/src/scripts/get-user-name.ts +++ b/packages/frontend/src/scripts/get-user-name.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/hotkey.ts b/packages/frontend/src/scripts/hotkey.ts index 48c80c066b93c7f7e81c1d29b2b381ba7200d6c2..0600bff893b3c42dedbba55070eeb8e0e4a93e85 100644 --- a/packages/frontend/src/scripts/hotkey.ts +++ b/packages/frontend/src/scripts/hotkey.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/i18n.ts b/packages/frontend/src/scripts/i18n.ts index 8e5f17f38ae0dbe30f7f82ecd04642e72eef0ba8..c2f44a33cc4713c2d33cad610b7eca85cd9df57c 100644 --- a/packages/frontend/src/scripts/i18n.ts +++ b/packages/frontend/src/scripts/i18n.ts @@ -1,34 +1,294 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ +import type { ILocale, ParameterizedString } from '../../../../locales/index.js'; -export class I18n<T extends Record<string, any>> { - public ts: T; +type FlattenKeys<T extends ILocale, TPrediction> = keyof { + [K in keyof T as T[K] extends ILocale + ? FlattenKeys<T[K], TPrediction> extends infer C extends string + ? `${K & string}.${C}` + : never + : T[K] extends TPrediction + ? K + : never]: T[K]; +}; - constructor(locale: T) { - this.ts = locale; +type ParametersOf<T extends ILocale, TKey extends FlattenKeys<T, ParameterizedString>> = TKey extends `${infer K}.${infer C}` + // @ts-expect-error -- C ã¯æ˜Žã‚‰ã‹ã« FlattenKeys<T[K], ParameterizedString> ã«ãªã‚‹ãŒã€åž‹ã‚·ã‚¹ãƒ†ãƒ ã¯ã“ã“ã§ã¯ TKey ãŒãƒ‰ãƒƒãƒˆåŒºåˆ‡ã‚Šã§ã‚ã‚‹ã“ã¨ã®ã‚³ãƒ³ãƒ†ã‚ストをæŒãŸãªã„ã®ã§ã€åž‹ã‚·ã‚¹ãƒ†ãƒ ã«åˆæ³•ã«ã¦ç¤ºã™ã“ã¨ã¯ã§ããªã„。 + ? ParametersOf<T[K], C> + : TKey extends keyof T + ? T[TKey] extends ParameterizedString<infer P> + ? P + : never + : never; +type Tsx<T extends ILocale> = { + readonly [K in keyof T as T[K] extends string ? never : K]: T[K] extends ParameterizedString<infer P> + ? (arg: { readonly [_ in P]: string | number }) => string + // @ts-expect-error -- 証明çœç•¥ + : Tsx<T[K]>; +}; + +export class I18n<T extends ILocale> { + private tsxCache?: Tsx<T>; + + constructor(public locale: T) { //#region BIND this.t = this.t.bind(this); //#endregion } - // string ã«ã—ã¦ã„ã‚‹ã®ã¯ã€ãƒ‰ãƒƒãƒˆåŒºåˆ‡ã‚Šã§ã®ãƒ‘ス指定を許å¯ã™ã‚‹ãŸã‚ - // ãªã‚‹ã¹ãã“ã®ãƒ¡ã‚½ãƒƒãƒ‰ä½¿ã†ã‚ˆã‚Šã‚‚locale直接å‚ç…§ã®æ–¹ãŒvueã®ã‚ャッシュ効ã„ã¦ãƒ‘フォーマンスãŒè‰¯ã„ã‹ã‚‚ - public t(key: string, args?: Record<string, string | number>): string { - try { - let str = key.split('.').reduce((o, i) => o[i], this.ts) as unknown as string; + public get ts(): T { + if (_DEV_) { + class Handler<TTarget extends ILocale> implements ProxyHandler<TTarget> { + get(target: TTarget, p: string | symbol): unknown { + const value = target[p as keyof TTarget]; + + if (typeof value === 'object') { + return new Proxy(value, new Handler<TTarget[keyof TTarget] & ILocale>()); + } + + if (typeof value === 'string') { + const parameters = Array.from(value.matchAll(/\{(\w+)\}/g), ([, parameter]) => parameter); + + if (parameters.length) { + console.error(`Missing locale parameters: ${parameters.join(', ')} at ${String(p)}`); + } + + return value; + } + + console.error(`Unexpected locale key: ${String(p)}`); + + return p; + } + } + + return new Proxy(this.locale, new Handler()); + } + + return this.locale; + } + + public get tsx(): Tsx<T> { + if (_DEV_) { + if (this.tsxCache) { + return this.tsxCache; + } + + class Handler<TTarget extends ILocale> implements ProxyHandler<TTarget> { + get(target: TTarget, p: string | symbol): unknown { + const value = target[p as keyof TTarget]; + + if (typeof value === 'object') { + return new Proxy(value, new Handler<TTarget[keyof TTarget] & ILocale>()); + } + + if (typeof value === 'string') { + const quasis: string[] = []; + const expressions: string[] = []; + let cursor = 0; + + while (~cursor) { + const start = value.indexOf('{', cursor); + + if (!~start) { + quasis.push(value.slice(cursor)); + break; + } + + quasis.push(value.slice(cursor, start)); + + const end = value.indexOf('}', start); + + expressions.push(value.slice(start + 1, end)); + + cursor = end + 1; + } + + if (!expressions.length) { + console.error(`Unexpected locale key: ${String(p)}`); + + return () => value; + } + + return (arg) => { + let str = quasis[0]; + + for (let i = 0; i < expressions.length; i++) { + if (!Object.hasOwn(arg, expressions[i])) { + console.error(`Missing locale parameters: ${expressions[i]} at ${String(p)}`); + } + + str += arg[expressions[i]] + quasis[i + 1]; + } + + return str; + }; + } + + console.error(`Unexpected locale key: ${String(p)}`); + + return p; + } + } + + return this.tsxCache = new Proxy(this.locale, new Handler()) as unknown as Tsx<T>; + } + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (this.tsxCache) { + return this.tsxCache; + } + + function build(target: ILocale): Tsx<T> { + const result = {} as Tsx<T>; + + for (const k in target) { + if (!Object.hasOwn(target, k)) { + continue; + } + + const value = target[k as keyof typeof target]; + + if (typeof value === 'object') { + result[k] = build(value as ILocale); + } else if (typeof value === 'string') { + const quasis: string[] = []; + const expressions: string[] = []; + let cursor = 0; + + while (~cursor) { + const start = value.indexOf('{', cursor); + + if (!~start) { + quasis.push(value.slice(cursor)); + break; + } + + quasis.push(value.slice(cursor, start)); + + const end = value.indexOf('}', start); + + expressions.push(value.slice(start + 1, end)); + + cursor = end + 1; + } + + if (!expressions.length) { + continue; + } + + result[k] = (arg) => { + let str = quasis[0]; + + for (let i = 0; i < expressions.length; i++) { + str += arg[expressions[i]] + quasis[i + 1]; + } + + return str; + }; + } + } + return result; + } + + return this.tsxCache = build(this.locale); + } + + /** + * @deprecated ãªã‚‹ã¹ãã“ã®ãƒ¡ã‚½ãƒƒãƒ‰ä½¿ã†ã‚ˆã‚Šã‚‚ ts 直接å‚ç…§ã®æ–¹ãŒ vue ã®ã‚ャッシュ効ã„ã¦ãƒ‘フォーマンスãŒè‰¯ã„ã‹ã‚‚ + */ + public t<TKey extends FlattenKeys<T, string>>(key: TKey): string; + /** + * @deprecated ãªã‚‹ã¹ãã“ã®ãƒ¡ã‚½ãƒƒãƒ‰ä½¿ã†ã‚ˆã‚Šã‚‚ tsx 直接å‚ç…§ã®æ–¹ãŒ vue ã®ã‚ャッシュ効ã„ã¦ãƒ‘フォーマンスãŒè‰¯ã„ã‹ã‚‚ + */ + public t<TKey extends FlattenKeys<T, ParameterizedString>>(key: TKey, args: { readonly [_ in ParametersOf<T, TKey>]: string | number }): string; + public t(key: string, args?: { readonly [_: string]: string | number }) { + let str: string | ParameterizedString | ILocale = this.locale; + + for (const k of key.split('.')) { + str = str[k]; - if (args) { - for (const [k, v] of Object.entries(args)) { - str = str.replace(`{${k}}`, v.toString()); + if (_DEV_) { + if (typeof str === 'undefined') { + console.error(`Unexpected locale key: ${key}`); + return key; } } - return str; - } catch (err) { - console.warn(`missing localization '${key}'`); - return key; } + + if (args) { + if (_DEV_) { + const missing = Array.from((str as string).matchAll(/\{(\w+)\}/g), ([, parameter]) => parameter).filter(parameter => !Object.hasOwn(args, parameter)); + + if (missing.length) { + console.error(`Missing locale parameters: ${missing.join(', ')} at ${key}`); + } + } + + for (const [k, v] of Object.entries(args)) { + const search = `{${k}}`; + + if (_DEV_) { + if (!(str as string).includes(search)) { + console.error(`Unexpected locale parameter: ${k} at ${key}`); + } + } + + str = (str as string).replace(search, v.toString()); + } + } + + return str; } } + +if (import.meta.vitest) { + const { describe, expect, it } = import.meta.vitest; + + describe('i18n', () => { + it('t', () => { + const i18n = new I18n({ + foo: 'foo', + bar: { + baz: 'baz', + qux: 'qux {0}' as unknown as ParameterizedString<'0'>, + quux: 'quux {0} {1}' as unknown as ParameterizedString<'0' | '1'>, + }, + }); + + expect(i18n.t('foo')).toBe('foo'); + expect(i18n.t('bar.baz')).toBe('baz'); + expect(i18n.tsx.bar.qux({ 0: 'hoge' })).toBe('qux hoge'); + expect(i18n.tsx.bar.quux({ 0: 'hoge', 1: 'fuga' })).toBe('quux hoge fuga'); + }); + it('ts', () => { + const i18n = new I18n({ + foo: 'foo', + bar: { + baz: 'baz', + qux: 'qux {0}' as unknown as ParameterizedString<'0'>, + quux: 'quux {0} {1}' as unknown as ParameterizedString<'0' | '1'>, + }, + }); + + expect(i18n.ts.foo).toBe('foo'); + expect(i18n.ts.bar.baz).toBe('baz'); + }); + it('tsx', () => { + const i18n = new I18n({ + foo: 'foo', + bar: { + baz: 'baz', + qux: 'qux {0}' as unknown as ParameterizedString<'0'>, + quux: 'quux {0} {1}' as unknown as ParameterizedString<'0' | '1'>, + }, + }); + + expect(i18n.tsx.bar.qux({ 0: 'hoge' })).toBe('qux hoge'); + expect(i18n.tsx.bar.quux({ 0: 'hoge', 1: 'fuga' })).toBe('quux hoge fuga'); + }); + }); +} diff --git a/packages/frontend/src/scripts/idb-proxy.ts b/packages/frontend/src/scripts/idb-proxy.ts index a20cfcb1d0c8f6f29d2a2e05e25c67f293b42ca9..1ca0990ba9e2c3e2f564ab60e1ddb48cef59c00f 100644 --- a/packages/frontend/src/scripts/idb-proxy.ts +++ b/packages/frontend/src/scripts/idb-proxy.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/idle-render.ts b/packages/frontend/src/scripts/idle-render.ts index ac1be50c739f0f92e8f1465097d1e258ddb22dc0..6adfedcb9f3b626c67f381ff77090242b7d9b823 100644 --- a/packages/frontend/src/scripts/idle-render.ts +++ b/packages/frontend/src/scripts/idle-render.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/init-chart.ts b/packages/frontend/src/scripts/init-chart.ts index ebf27667d7550f0dd74ffd9516a50db747682927..2465a14703dd528effd57e2ae677a3bb80d9a5fa 100644 --- a/packages/frontend/src/scripts/init-chart.ts +++ b/packages/frontend/src/scripts/init-chart.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/initialize-sw.ts b/packages/frontend/src/scripts/initialize-sw.ts index 007fc0f2f7e08c915c581fba5ea71e0ff1caf1a6..1517e4e1e8fa7be0ea821ce84f7d2af2d77380bb 100644 --- a/packages/frontend/src/scripts/initialize-sw.ts +++ b/packages/frontend/src/scripts/initialize-sw.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/install-plugin.ts b/packages/frontend/src/scripts/install-plugin.ts index 1310a0dc7310d316195333db2644e972cbcfdbed..15b0cedc79feddb1e646ea6e30ac6caa6e274726 100644 --- a/packages/frontend/src/scripts/install-plugin.ts +++ b/packages/frontend/src/scripts/install-plugin.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -10,6 +10,7 @@ import { Interpreter, Parser, utils } from '@syuilo/aiscript'; import type { Plugin } from '@/store.js'; import { ColdDeviceStorage } from '@/store.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; export type AiScriptPluginMeta = { @@ -63,7 +64,11 @@ export async function parsePluginMeta(code: string): Promise<AiScriptPluginMeta> try { ast = parser.parse(code); } catch (err) { - throw new Error('Aiscript syntax error'); + if (err instanceof Error) { + throw new Error(`Aiscript syntax error\n${(err as Error).message}`); + } else { + throw new Error('Aiscript syntax error'); + } } const meta = Interpreter.collectMetadata(ast); @@ -110,7 +115,7 @@ export async function installPlugin(code: string, meta?: AiScriptPluginMeta) { }, { done: async result => { const { name, permissions } = result; - const { token } = await os.api('miauth/gen-token', { + const { token } = await misskeyApi('miauth/gen-token', { session: null, name: name, permission: permissions, diff --git a/packages/frontend/src/scripts/install-theme.ts b/packages/frontend/src/scripts/install-theme.ts index 394b642bf4955ee9293b45793ea892041cd73d80..866f1225bf7e75c5a0f70243e89d94a4c9a958bc 100644 --- a/packages/frontend/src/scripts/install-theme.ts +++ b/packages/frontend/src/scripts/install-theme.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/intl-const.ts b/packages/frontend/src/scripts/intl-const.ts index ea16c9c2aec275fd111b3c4f4324d82638dde333..aaa4f0a86edf4898662d1a3f6caeaea88f057c4c 100644 --- a/packages/frontend/src/scripts/intl-const.ts +++ b/packages/frontend/src/scripts/intl-const.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -33,6 +33,10 @@ try { } export const dateTimeFormat = _dateTimeFormat; +export const timeZone = dateTimeFormat.resolvedOptions().timeZone; + +export const hemisphere = /^(australia|pacific|antarctica|indian)\//i.test(timeZone) ? 'S' : 'N'; + let _numberFormat: Intl.NumberFormat; try { _numberFormat = new Intl.NumberFormat(versatileLang); diff --git a/packages/frontend/src/scripts/is-device-darkmode.ts b/packages/frontend/src/scripts/is-device-darkmode.ts index badc295726bc7f90fb57fc6d4992750f91a86d9e..4f487c7cb9907912e60ddd9b076143336c786d14 100644 --- a/packages/frontend/src/scripts/is-device-darkmode.ts +++ b/packages/frontend/src/scripts/is-device-darkmode.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/isFfVisibleForMe.ts b/packages/frontend/src/scripts/isFfVisibleForMe.ts index dc0e90d20a5629dd8bc700418fafb68c42887073..406404c4622c940bdd0670df2962f6f872659bbb 100644 --- a/packages/frontend/src/scripts/isFfVisibleForMe.ts +++ b/packages/frontend/src/scripts/isFfVisibleForMe.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/keycode.ts b/packages/frontend/src/scripts/keycode.ts index 57bc4d19ba01b6969d735bc681e6f42d336e9cc0..bc1f485f5ee23eb42195ebc5bbc9666ab0b893b0 100644 --- a/packages/frontend/src/scripts/keycode.ts +++ b/packages/frontend/src/scripts/keycode.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/langmap.ts b/packages/frontend/src/scripts/langmap.ts index 3912d58d8205d3aecef8bdb32cff1617a73abff7..b32de1596378a772cbca4cd124dbfe53b073152a 100644 --- a/packages/frontend/src/scripts/langmap.ts +++ b/packages/frontend/src/scripts/langmap.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/login-id.ts b/packages/frontend/src/scripts/login-id.ts index fe0e17e66ea806285a32a3963006e460e992c149..b52735caa0dbe051c38ea57b668f86040c2db83b 100644 --- a/packages/frontend/src/scripts/login-id.ts +++ b/packages/frontend/src/scripts/login-id.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/lookup-user.ts b/packages/frontend/src/scripts/lookup-user.ts index a35fe898e4ecc641f9b4225ada94156f97be5ad4..efc9132e757c1bfd6b83064888e56aed88ab8104 100644 --- a/packages/frontend/src/scripts/lookup-user.ts +++ b/packages/frontend/src/scripts/lookup-user.ts @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import * as Misskey from 'misskey-js'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; export async function lookupUser() { const { canceled, result } = await os.inputText({ @@ -17,8 +18,8 @@ export async function lookupUser() { os.pageWindow(`/admin/user/${user.id}`); }; - const usernamePromise = os.api('users/show', Misskey.acct.parse(result)); - const idPromise = os.api('users/show', { userId: result }); + const usernamePromise = misskeyApi('users/show', Misskey.acct.parse(result)); + const idPromise = misskeyApi('users/show', { userId: result }); let _notFound = false; const notFound = () => { if (_notFound) { diff --git a/packages/frontend/src/scripts/lookup.ts b/packages/frontend/src/scripts/lookup.ts index 979f40f03872b5a3dcb53888723a566b92612544..7f020b15cc2bd7c220b4fe0feb66a7f0f173d502 100644 --- a/packages/frontend/src/scripts/lookup.ts +++ b/packages/frontend/src/scripts/lookup.ts @@ -1,12 +1,13 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; -import { mainRouter } from '@/router.js'; import { Router } from '@/nirax.js'; +import { mainRouter } from '@/router/main.js'; export async function lookup(router?: Router) { const _router = router ?? mainRouter; @@ -28,7 +29,7 @@ export async function lookup(router?: Router) { } if (query.startsWith('https://')) { - const promise = os.api('ap/show', { + const promise = misskeyApi('ap/show', { uri: query, }); diff --git a/packages/frontend/src/scripts/media-proxy.ts b/packages/frontend/src/scripts/media-proxy.ts index 559e61211db0865beeca0d18c1e5ae40ddd5ca55..099a22163af44a0e8c2e6c3444bda13e2706fada 100644 --- a/packages/frontend/src/scripts/media-proxy.ts +++ b/packages/frontend/src/scripts/media-proxy.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/merge.ts b/packages/frontend/src/scripts/merge.ts new file mode 100644 index 0000000000000000000000000000000000000000..4e39a0fa06ec8b11fc10634eb32fd4e1a02c3801 --- /dev/null +++ b/packages/frontend/src/scripts/merge.ts @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { deepClone } from './clone.js'; +import type { Cloneable } from './clone.js'; + +type DeepPartial<T> = { + [P in keyof T]?: T[P] extends Record<string | number | symbol, unknown> ? DeepPartial<T[P]> : T[P]; +}; + +function isPureObject(value: unknown): value is Record<string | number | symbol, unknown> { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +/** + * valueã«ãªã„ã‚ーをdefã‹ã‚‰ã‚‚らã†ï¼ˆå†å¸°çš„)\ + * nullã¯ãã®ã¾ã¾ã€undefinedã¯defã®å€¤ + **/ +export function deepMerge<X extends Record<string | number | symbol, unknown>>(value: DeepPartial<X>, def: X): X { + if (isPureObject(value) && isPureObject(def)) { + const result = deepClone(value as Cloneable) as X; + for (const [k, v] of Object.entries(def) as [keyof X, X[keyof X]][]) { + if (!Object.prototype.hasOwnProperty.call(value, k) || value[k] === undefined) { + result[k] = v; + } else if (isPureObject(v) && isPureObject(result[k])) { + const child = deepClone(result[k] as Cloneable) as DeepPartial<X[keyof X] & Record<string | number | symbol, unknown>>; + result[k] = deepMerge<typeof v>(child, v); + } + } + return result; + } + throw new Error('deepMerge: value and def must be pure objects'); +} diff --git a/packages/frontend/src/scripts/mfm-function-picker.ts b/packages/frontend/src/scripts/mfm-function-picker.ts index 6e25cc856ce537aef4d73eff3d75165a20299f6d..36de146c27c355eba787fb9357dd21c9663f4fae 100644 --- a/packages/frontend/src/scripts/mfm-function-picker.ts +++ b/packages/frontend/src/scripts/mfm-function-picker.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/api.ts b/packages/frontend/src/scripts/misskey-api.ts similarity index 68% rename from packages/frontend/src/scripts/api.ts rename to packages/frontend/src/scripts/misskey-api.ts index 8f3a163938b1897828b598002e432268399eedfb..49fb6f9e59c670f6ac0d7972b55fee911ce4ca35 100644 --- a/packages/frontend/src/scripts/api.ts +++ b/packages/frontend/src/scripts/misskey-api.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -10,12 +10,17 @@ import { $i } from '@/account.js'; export const pendingApiRequestsCount = ref(0); // Implements Misskey.api.ApiClient.request -export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>( +export function misskeyApi< + ResT = void, + E extends keyof Misskey.Endpoints = keyof Misskey.Endpoints, + P extends Misskey.Endpoints[E]['req'] = Misskey.Endpoints[E]['req'], + _ResT = ResT extends void ? Misskey.api.SwitchCaseResponseType<E, P> : ResT, +>( endpoint: E, data: P = {} as any, token?: string | null | undefined, signal?: AbortSignal, -): Promise<Misskey.api.SwitchCaseResponseType<E, P>> { +): Promise<_ResT> { if (endpoint.includes('://')) throw new Error('invalid endpoint'); pendingApiRequestsCount.value++; @@ -23,7 +28,7 @@ export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoin pendingApiRequestsCount.value--; }; - const promise = new Promise<Misskey.Endpoints[E]['res'] | void>((resolve, reject) => { + const promise = new Promise<_ResT>((resolve, reject) => { // Append a credential if ($i) (data as any).i = $i.token; if (token !== undefined) (data as any).i = token; @@ -44,7 +49,7 @@ export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoin if (res.status === 200) { resolve(body); } else if (res.status === 204) { - resolve(); + resolve(undefined as _ResT); // void -> undefined } else { reject(body.error); } @@ -57,10 +62,15 @@ export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoin } // Implements Misskey.api.ApiClient.request -export function apiGet<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>( +export function misskeyApiGet< + ResT = void, + E extends keyof Misskey.Endpoints = keyof Misskey.Endpoints, + P extends Misskey.Endpoints[E]['req'] = Misskey.Endpoints[E]['req'], + _ResT = ResT extends void ? Misskey.api.SwitchCaseResponseType<E, P> : ResT, +>( endpoint: E, data: P = {} as any, -): Promise<Misskey.api.SwitchCaseResponseType<E, P>> { +): Promise<_ResT> { pendingApiRequestsCount.value++; const onFinally = () => { @@ -69,7 +79,7 @@ export function apiGet<E extends keyof Misskey.Endpoints, P extends Misskey.Endp const query = new URLSearchParams(data as any); - const promise = new Promise<Misskey.Endpoints[E]['res'] | void>((resolve, reject) => { + const promise = new Promise<_ResT>((resolve, reject) => { // Send request window.fetch(`${apiUrl}/${endpoint}?${query}`, { method: 'GET', @@ -81,7 +91,7 @@ export function apiGet<E extends keyof Misskey.Endpoints, P extends Misskey.Endp if (res.status === 200) { resolve(body); } else if (res.status === 204) { - resolve(); + resolve(undefined as _ResT); // void -> undefined } else { reject(body.error); } diff --git a/packages/frontend/src/scripts/navigator.ts b/packages/frontend/src/scripts/navigator.ts index b13186a10ee4b1aa254c8a513d2d6a95e2b42f81..ffc0a457f418fcab1f7e8ce4f177723bd1888783 100644 --- a/packages/frontend/src/scripts/navigator.ts +++ b/packages/frontend/src/scripts/navigator.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/nyaize.ts b/packages/frontend/src/scripts/nyaize.ts index 62833b4de3e8ec8fd0deb3ee87c02be790363247..58ed88fed15869d3646a2ace1df89ee025e165ce 100644 --- a/packages/frontend/src/scripts/nyaize.ts +++ b/packages/frontend/src/scripts/nyaize.ts @@ -1,23 +1,28 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -const enRegex1 = /(?<=n)a/gi; -const enRegex2 = /(?<=morn)ing/gi; -const enRegex3 = /(?<=every)one/gi; const koRegex1 = /[나-낳]/g; const koRegex2 = /(다$)|(다(?=\.))|(다(?= ))|(다(?=!))|(다(?=\?))/gm; const koRegex3 = /(야(?=\?))|(야$)|(야(?= ))/gm; +function ifAfter(prefix, fn) { + const preLen = prefix.length; + const regex = new RegExp(prefix,'i'); + return (x,pos,string) => { + return pos > 0 && string.substring(pos-preLen,pos).match(regex) ? fn(x) : x; + }; +} + export function nyaize(text: string): string { return text // ja-JP .replaceAll('ãª', 'ã«ã‚ƒ').replaceAll('ナ', 'ニャ').replaceAll('ï¾…', 'ニャ') // en-US - .replace(enRegex1, x => x === 'A' ? 'YA' : 'ya') - .replace(enRegex2, x => x === 'ING' ? 'YAN' : 'yan') - .replace(enRegex3, x => x === 'ONE' ? 'NYAN' : 'nyan') + .replace(/a/gi, ifAfter('n', x => x === 'A' ? 'YA' : 'ya')) + .replace(/ing/gi, ifAfter('morn', x => x === 'ING' ? 'YAN' : 'yan')) + .replace(/one/gi, ifAfter('every', x => x === 'ONE' ? 'NYAN' : 'nyan')) // ko-KR .replace(koRegex1, match => String.fromCharCode( match.charCodeAt(0)! + 'ëƒ'.charCodeAt(0) - '나'.charCodeAt(0), diff --git a/packages/frontend/src/scripts/page-metadata.ts b/packages/frontend/src/scripts/page-metadata.ts index 369e46aae1693dd9a04e7671efd4fb849f3a4d97..0e3b093ecf957769b5eec28ab116dbc53acb48bb 100644 --- a/packages/frontend/src/scripts/page-metadata.ts +++ b/packages/frontend/src/scripts/page-metadata.ts @@ -1,13 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import * as Misskey from 'misskey-js'; -import { ComputedRef, inject, isRef, onActivated, onMounted, provide, ref, Ref } from 'vue'; - -export const setPageMetadata = Symbol('setPageMetadata'); -export const pageMetadataProvider = Symbol('pageMetadataProvider'); +import { MaybeRefOrGetter, Ref, inject, isRef, onActivated, onBeforeUnmount, provide, ref, toValue, watch } from 'vue'; export type PageMetadata = { title: string; @@ -18,29 +15,56 @@ export type PageMetadata = { needWideArea?: boolean; }; -export function definePageMetadata(metadata: PageMetadata | null | Ref<PageMetadata | null> | ComputedRef<PageMetadata | null>): void { - const _metadata = isRef(metadata) ? metadata : ref(metadata); +type PageMetadataGetter = () => PageMetadata; +type PageMetadataReceiver = (getter: PageMetadataGetter) => void; - provide(pageMetadataProvider, _metadata); +const RECEIVER_KEY = Symbol('ReceiverKey'); +const setReceiver = (v: PageMetadataReceiver): void => { + provide<PageMetadataReceiver>(RECEIVER_KEY, v); +}; +const getReceiver = (): PageMetadataReceiver | undefined => { + return inject<PageMetadataReceiver>(RECEIVER_KEY); +}; - const set = inject(setPageMetadata) as any; - if (set) { - set(_metadata); +const METADATA_KEY = Symbol('MetadataKey'); +const setMetadata = (v: Ref<PageMetadata | null>): void => { + provide<Ref<PageMetadata | null>>(METADATA_KEY, v); +}; +const getMetadata = (): Ref<PageMetadata | null> | undefined => { + return inject<Ref<PageMetadata | null>>(METADATA_KEY); +}; - onMounted(() => { - set(_metadata); - }); +export const definePageMetadata = (maybeRefOrGetterMetadata: MaybeRefOrGetter<PageMetadata>): void => { + const metadataRef = ref(toValue(maybeRefOrGetterMetadata)); + const metadataGetter = () => metadataRef.value; + const receiver = getReceiver(); - onActivated(() => { - set(_metadata); - }); - } -} + // setup handler + receiver?.(metadataGetter); -export function provideMetadataReceiver(callback: (info: ComputedRef<PageMetadata>) => void): void { - provide(setPageMetadata, callback); -} + // update handler + onBeforeUnmount(watch( + () => toValue(maybeRefOrGetterMetadata), + (metadata) => { + metadataRef.value = metadata; + receiver?.(metadataGetter); + }, + { deep: true }, + )); + onActivated(() => { + receiver?.(metadataGetter); + }); +}; -export function injectPageMetadata(): PageMetadata | undefined { - return inject(pageMetadataProvider); -} +export const provideMetadataReceiver = (receiver: PageMetadataReceiver): void => { + setReceiver(receiver); +}; + +export const provideReactiveMetadata = (metadataRef: Ref<PageMetadata | null>): void => { + setMetadata(metadataRef); +}; + +export const injectReactiveMetadata = (): Ref<PageMetadata | null> => { + const metadataRef = getMetadata(); + return isRef(metadataRef) ? metadataRef : ref(null); +}; diff --git a/packages/frontend/src/scripts/physics.ts b/packages/frontend/src/scripts/physics.ts index cf9fad70ebc2538f8a3deb7065a3225f162a0593..8a4e9319b3ec34a9a6296f112c7120206777b220 100644 --- a/packages/frontend/src/scripts/physics.ts +++ b/packages/frontend/src/scripts/physics.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/please-login.ts b/packages/frontend/src/scripts/please-login.ts index e6c08dfbc0d8fe728e6b3bd0aa21373c1d95c39a..9e512727912bad0eeea90aba3a0b5febe5315172 100644 --- a/packages/frontend/src/scripts/please-login.ts +++ b/packages/frontend/src/scripts/please-login.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/popout.ts b/packages/frontend/src/scripts/popout.ts index 0c2ff169928973b65c0e629868525426b8ecd85d..1caa2dfc21017074e4233f2176e312e94d6a5f3a 100644 --- a/packages/frontend/src/scripts/popout.ts +++ b/packages/frontend/src/scripts/popout.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/popup-position.ts b/packages/frontend/src/scripts/popup-position.ts index 0a799c566558b1255ad3731d7ae67eb5def31738..8c9e3c02c36d8b2cca65a82201d48f5494cc96b2 100644 --- a/packages/frontend/src/scripts/popup-position.ts +++ b/packages/frontend/src/scripts/popup-position.ts @@ -1,10 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ export function calcPopupPosition(el: HTMLElement, props: { - anchorElement: HTMLElement | null; + anchorElement?: HTMLElement | null; innerMargin: number; direction: 'top' | 'bottom' | 'left' | 'right'; align: 'top' | 'bottom' | 'left' | 'right' | 'center'; diff --git a/packages/frontend/src/scripts/post-message.ts b/packages/frontend/src/scripts/post-message.ts index 80441caf157c8b87349ed012d9df358b9723f5f6..31a9ac1ad9dca54f33892827884254d48bee010b 100644 --- a/packages/frontend/src/scripts/post-message.ts +++ b/packages/frontend/src/scripts/post-message.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/reaction-picker.ts b/packages/frontend/src/scripts/reaction-picker.ts index 9b13e794f5c273e3615671658067b5659d2c5938..7aec05c0cf6437e0360e7ae430a3d3e3b48c20f1 100644 --- a/packages/frontend/src/scripts/reaction-picker.ts +++ b/packages/frontend/src/scripts/reaction-picker.ts @@ -1,8 +1,9 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ +import * as Misskey from 'misskey-js'; import { defineAsyncComponent, Ref, ref } from 'vue'; import { popup } from '@/os.js'; import { defaultStore } from '@/store.js'; @@ -10,6 +11,7 @@ import { defaultStore } from '@/store.js'; class ReactionPicker { private src: Ref<HTMLElement | null> = ref(null); private manualShowing = ref(false); + private targetNote: Ref<Misskey.entities.Note | null> = ref(null); private onChosen?: (reaction: string) => void; private onClosed?: () => void; @@ -23,6 +25,7 @@ class ReactionPicker { src: this.src, pinnedEmojis: reactionsRef, asReactionPicker: true, + targetNote: this.targetNote, manualShowing: this.manualShowing, }, { done: reaction => { @@ -38,8 +41,9 @@ class ReactionPicker { }); } - public show(src: HTMLElement, onChosen?: ReactionPicker['onChosen'], onClosed?: ReactionPicker['onClosed']) { + public show(src: HTMLElement | null, targetNote: Misskey.entities.Note | null, onChosen?: ReactionPicker['onChosen'], onClosed?: ReactionPicker['onClosed']) { this.src.value = src; + this.targetNote.value = targetNote; this.manualShowing.value = true; this.onChosen = onChosen; this.onClosed = onClosed; diff --git a/packages/frontend/src/scripts/safe-parse.ts b/packages/frontend/src/scripts/safe-parse.ts new file mode 100644 index 0000000000000000000000000000000000000000..6bfcef6c362c208d4b3dbc4cbb3eb8a84e9c73d7 --- /dev/null +++ b/packages/frontend/src/scripts/safe-parse.ts @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export function safeParseFloat(str: unknown): number | null { + if (typeof str !== 'string' || str === '') return null; + const num = parseFloat(str); + if (isNaN(num)) return null; + return num; +} diff --git a/packages/frontend/src/scripts/safe-uri-decode.ts b/packages/frontend/src/scripts/safe-uri-decode.ts index 625d8c34a77dbc7848e485f1007138cd998b585e..0edf4e9eba0f32f6bded54812f4a71afb74a1379 100644 --- a/packages/frontend/src/scripts/safe-uri-decode.ts +++ b/packages/frontend/src/scripts/safe-uri-decode.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/scroll.ts b/packages/frontend/src/scripts/scroll.ts index 1f626e4c0dfd70f45650e07d38d5ca7007675cfa..8edb6fca05391389ebb2a45dc0bbf8e3fdc640a9 100644 --- a/packages/frontend/src/scripts/scroll.ts +++ b/packages/frontend/src/scripts/scroll.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/search-emoji.ts b/packages/frontend/src/scripts/search-emoji.ts new file mode 100644 index 0000000000000000000000000000000000000000..4192a2df8fa65031df87f56f8b97553826c4411d --- /dev/null +++ b/packages/frontend/src/scripts/search-emoji.ts @@ -0,0 +1,106 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export type EmojiDef = { + emoji: string; + name: string; + url: string; + aliasOf?: string; +} | { + emoji: string; + name: string; + aliasOf?: string; + isCustomEmoji?: true; +}; +type EmojiScore = { emoji: EmojiDef, score: number }; + +export function searchEmoji(query: string | null, emojiDb: EmojiDef[], max = 30): EmojiDef[] { + if (!query) { + return []; + } + + const matched = new Map<string, EmojiScore>(); + // 完全一致(エイリアスãªã—) + emojiDb.some(x => { + if (x.name.toLowerCase() === query && !x.aliasOf) { + matched.set(x.name, { emoji: x, score: query.length + 3 }); + } + return matched.size === max; + }); + + // 完全一致(エイリアス込ã¿ï¼‰ + if (matched.size < max) { + emojiDb.some(x => { + if (x.name.toLowerCase() === query && !matched.has(x.aliasOf ?? x.name)) { + matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length + 2 }); + } + return matched.size === max; + }); + } + + // å‰æ–¹ä¸€è‡´ï¼ˆã‚¨ã‚¤ãƒªã‚¢ã‚¹ãªã—) + if (matched.size < max) { + emojiDb.some(x => { + if (x.name.toLowerCase().startsWith(query) && !x.aliasOf && !matched.has(x.name)) { + matched.set(x.name, { emoji: x, score: query.length + 1 }); + } + return matched.size === max; + }); + } + + // å‰æ–¹ä¸€è‡´ï¼ˆã‚¨ã‚¤ãƒªã‚¢ã‚¹è¾¼ã¿ï¼‰ + if (matched.size < max) { + emojiDb.some(x => { + if (x.name.toLowerCase().startsWith(query) && !matched.has(x.aliasOf ?? x.name)) { + matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length }); + } + return matched.size === max; + }); + } + + // 部分一致(エイリアス込ã¿ï¼‰ + if (matched.size < max) { + emojiDb.some(x => { + if (x.name.toLowerCase().includes(query) && !matched.has(x.aliasOf ?? x.name)) { + matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length - 1 }); + } + return matched.size === max; + }); + } + + // 簡易ã‚ã„ã¾ã„検索(3æ–‡å—以上) + if (matched.size < max && query.length > 3) { + const queryChars = [...query]; + const hitEmojis = new Map<string, EmojiScore>(); + + for (const x of emojiDb) { + // æ–‡å—列ã®ä½ç½®ã‚’進ã‚ãªãŒã‚‰ã€ã‚¯ã‚¨ãƒªã®æ–‡å—ã‚’é †ç•ªã«æŽ¢ã™ + + let pos = 0; + let hit = 0; + for (const c of queryChars) { + pos = x.name.indexOf(c, pos); + if (pos <= -1) break; + hit++; + } + + // åŠåˆ†ä»¥ä¸Šã®æ–‡å—ãŒå«ã¾ã‚Œã¦ã„ã‚Œã°ãƒ’ットã¨ã™ã‚‹ + if (hit > Math.ceil(queryChars.length / 2) && hit - 2 > (matched.get(x.aliasOf ?? x.name)?.score ?? 0)) { + hitEmojis.set(x.aliasOf ?? x.name, { emoji: x, score: hit - 2 }); + } + } + + // ヒットã—ãŸã‚‚ã®ã‚’å…¨éƒ¨è¿½åŠ ã™ã‚‹ã¨é›‘多ã«ãªã‚‹ã®ã§ã€å…ˆé ã®6件程度ã ã‘ã«ã—ã¦ãŠã(6件ï¼ã‚ªãƒ¼ãƒˆã‚³ãƒ³ãƒ—リートã®ãƒãƒƒãƒ—アップã®ã‚µã‚¤ã‚ºåˆ†ï¼‰ + [...hitEmojis.values()] + .sort((x, y) => y.score - x.score) + .slice(0, 6) + .forEach(it => matched.set(it.emoji.name, it)); + } + + return [...matched.values()] + .sort((x, y) => y.score - x.score) + .slice(0, max) + .map(it => it.emoji); +} diff --git a/packages/frontend/src/scripts/select-file.ts b/packages/frontend/src/scripts/select-file.ts index 674c762fac1255bbb0d5fe5ed065fc294453cffc..fd7cfc697b9910cc4cbf3225d159340eadbc4c74 100644 --- a/packages/frontend/src/scripts/select-file.ts +++ b/packages/frontend/src/scripts/select-file.ts @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { useStream } from '@/stream.js'; import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; @@ -65,7 +66,7 @@ export function chooseFileFromUrl(): Promise<Misskey.entities.DriveFile> { } }); - os.api('drive/files/upload-from-url', { + misskeyApi('drive/files/upload-from-url', { url: url, folderId: defaultStore.state.uploadFolder, marker, diff --git a/packages/frontend/src/scripts/show-moved-dialog.ts b/packages/frontend/src/scripts/show-moved-dialog.ts index b4defbfe7d3d0d9df48c2e54e896259caa97b57e..35b3ef79d8a672f5f208a55985ca1d43b7cd40da 100644 --- a/packages/frontend/src/scripts/show-moved-dialog.ts +++ b/packages/frontend/src/scripts/show-moved-dialog.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/show-suspended-dialog.ts b/packages/frontend/src/scripts/show-suspended-dialog.ts index a2fd5db45312e826a340466f2e66b73cb4f353b9..8b89dbb93649524029deaef59f8384d3482eb29b 100644 --- a/packages/frontend/src/scripts/show-suspended-dialog.ts +++ b/packages/frontend/src/scripts/show-suspended-dialog.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/shuffle.ts b/packages/frontend/src/scripts/shuffle.ts index d9d5bb10373358cdd8f930c13822b51b64abdfb5..fed16bc71c6a4de8a72274ba9cd8344ca88a0f38 100644 --- a/packages/frontend/src/scripts/shuffle.ts +++ b/packages/frontend/src/scripts/shuffle.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/snowfall-effect.ts b/packages/frontend/src/scripts/snowfall-effect.ts index a09f02cec08243cad9e47a00791fd9b695df5678..11fcaa07163d64ac6d7f365489d727802c7a2409 100644 --- a/packages/frontend/src/scripts/snowfall-effect.ts +++ b/packages/frontend/src/scripts/snowfall-effect.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -17,20 +17,20 @@ export class SnowfallEffect { uniform vec3 u_worldSize; uniform float u_gravity; uniform float u_wind; + uniform float u_spin_factor; + uniform float u_turbulence; void main() { v_color = a_color; - v_rotation = a_rotation.x + u_time * a_rotation.y; + v_rotation = a_rotation.x + (u_time * u_spin_factor) * a_rotation.y; vec3 pos = a_position.xyz; - float turbulence = 1.0; - pos.x = mod(pos.x + u_time + u_wind * a_speed.x, u_worldSize.x * 2.0) - u_worldSize.x; pos.y = mod(pos.y - u_time * a_speed.y * u_gravity, u_worldSize.y * 2.0) - u_worldSize.y; - pos.x += sin(u_time * a_speed.z * turbulence) * a_rotation.z; - pos.z += cos(u_time * a_speed.z * turbulence) * a_rotation.z; + pos.x += sin(u_time * a_speed.z * u_turbulence) * a_rotation.z; + pos.z += cos(u_time * a_speed.z * u_turbulence) * a_rotation.z; gl_Position = u_projection * vec4(pos.xyz, a_position.w); gl_PointSize = (a_size / gl_Position.w) * 100.0; @@ -105,6 +105,7 @@ export class SnowfallEffect { private opacity = 1; private size = 4; private snowflake = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAVlpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KTMInWQAAErRJREFUeAHdmgnYlmPax5MShaxRKRElPmXJXpaSsRxDU0bTZ+kt65RloiRDltEMQsxYKmS+zzYjxCCamCzV2LchResMIxFRQ1G93+93Pdf5dL9v7zuf4/hm0fc/jt9znddy3/e1nNd53c/7vHXq/AtVWVnZA/bzkaQjoWG298DeMdvrmP6/EIOqC4fBsbAx7Arz4TaYBPXgWVDnO2jSBrB2T0IMIA9mCmmoE8aonPkR6WPZHlp9xSlfeyeBzq9bHBD5feEdUGfDXBgBqnde+a2wvw/dYdNctvZNAp1PnTaFttA6JgP7eVgBM0CNzgO9HNvy0AcYDda6SaDTdXOnz8X+IkZDugAGQmOYA+ob6Ah/MIOMDRPhJjgJ6uV7pXtWt81/50SnY/Wvwn4ZDHAvwJ9ATYcxyaqsnEnqZCyCPaE80BgYZXG/5A3VyyP/b08LHa11z9KmFUwA5eqruRBHYX1s8WSI1Xcbme8Mt8PWUCU+kF8XbFN+dtH+p06OD4IU8EjD/VOZ5bnezq0XHcHuC2oV7BDlkVIWq56uIX8UjAO31GRIMYW0Vo/xXtSXJyTuXVO6xk1qalRTmQ9AfqzEvog2XYpllnsd6Qr4unCPT7NtByu0uU7vuAaOoy1JuvfXpJdTvSX0gI1gCXwGZdFmEFxoQb7Wid8s7lNu+I8wuHGsTqz2zpQ9DAa5R6HC55A2gvCMXthvwi25bjx26H0M9/9f4Rnok9s0zulFlC2HzzP9cnld8nH/p7DVrbmuIfYs6JLz9U3/z+KGadDeCDsmwre7GyEifn/su8HVSsL2HeBn8CK8AW+B7u9R5yrPgyOjvSn5DWAaXAG2UU7CE9Ayt4k4sR1lX4LaLdd9gn2ftsL+Vtuh1Dp/elH1C8lvCdUj8kDK3gbP8XdhCnSC86rcsNSR9pQvhc/gVlB9bUfqoFNAy/mLrUROrpMwCtpBxBbTtLqkF4K6IF9rf57I9pnYekx5AS0P1VhopXso9pR5buC7+kewU86nFcB+BT4EXdIvNO73sRBubGTXLZtTtgp+DEb++bACdqBuJOlAaMMzLVM3whegNznQDtCb+pW5b8YY76euB5+7pxm0IbzCfS8m3Zf2q4T8/+4JNArXGoptpxz8LqDmQJq0Qnostt/sfIn5GygD4/Zeq7B7wljQO2yjB/QGj0Pjxz4wGdqXrkjXtCT/ISyDa6EPpHrSraFjvnecFpMoMx40Br3xSlD262rYObevddHTs2kYwWUG9uP5It/f1eU5Xw9btwoXPALbwYXcg+unG/KB3Rq8n9ddAOpn4Kr8BAaBcltcDo9D7Ouavig1o34x7F94xqPk74eLQH0MH8HvwS3SLPe9iheEG6f70KiuLpZv6sxG/Va5bFJOabaO7ucAvGEbeAH+AN1hV7iDOidQFz4A2oJb6D1YDhXZHkTqpL8EbqHDYRtwW20AsdIb8syl5N2e6dTAPB2mWYa+hE4Qk7I59iMwFZ70GlJlfyuTVfygs7Hyw7HbwI0w3Tak14BqEtdg7wVdIx8pZbtBUbrjZeA3vUPBANkU+sEehev8O4Db6QpwYm+D8II0KPKHwUFeQ3oLDIMN4WgID1yOPQ+MAXMhNAtju3ztmtuAypiAw7EXwo/Am+0NfUG5mknYc6GfGVIjsoFNuyuoh8COuDcd2LmwA9jWE8bB3Q7N4XrwWAz5XOXR+Tx4n6FgdHeB6sF/w2QwhlSXdXvl/jixx4NH8GW5LDzb7GrR4ES4F5QddB99CieAwStOAPegdUZ2B71F3AXbQSn3vJ1bYaYWrayh3NUPTcbYFExVW3CfXwlvgfoavMbnDAY9dxGo6dCt0LeaB54H4UydDEPA2R4PDlrFLB9XuNmTlO+Xr7X9ZNBr9J4+EN8AMcv6ButpMND9FM6EnTOHkLrSnvtzwbbq3vwMB2ow/qWFSC8ZC++ZQaldbquH2afQWbl8TdcvVtC6LtipifAuOKt6gA9Tzqgzb5R2gP1hX3DVtZVHVvdklY5DA5beIkVPuZn8LOgAnWEfeAaUkxCan/voBNkfF+U5cFu5z5XlxZU20OmZtgm1K45VO4naNCukrcBZVk/CD+E/YBjoYjXJY8Zg9DxsDrbbBHTRotxOrug4eBs+hHgWZtKzfHrdXHBi9gDvqzxFHNA5KVfyBCf0ExgB7nkXStLLEKkniNf0AzUs5+ublkVFKiC9FBZAvGxshT0NnN3zoSUYSJQPcjAvm0HmjcIPemNS96F6E36drFLwugx7EEzNZV/l9IjoEPkW4B7eFtYH9QKcBcfA/aCWgpPQOT+zMbb9fS3nDbYR2MdgV0S5aVlUhLs0w45IHi7sqnnGJ2E7CXqHWgZXgJ1y8KqpDUmfSLmSV5yB/XrpDqVP8ofmehNdOv7I0ShfP4yyJdl2a4SchI1gCXgkHgljYfvc1i3cs/SU1A9jQRpfri/b0Sal1RrtSj4ULyHprY5C6+6E1+EBULq0E+DK7A96iwqX0z4td8B3dCdob5gD3UB3j9fUcNuDKFOvgc+bZAZFf4Zgu/q/AGPMgfm+5ShPWay+k6I31BwAvVDRYL2cuqfUVTkfnTqvVFx5ai7/MXn3tp1UrtRkDWRsaAMjzaD08uJ1irz7+8ps/6ZYj90V3FKrQBkvmubULbN7vs7tZRyJV9w0ePLbQ4PcJspqXnkbhbgoGk/AVptZRxpB0hU7Mpc1x34cdgKPm1dzeTts9XPwlFAO5Au4BDbO7ZycO7J9A/Zh2b4A2+ucALefWpTrflDKVq4kHQBOoi9PO1qvsDeGd6AxXAJbQ5VxlFrW8EnDcJlTsOPcjElxL7WNy7AduC4f2+A/rSN/Hyg7YMBTxgqPUT3F2HAqtIb58GvQW86GqyG+ff4UWz0FBuH4UhaTal1vmAGfg98dfP4d4HPGwmwYAg+D2/J7uU0ap/YaolHZVbBj5d1DaSK8ADsmqiH2JIhgNRhbPZrbhSdZ5heVJGw7477VfYuaagMK2sM8iMloga1HXAt/AeWELgQnR/0Z7k3W6pe3xTn/JamTFPGnPMZSj6p90rA8YOziwHcnH/EgTovJlJ0LPSHkyrTKmZNJ+8KrYKBsCQeB0pWdBFNleieMgzjL44jejTK1CPSY0CiMdyOT09g6ni5O3Ceg51U4VNLaPSA3SDNEwwiKFdgHgANNrpjb7UVejYTYCuZ92DR42HYh8gfDJfAMqBi4dqxk+RrKGkD0YXNsA6AT5qCUXhBe5CR0gPCC4dhqKFwI1m1qX0hr94CotDE4aAd3PCyBX4Jyn+sNL5tBDsRAp3S7b5KVYwa2A0nHaO5AXBeDtnlMxizsW+HomLh8zX9R5sTeBSEn/cqc2Tvak9eDXCyP2PgbYWzn2gefHxT7+0Qu/h18DO7XmPWYcYqSXuHz2myb6G7RNs7meLgeMxXugbiPA3clQx0xtgNPGN819L7+oCzvm6zSx+EkI+Du3Pe0LbOd/jqc7dhG9Wib+mJ5jaJBuL8e4B5aAMpAomKlb8d+KZWUVnw+dgzKSdDtvKaLDyJ1ReZB7O0J2EV5Xwd8OsTJExNpu7Q1SJ8zgy7K93UCX4P4mr4udoyhPGDKygOP+tomIFarMw2d+cfgF2DnDVAGoBvzw33YTHgPDoXQ7Fx/Wy6YkdMrcrmrehO4Pz3WvP90cIVPgonwITg4973yu0XTZK0+ZQaQd+K816twVAwKO71ZRj9zeg7lcVzXHghpVN4n2G3BAHQ1NILx4MBjoppgLwL3Ww8IHZsf6vGk3O8fwx9heK7rhD0o2zdg75JtT6GzQQ8KzcZwElSr3M5J85ktYCzEG+Gx2NNzm/Cm5pSp+K2gfLrZbg3RcB2IQcZN1qPM3+l06SjbAltX/TiXe1wtg7+AdR+AcgIs7xUPw94XxuTrnOD4E1bEoe9Rptw+DWGOGeQi7JOs1SfKKfk+epcakPNxbI8uFVdem8vT6aJdq7jASYjOFPdQDP4Q6t+Em8HVutmbkbYH9Tv4LcQW+H6ujy9Wrtxc6A7vQnznb5TbHUPZ0mw7CeoaOBAegmfBIKw8WZzs34M/oNiPGPzB2KHdrVMUlD29VFLLpw2jMWmnaIbdDNxXur+dWgVumTMglI4zMgbUEV5LmjqW7XnRkDS9qhbu/xZlZ8LWuc3UfM22Of80aVcYDJ/lstdIWxXu0TGXm/TO19vveHWuOglUxOo6iMfyBe7JOEp01ech9puuuBCMA8pVcUUNUB5lqgMYwJyE1oXOGTh9v1gO6kmogKEwHtREMHYofz5zAl3lJ2AWqJfgfohJiKB8HWWfg54YA9Zr1fn5Xmm80SdvHhNwVmq2umF8vWxA+WRwwE9BPNhOulrq0nxz97j6Go6DF8HYcBfYyer6MwWuoINeDG6roq4iE97QCtsJuxWc2JrkCeKEbgX7waOgnLiavxdQEWfohtgRwCrygIoxoQv1K0FNgR7gAKPTB+dr5lAWMliqmbAb7AzbgCs42vYK21NmOiwHJ9atpdxqDlhdA75QdYJT4XUYDfbBiVRe5ySoZTAbBpeekp6T4lo5uFnBz0fpJ6P8E9SJufEdXHipdRA/mw2hzmvfhrfgfjCKPwJnwn2g3igldb4hNaD5a6/fz7eHVuAb2wPwPs+4DB7E/hTagd64BbgoC6Ab9IAfgn+OX0p/ppAaGxZjnw6+Ep8DK8Cj0IDrmHw3GaeN9EZ/AlxFfk1RuVGUYu8K00D9Fa6EvrAUVKzO29gXg9vC1VW3g540w0xBcU2hKJnz+FxYvTCXWaduK/StuTZlLcD6JjnfEvsb6A56m32z78q4FMGw1gA4lEa60WmwMeiSnsljIBSDmEOBE3RdfvggbMuMIbNhItgJtbyUpE9ddjA0Bid1sderXDaQ1OdPAO9zH6hDcpuG2Ml7SQfArHRx6Xpf3JTluySrsrIP6Seg9/iMqsEvF6YZoXIDeAZCRmpneAHEnnLQnaEuXATX53schR3n/e7YyuvOT1bpnyV107Io3xZ6QWs4EirAyXkEqqvK3xa9CQ0c5C5xQ+zN8kWjcr2xZxTsBHfmsipbP671ZmW3wHYA58DdEPobhtwVF2HfBE9H3pT8xjkdja3iiDK4PQBO8Dx4B9wiH8JKeANcKTUW9IITwKNMeYrcArfDhVDsb1pVyty26le5D97/zWzrzVUGXyVjI0WjHUgq4CjoAuGiRuuJkN7mSJX7cn+uaZNyfBBgDHZqXvqsU2cZ6aPwChgE/ap8M9wLbSH+0DKOaw18z8N12GPAyf4BfADbwBmwCbxAHY9NvxQXx2GgVLZXPvurZDE0rqk5+NmAm8U2aIbdH9yDalgpSS80ltlB29fPqW9c8XLUHnsIuGquqt8gN7edwtazrOsAn4MysLryX8BD4Ap3y+0dZROIwPsl9h/hHjgit4lXdrdvHN8dc91wyk7JdvIS7VpF46Jb2ZGz4WJIRyBpBKQW3oR8lZuSvwQMhKtAfQUpYuf27cgbNx6EEeDAzgMHPwYMYi2gEcSfxC7B9qicDMoo/1vQI8p9IG88WAY/yeVpYrJdHpf5vytu4Ky7X46xIamrvjDb52OrG3K+HrZt4xq9wYEZPGPVfp7bhsdE2os2ylV6J1n5mbYPUX4S7AkGX+OAk2t6mm1Iw3PtQ+O4LuooK26RYvW3s7nBLZDiAGlbUHYiRV/S5AWk28DTEFqB4eo+B+n1M55Ivhu4kspj92uYCm6Px0Gv61lor0fcDQNBrQQnOr71lVeYsm894L/bkBuFe/u93eBngJtJMlwTDIDKyfDt6n3se8Dt8jHoNU0o70waq34obZ8lPx4coG+LbifrP6Pt0aQvwn65LFzcAHY8ZUtgAnwExp2WoMpeQLvaA12p7bf/pLPFmS3a/ajr750cfE43wX4YYmU9wi7IddHBCsrc69vm8uuwQydYVhQVvmsUn7s+ebfD0GhXrI+yf2jqA4oPKdo+iHxMwHbYRmgjta4cUTqCWXkg0UHatIR4SxxWKK9PeXhgKiZfxWOthzXuGff4p6b54bH3Y3W3pNxJcK8ebgdI44iys0G0N/8qKGOAGg9Ni50n3yjy2GkxSKtMRtT/21I7Fg/H9lRIX6qK5YX6zSjvDL4BGiBfBnUNmFdzwfKX4Ct40OtJv1sDj0Hlzrk6xbM3tob7uCf4amyk96VHvQg7gltGzQG9wpcwX6BCesfJ3/kJiMmgs+Gm4errUeZqF+Up4IoOzoWLcmqETyLve/2BsKkFpGUvK7VYCz6j06RbQx+ogHhN3Qdb3QF+a/wVKF94OhSHR77sWcXytcKm82usHGW9QE2B3skq/QB7APaqnJ9NuvaufnF1GIhxYH3LSAeA+hM0hMfgNzATdHvjgDHDv+qkP8gW77XW2gwmYsJe2F3zZDgxI7NteTo+/1WD/B9Au3Zjh2RyrgAAAABJRU5ErkJggg=='; + private mode = 'snow'; private INITIAL_BUFFERS = () => ({ position: { size: 3, value: [] }, @@ -119,6 +120,8 @@ export class SnowfallEffect { worldSize: { type: 'vec3', value: [0, 0, 0] }, gravity: { type: 'float', value: this.gravity }, wind: { type: 'float', value: 0 }, + spin_factor: { type: 'float', value: this.mode === 'sakura' ? 8 : 1 }, + turbulence: { type: 'float', value: this.mode === 'sakura' ? 2 : 1 }, projection: { type: 'mat4', value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], @@ -153,7 +156,16 @@ export class SnowfallEffect { easing: 0.0005, }; - constructor() { + constructor(options: { + sakura?: boolean; + }) { + if (options.sakura) { + this.mode = 'sakura'; + this.snowflake = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAFEmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgeG1wOkNyZWF0ZURhdGU9IjIwMjQtMDItMDFUMTQ6Mzk6NTYrMDkwMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDItMDFUMTQ6NDU6MzQrMDk6MDAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjQtMDItMDFUMTQ6NDU6MzQrMDk6MDAiCiAgIHBob3Rvc2hvcDpEYXRlQ3JlYXRlZD0iMjAyNC0wMi0wMVQxNDozOTo1NiswOTAwIgogICBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIgogICBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiCiAgIGV4aWY6UGl4ZWxYRGltZW5zaW9uPSI2NCIKICAgZXhpZjpQaXhlbFlEaW1lbnNpb249IjY0IgogICBleGlmOkNvbG9yU3BhY2U9IjEiCiAgIHRpZmY6SW1hZ2VXaWR0aD0iNjQiCiAgIHRpZmY6SW1hZ2VMZW5ndGg9IjY0IgogICB0aWZmOlJlc29sdXRpb25Vbml0PSIyIgogICB0aWZmOlhSZXNvbHV0aW9uPSI3Mi8xIgogICB0aWZmOllSZXNvbHV0aW9uPSI3Mi8xIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0icHJvZHVjZWQiCiAgICAgIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFmZmluaXR5IFBob3RvIDIgMi4zLjEiCiAgICAgIHN0RXZ0OndoZW49IjIwMjQtMDItMDFUMTQ6NDU6MzQrMDk6MDAiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0iciI/PhldI30AAAGBaUNDUHNSR0IgSUVDNjE5NjYtMi4xAAAokXWRu0sDQRCHP6Mh4oOIWlhYBPHRJBIjiDYWEV+gFjGCr+ZyuUuEJB53JyK2gq2gINr4KvQv0FawFgRFEcTaWtFG5ZwzgQQxs+zst7+dGXZnwRPPqFmrKgzZnG3GRqOB2bn5gO8FDxV46aJRUS1jcnokTln7uJdYsduQW6t83L9Wm9QsFSqqhQdVw7SFx4QnVm3D5R3hZjWtJIXPhIOmXFD4ztUTeX5xOZXnL5fNeGwIPA3CgVQJJ0pYTZtZYXk57dnMilq4j/uSOi03My1rm8xWLGKMEiXAOMMM0UcPA+L7CBGhW3aUyQ//5k+xLLmqeIM1TJZIkcYmKOqKVNdk1UXXZGRYc/v/t6+W3hvJV6+LgvfZcd46wLcN31uO83nkON/HUPkEl7li/vIh9L+LvlXU2g/AvwHnV0UtsQsXm9DyaCim8itVyvToOryeQv0cNN1AzUK+Z4VzTh4gvi5fdQ17+9Ap8f7FHyc6Z8kcDq1+AAAACXBIWXMAAAsTAAALEwEAmpwYAAADwElEQVR4nO2bT4hWVRjGf75TkhoEkhSa/9ocRIIwCsrE1pVnLbkYdFdGgQRS6caVm3CVy2oRuqmQ2yJXKTJh4GqCGs/CJCcLccAJ/yDpnGnxHYeZ4TrNfOc55y78nuWdc3/ve57v+b65f86BgQaqotiE5bEJKxYx7onYhOU1egKwGkViE/YCN4Cx2ITNC4xbDVwAJmMT9tXobVnpArEJe4CvZx0aB7aZdxPzxhkwArw66/Ae8+5Eyf6KJiA2YRPw+bzD64EjLcP3MXfyAMdjEzYWaG1GxRIQmzAEnAVeb/nzFPCSeTeaxj4FBOCZlrEjwBvm3VSJPksm4BPaJw8wBHwXm/BibMIW4HvaJ09ifFygP6BQAtKkfgEeEyHvAy+YdxdFvBmVSsBBdJMnsQ4KeTOSJyA2YT1wCXhcjL4HPG/e/amElkjAAfSTJzEPqKHSBKQLmSvAKiV3lm4BG8y7GyqgOgHvU27yAE+mGjLJEhCbsBL4A3haxXyIJoCN5t0dBUyZgF2UnzypxtsqmNKAt4SsarUkX4F0I3ONOgkAuA48a97FXJAqAa9Qb/IAa4CXFSCVATXjL635yBuQ/RsQm7AWuCroZamaBtaZd3/nQBQJeFPA6EfLFLUVBrwmYPSr7bkAhQHPCRj9al0uQGHAWgGjs9oKA7I/hS5rZ/0XSC86JDclGVph3t3t9+TcBHT56T9QVg+5BnT5/X+grB4GCcgs/sgnYCjzfIWyesg14Hrm+Qpl9ZBrwMT/DymurB4GCeiyuEidGnCN3n15V5pOPfStLAPMu1vAWA4jU7+Zd7dzAIqboREBo7PaCgN+EjA6qz1IQDbAu9/prQeorUvm3eVciOqx+JcizlL0hQKiMuAreiu/amkq1cyWxADz7ipwWsFapH4w7/5SgJRvh+cviCyp4yqQeonMOWCHktmic+bdThVMvUSmyFK2kjWkBph354FTSuY8nTLvflYCSyyT+xD4pwB3EvhADZUbYN5dAfarucB+825cDS25WvwksFuEO2nevSNizVHJ1eLvAoplrePAewJOq4oZYN5NAsPkPTCZBoYTq4iK7hgx734EjmUgjpl3Z1T9tKnGpqlP6e+p0Vg6t6iKG5De3A6ztJul+/Si3/db38WqyrY58+4CcHQJpxxN5xRXFQOSjgCjixg3SvuusiKqZoB59y+964KbCwy7Cew27+7V6apuAkibnhbaEbq3xMaohVTVAADz7hvgMHN/FKeAQ+bdt7X7Kb519mGKTdgKfEbvYucj8+7XLvr4DxAA134c0w/5AAAAAElFTkSuQmCC'; + this.size = 10; + this.density = 1 / 280; + } + const canvas = this.initCanvas(); const gl = canvas.getContext('webgl2', { antialias: true }); if (gl == null) throw new Error('Failed to get WebGL context'); diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts index 2f7545ef0daf824540eeeec79987f26a92847d2d..fcd59510df1836c08e53245bcce0ee60c1edf8ee 100644 --- a/packages/frontend/src/scripts/sound.ts +++ b/packages/frontend/src/scripts/sound.ts @@ -1,11 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import type { SoundStore } from '@/store.js'; import { defaultStore } from '@/store.js'; -import * as os from '@/os.js'; let ctx: AudioContext; const cache = new Map<string, AudioBuffer>(); @@ -89,63 +88,33 @@ export type OperationType = typeof operationTypes[number]; /** * 音声をèªã¿è¾¼ã‚€ - * @param soundStore サウンドè¨å®š + * @param url url * @param options `useCache`: デフォルトã¯`true` 一度å†ç”Ÿã—ãŸéŸ³å£°ã¯ã‚ャッシュã™ã‚‹ */ -export async function loadAudio(soundStore: SoundStore, options?: { useCache?: boolean; }) { - if (_DEV_) console.log('loading audio. opts:', options); - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (soundStore.type === null || (soundStore.type === '_driveFile_' && !soundStore.fileUrl)) { - return; - } +export async function loadAudio(url: string, options?: { useCache?: boolean; }) { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (ctx == null) { ctx = new AudioContext(); } if (options?.useCache ?? true) { - if (soundStore.type === '_driveFile_' && cache.has(soundStore.fileId)) { - if (_DEV_) console.log('use cache'); - return cache.get(soundStore.fileId) as AudioBuffer; - } else if (cache.has(soundStore.type)) { - if (_DEV_) console.log('use cache'); - return cache.get(soundStore.type) as AudioBuffer; + if (cache.has(url)) { + return cache.get(url) as AudioBuffer; } } let response: Response; - if (soundStore.type === '_driveFile_') { - try { - response = await fetch(soundStore.fileUrl); - } catch (err) { - try { - // URLãŒå¤‰ã‚ã£ã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚‹ã®ã§ãƒ‰ãƒ©ã‚¤ãƒ–å´ã‹ã‚‰URLã‚’å–å¾—ã™ã‚‹ãƒ•ã‚©ãƒ¼ãƒ«ãƒãƒƒã‚¯ - const apiRes = await os.api('drive/files/show', { - fileId: soundStore.fileId, - }); - response = await fetch(apiRes.url); - } catch (fbErr) { - // ãã‚Œã§ã‚‚ç„¡ç†ãªã‚‰è«¦ã‚ã‚‹ - return; - } - } - } else { - try { - response = await fetch(`/client-assets/sounds/${soundStore.type}.mp3`); - } catch (err) { - return; - } + try { + response = await fetch(url); + } catch (err) { + return; } const arrayBuffer = await response.arrayBuffer(); const audioBuffer = await ctx.decodeAudioData(arrayBuffer); if (options?.useCache ?? true) { - if (soundStore.type === '_driveFile_') { - cache.set(soundStore.fileId, audioBuffer); - } else { - cache.set(soundStore.type, audioBuffer); - } + cache.set(url, audioBuffer); } return audioBuffer; @@ -155,13 +124,12 @@ export async function loadAudio(soundStore: SoundStore, options?: { useCache?: b * 既定ã®ã‚¹ãƒ—ライトをå†ç”Ÿã™ã‚‹ * @param type スプライトã®ç¨®é¡žã‚’指定 */ -export function play(operationType: OperationType) { +export function playMisskeySfx(operationType: OperationType) { const sound = defaultStore.state[`sound_${operationType}`]; - if (_DEV_) console.log('play', operationType, sound); - if (sound.type == null || !canPlay) return; + if (sound.type == null || !canPlay || ('userActivation' in navigator && !navigator.userActivation.hasBeenActive)) return; canPlay = false; - playFile(sound).finally(() => { + playMisskeySfxFile(sound).finally(() => { // ã”ãçŸæ™‚é–“ã«éŸ³ãŒé‡è¤‡ã—ãªã„よã†ã« setTimeout(() => { canPlay = true; @@ -173,26 +141,59 @@ export function play(operationType: OperationType) { * サウンドè¨å®šå½¢å¼ã§æŒ‡å®šã•ã‚ŒãŸéŸ³å£°ã‚’å†ç”Ÿã™ã‚‹ * @param soundStore サウンドè¨å®š */ -export async function playFile(soundStore: SoundStore) { - const buffer = await loadAudio(soundStore); +export async function playMisskeySfxFile(soundStore: SoundStore) { + if (soundStore.type === null || (soundStore.type === '_driveFile_' && !soundStore.fileUrl)) { + return; + } + const masterVolume = defaultStore.state.sound_masterVolume; + if (isMute() || masterVolume === 0 || soundStore.volume === 0) { + return; + } + const url = soundStore.type === '_driveFile_' ? soundStore.fileUrl : `/client-assets/sounds/${soundStore.type}.mp3`; + const buffer = await loadAudio(url); if (!buffer) return; - createSourceNode(buffer, soundStore.volume)?.start(); + const volume = soundStore.volume * masterVolume; + createSourceNode(buffer, { volume }).soundSource.start(); } -export function createSourceNode(buffer: AudioBuffer, volume: number) : AudioBufferSourceNode | null { - const masterVolume = defaultStore.state.sound_masterVolume; - if (isMute() || masterVolume === 0 || volume === 0) { - return null; +export async function playUrl(url: string, opts: { + volume?: number; + pan?: number; + playbackRate?: number; +}) { + if (opts.volume === 0) { + return; } + const buffer = await loadAudio(url); + if (!buffer) return; + createSourceNode(buffer, opts).soundSource.start(); +} + +export function createSourceNode(buffer: AudioBuffer, opts: { + volume?: number; + pan?: number; + playbackRate?: number; +}): { + soundSource: AudioBufferSourceNode; + panNode: StereoPannerNode; + gainNode: GainNode; +} { + const panNode = ctx.createStereoPanner(); + panNode.pan.value = opts.pan ?? 0; const gainNode = ctx.createGain(); - gainNode.gain.value = masterVolume * volume; + + gainNode.gain.value = opts.volume ?? 1; const soundSource = ctx.createBufferSource(); soundSource.buffer = buffer; - soundSource.connect(gainNode).connect(ctx.destination); + soundSource.playbackRate.value = opts.playbackRate ?? 1; + soundSource + .connect(panNode) + .connect(gainNode) + .connect(ctx.destination); - return soundSource; + return { soundSource, panNode, gainNode }; } /** diff --git a/packages/frontend/src/scripts/sticky-sidebar.ts b/packages/frontend/src/scripts/sticky-sidebar.ts index f233c3648e7eded5eb93918515f95be490beecac..50f1e6ecc8641da14a3f0e0f55396409afa55298 100644 --- a/packages/frontend/src/scripts/sticky-sidebar.ts +++ b/packages/frontend/src/scripts/sticky-sidebar.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/test-utils.ts b/packages/frontend/src/scripts/test-utils.ts index 1b42811faabfdb4e8c94ca4ae14d2605d07bfec9..52bb2d94e0e8f552cc323469d05dea9401bd116d 100644 --- a/packages/frontend/src/scripts/test-utils.ts +++ b/packages/frontend/src/scripts/test-utils.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/theme-editor.ts b/packages/frontend/src/scripts/theme-editor.ts index 275f4bcdaa293813a11648501db1df9496049b6c..0092af1640ee66e6437f2a92efca324fd4c6adbc 100644 --- a/packages/frontend/src/scripts/theme-editor.ts +++ b/packages/frontend/src/scripts/theme-editor.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts index a174f51756481b148991856eeef3f9b9a4163beb..c49593ed42ca43e2b7cbbee6188630e7b29f620d 100644 --- a/packages/frontend/src/scripts/theme.ts +++ b/packages/frontend/src/scripts/theme.ts @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { ref } from 'vue'; import tinycolor from 'tinycolor2'; import { deepClone } from './clone.js'; +import type { BuiltinTheme } from 'shiki'; import { globalEvents } from '@/events.js'; import lightTheme from '@/themes/_light.json5'; import darkTheme from '@/themes/_dark.json5'; @@ -18,6 +19,13 @@ export type Theme = { desc?: string; base?: 'dark' | 'light'; props: Record<string, string>; + codeHighlighter?: { + base: BuiltinTheme; + overrides?: Record<string, any>; + } | { + base: '_none_'; + overrides: Record<string, any>; + }; }; export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X')); @@ -57,7 +65,7 @@ export const getBuiltinThemesRef = () => { const themeFontFaceName = 'sharkey-theme-font-face'; -let timeout = null; +let timeout: number | null = null; export function applyTheme(theme: Theme, persist = true) { if (timeout) window.clearTimeout(timeout); diff --git a/packages/frontend/src/scripts/time.ts b/packages/frontend/src/scripts/time.ts index 4479db10818ffc0631d0e8f367f0d5d28a3af893..275b67ed00be1e68aecf1d088804fb1d9cd54b2b 100644 --- a/packages/frontend/src/scripts/time.ts +++ b/packages/frontend/src/scripts/time.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/timezones.ts b/packages/frontend/src/scripts/timezones.ts index 55f9be393fad8295f96b41bfb4caa11021315aa3..c7582e06dad50abab9342d07efe60b45c3b7f41d 100644 --- a/packages/frontend/src/scripts/timezones.ts +++ b/packages/frontend/src/scripts/timezones.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/touch.ts b/packages/frontend/src/scripts/touch.ts index 05f379e4aa602ab80a9b58a479b737b2cda83e06..13c9d648dc577b7b23783b5e78a85db53c5397b6 100644 --- a/packages/frontend/src/scripts/touch.ts +++ b/packages/frontend/src/scripts/touch.ts @@ -1,8 +1,9 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ +import { ref } from 'vue'; import { deviceKind } from '@/scripts/device-kind.js'; const isTouchSupported = 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 0; @@ -16,3 +17,6 @@ if (isTouchSupported && !isTouchUsing) { isTouchUsing = true; }, { passive: true }); } + +/** (MkHorizontalSwipe) 横スワイプä¸ã‹ï¼Ÿ */ +export const isHorizontalSwipeSwiping = ref(false); diff --git a/packages/frontend/src/scripts/unison-reload.ts b/packages/frontend/src/scripts/unison-reload.ts index 65fc090888c522faab2c2bbc5cdb87b4124f6b81..a24941d02e887bf787541a8e5f95e3d5a0f00096 100644 --- a/packages/frontend/src/scripts/unison-reload.ts +++ b/packages/frontend/src/scripts/unison-reload.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/upload.ts b/packages/frontend/src/scripts/upload.ts index b896376ec880cffe2b8e54df17bde4095fa9a903..6c46b2bc1ba2db2a12af32f00aabb6e1face81e0 100644 --- a/packages/frontend/src/scripts/upload.ts +++ b/packages/frontend/src/scripts/upload.ts @@ -1,11 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { reactive, ref } from 'vue'; import * as Misskey from 'misskey-js'; -import { readAndCompressImage } from 'browser-image-resizer'; +import { readAndCompressImage } from '@misskey-dev/browser-image-resizer'; import { getCompressionConfig } from './upload/compress-config.js'; import { defaultStore } from '@/store.js'; import { apiUrl } from '@/config.js'; diff --git a/packages/frontend/src/scripts/upload/compress-config.ts b/packages/frontend/src/scripts/upload/compress-config.ts index 2deb9cbb816db72fba7b352c3e9caad02544d1fe..3046b7f518b9fce8616bf0dbb4bb9bee41f64eb6 100644 --- a/packages/frontend/src/scripts/upload/compress-config.ts +++ b/packages/frontend/src/scripts/upload/compress-config.ts @@ -1,11 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import isAnimated from 'is-file-animated'; import { isWebpSupported } from './isWebpSupported.js'; -import type { BrowserImageResizerConfig } from 'browser-image-resizer'; +import type { BrowserImageResizerConfigWithConvertedOutput } from '@misskey-dev/browser-image-resizer'; const compressTypeMap = { 'image/jpeg': { quality: 0.90, mimeType: 'image/webp' }, @@ -21,7 +21,7 @@ const compressTypeMapFallback = { 'image/svg+xml': { quality: 1, mimeType: 'image/png' }, } as const; -export async function getCompressionConfig(file: File): Promise<BrowserImageResizerConfig | undefined> { +export async function getCompressionConfig(file: File): Promise<BrowserImageResizerConfigWithConvertedOutput | undefined> { const imgConfig = (isWebpSupported() ? compressTypeMap : compressTypeMapFallback)[file.type]; if (!imgConfig || await isAnimated(file)) { return; diff --git a/packages/frontend/src/scripts/upload/isWebpSupported.ts b/packages/frontend/src/scripts/upload/isWebpSupported.ts index 185c3e6b40c653e56a94242b0209a34d63420aaf..2511236eccd01b8df2bc5c8bc779232a0432df90 100644 --- a/packages/frontend/src/scripts/upload/isWebpSupported.ts +++ b/packages/frontend/src/scripts/upload/isWebpSupported.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/url.ts b/packages/frontend/src/scripts/url.ts index 625f4ce057fa8e35a431780627051c5248dbfd4d..e3072b3b7d2b62df354623ed78e7ec075f8d9225 100644 --- a/packages/frontend/src/scripts/url.ts +++ b/packages/frontend/src/scripts/url.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/use-chart-tooltip.ts b/packages/frontend/src/scripts/use-chart-tooltip.ts index 3d6489c3b89888c73dead7de0f2ca152a9a14135..7e4bf5c9c6652e53f8ec714bd2d50b557ad43b72 100644 --- a/packages/frontend/src/scripts/use-chart-tooltip.ts +++ b/packages/frontend/src/scripts/use-chart-tooltip.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/use-document-visibility.ts b/packages/frontend/src/scripts/use-document-visibility.ts index a9e2512eb3170d1dfb14462e4c6995c33149518d..a8f4d5e03ae2158d609ca78dcc1a8f85f6a4064b 100644 --- a/packages/frontend/src/scripts/use-document-visibility.ts +++ b/packages/frontend/src/scripts/use-document-visibility.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/use-interval.ts b/packages/frontend/src/scripts/use-interval.ts index b8c5431fb6fc330bb04ce144d43f6d309780e33c..b50e78c3cccfa0a0dab0c2dd0ad57bb1c17ac46c 100644 --- a/packages/frontend/src/scripts/use-interval.ts +++ b/packages/frontend/src/scripts/use-interval.ts @@ -1,9 +1,9 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import { onMounted, onUnmounted } from 'vue'; +import { onActivated, onDeactivated, onMounted, onUnmounted } from 'vue'; export function useInterval(fn: () => void, interval: number, options: { immediate: boolean; @@ -28,6 +28,16 @@ export function useInterval(fn: () => void, interval: number, options: { intervalId = null; }; + onActivated(() => { + if (intervalId) return; + if (options.immediate) fn(); + intervalId = window.setInterval(fn, interval); + }); + + onDeactivated(() => { + clear(); + }); + onUnmounted(() => { clear(); }); diff --git a/packages/frontend/src/scripts/use-leave-guard.ts b/packages/frontend/src/scripts/use-leave-guard.ts index c9750c3923251b10efe5471fec9efa5b225e4b53..5f7e56e8a95727513ce791e0581498fada4863d2 100644 --- a/packages/frontend/src/scripts/use-leave-guard.ts +++ b/packages/frontend/src/scripts/use-leave-guard.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/use-note-capture.ts b/packages/frontend/src/scripts/use-note-capture.ts index bcdba5455afa6f13c4ff8973cd3b0ab5126cb48a..3baa45d50ff38206376aba06d085d23b6e023524 100644 --- a/packages/frontend/src/scripts/use-note-capture.ts +++ b/packages/frontend/src/scripts/use-note-capture.ts @@ -1,16 +1,17 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import { onUnmounted, Ref } from 'vue'; +import { onUnmounted, Ref, ShallowRef } from 'vue'; import * as Misskey from 'misskey-js'; import { useStream } from '@/stream.js'; import { $i } from '@/account.js'; import * as os from '@/os.js'; +import { misskeyApi } from './misskey-api.js'; export function useNoteCapture(props: { - rootEl: Ref<HTMLElement>; + rootEl: ShallowRef<HTMLElement | undefined>; note: Ref<Misskey.entities.Note>; pureNote: Ref<Misskey.entities.Note>; isDeletedRef: Ref<boolean>; @@ -32,7 +33,7 @@ export function useNoteCapture(props: { // notes/show may throw if the current user can't see the note try { - const replyNote = await os.api('notes/show', { + const replyNote = await misskeyApi('notes/show', { noteId: body.id, }); @@ -100,7 +101,7 @@ export function useNoteCapture(props: { case 'updated': { try { - const editedNote = await os.api('notes/show', { + const editedNote = await misskeyApi('notes/show', { noteId: id, }); @@ -121,7 +122,7 @@ export function useNoteCapture(props: { function capture(withHandler = false): void { if (connection) { // TODO: ã“ã®ãƒŽãƒ¼ãƒˆãŒã‚¹ãƒˆãƒªãƒ¼ãƒŸãƒ³ã‚°çµŒç”±ã§æµã‚Œã¦ããŸå ´åˆã®ã¿ sr ã™ã‚‹ - connection.send(document.body.contains(props.rootEl.value) ? 'sr' : 's', { id: note.value.id }); + connection.send(document.body.contains(props.rootEl.value ?? null as Node | null) ? 'sr' : 's', { id: note.value.id }); if (pureNote.value.id !== note.value.id) connection.send('s', { id: pureNote.value.id }); if (withHandler) connection.on('noteUpdated', onStreamNoteUpdated); } diff --git a/packages/frontend/src/scripts/use-tooltip.ts b/packages/frontend/src/scripts/use-tooltip.ts index aaf0a0285ad9ff7ecb020d754025c15a361f13ab..a26d08cce73790f1615de1422d0a0ee685193a35 100644 --- a/packages/frontend/src/scripts/use-tooltip.ts +++ b/packages/frontend/src/scripts/use-tooltip.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/scripts/worker-multi-dispatch.ts b/packages/frontend/src/scripts/worker-multi-dispatch.ts index 7686b687c5d4d0704ef013ca00b6cca44551a193..6b3fcd938334d03595e57508d2dfe6aeada60085 100644 --- a/packages/frontend/src/scripts/worker-multi-dispatch.ts +++ b/packages/frontend/src/scripts/worker-multi-dispatch.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 18cfad2102e3bc9b975c382df537fbf17e9c64cc..2cf17b27c5726fee25ae101841d8809f2362f031 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -7,7 +7,9 @@ import { markRaw, ref } from 'vue'; import * as Misskey from 'misskey-js'; import { miLocalStorage } from './local-storage.js'; import type { SoundType } from '@/scripts/sound.js'; +import type { BuiltinTheme as ShikiBuiltinTheme } from 'shiki'; import { Storage } from '@/pizzax.js'; +import { hemisphere } from '@/scripts/intl-const.js'; interface PostFormAction { title: string, @@ -151,6 +153,14 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: true, }, + showVisibilitySelectorOnBoost: { + where: 'account', + default: true, + }, + visibilityOnBoost: { + where: 'account', + default: 'public' as 'public' | 'home' | 'followers', + }, menu: { where: 'deviceAccount', @@ -204,6 +214,13 @@ export const defaultStore = markRaw(new Storage('base', { default: { src: 'home' as 'home' | 'local' | 'social' | 'global' | 'bubble' | `list:${string}`, userList: null as Misskey.entities.UserList | null, + filter: { + withReplies: true, + withRenotes: true, + withBots: true, + withSensitive: true, + onlyFiles: false, + }, }, }, pinnedUserLists: { @@ -247,6 +264,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, + warnMissingAltText: { + where: 'device', + default: true, + }, imageNewTab: { where: 'device', default: false, @@ -391,6 +412,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, + oneko: { + where: 'device', + default: false, + }, clickToOpen: { where: 'device', default: true, @@ -427,14 +452,6 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, - tlWithReplies: { - where: 'device', - default: false, - }, - tlWithBots: { - where: 'device', - default: true, - }, defaultWithReplies: { where: 'account', default: false, @@ -460,6 +477,21 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, + dropAndFusion: { + where: 'device', + default: { + bgmVolume: 0.25, + sfxVolume: 1, + }, + }, + hemisphere: { + where: 'device', + default: hemisphere as 'N' | 'S', + }, + enableHorizontalSwipe: { + where: 'device', + default: true, + }, sound_masterVolume: { where: 'device', diff --git a/packages/frontend/src/stream.ts b/packages/frontend/src/stream.ts index 5f0826b4e3c96de55e9b796200638d838ad4f39e..0c5ee06197ba54e60636b60db72ef43faf12aa48 100644 --- a/packages/frontend/src/stream.ts +++ b/packages/frontend/src/stream.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index fd2716bf9d378523e4475a967c78ee8ffafeefb8..d8760099614ba5d966828d02408dcaa7c3009d15 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -9,7 +9,7 @@ } /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * * SPDX-License-Identifier: AGPL-3.0-only */ @@ -37,6 +37,9 @@ --margin: var(--marginHalf); } + --avatar: 48px; + --thread-width: 2px; + //--ad: rgb(255 169 0 / 10%); } @@ -250,6 +253,10 @@ rt { line-height: inherit; max-width: 100%; + &:hover { + text-decoration: none; + } + &:focus-visible { outline: none; } @@ -444,6 +451,39 @@ rt { transition-timing-function: cubic-bezier(0,.5,.5,1); } +._woodenFrame { + padding: 7px; + background: #8C4F26; + box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c; + border-radius: 10px; + + --bg: #F1E8DC; + --panel: #fff; + --fg: #693410; + --switchOffBg: rgba(0, 0, 0, 0.1); + --switchOffFg: rgb(255, 255, 255); + --switchOnBg: var(--accent); + --switchOnFg: rgb(255, 255, 255); +} + +._woodenFrameH { + display: flex; + gap: 6px; +} + +._woodenFrameInner { + padding: 8px; + margin-top: 8px; + background: var(--bg); + box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410; + border-radius: 6px; + color: var(--fg); + + &:first-child { + margin-top: 0; + } +} + ._transition_zoom-enter-active, ._transition_zoom-leave-active { transition: opacity 0.5s, transform 0.5s !important; } @@ -452,13 +492,13 @@ rt { transform: scale(0.9); } -@keyframes blink { +@keyframes global-blink { 0% { opacity: 1; transform: scale(1); } 30% { opacity: 1; transform: scale(1); } 90% { opacity: 0; transform: scale(0.5); } } -@keyframes tada { +@keyframes global-tada { from { transform: scale3d(1, 1, 1); } @@ -488,7 +528,7 @@ rt { ._anime_bounce { will-change: transform; - animation: bounce ease 0.7s; + animation: global-bounce ease 0.7s; animation-iteration-count: 1; transform-origin: 50% 50%; } @@ -500,7 +540,7 @@ rt { transition: transform 0.1s ease; } -@keyframes bounce { +@keyframes global-bounce { 0% { transform: scaleX(0.90) scaleY(0.90) ; } diff --git a/packages/frontend/src/theme-store.ts b/packages/frontend/src/theme-store.ts index f37c01cca1315f6c76a65db61fc276801d76d22f..c41cc1765245140421ef16f1d1daf9e6657b81d2 100644 --- a/packages/frontend/src/theme-store.ts +++ b/packages/frontend/src/theme-store.ts @@ -1,11 +1,11 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { Theme, getBuiltinThemes } from '@/scripts/theme.js'; import { miLocalStorage } from '@/local-storage.js'; -import { api } from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { $i } from '@/account.js'; const lsCacheKey = $i ? `themes:${$i.id}` as const : null; @@ -19,7 +19,7 @@ export async function fetchThemes(): Promise<void> { if ($i == null) return; try { - const themes = await api('i/registry/get', { scope: ['client'], key: 'themes' }); + const themes = await misskeyApi('i/registry/get', { scope: ['client'], key: 'themes' }); miLocalStorage.setItem(lsCacheKey!, JSON.stringify(themes)); } catch (err) { if (err.code === 'NO_SUCH_KEY') return; @@ -35,13 +35,13 @@ export async function addTheme(theme: Theme): Promise<void> { } await fetchThemes(); const themes = getThemes().concat(theme); - await api('i/registry/set', { scope: ['client'], key: 'themes', value: themes }); + await misskeyApi('i/registry/set', { scope: ['client'], key: 'themes', value: themes }); miLocalStorage.setItem(lsCacheKey!, JSON.stringify(themes)); } export async function removeTheme(theme: Theme): Promise<void> { if ($i == null) return; const themes = getThemes().filter(t => t.id !== theme.id); - await api('i/registry/set', { scope: ['client'], key: 'themes', value: themes }); + await misskeyApi('i/registry/set', { scope: ['client'], key: 'themes', value: themes }); miLocalStorage.setItem(lsCacheKey!, JSON.stringify(themes)); } diff --git a/packages/frontend/src/themes/_dark.json5 b/packages/frontend/src/themes/_dark.json5 index 3f5822977abcb6d978b37de025cdab8c00449c68..7b70aa1e0941cb7e5184e7b8f7a6c142c55f14ea 100644 --- a/packages/frontend/src/themes/_dark.json5 +++ b/packages/frontend/src/themes/_dark.json5 @@ -30,6 +30,7 @@ panelHeaderFg: '@fg', panelHeaderDivider: 'rgba(0, 0, 0, 0)', panelBorder: '" solid 1px var(--divider)', + thread: ':lighten<12<@panel', acrylicPanel: ':alpha<0.5<@panel', windowHeader: ':alpha<0.85<@panel', popup: ':lighten<3<@panel', @@ -94,4 +95,8 @@ X16: ':alpha<0.7<@panel', X17: ':alpha<0.8<@bg', }, + + codeHighlighter: { + base: 'one-dark-pro', + }, } diff --git a/packages/frontend/src/themes/_light.json5 b/packages/frontend/src/themes/_light.json5 index 6ebfcaafeb5319b3b557ff02e83af2c3ebaad587..d797aec734954d47773f8f782ca1b9169064e15b 100644 --- a/packages/frontend/src/themes/_light.json5 +++ b/packages/frontend/src/themes/_light.json5 @@ -30,6 +30,7 @@ panelHeaderFg: '@fg', panelHeaderDivider: 'rgba(0, 0, 0, 0)', panelBorder: '" solid 1px var(--divider)', + thread: ':darken<12<@panel', acrylicPanel: ':alpha<0.5<@panel', windowHeader: ':alpha<0.85<@panel', popup: ':lighten<3<@panel', @@ -94,4 +95,8 @@ X16: ':alpha<0.7<@panel', X17: ':alpha<0.8<@bg', }, + + codeHighlighter: { + base: 'catppuccin-latte', + }, } diff --git a/packages/frontend/src/themes/l-sushi.json5 b/packages/frontend/src/themes/l-sushi.json5 index e787d63734b5e25f7ef9df2d2450bc626bebbd86..f1523b698c381758343b88a32f8c7ff19f4456c1 100644 --- a/packages/frontend/src/themes/l-sushi.json5 +++ b/packages/frontend/src/themes/l-sushi.json5 @@ -14,6 +14,6 @@ renote: '@accent', link: '@accent', mention: '@accent', - hashtag: '#229e82', + hashtag: '@accent', }, } diff --git a/packages/frontend/src/type.ts b/packages/frontend/src/type.ts new file mode 100644 index 0000000000000000000000000000000000000000..9c0fc2a11e6dc2fbf68e6d38b37d6a57c6f211dd --- /dev/null +++ b/packages/frontend/src/type.ts @@ -0,0 +1,3 @@ +export type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] }; + +export type WithNonNullable<T, K extends keyof T> = T & { [P in K]-?: NonNullable<T[P]> }; diff --git a/packages/frontend/src/types/date-separated-list.ts b/packages/frontend/src/types/date-separated-list.ts index 678193ca980707f8df44a5d5c980b0d5ef4a99b1..af685cff128cb21f0d9c5c305920f70c20fa0074 100644 --- a/packages/frontend/src/types/date-separated-list.ts +++ b/packages/frontend/src/types/date-separated-list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/types/menu.ts b/packages/frontend/src/types/menu.ts index f4516bbe5bb7c490a1f8f1e508f11fef1db99416..712f3464e5dc1e6bb9efca424687b88ad3482637 100644 --- a/packages/frontend/src/types/menu.ts +++ b/packages/frontend/src/types/menu.ts @@ -1,10 +1,10 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import * as Misskey from 'misskey-js'; -import { Ref } from 'vue'; +import { ComputedRef, Ref } from 'vue'; export type MenuAction = (ev: MouseEvent) => void; @@ -15,7 +15,7 @@ export type MenuLink = { type: 'link', to: string, text: string, icon?: string, export type MenuA = { type: 'a', href: string, target?: string, download?: string, text: string, icon?: string, indicate?: boolean }; export type MenuUser = { type: 'user', user: Misskey.entities.User, active?: boolean, indicate?: boolean, action: MenuAction }; export type MenuSwitch = { type: 'switch', ref: Ref<boolean>, text: string, disabled?: boolean | Ref<boolean> }; -export type MenuButton = { type?: 'button', text: string, icon?: string, indicate?: boolean, danger?: boolean, active?: boolean, avatar?: Misskey.entities.User; action: MenuAction }; +export type MenuButton = { type?: 'button', text: string, icon?: string, indicate?: boolean, danger?: boolean, active?: boolean | ComputedRef<boolean>, avatar?: Misskey.entities.User; action: MenuAction }; export type MenuParent = { type: 'parent', text: string, icon?: string, children: MenuItem[] | (() => Promise<MenuItem[]> | MenuItem[]) }; export type MenuPending = { type: 'pending' }; diff --git a/packages/frontend/src/types/page-header.ts b/packages/frontend/src/types/page-header.ts index 295b97a7fdc4cab8f793985b857236b050f37afc..e9807a293924740c07808d6788f5ce21c2f400c7 100644 --- a/packages/frontend/src/types/page-header.ts +++ b/packages/frontend/src/types/page-header.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/ui/_common_/announcements.vue b/packages/frontend/src/ui/_common_/announcements.vue index 913fa35cc20db2aa39dc33a16dc800226f832ca1..b49eff91482f5f635fa720ad0b72aa3dbb0a849c 100644 --- a/packages/frontend/src/ui/_common_/announcements.vue +++ b/packages/frontend/src/ui/_common_/announcements.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts index a3adbfb1b1a0f210ad1ed8e6e52f904b61beb436..3d5b42241ee94e551ab077c7ffd74b5bbc7d1d28 100644 --- a/packages/frontend/src/ui/_common_/common.ts +++ b/packages/frontend/src/ui/_common_/common.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -97,7 +97,13 @@ export function openInstanceMenu(ev: MouseEvent) { action: () => { window.open(instance.privacyPolicyUrl, '_blank', 'noopener'); }, - } : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl) ? undefined : { type: 'divider' }, { + } : undefined, (instance.donationUrl) ? { + text: i18n.ts.donation, + icon: 'ph-hand-coins ph-bold ph-lg', + action: () => { + window.open(instance.donationUrl, '_blank', 'noopener'); + }, + } : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl && !instance.donationUrl) ? undefined : { type: 'divider' }, { text: i18n.ts.help, icon: 'ph-question ph-bold ph-lg', action: () => { diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue index 6ece7d86d75187f73af0a4a1d1abf4a3a8344c15..4fe53ae6a34bf4f9bb2e1705daae4e1ca09f9357 100644 --- a/packages/frontend/src/ui/_common_/common.vue +++ b/packages/frontend/src/ui/_common_/common.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -42,6 +42,8 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="dev" id="devTicker"><span>DEV BUILD</span></div> <div v-if="$i && $i.isBot" id="botWarn"><span>{{ i18n.ts.loggedInAsBot }}</span></div> + +<SkOneko v-if="defaultStore.state.oneko"/> </template> <script lang="ts" setup> @@ -49,7 +51,8 @@ import { defineAsyncComponent, ref } from 'vue'; import * as Misskey from 'misskey-js'; import { swInject } from './sw-inject.js'; import XNotification from './notification.vue'; -import { popups, pendingApiRequestsCount } from '@/os.js'; +import { popups } from '@/os.js'; +import { pendingApiRequestsCount } from '@/scripts/misskey-api.js'; import { uploads } from '@/scripts/upload.js'; import * as sound from '@/scripts/sound.js'; import { $i } from '@/account.js'; @@ -58,6 +61,8 @@ import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; import { globalEvents } from '@/events.js'; +const SkOneko = defineAsyncComponent(() => import('@/components/SkOneko.vue')); + const XStreamIndicator = defineAsyncComponent(() => import('./stream-indicator.vue')); const XUpload = defineAsyncComponent(() => import('./upload.vue')); @@ -82,7 +87,7 @@ function onNotification(notification: Misskey.entities.Notification, isClient = }, 6000); } - sound.play('notification'); + sound.playMisskeySfx('notification'); } if ($i) { diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index 618be2db88000b30e53d0ff872f4719017df1f42..85340fa2b781b92945450fa41994151d3f10267f 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div :class="$style.bottom"> <button class="_button" :class="$style.post" data-cy-open-post-form @click="os.post"> - <i :class="$style.postIcon" class="ph-pencil ph-bold ph-lg ti-fw"></i><span style="position: relative;">{{ i18n.ts.note }}</span> + <i :class="$style.postIcon" class="ph-pencil-simple ph-bold ph-lg ti-fw"></i><span style="position: relative;">{{ i18n.ts.note }}</span> </button> <button class="_button" :class="$style.account" @click="openAccountMenu"> <MkAvatar :user="$i" :class="$style.avatar"/><MkAcct :class="$style.acct" class="_nowrap" :user="$i"/> @@ -254,7 +254,7 @@ function more() { left: 20px; color: var(--navIndicator); font-size: 8px; - animation: blink 1s infinite; + animation: global-blink 1s infinite; &:has(.itemIndicateValueIcon) { animation: none; diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 20d4564770ab18c9f0cfe54922ed5d05a2fe14c9..65763bcfa8f4687ae968722f3227a8276eb0d293 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div :class="$style.bottom"> <button v-tooltip.noDelay.right="i18n.ts.note" class="_button" :class="[$style.post]" data-cy-open-post-form @click="os.post"> - <i class="ph-pencil ph-bold ph-lg ti-fw" :class="$style.postIcon"></i><span :class="$style.postText">{{ i18n.ts.note }}</span> + <i class="ph-pencil-simple ph-bold ph-lg ti-fw" :class="$style.postIcon"></i><span :class="$style.postText">{{ i18n.ts.note }}</span> </button> <button v-tooltip.noDelay.right="`${i18n.ts.account}: @${$i.username}`" class="_button" :class="[$style.account]" @click="openAccountMenu"> <MkAvatar :user="$i" :class="$style.avatar"/><MkAcct class="_nowrap" :class="$style.acct" :user="$i"/> @@ -313,7 +313,7 @@ function more(ev: MouseEvent) { left: 20px; color: var(--navIndicator); font-size: 8px; - animation: blink 1s infinite; + animation: global-blink 1s infinite; &:has(.itemIndicateValueIcon) { animation: none; @@ -483,7 +483,7 @@ function more(ev: MouseEvent) { left: 24px; color: var(--navIndicator); font-size: 8px; - animation: blink 1s infinite; + animation: global-blink 1s infinite; &:has(.itemIndicateValueIcon) { animation: none; diff --git a/packages/frontend/src/ui/_common_/notification.vue b/packages/frontend/src/ui/_common_/notification.vue index dc1a9a1b243bcb1f63311604e61e5a1218aef1cc..29ae04387ae61dc05f0a2f6b3dc56d7cf5ed960d 100644 --- a/packages/frontend/src/ui/_common_/notification.vue +++ b/packages/frontend/src/ui/_common_/notification.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/ui/_common_/statusbar-federation.vue b/packages/frontend/src/ui/_common_/statusbar-federation.vue index c92695afedf8b49b0b0ac87d41a14b2ce4b1191c..8dad6666235daac237d61f9b00e6b511087e0366 100644 --- a/packages/frontend/src/ui/_common_/statusbar-federation.vue +++ b/packages/frontend/src/ui/_common_/statusbar-federation.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import MarqueeText from '@/components/MkMarquee.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { useInterval } from '@/scripts/use-interval.js'; import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; @@ -52,7 +52,7 @@ const fetching = ref(true); const key = ref(0); const tick = () => { - os.api('federation/instances', { + misskeyApi('federation/instances', { sort: '+latestRequestReceivedAt', limit: 30, }).then(res => { diff --git a/packages/frontend/src/ui/_common_/statusbar-rss.vue b/packages/frontend/src/ui/_common_/statusbar-rss.vue index 58e109ad7f270b9d774308a7959f3e918068566f..b973a4fd6bb106b0a0f92ef3725f9870af8335fe 100644 --- a/packages/frontend/src/ui/_common_/statusbar-rss.vue +++ b/packages/frontend/src/ui/_common_/statusbar-rss.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/ui/_common_/statusbar-user-list.vue b/packages/frontend/src/ui/_common_/statusbar-user-list.vue index 6057174ba833db749a13ba029b4618cc3913731e..67f8b109c484a6d15855b510867852e0cd05e64e 100644 --- a/packages/frontend/src/ui/_common_/statusbar-user-list.vue +++ b/packages/frontend/src/ui/_common_/statusbar-user-list.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref, watch } from 'vue'; import * as Misskey from 'misskey-js'; import MarqueeText from '@/components/MkMarquee.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { useInterval } from '@/scripts/use-interval.js'; import { getNoteSummary } from '@/scripts/get-note-summary.js'; import { notePage } from '@/filters/note.js'; @@ -54,7 +54,7 @@ const key = ref(0); const tick = () => { if (props.userListId == null) return; - os.api('notes/user-list-timeline', { + misskeyApi('notes/user-list-timeline', { listId: props.userListId, }).then(res => { notes.value = res; diff --git a/packages/frontend/src/ui/_common_/statusbars.vue b/packages/frontend/src/ui/_common_/statusbars.vue index 81445df1e9bf84021affabbf475b4c2fef8db44f..872c69810c750011736e1571ca79e260d081cdcb 100644 --- a/packages/frontend/src/ui/_common_/statusbars.vue +++ b/packages/frontend/src/ui/_common_/statusbars.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/ui/_common_/stream-indicator.vue b/packages/frontend/src/ui/_common_/stream-indicator.vue index 4cb773d28aff9d9152f0ceaee4854ea8f2ec667e..968c3969bb15ab03f53cc3a2890fb403dabb445a 100644 --- a/packages/frontend/src/ui/_common_/stream-indicator.vue +++ b/packages/frontend/src/ui/_common_/stream-indicator.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/ui/_common_/sw-inject.ts b/packages/frontend/src/ui/_common_/sw-inject.ts index 5239b76705e506ceef94418ee850f052cc870b57..ff851ad99f2b3977835cf6b3d2f79c5461918c90 100644 --- a/packages/frontend/src/ui/_common_/sw-inject.ts +++ b/packages/frontend/src/ui/_common_/sw-inject.ts @@ -1,13 +1,14 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import { api, post } from '@/os.js'; +import { post } from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { $i, login } from '@/account.js'; import { getAccountFromId } from '@/scripts/get-account-from-id.js'; -import { mainRouter } from '@/router.js'; import { deepClone } from '@/scripts/clone.js'; +import { mainRouter } from '@/router/main.js'; export function swInject() { navigator.serviceWorker.addEventListener('message', async ev => { @@ -30,10 +31,10 @@ export function swInject() { // プッシュ通知ã‹ã‚‰æ¥ãŸreply,renoteã¯truncateBodyãŒé€šã•ã‚Œã¦ã„ã‚‹ãŸã‚〠// 完全ãªãƒŽãƒ¼ãƒˆã‚’å–å¾—ã—ãªãŠã™ if (props.reply) { - props.reply = await api('notes/show', { noteId: props.reply.id }); + props.reply = await misskeyApi('notes/show', { noteId: props.reply.id }); } if (props.renote) { - props.renote = await api('notes/show', { noteId: props.renote.id }); + props.renote = await misskeyApi('notes/show', { noteId: props.renote.id }); } return post(props); } diff --git a/packages/frontend/src/ui/_common_/upload.vue b/packages/frontend/src/ui/_common_/upload.vue index eb8c114f1746d845cc98779d1ac111a569be9189..244bac6f102eadcc8b7e17c424b564be9762d953 100644 --- a/packages/frontend/src/ui/_common_/upload.vue +++ b/packages/frontend/src/ui/_common_/upload.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/ui/classic.header.vue b/packages/frontend/src/ui/classic.header.vue index f0e0271128ccd1b32c85f92d47f2570005e89867..527670e103a947a0c364d155f60ce409aa53bbdc 100644 --- a/packages/frontend/src/ui/classic.header.vue +++ b/packages/frontend/src/ui/classic.header.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only </button> <div class="post" @click="os.post()"> <MkButton class="button" gradate full rounded> - <i class="ph-pencil ph-bold ph-lg ti-fw"></i> + <i class="ph-pencil-simple ph-bold ph-lg ti-fw"></i> </MkButton> </div> </div> @@ -141,7 +141,7 @@ onMounted(() => { left: 0; color: var(--navIndicator); font-size: 8px; - animation: blink 1s infinite; + animation: global-blink 1s infinite; } &:hover { diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue index 4fa8f1b434ba9e9de45b783f39fa8b1fc4b7d90f..25b9095574bedf585a34fde17afd31caf6c1e565 100644 --- a/packages/frontend/src/ui/classic.sidebar.vue +++ b/packages/frontend/src/ui/classic.sidebar.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only </button> <div class="post" data-cy-open-post-form @click="os.post"> <MkButton class="button" gradate full rounded> - <i class="ph-pencil ph-bold ph-lg ti-fw"></i><span v-if="!iconOnly" class="text">{{ i18n.ts.note }}</span> + <i class="ph-pencil-simple ph-bold ph-lg ti-fw"></i><span v-if="!iconOnly" class="text">{{ i18n.ts.note }}</span> </MkButton> </div> <div class="divider"></div> @@ -221,7 +221,7 @@ watch(defaultStore.reactiveState.menuDisplay, () => { left: 0; color: var(--navIndicator); font-size: 8px; - animation: blink 1s infinite; + animation: global-blink 1s infinite; &:has(.itemIndicateValueIcon) { animation: none; diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue index 3bb9097985cbb7dac16e0f0a44da54e29a9de301..ea9ea56b906de3125024436b76747ebfe02dcd35 100644 --- a/packages/frontend/src/ui/classic.vue +++ b/packages/frontend/src/ui/classic.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -52,19 +52,21 @@ import XCommon from './_common_/common.vue'; import { instanceName } from '@/config.js'; import { StickySidebar } from '@/scripts/sticky-sidebar.js'; import * as os from '@/os.js'; -import { mainRouter } from '@/router.js'; -import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js'; +import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import { miLocalStorage } from '@/local-storage.js'; +import { mainRouter } from '@/router/main.js'; const XHeaderMenu = defineAsyncComponent(() => import('./classic.header.vue')); const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue')); +const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index'); + const DESKTOP_THRESHOLD = 1100; const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD); -const pageMetadata = ref<null | PageMetadata>(); +const pageMetadata = ref<null | PageMetadata>(null); const widgetsShowing = ref(false); const fullView = ref(false); const globalHeaderHeight = ref(0); @@ -75,12 +77,18 @@ const widgetsLeft = ref<HTMLElement>(); const widgetsRight = ref<HTMLElement>(); provide('router', mainRouter); -provideMetadataReceiver((info) => { - pageMetadata.value = info.value; +provideMetadataReceiver((metadataGetter) => { + const info = metadataGetter(); + pageMetadata.value = info; if (pageMetadata.value) { - document.title = `${pageMetadata.value.title} | ${instanceName}`; + if (isRoot.value && pageMetadata.value.title === instanceName) { + document.title = pageMetadata.value.title; + } else { + document.title = `${pageMetadata.value.title} | ${instanceName}`; + } } }); +provideReactiveMetadata(pageMetadata); provide('shouldHeaderThin', showMenuOnTop.value); provide('forceSpacerMin', true); diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue index 0df814fc88d7c2a50208b4d3c24991284c3f7790..68c7f0fcd23e3bf989eaa8d2ee2ff619b9336c92 100644 --- a/packages/frontend/src/ui/deck.vue +++ b/packages/frontend/src/ui/deck.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -58,7 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ $i.unreadNotificationsCount > 99 ? '99+' : $i.unreadNotificationsCount }}</span> </span> </button> - <button :class="$style.postButton" class="_button" @click="os.post()"><i :class="$style.navButtonIcon" class="ph-pencil ph-bold ph-lg"></i></button> + <button :class="$style.postButton" class="_button" @click="os.post()"><i :class="$style.navButtonIcon" class="ph-pencil-simple ph-bold ph-lg"></i></button> </div> <Transition @@ -103,7 +103,6 @@ import * as os from '@/os.js'; import { navbarItemDef } from '@/navbar.js'; import { $i } from '@/account.js'; import { i18n } from '@/i18n.js'; -import { mainRouter } from '@/router.js'; import { unisonReload } from '@/scripts/unison-reload.js'; import { deviceKind } from '@/scripts/device-kind.js'; import { defaultStore } from '@/store.js'; @@ -117,6 +116,8 @@ import XWidgetsColumn from '@/ui/deck/widgets-column.vue'; import XMentionsColumn from '@/ui/deck/mentions-column.vue'; import XDirectColumn from '@/ui/deck/direct-column.vue'; import XRoleTimelineColumn from '@/ui/deck/role-timeline-column.vue'; +import { mainRouter } from '@/router/main.js'; +import { MenuItem } from '@/types/menu.js'; const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue')); const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announcements.vue')); @@ -189,7 +190,7 @@ const addColumn = async (ev) => { const { canceled, result: column } = await os.select({ title: i18n.ts._deck.addColumn, items: columns.map(column => ({ - value: column, text: i18n.t('_deck._columns.' + column), + value: column, text: i18n.ts._deck._columns[column], })), }); if (canceled) return; @@ -197,7 +198,7 @@ const addColumn = async (ev) => { addColumnToStore({ type: column, id: uuid(), - name: i18n.t('_deck._columns.' + column), + name: i18n.ts._deck._columns[column], width: 330, }); }; @@ -221,42 +222,41 @@ document.documentElement.style.scrollBehavior = 'auto'; loadDeck(); function changeProfile(ev: MouseEvent) { - const items = ref([{ + let items: MenuItem[] = [{ text: deckStore.state.profile, - active: true.valueOf, - }]); + active: true, + action: () => {}, + }]; getProfiles().then(profiles => { - items.value = [{ - text: deckStore.state.profile, - active: true.valueOf, - }, ...(profiles.filter(k => k !== deckStore.state.profile).map(k => ({ + items.push(...(profiles.filter(k => k !== deckStore.state.profile).map(k => ({ text: k, action: () => { deckStore.set('profile', k); unisonReload(); }, - }))), { type: 'divider' }, { + }))), { type: 'divider' as const }, { text: i18n.ts._deck.newProfile, icon: 'ph-plus ph-bold ph-lg', action: async () => { const { canceled, result: name } = await os.inputText({ title: i18n.ts._deck.profile, - allowEmpty: false, + minLength: 1, }); if (canceled) return; deckStore.set('profile', name); unisonReload(); }, - }]; + }); + }).then(() => { + os.popupMenu(items, ev.currentTarget ?? ev.target); }); - os.popupMenu(items, ev.currentTarget ?? ev.target); } async function deleteProfile() { const { canceled } = await os.confirm({ type: 'warning', - text: i18n.t('deleteAreYouSure', { x: deckStore.state.profile }), + text: i18n.tsx.deleteAreYouSure({ x: deckStore.state.profile }), }); if (canceled) return; @@ -325,7 +325,7 @@ body { } .rootIsMobile { - padding-bottom: 100px; + padding-bottom: 58px; } .main { @@ -446,20 +446,20 @@ body { .navButton { position: relative; padding: 0; - aspect-ratio: 1; + height: 32px; width: 100%; max-width: 60px; margin: auto; - border-radius: var(--radius-full); - background: var(--panel); + border-radius: var(--radius-lg); + background: transparent; color: var(--fg); &:hover { - background: var(--panelHighlight); + color: var(--accent); } &:active { - background: var(--X2); + color: var(--accent); } } @@ -470,15 +470,17 @@ body { &:hover { background: linear-gradient(90deg, var(--X8), var(--X8)); + color: var(--fgOnAccent); } &:active { background: linear-gradient(90deg, var(--X8), var(--X8)); + color: var(--fgOnAccent); } } .navButtonIcon { - font-size: 18px; + font-size: 16px; vertical-align: middle; } @@ -488,7 +490,7 @@ body { left: 0; color: var(--indicator); font-size: 16px; - animation: blink 1s infinite; + animation: global-blink 1s infinite; &:has(.itemIndicateValueIcon) { animation: none; diff --git a/packages/frontend/src/ui/deck/antenna-column.vue b/packages/frontend/src/ui/deck/antenna-column.vue index 7cd1d6aee94142816555d7685a40fe166ab687ee..79c7c48073c1922e69f8dba0c755aaefacd29afe 100644 --- a/packages/frontend/src/ui/deck/antenna-column.vue +++ b/packages/frontend/src/ui/deck/antenna-column.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -19,6 +19,7 @@ import XColumn from './column.vue'; import { updateColumn, Column } from './deck-store.js'; import MkTimeline from '@/components/MkTimeline.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ @@ -35,7 +36,7 @@ onMounted(() => { }); async function setAntenna() { - const antennas = await os.api('antennas/list'); + const antennas = await misskeyApi('antennas/list'); const { canceled, result: antenna } = await os.select({ title: i18n.ts.selectAntenna, items: antennas.map(x => ({ @@ -55,7 +56,7 @@ function editAntenna() { const menu = [ { - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', text: i18n.ts.selectAntenna, action: setAntenna, }, diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue index 95ed900f7dcd38da88891db71a0e090744ec03ab..984de82c3fd58f126495de8765b09239f807ce87 100644 --- a/packages/frontend/src/ui/deck/channel-column.vue +++ b/packages/frontend/src/ui/deck/channel-column.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-if="column.channelId"> <div style="padding: 8px; text-align: center;"> - <MkButton primary gradate rounded inline @click="post"><i class="ph-pencil ph-bold ph-lg"></i></MkButton> + <MkButton primary gradate rounded inline small @click="post"><i class="ph-pencil-simple ph-bold ph-lg"></i></MkButton> </div> <MkTimeline ref="timeline" src="channel" :channel="column.channelId"/> </template> @@ -26,6 +26,7 @@ import { updateColumn, Column } from './deck-store.js'; import MkTimeline from '@/components/MkTimeline.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ @@ -41,7 +42,7 @@ if (props.column.channelId == null) { } async function setChannel() { - const channels = await os.api('channels/my-favorites', { + const channels = await misskeyApi('channels/my-favorites', { limit: 100, }); const { canceled, result: channel } = await os.select({ @@ -60,7 +61,7 @@ async function setChannel() { async function post() { if (!channel.value || channel.value.id !== props.column.channelId) { - channel.value = await os.api('channels/show', { + channel.value = await misskeyApi('channels/show', { channelId: props.column.channelId, }); } @@ -71,7 +72,7 @@ async function post() { } const menu = [{ - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', text: i18n.ts.selectChannel, action: setChannel, }]; diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue index 9ed7e452e3cda0f3317688f1f2b5fb8fe255b337..f9efb9d88ceabcccfe06ff5252e71d53c51eca0f 100644 --- a/packages/frontend/src/ui/deck/column.vue +++ b/packages/frontend/src/ui/deck/column.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts index e68b7bba8c076fc8dcf6bd270c9d829c591430a7..6c4e2fd52b28295e9f2c365ef63560d50fb136ad 100644 --- a/packages/frontend/src/ui/deck/deck-store.ts +++ b/packages/frontend/src/ui/deck/deck-store.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -7,7 +7,7 @@ import { throttle } from 'throttle-debounce'; import { markRaw } from 'vue'; import { notificationTypes } from 'misskey-js'; import { Storage } from '@/pizzax.js'; -import { api } from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { deepClone } from '@/scripts/clone.js'; type ColumnWidget = { @@ -70,7 +70,7 @@ export const loadDeck = async () => { let deck; try { - deck = await api('i/registry/get', { + deck = await misskeyApi('i/registry/get', { scope: ['client', 'deck', 'profiles'], key: deckStore.state.profile, }); @@ -95,7 +95,7 @@ export const loadDeck = async () => { // TODO: deckãŒloadã•ã‚Œã¦ã„ãªã„状態ã§saveã™ã‚‹ã¨æ„図ã›ãšä¸Šæ›¸ããŒç™ºç”Ÿã™ã‚‹ã®ã§å¯¾ç–ã™ã‚‹ export const saveDeck = throttle(1000, () => { - api('i/registry/set', { + misskeyApi('i/registry/set', { scope: ['client', 'deck', 'profiles'], key: deckStore.state.profile, value: { @@ -106,13 +106,13 @@ export const saveDeck = throttle(1000, () => { }); export async function getProfiles(): Promise<string[]> { - return await api('i/registry/keys', { + return await misskeyApi('i/registry/keys', { scope: ['client', 'deck', 'profiles'], }); } export async function deleteProfile(key: string): Promise<void> { - return await api('i/registry/remove', { + return await misskeyApi('i/registry/remove', { scope: ['client', 'deck', 'profiles'], key: key, }); diff --git a/packages/frontend/src/ui/deck/direct-column.vue b/packages/frontend/src/ui/deck/direct-column.vue index e2a212be46f32e0506307fd94e6f0df861f2f6cc..09412ce386748325159a3f19836f96b9c619725c 100644 --- a/packages/frontend/src/ui/deck/direct-column.vue +++ b/packages/frontend/src/ui/deck/direct-column.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/ui/deck/list-column.vue b/packages/frontend/src/ui/deck/list-column.vue index 45ecc476e7ae788f515a04be2d792d8d93e6544a..128562823b988df2ee9e81d096b61a85d3ba26ec 100644 --- a/packages/frontend/src/ui/deck/list-column.vue +++ b/packages/frontend/src/ui/deck/list-column.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -19,6 +19,7 @@ import XColumn from './column.vue'; import { updateColumn, Column } from './deck-store.js'; import MkTimeline from '@/components/MkTimeline.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ @@ -40,7 +41,7 @@ watch(withRenotes, v => { }); async function setList() { - const lists = await os.api('users/lists/list'); + const lists = await misskeyApi('users/lists/list'); const { canceled, result: list } = await os.select({ title: i18n.ts.selectList, items: lists.map(x => ({ @@ -60,7 +61,7 @@ function editList() { const menu = [ { - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', text: i18n.ts.selectList, action: setList, }, diff --git a/packages/frontend/src/ui/deck/main-column.vue b/packages/frontend/src/ui/deck/main-column.vue index cd567040f4cd79f3518df3ba5bf5c17d650a4f56..847dcf247a6bf30fa80c35c69290d3f6166615d6 100644 --- a/packages/frontend/src/ui/deck/main-column.vue +++ b/packages/frontend/src/ui/deck/main-column.vue @@ -1,14 +1,14 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <XColumn v-if="deckStore.state.alwaysShowMainColumn || mainRouter.currentRoute.value.name !== 'index'" :column="column" :isStacked="isStacked"> <template #header> - <template v-if="pageMetadata?.value"> - <i :class="pageMetadata?.value.icon"></i> - {{ pageMetadata?.value.title }} + <template v-if="pageMetadata"> + <i :class="pageMetadata.icon"></i> + {{ pageMetadata.title }} </template> </template> @@ -19,15 +19,15 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ComputedRef, provide, shallowRef, ref } from 'vue'; +import { provide, shallowRef, ref } from 'vue'; import XColumn from './column.vue'; import { deckStore, Column } from '@/ui/deck/deck-store.js'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; -import { mainRouter } from '@/router.js'; -import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js'; +import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; import { useScrollPositionManager } from '@/nirax.js'; import { getScrollContainer } from '@/scripts/scroll.js'; +import { mainRouter } from '@/router/main.js'; defineProps<{ column: Column; @@ -35,12 +35,14 @@ defineProps<{ }>(); const contents = shallowRef<HTMLElement>(); -const pageMetadata = ref<null | ComputedRef<PageMetadata>>(); +const pageMetadata = ref<null | PageMetadata>(null); provide('router', mainRouter); -provideMetadataReceiver((info) => { +provideMetadataReceiver((metadataGetter) => { + const info = metadataGetter(); pageMetadata.value = info; }); +provideReactiveMetadata(pageMetadata); /* function back() { diff --git a/packages/frontend/src/ui/deck/mentions-column.vue b/packages/frontend/src/ui/deck/mentions-column.vue index 7df07fd8d7a4ee622a0a33e6613f2024c6ad7164..70ec98119a9d22361aaa5c261342555488a45185 100644 --- a/packages/frontend/src/ui/deck/mentions-column.vue +++ b/packages/frontend/src/ui/deck/mentions-column.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/ui/deck/notifications-column.vue b/packages/frontend/src/ui/deck/notifications-column.vue index 6a28bab0911a15e7f16da7451cb025814d24a1b0..837953b1e8971a1f883ba259053332148e0aea18 100644 --- a/packages/frontend/src/ui/deck/notifications-column.vue +++ b/packages/frontend/src/ui/deck/notifications-column.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -40,7 +40,7 @@ function func() { } const menu = [{ - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', text: i18n.ts.notificationSetting, action: func, }]; diff --git a/packages/frontend/src/ui/deck/role-timeline-column.vue b/packages/frontend/src/ui/deck/role-timeline-column.vue index 5fbd1389b7b4d1ff5d301528413b5e0c1a2b60ca..1a673a17538b2b6f2f56a04172a45994257fe7bd 100644 --- a/packages/frontend/src/ui/deck/role-timeline-column.vue +++ b/packages/frontend/src/ui/deck/role-timeline-column.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -19,6 +19,7 @@ import XColumn from './column.vue'; import { updateColumn, Column } from './deck-store.js'; import MkTimeline from '@/components/MkTimeline.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ @@ -35,7 +36,7 @@ onMounted(() => { }); async function setRole() { - const roles = (await os.api('roles/list')).filter(x => x.isExplorable); + const roles = (await misskeyApi('roles/list')).filter(x => x.isExplorable); const { canceled, result: role } = await os.select({ title: i18n.ts.role, items: roles.map(x => ({ @@ -50,7 +51,7 @@ async function setRole() { } const menu = [{ - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', text: i18n.ts.role, action: setRole, }]; diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue index f6167b08f94b44929cef0dfe69c8102f86265fce..3745d026e8463f9f19547cd5355866eba30b3110 100644 --- a/packages/frontend/src/ui/deck/tl-column.vue +++ b/packages/frontend/src/ui/deck/tl-column.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -114,7 +114,7 @@ async function setType() { } const menu = [{ - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', text: i18n.ts.timeline, action: setType, }, { diff --git a/packages/frontend/src/ui/deck/widgets-column.vue b/packages/frontend/src/ui/deck/widgets-column.vue index d111f92443dd95c2f1c1bea86f03da631f9b7e53..92d1a673f60f371320fd46f51b991f9042c2ce57 100644 --- a/packages/frontend/src/ui/deck/widgets-column.vue +++ b/packages/frontend/src/ui/deck/widgets-column.vue @@ -1,11 +1,11 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <XColumn :menu="menu" :naked="true" :column="column" :isStacked="isStacked"> - <template #header><i class="ph-squares-four ph-bold ph-lg" style="margin-right: 8px;"></i>{{ column.name }}</template> + <template #header><i class="ph-stack ph-bold ph-lg" style="margin-right: 8px;"></i>{{ column.name }}</template> <div :class="$style.root"> <div v-if="!(column.widgets && column.widgets.length > 0) && !edit" :class="$style.intro">{{ i18n.ts._deck.widgetsIntroduction }}</div> @@ -49,7 +49,7 @@ function func() { } const menu = [{ - icon: 'ph-pencil ph-bold ph-lg', + icon: 'ph-pencil-simple ph-bold ph-lg', text: i18n.ts.editWidgets, action: func, }]; diff --git a/packages/frontend/src/ui/minimum.vue b/packages/frontend/src/ui/minimum.vue index f32f2de3df438c38851ee4fb5a8aaa9cc49a4357..db5eb19c2079e680ce6219fac5a45489ab1c2f44 100644 --- a/packages/frontend/src/ui/minimum.vue +++ b/packages/frontend/src/ui/minimum.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -14,21 +14,29 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { provide, ComputedRef, ref } from 'vue'; +import { computed, provide, ref } from 'vue'; import XCommon from './_common_/common.vue'; -import { mainRouter } from '@/router.js'; -import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js'; +import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; import { instanceName } from '@/config.js'; +import { mainRouter } from '@/router/main.js'; -const pageMetadata = ref<null | ComputedRef<PageMetadata>>(); +const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index'); + +const pageMetadata = ref<null | PageMetadata>(null); provide('router', mainRouter); -provideMetadataReceiver((info) => { +provideMetadataReceiver((metadataGetter) => { + const info = metadataGetter(); pageMetadata.value = info; - if (pageMetadata.value.value) { - document.title = `${pageMetadata.value.value.title} | ${instanceName}`; + if (pageMetadata.value) { + if (isRoot.value && pageMetadata.value.title === instanceName) { + document.title = pageMetadata.value.title; + } else { + document.title = `${pageMetadata.value.title} | ${instanceName}`; + } } }); +provideReactiveMetadata(pageMetadata); document.documentElement.style.overflowY = 'scroll'; </script> diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index 1d8e26bfccc8c85c7aab40eb72d3b3bfbdd6ce0d..3a48c5eab9f1d2ec890eb81e6f51c71d881017f5 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -22,19 +22,19 @@ SPDX-License-Identifier: AGPL-3.0-only <XWidgets/> </div> - <button v-if="(!isDesktop || pageMetadata?.needWideArea) && !isMobile" :class="$style.widgetButton" class="_button" @click="widgetsShowing = true"><i class="ph-squares-four ph-bold ph-lg"></i></button> + <button v-if="(!isDesktop || pageMetadata?.needWideArea) && !isMobile" :class="$style.widgetButton" class="_button" @click="widgetsShowing = true"><i class="ph-stack ph-bold ph-lg"></i></button> <div v-if="isMobile" ref="navFooter" :class="$style.nav"> <button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i :class="$style.navButtonIcon" class="ph-list ph-bold ph-lg-2"></i><span v-if="menuIndicated" :class="$style.navButtonIndicator"><i class="_indicatorCircle"></i></span></button> - <button :class="$style.navButton" class="_button" @click="mainRouter.currentRoute.value.name === 'index' ? top() : mainRouter.push('/')"><i :class="$style.navButtonIcon" class="ph-house ph-bold ph-lg"></i></button> + <button :class="$style.navButton" class="_button" @click="isRoot ? top() : mainRouter.push('/')"><i :class="$style.navButtonIcon" class="ph-house ph-bold ph-lg"></i></button> <button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')"> <i :class="$style.navButtonIcon" class="ph-bell ph-bold ph-lg"></i> <span v-if="$i?.hasUnreadNotification" :class="$style.navButtonIndicator"> <span class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ $i.unreadNotificationsCount > 99 ? '99+' : $i.unreadNotificationsCount }}</span> </span> </button> - <button :class="$style.navButton" class="_button" @click="widgetsShowing = true"><i :class="$style.navButtonIcon" class="ph-squares-four ph-bold ph-lg"></i></button> - <button :class="$style.postButton" class="_button" @click="os.post()"><i :class="$style.navButtonIcon" class="ph-pencil ph-bold ph-lg"></i></button> + <button :class="$style.navButton" class="_button" @click="widgetsShowing = true"><i :class="$style.navButtonIcon" class="ph-stack ph-bold ph-lg"></i></button> + <button :class="$style.postButton" class="_button" @click="os.post()"><i :class="$style.navButtonIcon" class="ph-pencil-simple ph-bold ph-lg"></i></button> </div> <Transition @@ -105,18 +105,20 @@ import { defaultStore } from '@/store.js'; import { navbarItemDef } from '@/navbar.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; -import { mainRouter } from '@/router.js'; -import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js'; +import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; import { deviceKind } from '@/scripts/device-kind.js'; import { miLocalStorage } from '@/local-storage.js'; import { CURRENT_STICKY_BOTTOM } from '@/const.js'; import { useScrollPositionManager } from '@/nirax.js'; +import { mainRouter } from '@/router/main.js'; const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue')); const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/navbar.vue')); const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue')); const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announcements.vue')); +const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index'); + const DESKTOP_THRESHOLD = 1100; const MOBILE_THRESHOLD = 500; @@ -127,18 +129,24 @@ window.addEventListener('resize', () => { isMobile.value = deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD; }); -const pageMetadata = ref<null | PageMetadata>(); +const pageMetadata = ref<null | PageMetadata>(null); const widgetsShowing = ref(false); const navFooter = shallowRef<HTMLElement>(); const contents = shallowRef<InstanceType<typeof MkStickyContainer>>(); provide('router', mainRouter); -provideMetadataReceiver((info) => { - pageMetadata.value = info.value; +provideMetadataReceiver((metadataGetter) => { + const info = metadataGetter(); + pageMetadata.value = info; if (pageMetadata.value) { - document.title = `${pageMetadata.value.title} | ${instanceName}`; + if (isRoot.value && pageMetadata.value.title === instanceName) { + document.title = pageMetadata.value.title; + } else { + document.title = `${pageMetadata.value.title} | ${instanceName}`; + } } }); +provideReactiveMetadata(pageMetadata); const menuIndicated = computed(() => { for (const def in navbarItemDef) { @@ -406,20 +414,20 @@ $widgets-hide-threshold: 1090px; .navButton { position: relative; padding: 0; - aspect-ratio: 1; + height: 32px; width: 100%; max-width: 60px; margin: auto; - border-radius: var(--radius-full); - background: var(--panel); + border-radius: var(--radius-lg); + background: transparent; color: var(--fg); &:hover { - background: var(--panelHighlight); + color: var(--accent); } &:active { - background: var(--X2); + color: var(--accent); } } @@ -430,15 +438,17 @@ $widgets-hide-threshold: 1090px; &:hover { background: linear-gradient(90deg, var(--X8), var(--X8)); + color: var(--fgOnAccent); } &:active { background: linear-gradient(90deg, var(--X8), var(--X8)); + color: var(--fgOnAccent); } } .navButtonIcon { - font-size: 18px; + font-size: 16px; vertical-align: middle; } @@ -448,7 +458,7 @@ $widgets-hide-threshold: 1090px; left: 0; color: var(--indicator); font-size: 16px; - animation: blink 1s infinite; + animation: global-blink 1s infinite; &:has(.itemIndicateValueIcon) { animation: none; diff --git a/packages/frontend/src/ui/universal.widgets.vue b/packages/frontend/src/ui/universal.widgets.vue index 57d6ae03304521ec138e0ce2dfe00c6e7ed56b24..7e413284030f6acfd783c77c1d47a47cb9398f6a 100644 --- a/packages/frontend/src/ui/universal.widgets.vue +++ b/packages/frontend/src/ui/universal.widgets.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <XWidgets :edit="editMode" :widgets="widgets" @addWidget="addWidget" @removeWidget="removeWidget" @updateWidget="updateWidget" @updateWidgets="updateWidgets" @exit="editMode = false"/> <button v-if="editMode" class="_textButton" style="font-size: 0.9em;" @click="editMode = false"><i class="ph-check ph-bold ph-lg"></i> {{ i18n.ts.editWidgetsExit }}</button> - <button v-else class="_textButton" data-cy-widget-edit :class="$style.edit" style="font-size: 0.9em;" @click="editMode = true"><i class="ph-pencil ph-bold ph-lg"></i> {{ i18n.ts.editWidgets }}</button> + <button v-else class="_textButton" data-cy-widget-edit :class="$style.edit" style="font-size: 0.9em;" @click="editMode = true"><i class="ph-pencil-simple ph-bold ph-lg"></i> {{ i18n.ts.editWidgets }}</button> </div> </template> diff --git a/packages/frontend/src/ui/visitor.vue b/packages/frontend/src/ui/visitor.vue index 78d7e2368989827a344516e711368f325cf96f66..f76372ae344c0f0b08e3e35f4e314584030bbc09 100644 --- a/packages/frontend/src/ui/visitor.vue +++ b/packages/frontend/src/ui/visitor.vue @@ -1,11 +1,11 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> <template> <div class="mk-app"> - <div v-if="!narrow && !root" class="side"> + <div v-if="!narrow && !isRoot" class="side"> <div class="banner" :style="{ backgroundImage: instance.backgroundImageUrl ? `url(${ instance.backgroundImageUrl })` : 'none' }"></div> <div class="dashboard"> <MkVisitorDashboard/> @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div class="main"> - <div v-if="!root" class="header"> + <div v-if="!isRoot" class="header"> <div v-if="narrow === false" class="wide"> <MkA to="/" class="link" activeClass="active"><i class="ph-house ph-bold ph-lg icon"></i> {{ i18n.ts.home }}</MkA> <MkA v-if="isTimelineAvailable" to="/timeline" class="link" activeClass="active"><i class="ph-chat-text ph-bold ph-lg icon"></i> {{ i18n.ts.timeline }}</MkA> @@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> <div class="contents"> - <main v-if="!root" style="container-type: inline-size;"> + <main v-if="!isRoot" style="container-type: inline-size;"> <RouterView/> </main> <main v-else> @@ -67,31 +67,40 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ComputedRef, onMounted, provide, ref, computed } from 'vue'; +import { onMounted, provide, ref, computed } from 'vue'; import * as Misskey from 'misskey-js'; import XCommon from './_common_/common.vue'; import { instanceName } from '@/config.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { instance } from '@/instance.js'; import XSigninDialog from '@/components/MkSigninDialog.vue'; import XSignupDialog from '@/components/MkSignupDialog.vue'; import { ColdDeviceStorage, defaultStore } from '@/store.js'; -import { mainRouter } from '@/router.js'; -import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js'; +import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue'; +import { mainRouter } from '@/router/main.js'; + +const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index'); const DESKTOP_THRESHOLD = 1100; -const pageMetadata = ref<null | ComputedRef<PageMetadata>>(); +const pageMetadata = ref<null | PageMetadata>(null); provide('router', mainRouter); -provideMetadataReceiver((info) => { +provideMetadataReceiver((metadataGetter) => { + const info = metadataGetter(); pageMetadata.value = info; - if (pageMetadata.value.value) { - document.title = `${pageMetadata.value.value.title} | ${instanceName}`; + if (pageMetadata.value) { + if (isRoot.value && pageMetadata.value.title === instanceName) { + document.title = pageMetadata.value.title; + } else { + document.title = `${pageMetadata.value.title} | ${instanceName}`; + } } }); +provideReactiveMetadata(pageMetadata); const announcements = { endpoint: 'announcements', @@ -117,9 +126,7 @@ const keymap = computed(() => { }; }); -const root = computed(() => mainRouter.currentRoute.value.name === 'index'); - -os.api('meta', { detail: true }).then(res => { +misskeyApi('meta', { detail: true }).then(res => { meta.value = res; }); diff --git a/packages/frontend/src/ui/zen.vue b/packages/frontend/src/ui/zen.vue index 9f92f787647778ce4562049f6ba08502b2578a73..77193a34758796b51907ddc471c8a343b7143e59 100644 --- a/packages/frontend/src/ui/zen.vue +++ b/packages/frontend/src/ui/zen.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -22,24 +22,32 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { provide, ComputedRef, ref } from 'vue'; +import { computed, provide, ref } from 'vue'; import XCommon from './_common_/common.vue'; -import { mainRouter } from '@/router.js'; -import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js'; +import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; import { instanceName, ui } from '@/config.js'; import { i18n } from '@/i18n.js'; +import { mainRouter } from '@/router/main.js'; -const pageMetadata = ref<null | ComputedRef<PageMetadata>>(); +const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index'); + +const pageMetadata = ref<null | PageMetadata>(null); const showBottom = !(new URLSearchParams(location.search)).has('zen') && ui === 'deck'; provide('router', mainRouter); -provideMetadataReceiver((info) => { +provideMetadataReceiver((metadataGetter) => { + const info = metadataGetter(); pageMetadata.value = info; - if (pageMetadata.value.value) { - document.title = `${pageMetadata.value.value.title} | ${instanceName}`; + if (pageMetadata.value) { + if (isRoot.value && pageMetadata.value.title === instanceName) { + document.title = pageMetadata.value.title; + } else { + document.title = `${pageMetadata.value.title} | ${instanceName}`; + } } }); +provideReactiveMetadata(pageMetadata); function goToMisskey() { window.location.href = '/'; diff --git a/packages/frontend/src/unicode-emoji-indexes/ja-JP.json b/packages/frontend/src/unicode-emoji-indexes/ja-JP.json new file mode 100644 index 0000000000000000000000000000000000000000..9c491804f2fce04ec217ada703c91feaddeaa899 --- /dev/null +++ b/packages/frontend/src/unicode-emoji-indexes/ja-JP.json @@ -0,0 +1,1866 @@ +{ + "😀":["ã«ã‚„ã«ã‚„ã—ãŸé¡”","é¡”","ã«ã‚„ã«ã‚„","幸ã›","ã—ã‚ã‚ã›"], + "😃":["å£ã‚’é–‹ã‘ãŸç¬‘é¡”","é¡”","å£","é–‹ã‘ã‚‹","笑顔","幸ã›","ã—ã‚ã‚ã›"], + "😄":["å£ã‚’é–‹ã‘ã¦ç›®ãŒç¬‘ã£ã¦ã„る笑顔","ç›®","é¡”","å£","é–‹ã‘ã‚‹","笑顔","幸ã›","ã—ã‚ã‚ã›"], + "ðŸ˜":["ã«ã‚„ã«ã‚„ã—ãŸé¡”","ç›®","é¡”","ã«ã‚„ã«ã‚„","笑顔"], + "😆":["å£ã‚’é–‹ã‘ã¦ç¬‘ã£ã¦ã„ã‚‹é¡”","é¡”","笑ã„","å£","é–‹ã‘ã‚‹","満足","笑顔"], + "😅":["å£ã‚’é–‹ã‘ã¦å†·ã‚„æ±—ã‚’ã‹ã„ãŸç¬‘é¡”","ãžã£ã¨ã™ã‚‹","é¡”","å£ã‚’é–‹ã‘ã‚‹","笑顔","冷や汗"], + "😂":["嬉ã—æ³£ã","é¡”","嬉ã—ã„","ã†ã‚Œã—ã„","笑ã†","æ³£ã","涙"], + "🤣":["大爆笑","é¡”","床","笑ã„","大笑ã„","爆笑","ãã‚‹ãã‚‹"], + "😇":["天使ã®ç¬‘é¡”","天使","é¡”","ãŠã¨ãŽè©±","ファンタジー","天使ã®è¼ª","無邪気","笑顔"], + "😉":["ウインクã—ãŸé¡”","é¡”","ウインク"], + "😊":["ç›®ãŒç¬‘ã£ã¦ã„る笑顔","赤é¢","ç›®","é¡”","笑顔"], + "🙂":["微笑ã¿","é¡”","笑顔","幸ã›","ã—ã‚ã‚ã›"], + "🙃":["逆ã•ã®é¡”","é¡”","逆ã•","ã•ã‹ã•"], + "☺ï¸":["笑顔","é¡”","輪éƒ","リラックス"], + "😋":["食ã¹ç‰©ã‚’味ã‚ã†é¡”","美味ã—ã„","ãŠã„ã—ã„","é¡”","味ã‚ã†","ãµãƒ¼ã‚€","ã†ã¾ã„"], + "😌":["ã»ã£ã¨ã—ãŸé¡”","é¡”","安心","ã»ã£ã¨ã™ã‚‹"], + "ðŸ˜":["ç›®ãŒãƒãƒ¼ãƒˆã®ç¬‘é¡”","ç›®","é¡”","ãƒãƒ¼ãƒˆ","æ„›","笑顔"], + "🥰":["笑顔ã¨ãƒãƒ¼ãƒˆ","é¡”","敬愛","ã¹ãŸã¼ã‚Œ","æ„›"], + "😘":["投ã’ã‚ッス","é¡”","ãƒãƒ¼ãƒˆ","ã‚ス"], + "😗":["ã‚スをã™ã‚‹é¡”","é¡”","ã‚ス"], + "😙":["笑顔ã§ã‚ス","ç›®","é¡”","ã‚ス","笑顔"], + "😚":["目を閉ã˜ã¦ã‚スをã™ã‚‹é¡”","é–‰ã˜ãŸ","ç›®","é¡”","ã‚ス"], + "🥲":["涙ã®å‡ºã¦ã„る笑顔","æ³£ã","幸ã›","æ„Ÿè¬ã™ã‚‹","誇りã«æ€ã†","安心ã™ã‚‹","笑ã†"], + "🤪":["ãŠã©ã‘ãŸé¡”","ç›®","ã«ã‚„ã«ã‚„","変","興奮","ワイルド"], + "😜":["舌を出ã—ã¦ã‚¦ã‚¤ãƒ³ã‚¯ã—ã¦ã„ã‚‹é¡”","ç›®","é¡”","冗談","舌","ウインク"], + "ðŸ˜":["舌を出ã—ã¦ç›®ã‚’ç´°ã‚ã¦ã„ã‚‹é¡”","ç›®","é¡”","怖ã„","æã„","ã“ã‚ã„","味","舌"], + "😛":["舌を出ã—ã¦ã„ã‚‹é¡”","é¡”","舌"], + "🤑":["強欲ãªé¡”","é¡”","ãŠé‡‘","å£"], + "😎":["サングラスをã‹ã‘ãŸé¡”","明るã„","ã‹ã£ã“ã„ã„","ç›®","アイウエア","é¡”","眼é¡","メガãƒ","笑顔","太陽","サングラス","天気"], + "🤓":["オタク","é¡”","変ãªäºº"], + "🥸":["仮装ã—ãŸé¡”","仮装","メガãƒ","匿åã®äºº","é¼»"], + "ðŸ§":["片メガãƒã‚’ã‹ã‘ãŸé¡”","退屈","裕ç¦","豊ã‹"], + "🤠":["カウボーイãƒãƒƒãƒˆã®é¡”","カウボーイ","カウガール","é¡”","帽å"], + "🥳":["パーティーフェイス","é¡”","ç¥å…¸","帽å","角","パーティー"], + "🤡":["ピエãƒã®é¡”","ピエãƒ","é¡”"], + "ðŸ˜":["ã«ã‚„ã«ã‚„ã—ãŸé¡”","é¡”","ã«ã‚„ã«ã‚„"], + "😶":["å£ã®ãªã„é¡”","é¡”","å£","é™ã‹ã«","沈黙"], + "🫥":["点線ã®é¡”","è½ã¡è¾¼ã‚“ã ","消ãˆã‚‹","éš ã‚Œã‚‹","内å‘çš„","ç›®ã«è¦‹ãˆãªã„"], + "ðŸ˜":["普通ã®é¡”","無表情","é¡”","å¹³é™"], + "🫤":["å£ãŒæ–œã‚ã«ãªã£ãŸé¡”","ãŒã£ã‹ã‚Š","無関心","ç–‘ã„æ·±ã„","ä¸å®‰"], + "😑":["無表情","é¡”","ãƒãƒ¼ã‚«ãƒ¼ãƒ•ã‚§ã‚¤ã‚¹","無感情"], + "😒":["é¢ç™½ããªã•ãã†ãªé¡”","é¡”","ã¤ã¾ã‚‰ãªã„","ä¸å¹¸"], + "🙄":["ãã‚‹ãã‚‹ç›®ã®é¡”","ç›®","é¡”","ãã‚‹ãã‚‹"], + "🤨":["眉ãŒä¸ŠãŒã£ã¦ã„ã‚‹é¡”","ä¸ä¿¡","ç–‘ã„æ·±ã„","éžé›£","疑念","ã‚„ã‚„é©šã","æ‡ç–‘çš„"], + "🤔":["考ãˆã¦ã„ã‚‹é¡”","é¡”","考ãˆä¸"], + "🤫":["シッã¨è¨€ã£ã¦ã„ã‚‹é¡”","シーッ","é™ã‹","黙る"], + "ðŸ¤":["å£ã‚’手ã§è¦†ã£ãŸé¡”","ç›®","笑顔","覆ã†","å£","手"], + "🫢":["目を開ã„ã¦å£ã‚’手ã§è¦†ã£ãŸé¡”","驚嘆","ç•æ•¬","ä¸ä¿¡","狼狽","怖ã„","é©šã"], + "🫡":["敬礼ã—ã¦ã„ã‚‹é¡”","ok","敬礼","晴天","部隊","ã¯ã„"], + "🤗":["両手を広ã’ãŸç¬‘é¡”","é¡”","ãƒã‚°","抱ãã—ã‚ã‚‹"], + "🫣":["ã®ãžã見ã—ã¦ã„ã‚‹é¡”","é…了","ã®ãžã見","å‡è¦–","ãƒãƒ©è¦‹"], + "🤥":["嘘ã¤ãé¡”","é¡”","嘘","ã†ã","ピノã‚オ"], + "😳":["赤ããªã£ãŸé¡”","ã¼ãƒ¼ã£ã¨ã—ãŸ","ã¼ã†ã£ã¨ã—ãŸ","é¡”","赤é¢"], + "😞":["ãŒã£ã‹ã‚Šã—ãŸé¡”","ãŒã£ã‹ã‚Š","é¡”"], + "😟":["ä¸å®‰ãªé¡”","é¡”","心é…","ä¸å®‰"], + "😤":["å‹ã¡èª‡ã£ãŸé¡”","é¡”","å‹åˆ©","å‹ã¤"], + "😠":["怒ã£ãŸé¡”","怒り","怒ã£ãŸ","é¡”","激怒"], + "😡":["ãµãれ顔","怒り","怒ã£ãŸ","é¡”","激怒","ãµãã‚Œã£é¢","ãµãã‚Œã£ã¤ã‚‰","憤怒","赤"], + "🤬":["å£ãŒè¨˜å·ã§è¦†ã‚ã‚ŒãŸé¡”","呪ã„","ã®ã®ã—ã‚Š","罵り"], + "😔":["悲ã—ã’ãªé¡”","ãŒã£ã‹ã‚Š","é¡”","悲ã—ã„"], + "😕":["å›°ã£ãŸé¡”","å›°ã£ãŸ","ã“ã¾ã£ãŸ","é¡”"], + "ðŸ™":["ã”機嫌斜ã‚","é¡”","ã—ã‹ã‚ã£é¢","ã—ã‹ã‚ã£ã¤ã‚‰","悲ã—ã„","ä¸å¹¸"], + "☹":["ã—ã‹ã‚ã£ã¤ã‚‰","é¡”","ã—ã‹ã‚ã£é¢","悲ã—ã„","ä¸å¹¸"], + "😬":["ã—ã‹ã‚ã£é¢","é¡”","ã—ã‹ã‚ã£ã¤ã‚‰"], + "🥺":["訴ãˆã‹ã‘ã‚‹é¡”","é¡”","物乞ã„","慈悲","å犬ã®ç›®"], + "😣":["我慢ã—ã¦ã„ã‚‹é¡”","é¡”","ãŒã‚“ã°ã‚‹","é ‘å¼µã‚‹"], + "😖":["ã†ã‚ãŸãˆãŸé¡”","戸惑ã„","ã¨ã¾ã©ã„","ã†ã‚ãŸãˆ","é¡”"], + "😫":["疲れãŸé¡”","é¡”","疲れãŸ","ã¤ã‹ã‚ŒãŸ"], + "😩":["ã†ã‚“ã–ã‚Šã—ã¦ã„ã‚‹é¡”","é¡”","疲れãŸ","ã¤ã‹ã‚ŒãŸ","ã†ã‚“ã–ã‚Š"], + "🥱":["ã‚ãã³ã—ã¦ã„ã‚‹é¡”","飽ããŸ","疲れãŸ","ã‚ãã³"], + "😪":["çœ ã„é¡”","é¡”","å¯ã‚‹","ç¡çœ "], + "😮â€ðŸ’¨":["ãŸã‚æ¯ã®å‡ºã¦ã„ã‚‹é¡”","é¡”","ãŸã‚æ¯","æ¯åˆ‡ã‚Œ","ã†ã‚ã","安心","ã•ã•ã‚„ã","å£ç¬›"], + "😮":["å£ã‚’é–‹ã‘ãŸç¬‘é¡”","é¡”","å£","é–‹ã‘ã‚‹","åŒæƒ…"], + "😱":["絶å«ã—ãŸé¡”","é¡”","æ怖","怖ã„","æã„","ã“ã‚ã„","ムンク","怯ãˆ","絶å«"], + "😨":["ゾッã¨ã—ã¦ã„ã‚‹é¡”","é¡”","æ怖","æã„","怖ã„","ã“ã‚ã„","怯ãˆ"], + "😰":["å£ã‚’é–‹ã‘ã¦å†·ã‚„æ±—ã‚’ã‹ã„ãŸé¡”","é’ã–ã‚ã‚‹","ãžã£ã¨ã™ã‚‹","é¡”","å£","é–‹ã‘ã‚‹","急ã","冷や汗"], + "😥":["ãŒã£ã‹ã‚Šã—ãŸãŒå®‰å¿ƒã—ãŸé¡”","ãŒã£ã‹ã‚Š","é¡”","安心","ã»ã£ã¨ã™ã‚‹","やれやれ"], + "😓":["冷や汗をã‹ã„ã¦ã„ã‚‹é¡”","ãžã£ã¨ã™ã‚‹","é¡”","冷や汗"], + "😯":["è½ã¡ç€ã„ãŸé¡”","é¡”","黙る","呆然","é©šã"], + "😦":["心é…ãã†ãªé¡”ã®çµµæ–‡å—","é¡”","ã—ã‹ã‚ã£é¢","ã—ã‹ã‚ã£ã¤ã‚‰","å£","é–‹ã‘ã‚‹"], + "😧":["苦悩ã«æº€ã¡ãŸé¡”","苦悩","é¡”"], + "🥹":["涙をã“らãˆã¦ã„ã‚‹é¡”","怒る","æ³£ã","誇りã«æ€ã†","逆らã†","悲ã—ã‚€"], + "😢":["æ³£ãé¡”","æ³£ã","é¡”","悲ã—ã„","涙"], + "ðŸ˜":["å·æ³£","æ³£ã","é¡”","悲ã—ã„","涙"], + "🤤":["よã れを垂らã—ãŸé¡”","よã ã‚Œ","é¡”"], + "🤩":["スターã«å¤¢ä¸","ç›®","é¡”","ã«ã‚„ã«ã‚„","星","夢想的"], + "😵":["ç›®ãŒãƒãƒ„ã«ãªã£ãŸé¡”","ã‚ã¾ã„","é¡”","ãƒãƒ„","ç›®"], + "😵â€ðŸ’«":["ç›®ãŒãã‚‹ãã‚‹ã—ã¦ã„ã‚‹é¡”","ã‚ã¾ã„","é¡”","ç›®","ã†ã£ã¨ã‚Š","ãã‚‹ãã‚‹","トラブル","ãŠãƒ¼"], + "🥴":["ã¼ã‚“ã‚„ã—ã‚ŠãŸé¡”","é¡”","ç›®ã¾ã„","é…©é…Š","ã»ã‚é…”ã„","ã¾ã£ã™ãã§ãªã„ç›®","波状ã®å£"], + "😲":["é©šã„ãŸé¡”","é©šã","ã³ã£ãã‚Š","é¡”","ショック","é©šæ„•"], + "🫨":["震ãˆã‚‹é¡”","地震","é¡”","震ãˆ","è¡æ’ƒ","振動"], + "🤯":["爆発ã—ãŸé ","é¡”","ショック","爆発","ç‹‚æ°—","ã³ã£ãã‚Š"], + "🫠":["ã»ã‚ã‚Šã¨ã—ãŸé¡”","消ãˆã‚‹","溶解ã™ã‚‹","液体","溶ã‘ã‚‹"], + "ðŸ¤":["ãŠå£ãƒãƒ£ãƒƒã‚¯","é¡”","å£","ãƒãƒ£ãƒƒã‚¯"], + "😷":["マスクをã—ãŸé¡”","風邪","ã‹ãœ","医者","é¡”","マスク","è–¬","ç—…æ°—"], + "🤕":["怪我","包帯","é¡”","å‚·","ã‚ズ","ã‘ãŒ"], + "🤒":["温度計をãã‚ãˆãŸé¡”","é¡”","ç—…æ°—","風邪","ã‹ãœ","体温計"], + "🤮":["åããã†ãªé¡”","ç—…æ°—","嘔å","風邪","ã‹ãœ","åã"], + "🤢":["åããã†ãªé¡”","é¡”","åãæ°—","嘔å"], + "🤧":["ãã—ゃã¿ã‚’ã™ã‚‹é¡”","é¡”","ãã—ゃã¿","ãƒã‚¯ã‚·ãƒ§ãƒ³"], + "🥵":["ã»ã¦ã£ãŸé¡”","é¡”","熱ã£ã½ã„","熱射病","ã»ã¦ã£ãŸ","赤ら顔","æ±—ã‚’ã‹ã„ãŸ"], + "🥶":["é’ã–ã‚ãŸé¡”","é¡”","ãžã£ã¨ã™ã‚‹","å‡ãˆã‚‹","å‡å‚·","ã¤ã‚‰ã‚‰"], + "😶â€ðŸŒ«ï¸":["雲ã§è¦†ã‚ã‚ŒãŸé¡”","é¡”","ãŠã£ã¡ã‚‡ã“ã¡ã‚‡ã„","éžç¾å®Ÿçš„","夢","ã‚‚ã‚„","雲ã§è¦†ã‚ã‚ŒãŸé "], + "😴":["å¯é¡”","é¡”","å¯ã‚‹","ç¡çœ ","スヤスヤ"], + "💤":["ç¡çœ ","マンガ","漫画","å¯ã‚‹","スヤスヤ"], + "😈":["角ã¤ã笑顔","é¡”","ãŠã¨ãŽè©±","ファンタジー","角","笑顔"], + "👿":["å°æ‚ªé”","鬼","悪é”","é¡”","ãŠã¨ãŽè©±","ファンタジー"], + "👹":["鬼","妖怪","é¡”","昔話","ファンタジー","日本","モンスター"], + "👺":["天狗","妖怪","é¡”","昔話","ファンタジー","日本","モンスター"], + "💩":["ã†ã‚“ã¡","マンガ","漫画","フン","é¡”","モンスター"], + "👻":["ãŠåŒ–ã‘","妖怪","é¡”","ãŠã¨ãŽè©±","ファンタジー","幽霊","モンスター","ãƒãƒã‚¦ã‚£ãƒ¼ãƒ³"], + "💀":["ドクãƒ","体","æ»","é¡”","ãŠã¨ãŽè©±","モンスター","骸骨","ãƒãƒã‚¦ã‚£ãƒ¼ãƒ³"], + "☠":["ドクãƒãƒžãƒ¼ã‚¯","体","交差ã—ãŸéª¨","æ»","é¡”","モンスター","骸骨","ãƒãƒã‚¦ã‚£ãƒ¼ãƒ³"], + "👽":["宇宙人","怪ç£","異星人","é¡”","ãŠã¨ãŽè©±","ファンタジー","モンスター","宇宙","UFO"], + "🤖":["ãƒãƒœãƒƒãƒˆã®é¡”","é¡”","モンスター","ãƒãƒœãƒƒãƒˆ"], + "🎃":["ジャック・オ・ランタン","イベント","ãŠç¥ã„","エンタメ","ãƒãƒã‚¦ã‚£ãƒ³","ジャックオランタン","ランタン","ã‹ã¼ã¡ã‚ƒ"], + "😺":["å£ã‚’é–‹ã‘ã¦ç¬‘ã†çŒ«","猫","ãƒã‚³","é¡”","å£","é–‹ã‘ã‚‹","笑顔"], + "😸":["ニヤニヤ笑ã†çŒ«","猫","ãƒã‚³","ç›®","é¡”","ニヤニヤ","笑顔"], + "😹":["嬉ã—æ³£ãã—ãŸãƒã‚³ã®é¡”","猫","ãƒã‚³","é¡”","嬉ã—ã„","ã†ã‚Œã—ã„","涙"], + "😻":["ãƒãƒ¼ãƒˆã®ç›®ã‚’ã—ãŸçŒ«ã®ç¬‘é¡”","猫","ãƒã‚³","ç›®","é¡”","ãƒãƒ¼ãƒˆ","æ„›","笑顔"], + "😼":["ニヤリã¨ç¬‘ã†çŒ«ã®é¡”","猫","ãƒã‚³","é¡”","皮肉","笑顔","ニヤリ"], + "😽":["目を閉ã˜ã¦ã‚スをã™ã‚‹çŒ«","猫","ãƒã‚³","ç›®","é¡”","ã‚ス"], + "🙀":["疲れãŸãƒã‚³ã®é¡”","猫","ãƒã‚³","é¡”","ã³ã£ãã‚Š","é©šã","ã†ã‚“ã–ã‚Š"], + "😿":["æ³£ã„ãŸãƒã‚³ã®é¡”","猫","ãƒã‚³","æ³£ã","é¡”","悲ã—ã„","涙"], + "😾":["怒ã£ãŸãƒã‚³ã®é¡”","猫","ãƒã‚³","é¡”","怒る","ãµãã‚Œã£é¢","ãµãã‚Œã£ã¤ã‚‰"], + "🫶":["ãƒãƒ¼ãƒˆãƒãƒ¼ã‚º","æ„›"], + "ðŸ‘":["é–‹ã„ãŸæ‰‹","体","手","広ã’ã‚‹"], + "🤲":["上ã«å‘ã‘ãŸä¸¡æ‰‹ã®ã²ã‚‰","体","祈り","カップã®ã‚ˆã†ã«ä¸¸ã‚ãŸæ‰‹"], + "🙌":["両手を上ã’ã‚‹","体","ãŠç¥ã„","ジェスãƒãƒ£ãƒ¼","手","ãƒãƒ³ã‚¶ã‚¤","万æ³","挙ã’ã‚‹"], + "ðŸ‘":["æ‹æ‰‹","体","手をå©ã","手"], + "ðŸ™":["æ¡ã£ãŸæ‰‹","é ¼ã‚€","体","ãŠè¾žå„€","手をåˆã‚ã›ã‚‹","ジェスãƒãƒ£ãƒ¼","手","ãŠé¡˜ã„","祈る","ã‚ã‚ŠãŒã¨ã†","æ„Ÿè¬"], + "ðŸ¤":["æ¡æ‰‹","åˆæ„","手","手をçµã¶","会è°"], + "ðŸ‘":["イイã","体","上","手","指","サムズアップ","+1"], + "👎":["ダメ","体","下","手","指","サムズダウン","-1"], + "👊":["æ¡ã‚Šã“ã¶ã—","体","æ¡ã‚‹","拳","ã“ã¶ã—","グー","手","パンãƒ","接近"], + "✊":["ã“ã¶ã—","体","æ¡ã‚‹","拳","グー","手","パンãƒ"], + "🤛":["å·¦å‘ãã®ã“ã¶ã—","体","拳","å·¦å‘ã"], + "🤜":["å³å‘ãã®ã“ã¶ã—","体","拳","å³å‘ã"], + "🤞":["交差ã•ã›ãŸæŒ‡","体","交差","指","手","幸é‹"], + "✌":["Vサイン","体","手","V","ブイ","å‹ã¤","å‹åˆ©","ピース"], + "🫰":["人差ã—指ã¨è¦ªæŒ‡ã‚’交差ã—ãŸæ‰‹","高ã„","ãƒãƒ¼ãƒˆ","æ„›","ãŠé‡‘","スナップ"], + "🤘":["コルナ","体","指","手","角","最高"], + "🤟":["æ„›ã—ã¦ã‚‹ã®ã‚¸ã‚§ã‚¹ãƒãƒ£ãƒ¼","体","æ„›ã—ã¦ã‚‹","好ã","手"], + "👌":["OKサイン","体","手","OK"], + "🤌":["ã¤ã¾ã‚“ã§ã„る指","指","手ã¶ã‚Š","å°‹å•","ã¤ã¾ã‚€","皮肉"], + "ðŸ¤":["ã¤ã¾ã‚“ã§ã„る手","体","手","å°ã•ã„","å°åž‹","ã¡ã£ã¡ã‚ƒã„"], + "👈":["左指差ã—","手ã®ç”²","体","指","手","人差ã—指","指ã•ã™"], + "🫳":["手ã®ã²ã‚‰ã‚’下ã«ã—ãŸæ‰‹","退ã‘ã‚‹","è½ã¨ã™","シッシ"], + "🫴":["手ã®ã²ã‚‰ã‚’上ã«ã—ãŸæ‰‹","手招ã","æ•ç²","æ¥ã‚‹","申ã—出"], + "👉":["指差ã—","手ã®ç”²","体","指","手","人差ã—指","指ã•ã™"], + "👆":["指差ã—","手ã®ç”²","体","指","手","人差ã—指","指ã•ã™","上"], + "👇":["指差ã—","手ã®ç”²","体","下","指","手","人差ã—指","指ã•ã™"], + "â˜":["指差ã—","体","指","手","人差ã—指","指ã•ã™","上"], + "✋":["挙手","体","手"], + "🤚":["手ã®ç”²","体","挙ã’ã‚‹"], + "ðŸ–":["広ã’ãŸæ‰‹ã®ã²ã‚‰","体","指","手","広ã’ã‚‹"], + "🖖":["長寿ã¨ç¹æ „ã‚’","体","指","手","スãƒãƒƒã‚¯","ãƒãƒ«ã‚«ãƒ³"], + "👋":["ãƒã‚¤ãƒã‚¤","体","手","振る","ã‚„ã£ã»ãƒ¼","ヤッホー","ã“ã‚“ã«ã¡ã¯"], + "🤙":["電話ã®å½¢ã®æ‰‹","体","電話","手"], + "🫲":["左手","手","å·¦","ã²ã ã‚Š"], + "🫱":["å³æ‰‹","手","å³","ã¿ãŽ"], + "🫷":["左を押ã—ã¦ã„る手","辞退","ãƒã‚¤ã‚¿ãƒƒãƒ","左方å‘","押ã—付ã‘ã‚‹","æ–ã‚‹","åœæ¢","å¾…ã¤"], + "🫸":["å³ã‚’押ã—ã¦ã„る手","辞退","ãƒã‚¤ã‚¿ãƒƒãƒ","押ã—付ã‘ã‚‹","æ–ã‚‹","å³æ–¹å‘","åœæ¢","å¾…ã¤"], + "💪":["曲ã’ãŸä¸Šè…•äºŒé ç‹","力ã“ã¶","体","マンガ","漫画","é‹å‹•","ç‹è‚‰","力","マッスル","マッãƒãƒ§"], + "🦾":["メカニカルアーム","アクセシビリティ","義手","人å£è£…å…·","体"], + "🖕":["ä¸æŒ‡ã‚’ç«‹ã¦ãŸæ‰‹","体","指","手","ä¸æŒ‡"], + "🫵":["見ã¦ã„る人を指ã—ã¦ã„る人差ã—指","指ã™","ã‚ãªãŸ","指"], + "âœ":["書ã„ã¦ã„る手","体","手","書ã"], + "🤳":["自撮り","カメラ","æºå¸¯","è…•"], + "💅":["マニã‚ュア","体","ケア","化粧å“","コスメ","爪","ãƒã‚¤ãƒ«"], + "🦵":["è„š","体","ã‚ック","手足"], + "🦿":["機械ã®è„š","アクセシビリティ","義足","人å£è£…å…·","体"], + "🦶":["足","体","ã‚ック","è¸ã¿ã¤ã‘ã‚‹"], + "👄":["å£","体","唇","クãƒãƒ“ル"], + "🫦":["ã‹ã‚“ã§ã„る唇","心é…","怖ã„","浮気","神経質","ä¸æ„‰å¿«","ä¸å®‰"], + "🦷":["æ¯","体","æ¯åŒ»è€…"], + "👅":["舌","体"], + "👂":["耳","体","é¼»"], + "🦻":["補è´å™¨ã‚’付ã‘ã¦ã„る耳","アクセシビリティ","補è´å™¨","èžã","体","耳"], + "👃":["é¼»","体"], + "ðŸ‘":["ç›®","体"], + "👀":["ç›®","体","é¡”"], + "🧠":["脳","体","臓器","知的","è³¢ã„"], + "🫀":["解剖å¦çš„ãªå¿ƒè‡“","解剖å¦","心臓å¦","心臓","臓器","脈"], + "ðŸ«":["肺","æ¯","呼気","å¸å…¥","臓器","呼å¸"], + "🦴":["骨","体","éª¨æ ¼"], + "👤":["上åŠèº«ã®ã‚·ãƒ«ã‚¨ãƒƒãƒˆ","上åŠèº«","シルエット"], + "👥":["上åŠèº«ã®ã‚·ãƒ«ã‚¨ãƒƒãƒˆ","上åŠèº«","シルエット"], + "🗣":["å–‹ã‚‹é ã®ã‚·ãƒ«ã‚¨ãƒƒãƒˆ","é¡”","é ","シルエット","ã—ゃã¹ã‚‹","話ã™"], + "🫂":["ãƒã‚°ã—ã¦ã„る人ãŸã¡","ã•ã‚ˆã†ãªã‚‰","ã“ã‚“ã«ã¡ã¯","ãƒã‚°","ã‚ã‚ŠãŒã¨ã†"], + "👶":["赤ã¡ã‚ƒã‚“"], + "👧":["女ã®å","少女","処女","ãŠã¨ã‚座","星座","åä¾›"], + "🧒":["åä¾›","人","å°‘å¹´","少女"], + "👦":["ç”·ã®å","å°‘å¹´","åä¾›"], + "👩":["女性","女","ãŠã‚“ãª"], + "🧑":["æˆäººå‘ã‘","人","大人","男性","女性","女","ç”·","ãŠã¨ã“","ãŠã‚“ãª"], + "👨":["男性","å£ã²ã’","ç”·","ãŠã¨ã“"], + "👩â€ðŸ¦±":["女性,å·»ã毛","å·»ã毛","髪","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸ¦±":["人,å·»ã毛","å·»ã毛","髪"], + "👨â€ðŸ¦±":["男性,å·»ã毛","å·»ã毛","髪","男性","ç”·","ãŠã¨ã“"], + "👩â€ðŸ¦°":["女性,赤毛","赤","髪","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸ¦°":["人,赤毛","赤","髪"], + "👨â€ðŸ¦°":["男性,赤毛","赤","髪","男性","ç”·","ãŠã¨ã“"], + "👱â€â™€ï¸":["女性,金髪","ブãƒãƒ³ãƒ‰","髪","女","ãŠã‚“ãª"], + "👱":["人,金髪","金髪","ブãƒãƒ³ãƒ‰","髪"], + "👱â€â™‚ï¸":["男性,金髪","ブãƒãƒ³ãƒ‰","髪","ç”·","男性","ãŠã¨ã“"], + "👩â€ðŸ¦³":["女性,白髪","白","髪","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸ¦³":["人,白髪","白","髪"], + "👨â€ðŸ¦³":["男性,白髪","白","髪","男性","ç”·","ãŠã¨ã“"], + "👩â€ðŸ¦²":["女性,禿","禿","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸ¦²":["人,禿","禿"], + "👨â€ðŸ¦²":["男性,禿","禿","男性","ç”·","ãŠã¨ã“"], + "🧔â€â™€ï¸":["ã²ã’ã®ã‚る女性","ã‚ã”ã²ã’","ã²ã’を生やã—ãŸ","女性","女","ãŠã‚“ãª"], + "🧔":["ã‚ã”ã²ã’ã®ã‚る人","ã‚ã”ã²ã’","ã²ã’を生やã—ãŸ"], + "🧔â€â™‚ï¸":["ã²ã’ã®ã‚る男性","ã‚ã”ã²ã’","ã²ã’を生やã—ãŸ","男性","ç”·","ãŠã¨ã“"], + "👵":["ãŠã°ã‚ã•ã‚“","ãŠã°ã‚ã¡ã‚ƒã‚“","è€äºº","女性","女","ãŠã‚“ãª"], + "🧓":["高齢者","人","男性","女性","女","ç”·","ãŠã¨ã“","ãŠã‚“ãª"], + "👴":["ãŠã˜ã„ã•ã‚“","ãŠã˜ã„ã¡ã‚ƒã‚“","è€äºº","ç”·","ãŠã¨ã“","男性"], + "👲":["スカルã‚ャップをã‹ã¶ã£ã¦ã„る人","ä¸å›½å¸½","帽å"], + "👳â€â™€ï¸":["ターãƒãƒ³ã‚’å·»ã„ã¦ã„る女性","ターãƒãƒ³","女性","女","ãŠã‚“ãª"], + "👳":["ターãƒãƒ³ã‚’å·»ã„ã¦ã„る人","ターãƒãƒ³"], + "👳â€â™‚ï¸":["ターãƒãƒ³ã‚’å·»ã„ã¦ã„る男性","ターãƒãƒ³","ç”·","ãŠã¨ã“","男性"], + "🧕":["ヘッドスカーフをã‹ã¶ã£ãŸå¥³æ€§","ヘッドスカーフ","ヒジャブ","マンティラ","ティãƒã‚§ãƒ«","ãƒãƒ³ãƒ€ãƒŠ","é ã®ã‚¹ã‚«ãƒ¼ãƒ•","女性","女","ãŠã‚“ãª"], + "👮â€â™€ï¸":["女性è¦å¯Ÿå®˜","è¦å¯Ÿå®˜","è¦å®˜","è¦å¯Ÿ","女性","女","ãŠã‚“ãª"], + "👮":["è¦å¯Ÿå®˜","è¦å®˜","è¦å¯Ÿ"], + "👮â€â™‚ï¸":["男性è¦å¯Ÿå®˜","è¦å¯Ÿå®˜","è¦å®˜","è¦å¯Ÿ","ç”·","ãŠã¨ã“","男性"], + "👩â€ðŸš’":["女性消防士","ç«","ç«äº‹","消防","消防士","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸš’":["消防士","ç«äº‹"], + "👨â€ðŸš’":["男性消防士","ç«","ç«äº‹","消防","消防士","ç”·","ãŠã¨ã“","男性"], + "👷â€â™€ï¸":["女性ã®å»ºè¨ä½œæ¥å“¡","工事","建è¨","作æ¥å“¡","女性","女","ãŠã‚“ãª"], + "👷":["建è¨ä½œæ¥å“¡","工事","建è¨","作æ¥å“¡"], + "👷â€â™‚ï¸":["男性ã®å»ºè¨ä½œæ¥å“¡","建è¨","作æ¥å“¡","男性","ç”·","ãŠã¨ã“"], + "👩â€ðŸ":["男性ã®å·¥å ´ä½œæ¥å“¡","å·¥å ´","å·¥æ¥","作æ¥å“¡","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸ":["å·¥å ´ä½œæ¥å“¡","å·¥å ´","å·¥æ¥","溶接"], + "👨â€ðŸ":["男性ã®å·¥å ´ä½œæ¥å“¡","å·¥å ´","å·¥æ¥","作æ¥å“¡","ç”·","ãŠã¨ã“","男性"], + "👩â€ðŸ”§":["女性整備士","è·äºº","é…管工","電気技師","ä¿®ç†äºº","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸ”§":["整備士","è·äºº","é…管工","電気技師","ä¿®ç†äºº"], + "👨â€ðŸ”§":["男性整備士","è·äºº","é…管工","電気技師","ä¿®ç†äºº","ç”·","ãŠã¨ã“","男性"], + "👩â€ðŸŒ¾":["女性ã®è¾²æ¥å¾“事者","è¾²å ´åŠ´åƒè€…","ç‰§å ´ä¸»","åºå¸«","農家","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸŒ¾":["è¾²æ¥å¾“事者","è¾²å ´åŠ´åƒè€…","ç‰§å ´ä¸»","åºå¸«","農家"], + "👨â€ðŸŒ¾":["男性ã®è¾²æ¥å¾“事者","è¾²å ´åŠ´åƒè€…","ç‰§å ´ä¸»","åºå¸«","農家","ç”·","ãŠã¨ã“","男性"], + "👩â€ðŸ³":["女性ã®æ–™ç†äºº","食å“","サービス","シェフ","コック","æ–™ç†äºº","æ–™ç†","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸ³":["æ–™ç†äºº","食å“","サービス","シェフ","コック","æ–™ç†"], + "👨â€ðŸ³":["男性ã®æ–™ç†äºº","食å“","サービス","シェフ","コック","æ–™ç†äºº","æ–™ç†","ç”·","ãŠã¨ã“","男性"], + "👩â€ðŸŽ¤":["男性シンガー","音楽","ミュージシャン","ãƒãƒƒã‚¯","ãƒãƒƒã‚«ãƒ¼","ãƒãƒƒã‚¯ã‚¹ã‚¿ãƒ¼","芸能人","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸŽ¤":["æŒæ‰‹","音楽","ミュージシャン","ãƒãƒƒã‚¯","ãƒãƒƒã‚«ãƒ¼","ãƒãƒƒã‚¯ã‚¹ã‚¿ãƒ¼","芸能人"], + "👨â€ðŸŽ¤":["男性シンガー","音楽","ミュージシャン","ãƒãƒƒã‚¯","ãƒãƒƒã‚«ãƒ¼","ãƒãƒƒã‚¯ã‚¹ã‚¿ãƒ¼","芸能人","ç”·","ãŠã¨ã“","男性"], + "👩â€ðŸŽ¨":["女性アーティスト","芸術","アート","芸術家","アーティスト","絵画","画家","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸŽ¨":["アーティスト","芸術","アート","芸術家","絵画","画家"], + "👨â€ðŸŽ¨":["男性アーティスト","芸術","アート","芸術家","アーティスト","絵画","画家","ç”·","ãŠã¨ã“","男性"], + "👩â€ðŸ«":["女性ã®æ•™å¸«","教育","先生","教授","教師","講師","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸ«":["教師","教育","先生","教授","講師"], + "👨â€ðŸ«":["男性ã®æ•™å¸«","教育","先生","教授","教師","講師","ç”·","ãŠã¨ã“","男性"], + "👩â€ðŸŽ“":["女å生徒","å¦ç”Ÿ","å’æ¥ç”Ÿ","教育","å¦æ ¡","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸŽ“":["生徒","å¦ç”Ÿ","å’æ¥ç”Ÿ","教育","å¦æ ¡"], + "👨â€ðŸŽ“":["ç”·å生徒","å¦ç”Ÿ","å’æ¥ç”Ÿ","教育","å¦æ ¡","ç”·","ãŠã¨ã“","男性"], + "👩â€ðŸ’¼":["男性会社員","オフィス","会計士","銀行家","管ç†è·","顧å•","事務員","アナリスト","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸ’¼":["会社員","オフィス","会計士","銀行家","管ç†è·","顧å•","事務員","アナリスト"], + "👨â€ðŸ’¼":["男性会社員","オフィス","会計士","銀行家","管ç†è·","顧å•","事務員","アナリスト","ç”·","ãŠã¨ã“","男性"], + "👩â€ðŸ’»":["女性技術者","テクノãƒã‚¸ãƒ¼","ソフトウェア","エンジニア","プãƒã‚°ãƒ©ãƒžãƒ¼","ラップトップ","ノートパソコン","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸ’»":["技術者","テクノãƒã‚¸ãƒ¼","ソフトウェア","エンジニア","プãƒã‚°ãƒ©ãƒžãƒ¼","ラップトップ","ノートパソコン"], + "👨â€ðŸ’»":["男性技術者","テクノãƒã‚¸ãƒ¼","ソフトウェア","エンジニア","プãƒã‚°ãƒ©ãƒžãƒ¼","ラップトップ","ノートパソコン","ç”·","ãŠã¨ã“","男性"], + "👩â€ðŸ”¬":["女性科å¦è€…","科å¦è€…","化å¦è€…","技術者","æ•°å¦è€…","物ç†å¦è€…","生物å¦è€…","検査技師","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸ”¬":["科å¦è€…","化å¦è€…","技術者","æ•°å¦è€…","物ç†å¦è€…","生物å¦è€…","検査技師"], + "👨â€ðŸ”¬":["男性科å¦è€…","科å¦è€…","化å¦è€…","技術者","æ•°å¦è€…","物ç†å¦è€…","生物å¦è€…","検査技師","ç”·","ãŠã¨ã“","男性"], + "👩â€ðŸš€":["女性宇宙飛行士","宇宙","星","月","惑星","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸš€":["宇宙飛行士","宇宙","星","月","惑星"], + "👨â€ðŸš€":["男性宇宙飛行士","宇宙","星","月","惑星","ç”·","ãŠã¨ã“","男性"], + "👩â€âš•ï¸":["女性医療関係者","医師","内科医","医å¦åšå£«","看è·å¸«","æ¯ç§‘医","医療専門家","療法士","女性","女","ãŠã‚“ãª"], + "🧑â€âš•ï¸":["医療関係者","医師","内科医","医å¦åšå£«","看è·å¸«","æ¯ç§‘医","医療専門家","療法士"], + "👨â€âš•ï¸":["男性医療関係者","医師","内科医","医å¦åšå£«","看è·å¸«","æ¯ç§‘医","医療専門家","療法士","ç”·","ãŠã¨ã“","男性"], + "👩â€âš–ï¸":["女性è£åˆ¤å®˜","è£åˆ¤å®˜","法廷","è£åˆ¤æ‰€","法律","女性","女","ãŠã‚“ãª"], + "🧑â€âš–ï¸":["è£åˆ¤å®˜","法廷","è£åˆ¤æ‰€","法律"], + "👨â€âš–ï¸":["男性è£åˆ¤å®˜","è£åˆ¤å®˜","法廷","è£åˆ¤æ‰€","法律","ç”·","ãŠã¨ã“","男性"], + "👩â€âœˆï¸":["女性パイãƒãƒƒãƒˆ","パイãƒãƒƒãƒˆ","飛行機","æ“縦士","航空","女性","女","ãŠã‚“ãª"], + "🧑â€âœˆï¸":["パイãƒãƒƒãƒˆ","飛行機","æ“縦士","航空"], + "👨â€âœˆï¸":["男性パイãƒãƒƒãƒˆ","パイãƒãƒƒãƒˆ","飛行機","æ“縦士","航空","ç”·","ãŠã¨ã“","男性"], + "💂â€â™€ï¸":["女性è¦å‚™å“¡","è¦å‚™å“¡","è¦å‚™","女性","女","ãŠã‚“ãª"], + "💂":["è¦å‚™å“¡","è¦å‚™"], + "💂â€â™‚ï¸":["男性è¦å‚™å“¡","è¦å‚™å“¡","è¦å‚™","ç”·","ãŠã¨ã“","男性"], + "🥷":["å¿è€…","戦士","éš ã•ã‚ŒãŸ","ステルス"], + "🕵ï¸â€â™€ï¸":["女性ã®æŽ¢åµ","探åµ","刑事","スパイ","女性","女","ãŠã‚“ãª"], + "🕵":["探åµ","刑事","スパイ"], + "🕵ï¸â€â™‚ï¸":["男性ã®æŽ¢åµ","探åµ","刑事","スパイ","ç”·","ãŠã¨ã“","男性"], + "🤶":["ミセス・クãƒãƒ¼ã‚¹","イベント","ãŠç¥ã„","クリスマス","æ¯","サンタ","クãƒãƒ¼ã‚¹","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸŽ„":["ミクスクãƒãƒ¼ã‚¹","アクティビティ","ãŠç¥ã„","クリスマス","サンタ","クãƒãƒ¼ã‚¹"], + "🎅":["サンタクãƒãƒ¼ã‚¹","イベント","ãŠç¥ã„","クリスマス","父","サンタ","クãƒãƒ¼ã‚¹","ç”·","ãŠã¨ã“","男性"], + "👼":["天使ã®èµ¤ã¡ã‚ƒã‚“","天使","赤ã¡ã‚ƒã‚“","é¡”","ãŠã¨ãŽè©±","ファンタジー"], + "👸":["ãŠå§«ã•ã¾","ãŠã¨ãŽè©±","ファンタジー","女王","女性","女","ãŠã‚“ãª"], + "🫅":["çŽ‹å† ã‚’ã‹ã¶ã£ãŸäºº","ãŠã¨ãŽè©±","ファンタジー","国王","è²´æ—","王","王æ—"], + "🤴":["王å様","ãŠã¨ãŽè©±","ファンタジー","王","ç”·","ãŠã¨ã“","男性"], + "👰":["ベールを付ã‘ãŸå¥³æ€§","花å«","ベール","çµå©šå¼","女性","女","ãŠã‚“ãª"], + "👰â€â™€ï¸":["ベールを付ã‘ãŸäºº","花å«","ベール","çµå©šå¼"], + "👰â€â™‚ï¸":["ベールを付ã‘ãŸç”·æ€§","花å«","ベール","ウェディング","男性","ç”·","ãŠã¨ã“"], + "🤵â€â™€ï¸":["ã‚¿ã‚シードã®å¥³æ€§","ã‚¿ã‚シード","ウェディング","女性","女","ãŠã‚“ãª"], + "🤵":["ã‚¿ã‚シードをç€ã‚‹äºº","花婿","ã‚¿ã‚シード","ウェディング"], + "🤵â€â™‚ï¸":["ã‚¿ã‚シードã®ç”·æ€§","花婿","ã‚¿ã‚シード","ウェディング","男性","ç”·","ãŠã¨ã“"], + "🩷":["ピンクã®ãƒãƒ¼ãƒˆ","ã‹ã‚ã„ã„","ãƒãƒ¼ãƒˆ","好ã","æ„›","ピンク"], + "🩵":["ライトブルーã®ãƒãƒ¼ãƒˆ","シアン","ãƒãƒ¼ãƒˆ","ライトブルー","コガモ"], + "🩶":["グレーã®ãƒãƒ¼ãƒˆ","グレー","ãƒãƒ¼ãƒˆ","シルãƒãƒ¼","スレート"], + "🕴ï¸â€â™€ï¸":["å®™ã«æµ®ã„ãŸã‚¹ãƒ¼ãƒ„ã®å¥³æ€§","ビジãƒã‚¹","スーツ","女性","女","ãŠã‚“ãª"], + "🕴":["å®™ã«æµ®ã„ãŸã‚¹ãƒ¼ãƒ„ã®äºº","ビジãƒã‚¹","スーツ"], + "🕴ï¸â€â™‚ï¸":["å®™ã«æµ®ã„ãŸã‚¹ãƒ¼ãƒ„ã®ç”·æ€§","ビジãƒã‚¹","スーツ","ç”·","ãŠã¨ã“","男性"], + "🦸â€â™€ï¸":["女性ã®ã‚¹ãƒ¼ãƒ‘ーヒーãƒãƒ¼","空想","å–„","ヒãƒã‚¤ãƒ³","超大国","女性","女","ãŠã‚“ãª"], + "🦸":["スーパーヒーãƒãƒ¼","空想","å–„","ヒーãƒãƒ¼","ヒãƒã‚¤ãƒ³","超大国"], + "🦸â€â™‚ï¸":["男性ã®ã‚¹ãƒ¼ãƒ‘ーヒーãƒãƒ¼","空想","å–„","ヒーãƒãƒ¼","超大国","男性","ç”·","ãŠã¨ã“"], + "🦹â€â™€ï¸":["女性ã®æ‚ªå…š","空想","悪","犯罪","悪事","超大国","悪役","女性","女","ãŠã‚“ãª"], + "🦹":["悪党","空想","悪","犯罪","悪事","超大国","悪役"], + "🦹â€â™‚ï¸":["男性ã®æ‚ªå…š","空想","悪","犯罪","悪事","超大国","悪役","男性","ç”·","ãŠã¨ã“"], + "🧙â€â™€ï¸":["女性ã®é”法使ã„","空想","é”女","女ã®é”法使ã„","女性","女","ãŠã‚“ãª"], + "🧙":["é”法使ã„","空想","é”術師","ç”·ã®é”法使ã„"], + "🧙â€â™‚ï¸":["男性ã®é”法使ã„","空想","é”術師","ç”·ã®é”法使ã„","男性","ç”·","ãŠã¨ã“"], + "ðŸ§â€â™€ï¸":["女性ã®å°äºº","空想","å°äºº","å…ˆã®ã¨ãŒã£ãŸè€³","女性","女","ãŠã‚“ãª"], + "ðŸ§":["å°äºº","空想","å…ˆã®ã¨ãŒã£ãŸè€³"], + "ðŸ§â€â™‚ï¸":["男性ã®å°äºº","空想","å°äºº","å…ˆã®ã¨ãŒã£ãŸè€³","男性","ç”·","ãŠã¨ã“"], + "🧚â€â™€ï¸":["女性ã®å¦–ç²¾","空想","ティターニア","ウィングス","女性","女","ãŠã‚“ãª"], + "🧚":["妖精","空想","ティターニア","ウィングス"], + "🧚â€â™‚ï¸":["男性ã®å¦–ç²¾","空想","オベãƒãƒ³","å°å¦–ç²¾","男性","ç”·","ãŠã¨ã“"], + "🧞â€â™€ï¸":["女性ã®ç²¾éœŠ","空想","精霊","女性","女","ãŠã‚“ãª"], + "🧞":["精霊","空想"], + "🧞â€â™‚ï¸":["男性ã®ç²¾éœŠ","空想","精霊","男性","ç”·","ãŠã¨ã“"], + "🧜â€â™€ï¸":["女性ã®äººéš","空想","女性","女","ãŠã‚“ãª"], + "🧜":["人éš","空想"], + "🧜â€â™‚ï¸":["男性ã®äººéš","空想","人éš","男性","ç”·","ãŠã¨ã“"], + "🧌":["釣り","ãŠã¨ãŽè©±","ファンタジ","モンスター"], + "🧛â€â™€ï¸":["女性ã®å¸è¡€é¬¼","空想","アンデッド","女性","女","ãŠã‚“ãª"], + "🧛":["å¸è¡€é¬¼","空想","ドラã‚ュラ","アンデッド"], + "🧛â€â™‚ï¸":["男性ã®å¸è¡€é¬¼","空想","ドラã‚ュラ","アンデッド","男性","ç”·","ãŠã¨ã“"], + "🧟â€â™€ï¸":["女性ã®ã‚¾ãƒ³ãƒ“","空想","アンデッド","女性","女","ãŠã‚“ãª"], + "🧟":["ゾンビ","空想","アンデッド"], + "🧟â€â™‚ï¸":["男性ã®ã‚¾ãƒ³ãƒ“","空想","アンデッド","男性","ç”·","ãŠã¨ã“"], + "🙇â€â™€ï¸":["æ·±ããŠè¾žå„€ã™ã‚‹å¥³æ€§","è¬ç½ª","ãŠè¾žå„€","ジェスãƒãƒ£ãƒ¼","ã”ã‚ã‚“ãªã•ã„","女性","女","ãŠã‚“ãª"], + "🙇":["æ·±ããŠè¾žå„€ã—ãŸäºº","è¬ç½ª","ãŠè¾žå„€","ジェスãƒãƒ£ãƒ¼","ã”ã‚ã‚“ãªã•ã„"], + "🙇â€â™‚ï¸":["æ·±ããŠè¾žå„€ã™ã‚‹ç”·æ€§","è¬ç½ª","ãŠè¾žå„€","ジェスãƒãƒ£ãƒ¼","ã”ã‚ã‚“ãªã•ã„","ç”·","ãŠã¨ã“","男性"], + "ðŸ’â€â™€ï¸":["案内ã™ã‚‹å¥³æ€§","手","助ã‘","æƒ…å ±","ãšã†ãšã†ã—ã„","女性","女","ãŠã‚“ãª"], + "ðŸ’":["案内ã™ã‚‹äºº","手","助ã‘","æƒ…å ±","ãšã†ãšã†ã—ã„","女性","女","ãŠã‚“ãª"], + "ðŸ’â€â™‚ï¸":["案内ã™ã‚‹ç”·æ€§","手","助ã‘","æƒ…å ±","ãšã†ãšã†ã—ã„","ç”·","ãŠã¨ã“","男性"], + "🙅â€â™€ï¸":["NGサインã®å¥³æ€§","ç¦ã˜ã‚‹","ジェスãƒãƒ£ãƒ¼","手","ã ã‚","ダメ","ç¦æ¢","女性","女","ãŠã‚“ãª"], + "🙅":["NGサインã®äºº","ç¦ã˜ã‚‹","ジェスãƒãƒ£ãƒ¼","手","ã ã‚","ダメ","ç¦æ¢"], + "🙅â€â™‚ï¸":["NGサインã®ç”·æ€§","ç¦ã˜ã‚‹","ジェスãƒãƒ£ãƒ¼","手","ã ã‚","ダメ","ç¦æ¢","ç”·","ãŠã¨ã“","男性"], + "🙆â€â™€ï¸":["OKサインã®å¥³æ€§","ジェスãƒãƒ£ãƒ¼","手","ok","女性","女","ãŠã‚“ãª"], + "🙆":["OKサインã®äºº","ジェスãƒãƒ£ãƒ¼","手","OK"], + "🙆â€â™‚ï¸":["OKサインã®ç”·æ€§","ジェスãƒãƒ£ãƒ¼","手","ok","ç”·","ãŠã¨ã“","男性"], + "🤷â€â™€ï¸":["è‚©ã‚’ã™ãã‚る女性","ç–‘ã„","無知","無関心","è‚©ã‚’ã™ãã‚ã‚‹","女性","女","ãŠã‚“ãª"], + "🤷":["è‚©ã‚’ã™ãã‚る人","ç–‘ã„","無知","無関心","è‚©ã‚’ã™ãã‚ã‚‹"], + "🤷â€â™‚ï¸":["è‚©ã‚’ã™ãã‚る男性","ç–‘ã„","無知","無関心","è‚©ã‚’ã™ãã‚ã‚‹","ç”·","ãŠã¨ã“","男性"], + "🙋â€â™€ï¸":["片手を上ã’ã¦å–œã¶å¥³æ€§","ジェスãƒãƒ£ãƒ¼","手","幸ã›","ã—ã‚ã‚ã›","挙ã’ã‚‹","女性","女","ãŠã‚“ãª"], + "🙋":["片手を上ã’ã¦å–œã¶äºº","ジェスãƒãƒ£ãƒ¼","手","幸ã›","ã—ã‚ã‚ã›","挙ã’ã‚‹"], + "🙋â€â™‚ï¸":["片手を上ã’ã¦å–œã¶ç”·æ€§","ジェスãƒãƒ£ãƒ¼","手","幸ã›","ã—ã‚ã‚ã›","挙ã’ã‚‹","ç”·","ãŠã¨ã“","男性"], + "🤦â€â™€ï¸":["顔を押ã•ãˆã‚‹å¥³æ€§","ä¸ä¿¡","憤慨","é¡”","手ã®ã²ã‚‰","女性","女","ãŠã‚“ãª"], + "🤦":["手ã®ã²ã‚‰ã‚’é¡”ã«å½“ã¦ã‚‹äºº","ä¸ä¿¡","憤慨","é¡”","手ã®ã²ã‚‰"], + "🤦â€â™‚ï¸":["顔を押ã•ãˆã‚‹ç”·æ€§","ä¸ä¿¡","憤慨","é¡”","手ã®ã²ã‚‰","ç”·","ãŠã¨ã“","男性"], + "ðŸ§â€â™€ï¸":["耳ãŒä¸è‡ªç”±ãªå¥³æ€§","アクセシビリティ","耳ãŒä¸è‡ªç”±","女性","女","ãŠã‚“ãª"], + "ðŸ§":["耳ãŒä¸è‡ªç”±ãªäºº","アクセシビリティ","耳ãŒä¸è‡ªç”±"], + "ðŸ§â€â™‚ï¸":["耳ãŒä¸è‡ªç”±ãªç”·æ€§","アクセシビリティ","耳ãŒä¸è‡ªç”±","男性","ç”·","ãŠã¨ã“"], + "🙎â€â™€ï¸":["ãµãã‚Œã£é¢ã®å¥³æ€§","ジェスãƒãƒ£ãƒ¼","ãµãã‚Œã£é¢","ãµãã‚Œã£ã¤ã‚‰","女性","女","ãŠã‚“ãª"], + "🙎":["怒ã£ãŸé¡”ã®äºº","ジェスãƒãƒ£ãƒ¼","ãµãã‚Œã£é¢","ãµãã‚Œã£ã¤ã‚‰"], + "🙎â€â™‚ï¸":["ãµãã‚Œã£é¢ã®ç”·æ€§","ジェスãƒãƒ£ãƒ¼","ãµãã‚Œã£é¢","ãµãã‚Œã£ã¤ã‚‰","ç”·","ãŠã¨ã“","男性"], + "ðŸ™â€â™€ï¸":["顔をã—ã‹ã‚ãŸå¥³æ€§","ã—ã‹ã‚é¢","ジェスãƒãƒ£ãƒ¼","悲ã—ã„","女性","女","ãŠã‚“ãª"], + "ðŸ™":["ä¸æº€ãªé¡”ã®äºº","ã—ã‹ã‚é¢","ジェスãƒãƒ£ãƒ¼","悲ã—ã„"], + "ðŸ™â€â™‚ï¸":["顔をã—ã‹ã‚ãŸç”·æ€§","ã—ã‹ã‚é¢","ジェスãƒãƒ£ãƒ¼","悲ã—ã„","男性","ç”·","ãŠã¨ã“"], + "💇â€â™€ï¸":["髪を切られã¦ã„る女性","ç†é«ªå¸«","美容師","美容","散髪","ヘアカット","美容院","女性","女","ãŠã‚“ãª"], + "💇":["髪を切られã¦ã„る人","ç†é«ªå¸«","美容師","美容","散髪","ヘアカット","美容院"], + "💇â€â™‚ï¸":["髪を切られã¦ã„る男性","ç†é«ªå¸«","美容師","美容","散髪","ヘアカット","美容院","ç”·","ãŠã¨ã“","男性"], + "💆â€â™€ï¸":["フェイスマッサージをå—ã‘る女性","マッサージ","サãƒãƒ³","女性","女","ãŠã‚“ãª"], + "💆":["フェイスマッサージをå—ã‘る人","マッサージ","サãƒãƒ³"], + "💆â€â™‚ï¸":["フェイスマッサージをå—ã‘る男性","マッサージ","サãƒãƒ³","ç”·","ãŠã¨ã“","男性"], + "🤰":["妊婦","å¦Šå¨ ","赤ã¡ã‚ƒã‚“","女性","女","ãŠã‚“ãª","è…¹","ãµãã‚ŒãŸ","ãµã£ãらã—ãŸ"], + "🫄":["å¦Šå¨ ã—ãŸäºº","è…¹","ãµãã‚ŒãŸ","ãµã£ãらã—ãŸ","å¦Šå¨ ","赤ã¡ã‚ƒã‚“"], + "🫃":["å¦Šå¨ ã—ã¦ã„る男性","è…¹","ãµãã‚ŒãŸ","ãµã£ãらã—ãŸ","å¦Šå¨ ","赤ã¡ã‚ƒã‚“","男性","ç”·","ãŠã¨ã“"], + "🤱":["æ¯ä¹³","胸","赤ã¡ã‚ƒã‚“","赤んåŠ","ä¹³å…","å¹¼å…","æ¯","åä¾›","ä¿è‚²","ミルク","女性","女","ãŠã‚“ãª"], + "👩â€ðŸ¼":["赤ã¡ã‚ƒã‚“ã«ã”飯をã‚ã’る女性","赤ã¡ã‚ƒã‚“","ä¹³å…","åä¾›","授乳","ミルク","ボトル","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸ¼":["赤ã¡ã‚ƒã‚“ã«ã”飯をã‚ã’る人","赤ã¡ã‚ƒã‚“","ä¹³å…","åä¾›","授乳","ミルク","ボトル"], + "👨â€ðŸ¼":["赤ã¡ã‚ƒã‚“ã«ã”飯をã‚ã’る男性","赤ã¡ã‚ƒã‚“","ä¹³å…","åä¾›","授乳","ミルク","ボトル","男性","ç”·","ãŠã¨ã“"], + "🧎â€â™€ï¸":["è†ç«‹ã¡ã—ã¦ã„る女性","è†","è†ç«‹ã¡","女性","女","ãŠã‚“ãª"], + "🧎":["è†ç«‹ã¡ã—ã¦ã„る人","è†","è†ç«‹ã¡"], + "🧎â€â™‚ï¸":["è†ç«‹ã¡ã—ã¦ã„る男性","è†","è†ç«‹ã¡","男性","ç”·","ãŠã¨ã“"], + "ðŸ§â€â™€ï¸":["ç«‹ã£ã¦ã„る女性","ç«‹ã¤","スタンディング","女性","女","ãŠã‚“ãª"], + "ðŸ§":["ç«‹ã£ã¦ã„る人","ç«‹ã¡","スタンディング"], + "ðŸ§â€â™‚ï¸":["ç«‹ã£ã¦ã„る男性","ç«‹ã¤","スタンディング","男性","ç”·","ãŠã¨ã“"], + "🚶â€â™€ï¸":["æ©ã女性","ãƒã‚¤ã‚ング","æ©è¡Œè€…","æ©ã","ウォーã‚ング","女性","女","ãŠã‚“ãª"], + "🚶":["æ©ã人","ãƒã‚¤ã‚ング","æ©è¡Œè€…","æ©ã","ウォーã‚ング"], + "🚶â€â™‚ï¸":["æ©ã男性","ãƒã‚¤ã‚ング","æ©è¡Œè€…","æ©ã","ウォーã‚ング","ç”·","ãŠã¨ã“","男性"], + "👩â€ðŸ¦¯":["白æ–ã‚’æŒã£ãŸå¥³æ€§","アクセシビリティ","ç›®ãŒä¸è‡ªç”±","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸ¦¯":["白æ–ã‚’æŒã£ãŸäºº","アクセシビリティ","ç›®ãŒä¸è‡ªç”±"], + "👨â€ðŸ¦¯":["白æ–ã‚’æŒã£ãŸç”·æ€§","アクセシビリティ","ç›®ãŒä¸è‡ªç”±","男性","ç”·","ãŠã¨ã“"], + "ðŸƒâ€â™€ï¸":["走る女性","マラソン","ランナー","ランニング","女性","女","ãŠã‚“ãª"], + "ðŸƒ":["走る人","マラソン","ランナー","ランニング"], + "ðŸƒâ€â™‚ï¸":["走る男性","マラソン","ランナー","ランニング","ç”·","ãŠã¨ã“","男性"], + "👩â€ðŸ¦¼":["電動車ã„ã™ã«åº§ã£ã¦ã„る女性","アクセシビリティ","車ã„ã™","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸ¦¼":["電動車ã„ã™ã«åº§ã£ã¦ã„る人","アクセシビリティ","車ã„ã™"], + "👨â€ðŸ¦¼":["電動車ã„ã™ã«åº§ã£ã¦ã„る男性","アクセシビリティ","車ã„ã™","男性","ç”·","ãŠã¨ã“"], + "👩â€ðŸ¦½":["手動車ã„ã™ã«åº§ã£ã¦ã„る女性","アクセシビリティ","車ã„ã™","女性","女","ãŠã‚“ãª"], + "🧑â€ðŸ¦½":["手動車ã„ã™ã«åº§ã£ã¦ã„る人","アクセシビリティ","車ã„ã™"], + "👨â€ðŸ¦½":["手動車ã„ã™ã«åº§ã£ã¦ã„る男性","アクセシビリティ","車ã„ã™","男性","ç”·","ãŠã¨ã“"], + "💃":["女性ダンサー","ダンス","踊る","ダンサー","女性","女","ãŠã‚“ãª"], + "🕺":["男性ダンサー","ダンス","踊る","ダンサー","ç”·","ãŠã¨ã“","男性"], + "👯â€â™€ï¸":["ãƒãƒ‹ãƒ¼ã‚¬ãƒ¼ãƒ«","ã†ã•ãŽè€³","ダンサー","女性","女","ãŠã‚“ãª"], + "👯":["ã†ã•ãŽè€³ã®äºº","ã†ã•ãŽè€³","ダンサー"], + "👯â€â™‚ï¸":["ã†ã•ãŽè€³ã®ç”·æ€§","ã†ã•ãŽè€³","ダンサー","ç”·","ãŠã¨ã“","男性"], + "👫":["手をã¤ãªã„ã 男女","カップル","手","ã¤ãªã","ç”·","女","男女","ãŠã¨ã“","ãŠã‚“ãª"], + "ðŸ‘":["手をã¤ãªã„ã 女性","カップル","手","ã¤ãªã","女性","女","ãŠã‚“ãª","プライド","lgbt","レズビアン"], + "👬":["手をã¤ãªã„ã 男性","カップル","手","ã¤ãªã","男性","ç”·","ãŠã¨ã“","プライド","lgbt","ゲイ"], + "🧑â€ðŸ¤â€ðŸ§‘":["手をã¤ãªã„ã 人ãŸã¡","カップル","手","æ¡ã‚‹"], + "👩â€â¤ï¸â€ðŸ‘¨":["ãƒãƒ¼ãƒˆã®ã‚«ãƒƒãƒ—ル (女性ã€ç”·æ€§)","カップル","ãƒãƒ¼ãƒˆ","æ„›","æ‹æ„›","ç”·","女","男女","ãŠã¨ã“","ãŠã‚“ãª"], + "👩â€â¤ï¸â€ðŸ‘©":["ãƒãƒ¼ãƒˆã®ã‚«ãƒƒãƒ—ル (女性ã€å¥³æ€§)","カップル","ãƒãƒ¼ãƒˆ","æ„›","æ‹æ„›","女性","女","ãŠã‚“ãª","プライド","lgbt","レズビアン"], + "💑":["ãƒãƒ¼ãƒˆã®ã‚«ãƒƒãƒ—ル","カップル","ãƒãƒ¼ãƒˆ","æ„›","æ‹æ„›","ç”·","女","男女","ãŠã¨ã“","ãŠã‚“ãª"], + "👨â€â¤ï¸â€ðŸ‘¨":["ãƒãƒ¼ãƒˆã®ã‚«ãƒƒãƒ—ル (男性ã€ç”·æ€§)","カップル","ãƒãƒ¼ãƒˆ","æ„›","æ‹æ„›","男性","ç”·","ãŠã¨ã“","プライド","lgbt","ゲイ"], + "👩â€â¤ï¸â€ðŸ’‹â€ðŸ‘¨":["ã‚ス (女性ã€ç”·æ€§)","カップル","ã‚ス","ãƒãƒ¼ãƒˆ","æ„›","æ‹æ„›","ç”·","女","男女","ãŠã¨ã“","ãŠã‚“ãª"], + "👩â€â¤ï¸â€ðŸ’‹â€ðŸ‘©":["ã‚ス (女性ã€å¥³æ€§)","カップル","ã‚ス","ãƒãƒ¼ãƒˆ","æ„›","æ‹æ„›","女性","女","ãŠã‚“ãª","プライド","lgbt","ゲイ"], + "ðŸ’":["ã‚ス","カップル","ã‚ス","ãƒãƒ¼ãƒˆ","æ„›","æ‹æ„›","ç”·","女","男女","ãŠã¨ã“","ãŠã‚“ãª"], + "👨â€â¤ï¸â€ðŸ’‹â€ðŸ‘¨":["ã‚ス (男性ã€ç”·æ€§)","カップル","ã‚ス","ãƒãƒ¼ãƒˆ","æ„›","æ‹æ„›","男性","ç”·","ãŠã¨ã“","プライド","lgbt","ゲイ"], + "👪":["家æ—","父親","æ¯è¦ª","ç”·","女","男女","ãŠã¨ã“","ãŠã‚“ãª","ç”·ã®å","ã“ã©ã‚‚"], + "👨â€ðŸ‘©â€ðŸ‘§":["å®¶æ— (男性ã€å¥³æ€§ã€å¥³ã®å)","父親","æ¯è¦ª","ç”·","女","男女","ãŠã¨ã“","ãŠã‚“ãª","女ã®å","ã“ã©ã‚‚"], + "👨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦":["å®¶æ— (男性ã€å¥³æ€§ã€å¥³ã®åã€ç”·ã®å)","父親","æ¯è¦ª","ç”·","女","男女","ãŠã¨ã“","ãŠã‚“ãª","ç”·ã®å","女ã®å","ã“ã©ã‚‚"], + "👨â€ðŸ‘©â€ðŸ‘¦â€ðŸ‘¦":["å®¶æ— (男性ã€å¥³æ€§ã€ç”·ã®åã€ç”·ã®å)","父親","æ¯è¦ª","ç”·","女","男女","ãŠã¨ã“","ãŠã‚“ãª","ç”·ã®å","ã“ã©ã‚‚"], + "👨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘§":["å®¶æ— (男性ã€å¥³æ€§ã€å¥³ã®åã€å¥³ã®å)","父親","æ¯è¦ª","ç”·","女","男女","ãŠã¨ã“","ãŠã‚“ãª","女ã®å","ã“ã©ã‚‚"], + "👩â€ðŸ‘©â€ðŸ‘¦":["å®¶æ— (女性ã€å¥³æ€§ã€ç”·ã®å)","家æ—","æ¯è¦ª","女性","女","ãŠã‚“ãª","ç”·ã®å","åä¾›","プライド","lgbt","レズビアン"], + "👩â€ðŸ‘©â€ðŸ‘§":["å®¶æ— (女性ã€å¥³æ€§ã€å¥³ã®å)","家æ—","æ¯è¦ª","女性","女","ãŠã‚“ãª","女ã®å","åä¾›","プライド","lgbt","レズビアン"], + "👩â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦":["å®¶æ— (女性ã€å¥³æ€§ã€å¥³ã®åã€ç”·ã®å)","家æ—","æ¯è¦ª","女性","女","ãŠã‚“ãª","ç”·ã®å","女ã®å","åä¾›","プライド","lgbt","レズビアン"], + "👩â€ðŸ‘©â€ðŸ‘¦â€ðŸ‘¦":["å®¶æ— (女性ã€å¥³æ€§ã€ç”·ã®åã€ç”·ã®å)","家æ—","æ¯è¦ª","女性","女","ãŠã‚“ãª","ç”·ã®å","åä¾›","プライド","lgbt","レズビアン"], + "👩â€ðŸ‘©â€ðŸ‘§â€ðŸ‘§":["å®¶æ— (女性ã€å¥³æ€§ã€å¥³ã®åã€å¥³ã®å)","家æ—","æ¯è¦ª","女性","女","ãŠã‚“ãª","女ã®å","åä¾›","プライド","lgbt","レズビアン"], + "👨â€ðŸ‘¨â€ðŸ‘¦":["å®¶æ— (男性ã€ç”·æ€§ã€ç”·ã®å)","家æ—","父親","男性","ç”·","ãŠã¨ã“","ç”·ã®å","åä¾›","プライド","lgbt","ゲイ"], + "👨â€ðŸ‘¨â€ðŸ‘§":["å®¶æ— (男性ã€ç”·æ€§ã€å¥³ã®å)","家æ—","父親","男性","ç”·","ãŠã¨ã“","女ã®å","åä¾›","プライド","lgbt","ゲイ"], + "👨â€ðŸ‘¨â€ðŸ‘§â€ðŸ‘¦":["å®¶æ— (男性ã€ç”·æ€§ã€å¥³ã®åã€ç”·ã®å)","家æ—","父親","男性","ç”·","ãŠã¨ã“","ç”·ã®å","女ã®å","åä¾›","プライド","lgbt","ゲイ"], + "👨â€ðŸ‘¨â€ðŸ‘¦â€ðŸ‘¦":["å®¶æ— (男性ã€ç”·æ€§ã€ç”·ã®åã€ç”·ã®å)","家æ—","父親","男性","ç”·","ãŠã¨ã“","ç”·ã®å","åä¾›","プライド","lgbt","ゲイ"], + "👨â€ðŸ‘¨â€ðŸ‘§â€ðŸ‘§":["å®¶æ— (男性ã€ç”·æ€§ã€å¥³ã®åã€å¥³ã®å)","家æ—","父親","男性","ç”·","ãŠã¨ã“","女ã®å","åä¾›","プライド","lgbt","ゲイ"], + "👩â€ðŸ‘¦":["家æ—(女性ã€ç”·ã®å)","家æ—","æ¯è¦ª","女性","女","ãŠã‚“ãª","ç”·ã®å","ã“ã©ã‚‚"], + "👩â€ðŸ‘§":["家æ—(女性ã€å¥³ã®å)","家æ—","æ¯è¦ª","女性","女","ãŠã‚“ãª","女ã®å","ã“ã©ã‚‚"], + "👩â€ðŸ‘§â€ðŸ‘¦":["家æ—(女性ã€å¥³ã®åã€ç”·ã®å)","家æ—","æ¯è¦ª","女性","女","男性","女ã®å","ç”·ã®å","ã“ã©ã‚‚"], + "👩â€ðŸ‘¦â€ðŸ‘¦":["家æ—(女性ã€ç”·ã®åã€ç”·ã®å)","家æ—","æ¯è¦ª","女性","女","ãŠã‚“ãª","ç”·ã®å","ã“ã©ã‚‚"], + "👩â€ðŸ‘§â€ðŸ‘§":["家æ—(女性ã€å¥³ã®åã€å¥³ã®å)","家æ—","æ¯è¦ª","女性","女","ãŠã‚“ãª","女ã®å","ã“ã©ã‚‚"], + "👨â€ðŸ‘¦":["家æ—(男性ã€ç”·ã®å)","父親","ç”·","ãŠã¨ã“","男性","ç”·ã®å","ã“ã©ã‚‚"], + "👨â€ðŸ‘§":["家æ—(男性ã€å¥³ã®å)","父親","ç”·","男女","女ã®å","ã“ã©ã‚‚"], + "👨â€ðŸ‘§â€ðŸ‘¦":["家æ—(男性ã€å¥³ã®åã€ç”·ã®å)","父親","ç”·","ãŠã¨ã“","男性","ç”·ã®å","女ã®å","ã“ã©ã‚‚"], + "👨â€ðŸ‘¦â€ðŸ‘¦":["家æ—(男性ã€ç”·ã®åã€ç”·ã®å)","父親","ç”·","ãŠã¨ã“","男性","ç”·ã®å","ã“ã©ã‚‚"], + "👨â€ðŸ‘§â€ðŸ‘§":["家æ—(男性ã€å¥³ã®åã€å¥³ã®å)","父親","ç”·","男女","女ã®å","ã“ã©ã‚‚"], + "👚":["レディースウェア","æœ","女性","ãŠã‚“ãª"], + "👕":["Tシャツ","æœ","シャツ"], + "🥼":["白衣","æœ","医者","実験","科å¦è€…"], + "🦺":["安全ベスト","緊急","安全","ベスト"], + "🧥":["コート","æœ","ジャケット"], + "👖":["ジーンズ","æœ","パンツ","ズボン"], + "👔":["ãƒã‚¯ã‚¿ã‚¤","æœ"], + "👗":["ドレス","æœ"], + "👘":["ç€ç‰©","æœ","å’Œæœ"], + "🥻":["サリー","æœ","ドレス"], + "🩱":["ワンピース","æœ","æ°´ç€","スイミングウェア","æ°´æ³³"], + "👙":["ビã‚ニ","æœ","æ°´æ³³"], + "🩲":["ブリーフ","æœ","æ°´ç€","スイミングウェア","æ°´æ³³","下ç€"], + "🩳":["ショーツ","æœ","æ°´ç€","スイミングウェア","æ°´æ³³","下ç€"], + "💄":["å£ç´…","化粧å“","コスメ","化粧","メイク"], + "💋":["ã‚スマーク","ãƒãƒ¼ãƒˆ","ã‚ス","唇","クãƒãƒ“ル","マーク","æ‹æ„›","ãƒãƒžãƒ³ã‚¹"], + "👣":["足ã‚ã¨","体","æœ","足跡","ã‚ã—ã‚ã¨"], + "🧦":["é´ä¸‹","æœ","ソックス","一組"], + "🩴":["ゴム製サンダル","ビーãƒ","サンダル","è‰å±¥"], + "👠":["ãƒã‚¤ãƒ’ール","æœ","ヒール","é´","女性","ãŠã‚“ãª"], + "👡":["レディースサンダル","æœ","サンダル","é´","女性","ãŠã‚“ãª"], + "👢":["レディースブーツ","ブーツ","æœ","é´","女性","ãŠã‚“ãª"], + "🥿":["レディースフラットシューズ","æœ","ãƒãƒ¬ã‚¨ãƒ•ãƒ©ãƒƒãƒˆ","スリッãƒãƒ³","スリッパ"], + "👞":["メンズシューズ","æœ","男性","ãŠã¨ã“","é´"], + "👟":["é‹å‹•é´","é‹å‹•","æœ","シューズ","スニーカー"], + "🩰":["ãƒãƒ¬ã‚¨ã‚·ãƒ¥ãƒ¼ã‚º","æœ","シューズ","ãƒãƒ¬ã‚¨","ダンス"], + "🥾":["ãƒã‚¤ã‚ングブーツ","æœ","ãƒãƒƒã‚¯ãƒ‘ック","ブーツ","ã‚ャンプ","ãƒã‚¤ã‚ング"], + "🧢":["ã‚ャップ","æœ","野çƒ","ãƒãƒƒãƒˆ","帽å"], + "👒":["レディースãƒãƒƒãƒˆ","æœ","帽å","女性","ãŠã‚“ãª"], + "🎩":["シルクãƒãƒƒãƒˆ","アクティビティ","æœ","エンターテインメント","娯楽","帽å","トップス"], + "🎓":["å’æ¥å¼ã®è§’帽","アクティビティ","帽å","ãŠç¥ã„","æœ","å’æ¥","ãƒãƒƒãƒˆ"], + "👑":["å† ","æœ","çŽ‹å† ","王","女王"], + "⛑":["白åå—ã®ãƒ˜ãƒ«ãƒ¡ãƒƒãƒˆ","救助","åå—","é¡”","帽å","ヘルメット"], + "🪖":["è»éšŠã®ãƒ˜ãƒ«ãƒ¡ãƒƒãƒˆ","è»","ヘルメット","è»éšŠ","è»äºº","兵士"], + "🎒":["ランドセル","アクティビティ","éž„","ãƒãƒƒã‚°","å¦ç”Ÿéž„","å¦æ ¡"], + "ðŸ‘":["ãƒãƒ¼ãƒ","éž„","ãƒãƒƒã‚°","æœ"], + "👛":["財布","æœ","コイン"], + "👜":["ãƒãƒ³ãƒ‰ãƒãƒƒã‚°","éž„","ãƒãƒƒã‚°","æœ"], + "💼":["ブリーフケース"], + "👓":["眼é¡","æœ","ç›®","メガãƒ","アイウェア"], + "🕶":["サングラス","æš—ã„","ç›®","眼é¡","メガãƒ"], + "🥽":["ゴーグル","æœ","ç›®ã®ä¿è·","æ°´æ³³","溶接"], + "🧣":["スカーフ","æœ","首"], + "🧤":["手袋","æœ","手"], + "ðŸ’":["指輪","ダイヤモンド","æ‹æ„›","ãƒãƒžãƒ³ã‚¹"], + "🌂":["é–‰ã˜ãŸå‚˜","æœ","雨","傘","天気"], + "☂":["傘","æœ","雨","天気"], + "ðŸ¶":["イヌã®é¡”","犬","イヌ","é¡”","ペット"], + "ðŸ±":["ãƒã‚³ã®é¡”","猫","ãƒã‚³","é¡”","ペット"], + "ðŸ":["ãƒã‚ºãƒŸã®é¡”","é¡”","ãƒã‚ºãƒŸ"], + "ðŸ¹":["ãƒãƒ スターã®é¡”","é¡”","ãƒãƒ スター","ペット"], + "ðŸ°":["ウサギã®é¡”","ãƒãƒ‹ãƒ¼","é¡”","ペット","ウサギ"], + "ðŸ»":["クマã®é¡”","熊","クマ","é¡”"], + "🧸":["テディベア","玩具","ビãƒãƒ¼ãƒ‰","ã¬ã„ãã‚‹ã¿","ãŠã‚‚ã¡ã‚ƒ"], + "ðŸ¼":["パンダã®é¡”","é¡”","パンダ","熊"], + "ðŸ»â€â„ï¸":["ã‚·ãƒã‚¯ãƒž","é¡”","北極","熊","白"], + "ðŸ¨":["コアラ","熊","有袋類","オーストラリア"], + "ðŸ¯":["トラã®é¡”","é¡”","虎","トラ"], + "ðŸ¦":["ライオンã®é¡”","é¡”","ã—ã—座","ライオン","星座"], + "ðŸ®":["ウシã®é¡”","牛","ウシ","é¡”"], + "ðŸ·":["ブタã®é¡”","é¡”","豚","ブタ"], + "ðŸ½":["ブタã®é¼»","é¡”","é¼»","豚","ブタ"], + "ðŸ¸":["カエルã®é¡”","é¡”","è›™","カエル"], + "ðŸµ":["サルã®é¡”","é¡”","猿","サル"], + "🙈":["見ã–ã‚‹","悪ã„","é¡”","ç¦ã˜ã‚‹","ジェスãƒãƒ£ãƒ¼","猿","サル","ã ã‚","ダメ","ç¦æ¢","見る"], + "🙉":["èžã‹ã–ã‚‹","悪ã„","é¡”","ç¦ã˜ã‚‹","ジェスãƒãƒ£ãƒ¼","èžã","サル","ãªã„","ãªã—","ç¦æ¢"], + "🙊":["言ã‚ã–ã‚‹","悪ã„","é¡”","ç¦ã˜ã‚‹","ジェスãƒãƒ£ãƒ¼","猿","サル","ãªã„","ãªã—","ç¦æ¢","話ã™"], + "ðŸ’":["サル","猿"], + "ðŸ¦":["ゴリラ"], + "🦧":["オランウータン","類人猿"], + "ðŸ”":["ニワトリ"], + "ðŸ§":["ペンギン"], + "ðŸ¦":["é³¥"], + "ðŸ¦â€â¬›":["é»’ã„é³¥","é³¥","é»’","カラス","ワタリガラス","ミヤマガラス"], + "ðŸ¤":["ヒヨコ","赤ã¡ã‚ƒã‚“","ã²ã‚ˆã“"], + "ðŸ£":["ã²ã‚ˆã“","赤ã¡ã‚ƒã‚“","åµåŒ–"], + "ðŸ¥":["æ£é¢ã‚’å‘ã„ãŸãƒ’ヨコ","赤ã¡ã‚ƒã‚“","ã²ã‚ˆã“"], + "ðŸº":["オオカミã®é¡”","é¡”","オオカミ"], + "🦊":["ã‚ツãƒã®é¡”","é¡”","ã‚ツãƒ"], + "ðŸ¦":["アライグマ","é¡”","好奇心ãŒå¼·ã„","ãšã‚‹è³¢ã„"], + "ðŸ—":["イノシシ","豚"], + "ðŸ´":["ウマã®é¡”","é¡”","馬"], + "🦓":["シマウマ","é¡”"], + "🦒":["ã‚リン","é¡”"], + "🦌":["ã‚·ã‚«"], + "🫎":["ヘラジカ","動物","æžè§’","エルク","哺乳類"], + "🦘":["カンガルー","オーストラリア","ジャンプ","有袋類"], + "🦥":["æ€ æƒ°","ãªã¾ã‘ã‚‹","é…ã„"], + "🦦":["カワウソ","釣り","ãµã–ã‘ã‚‹"], + "🦫":["ビーãƒãƒ¼","ダム"], + "🦄":["ユニコーンã®é¡”","é¡”","ユニコーン"], + "ðŸ":["ミツãƒãƒ","ãƒãƒ","昆虫"], + "ðŸ›":["虫","昆虫"], + "🦋":["ãƒãƒ§ã‚¦","è¶","昆虫","美ã—ã„"], + "ðŸŒ":["カタツムリ"], + "🪲":["甲虫","虫","昆虫"], + "ðŸž":["テントウムシ","カブトムシ","昆虫","ã¦ã‚“ã¨ã†è™«"], + "ðŸœ":["アリ","蟻","昆虫"], + "🦗":["クリケット","コオãƒã‚®","ãƒãƒƒã‚¿ç›®","昆虫"], + "🪳":["ã‚´ã‚ブリ","昆虫","害虫"], + "🕷":["クモ","昆虫","蜘蛛"], + "🕸":["クモã®å·£","クモ","å·£"], + "🦂":["サソリ","ã•ãり座","ã•ãã‚Š","星座"], + "🦟":["蚊","ç—…æ°—","熱","昆虫","マラリア","ウイルス"], + "🪰":["ãƒã‚¨","害虫","昆虫","蛆虫"], + "🪱":["è •è™«","環形動物","ミミズ","寄生虫"], + "🦠":["微生物","アメーãƒ","ãƒã‚¯ãƒ†ãƒªã‚¢","ウイルス"], + "ðŸ¢":["カメ"], + "ðŸ":["ヘビ","é‹æ¬äºº","ã¸ã³ã¤ã‹ã„座","蛇","星座"], + "🦎":["トカゲ","爬虫類"], + "ðŸ™":["タコ","蛸"], + "🦑":["イカ","軟体動物","çƒè³Š"], + "🪼":["クラゲ","焼ã","無脊椎動物","ゼリー","æµ·","ç—›ã„","刺毛"], + "🦞":["ãƒãƒ–スター","ビスク","爪","シーフード"], + "🦀":["カニ","ã‹ã«åº§","蟹","星座"], + "ðŸ¦":["エビ","è²","å°ã•ã„"], + "🦪":["ã‚«ã‚","真ç ","ダイビング"], + "ðŸ ":["熱帯éš","éš","熱帯"], + "ðŸŸ":["éš","ã†ãŠåº§","星座"], + "ðŸ¡":["フグ","éš"], + "ðŸ¬":["イルカ","ã²ã‚Œ"], + "🦈":["サメ","éš"], + "ðŸ¦":["アザラシ","アシカ"], + "ðŸ³":["æ½®å¹ãクジラ","é¡”","æ½®å¹ã","クジラ"], + "ðŸ‹":["クジラ"], + "ðŸŠ":["ワニ"], + "ðŸ†":["ヒョウ"], + "ðŸ…":["トラ","虎"], + "ðŸƒ":["スイギュウ","水牛","æ°´"], + "ðŸ‚":["雄牛","牡牛","ãŠã†ã—座","星座"], + "ðŸ„":["ウシ","牛"], + "🦬":["ãƒã‚¤ã‚½ãƒ³","ãƒãƒƒãƒ•ã‚¡ãƒãƒ¼","群れ","ヴィセント"], + "ðŸª":["ヒトコブラクダ","ラクダ","ã“ã¶"], + "ðŸ«":["フタコブラクダ","フタコブ","ラクダ","ã“ã¶"], + "🦙":["ラマ","アルパカ","グアナコ","ビクーニャ","ウール"], + "ðŸ˜":["ゾウ","象"], + "ðŸ¦":["サイ"], + "🦛":["ã‚«ãƒ"], + "🦣":["マンモス","絶滅","大型","牙","毛ã«è¦†ã‚ã‚ŒãŸ"], + "ðŸ":["ヤギ","ã‚„ãŽåº§","星座"], + "ðŸ":["仔羊","ãŠã²ã¤ã˜åº§","ヒツジ","星座"], + "ðŸ‘":["ヒツジ","雌羊"], + "ðŸŽ":["馬","競馬","レース"], + "ðŸ«":["ãƒãƒ","動物","ブーãƒ","哺乳類","ラãƒ"], + "ðŸ–":["ブタ","雌豚"], + "🦇":["コウモリ","å¸è¡€é¬¼"], + "ðŸ“":["ãŠã‚“ã©ã‚Š"], + "🦃":["七é¢é³¥ï¼ˆé³¥ï¼‰","七é¢é³¥","é³¥"], + "🕊":["平和ã®é³©","é³¥","鳩","飛行","平和"], + "🦅":["ワシ","é³¥"], + "🦆":["アヒル","é³¥"], + "🪿":["ガãƒãƒ§ã‚¦","é³¥","家禽","è¦ç¬›ã®éŸ³"], + "🦢":["白鳥","é³¥","白鳥ã®é›„","醜ã„アヒルã®å"], + "🦉":["フクãƒã‚¦","é³¥","è³¢ã„"], + "🦩":["フラミンゴ","熱帯","鮮やã‹"], + "🦚":["オスã®ã‚¯ã‚¸ãƒ£ã‚¯","é³¥","メスã®ã‚¯ã‚¸ãƒ£ã‚¯"], + "🦜":["オウム","é³¥","海賊"], + "🦤":["ドードー","é³¥","絶滅"], + "🪽":["ç¾½","天使","航空","é³¥","飛行","神話"], + "🪶":["羽毛","é³¥","軽ã„","ç¾½"], + "ðŸ•":["イヌ","犬","ペット"], + "🦮":["盲導犬","アクセシビリティ","ç›®ãŒä¸è‡ªç”±","犬","ガイド"], + "ðŸ•â€ðŸ¦º":["介助犬","アクセシビリティ","支æ´","犬","サービス"], + "ðŸ©":["プードル","イヌ","犬"], + "ðŸˆ":["ãƒã‚³","猫","ペット"], + "ðŸˆâ€â¬›":["黒猫","é»’","猫","ペット","ãƒãƒã‚¦ã‚£ãƒ¼ãƒ³"], + "ðŸ‡":["ウサギ","ãƒãƒ‹ãƒ¼","ペット"], + "ðŸ€":["ãƒã‚ºãƒŸ"], + "ðŸ":["ãƒã‚ºãƒŸ"], + "ðŸ¿":["シマリス"], + "🦨":["スカンク","悪è‡","è‡ã†"], + "🦡":["アナグマ","ラーテル","ãã ã‚‹"], + "🦔":["ãƒãƒªãƒã‚ºãƒŸ","é¡”"], + "ðŸ¾":["動物ã®è¶³ã‚ã¨","足","è·¡"], + "ðŸ‰":["ドラゴン","ãŠã¨ãŽè©±"], + "ðŸ²":["ドラゴンã®é¡”","ドラゴン","é¡”","ãŠã¨ãŽè©±"], + "🦕":["竜脚類","ブラã‚オサウルス","ブãƒãƒ³ãƒˆã‚µã‚¦ãƒ«ã‚¹","ディプãƒãƒ‰ã‚¯ã‚¹","æç«œ"], + "🦖":["ティラノサウルス","Tレックス","æç«œ"], + "🌵":["サボテン","æ¤ç‰©"], + "🎄":["クリスマスツリー","アクティビティ","ãŠç¥ã„","クリスマス","エンターテイメント","ツリー"], + "🌲":["常緑樹","常緑","æ¤ç‰©","木"], + "🌳":["è½è‘‰æ¨¹","è½è‘‰æ€§","æ¤ç‰©","è½è‘‰","木"], + "🌴":["ヤシã®æœ¨","ヤシ","æ¤ç‰©","木"], + "🪴":["鉢æ¤ãˆ","æ¤ç‰©","観葉æ¤ç‰©"], + "🌱":["苗木","æ¤ç‰©","è‹¥ã„"], + "🌿":["ãƒãƒ¼ãƒ–","葉","æ¤ç‰©"], + "☘":["クãƒãƒ¼ãƒãƒ¼","æ¤ç‰©"], + "ðŸ€":["å››ã¤è‘‰ã®ã‚¯ãƒãƒ¼ãƒãƒ¼","4","クãƒãƒ¼ãƒãƒ¼","å››","葉","æ¤ç‰©"], + "ðŸŽ":["é–€æ¾","アクティビティ","竹","ãŠç¥ã„","日本","æ¾","æ¤ç‰©"], + "🎋":["七夕","アクティビティ","æ——","ãŠç¥ã„","エンターテイメント","日本","木"], + "ðŸƒ":["風ã«ãªã³ã葉","å¹ã","ã¯ãŸã‚ã","葉","æ¤ç‰©","風"], + "ðŸ‚":["è½ã¡è‘‰","è½ä¸‹","葉","æ¤ç‰©"], + "ðŸ":["カエデã®è‘‰","è½ä¸‹","葉","カエデ","æ¤ç‰©"], + "🌾":["稲穂","稲æŸ","ç©‚","æ¤ç‰©","ç±³"], + "🪺":["åµã®ã‚ã‚‹å·£","巣作り","é³¥ã®å·£","åµ"], + "🪹":["空ã®å·£","巣作り","é³¥ã®å·£"], + "🌺":["ãƒã‚¤ãƒ“スカス","花","æ¤ç‰©"], + "🌻":["ヒマワリ","花","æ¤ç‰©","太陽","ã²ã¾ã‚ã‚Š"], + "🌹":["ãƒãƒ©","花","æ¤ç‰©"], + "🥀":["ã—ãŠã‚ŒãŸèŠ±","花","ã—ãŠã‚ŒãŸ"], + "🌷":["ãƒãƒ¥ãƒ¼ãƒªãƒƒãƒ—","花","æ¤ç‰©"], + "🌼":["花","æ¤ç‰©"], + "🌸":["æ¡œ","花","æ¤ç‰©"], + "🪷":["ãƒã‚¹","ä»æ•™","花","ヒンドゥー教","インド","清浄","ベトナム"], + "🪻":["ヒアシンス","ブルーボンãƒãƒƒãƒˆ","花","ラベンダー","ルピナス","ノウルーズ","ç´«","ã‚ンギョソウ"], + "ðŸ’":["花æŸ","花","æ¤ç‰©","ãƒãƒžãƒ³ã‚¹"], + "ðŸ„":["ã‚ノコ","æ¤ç‰©"], + "ðŸš":["å·»ãè²","è²"], + "🪸":["サンゴ","大洋","ç¤"], + "🌎":["アメリカ大陸","アメリカ","地çƒ","世界"], + "ðŸŒ":["ヨーãƒãƒƒãƒ‘ã¨ã‚¢ãƒ•ãƒªã‚«åœ°åŸŸ","アフリカ","地çƒ","ヨーãƒãƒƒãƒ‘","世界"], + "ðŸŒ":["アジアã¨ã‚ªãƒ¼ã‚¹ãƒˆãƒ©ãƒªã‚¢","アジア","オーストラリア","地çƒ","世界"], + "🌕":["満月","月","宇宙","天気"], + "🌖":["å¯å¾…月","å三夜","月","宇宙","æ¬ ã‘","天気"], + "🌗":["下弦ã®æœˆ","月","弦","宇宙","天気"], + "🌘":["æ¬ ã‘ã¦ã„ã三日月","三日月","月","宇宙","æ¬ ã‘","天気"], + "🌑":["新月","晦","月","宇宙","天気"], + "🌒":["満ã¡ã¦ã„ã三日月","三日月","月","宇宙","上弦","天気"], + "🌓":["上弦ã®æœˆ","月","弦","宇宙","天気"], + "🌔":["å三夜月","å三夜","月","宇宙","上弦","天気"], + "🌙":["三日月","月","宇宙","天気"], + "🌚":["é¡”ã¤ã新月","é¡”","月","宇宙","天気"], + "ðŸŒ":["é¡”ã¤ã満月","明るã„","é¡”","満ã¡ãŸ","月","宇宙","天気"], + "🌛":["é¡”ã¤ã上弦ã®æœˆ","é¡”","月","弦","宇宙","天気"], + "🌜":["é¡”ãŒã‚る下弦ã®æœˆ","é¡”","月","弦","宇宙","天気"], + "â":["ä¸ãらã„ã®æ˜Ÿ","星"], + "🌟":["光る星","ãらã‚ã","赤ã„å…‰","è¼ã","è¼ã","星"], + "💫":["ãらãら","漫画","ã‚ã¾ã„","星"], + "✨":["ã‚ラã‚ラ","エンターテイメント","è¼ã","星"], + "☄":["彗星","宇宙"], + "ðŸª":["ç’°ã®ã‚る惑星","宇宙","惑星","土星"], + "🌞":["é¡”ã¤ã太陽","明るã„","é¡”","宇宙","太陽","天気"], + "☀ï¸":["太陽ã®å…‰","明るã„","光線","宇宙","太陽","晴天","天気"], + "🌤":["太陽ã¨å°ã•ãªé›²","雲","太陽","天気"], + "â›…":["晴れ時々曇り","雲","太陽","天気"], + "🌥":["æ™´ã‚Œã®ã¡æ›‡ã‚Š","雲","太陽","天気"], + "🌦":["æ™´ã‚Œã®ã¡æ›‡ã‚Šæ™‚々雨","雲","雨","太陽","天気"], + "â˜ï¸":["雲","天気"], + "🌧":["雨雲","雲","雨","天気"], + "⛈":["雷雨","雲","雨","é›·","天気"], + "🌩":["雷雲","雲","é›·","天気"], + "âš¡":["高電圧記å·","å±é™º","電気","é›·","電圧","ビリビリ"], + "🔥":["ç‚Ž","ç«","é“å…·"], + "💥":["è¡çªãƒžãƒ¼ã‚¯","ã©ã‹ãƒ¼ã‚“","è¡çª","漫画"], + "â„ï¸":["雪ã®çµæ™¶","冷ãŸã„","雪","天気"], + "🌨":["雪雲","雲","冷","雪","天気"], + "☃":["雪ã ã‚‹ã¾","冷","雪","天気"], + "⛄":["雪ã ã‚‹ã¾","冷","雪","天気"], + "🌬":["風ãŒå¹ã„ã¦ã„ã‚‹","風ãŒå¹ã","雲","é¡”","天気","風"], + "💨":["ダッシュ","漫画","èµ°ã‚‹"], + "🌪":["竜巻雲","雲","竜巻","天気","旋風"], + "🌫":["霧","雲","天気"], + "🌈":["虹","雨","レインボー","天気","プライド","lgbt"], + "☔":["雨ã¨å‚˜","衣類","ã—ãšã","雨","傘","天気"], + "💧":["雫","ãžã£ã¨ã™ã‚‹","漫画","ã—ãŸãŸã‚Š","æ±—","天気"], + "💦":["汗マーク","漫画","æ¿¡ã‚Œã¦ã„ã‚‹","æ±—"], + "🌊":["æ³¢","æµ·","æ°´","天気"], + "ðŸ":["é’ã‚Šã‚“ã”","リンゴ","フルーツ","果物","ç·‘","æ¤ç‰©"], + "ðŸŽ":["赤ã„リンゴ","リンゴ","フルーツ","果物","æ¤ç‰©","赤"], + "ðŸ":["梨","フルーツ","果物","æ¤ç‰©"], + "ðŸŠ":["ã¿ã‹ã‚“","フルーツ","果物","オレンジ","æ¤ç‰©","赤橙色"], + "ðŸ‹":["レモン","柑橘類","フルーツ","果物","æ¤ç‰©"], + "ðŸŒ":["ãƒãƒŠãƒŠ","フルーツ","果物","æ¤ç‰©"], + "ðŸ‰":["スイカ","フルーツ","果物","æ¤ç‰©"], + "ðŸ‡":["ブドウ","フルーツ","果物","æ¤ç‰©"], + "ðŸ“":["イãƒã‚´","ベリー","フルーツ","果物","æ¤ç‰©"], + "ðŸˆ":["メãƒãƒ³","フルーツ","果物","æ¤ç‰©"], + "ðŸ’":["ã•ãらんã¼","フルーツ","果物","æ¤ç‰©"], + "ðŸ«":["ブルーベリー","ベリー","ビルベリー","é’","フルーツ"], + "ðŸ‘":["桃","フルーツ","果物","æ¤ç‰©"], + "ðŸ¥":["マンゴー","熱帯","フルーツ"], + "ðŸ":["パイナップル","フルーツ","果物","æ¤ç‰©"], + "🥥":["ココナッツ","フルーツ"], + "ðŸ¥":["ã‚ウイフルーツ","フルーツ","果物","ã‚ウイ"], + "ðŸ…":["トマト","æ¤ç‰©","野èœ"], + "🥑":["アボカド","フルーツ","果物"], + "🫒":["オリーブ","フルーツ"], + "ðŸ†":["ナス","茄å","æ¤ç‰©","野èœ"], + "🌶":["トウガラシ","è¾›ã„","コショウ","æ¤ç‰©"], + "🫑":["ピーマン","å”è¾›å","コショウ","æ¤ç‰©","野èœ"], + "🥒":["ã‚ュウリ","ピクルス","野èœ"], + "🥬":["葉ã£ã±ã®ç·‘","ãƒãƒ³ã‚²ãƒ³èœ","ã‚ャベツ","ケール","レタス"], + "🥦":["ブãƒãƒƒã‚³ãƒªãƒ¼","野èœ"], + "🫛":["エンドウ豆ã®ã•ã‚„","豆","æžè±†","マメ科","エンドウ豆","ã•ã‚„","野èœ"], + "🧄":["ã«ã‚“ã«ã","野èœ","æ¤ç‰©","香味料"], + "🧅":["玉ããŽ","野èœ","æ¤ç‰©","香味料"], + "🌽":["トウモãƒã‚³ã‚·","コーン","æ¤ç‰©"], + "🥕":["ニンジン","野èœ"], + "🥗":["グリーンサラダ","ç·‘","サラダ"], + "🥔":["ジャガイモ","野èœ"], + "ðŸ ":["焼ã芋","ジャガイモ","焼ã","スイーツ"], + "🌰":["æ —","æ¤ç‰©"], + "🥜":["ピーナッツ","ナッツ","野èœ"], + "🫘":["豆","食ã¹ç‰©","腎臓","マメ"], + "ðŸ¯":["ãƒãƒ‹ãƒ¼ãƒãƒƒãƒˆ","ã¯ã¡ã¿ã¤","ãƒãƒƒãƒˆ","スイーツ"], + "ðŸž":["パン","ãƒãƒ¼ãƒ•"], + "ðŸ¥":["クãƒãƒ¯ãƒƒã‚µãƒ³","パン","三日月","ãƒãƒ¼ãƒ«","フレンãƒ"], + "🥖":["フランスパン","パン","フレンãƒ"], + "🫓":["フラットブレッド","アレパ","ラヴァシュ","ナン","ピタ"], + "🥨":["プレッツェル","ソフトプレッツェル","プレッツェルツイスト","パン"], + "🥯":["ベーグル","パン","クリームãƒãƒ¼ã‚º","ã²ã¨å¡—ã‚Š"], + "🥞":["パンケーã‚","クレープ","ホットケーã‚"], + "🧇":["ワッフル","ホットケーã‚"], + "🧀":["ãƒãƒ¼ã‚º"], + "ðŸ—":["ターã‚ー","骨","ニワトリ","è„š","家禽"], + "ðŸ–":["骨付ã肉","骨","肉"], + "🥩":["一切れã®è‚‰","肉","切り身","ラムãƒãƒ§ãƒƒãƒ—","豚","ステーã‚"], + "ðŸ¤":["エビフライ","フライ","エビ","å°ã‚¨ãƒ“","ã¦ã‚“ã·ã‚‰"], + "🥚":["åµ"], + "ðŸ³":["æ–™ç†","åµ","フライパン","é‹"], + "🥓":["ベーコン","肉"], + "ðŸ”":["ãƒãƒ³ãƒãƒ¼ã‚¬ãƒ¼","ãƒãƒ¼ã‚¬ãƒ¼"], + "ðŸŸ":["フライドãƒãƒ†ãƒˆ","フライド","ãƒãƒ†ãƒˆ"], + "ðŸŒ":["ホットドッグ","フランクフルトソーセージ","ホットドッグソーセージ","ソーセージ","ウィンナー","レッドホット"], + "ðŸ•":["ピザ","ãƒãƒ¼ã‚º","1æžš"], + "ðŸ":["スパゲッティ","パスタ"], + "🥪":["サンドウィッãƒ","パン","野èœ","ãƒãƒ¼ã‚º","肉","デリ"], + "🌮":["タコス","メã‚シコ"], + "🌯":["ブリトー","メã‚シコ"], + "🫔":["タマーレ","タマーリ","メã‚シカン","包ã¾ã‚ŒãŸ"], + "🥙":["フラットブレッドサンド","ファラフェル","フラットブレッド","ジャイãƒ","ケãƒãƒ–","è©°ã‚物"], + "🧆":["ファラフェル","ã²ã‚ˆã“豆"], + "ðŸœ":["ã©ã‚“ã¶ã‚Š","麺","ラーメン","è’¸ã—åŠ ç†±","スープ"], + "🥘":["パエリア","ã‚ャセãƒãƒ¼ãƒ«","é‹","æµ…ã„"], + "ðŸ²":["ãªã¹","é‹","ã‚·ãƒãƒ¥ãƒ¼"], + "🫕":["フォンデュ","ãƒãƒ¼ã‚º","ãƒãƒ§ã‚³ãƒ¬ãƒ¼ãƒˆ","フォデュ","溶ã‘ãŸ","ãƒãƒƒãƒˆ","スイス"], + "🥫":["缶詰","ã‹ã‚“ã¥ã‚","ä¿å˜ç”¨é£Ÿå“"], + "🫙":["瓶","香辛料","容器","空","ソース","貯蔵"], + "🧂":["å¡©","香辛料","シェーカー"], + "🧈":["ãƒã‚¿ãƒ¼","乳製å“"], + "🫚":["ショウガ","ビール","æ ¹","スパイス"], + "ðŸ¥":["ãªã‚‹ã¨","固形ã®é£Ÿã¹ç‰©","éš","練り物"], + "ðŸ£":["寿å¸"], + "ðŸ±":["å¼å½“ç®±","å¼å½“","ç®±"], + "ðŸ›":["カレーライス","カレー","ã”飯"], + "ðŸ™":["ãŠã«ãŽã‚Š","日本","ç±³"], + "ðŸš":["ã”ã¯ã‚“","æ–™ç†","ç±³"], + "ðŸ˜":["ã›ã‚“ã¹ã„","ç±³"], + "🥟":["餃å","ギョウザ"], + "ðŸ¢":["ãŠã§ã‚“","シーフード","串","スティック"], + "ðŸ¡":["団å","デザート","日本","串","スティック","スイーツ"], + "ðŸ§":["ã‹ãæ°·","デザート","æ°·","スイーツ"], + "ðŸ¨":["アイスクリーム","クリーム","デザート","æ°·","スイーツ"], + "ðŸ¦":["ソフトクリーム","クリーム","デザート","æ°·","アイスクリーム","ソフト","スイーツ"], + "ðŸ°":["ショートケーã‚","ケーã‚","デザート","ペイストリー","スライス","スイーツ"], + "🎂":["ãƒãƒ¼ã‚¹ãƒ‡ãƒ¼ã‚±ãƒ¼ã‚","誕生日","ケーã‚","ãŠç¥ã„","デザート","ペイストリー","スイーツ"], + "ðŸ§":["カップケーã‚","ベーカリー","スイーツ","デザート","ペイストリー"], + "🥧":["パイ","デザート","スイーツ"], + "ðŸ®":["カスタード","デザート","プリン","スイーツ"], + "ðŸ":["ペãƒãƒšãƒã‚ャンディー","ã‚ャンディ","デザート","ãƒãƒªãƒãƒƒãƒ—ã‚ャンディ","スイーツ"], + "ðŸ¬":["アメ","デザート","スイーツ"], + "ðŸ«":["ãƒãƒ§ã‚³ãƒ¬ãƒ¼ãƒˆ","ãƒãƒ¼","デザート","スイーツ"], + "ðŸ¿":["ãƒãƒƒãƒ—コーン"], + "ðŸ©":["ドーナツ","デザート","スイーツ"], + "ðŸª":["クッã‚ー","デザート","甘ã„"], + "🥠":["ãŠã¿ãã˜å…¥ã‚Šã‚¯ãƒƒã‚ー","フォーãƒãƒ¥ãƒ³ã‚¯ãƒƒã‚ー"], + "🥮":["月餅","秋","ç¥"], + "☕":["ホットドリンク","飲料","コーヒー","飲ã¿ç‰©","温ã‹ã„","蒸気","ãŠèŒ¶"], + "ðŸµ":["湯ã®ã¿","飲料","カップ","飲ã¿ç‰©","ãŠèŒ¶","湯飲ã¿"], + "🫖":["ティーãƒãƒƒãƒˆ","ドリンク","ãƒãƒƒãƒˆ","ティー","ケトル"], + "🥣":["ボウルã¨ã‚¹ãƒ—ーン","æœé£Ÿ","シリアル","ãŠç²¥","オートミール","ãƒãƒªãƒƒã‚¸","食器"], + "ðŸ¼":["哺乳瓶","赤ã¡ã‚ƒã‚“","ボトル","ドリンク","ミルク"], + "🥤":["カップã¨ã‚¹ãƒˆãƒãƒ¼","ジュース","ソーダ","モルト","ソフトドリンク","æ°´","食器"], + "🧋":["タピオカティー","ãƒãƒ–ル","ミルク","パール","ティー","ボãƒ","タピオカ","モミ"], + "🧃":["飲料ボックス","ジュース","飲料","ボックス","ドリンク","ストãƒãƒ¼"], + "🧉":["マテ","ドリンク","ボンビリヤ","イエルãƒ"], + "🥛":["コップã«å…¥ã£ãŸç‰›ä¹³","ドリンク","グラス","ミルク"], + "🫗":["æµã‚Œè¾¼ã‚€æ¶²ä½“","飲ã¿ç‰©","空","グラス","ã“ã¼ã‚Œã‚‹"], + "ðŸº":["ビール","ãƒãƒ¼","飲む","マグカップ"], + "ðŸ»":["ä¹¾æ¯","ãƒãƒ¼","ビール","ã‚«ãƒãƒ³","飲ã¿ç‰©","マグカップ"], + "ðŸ·":["ワイングラス","ãƒãƒ¼","飲料","飲ã¿ç‰©","グラス","ワイン"], + "🥂":["グラスã§ä¹¾æ¯","ç¥ã†","ã‚«ãƒãƒ³","飲ã¿ç‰©","グラス"], + "🥃":["タンブラー","グラス","é…’","ショット","ウイスã‚ー","ウィスã‚ー","ãƒãƒ¼ãƒœãƒ³"], + "ðŸ¸":["カクテルグラス","ãƒãƒ¼","カクテル","飲ã¿ç‰©","グラス"], + "ðŸ¹":["トãƒãƒ”カルドリンク","ãƒãƒ¼","飲ã¿ç‰©","トãƒãƒ”カル"], + "ðŸ¾":["瓶ã¨é£›ã³å‡ºã™æ “","ãƒãƒ¼","ボトル","シャンパン","シャンペン","シャンパーニュ","コルク","飲ã¿ç‰©","飛ã³å‡ºã™","パーティー"], + "ðŸ¶":["ã¨ã£ãã‚Šã¨ãŠã¡ã‚‡ã“","ãƒãƒ¼","飲料","ボトル","カップ","飲ã¿ç‰©","é…’"], + "🧊":["角氷","æ°·","立方体","冷ãŸã„","æ°·å±±"], + "🥄":["スプーン","食器"], + "ðŸ´":["フォークã¨ãƒŠã‚¤ãƒ•","調ç†","フォーク","ナイフ","食器"], + "ðŸ½":["フォークã¨ãƒŠã‚¤ãƒ•ã¨ãƒ—レート","調ç†","フォーク","ナイフ","プレート","食器"], + "🥢":["箸","ã¯ã—"], + "🥡":["テイクアウトボックス","テイクアウト","容器","ãŠæŒã¡å¸°ã‚Š"], + "âš½":["サッカーボール","ボール","サッカー"], + "ðŸ€":["ãƒã‚¹ã‚±ãƒƒãƒˆãƒœãƒ¼ãƒ«","ボール","ãƒã‚¹ã‚±ãƒƒãƒˆãƒªãƒ³ã‚°"], + "ðŸˆ":["アメリカンフットボール","アメリカン","ボール","フットボール"], + "âš¾":["野çƒ","ボール"], + "🥎":["ソフトボール","ボール","試åˆ","スãƒãƒ¼ãƒ„"], + "🎾":["テニスボール","ボール","ラケット","テニス"], + "ðŸ":["ãƒãƒ¬ãƒ¼ãƒœãƒ¼ãƒ«","ボール","試åˆ"], + "ðŸ‰":["ラグビー","ボール","フットボール"], + "🎱":["ビリヤード","8","エイトボール","ボール","エイト","ゲーム"], + "ðŸ¥":["空飛ã¶å††ç›¤","ディスク","アルティメット","ゴルフ","試åˆ","スãƒãƒ¼ãƒ„","フリスビー"], + "🪃":["ブーメラン","オーストラリア","逆戻り","è·³ã返り"], + "ðŸ“":["å“çƒã®ãƒ©ã‚±ãƒƒãƒˆã¨ãƒœãƒ¼ãƒ«","ボール","ãƒãƒƒãƒˆ","試åˆ","パドル","å“çƒ"], + "ðŸ¸":["ãƒãƒ‰ãƒŸãƒ³ãƒˆãƒ³ã®ãƒ©ã‚±ãƒƒãƒˆã¨ã‚·ãƒ£ãƒˆãƒ«","ãƒãƒ‰ãƒŸãƒ³ãƒˆãƒ³","ãƒãƒ¼ãƒ‡ã‚£ãƒ¼","試åˆ","ラケット","シャトル"], + "🥅":["ゴールãƒãƒƒãƒˆ","ゴール","ãƒãƒƒãƒˆ"], + "ðŸ’":["アイスホッケーã®ã‚¹ãƒ†ã‚£ãƒƒã‚¯ã¨ãƒ‘ック","試åˆ","ホッケー","æ°·","パック","スティック"], + "ðŸ‘":["フィールドホッケーã®ã‚¹ãƒ†ã‚£ãƒƒã‚¯ã¨ãƒœãƒ¼ãƒ«","ボール","フィールド","試åˆ","ホッケー","スティック"], + "ðŸ":["クリケットã®ãƒãƒƒãƒˆã¨ãƒœãƒ¼ãƒ«","ボール","フィールド","クリケット","試åˆ"], + "ðŸ¥":["ラクãƒã‚¹","ボール","スティック","試åˆ","スãƒãƒ¼ãƒ„"], + "🥌":["カーリングストーン","カーリング","ストーン"], + "⛳":["ゴルフã®ã‚«ãƒƒãƒ—","ピンフラッグ","ゴルフ","ホール"], + "ðŸ¹":["弓矢","射手","矢","弓","射手座","é“å…·","星座"], + "🎣":["釣竿ã¨éš","エンターテイメント","éš","棒"], + "🤿":["ダイビングマスク","ダイビング","スã‚ューãƒ","シュノーケル"], + "🥊":["ボクシンググãƒãƒ¼ãƒ–","ボクシング","ã‚°ãƒãƒ¼ãƒ–"], + "🥋":["é“ç€","柔é“","空手","æ¦é“","テコンドー","ユニフォーム"], + "⛸":["アイススケート","æ°·"], + "🎿":["スã‚ーã¨ã‚¹ã‚ーブーツ","スã‚ー","雪"], + "🛷":["ãã‚Š","ソリ","ルージュ","トボガン"], + "â›·":["スã‚ー","雪"], + "ðŸ‚":["スノーボーダー","スã‚ー","雪","スノーボード"], + "ðŸ‹ï¸â€â™€ï¸":["ウエイトをæŒã¡ä¸Šã’る女性","挙ã’","é‡é‡","女性","女","ãŠã‚“ãª"], + "ðŸ‹":["ウエイトをæŒã¡ä¸Šã’る人","挙ã’","é‡é‡"], + "ðŸ‹ï¸â€â™‚ï¸":["ウエイトをæŒã¡ä¸Šã’る男性","挙ã’","é‡é‡","ç”·","ãŠã¨ã“","男性"], + "🤺":["フェンシングをã™ã‚‹äºº","剣士","剣術","剣"], + "🤼â€â™€ï¸":["レスリングをã™ã‚‹å¥³æ€§","レスリング","レスリングé¸æ‰‹","女性","女","ãŠã‚“ãª"], + "🤼":["レスリングをã™ã‚‹äººãŸã¡","レスリング","レスリングé¸æ‰‹"], + "🤼â€â™‚ï¸":["レスリングをã™ã‚‹ç”·æ€§","レスリング","レスリングé¸æ‰‹","ç”·","ãŠã¨ã“","男性"], + "🤸â€â™€ï¸":["å´è»¢ã‚’ã™ã‚‹å¥³æ€§","å´æ–¹è»¢å›ž","体æ“","女性","女","ãŠã‚“ãª"], + "🤸":["å´è»¢ã‚’ã™ã‚‹äºº","å´æ–¹è»¢å›ž","体æ“"], + "🤸â€â™‚ï¸":["å´è»¢ã‚’ã™ã‚‹ç”·æ€§","å´æ–¹è»¢å›ž","体æ“","ç”·","ãŠã¨ã“","男性"], + "⛹ï¸â€â™€ï¸":["ボールをãƒã‚¦ãƒ³ãƒ‰ã•ã›ã‚‹å¥³æ€§","ボール","女性","女","ãŠã‚“ãª"], + "⛹":["ボールをãƒã‚¦ãƒ³ãƒ‰ã•ã›ã‚‹äºº","ボール"], + "⛹ï¸â€â™‚ï¸":["ボールをãƒã‚¦ãƒ³ãƒ‰ã•ã›ã‚‹ç”·æ€§","ボール","ç”·","ãŠã¨ã“","男性"], + "🤾â€â™€ï¸":["ãƒãƒ³ãƒ‰ãƒœãƒ¼ãƒ«ã‚’ã™ã‚‹å¥³æ€§","ボール","ãƒãƒ³ãƒ‰ãƒœãƒ¼ãƒ«","女性","女","ãŠã‚“ãª"], + "🤾":["ãƒãƒ³ãƒ‰ãƒœãƒ¼ãƒ«ã‚’ã™ã‚‹äºº","ボール","ãƒãƒ³ãƒ‰ãƒœãƒ¼ãƒ«"], + "🤾â€â™‚ï¸":["ãƒãƒ³ãƒ‰ãƒœãƒ¼ãƒ«ã‚’ã™ã‚‹ç”·æ€§","ボール","ãƒãƒ³ãƒ‰ãƒœãƒ¼ãƒ«","ç”·","ãŠã¨ã“","男性"], + "🧗â€â™€ï¸":["クライミングã—ã¦ã„る女性","クライミング","ãƒãƒƒã‚¯","女性","女","ãŠã‚“ãª"], + "🧗":["クライミングã—ã¦ã„る人","クライミング","ãƒãƒƒã‚¯"], + "🧗â€â™‚ï¸":["クライミングã—ã¦ã„る男性","クライミング","ãƒãƒƒã‚¯","男性","ç”·","ãŠã¨ã“"], + "ðŸŒï¸â€â™€ï¸":["ゴルフをã™ã‚‹å¥³æ€§","ボール","ゴルフ","ゴルファー","ゴルフã™ã‚‹","女性","女","ãŠã‚“ãª"], + "ðŸŒ":["ゴルフをã™ã‚‹äºº","ボール","ゴルフ","ゴルファー","ゴルフã™ã‚‹"], + "ðŸŒï¸â€â™‚ï¸":["ゴルフをã™ã‚‹ç”·æ€§","ボール","ゴルフ","ゴルファー","ゴルフã™ã‚‹","ç”·","ãŠã¨ã“","男性"], + "🧘â€â™€ï¸":["è“®è¯åº§ã®å¥³æ€§","瞑想","ヨガ","é™ç©","女性","女","ãŠã‚“ãª"], + "🧘":["è“®è¯åº§ã®äºº","瞑想","ヨガ","é™ç©"], + "🧘â€â™‚ï¸":["è“®è¯åº§ã®ç”·æ€§","瞑想","ヨガ","é™ç©","男性","ç”·","ãŠã¨ã“"], + "🧖â€â™€ï¸":["スãƒãƒ¼ãƒ ルームã«ã„る女性","サウナ","スãƒãƒ¼ãƒ ルーム","ãƒãƒžãƒ ","スãƒãƒ¼ãƒ ãƒã‚¹","女性","女","ãŠã‚“ãª"], + "🧖":["スãƒãƒ¼ãƒ ルームã«ã„る人","サウナ","スãƒãƒ¼ãƒ ルーム","ãƒãƒžãƒ ","スãƒãƒ¼ãƒ ãƒã‚¹"], + "🧖â€â™‚ï¸":["スãƒãƒ¼ãƒ ルームã«ã„る男性","サウナ","スãƒãƒ¼ãƒ ルーム","ãƒãƒžãƒ ","スãƒãƒ¼ãƒ ãƒã‚¹","男性","ç”·","ãŠã¨ã“"], + "ðŸ„â€â™€ï¸":["サーフィンをã™ã‚‹å¥³æ€§","サーファー","サーフィン","波乗り","女性","女","ãŠã‚“ãª"], + "ðŸ„":["サーフィンをã™ã‚‹äºº","サーファー","サーフィン","波乗り"], + "ðŸ„â€â™‚ï¸":["サーフィンをã™ã‚‹ç”·æ€§","サーファー","サーフィン","波乗り","ç”·","ãŠã¨ã“","男性"], + "ðŸŠâ€â™€ï¸":["æ³³ã女性","æ³³ã","æ°´æ³³","女性","女","ãŠã‚“ãª"], + "ðŸŠ":["水泳をã™ã‚‹äºº","æ³³ã","æ°´æ³³"], + "ðŸŠâ€â™‚ï¸":["æ³³ã男性","æ³³ã","æ°´æ³³","ç”·","ãŠã¨ã“","男性"], + "🤽â€â™€ï¸":["æ°´çƒã‚’ã™ã‚‹å¥³æ€§","ãƒãƒ","æ°´","æ°´çƒ","女性","女","ãŠã‚“ãª"], + "🤽":["æ°´çƒã‚’ã™ã‚‹äºº","ãƒãƒ","æ°´","æ°´çƒ"], + "🤽â€â™‚ï¸":["æ°´çƒã‚’ã™ã‚‹ç”·æ€§","ãƒãƒ","æ°´","æ°´çƒ","ç”·","ãŠã¨ã“","男性"], + "🚣â€â™€ï¸":["ボートを漕ã女性","ボート","漕ãŽèˆ¹","乗り物","漕艇","女性","女","ãŠã‚“ãª"], + "🚣":["ボートをã“ã人","ボート","漕ãŽèˆ¹","乗り物","漕艇"], + "🚣â€â™‚ï¸":["ボートを漕ã男性","ボート","漕ãŽèˆ¹","乗り物","漕艇","ç”·","ãŠã¨ã“","男性"], + "ðŸ‡":["競馬","馬","騎手","競走馬"], + "🚴â€â™€ï¸":["自転車ã«ä¹—る女性","自転車","自転車乗り","自転車ã«ä¹—る人","サイクリスト","女性","女","ãŠã‚“ãª"], + "🚴":["自転車ã«ä¹—る人","自転車","自転車乗り","サイクリスト"], + "🚴â€â™‚ï¸":["自転車ã«ä¹—る男性","自転車","自転車乗り","自転車ã«ä¹—る人","サイクリスト","ç”·","ãŠã¨ã“","男性"], + "🚵â€â™€ï¸":["マウンテンãƒã‚¤ã‚¯ã«ä¹—る女性","マウンテンãƒã‚¤ã‚¯ãƒ©ã‚¤ãƒ€ãƒ¼","クãƒã‚¹ãƒã‚¤ã‚¯","自転車","自転車乗り","自転車ã«ä¹—る人","サイクリスト","å±±","女性","女","ãŠã‚“ãª"], + "🚵":["マウンテンãƒã‚¤ã‚¯ã«ä¹—る人","マウンテンãƒã‚¤ã‚¯ãƒ©ã‚¤ãƒ€ãƒ¼","クãƒã‚¹ãƒã‚¤ã‚¯","自転車","自転車乗り","自転車ã«ä¹—る人","å±±"], + "🚵â€â™‚ï¸":["マウンテンãƒã‚¤ã‚¯ã«ä¹—る男性","マウンテンãƒã‚¤ã‚¯ãƒ©ã‚¤ãƒ€ãƒ¼","クãƒã‚¹ãƒã‚¤ã‚¯","自転車","自転車乗り","自転車ã«ä¹—る人","サイクリスト","å±±","ç”·","ãŠã¨ã“","男性"], + "🎽":["ランニングシャツã¨è¥·","ランニング","襷","シャツ"], + "🎖":["å‹²ç« ","ãŠç¥ã„","メダル","è»äº‹"], + "ðŸ…":["スãƒãƒ¼ãƒ„ã®ãƒ¡ãƒ€ãƒ«","メダル"], + "🥇":["金メダル","1ä½","金","メダル","1","第1ä½"], + "🥈":["銀メダル","メダル","2ä½","銀","2","第2ä½"], + "🥉":["銅メダル","銅","メダル","3ä½","3","第3ä½"], + "ðŸ†":["トãƒãƒ•ã‚£ãƒ¼","賞"], + "ðŸµ":["ãƒãƒ©é£¾ã‚Š","æ¤ç‰©"], + "🎗":["リマインダーリボン","ãŠç¥ã„","リマインダー","リボン"], + "🎫":["ãã£ã·","アクティビティ","å…¥å ´æ–™","エンターテイメント","ãƒã‚±ãƒƒãƒˆ"], + "🎟":["å…¥å ´åˆ¸","å…¥å ´æ–™","エンターテイメント","ãƒã‚±ãƒƒãƒˆ"], + "🎪":["サーカスå°å±‹","アクティビティ","サーカス","エンターテイメント","テント"], + "🤹â€â™€ï¸":["ジャグリングをã™ã‚‹å¥³æ€§","天秤","ジャグリング","女性","女","ãŠã‚“ãª"], + "🤹":["ジャグリングをã™ã‚‹äºº","ãƒãƒ©ãƒ³ã‚¹","ジャグリング"], + "🤹â€â™‚ï¸":["ジャグリングをã™ã‚‹ç”·æ€§","天秤","ジャグリング","男性","ç”·","ãŠã¨ã“"], + "ðŸŽ":["舞å°èŠ¸è¡“","アクティビティ","芸術","エンターテイメント","ä»®é¢","舞å°","シアター"], + "🎨":["çµµã®å…·ãƒ‘レット","アクティビティ","アート","エンターテイメント","美術館","絵画","パレット"], + "🎬":["ã‚«ãƒãƒ³ã‚³","アクティビティ","エンターテイメント","æ˜ ç”»"], + "🎤":["マイク","アクティビティ","エンターテイメント","カラオケ","マイクãƒãƒ•ã‚©ãƒ³"], + "🎧":["ヘッドホン","アクティビティ","イヤホン","エンターテイメント","ヘッドフォン"], + "🎼":["楽èœ","アクティビティ","エンターテイメント","音楽"], + "🎹":["éµç›¤","アクティビティ","エンターテイメント","楽器","ã‚ーボード","音楽","ピアノ"], + "🪗":["アコーディオン","コンサーティーナ","スクイーズボックス"], + "ðŸ¥":["ドラム","ドラムスティック","音楽"], + "🪘":["é•·ã„ドラム","ビート","コンガ","ドラム","リズム","ジャンベ"], + "🪇":["マラカス","ç¥ã†","楽器","音楽","騒音","打楽器","ガタガタ","リズム","シェイク"], + "🎷":["サックス","アクティビティ","エンターテイメント","楽器","音楽","サクソフォーン"], + "🎺":["トランペット","アクティビティ","エンターテイメント","楽器","音楽"], + "🪈":["フルート","竹","横笛å¥è€…","フルートå¥è€…","音楽","パイプ","リコーダー","å¹ã","木管楽器"], + "🎸":["ギター","アクティビティ","エンターテイメント","楽器","音楽"], + "🪕":["ãƒãƒ³ã‚¸ãƒ§ãƒ¼","アクティビティ","エンターテイメント","楽器","音楽"], + "🎻":["ãƒã‚¤ã‚ªãƒªãƒ³","アクティビティ","エンターテイメント","楽器","音楽"], + "🎲":["サイコãƒ","ã•ã„","エンターテイメント","ゲーム"], + "🧩":["パズルã®ãƒ”ース","手ãŒã‹ã‚Š","å™›ã¿åˆã†","ピース","パズル","ジグソー"], + "♟ï¸":["ãƒã‚§ã‚¹ã®ãƒãƒ¼ãƒ³","ãƒã‚§ã‚¹","駒","ゲーム","æ¨ã¦é§’"], + "🎯":["çš„ä¸","アクティビティ","ブル","ブルズアイ","ダーツ","エンターテイメント","ç›®","試åˆ","ヒット","標的"], + "🎳":["ボウリング","ボール","試åˆ"], + "🪀":["ヨーヨー","ãŠã‚‚ã¡ã‚ƒ","上下"], + "ðŸª":["凧","ãŠã‚‚ã¡ã‚ƒ","飛ã¶","舞ã†"], + "ðŸ›":["滑りå°","éŠåœ’地","éŠã³"], + "🎮":["テレビゲーム","コントãƒãƒ¼ãƒ©ãƒ¼","エンターテイメント","ゲーム","ビデオゲーム"], + "👾":["エイリアン","宇宙人","怪ç£","異星人","é¡”","ãŠã¨ãŽè©±","ファンタジー","モンスター","宇宙","UFO"], + "🎰":["スãƒãƒƒãƒˆãƒžã‚·ãƒ³","アクティビティ","ゲーム","スãƒãƒƒãƒˆ"], + "🚗":["自動車","車","乗り物"], + "🚙":["ã‚ャンピングカー","レクリエーション","RV","乗り物"], + "🚕":["タクシー","乗り物"], + "🛺":["オートリã‚シャ","人力車","トゥクトゥク"], + "🚌":["ãƒã‚¹","乗り物"], + "🚎":["トãƒãƒªãƒ¼ãƒã‚¹","ãƒã‚¹","è·¯é¢é›»è»Š","市街電車","乗り物"], + "ðŸŽ":["レーシングカー","車","競争"], + "🚓":["パトカー","車","パトãƒãƒ¼ãƒ«","è¦å¯Ÿ","乗り物"], + "🚑":["救急車","乗り物"], + "🚒":["消防車","エンジン","ç‚Ž","トラック","乗り物"], + "ðŸš":["マイクãƒãƒã‚¹","ãƒã‚¹","乗り物"], + "🛻":["ピックアップトラック","ピックアップ","トラック","乗り物"], + "🚚":["é…é”用トラック","é…é”","トラック","乗り物"], + "🚛":["トレーラー","大型トラック","セミ","トラック","乗り物"], + "🚜":["トラクター","乗り物"], + "ðŸ":["レースãƒã‚¤ã‚¯","オートãƒã‚¤","レース"], + "🛵":["スクーター","モーター"], + "🚲":["自転車","ãƒã‚¤ã‚¯","乗り物"], + "🦼":["電動車ã„ã™","アクセシビリティ","車ã„ã™"], + "🦽":["手動車ã„ã™","アクセシビリティ","車ã„ã™"], + "🛴":["ã‚ックボード","ã‚ック","スクーター"], + "🛹":["スケボー","スケート","ボード"], + "🛼":["ãƒãƒ¼ãƒ©ãƒ¼ã‚¹ã‚±ãƒ¼ãƒˆ","ãƒãƒ¼ãƒ©ãƒ¼","スケート"], + "🛞":["車輪","円","タイヤ","回転"], + "🚨":["パトライト","車","å…‰","è¦å¯Ÿ","回転","乗り物","サイレン","è¦å‘Š"], + "🚔":["パトカー","車","対å‘車","è¦å¯Ÿ","乗り物"], + "ðŸš":["ãƒã‚¹","対å‘車","乗り物"], + "🚘":["対å‘車","自動車","車","乗り物"], + "🚖":["タクシー","対å‘車","乗り物"], + "🚡":["ãƒãƒ¼ãƒ—ウェイ","空ä¸","ケーブル","車","ゴンドラ","トラムウェイ","乗り物"], + "🚠":["ãƒãƒ¼ãƒ—ウェイ","ケーブル","ゴンドラ","å±±","乗り物"], + "🚟":["高架鉄é“","鉄é“","乗り物"], + "🚃":["鉄é“車両","車","電気","鉄é“","列車","è·¯é¢","トãƒãƒªãƒ¼ãƒã‚¹","乗り物"], + "🚋":["è·¯é¢é›»è»Š","車","è·¯é¢","トãƒãƒªãƒ¼ãƒã‚¹","乗り物"], + "ðŸš":["モノレール","乗り物"], + "🚄":["新幹線","鉄é“","高速","列車","乗り物"], + "🚅":["新幹線","弾丸","鉄é“","高速","列車","乗り物"], + "🚈":["ライトレール","鉄é“","乗り物"], + "🚞":["山岳鉄é“","車","å±±","鉄é“","乗り物"], + "🚂":["蒸気機関車","エンジン","機関車","鉄é“","蒸気","列車","乗り物"], + "🚆":["電車","ç·šè·¯","乗り物"], + "🚇":["地下鉄","メトãƒ","乗り物"], + "🚊":["è·¯é¢é›»è»Š","トãƒãƒªãƒ¼ãƒã‚¹","乗り物"], + "🚉":["駅","ç·šè·¯","電車","乗り物"], + "ðŸš":["ヘリコプター","乗り物"], + "🛩":["å°åž‹èˆªç©ºæ©Ÿ","飛行機","乗り物"], + "✈ï¸":["飛行機","乗り物"], + "🛫":["飛行機ã®é›¢é™¸","飛行機","ãƒã‚§ãƒƒã‚¯ã‚¤ãƒ³","出発","乗り物"], + "🛬":["飛行機ã®ç€é™¸","飛行機","到ç€","ç€é™¸","乗り物"], + "🪂":["パラシュート","パラセール","スカイダイブ","ãƒãƒ³ã‚°ã‚°ãƒ©ã‚¤ãƒ€ãƒ¼"], + "💺":["座å¸","椅å"], + "🛰":["サテライト","衛星","宇宙","乗り物"], + "🚀":["ãƒã‚±ãƒƒãƒˆ","宇宙","乗り物"], + "🛸":["空飛ã¶å††ç›¤","UFO","宇宙人","異星人","宇宙","空想"], + "🛶":["カヌー","ボート"], + "⛵":["ヨット","ボート","リゾート","æµ·","乗り物"], + "🛥":["モーターボート","ボート","乗り物"], + "🚤":["スピードボート","ボート","乗り物"], + "â›´":["フェリー","ボート"], + "🛳":["旅客船","旅客","船","乗り物"], + "🚢":["船","乗り物"], + "🛟":["救命浮ã輪","æµ®ã輪","ライフジャケット","ライフセーãƒãƒ¼","救助","安全"], + "âš“":["ã„ã‹ã‚Š","船","ツール"], + "⛽":["ガソリンスタンド","燃料","ガソリン","給油機","サービスステーション"], + "🚧":["工事ä¸","工事用フェンス","建è¨å·¥äº‹"], + "ðŸš":["ãƒã‚¹åœ","ãƒã‚¹","åœæ¢"], + "🚦":["縦å‘ãã®ä¿¡å·æ©Ÿ","ä¿¡å·æ©Ÿ","ä¿¡å·","交通"], + "🚥":["横å‘ãã®ä¿¡å·æ©Ÿ","ä¿¡å·æ©Ÿ","ä¿¡å·","交通"], + "🛑":["一時åœæ¢æ¨™è˜","八角形","標è˜","åœæ¢"], + "🎡":["観覧車","アクティビティ","éŠåœ’地","エンターテイメント","フェリス"], + "🎢":["ジェットコースター","アクティビティ","éŠåœ’地","コースター","エンターテイメント","ãƒãƒ¼ãƒ©ãƒ¼"], + "🎠":["メリーゴーランド","アクティビティ","メリーゴーラウンド","エンターテイメント","馬"], + "ðŸ—":["建è¨ä¸","建物","建è¨"], + "ðŸŒ":["霧","天気"], + "🗼":["æ±äº¬ã‚¿ãƒ¯ãƒ¼","æ±äº¬","タワー"], + "ðŸ":["å·¥å ´","建物"], + "⛲":["å™´æ°´"], + "🎑":["ãŠæœˆè¦‹","アクティビティ","ãŠç¥ã„","授賞å¼","エンターテイメント","月"], + "â›°":["å±±"], + "ðŸ”":["雪山","寒ã„","å±±","雪"], + "🗻":["富士山","å±±"], + "🌋":["ç«å±±","å™´ç«","å±±","気象"], + "🗾":["日本列島","日本","地図"], + "ðŸ•":["ã‚ャンプ"], + "⛺":["テント","ã‚ャンプ"], + "ðŸž":["国立公園","公園"], + "🛣":["高速é“è·¯","ãƒã‚¤ã‚¦ã‚§ã‚¤","é“è·¯"], + "🛤":["ç·šè·¯","鉄é“","電車"], + "🌅":["æ—¥ã®å‡º","æœ","太陽","天候"], + "🌄":["å±±ã‹ã‚‰ã®æ—¥ã®å‡º","æœ","å±±","太陽","æ—¥ã®å‡º","天候"], + "ðŸœ":["ç ‚æ¼ "], + "ðŸ–":["ビーãƒã¨å‚˜","ビーãƒ","傘","パラソル"], + "ðŸ":["無人島","ç ‚æ¼ ","島"], + "🌇":["ビルã«æ²ˆã‚€å¤•é™½","建物","夕暮れ","太陽","夕日","天気"], + "🌆":["夕暮れã®è¡—並ã¿","建物","è¡—","夕暮れ","日暮れ","風景","太陽","夕日","天気"], + "ðŸ™":["街並ã¿","建物","è¡—"], + "🌃":["星空","夜","星","天気"], + "🌉":["夜ã®æ©‹","æ©‹","夜","天気"], + "🌌":["天ã®å·","宇宙","天気"], + "🌠":["æµã‚Œæ˜Ÿ","アクティビティ","è½ä¸‹","æµã‚Œã‚‹","宇宙","星"], + "🎇":["線香花ç«","アクティビティ","ãŠç¥ã„","エンターテイメント","花ç«","ã‚ラã‚ラ"], + "🎆":["花ç«","アクティビティ","ãŠç¥ã„","エンターテイメント"], + "🛖":["å°å±‹","家","扇形庫","パオ"], + "ðŸ˜":["家","建物"], + "ðŸ°":["西洋ã®åŸŽ","建物","城","ヨーãƒãƒƒãƒ‘"], + "ðŸ¯":["日本ã®åŸŽ","建物","城","日本"], + "ðŸŸ":["スタジアム"], + "🗽":["自由ã®å¥³ç¥ž","自由","åƒ"], + "ðŸ ":["家","建物","自宅"], + "ðŸ¡":["åºä»˜ãã®å®¶","建物","åº","自宅","家"], + "ðŸš":["廃墟","建物","廃屋","家"], + "ðŸ¢":["オフィスビル","建物"], + "ðŸ¬":["デパート","建物","店"], + "ðŸ£":["日本ã®éƒµä¾¿å±€","建物","日本","ãƒã‚¹ãƒˆ"], + "ðŸ¤":["ヨーãƒãƒƒãƒ‘ã®éƒµä¾¿å±€","建物","ヨーãƒãƒƒãƒ‘","ãƒã‚¹ãƒˆ"], + "ðŸ¥":["病院","建物","医師","è–¬"], + "ðŸ¦":["銀行","建物"], + "ðŸ¨":["ホテル","建物"], + "ðŸª":["コンビニエンスストア","建物","コンビニエンス","ストア"], + "ðŸ«":["å¦æ ¡","建物"], + "ðŸ©":["ラブホテル","建物","ホテル","ラブ"], + "💒":["çµå©šå¼","アクティビティ","ãƒãƒ£ãƒšãƒ«","ãƒãƒžãƒ³ã‚¹"], + "ðŸ›":["æ´å²çš„ãªå»ºç‰©","建物","æ´å²çš„ãª"], + "⛪":["教会","建物","クリスãƒãƒ£ãƒ³","åå—架","å®—æ•™"], + "🕌":["モスク","イスラム","ムスリム","å®—æ•™"], + "🛕":["ヒンドゥー教寺院","ヒンドゥー教","寺院","å®—æ•™"], + "ðŸ•":["シナゴーグ","ユダヤ人","ユダヤ教","å®—æ•™","ä¼šå ‚"], + "🕋":["ã‚«ã‚¢ãƒ","イスラム","ムスリム","å®—æ•™"], + "⛩":["神社","å®—æ•™","神é“"], + "⌚":["腕時計","時計"], + "📱":["æºå¸¯é›»è©±","æºå¸¯","コミュニケーション","モãƒã‚¤ãƒ«","電話"], + "📲":["ç€ä¿¡ä¸","矢å°","通話","æºå¸¯","コミュニケーション","モãƒã‚¤ãƒ«","æºå¸¯é›»è©±","å—ä¿¡","電話"], + "💻":["パソコン","ノートパソコン","コンピューター","パーソナル"], + "⌨":["ã‚ーボード","コンピューター"], + "🖥":["デスクトップパソコン","コンピューター","デスクトップ"], + "🖨":["プリンター","コンピューター"], + "🖱":["3ボタンマウス","3","ボタン","コンピューター","マウス","三"], + "🖲":["トラックボール","コンピューター"], + "🕹":["ジョイスティック","エンターテイメント","ゲーム","ビデオゲーム"], + "🗜":["圧縮","ツール","æ¬ é™¥"], + "💽":["MD","パソコン","光ディスク","エンターテイメント","ミニディスク","å…‰å¦"], + "💾":["フãƒãƒƒãƒ”ーディスク","コンピューター","ディスク","フãƒãƒƒãƒ”ー"], + "💿":["CDディスク","ブルーレイ","CD","コンピューター","ディスク","DVD","å…‰å¦"], + "📀":["DVD","ブルーレイ","CD","コンピューター","ディスク","エンターテイメント","å…‰å¦"], + "📼":["ビデオテープ","エンターテイメント","テープ","VHS","ビデオ","ビデオカセット"], + "📷":["カメラ","エンターテイメント","ビデオ"], + "📸":["フラッシュを焚ã„ãŸã‚«ãƒ¡ãƒ©","カメラ","フラッシュ","ビデオ"], + "📹":["ビデオカメラ","カメラ","エンターテイメント","ビデオ"], + "🎥":["ビデオカメラ","アクティビティ","カメラ","ã‚·ãƒãƒž","エンターテイメント","æ˜ ç”»"], + "📽":["æ˜ å†™æ©Ÿ","ã‚·ãƒãƒž","娯楽","フィルム","æ˜ ç”»","プãƒã‚¸ã‚§ã‚¯ã‚¿ãƒ¼","ビデオ"], + "🎞":["フィルムã®ãƒ•ãƒ¬ãƒ¼ãƒ ","ã‚·ãƒãƒž","エンターテイメント","フィルム","フレーム","æ˜ ç”»"], + "📞":["å—話器","コミュニケーション","電話","å—ä¿¡æ©Ÿ"], + "☎ï¸":["電話","æºå¸¯é›»è©±"], + "📟":["ãƒã‚±ãƒƒãƒˆãƒ™ãƒ«","コミュニケーション","ãƒã‚±ãƒ™ãƒ«"], + "📠":["FAX","コミュニケーション; fAX"], + "📺":["テレビ","エンターテイメント","TV","ビデオ"], + "📻":["ラジオ","エンターテイメント","ビデオ"], + "🎙":["スタジオマイク","マイク","音楽","スタジオ"], + "🎚":["調節ãƒãƒ¼","調節","音楽","ãƒãƒ¼"], + "🎛":["コントãƒãƒ¼ãƒ«ãƒŽãƒ–","コントãƒãƒ¼ãƒ«","ã¤ã¾ã¿","音楽"], + "â±":["ストップウォッãƒ","時計"], + "â²":["タイマー時計","時計","タイマー"], + "â°":["目覚ã¾ã—時計","アラーム","時計"], + "🕰":["ç½®ã時計","時計"], + "â³":["ç ‚æ™‚è¨ˆ","ç ‚","タイマー"], + "⌛":["ç ‚æ™‚è¨ˆ","ç ‚","タイマー"], + "🧮":["ãã‚ã°ã‚“","計算","カウント","集計表","æ•°å¦"], + "📡":["衛星アンテナ","アンテナ","コミュニケーション","パラボラアンテナ","衛星"], + "🔋":["é›»æ± ","ãƒãƒƒãƒ†ãƒªãƒ¼","é›»å","高エãƒãƒ«ã‚®ãƒ¼"], + "🪫":["ãƒãƒƒãƒ†ãƒªãƒ¼æ®‹é‡å°‘","ãƒãƒƒãƒ†ãƒªãƒ¼","é›»å","低エãƒãƒ«ã‚®ãƒ¼"], + "🔌":["コンセント","電気","プラグ"], + "💡":["é›»çƒ","漫画","電気","ã²ã‚‰ã‚ã","å…‰"], + "🔦":["æ‡ä¸é›»ç¯","電気","å…‰","é“å…·","ãŸã„ã¾ã¤"], + "🕯":["ã‚ã†ãã","å…‰"], + "🧯":["消ç«å™¨","消ç«","ç«","消ã™"], + "🗑":["ã”ã¿ç®±","ゴミ箱","ã”ã¿","ゴミ","缶","ビン"], + "🛢":["ドラム缶","ドラム","オイル"], + "🛒":["ショッピングカート","カート","ショッピング","トãƒãƒªãƒ¼"], + "💸":["ç¾½ã®ç”ŸãˆãŸãŠæœ","銀行","紙幣","請求書","ドル","飛ã¶","ãŠé‡‘","ç¾½"], + "💵":["ドルæœ","銀行","紙幣","ãŠæœ","通貨","ドル","ãŠé‡‘"], + "💴":["円記å·ã®å…¥ã£ãŸå°åˆ‡æ‰‹","銀行","紙幣","ãŠæœ","通貨","ãŠé‡‘","円"], + "💶":["ユーãƒæœ","銀行","紙幣","ãŠæœ","通貨","ユーãƒ","ãŠé‡‘"], + "💷":["ãƒãƒ³ãƒ‰æœ","銀行","紙幣","ãŠæœ","通貨","ãŠé‡‘","ãƒãƒ³ãƒ‰"], + "💰":["ドル袋","ãƒãƒƒã‚°","ドル","ãŠé‡‘"], + "🪙":["コイン","金","金属","ãŠé‡‘","銀","å®"], + "💳":["クレジットカード","銀行","カード","クレジット","ãŠé‡‘"], + "🪪":["身分証明書","è³‡æ ¼æƒ…å ±","ID","ライセンス","ã‚»ã‚ュリティ"], + "🧾":["é ˜åŽæ›¸","会計","簿記","è¨¼æ‹ ","証明"], + "💎":["å®çŸ³","ダイアモンド","ジュエル","ãƒãƒžãƒ³ã‚¹"], + "âš–":["ã¯ã‹ã‚Š","天秤","å…¬æ£","ã¦ã‚“ã³ã‚“座","物差ã—","é“å…·","é‡é‡","星座"], + "🦯":["白æ–","アクセシビリティ","ç›®ãŒä¸è‡ªç”±"], + "🧰":["é“å…·ç®±","胸","整備士","工具"], + "🔧":["レンãƒ","é“å…·"], + "🪛":["ドライãƒãƒ¼","ãã˜","工具"], + "🔨":["ãƒãƒ³ãƒžãƒ¼","é“å…·"], + "âš’":["ãƒãƒ³ãƒžãƒ¼ã¨ã¤ã‚‹ã¯ã—","ãƒãƒ³ãƒžãƒ¼","ã¤ã‚‹ã¯ã—","é“å…·"], + "🛠":["ãƒãƒ³ãƒžãƒ¼ã¨ãƒ¬ãƒ³ãƒ","ãƒãƒ³ãƒžãƒ¼","é“å…·","レンãƒ"], + "â›":["ã¤ã‚‹ã¯ã—","採掘","é“å…·"], + "🪓":["æ–§","ãŸãŸã切り","手斧","割る","木æ","工具"], + "🪚":["木工用ã®ã“ãŽã‚Š","大工","æ木","ã®ã“ãŽã‚Š","工具"], + "🔩":["ナットã¨ãƒœãƒ«ãƒˆ","ボルト","ナット","é“å…·"], + "âš™":["æ¯è»Š","ギア","é“å…·"], + "⛓":["鎖"], + "ðŸª":["フック","ã‚ãª","ã„ã‹ã•ã¾","ペテン","誘惑","フィッシング","ツール"], + "🪜":["ã¯ã—ã”","登る","横木","段","工具"], + "🧱":["れんãŒ","粘土","建è¨","モルタル","å£"], + "🪨":["ãƒãƒƒã‚¯","岩","å»ºé€ ç‰©","é‡ã„","固体","石"], + "🪵":["木æ","å»ºé€ ç‰©","丸太","æ木","木"], + "🔫":["æ°´é‰„ç ²","æ°´","ピストル","噴射器","銃"], + "🧨":["爆竹","ダイナマイト","ç«è–¬","花ç«"], + "💣":["爆弾"], + "🔪":["包ä¸","ã‚ッãƒãƒ³ãƒŠã‚¤ãƒ•","調ç†","ナイフ"], + "🗡":["çŸå‰£","ナイフ"], + "âš”":["交差ã—ãŸå‰£","交差","剣"], + "🛡":["盾"], + "🚬":["喫煙マーク","アクティビティ","å–«ç…™"], + "âš°":["棺","æ»"], + "🪦":["墓石","墓地","æ»","墓","å¢“å ´","ãƒãƒã‚¦ã‚£ãƒ¼ãƒ³"], + "âš±":["骨壷","æ»","葬儀"], + "ðŸº":["アンフォラ","ã¿ãšãŒã‚座","æ–™ç†","飲ã¿ç‰©","æ°´å·®ã—","é“å…·","星座"], + "🔮":["水晶玉","玉","水晶","ãŠã¨ãŽè©±","ファンタジー","å ã„","é“å…·"], + "🪄":["é”法ã®æ–","é”法","棒","é”女","é”法使ã„"], + "📿":["æ•°ç 状ã®ç¥ˆã‚Šã®ç”¨å…·","æ•°ç ","衣類","ãƒãƒƒã‚¯ãƒ¬ã‚¹","祈り","å®—æ•™"], + "🧿":["ナザールã®ãŠå®ˆã‚Š","æ•°ç 玉","ãŠå®ˆã‚Š","邪視","ナザール","è·ç¬¦"], + "🪬":["ãƒãƒ サ","ãŠå®ˆã‚Š","ファティマ","手","メアリー","ミリアム","ä¿è·"], + "💈":["ç†é«ªåº—ã®çœ‹æ¿æŸ±","ç†é«ªåº—","床屋","散髪","看æ¿æŸ±"], + "🧲":["ç£çŸ³","アトラクション","馬蹄"], + "âš—":["蒸留器","化å¦","実験","é“å…·"], + "🧪":["試験管","化å¦è€…","化å¦","実験","実験室","科å¦"], + "🧫":["ペトリ皿","ãƒã‚¯ãƒ†ãƒªã‚¢","生物å¦è€…","生物å¦","文化","実験室"], + "🧬":["DNA","生物å¦è€…","進化","éºä¼å","éºä¼åå¦","生命"], + "ðŸ”":["望é é¡","ツール"], + "🔬":["é¡•å¾®é¡","ツール"], + "🕳":["ç©´"], + "🩻":["Xç·š","骨","医師","医療","éª¨æ ¼"], + "💊":["è–¬","医師","ピル","ç—…æ°—"], + "💉":["注射器","医師","è–¬","注射é‡","注射","ç—…æ°—","é“å…·","ワクãƒãƒ³"], + "🩸":["è¡€1æ»´","医師","è–¬","血液","生ç†"], + "🩹":["ガーゼ付ãã°ã‚“ãã†ã“ã†","医師","è–¬","ãƒãƒ³ãƒ‰ã‚¨ã‚¤ãƒ‰","包帯","ã°ã‚“ãã†ã“ã†"], + "🩺":["è´è¨ºå™¨","医師","è–¬","心臓"], + "🌡":["温度計","天気","温度"], + "🩼":["æ¾è‘‰æ–","æ–","éšœç¢","怪我","移動補助","棒"], + "ðŸ·":["ラベル","è·æœ"], + "🔖":["ブックマーク","ã—ãŠã‚Š","å°"], + "🚽":["トイレ"], + "🪠":["プランジャー","フォースカップ","é…管工","å¸å¼•","トイレ"], + "🚿":["シャワー","æ°´"], + "ðŸ›":["ãƒã‚¹ã‚¿ãƒ–","風呂","浴槽"], + "🛀":["風呂","浴槽"], + "🪮":["ヘアピック","アフãƒ","ãã—","髪","ピック"], + "🪥":["æ¯ãƒ–ラシ","ãƒã‚¹ãƒ«ãƒ¼ãƒ ","ブラシ","ãã‚Œã„","æ¯åŒ»è€…","衛生","æ¯"], + "🪒":["カミソリ","é‹ã„","é«å‰ƒã‚Š"], + "🧴":["ãƒãƒ¼ã‚·ãƒ§ãƒ³ãƒœãƒˆãƒ«","ãƒãƒ¼ã‚·ãƒ§ãƒ³","ä¿æ¹¿å‰¤","シャンプー","日焼ã‘æ¢ã‚"], + "🧻":["ペーパーãƒãƒ¼ãƒ«","ペーパータオル","トイレットペーパー"], + "🧼":["ã›ã£ã‘ã‚“","棒","æ°´æµ´ã³","クリーニング","泡","ã›ã£ã‘ん入れ"], + "🫧":["ãƒãƒ–ル","ã’ã£ã·","ãã‚Œã„","ã›ã£ã‘ã‚“","æ°´ä¸"], + "🧽":["スãƒãƒ³ã‚¸","å¸åŽ","クリーニング","多å”性"], + "🧹":["ã»ã†ã","クリーニング","掃除","é”女"], + "🧺":["ãƒã‚¹ã‚±ãƒƒãƒˆ","è¾²æ¥","ランドリー","ピクニック"], + "🪣":["ãƒã‚±ãƒ„","ãŸã‚‹","手桶","大ã ã‚‹"], + "🔑":["éµ","éŒ ","パスワード"], + "ðŸ—":["å¤ã„éµ","ã‹ãŽ","éµ","éŒ ","å¤ã„"], + "🪤":["ãƒã‚ºãƒŸæ•ã‚Šå™¨","餌","ãƒã‚ºãƒŸ","齧æ¯å‹•ç‰©","輪ãªã‚","ã‚ãª"], + "🛋":["ソファーã¨ãƒ©ãƒ³ãƒ—","ソファー","ホテル","ランプ"], + "🪑":["椅å","座å¸","座る"], + "🛌":["宿泊施è¨","å¯ã‚‹","ホテル","ç¡çœ ","ベッド"], + "ðŸ›":["ベッド","ホテル","ç¡çœ "], + "🚪":["ドア","扉"], + "🪞":["é¡","åå°„","å射体","åå°„é¡"], + "🪟":["窓","æž ","æ–°é®®ãªç©ºæ°—","ガラス","é–‹å£éƒ¨","é€æ˜Ž","視界"], + "🧳":["手è·ç‰©","パッã‚ング","æ—…è¡Œ","スーツケース"], + "🛎":["å“上ベル","ベル","ホテル"], + "🖼":["é¡ã«å…¥ã£ãŸå†™çœŸ","アート","é¡ç¸","美術館","絵画","写真"], + "ðŸ§":["コンパス","ç£çŸ³","ナビゲーション","オリエンテーリング"], + "🗺":["世界地図","地図","世界"], + "â›±":["ç«‹ã¦ã‚‰ã‚ŒãŸãƒ‘ラソル","雨","æ™´ã‚Œ","傘","天気"], + "ðŸª":["折り畳ã¿æ‰‡å","冷å´","é æ…®ãŒã¡","ダンス","ファン","フラッター","熱","熱ã„","内気","広ãŒã‚‹"], + "🗿":["モヤイåƒ","モアイåƒ","é¡”","åƒ"], + "ðŸ›":["è²·ã„物袋","éž„","ホテル","è²·ã„物"], + "🎈":["風船","アクティビティ","ãŠç¥ã„","エンターテイメント"], + "ðŸŽ":["ã“ã„ã®ã¼ã‚Š","アクティビティ","鯉","ãŠç¥ã„","エンターテイメント","æ——","å¹æµã—"], + "🎀":["リボン","ãŠç¥ã„"], + "🧧":["赤ã„å°ç’","ギフト","幸é‹","紅包","利是","ãŠé‡‘"], + "ðŸŽ":["プレゼント","ç®±","ãŠç¥ã„","エンターテイメント","贈り物","包装"], + "🎊":["ãã™çŽ‰","アクティビティ","ãŠç¥ã„","ç´™å¹é›ª","エンターテイメント"], + "🎉":["クラッカー","アクティビティ","ãŠç¥ã„","エンターテイメント","パーティー","ジャーン"], + "🪅":["ピニャータ","ãŠç¥ã„","パーティー","ピナータ"], + "🪩":["ミラーボール","ダンス","ディスコ","è¼ã","パーティー"], + "🪆":["入れå人形","人形","入れå","ãƒã‚·ã‚¢"], + "🎎":["ã²ãªç¥ã‚Š","アクティビティ","ãŠç¥ã„","人形","エンターテイメント","ç¥ã‚Š","日本"], + "ðŸŽ":["風鈴","アクティビティ","é˜","ãŠç¥ã„","エンターテイメント","風"], + "ðŸ®":["居酒屋ã®æç¯","赤ã¡ã‚‡ã†ã¡ã‚“","居酒屋","日本","æç¯","ç¯ã‚Š","赤"], + "🪔":["ディヤランプ","ディヤ","ランプ","オイル"], + "✉ï¸":["å°ç’","Eメール","é›»åメール"], + "📩":["メールå—ä¿¡ä¸","矢å°","コミュニケーション","下","Eメール","é›»åメール","å°ç’","手紙","メール","é€ã‚‹","é€ä¿¡"], + "📨":["メールå—ä¿¡","コミュニケーション","Eメール","é›»åメール","å°ç’","å—ã‘å–ã‚‹","手紙","メール","å—ä¿¡"], + "📧":["Eメール","コミュニケーション","é›»åメール","手紙","メール"], + "💌":["ラブレター","ãƒãƒ¼ãƒˆ","手紙","æ„›","メール","ãƒãƒžãƒ³ã‚¹"], + "📮":["ãƒã‚¹ãƒˆ","コミュニケーション","メール","郵便å—ã‘"], + "📪":["æ——ãŒä¸‹ãŒã£ã¦ã„ã¦é–‰ã˜ã¦ã„る状態ã®éƒµä¾¿å—ã‘","é–‰ã˜ã‚‹","コミュニケーション","æ——","下ãŒã£ãŸ","メール","ãƒã‚¹ãƒˆ","郵便å—ã‘"], + "📫":["æ——ãŒä¸ŠãŒã£ã¦ã„ã¦é–‰ã˜ã¦ã„る状態ã®éƒµä¾¿å—ã‘","é–‰ã˜ã‚‹","コミュニケーション","æ——","メール","郵便å—ã‘","ãƒã‚¹ãƒˆ"], + "📬":["æ——ãŒä¸ŠãŒã£ã¦ã„ã¦é–‹ã„ã¦ã„る状態ã®éƒµä¾¿å—ã‘","コミュニケーション","æ——","メール","ãƒã‚¹ãƒˆ","é–‹ã‘ã‚‹","郵便å—ã‘"], + "ðŸ“":["æ——ãŒä¸‹ãŒã£ã¦ã„ã¦é–‹ã„ã¦ã„る郵便å—ã‘","コミュニケーション","æ——","下ã’","メール","メールボックス","é–‹ã‘ã‚‹","郵便å—ã‘"], + "📦":["è·ç‰©","ç®±","コミュニケーション","パッケージ","å°åŒ…"], + "📯":["郵便ラッパ","コミュニケーション","エンターテイメント","角","ãƒã‚¹ãƒˆ","郵便"], + "📥":["å—信トレイ","ç®±","コミュニケーション","手紙","メール","å—ä¿¡","トレイ"], + "📤":["é€ä¿¡ãƒˆãƒ¬ã‚¤","ç®±","コミュニケーション","手紙","メール","é€ä¿¡","トレイ"], + "📜":["巻物","ç´™"], + "📃":["原稿","カール","ドã‚ュメント","ページ"], + "📑":["ブックマークタブ","ブックマーク","マーク","マーカー","タブ"], + "📊":["棒グラフ","ãƒãƒ¼","ãƒãƒ£ãƒ¼ãƒˆ","グラフ"], + "📈":["上昇ã™ã‚‹ã‚°ãƒ©ãƒ•","上昇ãƒãƒ£ãƒ¼ãƒˆ","ãƒãƒ£ãƒ¼ãƒˆ","グラフ","æˆé•·","トレンド","上å‘ã"], + "📉":["下é™ã™ã‚‹ã‚°ãƒ©ãƒ•","下é™ãƒãƒ£ãƒ¼ãƒˆ","ãƒãƒ£ãƒ¼ãƒˆ","下","グラフ","トレンド"], + "📄":["文書","ページ"], + "📅":["カレンダー","日付"], + "📆":["æ—¥ã‚ãりカレンダー","カレンダー"], + "🗓":["リングカレンダー","カレンダー","パッド","らã›ã‚“状"], + "📇":["å刺フォルダ","カード","索引","ãƒãƒ¼ãƒ©ãƒ‡ãƒƒã‚¯ã‚¹"], + "🗃":["カードファイル","ç®±","カード","ファイル"], + "🗳":["投票用紙ã¨æŠ•ç¥¨ç®±","投票用紙","ç®±","票","投票"], + "🗄":["ファイルåŽç´åº«","åŽç´","ファイル"], + "📋":["クリップボード"], + "🗒":["リングノート","ノート","パッド","らã›ã‚“状"], + "ðŸ“":["フォルダ","ファイル"], + "📂":["é–‹ã„ãŸãƒ•ã‚©ãƒ«ãƒ€","ファイル","フォルダ","é–‹ã„ãŸ"], + "🗂":["仕切りカード","カード","仕切り","索引"], + "🗞":["丸ã‚ãŸæ–°èž","ニュース","æ–°èž","ç´™","丸ã‚ãŸ"], + "📰":["æ–°èž","コミュニケーション","ニュース","ç´™"], + "🪧":["プラカード","デモ","柵","抗è°","看æ¿"], + "📓":["ノート"], + "📕":["é–‰ã˜ãŸæœ¬","本","é–‰ã˜ã¦ã„ã‚‹"], + "📗":["緑色ã®æœ¬","本","ç·‘"], + "📘":["é’ã„本","é’","本"], + "📙":["オレンジ色ã®æœ¬","本","オレンジ"], + "📔":["装飾カãƒãƒ¼ã®ãƒŽãƒ¼ãƒˆ","本","ã‚«ãƒãƒ¼","装飾","ノート"], + "📒":["帳簿","元帳","ノート"], + "📚":["書ç±","本"], + "📖":["é–‹ã„ãŸæœ¬","本","é–‹ã„ãŸ"], + "🔗":["リンク"], + "📎":["クリップ","ペーパークリップ"], + "🖇":["繋ãŒã£ãŸãƒšãƒ¼ãƒ‘ークリップ","コミュニケーション","リンク","ペーパークリップ"], + "✂ï¸":["ãƒã‚µãƒŸ","ã¯ã•ã¿","é“å…·"], + "ðŸ“":["三角定è¦","定è¦","é…ç½®","三角"], + "ðŸ“":["定è¦","直定è¦"], + "📌":["画鋲","ピン"], + "ðŸ“":["画鋲","ピン"], + "🧷":["安全ピン","ãŠã‚€ã¤","パンクãƒãƒƒã‚¯"], + "🪡":["縫ã„é‡","刺ã—ã‚…ã†","è£ç¸«","縫ã„ç›®","縫åˆ","仕立ã¦"], + "🧵":["スレッド","縫ã„ç·¨ã¿","è£ç¸«","糸巻","糸","手工芸"], + "🧶":["糸","ボール","ã‹ãŽé‡ç·¨ã¿","ニット","手工芸"], + "🪢":["çµã³ç›®","ãƒãƒ¼ãƒ—","絡んã ","ã²ã‚‚","より糸","ãã˜ã‚Œ"], + "ðŸ”":["コインãƒãƒƒã‚«ãƒ¼","é–‰ã¾ã£ã¦ã„ã‚‹","éµ","æ–½éŒ ","防犯"], + "🔒":["éµ","é–‰ã˜ã‚‰ã‚ŒãŸ","æ–½éŒ "], + "🔓":["è§£éŒ ","æ–½éŒ ","é–‹ã‘ã‚‹"], + "ðŸ”":["éŒ å‰ã¨ãƒšãƒ³","インク","éŒ ","ペン先","ペン","プライãƒã‚·ãƒ¼"], + "🖊":["左下å‘ãã®ãƒœãƒ¼ãƒ«ãƒšãƒ³","ボールペン","コミュニケーション","ペン"], + "🖋":["左下å‘ãã®ä¸‡å¹´ç†","コミュニケーション","万年ç†","ペン"], + "✒ï¸":["ペン先","ペン"], + "ðŸ“":["メモ","コミュニケーション","鉛ç†"], + "âœï¸":["鉛ç†"], + "ðŸ–":["左下å‘ãã®ã‚¯ãƒ¬ãƒ¨ãƒ³","コミュニケーション","クレヨン"], + "🖌":["左下å‘ãã®ãƒ–ラシ","コミュニケーション","ペイントブラシ","çµµ"], + "ðŸ”":["å·¦å‘ã虫眼é¡","眼é¡","拡大","検索","ツール"], + "🔎":["å³å‘ã虫眼é¡","眼é¡","拡大","検索","ツール"], + "â¤ï¸":["赤色ã®ãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ"], + "🧡":["オレンジ色ã®ãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","オレンジ色"], + "💛":["黄色ã®ãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","黄色"], + "💚":["ç·‘ã®ãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","ç·‘"], + "💙":["é’ã®ãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","é’"], + "💜":["ç´«ã®ãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","ç´«"], + "🤎":["茶色ã®ãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","茶色"], + "🖤":["é»’ã„ãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","é»’","悪","悪者"], + "ðŸ¤":["白ã®ãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","白"], + "💔":["割れãŸãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","壊れる","ç ´å±€"], + "â£":["ãƒãƒ¼ãƒˆã®ãƒ“ックリマーク","ãƒãƒ¼ãƒˆ","ビックリマーク","記å·"], + "💕":["2ã¤ã®ãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","æ„›"], + "💞":["回転ã™ã‚‹ãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","回転"], + "💓":["鼓動ã™ã‚‹ãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","鼓動","ドã‚ドã‚"], + "💗":["光るãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","ワクワク","光る","鼓動","ç·Šå¼µ"], + "💖":["ãらã‚ããƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","ワクワク","ã‚ラã‚ラ"], + "💘":["射抜ã‹ã‚ŒãŸãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","矢","ã‚ューピッド","ãƒãƒžãƒ³ã‚¹"], + "ðŸ’":["リボン付ãã®ãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","リボン","ãƒãƒ¬ãƒ³ã‚¿ã‚¤ãƒ³"], + "â¤ï¸â€ðŸ”¥":["燃ãˆã¦ã„ã‚‹ãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","ç«","燃ãˆã‚‹","æ„›","熱情","神è–ãªãƒãƒ¼ãƒˆ"], + "â¤ï¸â€ðŸ©¹":["手当ã—ã¦ã„ã‚‹ãƒãƒ¼ãƒˆ","ãƒãƒ¼ãƒˆ","å¥åº·ã«ãªã‚‹","改善ã—ã¦ã„ã‚‹","手当ã—ã¦ã„ã‚‹","回復ã—ã¦ã„ã‚‹","ç—…ã¿ä¸ŠãŒã‚Š","元気"], + "💟":["ãƒãƒ¼ãƒˆã®ãƒ‡ã‚³ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³","ãƒãƒ¼ãƒˆ"], + "☮":["ピースマーク","平和"], + "âœ":["ラテンåå—","クリスãƒãƒ£ãƒ³","åå—架","å®—æ•™"], + "☪":["星ã¨ä¸‰æ—¥æœˆ","イスラム","ムスリム","å®—æ•™"], + "🕉":["オームマーク","ヒンドゥー教","オーム","å®—æ•™"], + "☸":["法輪","ä»æ•™å¾’","ダーマ","å®—æ•™"], + "✡":["ダビデã®æ˜Ÿ","ダビデ","ユダヤ人","ユダヤ教","å®—æ•™","星"], + "🔯":["å…芒星","å ã„","星"], + "🕎":["ãƒãƒŒãƒƒã‚ーヤー","ç‡å°","メノーラー","å®—æ•™"], + "☯":["陰陽","å®—æ•™","é“","é“家","陽","é™°"], + "☦":["八端åå—架","クリスãƒãƒ£ãƒ³","åå—架","å®—æ•™"], + "🪯":["カンダ","å®—æ•™","シーク教徒"], + "ðŸ›":["礼æ‹æ‰€","å®—æ•™","礼æ‹"], + "⛎":["ã¸ã³ã¤ã‹ã„座","é‹æ¬äºº","蛇","ヘビ","星座"], + "♈":["ãŠã²ã¤ã˜åº§","仔羊","星座"], + "♉":["ãŠã†ã—座","牡牛","雄牛","星座"], + "♊":["ãµãŸã”座","ãµãŸã”","星座"], + "♋":["ガン","ã‹ã«åº§","カニ","蟹","星座"], + "♌":["ã—ã—座","ライオン","星座"], + "â™":["ãŠã¨ã‚座","乙女","処女","星座"], + "♎":["ã¦ã‚“ã³ã‚“座","天秤","å…¬æ£","ã¯ã‹ã‚Š","星座"], + "â™":["ã•ãり座","ã•ãã‚Š","サソリ","星座"], + "â™":["ã„ã¦åº§","射手","射手座","星座"], + "♑":["ã‚„ãŽåº§","ヤギ","星座"], + "â™’":["ã¿ãšãŒã‚座","é‹æ¬äºº","æ°´","星座"], + "♓":["ã†ãŠåº§","éš","星座"], + "🆔":["四角囲ã¿ID","ID","è˜åˆ¥"], + "âš›":["å…ƒç´ è¨˜å·","無神論者","原å"], + "âš•ï¸":["アスクレピオスã®æ–","å¥åº·","世話","医師","è–¬","æ–","ヘビ"], + "☢":["放射能標è˜","放射能"], + "☣":["ãƒã‚¤ã‚ªãƒã‚¶ãƒ¼ãƒ‰æ¨™è˜","生物ç½å®³"], + "📴":["æºå¸¯é›»è©±é›»æºã‚ªãƒ•","æºå¸¯","コミュニケーション","モãƒã‚¤ãƒ«","オフ","æºå¸¯é›»è©±","電話"], + "📳":["マナーモード","æºå¸¯","コミュニケーション","モãƒã‚¤ãƒ«","モード","æºå¸¯é›»è©±","電話","ãƒã‚¤ãƒ–レーション"], + "🈶":["四角囲ã¿æœ‰","日本語","ã‚ã‚Š"], + "🈚":["四角囲ã¿ç„¡","四角囲ã¿å¦","日本語","ãªã—"], + "🈸":["四角囲ã¿ç”³","四角囲ã¿é©","ä¸å›½èªž","申請"], + "🈺":["四角囲ã¿å–¶","ä¸å›½èªž","å–¶æ¥"], + "🈷ï¸":["四角囲ã¿æœˆ","日本語","月極"], + "✴ï¸":["八稜星","星"], + "🆚":["四角囲ã¿VS","対","VS"], + "🉑":["丸囲ã¿è¨±å¯","丸囲ã¿å¯","ä¸å›½èªž","å¯èƒ½"], + "💮":["白ã„花","花","ãŸã„ã¸ã‚“よãã§ãã¾ã—ãŸ"], + "ðŸ‰":["丸囲ã¿å¾—","日本語","å¾—"], + "㊙ï¸":["丸囲ã¿ç§˜","ä¸å›½èªž","表æ„æ–‡å—","秘"], + "㊗ï¸":["丸囲ã¿ç¥","ä¸å›½èªž","ãŠã‚ã§ã¨ã†","ã—ã‚…ã"], + "🈴":["四角囲ã¿ã®åˆ","四角囲ã¿åˆ","ä¸å›½èªž","åˆæ ¼","é©åˆ"], + "🈵":["四角囲ã¿æº€","ä¸å›½èªž","満室","満車","満タン"], + "🈹":["四角囲ã¿å‰²","四角囲ã¿ã®å‰²","日本語","割引"], + "🈲":["四角囲ã¿ç¦","日本語","ç¦æ¢"], + "🅰ï¸":["黒四角囲ã¿A","A","血液型"], + "🅱ï¸":["黒四角囲ã¿B","B","血液型"], + "🆎":["黒四角囲ã¿AB","AB","血液型"], + "🆑":["四角囲ã¿CL","CL"], + "🅾ï¸":["黒四角囲ã¿O","血液型","O"], + "🆘":["四角囲ã¿SOS","ヘルプ","SOS"], + "â›”":["ç«‹å…¥ç¦æ¢","ç«‹ã¡å…¥ã‚Š","ç¦æ¢","ã ã‚","ã§ããªã„","ç¦ã˜ã‚‹","交通"], + "📛":["åæœ","ãƒãƒƒã‚¸","åå‰"], + "🚫":["進入ç¦æ¢","ç«‹ã¡å…¥ã‚Š","ç¦æ¢","ã ã‚","ã§ããªã„","ç¦ã˜ã‚‹"], + "âŒ":["ãƒãƒ„å°","ã‚ャンセル","記å·","掛ã‘ç®—","ä¹—ç®—","x"], + "â•":["太ã„大ããªä¸¸","丸","O"], + "💢":["怒りマーク","怒り","漫画","激怒"], + "♨ï¸":["温泉","温ã‹ã„","湧ã出る","蒸気"], + "🚷":["æ©è¡Œè€…ç«‹å…¥ç¦æ¢","ç¦æ¢","ã ã‚","ãªã„","æ©è¡Œè€…","ç¦ã˜ã‚‹"], + "🚯":["ãƒã‚¤æ¨ã¦ç¦æ¢","ç¦æ¢","ã”ã¿","ã ã‚","ãªã„","ç¦æ¢ã•ã‚Œã¦ã„ã‚‹"], + "🚳":["自転車ç¦æ¢","自転車","ãƒã‚¤ã‚¯","ç¦æ¢","ã ã‚","ã§ããªã„","ç¦ã˜ã‚‹","乗り物"], + "🚱":["飲用ä¸å¯","éžé£²æ–™æ°´","飲料","ç¦æ¢","ã ã‚","ãªã„","飲用","ç¦æ¢ã•ã‚Œã¦ã„ã‚‹","æ°´"], + "🔞":["18æ³æœªæº€ç¦æ¢","18","年齢制é™","åå…«","ç¦æ¢","ã ã‚","ãªã„","ç¦æ¢ã—ãŸ","未æˆå¹´è€…"], + "📵":["æºå¸¯é›»è©±ç¦æ¢","æºå¸¯","通信","ç¦æ¢","モãƒã‚¤ãƒ«","ã ã‚","ã§ããªã„","æºå¸¯é›»è©±","ç¦æ¢ã•ã‚Œã¦ã„ã‚‹","電話"], + "ðŸš":["ç¦ç…™","ç¦æ¢","ã ã‚","ã§ããªã„","ç¦æ¢ã•ã‚Œã¦ã„ã‚‹","å–«ç…™"], + "â—":["赤ã„ビックリマーク","ビックリ","マーク","記å·"], + "â•":["白ã„ビックリマーク","ビックリ","マーク","囲ã¿","記å·"], + "â“":["赤ã„ã¯ã¦ãªãƒžãƒ¼ã‚¯","マーク","記å·","ã¯ã¦ãª"], + "â”":["白ã„ã¯ã¦ãªãƒžãƒ¼ã‚¯","マーク","囲ã¿","記å·","ã¯ã¦ãª"], + "‼ï¸":["!!マーク","ãƒãƒ³ãƒãƒ³","ビックリ","マーク","記å·"], + "â‰ï¸":["!?","ビックリ","インテãƒãƒãƒ³ã‚°","マーク","記å·","ã¯ã¦ãª"], + "💯":["100点","100","フル","百","スコア"], + "🔅":["低è¼åº¦","明るã•","è–„æš—ã„","低"], + "🔆":["高è¼åº¦","明るã„","明るã•"], + "🔱":["トライデント","ã„ã‹ã‚Š","エンブレム","船","工具"], + "âšœ":["ユリã®ç´‹ç« "], + "〽ï¸":["庵点","å°","部分"], + "âš ï¸":["è¦å‘Š"], + "🚸":["交差点を渡るåä¾›ãŸã¡","åä¾›","交差点","æ©è¡Œè€…","交通"], + "🔰":["åˆå¿ƒè€…マーク","åˆå¿ƒè€…","マーク","ç·‘","日本","若葉","é“å…·","黄"], + "â™»ï¸":["リサイクルマーク","リサイクル"], + "🈯":["四角囲ã¿æŒ‡","日本語"], + "💹":["上昇トレンドã®ãƒãƒ£ãƒ¼ãƒˆã¨å††è¨˜å·","上昇ä¸å††ãƒãƒ£ãƒ¼ãƒˆ","銀行","ãƒãƒ£ãƒ¼ãƒˆ","通貨","グラフ","æˆé•·","å¸‚å ´","ãŠé‡‘","上昇","トレンド","上å‘ã","円"], + "â‡ï¸":["ã‚ラã‚ラ"], + "✳ï¸":["アスタリスク (8本構æˆ)","アスタリスク"], + "âŽ":["四角ã§å›²ã¾ã‚ŒãŸãƒãƒ„å°","マーク","四角"], + "✅":["白ã„太å—ã®ãƒã‚§ãƒƒã‚¯ãƒžãƒ¼ã‚¯","ãƒã‚§ãƒƒã‚¯","マーク"], + "💠":["ドット模様ã®ãƒ€ã‚¤ãƒ¤","漫画","ダイヤモンド","幾何å¦","内部"], + "🌀":["サイクãƒãƒ³","低気圧","ã‚ã¾ã„","竜巻","å°é¢¨","天気"], + "âž¿":["二é‡ã®ã‚«ãƒ¼ãƒ«çŠ¶ã®ãƒ«ãƒ¼ãƒ—","カール","ダブル","ループ"], + "ðŸŒ":["ååˆç·šãƒ»çµŒç·šã®ã‚る地çƒ","地çƒ","地çƒå„€","経線","世界"], + "♾":["ç„¡é™","æ°¸é ","æ™®éçš„"], + "â“‚ï¸":["丸囲ã¿M","円","M"], + "ðŸ§":["ATM","ATM記å·","自動","銀行","出ç´"], + "🚾":["トイレ","化粧室","ãŠæ‰‹æ´—ã„","æ°´","WC"], + "♿":["車ã„ã™","アクセス","車椅å"], + "🅿ï¸":["黒四角囲ã¿P","é§è»Šå ´"], + "🈳":["四角囲ã¿ç©º","四角囲ã¿ã®ç©º","ä¸å›½èªž","空室","空ã","空車"], + "🈂ï¸":["四角囲ã¿ã‚µ","日本人","サービス"], + "🛂":["入国審査","パスãƒãƒ¼ãƒˆ"], + "🛃":["税関"], + "🛄":["手è·ç‰©å—å–所","手è·ç‰©","å—ã‘å–ã‚Š"], + "🛅":["手è·ç‰©é ã‹ã‚Šæ‰€","手è·ç‰©","ãƒãƒƒã‚«ãƒ¼","æºè¡Œå“"], + "🚰":["飲料水","飲ã¿ç‰©","æ°´"], + "🛗":["エレベーター","アクセシビリティ","引ã上ã’","昇é™æ©Ÿ"], + "🚹":["男性ã®è¨˜å·","男性用","トイレ","ç”·","ãŠã¨ã“","男性"], + "♂ï¸":["男性記å·","男性","ç”·","ãŠã¨ã“"], + "🚺":["女性ã®è¨˜å·","女性用","トイレ","女","ãŠã‚“ãª","女性"], + "♀ï¸":["女性記å·","女性","女","ãŠã‚“ãª"], + "⚧ï¸":["トランスジェンダーサイン","トランスジェンダー","プライド","lgbt"], + "🚼":["赤ã¡ã‚ƒã‚“マーク","赤ã¡ã‚ƒã‚“","ãŠã‚€ã¤æ›¿ãˆ"], + "🚻":["トイレ","化粧室","WC"], + "🚮":["ゴミæ¨ã¦å ´","ビンã®ã‚´ãƒŸæ¨ã¦å ´","ゴミ","ゴミ箱"], + "🎦":["æ˜ ç”»","アクティビティ","カメラ","エンターテイメント","フィルム","å‹•ç”»"], + "📶":["アンテナ","ãƒãƒ¼","æºå¸¯","コミュニケーション","モãƒã‚¤ãƒ«","æºå¸¯é›»è©±","シグナル","電話"], + "🛜":["ç„¡ç·š","コンピュータ","インターãƒãƒƒãƒˆ","ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯","Wi-Fi","接続"], + "ðŸˆ":["四角囲ã¿ã‚³ã‚³","日本人"], + "🆖":["四角囲ã¿NG","NG"], + "🆗":["四角囲ã¿OK","OK"], + "🆙":["四角囲ã¿UP!","マーク","上"], + "🆒":["COOL","ã‹ã£ã“ã„ã„","クール"], + "🆕":["四角囲ã¿new","æ–°"], + "🆓":["四角囲ã¿FREE","フリー","ç„¡æ–™"], + "0ï¸âƒ£":["0ã‚ー","0","ã‚ー","ゼãƒ"], + "1ï¸âƒ£":["1ã‚ー","1","ã‚ー","一"], + "2ï¸âƒ£":["2ã‚ー","2","ã‚ー","ニ"], + "3ï¸âƒ£":["3ã‚ー","3","ã‚ー","三"], + "4ï¸âƒ£":["4ã‚ー","4","å››","ã‚ー"], + "5ï¸âƒ£":["5ã‚ー","5","五","ã‚ー"], + "6ï¸âƒ£":["6ã‚ー","6","ã‚ー","å…"], + "7ï¸âƒ£":["7ã‚ー","7","ã‚ー","七"], + "8ï¸âƒ£":["8ã‚ー","8","å…«","ã‚ー"], + "9ï¸âƒ£":["9ã‚ー","9","ã‚ー","ä¹"], + "🔟":["10ã‚ー","10","ã‚ー","å"], + "🔢":["番å·ã®å…¥åŠ›è¨˜å·","1234","入力","æ•°å—"], + "â–¶ï¸":["å³å‘ã三角","å†ç”Ÿãƒœã‚¿ãƒ³","矢å°","å†ç”Ÿ","å³","三角形"], + "â¸":["2本ã®åž‚ç›´ãƒãƒ¼","一時åœæ¢ãƒœã‚¿ãƒ³","ãƒãƒ¼","2å€","一時åœæ¢","åž‚ç›´"], + "â¯":["å³å‘ãã®ä¸‰è§’å½¢ã¨äºŒé‡åž‚直棒","å†ç”Ÿã¾ãŸã¯ä¸€æ™‚åœæ¢ãƒœã‚¿ãƒ³","矢å°","一時åœæ¢","å†ç”Ÿ","å³","三角形"], + "â¹":["åœæ¢","åœæ¢ãƒœã‚¿ãƒ³","四角"], + "âº":["録画","録画ボタン","丸"], + "âï¸":["å–り出ã—マーク","å–り出ã—ボタン"], + "â":["å³å‘ãã®äºŒé‡ä¸‰è§’å½¢ã¨åž‚直棒","「次ã®æ›²ã€ãƒœã‚¿ãƒ³","矢å°","次ã®å ´é¢","次ã®æ›²","三角形"], + "â®":["å·¦å‘ãã®äºŒé‡ä¸‰è§’å½¢ã¨åž‚直棒","「å‰ã®æ›²ã€ãƒœã‚¿ãƒ³","矢å°","å‰ã®å ´é¢","å‰ã®æ›²","三角形"], + "â©":["å³å‘ãã®äºŒé‡ä¸‰è§’å½¢","æ—©é€ã‚Šãƒœã‚¿ãƒ³","矢å°","2å€","高速","進む"], + "âª":["å·¦å‘ãã®äºŒé‡ä¸‰è§’å½¢","早戻ã—ボタン","矢å°","2å€","å·»ã戻ã—"], + "🔀":["ãã˜ã‚Šå³å‘ã矢å°ã®çµµæ–‡å—","シャッフル","矢å°","交差"], + "ðŸ”":["リピート","リピートボタン","矢å°","時計回り"], + "🔂":["1曲をリピートå†ç”Ÿ","リピートボタン","矢å°","時計回り","一度"], + "â—€ï¸":["å·¦å‘ãã®ä¸‰è§’å½¢","å転ボタン","矢å°","å·¦","å転","三角形"], + "🔼":["上å‘ãã®ä¸‰è§’å½¢","上ボタン","矢å°","ボタン","上"], + "🔽":["下å‘ãã®ä¸‰è§’å½¢","下ボタン","矢å°","ボタン","下"], + "â«":["上å‘ãã®äºŒé‡ä¸‰è§’å½¢","高速上昇ボタン","矢å°","ダブル","上"], + "â¬":["下å‘ãã®äºŒé‡ä¸‰è§’å½¢","高速ダウンボタン","矢å°","ダブル","下"], + "âž¡ï¸":["å³å‘ã矢å°","å³çŸ¢å°","矢å°","主è¦","æ–¹å‘","æ±"], + "⬅ï¸":["å·¦å‘ã矢å°","左矢å°","矢å°","主è¦","æ–¹å‘","西"], + "⬆ï¸":["上å‘ã矢å°","上矢å°","矢å°","主è¦","æ–¹å‘","北"], + "⬇ï¸":["下å‘ã矢å°","下矢å°","矢å°","主è¦","æ–¹å‘","下","å—"], + "↗ï¸":["å³ä¸ŠçŸ¢å°","矢å°","æ–¹å‘","æ–œã‚","北æ±"], + "↘ï¸":["å³ä¸‹çŸ¢å°","矢å°","æ–¹å‘","æ–œã‚","å—æ±"], + "↙ï¸":["左下矢å°","矢å°","æ–¹å‘","æ–œã‚","å—西"], + "↖ï¸":["左上矢å°","矢å°","æ–¹å‘","æ–œã‚","北西"], + "↕ï¸":["上下矢å°","矢å°","æ–¹å‘","æ–œã‚","北西"], + "↔ï¸":["å·¦å³çŸ¢å°","矢å°"], + "🔄":["ã†ãšã¾ã矢å°","å時計回り","矢å°","左回り"], + "↪ï¸":["å³å‘ã段付ã矢å°","å³ã«æ›²ãŒã£ãŸçŸ¢å°","矢å°"], + "↩ï¸":["å·¦å‘ã段付ã矢å°","å·¦ã«æ›²ãŒã£ãŸçŸ¢å°","矢å°"], + "🔃":["ループ矢å°","時計ã®é‡","矢å°","時計回り","リãƒãƒ¼ãƒ‰"], + "⤴ï¸":["å³ä¸Šã¸ã‚«ãƒ¼ãƒ–ã™ã‚‹çŸ¢å°","上ã¸ã‚«ãƒ¼ãƒ–ã™ã‚‹å³çŸ¢å°","矢å°"], + "⤵ï¸":["å³ä¸‹ã¸ã‚«ãƒ¼ãƒ–ã™ã‚‹çŸ¢å°","下ã«ã‚«ãƒ¼ãƒ–ã™ã‚‹å³çŸ¢å°","矢å°","下"], + "#ï¸âƒ£":["#ã‚ー","ãƒãƒƒã‚·ãƒ¥","ã‚ー","ãƒãƒ³ãƒ‰"], + "*⃣":["アスタリスクã‚ー","アスタリスク","ã‚ー","星"], + "ℹï¸":["æƒ…å ±æº","i","インフォメーション"], + "🔤":["アルファベット入力","abc","アルファベット","入力","ラテン","æ–‡å—"], + "🔡":["アルファベットå°æ–‡å—入力","abcd","入力","ラテン","æ–‡å—","å°æ–‡å—"], + "🔠":["アルファベット大文å—入力","入力","ラテン","æ–‡å—","大文å—"], + "🔣":["記å·å…¥åŠ›","入力"], + "🎵":["音符","アクティビティ","エンターテイメント","音楽"], + "🎶":["複数ã®éŸ³ç¬¦","アクティビティ","エンターテイメント","音楽","音符"], + "〰ï¸":["波線","ダッシュ","記å·","æ³¢"], + "âž°":["カール状ã®ãƒ«ãƒ¼ãƒ—","カール","ループ"], + "✔ï¸":["太å—ã®ãƒã‚§ãƒƒã‚¯ãƒžãƒ¼ã‚¯","ãƒã‚§ãƒƒã‚¯","マーク"], + "âž•":["太å—ã®+記å·","æ•°å¦","プラス"], + "âž–":["太å—ã®ãƒžã‚¤ãƒŠã‚¹è¨˜å·","æ•°å¦","マイナス"], + "âž—":["太å—ã®Ã·è¨˜å·","割り算","æ•°å¦"], + "✖ï¸":["太å—ã®Ã—å°","ã‚ャンセル","ä¹—ç®—","ã‹ã‘ã‚‹","x"], + "🟰":["太ã„ç‰å·","ç‰å¼","æ•°å¦","ç‰ã—ã„"], + "💲":["太å—ã®ãƒ‰ãƒ«è¨˜å·","通貨","ドル","ãŠé‡‘"], + "💱":["外貨両替","銀行","通貨","両替","ãŠé‡‘"], + "©ï¸":["コピーライトマーク","著作権"], + "®ï¸":["登録商標マーク","登録済ã¿","商標"], + "â„¢ï¸":["商標マーク","マーク","tm","商標"], + "🔚":["ENDã¨å·¦çŸ¢å°","矢å°","端"], + "🔙":["BACKã¨å·¦çŸ¢å°","矢å°","戻る"], + "🔛":["ON!ã¨å·¦å³çŸ¢å°","矢å°","マーク","オン"], + "ðŸ”":["TOPã¨ä¸ŠçŸ¢å°","矢å°","トップ","上"], + "🔜":["SOONã¨å³çŸ¢å°","矢å°","ã¾ã‚‚ãªã"], + "☑ï¸":["ãƒã‚§ãƒƒã‚¯å…¥ã‚Šãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹","投票","ボックス","ãƒã‚§ãƒƒã‚¯"], + "🔘":["ラジオボタン","ボタン","幾何å¦","ラジオ"], + "🔴":["赤丸","円","幾何å¦","赤"], + "🟠":["オレンジ色ã®å††","円","幾何å¦","オレンジ"], + "🟡":["黄色ã®ä¸¸","円","幾何å¦","茶色"], + "🟢":["緑丸","円","幾何å¦","ç·‘"], + "🔵":["é’丸","é’","円","幾何å¦"], + "🟣":["ç´«ã®ä¸¸","円","幾何å¦","ç´«"], + "🟤":["茶色ã®ä¸¸","円","幾何å¦","茶色"], + "âš«":["黒丸","円","幾何å¦"], + "⚪":["白丸","円","幾何å¦"], + "🟥":["赤ã®æ£æ–¹å½¢","æ£æ–¹å½¢","幾何å¦","赤"], + "🟧":["オレンジ色ã®æ£æ–¹å½¢","æ£æ–¹å½¢","幾何å¦","オレンジ"], + "🟨":["黄色ã®æ£æ–¹å½¢","æ£æ–¹å½¢","幾何å¦","黄色"], + "🟩":["ç·‘ã®æ£æ–¹å½¢","æ£æ–¹å½¢","幾何å¦","ç·‘"], + "🟦":["é’ã®æ£æ–¹å½¢","æ£æ–¹å½¢","幾何å¦","é’"], + "🟪":["ç´«ã®æ£æ–¹å½¢","æ£æ–¹å½¢","幾何å¦","ç´«"], + "🟫":["茶色ã®æ£æ–¹å½¢","æ£æ–¹å½¢","幾何å¦","茶色"], + "⬛":["é»’ã„大ããªå››è§’","幾何å¦","æ£æ–¹å½¢"], + "⬜":["白ã„大ããªå››è§’","幾何å¦","æ£æ–¹å½¢"], + "â—¼ï¸":["é»’ã„ä¸ãらã„ã®å››è§’","幾何å¦","æ£æ–¹å½¢"], + "â—»ï¸":["白ãã¦ä¸ãらã„ã®å››è§’","幾何å¦","æ£æ–¹å½¢"], + "â—¾":["é»’ãã¦ä¸ãらã„ã®å°ã•ã„四角","幾何å¦","æ£æ–¹å½¢"], + "â—½":["白ã„ä¸ãらã„ã®å°ã•ãªå››è§’","幾何å¦","æ£æ–¹å½¢"], + "â–ªï¸":["é»’ã„å°ã•ãªå››è§’","幾何å¦","æ£æ–¹å½¢"], + "â–«ï¸":["白ã„å°ã•ãªå››è§’","幾何å¦","æ£æ–¹å½¢"], + "🔸":["å°ã•ã„オレンジã®ãƒ€ã‚¤ãƒ¤ãƒ¢ãƒ³ãƒ‰","ダイヤモンド","幾何å¦","オレンジ"], + "🔹":["å°ã•ãã¦é’ã„ダイヤモンド","é’","ダイヤモンド","幾何å¦"], + "🔶":["大ãã„オレンジã®ãƒ€ã‚¤ãƒ¤","ダイヤモンド","幾何å¦","オレンジ"], + "🔷":["大ããã¦é’ã„ダイヤモンド","é’","ダイヤモンド","幾何å¦"], + "🔺":["上å‘ãã®èµ¤ã„三角形","上","幾何å¦","赤"], + "🔻":["下å‘ãã®ä¸‰è§’å½¢","ダウン","幾何å¦","赤"], + "🔲":["é»’ã„四角ボタン","ボタン","幾何å¦","æ£æ–¹å½¢"], + "🔳":["白ã„四角ボタン","ボタン","幾何å¦","囲ã¿","四角"], + "🔈":["スピーカー","音é‡"], + "🔉":["音é‡å°","é›»æºãŒå…¥ã£ãŸã‚¹ãƒ”ーカー","低ã„","スピーカー","音é‡","æ³¢"], + "🔊":["音é‡å¤§","大音é‡ã®ã‚¹ãƒ”ーカー","3","エンターテイメント","高ã„","音ã®å¤§ãã„","スピーカー","ボリューム"], + "🔇":["無音ã®ã‚¹ãƒ”ーカー","スピーカー","オフ","ミュート","é™éŸ³","無音","音é‡"], + "📣":["メガホン","å¿œæ´","コミュニケーション","拡声器"], + "📢":["拡声器","コミュニケーション","大声","スピーカー","パブリックアドレス","メガホン"], + "🔔":["ベル"], + "🔕":["ミュート","スラッシュベル","é˜","ç¦ã˜ã‚‰ã‚ŒãŸ","ã ã‚","ãªã„","ç¦æ¢","é™ã‹"], + "ðŸƒ":["トランプã®ã‚¸ãƒ§ãƒ¼ã‚«ãƒ¼","カード","エンターテイメント","ゲーム","ジョーカー","プレイ"], + "🀄":["麻雀牌ã®ä¸","ゲーム","麻雀","赤"], + "â™ ï¸":["トランプã®ã‚¹ãƒšãƒ¼ãƒ‰","カード","ゲーム","スペード","スーツ"], + "♣ï¸":["トランプã®ã‚¯ãƒ©ãƒ–","カード","クラブ","ゲーム","スーツ"], + "♥ï¸":["トランプã®ãƒãƒ¼ãƒˆ","カード","ゲーム","ãƒãƒ¼ãƒˆ","スーツ"], + "♦ï¸":["トランプã®ãƒ€ã‚¤ãƒ¤","カード","ダイヤ","ダイヤモンド","ゲーム","スーツ"], + "🎴":["花æœ","アクティビティ","カード","エンターテイメント","花","ゲーム","日本","プレイ"], + "ðŸ‘â€ðŸ—¨":["å¹ã出ã—ã®ç›®","å¹ã出ã—","ç›®","スピーãƒ","証人"], + "🗨":["å·¦å‘ãã®å¹ã出ã—","セリフ","スピーãƒ"], + "ðŸ’":["考ãˆå¹ã出ã—","å¹ã出ã—","泡","漫画","考ãˆ"], + "🗯":["å³å‘ãã®æ€’ã‚Šã®å¹ã出ã—","怒り","å¹ã出ã—","泡","激怒"], + "💬":["å¹ã出ã—","泡","漫画","セリフ","スピーãƒ"], + "ðŸ•":["1時","0分","1","時計","時","一"], + "🕑":["2時","0分","2","時計","時","二"], + "🕒":["3時","0分","3","時計","時","三"], + "🕓":["4時","0分","4","時計","å››","時"], + "🕔":["5時","0分","5","時計","五","時"], + "🕕":["6時","0分","6","時計","時","å…"], + "🕖":["7時","0分","7","時計","時","七"], + "🕗":["8時","0分","8","時計","å…«","時"], + "🕘":["9時","0分","9","時計","ä¹","時"], + "🕙":["10時","0分","10","時計","時","å"], + "🕚":["11時","0分","11","時計","å一","時"], + "🕛":["12時","0分","12","時計","å二","時"], + "🕜":["1時åŠ","1時","åŠ","時刻","一","30"], + "ðŸ•":["2時åŠ","2時","åŠ","時刻","30","二"], + "🕞":["3時åŠ","3時","åŠ","時刻","30","三"], + "🕟":["4時åŠ","30","4時","時刻","å››","åŠ"], + "🕠":["5時åŠ","30","5時","時刻","五","åŠ"], + "🕡":["6時åŠ","30","6時","時刻","å…","åŠ"], + "🕢":["7時åŠ","30","7時","時刻","七","åŠ"], + "🕣":["8時åŠ","30","8時","時刻","å…«","åŠ"], + "🕤":["9時åŠ","30","9時","時刻","ä¹","åŠ"], + "🕥":["10時åŠ","10時","åŠ","時刻","å","30"], + "🕦":["11時åŠ","11時","åŠ","時刻","å一","30"], + "🕧":["12時åŠ","12時","åŠ","時刻","30","å二"], + "ðŸ³":["ãªã³ã白旗","æ——","ãªã³ã"], + "ðŸ´":["ãªã³ãé»’æ——","æ——","ãªã³ã"], + "ðŸ":["ãƒã‚§ãƒƒã‚«ãƒ¼ãƒ•ãƒ©ãƒƒã‚°","市æ¾æ¨¡æ§˜","æ——","レース"], + "🚩":["三角旗","æ——","ãƒã‚¹ãƒˆ"], + "🎌":["交差旗","アクティビティ","ãŠç¥ã„","交差","交差ã—ãŸ","æ——","日本"], + "ðŸ´â€â˜ ï¸":["海賊旗","æ——","海賊"], + "ðŸ³ï¸â€ðŸŒˆ":["レインボーフラッグ","フラッグ","レインボー","プライド","lgbt"], + "ðŸ³ï¸â€âš§ï¸":["トラスジェンダーフラッグ","フラッグ","トランスジェンダー","プライド","lgbt"], + "🇦🇨":["アセンション島ã®æ——","アセンション","国旗","島"], + "🇦🇩":["アンドラ国旗","アンドラ","国旗"], + "🇦🇪":["アラブ首長国連邦国旗","首長国","国旗","アラブ首長国連邦","連邦"], + "🇦🇫":["アフガニスタン国旗","アフガニスタン","国旗"], + "🇦🇬":["アンティグア・ãƒãƒ¼ãƒ–ーダ国旗","アンティグア","ãƒãƒ¼ãƒ–ーダ","国旗"], + "🇦🇮":["アンギラ島ã®æ——","アンギラ島","国旗"], + "🇦🇱":["アルãƒãƒ‹ã‚¢å›½æ——","アルãƒãƒ‹ã‚¢","国旗"], + "🇦🇲":["アルメニア国旗","アルメニア","国旗"], + "🇦🇴":["アンゴラ国旗","アンゴラ","国旗"], + "🇦🇶":["å—極大陸ã®æ——","å—極大陸","国旗"], + "🇦🇷":["アルゼンãƒãƒ³å›½æ——","アルゼンãƒãƒ³","国旗"], + "🇦🇸":["ã‚¢ãƒ¡ãƒªã‚«é ˜ã‚µãƒ¢ã‚¢ã®æ——","ã‚¢ãƒ¡ãƒªã‚«é ˜","国旗","サモア"], + "🇦🇹":["オーストリア国旗","オーストリア","国旗"], + "🇦🇺":["オーストラリア国旗","オーストラリア","国旗","ãƒãƒ¼ãƒ‰","マクドナルド"], + "🇦🇼":["アルãƒå›½æ——","アルãƒ","国旗"], + "🇦🇽":["オーランド諸島ã®æ——","オーランド諸島","国旗"], + "🇦🇿":["アゼルãƒã‚¤ã‚¸ãƒ£ãƒ³å›½æ——","アゼルãƒã‚¤ã‚¸ãƒ£ãƒ³","国旗"], + "🇧🇦":["ボスニア・ヘルツェゴビナ国旗","ボスニア","国旗","ヘルツェゴビナ"], + "🇧🇧":["ãƒãƒ«ãƒãƒ‰ã‚¹å›½æ——","ãƒãƒ«ãƒãƒ‰ã‚¹","国旗"], + "🇧🇩":["ãƒãƒ³ã‚°ãƒ©ãƒ‡ã‚·ãƒ¥å›½æ——","ãƒãƒ³ã‚°ãƒ©ãƒ‡ã‚·ãƒ¥","国旗"], + "🇧🇪":["ベルギー国旗","ベルギー","国旗"], + "🇧🇫":["ブルã‚ナファソ国旗","ブルã‚ナファソ","国旗"], + "🇧🇬":["ブルガリア国旗","ブルガリア","国旗"], + "🇧ðŸ‡":["ãƒãƒ¼ãƒ¬ãƒ¼ãƒ³å›½æ——","ãƒãƒ¼ãƒ¬ãƒ¼ãƒ³","国旗"], + "🇧🇮":["ブルンジ国旗","ブルンジ","国旗"], + "🇧🇯":["ベナン国旗","ベナン","国旗"], + "🇧🇱":["サン・ãƒãƒ«ãƒ†ãƒ«ãƒŸãƒ¼å³¶ã®æ——","ãƒãƒ«ãƒ†ãƒ«ãƒŸãƒ¼","国旗","サン"], + "🇧🇲":["ãƒãƒŸãƒ¥ãƒ¼ãƒ€è«¸å³¶ã®æ——","ãƒãƒŸãƒ¥ãƒ¼ãƒ€è«¸å³¶","国旗"], + "🇧🇳":["ブルãƒã‚¤å›½æ——","ブルãƒã‚¤","ダルサラーム","国旗"], + "🇧🇴":["ボリビア国旗","ボリビア","国旗"], + "🇧🇶":["カリブ海ã®ã‚ªãƒ©ãƒ³ãƒ€é ˜å³¶ã®æ——","ボãƒãƒ¼ãƒ«å³¶","カリブ海","ユースタティウス","国旗","オランダ","サãƒ","シント"], + "🇧🇷":["ブラジル国旗","ブラジル","国旗"], + "🇧🇸":["ãƒãƒãƒžå›½æ——","ãƒãƒãƒž","国旗"], + "🇧🇹":["ブータン国旗","ブータン","国旗"], + "🇧🇼":["ボツワナ国旗","ボツワナ","国旗"], + "🇧🇾":["ベラルーシ国旗","ベラルーシ","国旗"], + "🇧🇿":["ベリーズ国旗","ベリーズ","国旗"], + "🇨🇦":["カナダ国旗","カナダ","国旗"], + "🇨🇨":["ココス諸島ã®æ——","ココス","国旗","諸島","ã‚ーリング"], + "🇨🇩":["コンゴ国旗 - ã‚ンシャサ","コンゴ","コンゴ - ã‚ンシャサ","コンゴ民主共和国","国旗","ã‚ンシャサ","共和国"], + "🇨🇫":["ä¸å¤®ã‚¢ãƒ•ãƒªã‚«å›½æ——","ä¸å¤®ã‚¢ãƒ•ãƒªã‚«å…±å’Œå›½","国旗","共和国"], + "🇨🇬":["コンゴã®æ—— - ブラザビル","ブラザビル","コンゴ","コンゴ共和国","コンゴ - ブラザビル","国旗","共和国"], + "🇨ðŸ‡":["スイス国旗","国旗","スイス"], + "🇨🇮":["コートジボワール国旗","コートジボワール","国旗"], + "🇨🇰":["クック諸島国旗","クック","国旗","諸島"], + "🇨🇱":["ãƒãƒªå›½æ——","ãƒãƒª","国旗"], + "🇨🇲":["カメルーン国旗","カメルーン","国旗"], + "🇨🇳":["ä¸å›½å›½æ——","ä¸å›½","国旗"], + "🇨🇴":["コãƒãƒ³ãƒ“ア国旗","コãƒãƒ³ãƒ“ã‚¢","国旗"], + "🇨🇷":["コスタリカ国旗","コスタリカ","国旗"], + "🇨🇺":["ã‚ューãƒå›½æ——","ã‚ューãƒ","国旗"], + "🇨🇻":["カーボベルデ国旗","カーボ","ケープ","国旗","ベルデ"], + "🇨🇼":["ã‚ュラソー島ã®æ——","アンティル諸島","ã‚ュラソー","国旗"], + "🇨🇽":["クリスマス島ã®æ——","クリスマス","国旗","島"], + "🇨🇾":["ã‚プãƒã‚¹å›½æ——","ã‚プãƒã‚¹","国旗"], + "🇨🇿":["ãƒã‚§ã‚³å›½æ——","ãƒã‚§ã‚³å…±å’Œå›½","国旗"], + "🇩🇪":["ドイツ国旗","国旗","ドイツ"], + "🇩🇯":["ジブãƒå›½æ——","ジブãƒ","国旗"], + "🇩🇰":["デンマーク国旗","デンマーク","国旗"], + "🇩🇲":["ドミニカ国旗","ドミニカ","国旗"], + "🇩🇴":["ドミニカ共和国国旗","ドミニカ共和国","国旗"], + "🇩🇿":["アルジェリア国旗","アルジェリア","国旗"], + "🇪🇨":["エクアドル国旗","エクアドル","国旗"], + "ðŸ´ó §ó ¢ó ¥ó ®ó §ó ¿":["イングランドã®æ——","イングランド","æ——"], + "🇪🇪":["エストニア国旗","エストニア","国旗"], + "🇪🇬":["エジプト国旗","エジプト","国旗"], + "🇪ðŸ‡":["西サãƒãƒ©ã®æ——","国旗","サãƒãƒ©","西","西サãƒãƒ©"], + "🇪🇷":["エリトリア国旗","エリトリア","国旗"], + "🇪🇸":["スペイン国旗","国旗","スペイン","セウタ","メリリャ"], + "🇪🇹":["エãƒã‚ªãƒ”ア国旗","エãƒã‚ªãƒ”ã‚¢","国旗"], + "🇪🇺":["欧州旗","欧州連åˆ","æ——"], + "🇫🇮":["フィンランド国旗","フィンランド","国旗"], + "🇫🇯":["フィジー国旗","フィジー","国旗"], + "🇫🇰":["フォークランド諸島ã®æ——","フォークランド","フォークランド諸島","国旗","諸島","マルビナス"], + "🇫🇲":["ミクãƒãƒã‚·ã‚¢å›½æ——","国旗","ミクãƒãƒã‚·ã‚¢"], + "🇫🇴":["フェãƒãƒ¼è«¸å³¶ã®æ——","フェãƒãƒ¼","æ——","諸島"], + "🇫🇷":["フランス国旗","国旗","フランス","クリッパートン島","セント・マーãƒãƒ³","サン・マルタン"], + "🇬🇦":["ガボン国旗","国旗","ガボン"], + "🇬🇧":["イギリス国旗","イギリス","ã‚¤ã‚®ãƒªã‚¹é ˜","コーンウォール","イングランド","国旗","グレートブリテン","アイルランド","北アイルランド","スコットランド","UK","ユニオンジャック","連åˆ","連åˆçŽ‹å›½","ウェールズ"], + "🇬🇩":["グレナダ国旗","国旗","グレナダ"], + "🇬🇪":["ジョージア国旗","国旗","ジョージア"], + "🇬🇫":["ãƒ•ãƒ©ãƒ³ã‚¹é ˜ã‚®ã‚¢ãƒŠã®æ——","国旗","ãƒ•ãƒ©ãƒ³ã‚¹é ˜","ギアナ"], + "🇬🇬":["ガーンジー国旗","国旗","ガーンジー"], + "🇬ðŸ‡":["ガーナ国旗","国旗","ガーナ"], + "🇬🇮":["ジブラルタル国旗","国旗","ジブラルタル"], + "🇬🇱":["グリーンランド国旗","国旗","グリーンランド"], + "🇬🇲":["ガンビア国旗","国旗","ガンビア"], + "🇬🇳":["ギニア国旗","国旗","ギニア"], + "🇬🇵":["グアドループ国旗","国旗","グアドループ"], + "🇬🇶":["赤é“ギニア国旗","赤é“ギニア","国旗","ギニア"], + "🇬🇷":["ギリシャ国旗","国旗","ギリシャ"], + "🇬🇸":["サウスジョージア・サウスサンドウィッãƒè«¸å³¶å›½æ——","国旗","ジョージア","諸島","サウス","サウスジョージア","サウスサンドウィッãƒ"], + "🇬🇹":["グアテマラ国旗","国旗","グアテマラ"], + "🇬🇺":["グアム旗","国旗","グアム"], + "🇬🇼":["ギニアビサウ国旗","ビサウ","国旗","ギニア"], + "🇬🇾":["ガイアナ国旗","国旗","ガイアナ"], + "ðŸ‡ðŸ‡°":["香港ã®æ——","ä¸å›½","国旗","香港"], + "ðŸ‡ðŸ‡³":["ホンジュラス国旗","国旗","ホンジュラス"], + "ðŸ‡ðŸ‡·":["クãƒã‚¢ãƒã‚¢å›½æ——","クãƒã‚¢ãƒã‚¢","国旗"], + "ðŸ‡ðŸ‡¹":["ãƒã‚¤ãƒå›½æ——","国旗","ãƒã‚¤ãƒ"], + "ðŸ‡ðŸ‡º":["ãƒãƒ³ã‚¬ãƒªãƒ¼å›½æ——","国旗","ãƒãƒ³ã‚¬ãƒªãƒ¼"], + "🇮🇨":["カナリア諸島ã®æ——","カナリア","国旗","諸島"], + "🇮🇩":["インドãƒã‚·ã‚¢å›½æ——","国旗","インドãƒã‚·ã‚¢"], + "🇮🇪":["アイルランド国旗","国旗","アイルランド"], + "🇮🇱":["イスラエル国旗","国旗","イスラエル"], + "🇮🇲":["マン島ã®æ——","国旗","マン島"], + "🇮🇳":["インド国旗","国旗","インド"], + "🇮🇴":["ã‚¤ã‚®ãƒªã‚¹é ˜ã‚¤ãƒ³ãƒ‰æ´‹åœ°åŸŸã®æ——","ã‚¤ã‚®ãƒªã‚¹é ˜","ãƒãƒ£ã‚´ã‚¹","æ——","インド洋","島","ディエゴガルシア"], + "🇮🇶":["イラク国旗","国旗","イラク"], + "🇮🇷":["イラン国旗","国旗","イラン"], + "🇮🇸":["アイスランド国旗","国旗","アイスランド"], + "🇮🇹":["イタリア国旗","国旗","イタリア"], + "🇯🇪":["ジャージー代官管轄区ã®æ——","国旗","ジャージー代官管轄区"], + "🇯🇲":["ジャマイカ国旗","国旗","ジャマイカ"], + "🇯🇴":["ヨルダン国旗","国旗","ヨルダン"], + "🇯🇵":["日本国旗","国旗","日本"], + "🇰🇪":["ケニア国旗","国旗","ケニア"], + "🇰🇬":["ã‚ルギス国旗","国旗","ã‚ルギス"], + "🇰ðŸ‡":["カンボジア国旗","カンボジア","国旗"], + "🇰🇮":["ã‚リãƒã‚¹å›½æ——","国旗","ã‚リãƒã‚¹"], + "🇰🇲":["コモãƒå›½æ——","コモãƒ","国旗"], + "🇰🇳":["セントクリストファー・ãƒã‚¤ãƒ“ス国旗","国旗","ã‚ッツ","ãƒã‚¤ãƒ“ス","セント"], + "🇰🇵":["北æœé®®å›½æ——","国旗","æœé®®","北","北æœé®®"], + "🇰🇷":["韓国国旗","国旗","韓国","å—","大韓民国"], + "🇰🇼":["クウェート国旗","国旗","クウェート"], + "🇰🇾":["ケイマン諸島ã®æ——","ケイマン","国旗","諸島"], + "🇰🇿":["カザフスタン国旗","国旗","カザフスタン"], + "🇱🇦":["ラオス国旗","国旗","ラオス"], + "🇱🇧":["レãƒãƒŽãƒ³å›½æ——","国旗","レãƒãƒŽãƒ³"], + "🇱🇨":["セントルシア国旗","国旗","セントルシア"], + "🇱🇮":["リヒテンシュタイン国旗","国旗","リヒテンシュタイン"], + "🇱🇰":["スリランカ国旗","国旗","スリランカ"], + "🇱🇷":["リベリア国旗","国旗","リベリア"], + "🇱🇸":["レソト国旗","国旗","レソト"], + "🇱🇹":["リトアニア国旗","国旗","リトアニア"], + "🇱🇺":["ルクセンブルク国旗","国旗","ルクセンブルク"], + "🇱🇻":["ラトビア国旗","国旗","ラトビア"], + "🇱🇾":["リビア国旗","国旗","リビア"], + "🇲🇦":["モãƒãƒƒã‚³å›½æ——","国旗","モãƒãƒƒã‚³"], + "🇲🇨":["モナコ国旗","国旗","モナコ"], + "🇲🇩":["モルドãƒå›½æ——","国旗","モルドãƒ"], + "🇲🇪":["モンテãƒã‚°ãƒå›½æ——","国旗","モンテãƒã‚°ãƒ"], + "🇲🇬":["マダガスカル国旗","国旗","マダガスカル"], + "🇲ðŸ‡":["マーシャル諸島国旗","国旗","諸島","マーシャル"], + "🇲🇰":["マケドニア国旗","国旗","マケドニア"], + "🇲🇱":["マリ国旗","国旗","マリ"], + "🇲🇲":["ミャンマー国旗","ビルマ","国旗","ミャンマー"], + "🇲🇳":["モンゴル国旗","国旗","モンゴル"], + "🇲🇴":["マカオã®æ——","ä¸å›½","国旗","マカオ"], + "🇲🇵":["北マリアナ諸島ã®æ——","国旗","諸島","マリアナ","北","北マリアナ"], + "🇲🇶":["マルティニークã®æ——","æ——","マルティニーク"], + "🇲🇷":["モーリタニア国旗","国旗","モーリタニア"], + "🇲🇸":["モントセラトã®æ——","æ——","モントセラト"], + "🇲🇹":["マルタ国旗","国旗","マルタ"], + "🇲🇺":["モーリシャス国旗","国旗","モーリシャス"], + "🇲🇻":["モルディブ国旗","国旗","モルディブ"], + "🇲🇼":["マラウイ国旗","国旗","マラウイ"], + "🇲🇽":["メã‚シコ国旗","国旗","メã‚シコ"], + "🇲🇾":["マレーシア国旗","国旗","マレーシア"], + "🇲🇿":["モザンビーク国旗","国旗","モザンビーク"], + "🇳🇦":["ナミビア国旗","国旗","ナミビア"], + "🇳🇨":["ニューカレドニアã®æ——","国旗","ニュー","ニューカレドニア"], + "🇳🇪":["ニジェール国旗","国旗","ニジェール"], + "🇳🇫":["ノーフォーク島ã®æ——","æ——","島","ノーフォーク"], + "🇳🇬":["ナイジェリア国旗","国旗","ナイジェリア"], + "🇳🇮":["ニカラグア国旗","国旗","ニカラグア"], + "🇳🇱":["オランダ国旗","国旗","オランダ"], + "🇳🇴":["ノルウェー国旗","æ——","ノルウェー","ブーã¹","スヴァールãƒãƒ«","ヤンマイエン"], + "🇳🇵":["ãƒãƒ‘ール国旗","国旗","ãƒãƒ‘ール"], + "🇳🇷":["ナウル国旗","国旗","ナウル"], + "🇳🇺":["ニウエ国旗","国旗","ニウエ"], + "🇳🇿":["ニュージーランド国旗","国旗","ニュー","ニュージーランド"], + "🇴🇲":["オマーン国旗","国旗","オマーン"], + "🇵🇦":["パナマ国旗","国旗","パナマ"], + "🇵🇪":["ペルー国旗","国旗","ペルー"], + "🇵🇫":["ãƒ•ãƒ©ãƒ³ã‚¹é ˜ãƒãƒªãƒã‚·ã‚¢ã®æ——","国旗","ãƒ•ãƒ©ãƒ³ã‚¹é ˜","ãƒãƒªãƒã‚·ã‚¢"], + "🇵🇬":["パプアニューギニア国旗","国旗","ギニア","ニュー","パプアニューギニア"], + "🇵ðŸ‡":["フィリピン国旗","国旗","フィリピン"], + "🇵🇰":["パã‚スタン国旗","国旗","パã‚スタン"], + "🇵🇱":["ãƒãƒ¼ãƒ©ãƒ³ãƒ‰å›½æ——","国旗","ãƒãƒ¼ãƒ©ãƒ³ãƒ‰"], + "🇵🇲":["サンピエール島・ミクãƒãƒ³å³¶ã®æ——","æ——","ミクãƒãƒ³","ピエール","サン"], + "🇵🇳":["ピトケアン諸島ã®æ——","æ——","諸島","ピトケアン"], + "🇵🇷":["プエルトリコã®æ——","国旗","プエルトリコ"], + "🇵🇸":["パレスãƒãƒŠè‡ªæ²»æ”¿åºœã®æ——","国旗","パレスãƒãƒŠ"], + "🇵🇹":["ãƒãƒ«ãƒˆã‚¬ãƒ«å›½æ——","国旗","ãƒãƒ«ãƒˆã‚¬ãƒ«"], + "🇵🇼":["パラオ国旗","国旗","パラオ"], + "🇵🇾":["パラグアイ国旗","国旗","パラグアイ"], + "🇶🇦":["カタール国旗","国旗","カタール"], + "🇷🇪":["レユニオンã®æ——","æ——","レユニオン"], + "🇷🇴":["ルーマニア国旗","国旗","ルーマニア"], + "🇷🇸":["セルビア国旗","国旗","セルビア"], + "🇷🇺":["ãƒã‚·ã‚¢å›½æ——","国旗","ãƒã‚·ã‚¢"], + "🇷🇼":["ルワンダ国旗","国旗","ルワンダ"], + "🇸🇦":["サウジアラビア国旗","国旗","サウジアラビア"], + "ðŸ´ó §ó ¢ó ³ó £ó ´ó ¿":["スコットランドã®æ——","スコットランド","æ——"], + "🇸🇧":["ソãƒãƒ¢ãƒ³è«¸å³¶å›½æ——","æ——","諸島","ソãƒãƒ¢ãƒ³"], + "🇸🇨":["セーシェル国旗","国旗","セーシェル"], + "🇸🇩":["スーダン国旗","国旗","スーダン"], + "🇸🇪":["スウェーデン国旗","国旗","スウェーデン"], + "🇸🇬":["シンガãƒãƒ¼ãƒ«å›½æ——","国旗","シンガãƒãƒ¼ãƒ«"], + "🇸ðŸ‡":["セントヘレナ島ã®æ——","æ——","ヘレナ","セント"], + "🇸🇮":["スãƒãƒ™ãƒ‹ã‚¢å›½æ——","国旗","スãƒãƒ™ãƒ‹ã‚¢"], + "🇸🇰":["スãƒãƒã‚ア国旗","国旗","スãƒãƒã‚ã‚¢"], + "🇸🇱":["シエラレオãƒå›½æ——","国旗","シエラレオãƒ"], + "🇸🇲":["サンマリノ国旗","国旗","サンマリノ"], + "🇸🇳":["ã‚»ãƒã‚¬ãƒ«å›½æ——","国旗","ã‚»ãƒã‚¬ãƒ«"], + "🇸🇴":["ソマリア国旗","国旗","ソマリア"], + "🇸🇷":["スリナム国旗","国旗","スリナム"], + "🇸🇸":["å—スーダン国旗","国旗","å—","å—スーダン","スーダン"], + "🇸🇹":["サントメ・プリンシペ国旗","国旗","プリンシペ","プリンシピ","サントメ","サォントメー"], + "🇸🇻":["エルサルãƒãƒ‰ãƒ«å›½æ——","エルサルãƒãƒ‰ãƒ«","国旗"], + "🇸🇽":["セント・マーãƒãƒ³å³¶ã®æ——","æ——","マーãƒãƒ³","セント"], + "🇸🇾":["シリア国旗","国旗","シリア"], + "🇸🇿":["スワジランド国旗","国旗","スワジランド"], + "🇹🇦":["トリスタンダクーニャã®æ——","æ——","トリスタン・ダ・クーニャ"], + "🇹🇨":["タークス・カイコス諸島ã®æ——","カイコス","æ——","諸島","タークス"], + "🇹🇩":["ãƒãƒ£ãƒ‰å›½æ——","ãƒãƒ£ãƒ‰","国旗"], + "🇹🇫":["ãƒ•ãƒ©ãƒ³ã‚¹é ˜å—方・å—極地域ã®æ——","å—極","国旗","ãƒ•ãƒ©ãƒ³ã‚¹é ˜"], + "🇹🇬":["トーゴ国旗","国旗","トーゴ"], + "🇹ðŸ‡":["タイ国旗","国旗","タイ"], + "🇹🇯":["タジã‚スタン国旗","国旗","タジã‚スタン"], + "🇹🇰":["トケラウ旗","国旗","トケラウ"], + "🇹🇱":["æ±ãƒ†ã‚£ãƒ¢ãƒ¼ãƒ«å›½æ——","æ±","æ±ãƒ†ã‚£ãƒ¢ãƒ¼ãƒ«","国旗","ティモール・レステ"], + "🇹🇲":["トルクメニスタン国旗","国旗","トルクメニスタン"], + "🇹🇳":["ãƒãƒ¥ãƒ‹ã‚¸ã‚¢å›½æ——","国旗","ãƒãƒ¥ãƒ‹ã‚¸ã‚¢"], + "🇹🇴":["トンガ国旗","国旗","トンガ"], + "🇹🇷":["トルコ国旗","国旗","トルコ"], + "🇹🇹":["トリニダード・トãƒã‚´å›½æ——","国旗","トãƒã‚´","トリニダード"], + "🇹🇻":["ツãƒãƒ«å›½æ——","国旗","ツãƒãƒ«"], + "🇹🇼":["å°æ¹¾ã®æ——","ä¸å›½","国旗","å°æ¹¾"], + "🇹🇿":["タンザニア国旗","国旗","タンザニア"], + "🇺🇦":["ウクライナ国旗","国旗","ウクライナ"], + "🇺🇬":["ウガンダ国旗","国旗","ウガンダ"], + "🇺🇳":["国連ã®æ——","æ——","国連","連åˆ","国際"], + "🇺🇸":["アメリカ国旗","アメリカ","æ——","åˆè¡†","åˆè¡†å›½","アメリカåˆè¡†å›½","åˆè¡†å›½é ˜æœ‰å°é›¢å³¶"], + "🇺🇾":["ウルグアイ国旗","国旗","ウルグアイ"], + "🇺🇿":["ウズベã‚スタン国旗","国旗","ウズベã‚スタン"], + "🇻🇦":["ãƒãƒã‚«ãƒ³å¸‚国旗","国旗","ãƒãƒã‚«ãƒ³"], + "🇻🇨":["セントビンセント・グレナディーン国旗","国旗","グレナディーン諸島","セント","ビンセント"], + "🇻🇪":["ベãƒã‚ºã‚¨ãƒ©å›½æ——","国旗","ベãƒã‚ºã‚¨ãƒ©"], + "🇻🇬":["ã‚¤ã‚®ãƒªã‚¹é ˜ãƒ´ã‚¡ãƒ¼ã‚¸ãƒ³è«¸å³¶ã®æ——","ã‚¤ã‚®ãƒªã‚¹é ˜","国旗","島","ヴァージン"], + "🇻🇮":["ã‚¢ãƒ¡ãƒªã‚«é ˜ãƒ´ã‚¡ãƒ¼ã‚¸ãƒ³è«¸å³¶ã®æ——","アメリカ","国旗","島","アメリカåˆè¡†å›½","åˆè¡†å›½","ヴァージン"], + "🇻🇳":["ベトナム国旗","国旗","ベトナム","ヴェトナム"], + "🇻🇺":["ãƒãƒŒã‚¢ãƒ„国旗","国旗","ãƒãƒŒã‚¢ãƒ„"], + "ðŸ´ó §ó ¢ó ·ó ¬ó ³ó ¿":["ウェールズã®æ——","ウェールズ","æ——"], + "🇼🇫":["ウォリス・フツナã®æ——","国旗","フツナ","ウォリス"], + "🇼🇸":["サモア国旗","国旗","サモア"], + "🇽🇰":["コソボ国旗","国旗","コソボ"], + "🇾🇪":["イエメン国旗","国旗","イエメン"], + "🇾🇹":["マヨットã®æ——","国旗","マヨット"], + "🇿🇦":["å—アフリカ国旗","国旗","å—","å—アフリカ"], + "🇿🇲":["ザンビア国旗","国旗","ザンビア"], + "🇿🇼":["ジンãƒãƒ–エ国旗","国旗","ジンãƒãƒ–エ"], + "": ["渋谷109", "SHIBUYA109", "109"] +} diff --git a/packages/frontend/src/unicode-emoji-indexes/ja-JP_hira.json b/packages/frontend/src/unicode-emoji-indexes/ja-JP_hira.json new file mode 100644 index 0000000000000000000000000000000000000000..2ad282d5017f836782e945dee8718174debc46fb --- /dev/null +++ b/packages/frontend/src/unicode-emoji-indexes/ja-JP_hira.json @@ -0,0 +1,1866 @@ +{ + "😀": ["ã«ã‚„ã«ã‚„ã—ãŸã‹ãŠ","ã‹ãŠ","ã«ã‚„ã«ã‚„","ã—ã‚ã‚ã›"], + "😃": ["ãã¡ã‚’ã‚ã‘ãŸãˆãŒãŠ","ã‹ãŠ","ãã¡","ã‚ã‘ã‚‹","ãˆãŒãŠ","ã—ã‚ã‚ã›"], + "😄": ["ãã¡ã‚’ã‚ã‘ã¦ã‚ãŒã‚らã£ã¦ã„ã‚‹ãˆãŒãŠ","ã‚","ã‹ãŠ","ãã¡","ã‚ã‘ã‚‹","ãˆãŒãŠ","ã—ã‚ã‚ã›"], + "ðŸ˜": ["ã«ã‚„ã«ã‚„ã—ãŸã‹ãŠ","ã‚","ã‹ãŠ","ã«ã‚„ã«ã‚„","ãˆãŒãŠ"], + "😆": ["ãã¡ã‚’ã‚ã‘ã¦ã‚らã£ã¦ã„ã‚‹ã‹ãŠ","ã‹ãŠ","ã‚らã„","ãã¡","ã‚ã‘ã‚‹","ã¾ã‚“ãžã","ãˆãŒãŠ"], + "😅": ["ãã¡ã‚’ã‚ã‘ã¦ã²ã‚„ã‚ã›ã‚’ã‹ã„ãŸãˆãŒãŠ","ãžã£ã¨ã™ã‚‹","ã‹ãŠ","ãã¡ã‚’ã‚ã‘ã‚‹","ãˆãŒãŠ","ã²ã‚„ã‚ã›"], + "😂": ["ã†ã‚Œã—ãªã","ã‹ãŠ","ã†ã‚Œã—ã„","ã‚らã†","ãªã","ãªã¿ã "], + "🤣": ["ã ã„ã°ãã—ょã†","ã‹ãŠ","ゆã‹","ã‚らã„","ãŠãŠã‚らã„","ã°ãã—ょã†","ãã‚‹ãã‚‹"], + "😇": ["ã¦ã‚“ã—ã®ãˆãŒãŠ","ã¦ã‚“ã—","ã‹ãŠ","ãŠã¨ãŽã°ãªã—","ãµãã‚“ãŸã˜ãƒ¼","ã¦ã‚“ã—ã®ã‚","ã‚€ã˜ã‚ƒã","ãˆãŒãŠ"], + "😉": ["ã†ã„ã‚“ãã—ãŸã‹ãŠ","ã‹ãŠ","ã†ã„ã‚“ã"], + "😊": ["ã‚ãŒã‚らã£ã¦ã„ã‚‹ãˆãŒãŠ","ã›ãã‚ã‚“","ã‚","ã‹ãŠ","ãˆãŒãŠ"], + "🙂": ["ã»ã»ãˆã¿","ã‹ãŠ","ãˆãŒãŠ","ã—ã‚ã‚ã›"], + "🙃": ["ã•ã‹ã•ã®ã‹ãŠ","ã‹ãŠ","ã•ã‹ã•"], + "☺ï¸": ["ãˆãŒãŠ","ã‹ãŠ","ã‚Šã‚“ã‹ã","りらã£ãã™"], + "😋": ["ãŸã¹ã‚‚ã®ã‚’ã‚ã˜ã‚ã†ã‹ãŠ","ãŠã„ã—ã„","ã‹ãŠ","ã‚ã˜ã‚ã†","ãµãƒ¼ã‚€","ã†ã¾ã„"], + "😌": ["ã»ã£ã¨ã—ãŸã‹ãŠ","ã‹ãŠ","ã‚ã‚“ã—ã‚“","ã»ã£ã¨ã™ã‚‹"], + "ðŸ˜": ["ã‚ãŒã¯ãƒ¼ã¨ã®ãˆãŒãŠ","ã‚","ã‹ãŠ","ã¯ãƒ¼ã¨","ã‚ã„","ãˆãŒãŠ"], + "🥰": ["ãˆãŒãŠã¨ã¯ãƒ¼ã¨","ã‹ãŠ","ã‘ã„ã‚ã„","ã¹ãŸã¼ã‚Œ","ã‚ã„"], + "😘": ["ãªã’ãã£ã™","ã‹ãŠ","ã¯ãƒ¼ã¨","ãã™"], + "😗": ["ãã™ã‚’ã™ã‚‹ã‹ãŠ","ã‹ãŠ","ãã™"], + "😙": ["ãˆãŒãŠã§ãã™","ã‚","ã‹ãŠ","ãã™","ãˆãŒãŠ"], + "😚": ["ã‚ã‚’ã¨ã˜ã¦ãã™ã‚’ã™ã‚‹ã‹ãŠ","ã¨ã˜ãŸ","ã‚","ã‹ãŠ","ãã™"], + "🥲": ["ãªã¿ã ã®ã§ã¦ã„ã‚‹ãˆãŒãŠ","ãªã","ã—ã‚ã‚ã›","ã‹ã‚“ã—ゃã™ã‚‹","ã»ã“ã‚Šã«ãŠã‚‚ã†","ã‚ã‚“ã—ã‚“ã™ã‚‹","ã‚らã†"], + "🤪": ["ãŠã©ã‘ãŸã‹ãŠ","ã‚","ã«ã‚„ã«ã‚„","ã¸ã‚“","ã“ã†ãµã‚“","ã‚ã„ã‚‹ã©"], + "😜": ["ã—ãŸã‚’ã ã—ã¦ã†ã„ã‚“ãã—ã¦ã„ã‚‹ã‹ãŠ","ã‚","ã‹ãŠ","ã˜ã‚‡ã†ã ã‚“","ã—ãŸ","ã†ã„ã‚“ã"], + "ðŸ˜": ["ã—ãŸã‚’ã ã—ã¦ã‚ã‚’ã»ãã‚ã¦ã„ã‚‹ã‹ãŠ","ã‚","ã‹ãŠ","ã“ã‚ã„","ã‚ã˜","ã—ãŸ"], + "😛": ["ã—ãŸã‚’ã ã—ã¦ã„ã‚‹ã‹ãŠ","ã‹ãŠ","ã—ãŸ"], + "🤑": ["ã”ã†ã‚ˆããªã‹ãŠ","ã‹ãŠ","ãŠã‹ã","ãã¡"], + "😎": ["ã•ã‚“ãらã™ã‚’ã‹ã‘ãŸã‹ãŠ","ã‚ã‹ã‚‹ã„","ã‹ã£ã“ã„ã„","ã‚","ã‚ã„ã†ã‡ã‚","ã‹ãŠ","ã‚ãŒã","ãˆãŒãŠ","ãŸã„よã†","ã•ã‚“ãらã™","ã¦ã‚“ã"], + "🤓": ["ãŠãŸã","ã‹ãŠ","ã¸ã‚“ãªã²ã¨"], + "🥸": ["ã‹ãã†ã—ãŸã‹ãŠ","ã‹ãã†","ã‚ãŒã","ã¨ãã‚ã„ã®ã²ã¨","ã¯ãª"], + "ðŸ§": ["ã‹ãŸã‚ãŒãã‚’ã‹ã‘ãŸã‹ãŠ","ãŸã„ãã¤","ゆã†ãµã","ゆãŸã‹"], + "🤠": ["ã‹ã†ã¼ãƒ¼ã„ã¯ã£ã¨ã®ã‹ãŠ","ã‹ã†ã¼ãƒ¼ã„","ã‹ã†ãŒãƒ¼ã‚‹","ã‹ãŠ","ã¼ã†ã—"], + "🥳": ["ã±ãƒ¼ã¦ãƒãƒ¼ãµã‡ã„ã™","ã‹ãŠ","ã—ã‚…ãã¦ã‚“","ã¼ã†ã—","ã¤ã®","ã±ãƒ¼ã¦ãƒãƒ¼"], + "🤡": ["ã´ãˆã‚ã®ã‹ãŠ","ã´ãˆã‚","ã‹ãŠ"], + "ðŸ˜": ["ã«ã‚„ã«ã‚„ã—ãŸã‹ãŠ","ã‹ãŠ","ã«ã‚„ã«ã‚„"], + "😶": ["ãã¡ã®ãªã„ã‹ãŠ","ã‹ãŠ","ãã¡","ã—ãšã‹ã«","ã¡ã‚“ã‚‚ã"], + "🫥": ["ã¦ã‚“ã›ã‚“ã®ã‹ãŠ","ãŠã¡ã“ã‚“ã ","ããˆã‚‹","ã‹ãれる","ãªã„ã“ã†ã¦ã","ã‚ã«ã¿ãˆãªã„"], + "ðŸ˜": ["ãµã¤ã†ã®ã‹ãŠ","ã‚€ã²ã‚‡ã†ã˜ã‚‡ã†","ã‹ãŠ","ã¸ã„ã›ã„"], + "🫤": ["ãã¡ãŒãªãªã‚ã«ãªã£ãŸã‹ãŠ","ãŒã£ã‹ã‚Š","ã‚€ã‹ã‚“ã—ã‚“","ã†ãŸãŒã„ã¶ã‹ã„","ãµã‚ã‚“"], + "😑": ["ã‚€ã²ã‚‡ã†ã˜ã‚‡ã†","ã‹ãŠ","ã½ãƒ¼ã‹ãƒ¼ãµã‡ã„ã™","ã‚€ã‹ã‚“ã˜ã‚‡ã†"], + "😒": ["ãŠã‚‚ã—ã‚ããªã•ãã†ãªã‹ãŠ","ã‹ãŠ","ã¤ã¾ã‚‰ãªã„","ãµã“ã†"], + "🙄": ["ãã‚‹ãã‚‹ã‚ã®ã‹ãŠ","ã‚","ã‹ãŠ","ãã‚‹ãã‚‹"], + "🤨": ["ã¾ã‚†ãŒã‚ãŒã£ã¦ã„ã‚‹ã‹ãŠ","ãµã—ã‚“","ã†ãŸãŒã„ã¶ã‹ã„","ã²ãªã‚“","ãŽãã‚“","ã‚„ã‚„ãŠã©ã‚ã","ã‹ã„ãŽã¦ã"], + "🤔": ["ã‹ã‚“ãŒãˆã¦ã„ã‚‹ã‹ãŠ","ã‹ãŠ","ã‹ã‚“ãŒãˆã¡ã‚…ã†"], + "🤫": ["ã—ã£ã¨ã„ã£ã¦ã„ã‚‹ã‹ãŠ","ã—ーã£","ã—ãšã‹","ã ã¾ã‚‹"], + "ðŸ¤": ["ãã¡ã‚’ã¦ã§ãŠãŠã£ãŸã‹ãŠ","ã‚","ãˆãŒãŠ","ãŠãŠã†","ãã¡","ã¦"], + "🫢": ["ã‚ã‚’ã²ã‚‰ã„ã¦ãã¡ã‚’ã¦ã§ãŠãŠã£ãŸã‹ãŠ","ãょã†ãŸã‚“","ã„ã‘ã„","ãµã—ã‚“","ã‚ã†ã°ã„","ã“ã‚ã„","ãŠã©ã‚ã"], + "🫡": ["ã‘ã„ã‚Œã„ã—ã¦ã„ã‚‹ã‹ãŠ","ok","ã‘ã„ã‚Œã„","ã›ã„ã¦ã‚“","ã¶ãŸã„","ã¯ã„"], + "🤗": ["りょã†ã¦ã‚’ã²ã‚ã’ãŸãˆãŒãŠ","ã‹ãŠ","ã¯ã","ã ãã—ã‚ã‚‹"], + "🫣": ["ã®ãžãã¿ã—ã¦ã„ã‚‹ã‹ãŠ","ã¿ã‚Šã‚‡ã†","ã®ãžãã¿","ãŽã‚‡ã†ã—","ã¡ã‚‰ã¿"], + "🤥": ["ã†ãã¤ããŒãŠ","ã‹ãŠ","ã†ã","ã´ã®ããŠ"], + "😳": ["ã‚ã‹ããªã£ãŸã‹ãŠ","ã¼ãƒ¼ã£ã¨ã—ãŸ","ã¼ã†ã£ã¨ã—ãŸ","ã‹ãŠ","ã›ãã‚ã‚“"], + "😞": ["ãŒã£ã‹ã‚Šã—ãŸã‹ãŠ","ãŒã£ã‹ã‚Š","ã‹ãŠ"], + "😟": ["ãµã‚ã‚“ãªã‹ãŠ","ã‹ãŠ","ã—ã‚“ã±ã„","ãµã‚ã‚“"], + "😤": ["ã‹ã¡ã»ã“ã£ãŸã‹ãŠ","ã‹ãŠ","ã—ょã†ã‚Š","ã‹ã¤"], + "😠": ["ãŠã“ã£ãŸã‹ãŠ","ã„ã‹ã‚Š","ãŠã“ã£ãŸ","ã‹ãŠ","ã’ãã©"], + "😡": ["ãµãã‚ŒãŒãŠ","ã„ã‹ã‚Š","ãŠã“ã£ãŸ","ã‹ãŠ","ã’ãã©","ãµãã‚Œã£ã¤ã‚‰","ãµã‚“ã©","ã‚ã‹"], + "🤬": ["ãã¡ãŒãã”ã†ã§ãŠãŠã‚ã‚ŒãŸã‹ãŠ","ã®ã‚ã„","ã®ã®ã—ã‚Š"], + "😔": ["ã‹ãªã—ã’ãªã‹ãŠ","ãŒã£ã‹ã‚Š","ã‹ãŠ","ã‹ãªã—ã„"], + "😕": ["ã“ã¾ã£ãŸã‹ãŠ","ã“ã¾ã£ãŸ","ã‹ãŠ"], + "ðŸ™": ["ã”ãã’ã‚“ãªãªã‚","ã‹ãŠ","ã—ã‹ã‚ã£ã¤ã‚‰","ã‹ãªã—ã„","ãµã“ã†"], + "☹": ["ã—ã‹ã‚ã£ã¤ã‚‰","ã‹ãŠ","ã‹ãªã—ã„","ãµã“ã†"], + "😬": ["ã—ã‹ã‚ã£ã¤ã‚‰","ã‹ãŠ"], + "🥺": ["ã†ã£ãŸãˆã‹ã‘ã‚‹ã‹ãŠ","ã‹ãŠ","ã‚‚ã®ã”ã„","ã˜ã²","ã“ã„ã¬ã®ã‚"], + "😣": ["ãŒã¾ã‚“ã—ã¦ã„ã‚‹ã‹ãŠ","ã‹ãŠ","ãŒã‚“ã°ã‚‹"], + "😖": ["ã†ã‚ãŸãˆãŸã‹ãŠ","ã¨ã¾ã©ã„","ã†ã‚ãŸãˆ","ã‹ãŠ"], + "😫": ["ã¤ã‹ã‚ŒãŸã‹ãŠ","ã‹ãŠ","ã¤ã‹ã‚ŒãŸ"], + "😩": ["ã†ã‚“ã–ã‚Šã—ã¦ã„ã‚‹ã‹ãŠ","ã‹ãŠ","ã¤ã‹ã‚ŒãŸ","ã†ã‚“ã–ã‚Š"], + "🥱": ["ã‚ãã³ã—ã¦ã„ã‚‹ã‹ãŠ","ã‚ããŸ","ã¤ã‹ã‚ŒãŸ","ã‚ãã³"], + "😪": ["ãã‚€ã„ã‹ãŠ","ã‹ãŠ","ãã‚‹","ã™ã„ã¿ã‚“"], + "😮â€ðŸ’¨": ["ãŸã‚ã„ãã®ã§ã¦ã„ã‚‹ã‹ãŠ","ã‹ãŠ","ãŸã‚ã„ã","ã„ããŽã‚Œ","ã†ã‚ã","ã‚ã‚“ã—ã‚“","ã•ã•ã‚„ã","ãã¡ã¶ãˆ"], + "😮": ["ãã¡ã‚’ã‚ã‘ãŸãˆãŒãŠ","ã‹ãŠ","ãã¡","ã‚ã‘ã‚‹","ã©ã†ã˜ã‚‡ã†"], + "😱": ["ãœã£ãょã†ã—ãŸã‹ãŠ","ã‹ãŠ","ãょã†ãµ","ã“ã‚ã„","むんã","ãŠã³ãˆ","ãœã£ãょã†"], + "😨": ["ãžã£ã¨ã—ã¦ã„ã‚‹ã‹ãŠ","ã‹ãŠ","ãょã†ãµ","ã“ã‚ã„","ãŠã³ãˆ"], + "😰": ["ãã¡ã‚’ã‚ã‘ã¦ã²ã‚„ã‚ã›ã‚’ã‹ã„ãŸã‹ãŠ","ã‚ãŠã–ã‚ã‚‹","ãžã£ã¨ã™ã‚‹","ã‹ãŠ","ãã¡","ã‚ã‘ã‚‹","ã„ãã","ã²ã‚„ã‚ã›"], + "😥": ["ãŒã£ã‹ã‚Šã—ãŸãŒã‚ã‚“ã—ã‚“ã—ãŸã‹ãŠ","ãŒã£ã‹ã‚Š","ã‹ãŠ","ã‚ã‚“ã—ã‚“","ã»ã£ã¨ã™ã‚‹","やれやれ"], + "😓": ["ã²ã‚„ã‚ã›ã‚’ã‹ã„ã¦ã„ã‚‹ã‹ãŠ","ãžã£ã¨ã™ã‚‹","ã‹ãŠ","ã²ã‚„ã‚ã›"], + "😯": ["ãŠã¡ã¤ã„ãŸã‹ãŠ","ã‹ãŠ","ã ã¾ã‚‹","ã¼ã†ãœã‚“","ãŠã©ã‚ã"], + "😦": ["ã—ã‚“ã±ã„ãã†ãªã‹ãŠã®ãˆã‚‚ã˜","ã‹ãŠ","ã—ã‹ã‚ã£ã¤ã‚‰","ãã¡","ã‚ã‘ã‚‹"], + "😧": ["ãã®ã†ã«ã¿ã¡ãŸã‹ãŠ","ãã®ã†","ã‹ãŠ"], + "🥹": ["ãªã¿ã ã‚’ã“らãˆã¦ã„ã‚‹ã‹ãŠ","ãŠã“ã‚‹","ãªã","ã»ã“ã‚Šã«ãŠã‚‚ã†","ã•ã‹ã‚‰ã†","ã‹ãªã—ã‚€"], + "😢": ["ãªããŒãŠ","ãªã","ã‹ãŠ","ã‹ãªã—ã„","ãªã¿ã "], + "ðŸ˜": ["ã”ã†ãã‚…ã†","ãªã","ã‹ãŠ","ã‹ãªã—ã„","ãªã¿ã "], + "🤤": ["よã れをãŸã‚‰ã—ãŸã‹ãŠ","よã ã‚Œ","ã‹ãŠ"], + "🤩": ["ã™ãŸãƒ¼ã«ã‚€ã¡ã‚…ã†","ã‚","ã‹ãŠ","ã«ã‚„ã«ã‚„","ã»ã—","ã‚€ãã†ã¦ã"], + "😵": ["ã‚ãŒã°ã¤ã«ãªã£ãŸã‹ãŠ","ã‚ã¾ã„","ã‹ãŠ","ã°ã¤","ã‚"], + "😵â€ðŸ’«": ["ã‚ãŒãã‚‹ãã‚‹ã—ã¦ã„ã‚‹ã‹ãŠ","ã‚ã¾ã„","ã‹ãŠ","ã‚","ã†ã£ã¨ã‚Š","ãã‚‹ãã‚‹","ã¨ã‚‰ã¶ã‚‹","ãŠãƒ¼"], + "🥴": ["ã¼ã‚“ã‚„ã—ã‚ŠãŸã‹ãŠ","ã‹ãŠ","ã‚ã¾ã„","ã‚ã„ã¦ã„","ã»ã‚よã„","ã¾ã£ã™ãã§ãªã„ã‚","ã¯ã˜ã‚‡ã†ã®ãã¡"], + "😲": ["ãŠã©ã‚ã„ãŸã‹ãŠ","ãŠã©ã‚ã","ã³ã£ãã‚Š","ã‹ãŠ","ã—ょã£ã","ãょã†ãŒã"], + "🫨": ["ãµã‚‹ãˆã‚‹ã‹ãŠ","ã˜ã—ã‚“","ã‹ãŠ","ãµã‚‹ãˆ","ã—ょã†ã’ã","ã—ã‚“ã©ã†"], + "🤯": ["ã°ãã¯ã¤ã—ãŸã‚ãŸã¾","ã‹ãŠ","ã—ょã£ã","ã°ãã¯ã¤","ãょã†ã","ã³ã£ãã‚Š"], + "🫠": ["ã»ã‚ã‚Šã¨ã—ãŸã‹ãŠ","ããˆã‚‹","よã†ã‹ã„ã™ã‚‹","ãˆããŸã„","ã¨ã‘ã‚‹"], + "ðŸ¤": ["ãŠãã¡ã¡ã‚ƒã£ã","ã‹ãŠ","ãã¡","ã¡ã‚ƒã£ã"], + "😷": ["ã¾ã™ãã‚’ã—ãŸã‹ãŠ","ã‹ãœ","ã„ã—ゃ","ã‹ãŠ","ã¾ã™ã","ãã™ã‚Š","ã³ã‚‡ã†ã"], + "🤕": ["ã‘ãŒ","ã»ã†ãŸã„","ã‹ãŠ","ããš"], + "🤒": ["ãŠã‚“ã©ã‘ã„ã‚’ãã‚ãˆãŸã‹ãŠ","ã‹ãŠ","ã³ã‚‡ã†ã","ã‹ãœ","ãŸã„ãŠã‚“ã‘ã„"], + "🤮": ["ã¯ããã†ãªã‹ãŠ","ã³ã‚‡ã†ã","ãŠã†ã¨","ã‹ãœ","ã¯ã"], + "🤢": ["ã¯ããã†ãªã‹ãŠ","ã‹ãŠ","ã¯ãã‘","ãŠã†ã¨"], + "🤧": ["ãã—ゃã¿ã‚’ã™ã‚‹ã‹ãŠ","ã‹ãŠ","ãã—ゃã¿","ã¯ãã—ょん"], + "🥵": ["ã»ã¦ã£ãŸã‹ãŠ","ã‹ãŠ","ãã¤ã£ã½ã„","ãã£ã—ゃã³ã‚‡ã†","ã»ã¦ã£ãŸ","ã‚ã‹ã‚‰ãŒãŠ","ã‚ã›ã‚’ã‹ã„ãŸ"], + "🥶": ["ã‚ãŠã–ã‚ãŸã‹ãŠ","ã‹ãŠ","ãžã£ã¨ã™ã‚‹","ã“ã”ãˆã‚‹","ã¨ã†ã—ょã†","ã¤ã‚‰ã‚‰"], + "😶â€ðŸŒ«ï¸": ["ãã‚‚ã§ãŠãŠã‚ã‚ŒãŸã‹ãŠ","ã‹ãŠ","ãŠã£ã¡ã‚‡ã“ã¡ã‚‡ã„","ã²ã’ã‚“ã˜ã¤ã¦ã","ゆã‚","ã‚‚ã‚„","ãã‚‚ã§ãŠãŠã‚ã‚ŒãŸã‚ãŸã¾"], + "😴": ["ããŒãŠ","ã‹ãŠ","ãã‚‹","ã™ã„ã¿ã‚“","ã™ã‚„ã™ã‚„"], + "💤": ["ã™ã„ã¿ã‚“","ã¾ã‚“ãŒ","ãã‚‹","ã™ã‚„ã™ã‚„"], + "😈": ["ã¤ã®ã¤ããˆãŒãŠ","ã‹ãŠ","ãŠã¨ãŽã°ãªã—","ãµãã‚“ãŸã˜ãƒ¼","ã¤ã®","ãˆãŒãŠ"], + "👿": ["ã—ょã†ã‚ãã¾","ãŠã«","ã‚ãã¾","ã‹ãŠ","ãŠã¨ãŽã°ãªã—","ãµãã‚“ãŸã˜ãƒ¼"], + "👹": ["ãŠã«","よã†ã‹ã„","ã‹ãŠ","ã‚€ã‹ã—ã°ãªã—","ãµãã‚“ãŸã˜ãƒ¼","ã«ã£ã½ã‚“","ã‚‚ã‚“ã™ãŸãƒ¼"], + "👺": ["ã¦ã‚“ã","よã†ã‹ã„","ã‹ãŠ","ã‚€ã‹ã—ã°ãªã—","ãµãã‚“ãŸã˜ãƒ¼","ã«ã£ã½ã‚“","ã‚‚ã‚“ã™ãŸãƒ¼"], + "💩": ["ã†ã‚“ã¡","ã¾ã‚“ãŒ","ãµã‚“","ã‹ãŠ","ã‚‚ã‚“ã™ãŸãƒ¼"], + "👻": ["ãŠã°ã‘","よã†ã‹ã„","ã‹ãŠ","ãŠã¨ãŽã°ãªã—","ãµãã‚“ãŸã˜ãƒ¼","ゆã†ã‚Œã„","ã‚‚ã‚“ã™ãŸãƒ¼","ã¯ã‚ã†ãƒãƒ¼ã‚“"], + "💀": ["ã©ãã‚","ã‹ã‚‰ã ","ã—","ã‹ãŠ","ãŠã¨ãŽã°ãªã—","ã‚‚ã‚“ã™ãŸãƒ¼","ãŒã„ã“ã¤","ã¯ã‚ã†ãƒãƒ¼ã‚“"], + "☠": ["ã©ãã‚ã¾ãƒ¼ã","ã‹ã‚‰ã ","ã“ã†ã•ã—ãŸã»ã","ã—","ã‹ãŠ","ã‚‚ã‚“ã™ãŸãƒ¼","ãŒã„ã“ã¤","ã¯ã‚ã†ãƒãƒ¼ã‚“"], + "👽": ["ã†ã¡ã‚…ã†ã˜ã‚“","ã‹ã„ã˜ã‚…ã†","ã„ã›ã„ã˜ã‚“","ã‹ãŠ","ãŠã¨ãŽã°ãªã—","ãµãã‚“ãŸã˜ãƒ¼","ã‚‚ã‚“ã™ãŸãƒ¼","ã†ã¡ã‚…ã†","UFO"], + "🤖": ["ã‚ã¼ã£ã¨ã®ã‹ãŠ","ã‹ãŠ","ã‚‚ã‚“ã™ãŸãƒ¼","ã‚ã¼ã£ã¨"], + "🎃": ["ã˜ã‚ƒã£ã・ãŠãƒ»ã‚‰ã‚“ãŸã‚“","ã„ã¹ã‚“ã¨","ãŠã„ã‚ã„","ãˆã‚“ãŸã‚","ã¯ã‚ã†ãƒã‚“","ã˜ã‚ƒã£ããŠã‚‰ã‚“ãŸã‚“","らんãŸã‚“","ã‹ã¼ã¡ã‚ƒ"], + "😺": ["ãã¡ã‚’ã‚ã‘ã¦ã‚らã†ãã“","ãã“","ã‹ãŠ","ãã¡","ã‚ã‘ã‚‹","ãˆãŒãŠ"], + "😸": ["ã«ã‚„ã«ã‚„ã‚らã†ãã“","ãã“","ã‚","ã‹ãŠ","ã«ã‚„ã«ã‚„","ãˆãŒãŠ"], + "😹": ["ã†ã‚Œã—ãªãã—ãŸãã“ã®ã‹ãŠ","ãã“","ã‹ãŠ","ã†ã‚Œã—ã„","ãªã¿ã "], + "😻": ["ã¯ãƒ¼ã¨ã®ã‚ã‚’ã—ãŸãã“ã®ãˆãŒãŠ","ãã“","ã‚","ã‹ãŠ","ã¯ãƒ¼ã¨","ã‚ã„","ãˆãŒãŠ"], + "😼": ["ã«ã‚„ã‚Šã¨ã‚らã†ãã“ã®ã‹ãŠ","ãã“","ã‹ãŠ","ã²ã«ã","ãˆãŒãŠ","ã«ã‚„ã‚Š"], + "😽": ["ã‚ã‚’ã¨ã˜ã¦ãã™ã‚’ã™ã‚‹ãã“","ãã“","ã‚","ã‹ãŠ","ãã™"], + "🙀": ["ã¤ã‹ã‚ŒãŸãã“ã®ã‹ãŠ","ãã“","ã‹ãŠ","ã³ã£ãã‚Š","ãŠã©ã‚ã","ã†ã‚“ã–ã‚Š"], + "😿": ["ãªã„ãŸãã“ã®ã‹ãŠ","ãã“","ãªã","ã‹ãŠ","ã‹ãªã—ã„","ãªã¿ã "], + "😾": ["ãŠã“ã£ãŸãã“ã®ã‹ãŠ","ãã“","ã‹ãŠ","ãŠã“ã‚‹","ãµãã‚Œã£ã¤ã‚‰"], + "🫶": ["ã¯ãƒ¼ã¨ã½ãƒ¼ãš","ã‚ã„"], + "ðŸ‘": ["ã²ã‚‰ã„ãŸã¦","ã‹ã‚‰ã ","ã¦","ã²ã‚ã’ã‚‹"], + "🤲": ["ã†ãˆã«ã‚€ã‘ãŸã‚Šã‚‡ã†ã¦ã®ã²ã‚‰","ã‹ã‚‰ã ","ã„ã®ã‚Š","ã‹ã£ã·ã®ã‚ˆã†ã«ã¾ã‚‹ã‚ãŸã¦"], + "🙌": ["りょã†ã¦ã‚’ã‚ã’ã‚‹","ã‹ã‚‰ã ","ãŠã„ã‚ã„","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã¦","ã°ã‚“ã–ã„","ã‚ã’ã‚‹"], + "ðŸ‘": ["ã¯ãã—ã‚…","ã‹ã‚‰ã ","ã¦ã‚’ãŸãŸã","ã¦"], + "ðŸ™": ["ã«ãŽã£ãŸã¦","ãŸã®ã‚€","ã‹ã‚‰ã ","ãŠã˜ãŽ","ã¦ã‚’ã‚ã‚ã›ã‚‹","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã¦","ãŠããŒã„","ã„ã®ã‚‹","ã‚ã‚ŠãŒã¨ã†","ã‹ã‚“ã—ゃ"], + "ðŸ¤": ["ã‚ãã—ã‚…","ã”ã†ã„","ã¦","ã—ã‚…ã‚’ã‚€ã™ã¶","ã‹ã„ãŽ"], + "ðŸ‘": ["ã„ã„ã","ã‹ã‚‰ã ","ã†ãˆ","ã¦","ゆã³","ã•ã‚€ãšã‚ã£ã·","+1"], + "👎": ["ã ã‚","ã‹ã‚‰ã ","ã—ãŸ","ã¦","ゆã³","ã•ã‚€ãšã ã†ã‚“","-1"], + "👊": ["ã«ãŽã‚Šã“ã¶ã—","ã‹ã‚‰ã ","ã«ãŽã‚‹","ã“ã¶ã—","ãー","ã¦","ã±ã‚“ã¡","ã›ã£ãã‚“"], + "✊": ["ã“ã¶ã—","ã‹ã‚‰ã ","ã«ãŽã‚‹","ãー","ã¦","ã±ã‚“ã¡"], + "🤛": ["ã²ã ã‚Šã‚€ãã®ã“ã¶ã—","ã‹ã‚‰ã ","ã“ã¶ã—","ã²ã ã‚Šã‚€ã"], + "🤜": ["ã¿ãŽã‚€ãã®ã“ã¶ã—","ã‹ã‚‰ã ","ã“ã¶ã—","ã¿ãŽã‚€ã"], + "🤞": ["ã“ã†ã•ã•ã›ãŸã‚†ã³","ã‹ã‚‰ã ","ã“ã†ã•","ゆã³","ã¦","ã“ã†ã†ã‚“"], + "✌": ["Vã•ã„ã‚“","ã‹ã‚‰ã ","ã¦","V","ã¶ã„","ã‹ã¤","ã—ょã†ã‚Š","ã´ãƒ¼ã™"], + "🫰": ["ã²ã¨ã•ã—ゆã³ã¨ãŠã‚„ゆã³ã‚’ã“ã†ã•ã—ãŸã¦","ãŸã‹ã„","ã¯ãƒ¼ã¨","ã‚ã„","ãŠã‹ã","ã™ãªã£ã·"], + "🤘": ["ã“ã‚‹ãª","ã‹ã‚‰ã ","ゆã³","ã¦","ã¤ã®","ã•ã„ã“ã†"], + "🤟": ["ã‚ã„ã—ã¦ã‚‹ã®ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã‹ã‚‰ã ","ã‚ã„ã—ã¦ã‚‹","ã™ã","ã¦"], + "👌": ["OKã•ã„ã‚“","ã‹ã‚‰ã ","ã¦","OK"], + "🤌": ["ã¤ã¾ã‚“ã§ã„るゆã³","ゆã³","ã¦ã¶ã‚Š","ã˜ã‚“ã‚‚ã‚“","ã¤ã¾ã‚€","ã²ã«ã"], + "ðŸ¤": ["ã¤ã¾ã‚“ã§ã„ã‚‹ã¦","ã‹ã‚‰ã ","ã¦","ã¡ã„ã•ã„","ã“ãŒãŸ","ã¡ã£ã¡ã‚ƒã„"], + "👈": ["ã²ã りゆã³ã•ã—","ã¦ã®ã“ã†","ã‹ã‚‰ã ","ゆã³","ã¦","ã²ã¨ã•ã—ゆã³","ゆã³ã•ã™"], + "🫳": ["ã¦ã®ã²ã‚‰ã‚’ã—ãŸã«ã—ãŸã¦","ã—ã‚Šãžã‘ã‚‹","ãŠã¨ã™","ã—ã£ã—"], + "🫴": ["ã¦ã®ã²ã‚‰ã‚’ã†ãˆã«ã—ãŸã¦","ã¦ã¾ãã","ã»ã‹ã","ãã‚‹","ã‚‚ã†ã—ã§"], + "👉": ["ゆã³ã•ã—","ã¦ã®ã“ã†","ã‹ã‚‰ã ","ゆã³","ã¦","ã²ã¨ã•ã—ゆã³","ゆã³ã•ã™"], + "👆": ["ゆã³ã•ã—","ã¦ã®ã“ã†","ã‹ã‚‰ã ","ゆã³","ã¦","ã²ã¨ã•ã—ゆã³","ゆã³ã•ã™","ã†ãˆ"], + "👇": ["ゆã³ã•ã—","ã¦ã®ã“ã†","ã‹ã‚‰ã ","ã—ãŸ","ゆã³","ã¦","ã²ã¨ã•ã—ゆã³","ゆã³ã•ã™"], + "â˜": ["ゆã³ã•ã—","ã‹ã‚‰ã ","ゆã³","ã¦","ã²ã¨ã•ã—ゆã³","ゆã³ã•ã™","ã†ãˆ"], + "✋": ["ãょã—ã‚…","ã‹ã‚‰ã ","ã¦"], + "🤚": ["ã¦ã®ã“ã†","ã‹ã‚‰ã ","ã‚ã’ã‚‹"], + "ðŸ–": ["ã²ã‚ã’ãŸã¦ã®ã²ã‚‰","ã‹ã‚‰ã ","ゆã³","ã¦","ã²ã‚ã’ã‚‹"], + "🖖": ["ã¡ã‚‡ã†ã˜ã‚…ã¨ã¯ã‚“ãˆã„ã‚’","ã‹ã‚‰ã ","ゆã³","ã¦","ã™ã½ã£ã","ã°ã‚‹ã‹ã‚“"], + "👋": ["ã°ã„ã°ã„","ã‹ã‚‰ã ","ã¦","ãµã‚‹","ã‚„ã£ã»ãƒ¼","ã“ã‚“ã«ã¡ã¯"], + "🤙": ["ã§ã‚“ã‚ã®ã‹ãŸã¡ã®ã¦","ã‹ã‚‰ã ","ã§ã‚“ã‚","ã¦"], + "🫲": ["ã²ã ã‚Šã¦","ã¦","ã²ã ã‚Š"], + "🫱": ["ã¿ãŽã¦","ã¦","ã¿ãŽ"], + "🫷": ["ã²ã ã‚Šã‚’ãŠã—ã¦ã„ã‚‹ã¦","ã˜ãŸã„","ã¯ã„ãŸã£ã¡","ã²ã ã‚Šã»ã†ã“ã†","ãŠã—ã¤ã‘ã‚‹","ã“ã¨ã‚ã‚‹","ã¦ã„ã—","ã¾ã¤"], + "🫸": ["ã¿ãŽã‚’ãŠã—ã¦ã„ã‚‹ã¦","ã˜ãŸã„","ã¯ã„ãŸã£ã¡","ãŠã—ã¤ã‘ã‚‹","ã“ã¨ã‚ã‚‹","ã¿ãŽã»ã†ã“ã†","ã¦ã„ã—","ã¾ã¤"], + "💪": ["ã¾ã’ãŸã˜ã‚‡ã†ã‚ã‚“ã«ã¨ã†ãã‚“","ã¡ã‹ã‚‰ã“ã¶","ã‹ã‚‰ã ","ã¾ã‚“ãŒ","ã†ã‚“ã©ã†","ãã‚“ã«ã","ã¡ã‹ã‚‰","ã¾ã£ã™ã‚‹","ã¾ã£ã¡ã‚‡"], + "🦾": ["ã‚ã‹ã«ã‹ã‚‹ã‚ーむ","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ãŽã—ã‚…","ã˜ã‚“ã“ã†ãã†ã","ã‹ã‚‰ã "], + "🖕": ["ãªã‹ã‚†ã³ã‚’ãŸã¦ãŸã¦","ã‹ã‚‰ã ","ゆã³","ã¦","ãªã‹ã‚†ã³"], + "🫵": ["ã¿ã¦ã„ã‚‹ã²ã¨ã‚’ã•ã—ã¦ã„ã‚‹ã²ã¨ã•ã—ゆã³","ã•ã™","ã‚ãªãŸ","ゆã³"], + "âœ": ["ã‹ã„ã¦ã„ã‚‹ã¦","ã‹ã‚‰ã ","ã¦","ã‹ã"], + "🤳": ["ã˜ã©ã‚Š","ã‹ã‚ら","ã‘ã„ãŸã„","ã†ã§"], + "💅": ["ã¾ã«ãã‚…ã‚","ã‹ã‚‰ã ","ã‘ã‚","ã‘ã—ょã†ã²ã‚“","ã“ã™ã‚","ã¤ã‚","ãã„ã‚‹"], + "🦵": ["ã‚ã—","ã‹ã‚‰ã ","ãã£ã","ã¦ã‚ã—"], + "🦿": ["ãã‹ã„ã®ã‚ã—","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ãŽãã","ã˜ã‚“ã“ã†ãã†ã","ã‹ã‚‰ã "], + "🦶": ["ã‚ã—","ã‹ã‚‰ã ","ãã£ã","ãµã¿ã¤ã‘ã‚‹"], + "👄": ["ãã¡","ã‹ã‚‰ã ","ãã¡ã³ã‚‹"], + "🫦": ["ã‹ã‚“ã§ã„ã‚‹ãã¡ã³ã‚‹","ã—ã‚“ã±ã„","ã“ã‚ã„","ã†ã‚ã","ã—ã‚“ã‘ã„ã—ã¤","ãµã‚†ã‹ã„","ãµã‚ã‚“"], + "🦷": ["ã¯","ã‹ã‚‰ã ","ã¯ã„ã—ゃ"], + "👅": ["ã—ãŸ","ã‹ã‚‰ã "], + "👂": ["ã¿ã¿","ã‹ã‚‰ã ","ã¯ãª"], + "🦻": ["ã»ã¡ã‚‡ã†ãã‚’ã¤ã‘ã¦ã„ã‚‹ã¿ã¿","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã»ã¡ã‚‡ã†ã","ãã","ã‹ã‚‰ã ","ã¿ã¿"], + "👃": ["ã¯ãª","ã‹ã‚‰ã "], + "ðŸ‘": ["ã‚","ã‹ã‚‰ã "], + "👀": ["ã‚","ã‹ã‚‰ã ","ã‹ãŠ"], + "🧠": ["ã®ã†","ã‹ã‚‰ã ","ãžã†ã","ã¡ã¦ã","ã‹ã—ã“ã„"], + "🫀": ["ã‹ã„ã¼ã†ãŒãã¦ããªã—ã‚“ãžã†","ã‹ã„ã¼ã†ãŒã","ã—ã‚“ãžã†ãŒã","ã—ã‚“ãžã†","ãžã†ã","ã¿ã‚ƒã"], + "ðŸ«": ["ã¯ã„","ã„ã","ã“ã","ãã‚…ã†ã«ã‚…ã†","ãžã†ã","ã“ãã‚…ã†"], + "🦴": ["ã»ã","ã‹ã‚‰ã ","ã“ã£ã‹ã"], + "👤": ["ã˜ã‚‡ã†ã¯ã‚“ã—ã‚“ã®ã—ã‚‹ãˆã£ã¨","ã˜ã‚‡ã†ã¯ã‚“ã—ã‚“","ã—ã‚‹ãˆã£ã¨"], + "👥": ["ã˜ã‚‡ã†ã¯ã‚“ã—ã‚“ã®ã—ã‚‹ãˆã£ã¨","ã˜ã‚‡ã†ã¯ã‚“ã—ã‚“","ã—ã‚‹ãˆã£ã¨"], + "🗣": ["ã—ゃã¹ã‚‹ã‚ãŸã¾ã®ã—ã‚‹ãˆã£ã¨","ã‹ãŠ","ã‚ãŸã¾","ã—ã‚‹ãˆã£ã¨","ã—ゃã¹ã‚‹","ã¯ãªã™"], + "🫂": ["ã¯ãã—ã¦ã„ã‚‹ã²ã¨ãŸã¡","ã•ã‚ˆã†ãªã‚‰","ã“ã‚“ã«ã¡ã¯","ã¯ã","ã‚ã‚ŠãŒã¨ã†"], + "👶": ["ã‚ã‹ã¡ã‚ƒã‚“"], + "👧": ["ãŠã‚“ãªã®ã“","ã—ょã†ã˜ã‚‡","ã—ょã˜ã‚‡","ãŠã¨ã‚ã–","ã›ã„ã–","ã“ã©ã‚‚"], + "🧒": ["ã“ã©ã‚‚","ã²ã¨","ã—ょã†ãã‚“","ã—ょã†ã˜ã‚‡"], + "👦": ["ãŠã¨ã“ã®ã“","ã—ょã†ãã‚“","ã“ã©ã‚‚"], + "👩": ["ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑": ["ã›ã„ã˜ã‚“ã‚€ã‘","ã²ã¨","ãŠã¨ãª","ã ã‚“ã›ã„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª","ãŠã¨ã“"], + "👨": ["ã ã‚“ã›ã„","ãã¡ã²ã’","ãŠã¨ã“"], + "👩â€ðŸ¦±": ["ã˜ã‚‡ã›ã„","ã¾ãã’","ã‹ã¿","ãŠã‚“ãª"], + "🧑â€ðŸ¦±": ["ã²ã¨","ã¾ãã’","ã‹ã¿"], + "👨â€ðŸ¦±": ["ã ã‚“ã›ã„","ã¾ãã’","ã‹ã¿","ãŠã¨ã“"], + "👩â€ðŸ¦°": ["ã˜ã‚‡ã›ã„","ã‚ã‹ã’","ã‚ã‹","ã‹ã¿","ãŠã‚“ãª"], + "🧑â€ðŸ¦°": ["ã²ã¨","ã‚ã‹ã’","ã‚ã‹","ã‹ã¿"], + "👨â€ðŸ¦°": ["ã ã‚“ã›ã„","ã‚ã‹ã’","ã‚ã‹","ã‹ã¿","ãŠã¨ã“"], + "👱â€â™€ï¸": ["ã˜ã‚‡ã›ã„","ãã‚“ã±ã¤","ã¶ã‚ã‚“ã©","ã‹ã¿","ãŠã‚“ãª"], + "👱": ["ã²ã¨","ãã‚“ã±ã¤","ã¶ã‚ã‚“ã©","ã‹ã¿"], + "👱â€â™‚ï¸": ["ã ã‚“ã›ã„","ãã‚“ã±ã¤","ã¶ã‚ã‚“ã©","ã‹ã¿","ãŠã¨ã“"], + "👩â€ðŸ¦³": ["ã˜ã‚‡ã›ã„","ã¯ãã¯ã¤","ã—ã‚","ã‹ã¿","ãŠã‚“ãª"], + "🧑â€ðŸ¦³": ["ã²ã¨","ã¯ãã¯ã¤","ã—ã‚","ã‹ã¿"], + "👨â€ðŸ¦³": ["ã ã‚“ã›ã„","ã¯ãã¯ã¤","ã—ã‚","ã‹ã¿","ãŠã¨ã“"], + "👩â€ðŸ¦²": ["ã˜ã‚‡ã›ã„","ã¯ã’","ãŠã‚“ãª"], + "🧑â€ðŸ¦²": ["ã²ã¨","ã¯ã’"], + "👨â€ðŸ¦²": ["ã ã‚“ã›ã„","ã¯ã’","ãŠã¨ã“"], + "🧔â€â™€ï¸": ["ã²ã’ã®ã‚ã‚‹ã˜ã‚‡ã›ã„","ã‚ã”ã²ã’","ã²ã’ã‚’ã¯ã‚„ã—ãŸ","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧔": ["ã‚ã”ã²ã’ã®ã‚ã‚‹ã²ã¨","ã‚ã”ã²ã’","ã²ã’ã‚’ã¯ã‚„ã—ãŸ"], + "🧔â€â™‚ï¸": ["ã²ã’ã®ã‚ã‚‹ã ã‚“ã›ã„","ã‚ã”ã²ã’","ã²ã’ã‚’ã¯ã‚„ã—ãŸ","ã ã‚“ã›ã„","ãŠã¨ã“"], + "👵": ["ãŠã°ã‚ã•ã‚“","ãŠã°ã‚ã¡ã‚ƒã‚“","ã‚ã†ã˜ã‚“","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧓": ["ã“ã†ã‚Œã„ã—ゃ","ã²ã¨","ã ã‚“ã›ã„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª","ãŠã¨ã“"], + "👴": ["ãŠã˜ã„ã•ã‚“","ãŠã˜ã„ã¡ã‚ƒã‚“","ã‚ã†ã˜ã‚“","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👲": ["ã™ã‹ã‚‹ãゃã£ã·ã‚’ã‹ã¶ã£ã¦ã„ã‚‹ã²ã¨","ã¡ã‚…ã†ã”ãã¼ã†","ã¼ã†ã—"], + "👳â€â™€ï¸": ["ãŸãƒ¼ã°ã‚“ã‚’ã¾ã„ã¦ã„ã‚‹ã˜ã‚‡ã›ã„","ãŸãƒ¼ã°ã‚“","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "👳": ["ãŸãƒ¼ã°ã‚“ã‚’ã¾ã„ã¦ã„ã‚‹ã²ã¨","ãŸãƒ¼ã°ã‚“"], + "👳â€â™‚ï¸": ["ãŸãƒ¼ã°ã‚“ã‚’ã¾ã„ã¦ã„ã‚‹ã ã‚“ã›ã„","ãŸãƒ¼ã°ã‚“","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🧕": ["ã¸ã£ã©ã™ã‹ãƒ¼ãµã‚’ã‹ã¶ã£ãŸã˜ã‚‡ã›ã„","ã¸ã£ã©ã™ã‹ãƒ¼ãµ","ã²ã˜ã‚ƒã¶","ã¾ã‚“ã¦ãƒã‚‰","ã¦ãƒã¡ã‡ã‚‹","ã°ã‚“ã ãª","ã‚ãŸã¾ã®ã™ã‹ãƒ¼ãµ","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "👮â€â™€ï¸": ["ã˜ã‚‡ã›ã„ã‘ã„ã•ã¤ã‹ã‚“","ã‘ã„ã•ã¤ã‹ã‚“","ã‘ã„ã‹ã‚“","ã‘ã„ã•ã¤","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "👮": ["ã‘ã„ã•ã¤ã‹ã‚“","ã‘ã„ã‹ã‚“","ã‘ã„ã•ã¤"], + "👮â€â™‚ï¸": ["ã ã‚“ã›ã„ã‘ã„ã•ã¤ã‹ã‚“","ã‘ã„ã•ã¤ã‹ã‚“","ã‘ã„ã‹ã‚“","ã‘ã„ã•ã¤","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€ðŸš’": ["ã˜ã‚‡ã›ã„ã—ょã†ã¼ã†ã—","ã²","ã‹ã˜","ã—ょã†ã¼ã†","ã—ょã†ã¼ã†ã—","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸš’": ["ã—ょã†ã¼ã†ã—","ã‹ã˜"], + "👨â€ðŸš’": ["ã ã‚“ã›ã„ã—ょã†ã¼ã†ã—","ã²","ã‹ã˜","ã—ょã†ã¼ã†","ã—ょã†ã¼ã†ã—","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👷â€â™€ï¸": ["ã˜ã‚‡ã›ã„ã®ã‘ã‚“ã›ã¤ã•ãŽã‚‡ã†ã„ã‚“","ã“ã†ã˜","ã‘ã‚“ã›ã¤","ã•ãŽã‚‡ã†ã„ã‚“","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "👷": ["ã‘ã‚“ã›ã¤ã•ãŽã‚‡ã†ã„ã‚“","ã“ã†ã˜","ã‘ã‚“ã›ã¤","ã•ãŽã‚‡ã†ã„ã‚“"], + "👷â€â™‚ï¸": ["ã ã‚“ã›ã„ã®ã‘ã‚“ã›ã¤ã•ãŽã‚‡ã†ã„ã‚“","ã‘ã‚“ã›ã¤","ã•ãŽã‚‡ã†ã„ã‚“","ã ã‚“ã›ã„","ãŠã¨ã“"], + "👩â€ðŸ": ["ã ã‚“ã›ã„ã®ã“ã†ã˜ã‚‡ã†ã•ãŽã‚‡ã†ã„ã‚“","ã“ã†ã˜ã‚‡ã†","ã“ã†ãŽã‚‡ã†","ã•ãŽã‚‡ã†ã„ã‚“","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸ": ["ã“ã†ã˜ã‚‡ã†ã•ãŽã‚‡ã†ã„ã‚“","ã“ã†ã˜ã‚‡ã†","ã“ã†ãŽã‚‡ã†","よã†ã›ã¤"], + "👨â€ðŸ": ["ã ã‚“ã›ã„ã®ã“ã†ã˜ã‚‡ã†ã•ãŽã‚‡ã†ã„ã‚“","ã“ã†ã˜ã‚‡ã†","ã“ã†ãŽã‚‡ã†","ã•ãŽã‚‡ã†ã„ã‚“","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€ðŸ”§": ["ã˜ã‚‡ã›ã„ã›ã„ã³ã—","ã—ょãã«ã‚“","ã¯ã„ã‹ã‚“ã“ã†","ã§ã‚“ããŽã—","ã—ã‚…ã†ã‚Šã«ã‚“","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸ”§": ["ã›ã„ã³ã—","ã—ょãã«ã‚“","ã¯ã„ã‹ã‚“ã“ã†","ã§ã‚“ããŽã—","ã—ã‚…ã†ã‚Šã˜ã‚“"], + "👨â€ðŸ”§": ["ã ã‚“ã›ã„ã›ã„ã³ã—","ã—ょãã«ã‚“","ã¯ã„ã‹ã‚“ã“ã†","ã§ã‚“ããŽã—","ã—ã‚…ã†ã‚Šã˜ã‚“","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€ðŸŒ¾": ["ã˜ã‚‡ã›ã„ã®ã®ã†ãŽã‚‡ã†ã˜ã‚…ã†ã˜ã—ゃ","ã®ã†ã˜ã‚‡ã†ã‚ã†ã©ã†ã—ゃ","ã¼ãã˜ã‚‡ã†ã¬ã—","ã«ã‚ã—","ã®ã†ã‹","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸŒ¾": ["ã®ã†ãŽã‚‡ã†ã˜ã‚…ã†ã˜ã—ゃ","ã®ã†ã˜ã‚‡ã†ã‚ã†ã©ã†ã—ゃ","ã¼ãã˜ã‚‡ã†ã¬ã—","ã«ã‚ã—","ã®ã†ã‹"], + "👨â€ðŸŒ¾": ["ã ã‚“ã›ã„ã®ã®ã†ãŽã‚‡ã†ã˜ã‚…ã†ã˜ã—ゃ","ã®ã†ã˜ã‚‡ã†ã‚ã†ã©ã†ã—ゃ","ã¼ãã˜ã‚‡ã†ã¬ã—","ã«ã‚ã—","ã®ã†ã‹","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€ðŸ³": ["ã˜ã‚‡ã›ã„ã®ã‚Šã‚‡ã†ã‚Šã«ã‚“","ã—ょãã²ã‚“","ã•ãƒ¼ã³ã™","ã—ã‡ãµ","ã“ã£ã","りょã†ã‚Šã«ã‚“","りょã†ã‚Š","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸ³": ["りょã†ã‚Šã«ã‚“","ã—ょãã²ã‚“","ã•ãƒ¼ã³ã™","ã—ã‡ãµ","ã“ã£ã","りょã†ã‚Š"], + "👨â€ðŸ³": ["ã ã‚“ã›ã„ã®ã‚Šã‚‡ã†ã‚Šã˜ã‚“","ã—ょãã²ã‚“","ã•ãƒ¼ã³ã™","ã—ã‡ãµ","ã“ã£ã","りょã†ã‚Šã«ã‚“","りょã†ã‚Š","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€ðŸŽ¤": ["ã ã‚“ã›ã„ã—ã‚“ãŒãƒ¼","ãŠã‚“ãŒã","ã¿ã‚…ーã˜ã—ゃん","ã‚ã£ã","ã‚ã£ã‹ãƒ¼","ã‚ã£ãã™ãŸãƒ¼","ã’ã„ã®ã†ã˜ã‚“","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸŽ¤": ["ã‹ã—ã‚…","ãŠã‚“ãŒã","ã¿ã‚…ーã˜ã—ゃん","ã‚ã£ã","ã‚ã£ã‹ãƒ¼","ã‚ã£ãã™ãŸãƒ¼","ã’ã„ã®ã†ã˜ã‚“"], + "👨â€ðŸŽ¤": ["ã ã‚“ã›ã„ã—ã‚“ãŒãƒ¼","ãŠã‚“ãŒã","ã¿ã‚…ーã˜ã—ゃん","ã‚ã£ã","ã‚ã£ã‹ãƒ¼","ã‚ã£ãã™ãŸãƒ¼","ã’ã„ã®ã†ã˜ã‚“","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€ðŸŽ¨": ["ã˜ã‚‡ã›ã„ã‚ーã¦ãƒã™ã¨","ã’ã„ã˜ã‚…ã¤","ã‚ーã¨","ã’ã„ã˜ã‚…ã¤ã‹","ã‚ーã¦ãƒã™ã¨","ã‹ã„ãŒ","ãŒã‹","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸŽ¨": ["ã‚ーã¦ãƒã™ã¨","ã’ã„ã˜ã‚…ã¤","ã‚ーã¨","ã’ã„ã˜ã‚…ã¤ã‹","ã‹ã„ãŒ","ãŒã‹"], + "👨â€ðŸŽ¨": ["ã ã‚“ã›ã„ã‚ーã¦ãƒã™ã¨","ã’ã„ã˜ã‚…ã¤","ã‚ーã¨","ã’ã„ã˜ã‚…ã¤ã‹","ã‚ーã¦ãƒã™ã¨","ã‹ã„ãŒ","ãŒã‹","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€ðŸ«": ["ã˜ã‚‡ã›ã„ã®ãょã†ã—","ãょã†ã„ã","ã›ã‚“ã›ã„","ãょã†ã˜ã‚…","ãょã†ã—","ã“ã†ã—","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸ«": ["ãょã†ã—","ãょã†ã„ã","ã›ã‚“ã›ã„","ãょã†ã˜ã‚…","ã“ã†ã—"], + "👨â€ðŸ«": ["ã ã‚“ã›ã„ã®ãょã†ã—","ãょã†ã„ã","ã›ã‚“ã›ã„","ãょã†ã˜ã‚…","ãょã†ã—","ã“ã†ã—","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€ðŸŽ“": ["ã˜ã‚‡ã—ã›ã„ã¨","ãŒãã›ã„","ãã¤ãŽã‚‡ã†ã›ã„","ãょã†ã„ã","ãŒã£ã“ã†","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸŽ“": ["ã›ã„ã¨","ãŒãã›ã„","ãã¤ãŽã‚‡ã†ã›ã„","ãょã†ã„ã","ãŒã£ã“ã†"], + "👨â€ðŸŽ“": ["ã ã‚“ã—ã›ã„ã¨","ãŒãã›ã„","ãã¤ãŽã‚‡ã†ã›ã„","ãょã†ã„ã","ãŒã£ã“ã†","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€ðŸ’¼": ["ã ã‚“ã›ã„ã‹ã„ã—ゃã„ã‚“","ãŠãµãƒã™","ã‹ã„ã‘ã„ã—","ãŽã‚“ã“ã†ã‹","ã‹ã‚“ã‚Šã—ょã","ã“ã‚‚ã‚“","ã˜ã‚€ã„ã‚“","ã‚ãªã‚Šã™ã¨","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸ’¼": ["ã‹ã„ã—ゃã„ã‚“","ãŠãµãƒã™","ã‹ã„ã‘ã„ã—","ãŽã‚“ã“ã†ã‹","ã‹ã‚“ã‚Šã—ょã","ã“ã‚‚ã‚“","ã˜ã‚€ã„ã‚“","ã‚ãªã‚Šã™ã¨"], + "👨â€ðŸ’¼": ["ã ã‚“ã›ã„ã‹ã„ã—ゃã„ã‚“","ãŠãµãƒã™","ã‹ã„ã‘ã„ã—","ãŽã‚“ã“ã†ã‹","ã‹ã‚“ã‚Šã—ょã","ã“ã‚‚ã‚“","ã˜ã‚€ã„ã‚“","ã‚ãªã‚Šã™ã¨","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€ðŸ’»": ["ã˜ã‚‡ã›ã„ãŽã˜ã‚…ã¤ã—ゃ","ã¦ãã®ã‚ã˜ãƒ¼","ããµã¨ã†ã‡ã‚","ãˆã‚“ã˜ã«ã‚","ã·ã‚ãらã¾ãƒ¼","らã£ã·ã¨ã£ã·","ã®ãƒ¼ã¨ã±ãã“ã‚“","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸ’»": ["ãŽã˜ã‚…ã¤ã—ゃ","ã¦ãã®ã‚ã˜ãƒ¼","ããµã¨ã†ã‡ã‚","ãˆã‚“ã˜ã«ã‚","ã·ã‚ãらã¾ãƒ¼","らã£ã·ã¨ã£ã·","ã®ãƒ¼ã¨ã±ãã“ã‚“"], + "👨â€ðŸ’»": ["ã ã‚“ã›ã„ãŽã˜ã‚…ã¤ã—ゃ","ã¦ãã®ã‚ã˜ãƒ¼","ããµã¨ã†ã‡ã‚","ãˆã‚“ã˜ã«ã‚","ã·ã‚ãらã¾ãƒ¼","らã£ã·ã¨ã£ã·","ã®ãƒ¼ã¨ã±ãã“ã‚“","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€ðŸ”¬": ["ã˜ã‚‡ã›ã„ã‹ãŒãã—ゃ","ã‹ãŒãã—ゃ","ãŽã˜ã‚…ã¤ã—ゃ","ã™ã†ãŒãã—ゃ","ã¶ã¤ã‚ŠãŒãã—ゃ","ã›ã„ã¶ã¤ãŒãã—ゃ","ã‘ã‚“ã•ãŽã—","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸ”¬": ["ã‹ãŒãã—ゃ","ãŽã˜ã‚…ã¤ã—ゃ","ã™ã†ãŒãã—ゃ","ã¶ã¤ã‚ŠãŒãã—ゃ","ã›ã„ã¶ã¤ãŒãã—ゃ","ã‘ã‚“ã•ãŽã—"], + "👨â€ðŸ”¬": ["ã ã‚“ã›ã„ã‹ãŒãã—ゃ","ã‹ãŒãã—ゃ","ãŽã˜ã‚…ã¤ã—ゃ","ã™ã†ãŒãã—ゃ","ã¶ã¤ã‚ŠãŒãã—ゃ","ã›ã„ã¶ã¤ãŒãã—ゃ","ã‘ã‚“ã•ãŽã—","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€ðŸš€": ["ã˜ã‚‡ã›ã„ã†ã¡ã‚…ã†ã²ã“ã†ã—","ã†ã¡ã‚…ã†","ã»ã—","ã¤ã","ã‚ãã›ã„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸš€": ["ã†ã¡ã‚…ã†ã²ã“ã†ã—","ã†ã¡ã‚…ã†","ã»ã—","ã¤ã","ã‚ãã›ã„"], + "👨â€ðŸš€": ["ã ã‚“ã›ã„ã†ã¡ã‚…ã†ã²ã“ã†ã—","ã†ã¡ã‚…ã†","ã»ã—","ã¤ã","ã‚ãã›ã„","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€âš•ï¸": ["ã˜ã‚‡ã›ã„ã„りょã†ã‹ã‚“ã‘ã„ã—ゃ","ã„ã—","ãªã„ã‹ã„","ã„ãŒãã¯ã‹ã›","ã‹ã‚“ã”ã—","ã—ã‹ã„","ã„りょã†ã›ã‚“ã‚‚ã‚“ã‹","りょã†ã»ã†ã—","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€âš•ï¸": ["ã„りょã†ã‹ã‚“ã‘ã„ã—ゃ","ã„ã—","ãªã„ã‹ã„","ã„ãŒãã¯ã‹ã›","ã‹ã‚“ã”ã—","ã—ã‹ã„","ã„りょã†ã›ã‚“ã‚‚ã‚“ã‹","りょã†ã»ã†ã—"], + "👨â€âš•ï¸": ["ã ã‚“ã›ã„ã„りょã†ã‹ã‚“ã‘ã„ã—ゃ","ã„ã—","ãªã„ã‹ã„","ã„ãŒãã¯ã‹ã›","ã‹ã‚“ã”ã—","ã—ã‹ã„","ã„りょã†ã›ã‚“ã‚‚ã‚“ã‹","りょã†ã»ã†ã—","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€âš–ï¸": ["ã˜ã‚‡ã›ã„ã•ã„ã°ã‚“ã‹ã‚“","ã•ã„ã°ã‚“ã‹ã‚“","ã»ã†ã¦ã„","ã•ã„ã°ã‚“ã—ょ","ã»ã†ã‚Šã¤","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€âš–ï¸": ["ã•ã„ã°ã‚“ã‹ã‚“","ã»ã†ã¦ã„","ã•ã„ã°ã‚“ã—ょ","ã»ã†ã‚Šã¤"], + "👨â€âš–ï¸": ["ã ã‚“ã›ã„ã•ã„ã°ã‚“ã‹ã‚“","ã•ã„ã°ã‚“ã‹ã‚“","ã»ã†ã¦ã„","ã•ã„ã°ã‚“ã—ょ","ã»ã†ã‚Šã¤","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€âœˆï¸": ["ã˜ã‚‡ã›ã„ã±ã„ã‚ã£ã¨","ã±ã„ã‚ã£ã¨","ã²ã“ã†ã","ãã†ã˜ã‚…ã†ã—","ã“ã†ãã†","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€âœˆï¸": ["ã±ã„ã‚ã£ã¨","ã²ã“ã†ã","ãã†ã˜ã‚…ã†ã—","ã“ã†ãã†"], + "👨â€âœˆï¸": ["ã ã‚“ã›ã„ã±ã„ã‚ã£ã¨","ã±ã„ã‚ã£ã¨","ã²ã“ã†ã","ãã†ã˜ã‚…ã†ã—","ã“ã†ãã†","ãŠã¨ã“","ã ã‚“ã›ã„"], + "💂â€â™€ï¸": ["ã˜ã‚‡ã›ã„ã‘ã„ã³ã„ã‚“","ã‘ã„ã³ã„ã‚“","ã‘ã„ã³","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "💂": ["ã‘ã„ã³ã„ã‚“","ã‘ã„ã³"], + "💂â€â™‚ï¸": ["ã ã‚“ã›ã„ã‘ã„ã³ã„ã‚“","ã‘ã„ã³ã„ã‚“","ã‘ã„ã³","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🥷": ["ã«ã‚“ã˜ã‚ƒ","ã›ã‚“ã—","ã‹ãã•ã‚ŒãŸ","ã™ã¦ã‚‹ã™"], + "🕵ï¸â€â™€ï¸": ["ã˜ã‚‡ã›ã„ã®ãŸã‚“ã¦ã„","ãŸã‚“ã¦ã„","ã‘ã„ã˜","ã™ã±ã„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🕵": ["ãŸã‚“ã¦ã„","ã‘ã„ã˜","ã™ã±ã„"], + "🕵ï¸â€â™‚ï¸": ["ã ã‚“ã›ã„ã®ãŸã‚“ã¦ã„","ãŸã‚“ã¦ã„","ã‘ã„ã˜","ã™ã±ã„","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🤶": ["ã¿ã›ã™ãƒ»ãã‚ーã™","ã„ã¹ã‚“ã¨","ãŠã„ã‚ã„","ãã‚Šã™ã¾ã™","ã¯ã¯","ã•ã‚“ãŸ","ãã‚ーã™","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸŽ„": ["ã¿ãã™ãã‚ーã™","ã‚ãã¦ãƒã³ã¦ãƒ","ãŠã„ã‚ã„","ãã‚Šã™ã¾ã™","ã•ã‚“ãŸ","ãã‚ーã™"], + "🎅": ["ã•ã‚“ãŸãã‚ーã™","ã„ã¹ã‚“ã¨","ãŠã„ã‚ã„","ãã‚Šã™ã¾ã™","ã¡ã¡","ã•ã‚“ãŸ","ãã‚ーã™","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👼": ["ã¦ã‚“ã—ã®ã‚ã‹ã¡ã‚ƒã‚“","ã¦ã‚“ã—","ã‚ã‹ã¡ã‚ƒã‚“","ã‹ãŠ","ãŠã¨ãŽã°ãªã—","ãµãã‚“ãŸã˜ãƒ¼"], + "👸": ["ãŠã²ã‚ã•ã¾","ãŠã¨ãŽã°ãªã—","ãµãã‚“ãŸã˜ãƒ¼","ã˜ã‚‡ãŠã†","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🫅": ["ãŠã†ã‹ã‚“ã‚’ã‹ã¶ã£ãŸã²ã¨","ãŠã¨ãŽã°ãªã—","ãµãã‚“ãŸã˜ãƒ¼","ã“ããŠã†","ããžã","ãŠã†","ãŠã†ãžã"], + "🤴": ["ãŠã†ã˜ã•ã¾","ãŠã¨ãŽã°ãªã—","ãµãã‚“ãŸã˜ãƒ¼","ãŠã†","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👰": ["ã¹ãƒ¼ã‚‹ã‚’ã¤ã‘ãŸã˜ã‚‡ã›ã„","ã¯ãªã‚ˆã‚","ã¹ãƒ¼ã‚‹","ã‘ã£ã“ã‚“ã—ã","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "👰â€â™€ï¸": ["ã¹ãƒ¼ã‚‹ã‚’ã¤ã‘ãŸã²ã¨","ã¯ãªã‚ˆã‚","ã¹ãƒ¼ã‚‹","ã‘ã£ã“ã‚“ã—ã"], + "👰â€â™‚ï¸": ["ã¹ãƒ¼ã‚‹ã‚’ã¤ã‘ãŸã ã‚“ã›ã„","ã¯ãªã‚ˆã‚","ã¹ãƒ¼ã‚‹","ã†ã‡ã§ãƒã‚“ã","ã ã‚“ã›ã„","ãŠã¨ã“"], + "🤵â€â™€ï¸": ["ãŸãã—ーã©ã®ã˜ã‚‡ã›ã„","ãŸãã—ーã©","ã†ã‡ã§ãƒã‚“ã","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🤵": ["ãŸãã—ーã©ã‚’ãã‚‹ã²ã¨","ã¯ãªã‚€ã“","ãŸãã—ーã©","ã†ã‡ã§ãƒã‚“ã"], + "🤵â€â™‚ï¸": ["ãŸãã—ーã©ã®ã ã‚“ã›ã„","ã¯ãªã‚€ã“","ãŸãã—ーã©","ã†ã‡ã§ãƒã‚“ã","ã ã‚“ã›ã„","ãŠã¨ã“"], + "🩷": ["ã´ã‚“ãã®ã¯ãƒ¼ã¨","ã‹ã‚ã„ã„","ã¯ãƒ¼ã¨","ã™ã","ã‚ã„","ã´ã‚“ã"], + "🩵": ["らã„ã¨ã¶ã‚‹ãƒ¼ã®ã¯ãƒ¼ã¨","ã—ã‚ã‚“","ã¯ãƒ¼ã¨","らã„ã¨ã¶ã‚‹ãƒ¼","ã“ãŒã‚‚"], + "🩶": ["ãれーã®ã¯ãƒ¼ã¨","ãれー","ã¯ãƒ¼ã¨","ã—ã‚‹ã°ãƒ¼","ã™ã‚Œãƒ¼ã¨"], + "🕴ï¸â€â™€ï¸": ["ã¡ã‚…ã†ã«ã†ã„ãŸã™ãƒ¼ã¤ã®ã˜ã‚‡ã›ã„","ã³ã˜ãã™","ã™ãƒ¼ã¤","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🕴": ["ã¡ã‚…ã†ã«ã†ã„ãŸã™ãƒ¼ã¤ã®ã²ã¨","ã³ã˜ãã™","ã™ãƒ¼ã¤"], + "🕴ï¸â€â™‚ï¸": ["ã¡ã‚…ã†ã«ã†ã„ãŸã™ãƒ¼ã¤ã®ã ã‚“ã›ã„","ã³ã˜ãã™","ã™ãƒ¼ã¤","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🦸â€â™€ï¸": ["ã˜ã‚‡ã›ã„ã®ã™ãƒ¼ã±ãƒ¼ã²ãƒ¼ã‚ー","ãã†ãã†","ãœã‚“","ã²ã‚ã„ã‚“","ã¡ã‚‡ã†ãŸã„ã“ã","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🦸": ["ã™ãƒ¼ã±ãƒ¼ã²ãƒ¼ã‚ー","ãã†ãã†","ãœã‚“","ã²ãƒ¼ã‚ー","ã²ã‚ã„ã‚“","ã¡ã‚‡ã†ãŸã„ã“ã"], + "🦸â€â™‚ï¸": ["ã ã‚“ã›ã„ã®ã™ãƒ¼ã±ãƒ¼ã²ãƒ¼ã‚ー","ãã†ãã†","ãœã‚“","ã²ãƒ¼ã‚ー","ã¡ã‚‡ã†ãŸã„ã“ã","ã ã‚“ã›ã„","ãŠã¨ã“"], + "🦹â€â™€ï¸": ["ã˜ã‚‡ã›ã„ã®ã‚ãã¨ã†","ãã†ãã†","ã‚ã","ã¯ã‚“ã–ã„","ã‚ãã˜","ã¡ã‚‡ã†ãŸã„ã“ã","ã‚ãã‚„ã","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🦹": ["ã‚ãã¨ã†","ãã†ãã†","ã‚ã","ã¯ã‚“ã–ã„","ã‚ãã˜","ã¡ã‚‡ã†ãŸã„ã“ã","ã‚ãã‚„ã"], + "🦹â€â™‚ï¸": ["ã ã‚“ã›ã„ã®ã‚ãã¨ã†","ãã†ãã†","ã‚ã","ã¯ã‚“ã–ã„","ã‚ãã˜","ã¡ã‚‡ã†ãŸã„ã“ã","ã‚ãã‚„ã","ã ã‚“ã›ã„","ãŠã¨ã“"], + "🧙â€â™€ï¸": ["ã˜ã‚‡ã›ã„ã®ã¾ã»ã†ã¤ã‹ã„","ãã†ãã†","ã¾ã˜ã‚‡","ãŠã‚“ãªã®ã¾ã»ã†ã¤ã‹ã„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧙": ["ã¾ã»ã†ã¤ã‹ã„","ãã†ãã†","ã¾ã˜ã‚…ã¤ã—","ãŠã¨ã“ã®ã¾ã»ã†ã¤ã‹ã„"], + "🧙â€â™‚ï¸": ["ã ã‚“ã›ã„ã®ã¾ã»ã†ã¤ã‹ã„","ãã†ãã†","ã¾ã˜ã‚…ã¤ã—","ãŠã¨ã“ã®ã¾ã»ã†ã¤ã‹ã„","ã ã‚“ã›ã„","ãŠã¨ã“"], + "ðŸ§â€â™€ï¸": ["ã˜ã‚‡ã›ã„ã®ã“ã©ã‚‚","ãã†ãã†","ã“ã©ã‚‚","ã•ãã®ã¨ãŒã£ãŸã¿ã¿","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "ðŸ§": ["ã“ã©ã‚‚","ãã†ãã†","ã•ãã®ã¨ãŒã£ãŸã¿ã¿"], + "ðŸ§â€â™‚ï¸": ["ã ã‚“ã›ã„ã®ã“ã©ã‚‚","ãã†ãã†","ã“ã©ã‚‚","ã•ãã®ã¨ãŒã£ãŸã¿ã¿","ã ã‚“ã›ã„","ãŠã¨ã“"], + "🧚â€â™€ï¸": ["ã˜ã‚‡ã›ã„ã®ã‚ˆã†ã›ã„","ãã†ãã†","ã¦ãƒãŸãƒ¼ã«ã‚","ã†ãƒã‚“ãã™","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧚": ["よã†ã›ã„","ãã†ãã†","ã¦ãƒãŸãƒ¼ã«ã‚","ã†ãƒã‚“ãã™"], + "🧚â€â™‚ï¸": ["ã ã‚“ã›ã„ã®ã‚ˆã†ã›ã„","ãã†ãã†","ãŠã¹ã‚ã‚“","ã—ょã†ã‚ˆã†ã›ã„","ã ã‚“ã›ã„","ãŠã¨ã“"], + "🧞â€â™€ï¸": ["ã˜ã‚‡ã›ã„ã®ã›ã„ã‚Œã„","ãã†ãã†","ã›ã„ã‚Œã„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧞": ["ã›ã„ã‚Œã„","ãã†ãã†"], + "🧞â€â™‚ï¸": ["ã ã‚“ã›ã„ã®ã›ã„ã‚Œã„","ãã†ãã†","ã›ã„ã‚Œã„","ã ã‚“ã›ã„","ãŠã¨ã“"], + "🧜â€â™€ï¸": ["ã˜ã‚‡ã›ã„ã®ã«ã‚“ãŽã‚‡","ãã†ãã†","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧜": ["ã«ã‚“ãŽã‚‡","ãã†ãã†"], + "🧜â€â™‚ï¸": ["ã ã‚“ã›ã„ã®ã«ã‚“ãŽã‚‡","ãã†ãã†","ã«ã‚“ãŽã‚‡","ã ã‚“ã›ã„","ãŠã¨ã“"], + "🧌": ["ã¤ã‚Š","ãŠã¨ãŽã°ãªã—","ãµãã‚“ãŸã˜","ã‚‚ã‚“ã™ãŸãƒ¼"], + "🧛â€â™€ï¸": ["ã˜ã‚‡ã›ã„ã®ãã‚…ã†ã‘ã¤ã","ãã†ãã†","ã‚ã‚“ã§ã£ã©","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧛": ["ãã‚…ã†ã‘ã¤ã","ãã†ãã†","ã©ã‚‰ãゅら","ã‚ã‚“ã§ã£ã©"], + "🧛â€â™‚ï¸": ["ã ã‚“ã›ã„ã®ãã‚…ã†ã‘ã¤ã","ãã†ãã†","ã©ã‚‰ãゅら","ã‚ã‚“ã§ã£ã©","ã ã‚“ã›ã„","ãŠã¨ã“"], + "🧟â€â™€ï¸": ["ã˜ã‚‡ã›ã„ã®ãžã‚“ã³","ãã†ãã†","ã‚ã‚“ã§ã£ã©","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧟": ["ãžã‚“ã³","ãã†ãã†","ã‚ã‚“ã§ã£ã©"], + "🧟â€â™‚ï¸": ["ã ã‚“ã›ã„ã®ãžã‚“ã³","ãã†ãã†","ã‚ã‚“ã§ã£ã©","ã ã‚“ã›ã„","ãŠã¨ã“"], + "🙇â€â™€ï¸": ["ãµã‹ããŠã˜ãŽã™ã‚‹ã˜ã‚‡ã›ã„","ã—ゃã–ã„","ãŠã˜ãŽ","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã”ã‚ã‚“ãªã•ã„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🙇": ["ãµã‹ããŠã˜ãŽã—ãŸã²ã¨","ã—ゃã–ã„","ãŠã˜ãŽ","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã”ã‚ã‚“ãªã•ã„"], + "🙇â€â™‚ï¸": ["ãµã‹ããŠã˜ãŽã™ã‚‹ã ã‚“ã›ã„","ã—ゃã–ã„","ãŠã˜ãŽ","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã”ã‚ã‚“ãªã•ã„","ãŠã¨ã“","ã ã‚“ã›ã„"], + "ðŸ’â€â™€ï¸": ["ã‚ã‚“ãªã„ã™ã‚‹ã˜ã‚‡ã›ã„","ã¦","ãŸã™ã‘","ã˜ã‚‡ã†ã»ã†","ãšã†ãšã†ã—ã„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "ðŸ’": ["ã‚ã‚“ãªã„ã™ã‚‹ã²ã¨","ã¦","ãŸã™ã‘","ã˜ã‚‡ã†ã»ã†","ãšã†ãšã†ã—ã„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "ðŸ’â€â™‚ï¸": ["ã‚ã‚“ãªã„ã™ã‚‹ã ã‚“ã›ã„","ã¦","ãŸã™ã‘","ã˜ã‚‡ã†ã»ã†","ãšã†ãšã†ã—ã„","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🙅â€â™€ï¸": ["NGã•ã„ã‚“ã®ã˜ã‚‡ã›ã„","ãã‚“ã˜ã‚‹","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã¦","ã ã‚","ãã‚“ã—","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🙅": ["NGã•ã„ã‚“ã®ã²ã¨","ãã‚“ã˜ã‚‹","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã¦","ã ã‚","ãã‚“ã—"], + "🙅â€â™‚ï¸": ["NGã•ã„ã‚“ã®ã ã‚“ã›ã„","ãã‚“ã˜ã‚‹","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã¦","ã ã‚","ãã‚“ã—","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🙆â€â™€ï¸": ["OKã•ã„ã‚“ã®ã˜ã‚‡ã›ã„","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã¦","ok","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🙆": ["OKã•ã„ã‚“ã®ã²ã¨","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã¦","OK"], + "🙆â€â™‚ï¸": ["OKã•ã„ã‚“ã®ã ã‚“ã›ã„","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã¦","ok","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🤷â€â™€ï¸": ["ã‹ãŸã‚’ã™ãã‚ã‚‹ã˜ã‚‡ã›ã„","ã†ãŸãŒã„","ã‚€ã¡","ã‚€ã‹ã‚“ã—ã‚“","ã‹ãŸã‚’ã™ãã‚ã‚‹","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🤷": ["ã‹ãŸã‚’ã™ãã‚ã‚‹ã²ã¨","ã†ãŸãŒã„","ã‚€ã¡","ã‚€ã‹ã‚“ã—ã‚“","ã‹ãŸã‚’ã™ãã‚ã‚‹"], + "🤷â€â™‚ï¸": ["ã‹ãŸã‚’ã™ãã‚ã‚‹ã ã‚“ã›ã„","ã†ãŸãŒã„","ã‚€ã¡","ã‚€ã‹ã‚“ã—ã‚“","ã‹ãŸã‚’ã™ãã‚ã‚‹","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🙋â€â™€ï¸": ["ã‹ãŸã¦ã‚’ã‚ã’ã¦ã‚ˆã‚ã“ã¶ã˜ã‚‡ã›ã„","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã¦","ã—ã‚ã‚ã›","ã‚ã’ã‚‹","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🙋": ["ã‹ãŸã¦ã‚’ã‚ã’ã¦ã‚ˆã‚ã“ã¶ã²ã¨","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã¦","ã—ã‚ã‚ã›","ã‚ã’ã‚‹"], + "🙋â€â™‚ï¸": ["ã‹ãŸã¦ã‚’ã‚ã’ã¦ã‚ˆã‚ã“ã¶ã ã‚“ã›ã„","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã¦","ã—ã‚ã‚ã›","ã‚ã’ã‚‹","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🤦â€â™€ï¸": ["ã‹ãŠã‚’ãŠã•ãˆã‚‹ã˜ã‚‡ã›ã„","ãµã—ã‚“","ãµã‚“ãŒã„","ã‹ãŠ","ã¦ã®ã²ã‚‰","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🤦": ["ã¦ã®ã²ã‚‰ã‚’ã‹ãŠã«ã‚ã¦ã‚‹ã²ã¨","ãµã—ã‚“","ãµã‚“ãŒã„","ã‹ãŠ","ã¦ã®ã²ã‚‰"], + "🤦â€â™‚ï¸": ["ãŒãŠã‚’ãŠã•ãˆã‚‹ã ã‚“ã›ã„","ãµã—ã‚“","ãµã‚“ãŒã„","ã‹ãŠ","ã¦ã®ã²ã‚‰","ãŠã¨ã“","ã ã‚“ã›ã„"], + "ðŸ§â€â™€ï¸": ["ã¿ã¿ãŒãµã˜ã‚†ã†ãªã˜ã‚‡ã›ã„","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã¿ã¿ãŒãµã˜ã‚†ã†","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "ðŸ§": ["ã¿ã¿ãŒãµã˜ã‚†ã†ãªã²ã¨","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã¿ã¿ãŒãµã˜ã‚†ã†"], + "ðŸ§â€â™‚ï¸": ["ã¿ã¿ãŒãµã˜ã‚†ã†ãªã ã‚“ã›ã„","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã¿ã¿ãŒãµã˜ã‚†ã†","ã ã‚“ã›ã„","ãŠã¨ã“"], + "🙎â€â™€ï¸": ["ãµãã‚Œã£ã¤ã‚‰ã®ã˜ã‚‡ã›ã„","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ãµãã‚Œã£ã¤ã‚‰","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🙎": ["ãŠã“ã£ãŸã‹ãŠã®ã²ã¨","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ãµãã‚Œã£ã¤ã‚‰"], + "🙎â€â™‚ï¸": ["ãµãã‚Œã£ã¤ã‚‰ã®ã ã‚“ã›ã„","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ãµãã‚Œã£ã¤ã‚‰","ãŠã¨ã“","ã ã‚“ã›ã„"], + "ðŸ™â€â™€ï¸": ["ãŒãŠã‚’ã—ã‹ã‚ãŸã˜ã‚‡ã›ã„","ã—ã‹ã‚ã‚ã‚“","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã‹ãªã—ã„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "ðŸ™": ["ãµã¾ã‚“ãªã‹ãŠã®ã²ã¨","ã—ã‹ã‚ã‚ã‚“","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã‹ãªã—ã„"], + "ðŸ™â€â™‚ï¸": ["ãŒãŠã‚’ã—ã‹ã‚ãŸã ã‚“ã›ã„","ã—ã‹ã‚ã‚ã‚“","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã‹ãªã—ã„","ã ã‚“ã›ã„","ãŠã¨ã“"], + "💇â€â™€ï¸": ["ã‹ã¿ã‚’ãられã¦ã„ã‚‹ã˜ã‚‡ã›ã„","ã‚Šã¯ã¤ã—","ã³ã‚ˆã†ã—","ã³ã‚ˆã†","ã•ã‚“ã±ã¤","ã¸ã‚ã‹ã£ã¨","ã³ã‚ˆã†ã„ã‚“","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "💇": ["ã‹ã¿ã‚’ãられã¦ã„ã‚‹ã²ã¨","ã‚Šã¯ã¤ã—","ã³ã‚ˆã†ã—","ã³ã‚ˆã†","ã•ã‚“ã±ã¤","ã¸ã‚ã‹ã£ã¨","ã³ã‚ˆã†ã„ã‚“"], + "💇â€â™‚ï¸": ["ã‹ã¿ã‚’ãられã¦ã„ã‚‹ã ã‚“ã›ã„","ã‚Šã¯ã¤ã—","ã³ã‚ˆã†ã—","ã³ã‚ˆã†","ã•ã‚“ã±ã¤","ã¸ã‚ã‹ã£ã¨","ã³ã‚ˆã†ã„ã‚“","ãŠã¨ã“","ã ã‚“ã›ã„"], + "💆â€â™€ï¸": ["ãµã‡ã„ã™ã¾ã£ã•ãƒ¼ã˜ã‚’ã†ã‘ã‚‹ã˜ã‚‡ã›ã„","ã¾ã£ã•ãƒ¼ã˜","ã•ã‚ã‚“","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "💆": ["ãµã‡ã„ã™ã¾ã£ã•ãƒ¼ã˜ã‚’ã†ã‘ã‚‹ã²ã¨","ã¾ã£ã•ãƒ¼ã˜","ã•ã‚ã‚“"], + "💆â€â™‚ï¸": ["ãµã‡ã„ã™ã¾ã£ã•ãƒ¼ã˜ã‚’ã†ã‘ã‚‹ã ã‚“ã›ã„","ã¾ã£ã•ãƒ¼ã˜","ã•ã‚ã‚“","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🤰": ["ã«ã‚“ã·","ã«ã‚“ã—ã‚“","ã‚ã‹ã¡ã‚ƒã‚“","ã˜ã‚‡ã›ã„","ãŠã‚“ãª","ã¯ã‚‰","ãµãã‚ŒãŸ","ãµã£ãらã—ãŸ"], + "🫄": ["ã«ã‚“ã—ã‚“ã—ãŸã²ã¨","ã¯ã‚‰","ãµãã‚ŒãŸ","ãµã£ãらã—ãŸ","ã«ã‚“ã—ã‚“","ã‚ã‹ã¡ã‚ƒã‚“"], + "🫃": ["ã«ã‚“ã—ã‚“ã—ã¦ã„ã‚‹ã ã‚“ã›ã„","ã¯ã‚‰","ãµãã‚ŒãŸ","ãµã£ãらã—ãŸ","ã«ã‚“ã—ã‚“","ã‚ã‹ã¡ã‚ƒã‚“","ã ã‚“ã›ã„","ãŠã¨ã“"], + "🤱": ["ã¼ã«ã‚…ã†","ã‚€ã","ã‚ã‹ã¡ã‚ƒã‚“","ã‚ã‹ã‚“ã¼ã†","ã«ã‚…ã†ã˜","よã†ã˜","ã¯ã¯","ã“ã©ã‚‚","ã»ã„ã","ã¿ã‚‹ã","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "👩â€ðŸ¼": ["ã‚ã‹ã¡ã‚ƒã‚“ã«ã”ã¯ã‚“ã‚’ã‚ã’ã‚‹ã˜ã‚‡ã›ã„","ã‚ã‹ã¡ã‚ƒã‚“","ã«ã‚…ã†ã˜","ã“ã©ã‚‚","ã˜ã‚…ã«ã‚…ã†","ã¿ã‚‹ã","ã¼ã¨ã‚‹","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸ¼": ["ã‚ã‹ã¡ã‚ƒã‚“ã«ã”ã¯ã‚“ã‚’ã‚ã’ã‚‹ã²ã¨","ã‚ã‹ã¡ã‚ƒã‚“","ã«ã‚…ã†ã˜","ã“ã©ã‚‚","ã˜ã‚…ã«ã‚…ã†","ã¿ã‚‹ã","ã¼ã¨ã‚‹"], + "👨â€ðŸ¼": ["ã‚ã‹ã¡ã‚ƒã‚“ã«ã”ã¯ã‚“ã‚’ã‚ã’ã‚‹ã ã‚“ã›ã„","ã‚ã‹ã¡ã‚ƒã‚“","ã«ã‚…ã†ã˜","ã“ã©ã‚‚","ã˜ã‚…ã«ã‚…ã†","ã¿ã‚‹ã","ã¼ã¨ã‚‹","ã ã‚“ã›ã„","ãŠã¨ã“"], + "🧎â€â™€ï¸": ["ã²ã–ãŸã¡ã—ã¦ã„ã‚‹ã˜ã‚‡ã›ã„","ã²ã–","ã²ã–ãŸã¡","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧎": ["ã²ã–ãŸã¡ã—ã¦ã„ã‚‹ã²ã¨","ã²ã–","ã²ã–ãŸã¡"], + "🧎â€â™‚ï¸": ["ã²ã–ãŸã¡ã—ã¦ã„ã‚‹ã ã‚“ã›ã„","ã²ã–","ã²ã–ãŸã¡","ã ã‚“ã›ã„","ãŠã¨ã“"], + "ðŸ§â€â™€ï¸": ["ãŸã£ã¦ã„ã‚‹ã˜ã‚‡ã›ã„","ãŸã¤","ã™ãŸã‚“ã§ãƒã‚“ã","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "ðŸ§": ["ãŸã£ã¦ã„ã‚‹ã²ã¨","ãŸã¤","ã™ãŸã‚“ã§ãƒã‚“ã"], + "ðŸ§â€â™‚ï¸": ["ãŸã£ã¦ã„ã‚‹ã ã‚“ã›ã„","ãŸã¤","ã™ãŸã‚“ã§ãƒã‚“ã","ã ã‚“ã›ã„","ãŠã¨ã“"], + "🚶â€â™€ï¸": ["ã‚ã‚‹ãã˜ã‚‡ã›ã„","ã¯ã„ãã‚“ã","ã»ã“ã†ã—ゃ","ã‚ã‚‹ã","ã†ã‰ãƒ¼ãã‚“ã","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🚶": ["ã‚ã‚‹ãã²ã¨","ã¯ã„ãã‚“ã","ã»ã“ã†ã—ゃ","ã‚ã‚‹ã","ã†ã‰ãƒ¼ãã‚“ã"], + "🚶â€â™‚ï¸": ["ã‚ã‚‹ãã ã‚“ã›ã„","ã¯ã„ãã‚“ã","ã»ã“ã†ã—ゃ","ã‚ã‚‹ã","ã†ã‰ãƒ¼ãã‚“ã","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€ðŸ¦¯": ["ã—ã‚ã¤ãˆã‚’ã‚‚ã£ãŸã˜ã‚‡ã›ã„","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã‚ãŒãµã˜ã‚†ã†","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸ¦¯": ["ã—ã‚ã¤ãˆã‚’ã‚‚ã£ãŸã²ã¨","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã‚ãŒãµã˜ã‚†ã†"], + "👨â€ðŸ¦¯": ["ã—ã‚ã¤ãˆã‚’ã‚‚ã£ãŸã ã‚“ã›ã„","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã‚ãŒãµã˜ã‚†ã†","ã ã‚“ã›ã„","ãŠã¨ã“"], + "ðŸƒâ€â™€ï¸": ["ã¯ã—ã‚‹ã˜ã‚‡ã›ã„","ã¾ã‚‰ãã‚“","らんãªãƒ¼","らんã«ã‚“ã","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "ðŸƒ": ["ã¯ã—ã‚‹ã²ã¨","ã¾ã‚‰ãã‚“","らんãªãƒ¼","らんã«ã‚“ã"], + "ðŸƒâ€â™‚ï¸": ["ã¯ã—ã‚‹ã ã‚“ã›ã„","ã¾ã‚‰ãã‚“","らんãªãƒ¼","らんã«ã‚“ã","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👩â€ðŸ¦¼": ["ã§ã‚“ã©ã†ãã‚‹ã¾ã„ã™ã«ã™ã‚ã£ã¦ã„ã‚‹ã˜ã‚‡ã›ã„","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ãã‚‹ã¾ã„ã™","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸ¦¼": ["ã§ã‚“ã©ã†ãã‚‹ã¾ã„ã™ã«ã™ã‚ã£ã¦ã„ã‚‹ã²ã¨","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ãã‚‹ã¾ã„ã™"], + "👨â€ðŸ¦¼": ["ã§ã‚“ã©ã†ãã‚‹ã¾ã„ã™ã«ã™ã‚ã£ã¦ã„ã‚‹ã ã‚“ã›ã„","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ãã‚‹ã¾ã„ã™","ã ã‚“ã›ã„","ãŠã¨ã“"], + "👩â€ðŸ¦½": ["ã—ã‚…ã©ã†ãã‚‹ã¾ã„ã™ã«ã™ã‚ã£ã¦ã„ã‚‹ã˜ã‚‡ã›ã„","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ãã‚‹ã¾ã„ã™","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧑â€ðŸ¦½": ["ã—ã‚…ã©ã†ãã‚‹ã¾ã„ã™ã«ã™ã‚ã£ã¦ã„ã‚‹ã²ã¨","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ãã‚‹ã¾ã„ã™"], + "👨â€ðŸ¦½": ["ã—ã‚…ã©ã†ãã‚‹ã¾ã„ã™ã«ã™ã‚ã£ã¦ã„ã‚‹ã ã‚“ã›ã„","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ãã‚‹ã¾ã„ã™","ã ã‚“ã›ã„","ãŠã¨ã“"], + "💃": ["ã˜ã‚‡ã›ã„ã ã‚“ã•ãƒ¼","ã ã‚“ã™","ãŠã©ã‚‹","ã ã‚“ã•ãƒ¼","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🕺": ["ã ã‚“ã›ã„ã ã‚“ã•ãƒ¼","ã ã‚“ã™","ãŠã©ã‚‹","ã ã‚“ã•ãƒ¼","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👯â€â™€ï¸": ["ã°ã«ãƒ¼ãŒãƒ¼ã‚‹","ã†ã•ãŽã¿ã¿","ã ã‚“ã•ãƒ¼","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "👯": ["ã†ã•ãŽã¿ã¿ã®ã²ã¨","ã†ã•ãŽã¿ã¿","ã ã‚“ã•ãƒ¼"], + "👯â€â™‚ï¸": ["ã†ã•ãŽã¿ã¿ã®ã ã‚“ã›ã„","ã†ã•ãŽã¿ã¿","ã ã‚“ã•ãƒ¼","ãŠã¨ã“","ã ã‚“ã›ã„"], + "👫": ["ã—ã‚…ã‚’ã¤ãªã„ã ã ã‚“ã˜ã‚‡","ã‹ã£ã·ã‚‹","ã¦","ã¤ãªã","ãŠã¨ã“","ãŠã‚“ãª","ã ã‚“ã˜ã‚‡"], + "ðŸ‘": ["ã—ã‚…ã‚’ã¤ãªã„ã ã˜ã‚‡ã›ã„","ã‹ã£ã·ã‚‹","ã¦","ã¤ãªã","ã˜ã‚‡ã›ã„","ãŠã‚“ãª","ã·ã‚‰ã„ã©","lgbt","ã‚Œãšã³ã‚ã‚“"], + "👬": ["ã—ã‚…ã‚’ã¤ãªã„ã ã ã‚“ã›ã„","ã‹ã£ã·ã‚‹","ã¦","ã¤ãªã","ã ã‚“ã›ã„","ãŠã¨ã“","ã·ã‚‰ã„ã©","lgbt","ã’ã„"], + "🧑â€ðŸ¤â€ðŸ§‘": ["ã—ã‚…ã‚’ã¤ãªã„ã ã²ã¨ãŸã¡","ã‹ã£ã·ã‚‹","ã¦","ã«ãŽã‚‹"], + "👩â€â¤ï¸â€ðŸ‘¨": ["ã¯ãƒ¼ã¨ã®ã‹ã£ã·ã‚‹ (ã˜ã‚‡ã›ã„ã€ã ã‚“ã›ã„)","ã‹ã£ã·ã‚‹","ã¯ãƒ¼ã¨","ã‚ã„","れんã‚ã„","ãŠã¨ã“","ãŠã‚“ãª","ã ã‚“ã˜ã‚‡"], + "👩â€â¤ï¸â€ðŸ‘©": ["ã¯ãƒ¼ã¨ã®ã‹ã£ã·ã‚‹ (ã˜ã‚‡ã›ã„ã€ã˜ã‚‡ã›ã„)","ã‹ã£ã·ã‚‹","ã¯ãƒ¼ã¨","ã‚ã„","れんã‚ã„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª","ã·ã‚‰ã„ã©","lgbt","ã‚Œãšã³ã‚ã‚“"], + "💑": ["ã¯ãƒ¼ã¨ã®ã‹ã£ã·ã‚‹","ã‹ã£ã·ã‚‹","ã¯ãƒ¼ã¨","ã‚ã„","れんã‚ã„","ãŠã¨ã“","ãŠã‚“ãª","ã ã‚“ã˜ã‚‡"], + "👨â€â¤ï¸â€ðŸ‘¨": ["ã¯ãƒ¼ã¨ã®ã‹ã£ã·ã‚‹ (ã ã‚“ã›ã„ã€ã ã‚“ã›ã„)","ã‹ã£ã·ã‚‹","ã¯ãƒ¼ã¨","ã‚ã„","れんã‚ã„","ã ã‚“ã›ã„","ãŠã¨ã“","ã·ã‚‰ã„ã©","lgbt","ã’ã„"], + "👩â€â¤ï¸â€ðŸ’‹â€ðŸ‘¨": ["ãã™ (ã˜ã‚‡ã›ã„ã€ã ã‚“ã›ã„)","ã‹ã£ã·ã‚‹","ãã™","ã¯ãƒ¼ã¨","ã‚ã„","れんã‚ã„","ãŠã¨ã“","ãŠã‚“ãª","ã ã‚“ã˜ã‚‡"], + "👩â€â¤ï¸â€ðŸ’‹â€ðŸ‘©": ["ãã™ (ã˜ã‚‡ã›ã„ã€ã˜ã‚‡ã›ã„)","ã‹ã£ã·ã‚‹","ãã™","ã¯ãƒ¼ã¨","ã‚ã„","れんã‚ã„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª","ã·ã‚‰ã„ã©","lgbt","ã’ã„"], + "ðŸ’": ["ãã™","ã‹ã£ã·ã‚‹","ã¯ãƒ¼ã¨","ã‚ã„","れんã‚ã„","ãŠã¨ã“","ãŠã‚“ãª","ã ã‚“ã˜ã‚‡"], + "👨â€â¤ï¸â€ðŸ’‹â€ðŸ‘¨": ["ãã™ (ã ã‚“ã›ã„ã€ã ã‚“ã›ã„)","ã‹ã£ã·ã‚‹","ãã™","ã¯ãƒ¼ã¨","ã‚ã„","れんã‚ã„","ã ã‚“ã›ã„","ãŠã¨ã“","ã·ã‚‰ã„ã©","lgbt","ã’ã„"], + "👪": ["ã‹ãžã","ã¡ã¡ãŠã‚„","ã¯ã¯ãŠã‚„","ãŠã¨ã“","ãŠã‚“ãª","ã ã‚“ã˜ã‚‡","ãŠã¨ã“ã®ã“","ã“ã©ã‚‚"], + "👨â€ðŸ‘©â€ðŸ‘§": ["ã‹ãžã (ã ã‚“ã›ã„ã€ã˜ã‚‡ã›ã„ã€ãŠã‚“ãªã®ã“)","ã¡ã¡ãŠã‚„","ã¯ã¯ãŠã‚„","ãŠã¨ã“","ãŠã‚“ãª","ã ã‚“ã˜ã‚‡","ãŠã‚“ãªã®ã“","ã“ã©ã‚‚"], + "👨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦": ["ã‹ãžã (ã ã‚“ã›ã„ã€ã˜ã‚‡ã›ã„ã€ãŠã‚“ãªã®ã“ã€ãŠã¨ã“ã®ã“)","ã¡ã¡ãŠã‚„","ã¯ã¯ãŠã‚„","ãŠã¨ã“","ãŠã‚“ãª","ã ã‚“ã˜ã‚‡","ãŠã¨ã“ã®ã“","ãŠã‚“ãªã®ã“","ã“ã©ã‚‚"], + "👨â€ðŸ‘©â€ðŸ‘¦â€ðŸ‘¦": ["ã‹ãžã (ã ã‚“ã›ã„ã€ã˜ã‚‡ã›ã„ã€ãŠã¨ã“ã®ã“ã€ãŠã¨ã“ã®ã“)","ã¡ã¡ãŠã‚„","ã¯ã¯ãŠã‚„","ãŠã¨ã“","ãŠã‚“ãª","ã ã‚“ã˜ã‚‡","ãŠã¨ã“ã®ã“","ã“ã©ã‚‚"], + "👨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘§": ["ã‹ãžã (ã ã‚“ã›ã„ã€ã˜ã‚‡ã›ã„ã€ãŠã‚“ãªã®ã“ã€ãŠã‚“ãªã®ã“)","ã¡ã¡ãŠã‚„","ã¯ã¯ãŠã‚„","ãŠã¨ã“","ãŠã‚“ãª","ã ã‚“ã˜ã‚‡","ãŠã‚“ãªã®ã“","ã“ã©ã‚‚"], + "👩â€ðŸ‘©â€ðŸ‘¦": ["ã‹ãžã (ã˜ã‚‡ã›ã„ã€ã˜ã‚‡ã›ã„ã€ãŠã¨ã“ã®ã“)","ã‹ãžã","ã¯ã¯ãŠã‚„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª","ãŠã¨ã“ã®ã“","ã“ã©ã‚‚","ã·ã‚‰ã„ã©","lgbt","ã‚Œãšã³ã‚ã‚“"], + "👩â€ðŸ‘©â€ðŸ‘§": ["ã‹ãžã (ã˜ã‚‡ã›ã„ã€ã˜ã‚‡ã›ã„ã€ãŠã‚“ãªã®ã“)","ã‹ãžã","ã¯ã¯ãŠã‚„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª","ãŠã‚“ãªã®ã“","ã“ã©ã‚‚","ã·ã‚‰ã„ã©","lgbt","ã‚Œãšã³ã‚ã‚“"], + "👩â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦": ["ã‹ãžã (ã˜ã‚‡ã›ã„ã€ã˜ã‚‡ã›ã„ã€ãŠã‚“ãªã®ã“ã€ãŠã¨ã“ã®ã“)","ã‹ãžã","ã¯ã¯ãŠã‚„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª","ãŠã¨ã“ã®ã“","ãŠã‚“ãªã®ã“","ã“ã©ã‚‚","ã·ã‚‰ã„ã©","lgbt","ã‚Œãšã³ã‚ã‚“"], + "👩â€ðŸ‘©â€ðŸ‘¦â€ðŸ‘¦": ["ã‹ãžã (ã˜ã‚‡ã›ã„ã€ã˜ã‚‡ã›ã„ã€ãŠã¨ã“ã®ã“ã€ãŠã¨ã“ã®ã“)","ã‹ãžã","ã¯ã¯ãŠã‚„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª","ãŠã¨ã“ã®ã“","ã“ã©ã‚‚","ã·ã‚‰ã„ã©","lgbt","ã‚Œãšã³ã‚ã‚“"], + "👩â€ðŸ‘©â€ðŸ‘§â€ðŸ‘§": ["ã‹ãžã (ã˜ã‚‡ã›ã„ã€ã˜ã‚‡ã›ã„ã€ãŠã‚“ãªã®ã“ã€ãŠã‚“ãªã®ã“)","ã‹ãžã","ã¯ã¯ãŠã‚„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª","ãŠã‚“ãªã®ã“","ã“ã©ã‚‚","ã·ã‚‰ã„ã©","lgbt","ã‚Œãšã³ã‚ã‚“"], + "👨â€ðŸ‘¨â€ðŸ‘¦": ["ã‹ãžã (ã ã‚“ã›ã„ã€ã ã‚“ã›ã„ã€ãŠã¨ã“ã®ã“)","ã‹ãžã","ã¡ã¡ãŠã‚„","ã ã‚“ã›ã„","ãŠã¨ã“","ãŠã¨ã“ã®ã“","ã“ã©ã‚‚","ã·ã‚‰ã„ã©","lgbt","ã’ã„"], + "👨â€ðŸ‘¨â€ðŸ‘§": ["ã‹ãžã (ã ã‚“ã›ã„ã€ã ã‚“ã›ã„ã€ãŠã‚“ãªã®ã“)","ã‹ãžã","ã¡ã¡ãŠã‚„","ã ã‚“ã›ã„","ãŠã¨ã“","ãŠã‚“ãªã®ã“","ã“ã©ã‚‚","ã·ã‚‰ã„ã©","lgbt","ã’ã„"], + "👨â€ðŸ‘¨â€ðŸ‘§â€ðŸ‘¦": ["ã‹ãžã (ã ã‚“ã›ã„ã€ã ã‚“ã›ã„ã€ãŠã‚“ãªã®ã“ã€ãŠã¨ã“ã®ã“)","ã‹ãžã","ã¡ã¡ãŠã‚„","ã ã‚“ã›ã„","ãŠã¨ã“","ãŠã¨ã“ã®ã“","ãŠã‚“ãªã®ã“","ã“ã©ã‚‚","ã·ã‚‰ã„ã©","lgbt","ã’ã„"], + "👨â€ðŸ‘¨â€ðŸ‘¦â€ðŸ‘¦": ["ã‹ãžã (ã ã‚“ã›ã„ã€ã ã‚“ã›ã„ã€ãŠã¨ã“ã®ã“ã€ãŠã¨ã“ã®ã“)","ã‹ãžã","ã¡ã¡ãŠã‚„","ã ã‚“ã›ã„","ãŠã¨ã“","ãŠã¨ã“ã®ã“","ã“ã©ã‚‚","ã·ã‚‰ã„ã©","lgbt","ã’ã„"], + "👨â€ðŸ‘¨â€ðŸ‘§â€ðŸ‘§": ["ã‹ãžã (ã ã‚“ã›ã„ã€ã ã‚“ã›ã„ã€ãŠã‚“ãªã®ã“ã€ãŠã‚“ãªã®ã“)","ã‹ãžã","ã¡ã¡ãŠã‚„","ã ã‚“ã›ã„","ãŠã¨ã“","ãŠã‚“ãªã®ã“","ã“ã©ã‚‚","ã·ã‚‰ã„ã©","lgbt","ã’ã„"], + "👩â€ðŸ‘¦": ["ã‹ãžã(ã˜ã‚‡ã›ã„ã€ãŠã¨ã“ã®ã“)","ã‹ãžã","ã¯ã¯ãŠã‚„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª","ãŠã¨ã“ã®ã“","ã“ã©ã‚‚"], + "👩â€ðŸ‘§": ["ã‹ãžã(ã˜ã‚‡ã›ã„ã€ãŠã‚“ãªã®ã“)","ã‹ãžã","ã¯ã¯ãŠã‚„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª","ãŠã‚“ãªã®ã“","ã“ã©ã‚‚"], + "👩â€ðŸ‘§â€ðŸ‘¦": ["ã‹ãžã(ã˜ã‚‡ã›ã„ã€ãŠã‚“ãªã®ã“ã€ãŠã¨ã“ã®ã“)","ã‹ãžã","ã¯ã¯ãŠã‚„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª","ã ã‚“ã›ã„","ãŠã‚“ãªã®ã“","ãŠã¨ã“ã®ã“","ã“ã©ã‚‚"], + "👩â€ðŸ‘¦â€ðŸ‘¦": ["ã‹ãžã(ã˜ã‚‡ã›ã„ã€ãŠã¨ã“ã®ã“ã€ãŠã¨ã“ã®ã“)","ã‹ãžã","ã¯ã¯ãŠã‚„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª","ãŠã¨ã“ã®ã“","ã“ã©ã‚‚"], + "👩â€ðŸ‘§â€ðŸ‘§": ["ã‹ãžã(ã˜ã‚‡ã›ã„ã€ãŠã‚“ãªã®ã“ã€ãŠã‚“ãªã®ã“)","ã‹ãžã","ã¯ã¯ãŠã‚„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª","ãŠã‚“ãªã®ã“","ã“ã©ã‚‚"], + "👨â€ðŸ‘¦": ["ã‹ãžã(ã ã‚“ã›ã„ã€ãŠã¨ã“ã®ã“)","ã¡ã¡ãŠã‚„","ãŠã¨ã“","ã ã‚“ã›ã„","ãŠã¨ã“ã®ã“","ã“ã©ã‚‚"], + "👨â€ðŸ‘§": ["ã‹ãžã(ã ã‚“ã›ã„ã€ãŠã‚“ãªã®ã“)","ã¡ã¡ãŠã‚„","ãŠã¨ã“","ã ã‚“ã˜ã‚‡","ãŠã‚“ãªã®ã“","ã“ã©ã‚‚"], + "👨â€ðŸ‘§â€ðŸ‘¦": ["ã‹ãžã(ã ã‚“ã›ã„ã€ãŠã‚“ãªã®ã“ã€ãŠã¨ã“ã®ã“)","ã¡ã¡ãŠã‚„","ãŠã¨ã“","ã ã‚“ã›ã„","ãŠã¨ã“ã®ã“","ãŠã‚“ãªã®ã“","ã“ã©ã‚‚"], + "👨â€ðŸ‘¦â€ðŸ‘¦": ["ã‹ãžã(ã ã‚“ã›ã„ã€ãŠã¨ã“ã®ã“ã€ãŠã¨ã“ã®ã“)","ã¡ã¡ãŠã‚„","ãŠã¨ã“","ã ã‚“ã›ã„","ãŠã¨ã“ã®ã“","ã“ã©ã‚‚"], + "👨â€ðŸ‘§â€ðŸ‘§": ["ã‹ãžã(ã ã‚“ã›ã„ã€ãŠã‚“ãªã®ã“ã€ãŠã‚“ãªã®ã“)","ã¡ã¡ãŠã‚„","ãŠã¨ã“","ã ã‚“ã˜ã‚‡","ãŠã‚“ãªã®ã“","ã“ã©ã‚‚"], + "👚": ["ã‚Œã§ãƒãƒ¼ã™ã†ã‡ã‚","ãµã","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "👕": ["ã¦ãƒãƒ¼ã—ゃã¤","ãµã","ã—ゃã¤"], + "🥼": ["ã¯ãã„","ãµã","ã„ã—ゃ","ã˜ã£ã‘ã‚“","ã‹ãŒãã—ゃ"], + "🦺": ["ã‚ã‚“ãœã‚“ã¹ã™ã¨","ãã‚“ãã‚…ã†","ã‚ã‚“ãœã‚“","ã¹ã™ã¨"], + "🧥": ["ã“ーã¨","ãµã","ã˜ã‚ƒã‘ã£ã¨"], + "👖": ["ã˜ãƒ¼ã‚“ãš","ãµã","ã±ã‚“ã¤","ãšã¼ã‚“"], + "👔": ["ãããŸã„","ãµã"], + "👗": ["ã©ã‚Œã™","ãµã"], + "👘": ["ãã‚‚ã®","ãµã","ã‚ãµã"], + "🥻": ["ã•ã‚Šãƒ¼","ãµã","ã©ã‚Œã™"], + "🩱": ["ã‚ã‚“ã´ãƒ¼ã™","ãµã","ã¿ãšãŽ","ã™ã„ã¿ã‚“ãã†ã‡ã‚","ã™ã„ãˆã„"], + "👙": ["ã³ãã«","ãµã","ã™ã„ãˆã„"], + "🩲": ["ã¶ã‚Šãƒ¼ãµ","ãµã","ã¿ãšãŽ","ã™ã„ã¿ã‚“ãã†ã‡ã‚","ã™ã„ãˆã„","ã—ãŸãŽ"], + "🩳": ["ã—ょーã¤","ãµã","ã¿ãšãŽ","ã™ã„ã¿ã‚“ãã†ã‡ã‚","ã™ã„ãˆã„","ã—ãŸãŽ"], + "💄": ["ãã¡ã¹ã«","ã‘ã—ょã†ã²ã‚“","ã“ã™ã‚","ã‘ã—ょã†","ã‚ã„ã"], + "💋": ["ãã™ã¾ãƒ¼ã","ã¯ãƒ¼ã¨","ãã™","ãã¡ã³ã‚‹","ã¾ãƒ¼ã","れんã‚ã„","ã‚ã¾ã‚“ã™"], + "👣": ["ã‚ã—ã‚ã¨","ã‹ã‚‰ã ","ãµã"], + "🧦": ["ãã¤ã—ãŸ","ãµã","ãã£ãã™","ã„ã¡ãã¿"], + "🩴": ["ã”ã‚€ã›ã„ã•ã‚“ã ã‚‹","ã³ãƒ¼ã¡","ã•ã‚“ã ã‚‹","ãžã†ã‚Š"], + "👠": ["ã¯ã„ã²ãƒ¼ã‚‹","ãµã","ã²ãƒ¼ã‚‹","ãã¤","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "👡": ["ã‚Œã§ãƒãƒ¼ã™ã•ã‚“ã ã‚‹","ãµã","ã•ã‚“ã ã‚‹","ãã¤","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "👢": ["ã‚Œã§ãƒãƒ¼ã™ã¶ãƒ¼ã¤","ã¶ãƒ¼ã¤","ãµã","ãã¤","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🥿": ["ã‚Œã§ãƒãƒ¼ã™ãµã‚‰ã£ã¨ã—ゅーãš","ãµã","ã°ã‚Œãˆãµã‚‰ã£ã¨","ã™ã‚Šã£ã½ã‚“","ã™ã‚Šã£ã±"], + "👞": ["ã‚ã‚“ãšã—ゅーãš","ãµã","ã ã‚“ã›ã„","ãŠã¨ã“","ãã¤"], + "👟": ["ã†ã‚“ã©ã†ãã¤","ã†ã‚“ã©ã†","ãµã","ã—ゅーãš","ã™ã«ãƒ¼ã‹ãƒ¼"], + "🩰": ["ã°ã‚Œãˆã—ゅーãš","ãµã","ã—ゅーãš","ã°ã‚Œãˆ","ã ã‚“ã™"], + "🥾": ["ã¯ã„ãã‚“ãã¶ãƒ¼ã¤","ãµã","ã°ã£ãã±ã£ã","ã¶ãƒ¼ã¤","ãゃんã·","ã¯ã„ãã‚“ã"], + "🧢": ["ãゃã£ã·","ãµã","ã‚„ãã‚…ã†","ã¯ã£ã¨","ã¼ã†ã—"], + "👒": ["ã‚Œã§ãƒãƒ¼ã™ã¯ã£ã¨","ãµã","ã¼ã†ã—","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🎩": ["ã—ã‚‹ãã¯ã£ã¨","ã‚ãã¦ãƒã³ã¦ãƒ","ãµã","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚“ã‚ã‚“ã¨","ã”らã","ã¼ã†ã—","ã¨ã£ã·ã™"], + "🎓": ["ãã¤ãŽã‚‡ã†ã—ãã®ã‹ãã¼ã†","ã‚ãã¦ãƒã³ã¦ãƒ","ã¼ã†ã—","ãŠã„ã‚ã„","ãµã","ãã¤ãŽã‚‡ã†","ã¯ã£ã¨"], + "👑": ["ã‹ã‚“むり","ãµã","ãŠã†ã‹ã‚“","ãŠã†","ã˜ã‚‡ãŠã†"], + "⛑": ["ã—ã‚ã˜ã‚…ã†ã˜ã®ã¸ã‚‹ã‚ã£ã¨","ãã‚…ã†ã˜ã‚‡","ã˜ã‚…ã†ã˜","ã‹ãŠ","ã¼ã†ã—","ã¸ã‚‹ã‚ã£ã¨"], + "🪖": ["ãã‚“ãŸã„ã®ã¸ã‚‹ã‚ã£ã¨","ãã‚“","ã¸ã‚‹ã‚ã£ã¨","ãã‚“ãŸã„","ãã‚“ã˜ã‚“","ã¸ã„ã—"], + "🎒": ["らんã©ã›ã‚‹","ã‚ãã¦ãƒã³ã¦ãƒ","ã‹ã°ã‚“","ã°ã£ã","ãŒãã›ã„ã‹ã°ã‚“","ãŒã£ã“ã†"], + "ðŸ‘": ["ã½ãƒ¼ã¡","ã‹ã°ã‚“","ã°ã£ã","ãµã"], + "👛": ["ã•ã„ãµ","ãµã","ã“ã„ã‚“"], + "👜": ["ã¯ã‚“ã©ã°ã£ã","ã‹ã°ã‚“","ã°ã£ã","ãµã"], + "💼": ["ã¶ã‚Šãƒ¼ãµã‘ーã™"], + "👓": ["ã‚ãŒã","ãµã","ã‚","ã‚ã„ã†ã‡ã‚"], + "🕶": ["ã•ã‚“ãらã™","ãらã„","ã‚","ã‚ãŒã"], + "🥽": ["ã”ーãã‚‹","ãµã","ã‚ã®ã»ã”","ã™ã„ãˆã„","よã†ã›ã¤"], + "🧣": ["ã™ã‹ãƒ¼ãµ","ãµã","ãã³"], + "🧤": ["ã¦ã¶ãã‚","ãµã","ã¦"], + "ðŸ’": ["ゆã³ã‚","ã ã„ã‚„ã‚‚ã‚“ã©","れんã‚ã„","ã‚ã¾ã‚“ã™"], + "🌂": ["ã¨ã˜ãŸã‹ã•","ãµã","ã‚ã‚","ã‹ã•","ã¦ã‚“ã"], + "☂": ["ã‹ã•","ãµã","ã‚ã‚","ã¦ã‚“ã"], + "ðŸ¶": ["ã„ã¬ã®ã‹ãŠ","ã‘ã‚“","ã„ã¬","ã‹ãŠ","ãºã£ã¨"], + "ðŸ±": ["ãã“ã®ã‹ãŠ","ãã“","ã‹ãŠ","ãºã£ã¨"], + "ðŸ": ["ããšã¿ã®ã‹ãŠ","ã‹ãŠ","ããšã¿"], + "ðŸ¹": ["ã¯ã‚€ã™ãŸãƒ¼ã®ã‹ãŠ","ã‹ãŠ","ã¯ã‚€ã™ãŸãƒ¼","ãºã£ã¨"], + "ðŸ°": ["ã†ã•ãŽã®ã‹ãŠ","ã°ã«ãƒ¼","ã‹ãŠ","ãºã£ã¨","ã†ã•ãŽ"], + "ðŸ»": ["ãã¾ã®ã‹ãŠ","ãã¾","ã‹ãŠ"], + "🧸": ["ã¦ã§ãƒã¹ã‚","ãŠã‚‚ã¡ã‚ƒ","ã³ã‚ーã©","ã¬ã„ãã‚‹ã¿"], + "ðŸ¼": ["ã±ã‚“ã ã®ã‹ãŠ","ã‹ãŠ","ã±ã‚“ã ","ãã¾"], + "ðŸ»â€â„ï¸": ["ã—ã‚ãã¾","ã‹ãŠ","ã»ã£ãょã","ãã¾","ã—ã‚"], + "ðŸ¨": ["ã“ã‚ら","ãã¾","ゆã†ã¶ãã‚ã‚‹ã„","ãŠãƒ¼ã™ã¨ã‚‰ã‚Šã‚"], + "ðŸ¯": ["ã¨ã‚‰ã®ã‹ãŠ","ã‹ãŠ","ã¨ã‚‰"], + "ðŸ¦": ["らã„ãŠã‚“ã®ã‹ãŠ","ã‹ãŠ","ã—ã—ã–","らã„ãŠã‚“","ã›ã„ã–"], + "ðŸ®": ["ã†ã—ã®ã‹ãŠ","ã†ã—","ã‹ãŠ"], + "ðŸ·": ["ã¶ãŸã®ã‹ãŠ","ã‹ãŠ","ã¶ãŸ"], + "ðŸ½": ["ã¶ãŸã®ã¯ãª","ã‹ãŠ","ã¯ãª","ã¶ãŸ"], + "ðŸ¸": ["ã‹ãˆã‚‹ã®ã‹ãŠ","ã‹ãŠ","ã‹ãˆã‚‹"], + "ðŸµ": ["ã•ã‚‹ã®ã‹ãŠ","ã‹ãŠ","ã•ã‚‹"], + "🙈": ["ã¿ã–ã‚‹","ã‚ã‚‹ã„","ã‹ãŠ","ãã‚“ã˜ã‚‹","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã•ã‚‹","ã ã‚","ãã‚“ã—","ã¿ã‚‹"], + "🙉": ["ãã‹ã–ã‚‹","ã‚ã‚‹ã„","ã‹ãŠ","ãã‚“ã˜ã‚‹","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ãã","ã•ã‚‹","ãªã„","ãªã—","ãã‚“ã—"], + "🙊": ["ã„ã‚ã–ã‚‹","ã‚ã‚‹ã„","ã‹ãŠ","ãã‚“ã˜ã‚‹","ã˜ã‡ã™ã¡ã‚ƒãƒ¼","ã•ã‚‹","ãªã„","ãªã—","ãã‚“ã—","ã¯ãªã™"], + "ðŸ’": ["ã•ã‚‹"], + "ðŸ¦": ["ã”りら"], + "🦧": ["ãŠã‚‰ã‚“ã†ãƒ¼ãŸã‚“","ã‚‹ã„ã˜ã‚“ãˆã‚“"], + "ðŸ”": ["ã«ã‚ã¨ã‚Š"], + "ðŸ§": ["ãºã‚“ãŽã‚“"], + "ðŸ¦": ["ã¨ã‚Š"], + "ðŸ¦â€â¬›": ["ãã‚ã„ã¨ã‚Š","ã¨ã‚Š","ãã‚","ã‹ã‚‰ã™","ã‚ãŸã‚ŠãŒã‚‰ã™","ã¿ã‚„ã¾ãŒã‚‰ã™"], + "ðŸ¤": ["ã²ã‚ˆã“","ã‚ã‹ã¡ã‚ƒã‚“"], + "ðŸ£": ["ã²ã‚ˆã“","ã‚ã‹ã¡ã‚ƒã‚“","ãµã‹"], + "ðŸ¥": ["ã—ょã†ã‚ã‚“ã‚’ã‚€ã„ãŸã²ã‚ˆã“","ã‚ã‹ã¡ã‚ƒã‚“","ã²ã‚ˆã“"], + "ðŸº": ["ãŠãŠã‹ã¿ã®ã‹ãŠ","ã‹ãŠ","ãŠãŠã‹ã¿"], + "🦊": ["ãã¤ãã®ã‹ãŠ","ã‹ãŠ","ãã¤ã"], + "ðŸ¦": ["ã‚らã„ãã¾","ã‹ãŠ","ã“ã†ãã—ã‚“ãŒã¤ã‚ˆã„","ãšã‚‹ã‹ã—ã“ã„"], + "ðŸ—": ["ã„ã®ã—ã—","ã¶ãŸ"], + "ðŸ´": ["ã†ã¾ã®ã‹ãŠ","ã‹ãŠ","ã†ã¾"], + "🦓": ["ã—ã¾ã†ã¾","ã‹ãŠ"], + "🦒": ["ãã‚Šã‚“","ã‹ãŠ"], + "🦌": ["ã—ã‹"], + "🫎": ["ã¸ã‚‰ã˜ã‹","ã©ã†ã¶ã¤","ãˆã ã¤ã®","ãˆã‚‹ã","ã»ã«ã‚…ã†ã‚‹ã„"], + "🦘": ["ã‹ã‚“ãŒã‚‹ãƒ¼","ãŠãƒ¼ã™ã¨ã‚‰ã‚Šã‚","ã˜ã‚ƒã‚“ã·","ゆã†ã¶ãã‚ã‚‹ã„"], + "🦥": ["ãŸã„ã ","ãªã¾ã‘ã‚‹","ãŠãã„"], + "🦦": ["ã‹ã‚ã†ã","ã¥ã‚Š","ãµã–ã‘ã‚‹"], + "🦫": ["ã³ãƒ¼ã°ãƒ¼","ã ã‚€"], + "🦄": ["ゆã«ã“ーんã®ã‹ãŠ","ã‹ãŠ","ゆã«ã“ーん"], + "ðŸ": ["ã¿ã¤ã°ã¡","ã¯ã¡","ã“ã‚“ã¡ã‚…ã†"], + "ðŸ›": ["ã‚€ã—","ã“ã‚“ã¡ã‚…ã†"], + "🦋": ["ã¡ã‚‡ã†","ã“ã‚“ã¡ã‚…ã†","ã†ã¤ãã—ã„"], + "ðŸŒ": ["ã‹ãŸã¤ã‚€ã‚Š"], + "🪲": ["ã‹ã¶ã¨ã‚€ã—","ã‚€ã—","ã“ã‚“ã¡ã‚…ã†"], + "ðŸž": ["ã¦ã‚“ã¨ã†ã‚€ã—","ã‹ã¶ã¨ã‚€ã—","ã“ã‚“ã¡ã‚…ã†","ã¦ã‚“ã¨ã†ã¡ã‚…ã†"], + "ðŸœ": ["ã‚ã‚Š","ã“ã‚“ã¡ã‚…ã†"], + "🦗": ["ãã‚Šã‘ã£ã¨","ã“ãŠã‚ãŽ","ã°ã£ãŸã‚","ã“ã‚“ã¡ã‚…ã†"], + "🪳": ["ã”ãã¶ã‚Š","ã“ã‚“ã¡ã‚…ã†","ãŒã„ã¡ã‚…ã†"], + "🕷": ["ãã‚‚","ã“ã‚“ã¡ã‚…ã†"], + "🕸": ["ãã‚‚ã®ã™","ãã‚‚","ã™"], + "🦂": ["ã•ãã‚Š","ã•ãã‚Šã–","ã›ã„ã–"], + "🦟": ["ã‹","ã³ã‚‡ã†ã","ãã¤","ã“ã‚“ã¡ã‚…ã†","ã¾ã‚‰ã‚Šã‚","ã†ã„ã‚‹ã™"], + "🪰": ["ã¯ãˆ","ãŒã„ã¡ã‚…ã†","ã“ã‚“ã¡ã‚…ã†","ã†ã˜ã‚€ã—"], + "🪱": ["ãœã‚“ã¡ã‚…ã†","ãŸã¾ããŒãŸã©ã†ã¶ã¤","ã¿ã¿ãš","ãã›ã„ã¡ã‚…ã†"], + "🦠": ["ã³ã›ã„ã¶ã¤","ã‚ã‚ーã°","ã°ãã¦ã‚Šã‚","ã†ã„ã‚‹ã™"], + "ðŸ¢": ["ã‹ã‚"], + "ðŸ": ["ã¸ã³","ã†ã‚“ã±ã‚“ã«ã‚“","ã¸ã³ã¤ã‹ã„ã–","ã›ã„ã–"], + "🦎": ["ã¨ã‹ã’","ã¯ã¡ã‚…ã†ã‚‹ã„"], + "ðŸ™": ["ãŸã“"], + "🦑": ["ã„ã‹","ãªã‚“ãŸã„ã©ã†ã¶ã¤"], + "🪼": ["ãらã’","ãã™ã‚Š","ã‚€ã›ãã¤ã„ã©ã†ã¶ã¤","ãœã‚Šãƒ¼","ã†ã¿","ã„ãŸã„","ã—ã‚‚ã†"], + "🦞": ["ã‚ã¶ã™ãŸãƒ¼","ã³ã™ã","ã¤ã‚","ã—ーãµãƒ¼ã©"], + "🦀": ["ã‹ã«","ã‹ã«ã–","ã›ã„ã–"], + "ðŸ¦": ["ãˆã³","ã‹ã„","ã¡ã„ã•ã„"], + "🦪": ["ã‹ã","ã—ã‚“ã˜ã‚…","ã ã„ã³ã‚“ã"], + "ðŸ ": ["ãã£ãŸã„ãŽã‚‡","ã•ã‹ãª","ãã£ãŸã„"], + "ðŸŸ": ["ã•ã‹ãª","ã†ãŠã–","ã›ã„ã–"], + "ðŸ¡": ["ãµã","ã•ã‹ãª"], + "ðŸ¬": ["ã„ã‚‹ã‹","ã²ã‚Œ"], + "🦈": ["ã•ã‚","ã•ã‹ãª"], + "ðŸ¦": ["ã‚ã–らã—","ã‚ã—ã‹"], + "ðŸ³": ["ã—ãŠãµããã˜ã‚‰","ã‹ãŠ","ã—ãŠãµã","ãã˜ã‚‰"], + "ðŸ‹": ["ãã˜ã‚‰"], + "ðŸŠ": ["ã‚ã«"], + "ðŸ†": ["ã²ã‚‡ã†"], + "ðŸ…": ["ã¨ã‚‰"], + "ðŸƒ": ["ã™ã„ãŽã‚…ã†","ã¿ãš"], + "ðŸ‚": ["ゆã†ã†ã—","ãŠã™ã†ã—","ãŠã†ã—ã–","ã›ã„ã–"], + "ðŸ„": ["ã†ã—"], + "🦬": ["ã°ã„ãã‚“","ã°ã£ãµãã‚ー","むれ","ヴãƒã›ã‚“ã¨"], + "ðŸª": ["ã²ã¨ã“ã¶ã‚‰ãã ","らãã ","ã“ã¶"], + "ðŸ«": ["ãµãŸã“ã¶ã‚‰ãã ","ãµãŸã“ã¶","らãã ","ã“ã¶"], + "🦙": ["らã¾","ã‚ã‚‹ã±ã‹","ãã‚ãªã“","ã³ãーã«ã‚ƒ","ã†ãƒ¼ã‚‹"], + "ðŸ˜": ["ãžã†"], + "ðŸ¦": ["ã•ã„"], + "🦛": ["ã‹ã°"], + "🦣": ["ã¾ã‚“ã‚‚ã™","ãœã¤ã‚ã¤","ãŠãŠãŒãŸ","ãã°","ã‘ã«ãŠãŠã‚ã‚ŒãŸ"], + "ðŸ": ["ã‚„ãŽ","ã‚„ãŽã–","ã›ã„ã–"], + "ðŸ": ["ã“ã²ã¤ã˜","ãŠã²ã¤ã˜ã–","ã²ã¤ã˜","ã›ã„ã–"], + "ðŸ‘": ["ã²ã¤ã˜","ã‚ã™ã²ã¤ã˜"], + "ðŸŽ": ["ã†ã¾","ã‘ã„ã°","れーã™"], + "ðŸ«": ["ã‚ã°","ã©ã†ã¶ã¤","ã¶ãƒ¼ã‚","ã»ã«ã‚…ã†ã‚‹ã„","らã°"], + "ðŸ–": ["ã¶ãŸ","ã‚ã™ã¶ãŸ"], + "🦇": ["ã“ã†ã‚‚ã‚Š","ãã‚…ã†ã‘ã¤ã"], + "ðŸ“": ["ãŠã‚“ã©ã‚Š"], + "🦃": ["ã—ã¡ã‚ã‚“ã¡ã‚‡ã†ï¼ˆã¨ã‚Šï¼‰","ã—ã¡ã‚ã‚“ã¡ã‚‡ã†","ã¨ã‚Š"], + "🕊": ["ã¸ã„ã‚ã®ã¯ã¨","ã¨ã‚Š","ã¯ã¨","ã²ã“ã†","ã¸ã„ã‚"], + "🦅": ["ã‚ã—","ã¨ã‚Š"], + "🦆": ["ã‚ã²ã‚‹","ã¨ã‚Š"], + "🪿": ["ãŒã¡ã‚‡ã†","ã¨ã‚Š","ã‹ãã‚“","ã‘ã„ã¦ãã®ãŠã¨"], + "🦢": ["ã¯ãã¡ã‚‡ã†","ã¨ã‚Š","ã¯ãã¡ã‚‡ã†ã®ãŠ","ã¿ã«ãã„ã‚ã²ã‚‹ã®ã“"], + "🦉": ["ãµãã‚ã†","ã¨ã‚Š","ã‹ã—ã“ã„"], + "🦩": ["ãµã‚‰ã¿ã‚“ã”","ãã£ãŸã„","ã‚ã–ã‚„ã‹"], + "🦚": ["ãŠã™ã®ãã˜ã‚ƒã","ã¨ã‚Š","ã‚ã™ã®ãã˜ã‚ƒã"], + "🦜": ["ãŠã†ã‚€","ã¨ã‚Š","ã‹ã„ãžã"], + "🦤": ["ã©ãƒ¼ã©ãƒ¼","ã¨ã‚Š","ãœã¤ã‚ã¤"], + "🪽": ["ã¯ã","ã¦ã‚“ã—","ã“ã†ãã†","ã¨ã‚Š","ã²ã“ã†","ã—ã‚“ã‚"], + "🪶": ["ã†ã‚‚ã†","ã¨ã‚Š","ã‹ã‚‹ã„","ã¯ã"], + "ðŸ•": ["ã„ã¬","ã‘ã‚“","ãºã£ã¨"], + "🦮": ["ã‚‚ã†ã©ã†ã‘ã‚“","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã‚ãŒãµã˜ã‚†ã†","ã‘ã‚“","ãŒã„ã©"], + "ðŸ•â€ðŸ¦º": ["ã‹ã„ã˜ã‚‡ã„ã¬","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã—ãˆã‚“","ã‘ã‚“","ã•ãƒ¼ã³ã™"], + "ðŸ©": ["ã·ãƒ¼ã©ã‚‹","ã„ã¬","ã‘ã‚“"], + "ðŸˆ": ["ãã“","ãºã£ã¨"], + "ðŸˆâ€â¬›": ["ãã‚ãã“","ãã‚","ãã“","ãºã£ã¨","ã¯ã‚ã†ãƒãƒ¼ã‚“"], + "ðŸ‡": ["ã†ã•ãŽ","ã°ã«ãƒ¼","ãºã£ã¨"], + "ðŸ€": ["ããšã¿"], + "ðŸ": ["ããšã¿"], + "ðŸ¿": ["ã—ã¾ã‚Šã™"], + "🦨": ["ã™ã‹ã‚“ã","ã‚ãã—ã‚…ã†","ã«ãŠã†"], + "🦡": ["ã‚ãªãã¾","らーã¦ã‚‹","ãã ã‚‹"], + "🦔": ["ã¯ã‚Šããšã¿","ã‹ãŠ"], + "ðŸ¾": ["ã©ã†ã¶ã¤ã®ã‚ã—ã‚ã¨","ã‚ã—","ã‚ã¨"], + "ðŸ‰": ["ã©ã‚‰ã”ã‚“","ãŠã¨ãŽã°ãªã—"], + "ðŸ²": ["ã©ã‚‰ã”ã‚“ã®ã‹ãŠ","ã©ã‚‰ã”ã‚“","ã‹ãŠ","ãŠã¨ãŽã°ãªã—"], + "🦕": ["ã‚Šã‚…ã†ã‚ã—ã‚‹ã„","ã¶ã‚‰ããŠã•ã†ã‚‹ã™","ã¶ã‚ã‚“ã¨ã•ã†ã‚‹ã™","ã§ãƒã·ã‚ã©ãã™","ãょã†ã‚Šã‚…ã†"], + "🦖": ["ã¦ãƒã‚‰ã®ã•ã†ã‚‹ã™","Tã‚Œã£ãã™","ãょã†ã‚Šã‚…ã†"], + "🌵": ["ã•ã¼ã¦ã‚“","ã—ょãã¶ã¤"], + "🎄": ["ãã‚Šã™ã¾ã™ã¤ã‚Šãƒ¼","ã‚ãã¦ãƒã³ã¦ãƒ","ãŠã„ã‚ã„","ãã‚Šã™ã¾ã™","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã¤ã‚Šãƒ¼"], + "🌲": ["ã˜ã‚‡ã†ã‚Šã‚‡ãã˜ã‚…","ã˜ã‚‡ã†ã‚Šã‚‡ã","ã—ょãã¶ã¤","ã¯ãŸ"], + "🌳": ["らãよã†ã˜ã‚…","らãよã†ã›ã„","ã—ょãã¶ã¤","らãよã†","ã¯ãŸ"], + "🌴": ["ã‚„ã—ã®ã","ã‚„ã—","ã—ょãã¶ã¤","ã¯ãŸ"], + "🪴": ["ã¯ã¡ã†ãˆ","ã—ょãã¶ã¤","ã‹ã‚“よã†ã—ょãã¶ã¤"], + "🌱": ["ãªãˆãŽ","ã—ょãã¶ã¤","ã‚ã‹ã„"], + "🌿": ["ã¯ãƒ¼ã¶","ã¯","ã—ょãã¶ã¤"], + "☘": ["ãã‚ーã°ãƒ¼","ã—ょãã¶ã¤"], + "ðŸ€": ["よã£ã¤ã¯ã®ãã‚ーã°ãƒ¼","4","ãã‚ーã°ãƒ¼","よん","ã¯","ã—ょãã¶ã¤"], + "ðŸŽ": ["ã‹ã©ã¾ã¤","ã‚ãã¦ãƒã³ã¦ãƒ","ãŸã‘","ãŠã„ã‚ã„","ã«ã£ã½ã‚“","ã¾ã¤","ã—ょãã¶ã¤"], + "🎋": ["ãªãªã‚†ã†","ã‚ãã¦ãƒã³ã¦ãƒ","ã¯ãŸ","ãŠã„ã‚ã„","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã«ã£ã½ã‚“"], + "ðŸƒ": ["ã‹ãœã«ãªã³ãã¯","ãµã","ã¯ãŸã‚ã","ã¯","ã—ょãã¶ã¤","ãµã†"], + "ðŸ‚": ["ãŠã¡ã°","らã£ã‹","ã¯","ã—ょãã¶ã¤"], + "ðŸ": ["ã‹ãˆã§ã®ã¯","らã£ã‹","ã¯","ã‹ãˆã§","ã—ょãã¶ã¤"], + "🌾": ["ã„ãªã»","ã„ããŸã°","ã»","ã—ょãã¶ã¤","ã“ã‚"], + "🪺": ["ãŸã¾ã”ã®ã‚ã‚‹ã™","ã™ã¥ãã‚Š","ã¨ã‚Šã®ã™","ãŸã¾ã”"], + "🪹": ["ãらã®ã™","ã™ã¥ãã‚Š","ã¨ã‚Šã®ã™"], + "🌺": ["ã¯ã„ã³ã™ã‹ã™","ã¯ãª","ã—ょãã¶ã¤"], + "🌻": ["ã²ã¾ã‚ã‚Š","ã¯ãª","ã—ょãã¶ã¤","ãŸã„よã†"], + "🌹": ["ã°ã‚‰","ã¯ãª","ã—ょãã¶ã¤"], + "🥀": ["ã—ãŠã‚ŒãŸã¯ãª","ã¯ãª","ã—ãŠã‚ŒãŸ"], + "🌷": ["ã¡ã‚…ーりã£ã·","ã¯ãª","ã—ょãã¶ã¤"], + "🌼": ["ã¯ãª","ã—ょãã¶ã¤"], + "🌸": ["ã•ãら","ã¯ãª","ã—ょãã¶ã¤"], + "🪷": ["ã¯ã™","ã¶ã£ãょã†","ã¯ãª","ã²ã‚“ã©ã…ーãょã†","ã„ã‚“ã©","ã›ã„ã˜ã‚‡ã†","ã¹ã¨ãªã‚€"], + "🪻": ["ã²ã‚ã—ã‚“ã™","ã¶ã‚‹ãƒ¼ã¼ã‚“ãã£ã¨","ã¯ãª","らã¹ã‚“ã ー","ã‚‹ã´ãªã™","ã®ã†ã‚‹ãƒ¼ãš","むらã•ã","ãã‚“ãŽã‚‡ãã†"], + "ðŸ’": ["ã¯ãªãŸã°","ã¯ãª","ã—ょãã¶ã¤","ã‚ã¾ã‚“ã™"], + "ðŸ„": ["ãã®ã“","ã—ょãã¶ã¤"], + "ðŸš": ["ã¾ããŒã„","ã‹ã„"], + "🪸": ["ã•ã‚“ã”","ãŸã„よã†","ã—ょã†"], + "🌎": ["ã‚ã‚ã‚Šã‹ãŸã„ã‚Šã","ã‚ã‚ã‚Šã‹","ã¡ãã‚…ã†","ã›ã‹ã„"], + "ðŸŒ": ["よーã‚ã£ã±ã¨ã‚ãµã‚Šã‹ã¡ã„ã","ã‚ãµã‚Šã‹","ã¡ãã‚…ã†","よーã‚ã£ã±","ã›ã‹ã„"], + "ðŸŒ": ["ã‚ã˜ã‚ã¨ãŠãƒ¼ã™ã¨ã‚‰ã‚Šã‚","ã‚ã˜ã‚","ãŠãƒ¼ã™ã¨ã‚‰ã‚Šã‚","ã¡ãã‚…ã†","ã›ã‹ã„"], + "🌕": ["ã¾ã‚“ã’ã¤","ã¤ã","ã†ã¡ã‚…ã†","ã¦ã‚“ã"], + "🌖": ["ãã¾ã¡ã®ã¤ã","ã˜ã‚…ã†ã•ã‚“ã‚„","ã¤ã","ã†ã¡ã‚…ã†","ã‹ã‘","ã¦ã‚“ã"], + "🌗": ["ã‹ã’ã‚“ã®ã¤ã","ã¤ã","ã’ã‚“","ã†ã¡ã‚…ã†","ã¦ã‚“ã"], + "🌘": ["ã‹ã‘ã¦ã„ãã¿ã‹ã¥ã","ã•ã‚“ã˜ã¤ã’ã¤","ã¤ã","ã†ã¡ã‚…ã†","ã‹ã‘","ã¦ã‚“ã"], + "🌑": ["ã—ã‚“ã’ã¤","ã‹ã„","ã¤ã","ã†ã¡ã‚…ã†","ã¦ã‚“ã"], + "🌒": ["ã¿ã¡ã¦ã„ãã¿ã‹ã¥ã","ã•ã‚“ã˜ã¤ã’ã¤","ã¤ã","ã†ã¡ã‚…ã†","ã˜ã‚‡ã†ã’ã‚“","ã¦ã‚“ã"], + "🌓": ["ã˜ã‚‡ã†ã’ã‚“ã®ã¤ã","ã¤ã","ã’ã‚“","ã†ã¡ã‚…ã†","ã¦ã‚“ã"], + "🌔": ["ã˜ã‚…ã†ã•ã‚“ã‚„ã¤ã","ã˜ã‚…ã†ã•ã‚“ã‚„","ã¤ã","ã†ã¡ã‚…ã†","ã˜ã‚‡ã†ã’ã‚“","ã¦ã‚“ã"], + "🌙": ["ã•ã‚“ã˜ã¤ã’ã¤","ã¤ã","ã†ã¡ã‚…ã†","ã¦ã‚“ã"], + "🌚": ["ã‹ãŠã¤ãã—ã‚“ã’ã¤","ã‹ãŠ","ã¤ã","ã†ã¡ã‚…ã†","ã¦ã‚“ã"], + "ðŸŒ": ["ã‹ãŠã¤ãã¾ã‚“ã’ã¤","ã‚ã‹ã‚‹ã„","ã‹ãŠ","ã¿ã¡ãŸ","ã¤ã","ã†ã¡ã‚…ã†","ã¦ã‚“ã"], + "🌛": ["ã‹ãŠã¤ãã˜ã‚‡ã†ã’ã‚“ã®ã¤ã","ã‹ãŠ","ã¤ã","ã’ã‚“","ã†ã¡ã‚…ã†","ã¦ã‚“ã"], + "🌜": ["ãŒãŠãŒã‚ã‚‹ã‹ã’ã‚“ã®ã¤ã","ã‹ãŠ","ã¤ã","ã’ã‚“","ã†ã¡ã‚…ã†","ã¦ã‚“ã"], + "â": ["ã¡ã‚…ã†ãらã„ã®ã»ã—","ã»ã—"], + "🌟": ["ã²ã‹ã‚‹ã»ã—","ãらã‚ã","ã‚ã‹ã„ã²ã‹ã‚Š","ã‹ãŒã‚„ã","ã‹ãŒã‚„ã","ã»ã—"], + "💫": ["ãらãら","ã¾ã‚“ãŒ","ã‚ã¾ã„","ã»ã—"], + "✨": ["ãらãら","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã‹ãŒã‚„ã","ã»ã—"], + "☄": ["ã™ã„ã›ã„","ã†ã¡ã‚…ã†"], + "ðŸª": ["ãŸã¾ãã®ã‚ã‚‹ã‚ãã›ã„","ã†ã¡ã‚…ã†","ã‚ãã›ã„","ã©ã›ã„"], + "🌞": ["ã‹ãŠã¤ããŸã„よã†","ã‚ã‹ã‚‹ã„","ã‹ãŠ","ã†ã¡ã‚…ã†","ãŸã„よã†","ã¦ã‚“ã"], + "☀ï¸": ["ãŸã„よã†ã®ã²ã‹ã‚Š","ã‚ã‹ã‚‹ã„","ã“ã†ã›ã‚“","ã†ã¡ã‚…ã†","ãŸã„よã†","ã›ã„ã¦ã‚“","ã¦ã‚“ã"], + "🌤": ["ãŸã„よã†ã¨ã¡ã„ã•ãªãã‚‚","ãã‚‚","ãŸã„よã†","ã¦ã‚“ã"], + "â›…": ["ã¯ã‚Œã¨ãã©ããã‚‚ã‚Š","ãã‚‚","ãŸã„よã†","ã¦ã‚“ã"], + "🌥": ["ã¯ã‚Œã®ã¡ãã‚‚ã‚Š","ãã‚‚","ãŸã„よã†","ã¦ã‚“ã"], + "🌦": ["ã¯ã‚Œã®ã¡ãã‚‚ã‚Šã¨ãã©ãã‚ã‚","ãã‚‚","ã‚ã‚","ãŸã„よã†","ã¦ã‚“ã"], + "â˜ï¸": ["ãã‚‚","ã¦ã‚“ã"], + "🌧": ["ã‚ã¾ãã‚‚","ãã‚‚","ã‚ã‚","ã¦ã‚“ã"], + "⛈": ["らã„ã†","ãã‚‚","ã‚ã‚","ã‹ã¿ãªã‚Š","ã¦ã‚“ã"], + "🌩": ["らã„ã†ã‚“","ãã‚‚","ã‹ã¿ãªã‚Š","ã¦ã‚“ã"], + "âš¡": ["ã ã‹ã§ã‚“ã‚ã¤ãã”ã†","ãã‘ã‚“","ã§ã‚“ã","ã‹ã¿ãªã‚Š","ã§ã‚“ã‚ã¤","ã³ã‚Šã³ã‚Š"], + "🔥": ["ãˆã‚“","ã²","ã©ã†ã"], + "💥": ["ã—ょã†ã¨ã¤ã¾ãƒ¼ã","ã©ã‹ãƒ¼ã‚“","ã—ょã†ã¨ã¤","ã¾ã‚“ãŒ"], + "â„ï¸": ["ã›ã¤ã®ã‘ã£ã—ょã†","ã¤ã‚ãŸã„","ゆã","ã¦ã‚“ã"], + "🌨": ["ゆããã‚‚","ãã‚‚","ã‚Œã„","ゆã","ã¦ã‚“ã"], + "☃": ["ゆãã ã‚‹ã¾","ã‚Œã„","ゆã","ã¦ã‚“ã"], + "⛄": ["ゆãã ã‚‹ã¾","ã‚Œã„","ゆã","ã¦ã‚“ã"], + "🌬": ["ã‹ãœãŒãµã„ã¦ã„ã‚‹","ã‹ãœãŒãµã","ãã‚‚","ã‹ãŠ","ã¦ã‚“ã","ãµã†"], + "💨": ["ã ã£ã—ã‚…","ã¾ã‚“ãŒ","ã¯ã—ã‚‹"], + "🌪": ["ãŸã¤ã¾ããã‚‚","ãã‚‚","ãŸã¤ã¾ã","ã¦ã‚“ã","ã›ã‚“ã·ã†"], + "🌫": ["ãã‚Š","ãã‚‚","ã¦ã‚“ã"], + "🌈": ["ã«ã˜","ã‚ã‚","ã‚Œã„ã‚“ã¼ãƒ¼","ã¦ã‚“ã","ã·ã‚‰ã„ã©","lgbt"], + "☔": ["ã†ã¨ã‹ã•","ã„ã‚‹ã„","ã—ãšã","ã‚ã‚","ã‹ã•","ã¦ã‚“ã"], + "💧": ["ã—ãšã","ãžã£ã¨ã™ã‚‹","ã¾ã‚“ãŒ","ã—ãŸãŸã‚Š","ã‚ã›","ã¦ã‚“ã"], + "💦": ["ã‚ã›ã¾ãƒ¼ã","ã¾ã‚“ãŒ","ã¬ã‚Œã¦ã„ã‚‹","ã‚ã›"], + "🌊": ["ãªã¿","ã†ã¿","ã¿ãš","ã¦ã‚“ã"], + "ðŸ": ["ã‚ãŠã‚Šã‚“ã”","ã‚Šã‚“ã”","ãµã‚‹ãƒ¼ã¤","ãã ã‚‚ã®","ã¿ã©ã‚Š","ã—ょãã¶ã¤"], + "ðŸŽ": ["ã‚ã‹ã„ã‚Šã‚“ã”","ã‚Šã‚“ã”","ãµã‚‹ãƒ¼ã¤","ãã ã‚‚ã®","ã—ょãã¶ã¤","ã‚ã‹"], + "ðŸ": ["ãªã—","ãµã‚‹ãƒ¼ã¤","ãã ã‚‚ã®","ã—ょãã¶ã¤"], + "ðŸŠ": ["ã¿ã‹ã‚“","ãµã‚‹ãƒ¼ã¤","ãã ã‚‚ã®","ãŠã‚Œã‚“ã˜","ã—ょãã¶ã¤","ã‚ã‹ã ã„ã ã„ã„ã‚"], + "ðŸ‹": ["れもん","ã‹ã‚“ãã¤ã‚‹ã„","ãµã‚‹ãƒ¼ã¤","ãã ã‚‚ã®","ã—ょãã¶ã¤"], + "ðŸŒ": ["ã°ãªãª","ãµã‚‹ãƒ¼ã¤","ãã ã‚‚ã®","ã—ょãã¶ã¤"], + "ðŸ‰": ["ã™ã„ã‹","ãµã‚‹ãƒ¼ã¤","ãã ã‚‚ã®","ã—ょãã¶ã¤"], + "ðŸ‡": ["ã¶ã©ã†","ãµã‚‹ãƒ¼ã¤","ãã ã‚‚ã®","ã—ょãã¶ã¤"], + "ðŸ“": ["ã„ã¡ã”","ã¹ã‚Šãƒ¼","ãµã‚‹ãƒ¼ã¤","ãã ã‚‚ã®","ã—ょãã¶ã¤"], + "ðŸˆ": ["ã‚ã‚ã‚“","ãµã‚‹ãƒ¼ã¤","ãã ã‚‚ã®","ã—ょãã¶ã¤"], + "ðŸ’": ["ã•ãらんã¼","ãµã‚‹ãƒ¼ã¤","ãã ã‚‚ã®","ã—ょãã¶ã¤"], + "ðŸ«": ["ã¶ã‚‹ãƒ¼ã¹ã‚Šãƒ¼","ã¹ã‚Šãƒ¼","ã³ã‚‹ã¹ã‚Šãƒ¼","ã‚ãŠ","ãµã‚‹ãƒ¼ã¤"], + "ðŸ‘": ["ã‚‚ã‚‚","ãµã‚‹ãƒ¼ã¤","ãã ã‚‚ã®","ã—ょãã¶ã¤"], + "ðŸ¥": ["ã¾ã‚“ã”ー","ãã£ãŸã„","ãµã‚‹ãƒ¼ã¤"], + "ðŸ": ["ã±ã„ãªã£ã·ã‚‹","ãµã‚‹ãƒ¼ã¤","ãã ã‚‚ã®","ã—ょãã¶ã¤"], + "🥥": ["ã“ã“ãªã£ã¤","ãµã‚‹ãƒ¼ã¤"], + "ðŸ¥": ["ãã†ã„ãµã‚‹ãƒ¼ã¤","ãµã‚‹ãƒ¼ã¤","ãã ã‚‚ã®","ãã†ã„"], + "ðŸ…": ["ã¨ã¾ã¨","ã—ょãã¶ã¤","ã‚„ã•ã„"], + "🥑": ["ã‚ã¼ã‹ã©","ãµã‚‹ãƒ¼ã¤","ãã ã‚‚ã®"], + "🫒": ["ãŠã‚Šãƒ¼ã¶","ãµã‚‹ãƒ¼ã¤"], + "ðŸ†": ["ãªã™","ãªã™ã³","ã—ょãã¶ã¤","ã‚„ã•ã„"], + "🌶": ["ã¨ã†ãŒã‚‰ã—","ã‹ã‚‰ã„","ã“ã—ょã†","ã—ょãã¶ã¤"], + "🫑": ["ã´ãƒ¼ã¾ã‚“","ã¨ã†ãŒã‚‰ã—","ã“ã—ょã†","ã—ょãã¶ã¤","ã‚„ã•ã„"], + "🥒": ["ãã‚…ã†ã‚Š","ã´ãã‚‹ã™","ã‚„ã•ã„"], + "🥬": ["ã¯ã£ã±ã®ã¿ã©ã‚Š","ã¡ã‚“ã’ã‚“ã•ã„","ãゃã¹ã¤","ã‘ーる","ã‚ŒãŸã™"], + "🥦": ["ã¶ã‚ã£ã“りー","ã‚„ã•ã„"], + "🫛": ["ãˆã‚“ã©ã†ã¾ã‚ã®ã•ã‚„","ã¾ã‚","ãˆã ã¾ã‚","ã¾ã‚ã‹","ãˆã‚“ã©ã†ã¾ã‚","ã•ã‚„","ã‚„ã•ã„"], + "🧄": ["ã«ã‚“ã«ã","ã‚„ã•ã„","ã—ょãã¶ã¤","ã“ã†ã¿ã‚Šã‚‡ã†"], + "🧅": ["ãŸã¾ããŽ","ã‚„ã•ã„","ã—ょãã¶ã¤","ã“ã†ã¿ã‚Šã‚‡ã†"], + "🌽": ["ã¨ã†ã‚‚ã‚ã“ã—","ã“ーん","ã—ょãã¶ã¤"], + "🥕": ["ã«ã‚“ã˜ã‚“","ã‚„ã•ã„"], + "🥗": ["ãりーんã•ã‚‰ã ","ã¿ã©ã‚Š","ã•ã‚‰ã "], + "🥔": ["ã˜ã‚ƒãŒã„ã‚‚","ã‚„ã•ã„"], + "ðŸ ": ["ã‚„ãã„ã‚‚","ã˜ã‚ƒãŒã„ã‚‚","ã‚„ã","ã™ã„ーã¤"], + "🌰": ["ãã‚Š","ã—ょãã¶ã¤"], + "🥜": ["ã´ãƒ¼ãªã£ã¤","ãªã£ã¤","ã‚„ã•ã„"], + "🫘": ["ã¾ã‚","ãŸã¹ã‚‚ã®","ã˜ã‚“ãžã†"], + "ðŸ¯": ["ã¯ã«ãƒ¼ã½ã£ã¨","ã¯ã¡ã¿ã¤","ã½ã£ã¨","ã™ã„ーã¤"], + "ðŸž": ["ã±ã‚“","ã‚ーãµ"], + "ðŸ¥": ["ãã‚ã‚ã£ã•ã‚“","ã±ã‚“","ã•ã‚“ã˜ã¤ã’ã¤","ã‚ーる","ãµã‚Œã‚“ã¡"], + "🥖": ["ãµã‚‰ã‚“ã™ã±ã‚“","ã±ã‚“","ãµã‚Œã‚“ã¡"], + "🫓": ["ãµã‚‰ã£ã¨ã¶ã‚Œã£ã©","ã‚ã‚Œã±","らヴãã—ã‚…","ãªã‚“","ã´ãŸ"], + "🥨": ["ã·ã‚Œã£ã¤ã‡ã‚‹","ããµã¨ã·ã‚Œã£ã¤ã‡ã‚‹","ã·ã‚Œã£ã¤ã‡ã‚‹ã¤ã„ã™ã¨","ã±ã‚“"], + "🥯": ["ã¹ãƒ¼ãã‚‹","ã±ã‚“","ãりーむã¡ãƒ¼ãš","ã²ã¨ã¬ã‚Š"], + "🥞": ["ã±ã‚“ã‘ーã","ãれーã·","ã»ã£ã¨ã‘ーã"], + "🧇": ["ã‚ã£ãµã‚‹","ã»ã£ã¨ã‘ーã"], + "🧀": ["ã¡ãƒ¼ãš"], + "ðŸ—": ["ãŸãƒ¼ãー","ã»ã","ã«ã‚ã¨ã‚Š","ã‚ã—","ã‹ãã‚“"], + "ðŸ–": ["ã»ãã¤ãã«ã","ã»ã","ã«ã"], + "🥩": ["ã„ã¡ãã‚Œã®ã«ã","ã«ã","ãã‚Šã¿","らむã¡ã‚‡ã£ã·","ã¶ãŸ","ã™ã¦ãƒ¼ã"], + "ðŸ¤": ["ãˆã³ãµã‚‰ã„","ãµã‚‰ã„","ãˆã³","ã“ãˆã³","ã¦ã‚“ã·ã‚‰"], + "🥚": ["ãŸã¾ã”"], + "ðŸ³": ["りょã†ã‚Š","ãŸã¾ã”","ãµã‚‰ã„ã±ã‚“","ãªã¹"], + "🥓": ["ã¹ãƒ¼ã“ã‚“","ã«ã"], + "ðŸ”": ["ã¯ã‚“ã°ãƒ¼ãŒãƒ¼","ã°ãƒ¼ãŒãƒ¼"], + "ðŸŸ": ["ãµã‚‰ã„ã©ã½ã¦ã¨","ãµã‚‰ã„ã©","ã½ã¦ã¨"], + "ðŸŒ": ["ã»ã£ã¨ã©ã£ã","ãµã‚‰ã‚“ããµã‚‹ã¨ãーã›ãƒ¼ã˜","ã»ã£ã¨ã©ã£ããーã›ãƒ¼ã˜","ãーã›ãƒ¼ã˜","ã†ãƒã‚“ãªãƒ¼","ã‚Œã£ã©ã»ã£ã¨"], + "ðŸ•": ["ã´ã–","ã¡ãƒ¼ãš","1ã¾ã„"], + "ðŸ": ["ã™ã±ã’ã£ã¦ãƒ","ã±ã™ãŸ"], + "🥪": ["ã•ã‚“ã©ã†ãƒã£ã¡","ã±ã‚“","ã‚„ã•ã„","ã¡ãƒ¼ãš","ã«ã","ã§ã‚Š"], + "🌮": ["ãŸã“ã™","ã‚ãã—ã“"], + "🌯": ["ã¶ã‚Šã¨ãƒ¼","ã‚ãã—ã“"], + "🫔": ["ãŸã¾ãƒ¼ã‚Œ","ãŸã¾ãƒ¼ã‚Š","ã‚ãã—ã‹ã‚“","ã¤ã¤ã¾ã‚ŒãŸ"], + "🥙": ["ãµã‚‰ã£ã¨ã¶ã‚Œã£ã©ã•ã‚“ã©","ãµãらãµã‡ã‚‹","ãµã‚‰ã£ã¨ã¶ã‚Œã£ã©","ã˜ã‚ƒã„ã‚","ã‘ã°ã¶","ã¤ã‚ã‚‚ã®"], + "🧆": ["ãµãらãµã‡ã‚‹","ã²ã‚ˆã“ã¾ã‚"], + "ðŸœ": ["ã©ã‚“ã¶ã‚Š","ã‚ã‚“","らーã‚ã‚“","ã‚€ã—ã‹ãã¤","ã™ãƒ¼ã·"], + "🥘": ["ã±ãˆã‚Šã‚","ãゃã›ã‚ーる","ãªã¹","ã‚ã•ã„"], + "ðŸ²": ["ãªã¹","ã—ã¡ã‚…ー"], + "🫕": ["ãµã‰ã‚“ã§ã‚…","ã¡ãƒ¼ãš","ã¡ã‚‡ã“れーã¨","ãµã‰ã§ã‚…","ã¨ã‘ãŸ","ã½ã£ã¨","ã™ã„ã™"], + "🥫": ["ã‹ã‚“ã¥ã‚","ã»ãžã‚“よã†ã—ょãã²ã‚“"], + "🫙": ["ã³ã‚“","ã“ã†ã—んりょã†","よã†ã","ãら","ãーã™","ã¡ã‚‡ãžã†"], + "🧂": ["ã—ãŠ","ã“ã†ã—んりょã†","ã—ã‡ãƒ¼ã‹ãƒ¼"], + "🧈": ["ã°ãŸãƒ¼","ã«ã‚…ã†ã›ã„ã²ã‚“"], + "🫚": ["ã—ょã†ãŒ","ã³ãƒ¼ã‚‹","ã","ã™ã±ã„ã™"], + "ðŸ¥": ["ãªã‚‹ã¨","ã“ã‘ã„ã®ãŸã¹ã‚‚ã®","ã•ã‹ãª","ãã‚Šã‚‚ã®"], + "ðŸ£": ["ã™ã—"], + "ðŸ±": ["ã¹ã‚“ã¨ã†ã°ã“","ã¹ã‚“ã¨ã†","ã¯ã“"], + "ðŸ›": ["ã‹ã‚Œãƒ¼ã‚‰ã„ã™","ã‹ã‚Œãƒ¼","ã”ã¯ã‚“"], + "ðŸ™": ["ãŠã«ãŽã‚Š","ã«ã£ã½ã‚“","ã“ã‚"], + "ðŸš": ["ã”ã¯ã‚“","りょã†ã‚Š","ã“ã‚"], + "ðŸ˜": ["ã›ã‚“ã¹ã„","ã“ã‚"], + "🥟": ["ãŽã‚‡ã†ã–"], + "ðŸ¢": ["ãŠã§ã‚“","ã—ーãµãƒ¼ã©","ãã—","ã™ã¦ãƒã£ã"], + "ðŸ¡": ["ã ã‚“ã”","ã§ã–ーã¨","ã«ã£ã½ã‚“","ãã—","ã™ã¦ãƒã£ã","ã™ã„ーã¤"], + "ðŸ§": ["ã‹ãã”ãŠã‚Š","ã§ã–ーã¨","ã“ãŠã‚Š","ã™ã„ーã¤"], + "ðŸ¨": ["ã‚ã„ã™ãりーむ","ãりーむ","ã§ã–ーã¨","ã“ãŠã‚Š","ã™ã„ーã¤"], + "ðŸ¦": ["ããµã¨ãりーむ","ãりーむ","ã§ã–ーã¨","ã“ãŠã‚Š","ã‚ã„ã™ãりーむ","ããµã¨","ã™ã„ーã¤"], + "ðŸ°": ["ã—ょーã¨ã‘ーã","ã‘ーã","ã§ã–ーã¨","ãºã„ã™ã¨ã‚Šãƒ¼","ã™ã‚‰ã„ã™","ã™ã„ーã¤"], + "🎂": ["ã°ãƒ¼ã™ã§ãƒ¼ã‘ーã","ãŸã‚“ã˜ã‚‡ã†ã³","ã‘ーã","ãŠã„ã‚ã„","ã§ã–ーã¨","ãºã„ã™ã¨ã‚Šãƒ¼","ã™ã„ーã¤"], + "ðŸ§": ["ã‹ã£ã·ã‘ーã","ã¹ãƒ¼ã‹ã‚Šãƒ¼","ã™ã„ーã¤","ã§ã–ーã¨","ãºã„ã™ã¨ã‚Šãƒ¼"], + "🥧": ["ã±ã„","ã§ã–ーã¨","ã™ã„ーã¤"], + "ðŸ®": ["ã‹ã™ãŸãƒ¼ã©","ã§ã–ーã¨","ã·ã‚Šã‚“","ã™ã„ーã¤"], + "ðŸ": ["ãºã‚ãºã‚ãゃんã§ãƒãƒ¼","ãゃんã§ãƒ","ã§ã–ーã¨","ã‚ã‚Šã½ã£ã·ãゃんã§ãƒ","ã™ã„ーã¤"], + "ðŸ¬": ["ã‚ã‚","ã§ã–ーã¨","ã™ã„ーã¤"], + "ðŸ«": ["ã¡ã‚‡ã“れーã¨","ã°ãƒ¼","ã§ã–ーã¨","ã™ã„ーã¤"], + "ðŸ¿": ["ã½ã£ã·ã“ーん"], + "ðŸ©": ["ã©ãƒ¼ãªã¤","ã§ã–ーã¨","ã™ã„ーã¤"], + "ðŸª": ["ãã£ãー","ã§ã–ーã¨","ã‚ã¾ã„"], + "🥠": ["ãŠã¿ãã˜ã„ã‚Šãã£ãー","ãµã‰ãƒ¼ã¡ã‚…ã‚“ãã£ãー"], + "🥮": ["ã’ã£ãºã„","ã‚ã","ã¾ã¤ã‚Š"], + "☕": ["ã»ã£ã¨ã©ã‚Šã‚“ã","ã„んりょã†","ã“ーã²ãƒ¼","ã®ã¿ã‚‚ã®","ã‚ãŸãŸã‹ã„","ã˜ã‚‡ã†ã","ãŠã¡ã‚ƒ"], + "ðŸµ": ["ゆã®ã¿","ã„んりょã†","ã‹ã£ã·","ã®ã¿ã‚‚ã®","ãŠã¡ã‚ƒ"], + "🫖": ["ã¦ãƒãƒ¼ã½ã£ã¨","ã©ã‚Šã‚“ã","ã½ã£ã¨","ã¦ãƒãƒ¼","ã‘ã¨ã‚‹"], + "🥣": ["ã¼ã†ã‚‹ã¨ã™ã·ãƒ¼ã‚“","ã¡ã‚‡ã†ã—ょã","ã—ã‚Šã‚ã‚‹","ãŠã‹ã‚†","ãŠãƒ¼ã¨ã¿ãƒ¼ã‚‹","ã½ã‚Šã£ã˜","ã—ょã£ã"], + "ðŸ¼": ["ã»ã«ã‚…ã†ã³ã‚“","ã‚ã‹ã¡ã‚ƒã‚“","ã¼ã¨ã‚‹","ã©ã‚Šã‚“ã","ã¿ã‚‹ã"], + "🥤": ["ã‹ã£ã·ã¨ã™ã¨ã‚ー","ã˜ã‚…ーã™","ãーã ","ã‚‚ã‚‹ã¨","ããµã¨ã©ã‚Šã‚“ã","ã¿ãš","ã—ょã£ã"], + "🧋": ["ãŸã´ãŠã‹ã¦ãƒãƒ¼","ã°ã¶ã‚‹","ã¿ã‚‹ã","ã±ãƒ¼ã‚‹","ã¦ãƒãƒ¼","ã¼ã°","ãŸã´ãŠã‹","ã‚‚ã¿"], + "🧃": ["ã„んりょã†ã¼ã£ãã™","ã˜ã‚…ーã™","ã„んりょã†","ã¼ã£ãã™","ã©ã‚Šã‚“ã","ã™ã¨ã‚ー"], + "🧉": ["ã¾ã¦","ã©ã‚Šã‚“ã","ã¼ã‚“ã³ã‚Šã‚„","ã„ãˆã‚‹ã°"], + "🥛": ["ã“ã£ã·ã«ã¯ã„ã£ãŸãŽã‚…ã†ã«ã‚…ã†","ã©ã‚Šã‚“ã","ãらã™","ã¿ã‚‹ã"], + "🫗": ["ãªãŒã‚Œã“ã‚€ãˆããŸã„","ã®ã¿ã‚‚ã®","ãら","ãらã™","ã“ã¼ã‚Œã‚‹"], + "ðŸº": ["ã³ãƒ¼ã‚‹","ã°ãƒ¼","ã®ã‚€","ã¾ãã‹ã£ã·"], + "ðŸ»": ["ã‹ã‚“ã±ã„","ã°ãƒ¼","ã³ãƒ¼ã‚‹","ã‹ã¡ã‚“","ã®ã¿ã‚‚ã®","ã¾ãã‹ã£ã·"], + "ðŸ·": ["ã‚ã„ã‚“ãらã™","ã°ãƒ¼","ã„んりょã†","ã®ã¿ã‚‚ã®","ãらã™","ã‚ã„ã‚“"], + "🥂": ["ãらã™ã§ã‹ã‚“ã±ã„","ã„ã‚ã†","ã‹ã¡ã‚“","ã®ã¿ã‚‚ã®","ãらã™"], + "🥃": ["ãŸã‚“ã¶ã‚‰ãƒ¼","ãらã™","ã¦","ã—ょã£ã¨","ã†ã„ã™ãー","ã†ãƒã™ãー","ã°ãƒ¼ã¼ã‚“"], + "ðŸ¸": ["ã‹ãã¦ã‚‹ãらã™","ã°ãƒ¼","ã‹ãã¦ã‚‹","ã®ã¿ã‚‚ã®","ãらã™"], + "ðŸ¹": ["ã¨ã‚ã´ã‹ã‚‹ã©ã‚Šã‚“ã","ã°ãƒ¼","ã®ã¿ã‚‚ã®","ã¨ã‚ã´ã‹ã‚‹"], + "ðŸ¾": ["ã³ã‚“ã¨ã¨ã³ã ã™ã›ã‚“","ã°ãƒ¼","ã¼ã¨ã‚‹","ã—ゃんã±ã‚“","ã—ゃんãºã‚“","ã—ゃんã±ãƒ¼ã«ã‚…","ã“ã‚‹ã","ã®ã¿ã‚‚ã®","ã¨ã³ã ã™","ã±ãƒ¼ã¦ãƒãƒ¼"], + "ðŸ¶": ["ã¨ã£ãã‚Šã¨ãŠã¡ã‚‡ã“","ã°ãƒ¼","ã„んりょã†","ã¼ã¨ã‚‹","ã‹ã£ã·","ã®ã¿ã‚‚ã®","ã¦"], + "🧊": ["ã‹ãã“ãŠã‚Š","ã“ãŠã‚Š","ã‚Šã£ã½ã†ãŸã„","ã¤ã‚ãŸã„","ã²ã‚‡ã†ã–ã‚“"], + "🥄": ["ã™ã·ãƒ¼ã‚“","ã—ょã£ã"], + "ðŸ´": ["ãµã‰ãƒ¼ãã¨ãªã„ãµ","ã¡ã‚‡ã†ã‚Š","ãµã‰ãƒ¼ã","ãªã„ãµ","ã—ょã£ã"], + "ðŸ½": ["ãµã‰ãƒ¼ãã¨ãªã„ãµã¨ã·ã‚Œãƒ¼ã¨","ã¡ã‚‡ã†ã‚Š","ãµã‰ãƒ¼ã","ãªã„ãµ","ã·ã‚Œãƒ¼ã¨","ã—ょã£ã"], + "🥢": ["ã¯ã—"], + "🥡": ["ã¦ã„ãã‚ã†ã¨ã¼ã£ãã™","ã¦ã„ãã‚ã†ã¨","よã†ã","ãŠã‚‚ã¡ã‹ãˆã‚Š"], + "âš½": ["ã•ã£ã‹ãƒ¼ã¼ãƒ¼ã‚‹","ã¼ãƒ¼ã‚‹","ã•ã£ã‹ãƒ¼"], + "ðŸ€": ["ã°ã™ã‘ã£ã¨ã¼ãƒ¼ã‚‹","ã¼ãƒ¼ã‚‹","ã°ã™ã‘ã£ã¨ã‚Šã‚“ã"], + "ðŸˆ": ["ã‚ã‚ã‚Šã‹ã‚“ãµã£ã¨ã¼ãƒ¼ã‚‹","ã‚ã‚ã‚Šã‹ã‚“","ã¼ãƒ¼ã‚‹","ãµã£ã¨ã¼ãƒ¼ã‚‹"], + "âš¾": ["ã‚„ãã‚…ã†","ã¼ãƒ¼ã‚‹"], + "🥎": ["ããµã¨ã¼ãƒ¼ã‚‹","ã¼ãƒ¼ã‚‹","ã—ã‚ã„","ã™ã½ãƒ¼ã¤"], + "🎾": ["ã¦ã«ã™ã¼ãƒ¼ã‚‹","ã¼ãƒ¼ã‚‹","らã‘ã£ã¨","ã¦ã«ã™"], + "ðŸ": ["ã°ã‚Œãƒ¼ã¼ãƒ¼ã‚‹","ã¼ãƒ¼ã‚‹","ã—ã‚ã„"], + "ðŸ‰": ["らãã³ãƒ¼","ã¼ãƒ¼ã‚‹","ãµã£ã¨ã¼ãƒ¼ã‚‹"], + "🎱": ["ã³ã‚Šã‚„ーã©","8","ãˆã„ã¨ã¼ãƒ¼ã‚‹","ã¼ãƒ¼ã‚‹","ãˆã„ã¨","ã’ーむ"], + "ðŸ¥": ["ãらã¨ã¶ãˆã‚“ã°ã‚“","ã§ãƒã™ã","ã‚ã‚‹ã¦ãƒã‚ã£ã¨","ã”ã‚‹ãµ","ã—ã‚ã„","ã™ã½ãƒ¼ã¤","ãµã‚Šã™ã³ãƒ¼"], + "🪃": ["ã¶ãƒ¼ã‚らん","ãŠãƒ¼ã™ã¨ã‚‰ã‚Šã‚","ãŽã‚ƒãã‚‚ã©ã‚Š","ã¯ãã‹ãˆã‚Š"], + "ðŸ“": ["ãŸã£ãã‚…ã†ã®ã‚‰ã‘ã£ã¨ã¨ã¼ãƒ¼ã‚‹","ã¼ãƒ¼ã‚‹","ã°ã£ã¨","ã—ã‚ã„","ã±ã©ã‚‹","ãŸã£ãã‚…ã†"], + "ðŸ¸": ["ã°ã©ã¿ã‚“ã¨ã‚“ã®ã‚‰ã‘ã£ã¨ã¨ã—ゃã¨ã‚‹","ã°ã©ã¿ã‚“ã¨ã‚“","ã°ãƒ¼ã§ãƒãƒ¼","ã—ã‚ã„","らã‘ã£ã¨","ã—ゃã¨ã‚‹"], + "🥅": ["ã”ーるãã£ã¨","ã”ーる","ãã£ã¨"], + "ðŸ’": ["ã‚ã„ã™ã»ã£ã‘ーã®ã™ã¦ãƒã£ãã¨ã±ã£ã","ã—ã‚ã„","ã»ã£ã‘ー","ã“ãŠã‚Š","ã±ã£ã","ã™ã¦ãƒã£ã"], + "ðŸ‘": ["ãµãƒãƒ¼ã‚‹ã©ã»ã£ã‘ーã®ã™ã¦ãƒã£ãã¨ã¼ãƒ¼ã‚‹","ã¼ãƒ¼ã‚‹","ãµãƒãƒ¼ã‚‹ã©","ã—ã‚ã„","ã»ã£ã‘ー","ã™ã¦ãƒã£ã"], + "ðŸ": ["ãã‚Šã‘ã£ã¨ã®ã°ã£ã¨ã¨ã¼ãƒ¼ã‚‹","ã¼ãƒ¼ã‚‹","ãµãƒãƒ¼ã‚‹ã©","ãã‚Šã‘ã£ã¨","ã—ã‚ã„"], + "ðŸ¥": ["らãã‚ã™","ã¼ãƒ¼ã‚‹","ã™ã¦ãƒã£ã","ã—ã‚ã„","ã™ã½ãƒ¼ã¤"], + "🥌": ["ã‹ãƒ¼ã‚Šã‚“ãã™ã¨ãƒ¼ã‚“","ã‹ãƒ¼ã‚Šã‚“ã","ã™ã¨ãƒ¼ã‚“"], + "⛳": ["ã”ã‚‹ãµã®ã‹ã£ã·","ã´ã‚“ãµã‚‰ã£ã","ã”ã‚‹ãµ","ã»ãƒ¼ã‚‹"], + "ðŸ¹": ["ゆã¿ã‚„","ã—ゃã—ã‚…","ã‚„","ゆã¿","ã—ゃã—ã‚…ã–","ã©ã†ã","ã›ã„ã–"], + "🎣": ["ã¤ã‚Šã–ãŠã¨ã•ã‹ãª","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã•ã‹ãª","ã¼ã†"], + "🤿": ["ã ã„ã³ã‚“ãã¾ã™ã","ã ã„ã³ã‚“ã","ã™ãゅーã°","ã—ã‚…ã®ãƒ¼ã‘ã‚‹"], + "🥊": ["ã¼ãã—ã‚“ããã‚ーã¶","ã¼ãã—ã‚“ã","ãã‚ーã¶"], + "🥋": ["ã©ã†ãŽ","ã˜ã‚…ã†ã©ã†","ã‹ã‚‰ã¦","ã¶ã©ã†","ã¦ã“ã‚“ã©ãƒ¼","ゆã«ãµã‰ãƒ¼ã‚€"], + "⛸": ["ã‚ã„ã™ã™ã‘ーã¨","ã“ãŠã‚Š"], + "🎿": ["ã™ãーã¨ã™ãーã¶ãƒ¼ã¤","ã™ãー","ゆã"], + "🛷": ["ãã‚Š","るーã˜ã‚…","ã¨ã¼ãŒã‚“"], + "â›·": ["ã™ãー","ゆã"], + "ðŸ‚": ["ã™ã®ãƒ¼ã¼ãƒ¼ã ー","ã™ãー","ゆã","ã™ã®ãƒ¼ã¼ãƒ¼ã©"], + "ðŸ‹ï¸â€â™€ï¸": ["ã†ãˆã„ã¨ã‚’ã‚‚ã¡ã‚ã’ã‚‹ã˜ã‚‡ã›ã„","ã‚ã’","ã˜ã‚…ã†ã‚Šã‚‡ã†","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "ðŸ‹": ["ã†ãˆã„ã¨ã‚’ã‚‚ã¡ã‚ã’ã‚‹ã²ã¨","ã‚ã’","ã˜ã‚…ã†ã‚Šã‚‡ã†"], + "ðŸ‹ï¸â€â™‚ï¸": ["ã†ãˆã„ã¨ã‚’ã‚‚ã¡ã‚ã’ã‚‹ã ã‚“ã›ã„","ã‚ã’","ã˜ã‚…ã†ã‚Šã‚‡ã†","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🤺": ["ãµã‡ã‚“ã—ã‚“ãã‚’ã™ã‚‹ã²ã¨","ã‘ã‚“ã—","ã‘ã‚“ã˜ã‚…ã¤","ã‘ã‚“"], + "🤼â€â™€ï¸": ["ã‚Œã™ã‚Šã‚“ãã‚’ã™ã‚‹ã˜ã‚‡ã›ã„","ã‚Œã™ã‚Šã‚“ã","ã‚Œã™ã‚Šã‚“ãã›ã‚“ã—ã‚…","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🤼": ["ã‚Œã™ã‚Šã‚“ãã‚’ã™ã‚‹ã²ã¨ãŸã¡","ã‚Œã™ã‚Šã‚“ã","ã‚Œã™ã‚Šã‚“ãã›ã‚“ã—ã‚…"], + "🤼â€â™‚ï¸": ["ã‚Œã™ã‚Šã‚“ãã‚’ã™ã‚‹ã ã‚“ã›ã„","ã‚Œã™ã‚Šã‚“ã","ã‚Œã™ã‚Šã‚“ãã›ã‚“ã—ã‚…","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🤸â€â™€ï¸": ["ããã¦ã‚“ã‚’ã™ã‚‹ã˜ã‚‡ã›ã„","ããã»ã†ã¦ã‚“ã‹ã„","ãŸã„ãã†","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🤸": ["ããã¦ã‚“ã‚’ã™ã‚‹ã²ã¨","ããã»ã†ã¦ã‚“ã‹ã„","ãŸã„ãã†"], + "🤸â€â™‚ï¸": ["ããã¦ã‚“ã‚’ã™ã‚‹ã ã‚“ã›ã„","ããã»ã†ã¦ã‚“ã‹ã„","ãŸã„ãã†","ãŠã¨ã“","ã ã‚“ã›ã„"], + "⛹ï¸â€â™€ï¸": ["ã¼ãƒ¼ã‚‹ã‚’ã°ã†ã‚“ã©ã•ã›ã‚‹ã˜ã‚‡ã›ã„","ã¼ãƒ¼ã‚‹","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "⛹": ["ã¼ãƒ¼ã‚‹ã‚’ã°ã†ã‚“ã©ã•ã›ã‚‹ã²ã¨","ã¼ãƒ¼ã‚‹"], + "⛹ï¸â€â™‚ï¸": ["ã¼ãƒ¼ã‚‹ã‚’ã°ã†ã‚“ã©ã•ã›ã‚‹ã ã‚“ã›ã„","ã¼ãƒ¼ã‚‹","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🤾â€â™€ï¸": ["ã¯ã‚“ã©ã¼ãƒ¼ã‚‹ã‚’ã™ã‚‹ã˜ã‚‡ã›ã„","ã¼ãƒ¼ã‚‹","ã¯ã‚“ã©ã¼ãƒ¼ã‚‹","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🤾": ["ã¯ã‚“ã©ã¼ãƒ¼ã‚‹ã‚’ã™ã‚‹ã²ã¨","ã¼ãƒ¼ã‚‹","ã¯ã‚“ã©ã¼ãƒ¼ã‚‹"], + "🤾â€â™‚ï¸": ["ã¯ã‚“ã©ã¼ãƒ¼ã‚‹ã‚’ã™ã‚‹ã ã‚“ã›ã„","ã¼ãƒ¼ã‚‹","ã¯ã‚“ã©ã¼ãƒ¼ã‚‹","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🧗â€â™€ï¸": ["ãらã„ã¿ã‚“ãã—ã¦ã„ã‚‹ã˜ã‚‡ã›ã„","ãらã„ã¿ã‚“ã","ã‚ã£ã","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧗": ["ãらã„ã¿ã‚“ãã—ã¦ã„ã‚‹ã²ã¨","ãらã„ã¿ã‚“ã","ã‚ã£ã"], + "🧗â€â™‚ï¸": ["ãらã„ã¿ã‚“ãã—ã¦ã„ã‚‹ã ã‚“ã›ã„","ãらã„ã¿ã‚“ã","ã‚ã£ã","ã ã‚“ã›ã„","ãŠã¨ã“"], + "ðŸŒï¸â€â™€ï¸": ["ã”ã‚‹ãµã‚’ã™ã‚‹ã˜ã‚‡ã›ã„","ã¼ãƒ¼ã‚‹","ã”ã‚‹ãµ","ã”ã‚‹ãµãー","ã”ã‚‹ãµã™ã‚‹","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "ðŸŒ": ["ã”ã‚‹ãµã‚’ã™ã‚‹ã²ã¨","ã¼ãƒ¼ã‚‹","ã”ã‚‹ãµ","ã”ã‚‹ãµãー","ã”ã‚‹ãµã™ã‚‹"], + "ðŸŒï¸â€â™‚ï¸": ["ã”ã‚‹ãµã‚’ã™ã‚‹ã ã‚“ã›ã„","ã¼ãƒ¼ã‚‹","ã”ã‚‹ãµ","ã”ã‚‹ãµãー","ã”ã‚‹ãµã™ã‚‹","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🧘â€â™€ï¸": ["れんã’ã–ã®ã˜ã‚‡ã›ã„","ã‚ã„ãã†","よãŒ","ã›ã„ãŠã‚“","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧘": ["れんã’ã–ã®ã²ã¨","ã‚ã„ãã†","よãŒ","ã›ã„ãŠã‚“"], + "🧘â€â™‚ï¸": ["れんã’ã–ã®ã ã‚“ã›ã„","ã‚ã„ãã†","よãŒ","ã›ã„ãŠã‚“","ã ã‚“ã›ã„","ãŠã¨ã“"], + "🧖â€â™€ï¸": ["ã™ã¡ãƒ¼ã‚€ã‚‹ãƒ¼ã‚€ã«ã„ã‚‹ã˜ã‚‡ã›ã„","ã•ã†ãª","ã™ã¡ãƒ¼ã‚€ã‚‹ãƒ¼ã‚€","ã¯ã¾ã‚€","ã™ã¡ãƒ¼ã‚€ã°ã™","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🧖": ["ã™ã¡ãƒ¼ã‚€ã‚‹ãƒ¼ã‚€ã«ã„ã‚‹ã²ã¨","ã•ã†ãª","ã™ã¡ãƒ¼ã‚€ã‚‹ãƒ¼ã‚€","ã¯ã¾ã‚€","ã™ã¡ãƒ¼ã‚€ã°ã™"], + "🧖â€â™‚ï¸": ["ã™ã¡ãƒ¼ã‚€ã‚‹ãƒ¼ã‚€ã«ã„ã‚‹ã ã‚“ã›ã„","ã•ã†ãª","ã™ã¡ãƒ¼ã‚€ã‚‹ãƒ¼ã‚€","ã¯ã¾ã‚€","ã™ã¡ãƒ¼ã‚€ã°ã™","ã ã‚“ã›ã„","ãŠã¨ã“"], + "ðŸ„â€â™€ï¸": ["ã•ãƒ¼ãµãƒã‚“ã‚’ã™ã‚‹ã˜ã‚‡ã›ã„","ã•ãƒ¼ãµãー","ã•ãƒ¼ãµãƒã‚“","ãªã¿ã®ã‚Š","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "ðŸ„": ["ã•ãƒ¼ãµãƒã‚“ã‚’ã™ã‚‹ã²ã¨","ã•ãƒ¼ãµãー","ã•ãƒ¼ãµãƒã‚“","ãªã¿ã®ã‚Š"], + "ðŸ„â€â™‚ï¸": ["ã•ãƒ¼ãµãƒã‚“ã‚’ã™ã‚‹ã ã‚“ã›ã„","ã•ãƒ¼ãµãー","ã•ãƒ¼ãµãƒã‚“","ãªã¿ã®ã‚Š","ãŠã¨ã“","ã ã‚“ã›ã„"], + "ðŸŠâ€â™€ï¸": ["ãŠã‚ˆãã˜ã‚‡ã›ã„","ãŠã‚ˆã","ã™ã„ãˆã„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "ðŸŠ": ["ã™ã„ãˆã„ã‚’ã™ã‚‹ã²ã¨","ãŠã‚ˆã","ã™ã„ãˆã„"], + "ðŸŠâ€â™‚ï¸": ["ãŠã‚ˆãã ã‚“ã›ã„","ãŠã‚ˆã","ã™ã„ãˆã„","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🤽â€â™€ï¸": ["ã™ã„ãã‚…ã†ã‚’ã™ã‚‹ã˜ã‚‡ã›ã„","ã½ã‚","ã¿ãš","ã™ã„ãã‚…ã†","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🤽": ["ã™ã„ãã‚…ã†ã‚’ã™ã‚‹ã²ã¨","ã½ã‚","ã¿ãš","ã™ã„ãã‚…ã†"], + "🤽â€â™‚ï¸": ["ã™ã„ãã‚…ã†ã‚’ã™ã‚‹ã ã‚“ã›ã„","ã½ã‚","ã¿ãš","ã™ã„ãã‚…ã†","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🚣â€â™€ï¸": ["ã¼ãƒ¼ã¨ã‚’ã“ãã˜ã‚‡ã›ã„","ã¼ãƒ¼ã¨","ã“ãŽã¶ã","ã®ã‚Šã‚‚ã®","ãã†ã¦ã„","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🚣": ["ã¼ãƒ¼ã¨ã‚’ã“ãã²ã¨","ã¼ãƒ¼ã¨","ã“ãŽã¶ã","ã®ã‚Šã‚‚ã®","ãã†ã¦ã„"], + "🚣â€â™‚ï¸": ["ã¼ãƒ¼ã¨ã‚’ã“ãã ã‚“ã›ã„","ã¼ãƒ¼ã¨","ã“ãŽã¶ã","ã®ã‚Šã‚‚ã®","ãã†ã¦ã„","ãŠã¨ã“","ã ã‚“ã›ã„"], + "ðŸ‡": ["ã‘ã„ã°","ã†ã¾","ãã—ã‚…","ãょã†ãã†ã°"], + "🚴â€â™€ï¸": ["ã˜ã¦ã‚“ã—ゃã«ã®ã‚‹ã˜ã‚‡ã›ã„","ã˜ã¦ã‚“ã—ゃ","ã˜ã¦ã‚“ã—ゃã®ã‚Š","ã˜ã¦ã‚“ã—ゃã«ã®ã‚‹ã²ã¨","ã•ã„ãã‚Šã™ã¨","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🚴": ["ã˜ã¦ã‚“ã—ゃã«ã®ã‚‹ã²ã¨","ã˜ã¦ã‚“ã—ゃ","ã˜ã¦ã‚“ã—ゃã®ã‚Š","ã•ã„ãã‚Šã™ã¨"], + "🚴â€â™‚ï¸": ["ã˜ã¦ã‚“ã—ゃã«ã®ã‚‹ã ã‚“ã›ã„","ã˜ã¦ã‚“ã—ゃ","ã˜ã¦ã‚“ã—ゃã®ã‚Š","ã˜ã¦ã‚“ã—ゃã«ã®ã‚‹ã²ã¨","ã•ã„ãã‚Šã™ã¨","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🚵â€â™€ï¸": ["ã¾ã†ã‚“ã¦ã‚“ã°ã„ãã«ã®ã‚‹ã˜ã‚‡ã›ã„","ã¾ã†ã‚“ã¦ã‚“ã°ã„ãらã„ã ー","ãã‚ã™ã°ã„ã","ã˜ã¦ã‚“ã—ゃ","ã˜ã¦ã‚“ã—ゃã®ã‚Š","ã˜ã¦ã‚“ã—ゃã«ã®ã‚‹ã²ã¨","ã•ã„ãã‚Šã™ã¨","ã‚„ã¾","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🚵": ["ã¾ã†ã‚“ã¦ã‚“ã°ã„ãã«ã®ã‚‹ã²ã¨","ã¾ã†ã‚“ã¦ã‚“ã°ã„ãらã„ã ー","ãã‚ã™ã°ã„ã","ã˜ã¦ã‚“ã—ゃ","ã˜ã¦ã‚“ã—ゃã®ã‚Š","ã˜ã¦ã‚“ã—ゃã«ã®ã‚‹ã²ã¨","ã‚„ã¾"], + "🚵â€â™‚ï¸": ["ã¾ã†ã‚“ã¦ã‚“ã°ã„ãã«ã®ã‚‹ã ã‚“ã›ã„","ã¾ã†ã‚“ã¦ã‚“ã°ã„ãらã„ã ー","ãã‚ã™ã°ã„ã","ã˜ã¦ã‚“ã—ゃ","ã˜ã¦ã‚“ã—ゃã®ã‚Š","ã˜ã¦ã‚“ã—ゃã«ã®ã‚‹ã²ã¨","ã•ã„ãã‚Šã™ã¨","ã‚„ã¾","ãŠã¨ã“","ã ã‚“ã›ã„"], + "🎽": ["らんã«ã‚“ãã—ゃã¤ã¨ãŸã™ã","らんã«ã‚“ã","ãŸã™ã","ã—ゃã¤"], + "🎖": ["ãã‚“ã—ょã†","ãŠã„ã‚ã„","ã‚ã ã‚‹","ãã‚“ã˜"], + "ðŸ…": ["ã™ã½ãƒ¼ã¤ã®ã‚ã ã‚‹","ã‚ã ã‚‹"], + "🥇": ["ãã‚“ã‚ã ã‚‹","1ã„","ãã‚“","ã‚ã ã‚‹","1","ã ã„1ã„"], + "🥈": ["ãŽã‚“ã‚ã ã‚‹","ã‚ã ã‚‹","2ã„","ãŽã‚“","2","ã ã„2ã„"], + "🥉": ["ã©ã†ã‚ã ã‚‹","ã©ã†","ã‚ã ã‚‹","3ã„","3","ã ã„3ã„"], + "ðŸ†": ["ã¨ã‚ãµãƒãƒ¼","ã—ょã†"], + "ðŸµ": ["ã°ã‚‰ã‹ã–ã‚Š","ã—ょãã¶ã¤"], + "🎗": ["ã‚Šã¾ã„ã‚“ã ーりã¼ã‚“","ãŠã„ã‚ã„","ã‚Šã¾ã„ã‚“ã ー","ã‚Šã¼ã‚“"], + "🎫": ["ãã£ã·","ã‚ãã¦ãƒã³ã¦ãƒ","ã«ã‚…ã†ã˜ã‚‡ã†ã‚Šã‚‡ã†","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã¡ã‘ã£ã¨"], + "🎟": ["ã«ã‚…ã†ã˜ã‚‡ã†ã‘ã‚“","ã«ã‚…ã†ã˜ã‚‡ã†ã‚Šã‚‡ã†","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã¡ã‘ã£ã¨"], + "🎪": ["ã•ãƒ¼ã‹ã™ã”ã‚„","ã‚ãã¦ãƒã³ã¦ãƒ","ã•ãƒ¼ã‹ã™","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã¦ã‚“ã¨"], + "🤹â€â™€ï¸": ["ã˜ã‚ƒãã‚Šã‚“ãã‚’ã™ã‚‹ã˜ã‚‡ã›ã„","ã¦ã‚“ã³ã‚“","ã˜ã‚ƒãã‚Šã‚“ã","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "🤹": ["ã˜ã‚ƒãã‚Šã‚“ãã‚’ã™ã‚‹ã²ã¨","ã°ã‚‰ã‚“ã™","ã˜ã‚ƒãã‚Šã‚“ã"], + "🤹â€â™‚ï¸": ["ã˜ã‚ƒãã‚Šã‚“ãã‚’ã™ã‚‹ã ã‚“ã›ã„","ã¦ã‚“ã³ã‚“","ã˜ã‚ƒãã‚Šã‚“ã","ã ã‚“ã›ã„","ãŠã¨ã“"], + "ðŸŽ": ["ã¶ãŸã„ã’ã„ã˜ã‚…ã¤","ã‚ãã¦ãƒã³ã¦ãƒ","ã’ã„ã˜ã‚…ã¤","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã‹ã‚ã‚“","ã¶ãŸã„","ã—ã‚ãŸãƒ¼"], + "🎨": ["ãˆã®ãã±ã‚Œã£ã¨","ã‚ãã¦ãƒã³ã¦ãƒ","ã‚ーã¨","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã³ã˜ã‚…ã¤ã‹ã‚“","ã‹ã„ãŒ","ã±ã‚Œã£ã¨"], + "🎬": ["ã‹ã¡ã‚“ã“","ã‚ãã¦ãƒã³ã¦ãƒ","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãˆã„ãŒ"], + "🎤": ["ã¾ã„ã","ã‚ãã¦ãƒã³ã¦ãƒ","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã‹ã‚‰ãŠã‘","ã¾ã„ãã‚ãµã‰ã‚“"], + "🎧": ["ã¸ã£ã©ã»ã‚“","ã‚ãã¦ãƒã³ã¦ãƒ","ã„ã‚„ã»ã‚“","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã¸ã£ã©ãµã‰ã‚“"], + "🎼": ["ãŒããµ","ã‚ãã¦ãƒã³ã¦ãƒ","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãŠã‚“ãŒã"], + "🎹": ["ã‘ã‚“ã°ã‚“","ã‚ãã¦ãƒã³ã¦ãƒ","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãŒã£ã","ãーã¼ãƒ¼ã©","ãŠã‚“ãŒã","ã´ã‚ã®"], + "🪗": ["ã‚ã“ーã§ãƒãŠã‚“","ã“ã‚“ã•ãƒ¼ã¦ãƒãƒ¼ãª","ã™ãã„ーãšã¼ã£ãã™"], + "ðŸ¥": ["ã©ã‚‰ã‚€","ã©ã‚‰ã‚€ã™ã¦ãƒã£ã","ãŠã‚“ãŒã"], + "🪘": ["ãªãŒã„ã©ã‚‰ã‚€","ã³ãƒ¼ã¨","ã“ã‚“ãŒ","ã©ã‚‰ã‚€","ã‚Šãšã‚€","ã˜ã‚ƒã‚“ã¹"], + "🪇": ["ã¾ã‚‰ã‹ã™","ã„ã‚ã†","ãŒã£ã","ãŠã‚“ãŒã","ãã†ãŠã‚“","ã ãŒã£ã","ãŒãŸãŒãŸ","ã‚Šãšã‚€","ã—ã‡ã„ã"], + "🎷": ["ã•ã£ãã™","ã‚ãã¦ãƒã³ã¦ãƒ","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãŒã£ã","ãŠã‚“ãŒã","ã•ãããµã‰ãƒ¼ã‚“"], + "🎺": ["ã¨ã‚‰ã‚“ãºã£ã¨","ã‚ãã¦ãƒã³ã¦ãƒ","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãŒã£ã","ãŠã‚“ãŒã"], + "🪈": ["ãµã‚‹ãƒ¼ã¨","ãŸã‘","よã“ã¶ãˆãã†ã—ゃ","ãµã‚‹ãƒ¼ã¨ãã†ã—ゃ","ãŠã‚“ãŒã","ã±ã„ã·","ã‚Šã“ーã ー","ãµã","ã‚‚ã£ã‹ã‚“ãŒã£ã"], + "🎸": ["ãŽãŸãƒ¼","ã‚ãã¦ãƒã³ã¦ãƒ","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãŒã£ã","ãŠã‚“ãŒã"], + "🪕": ["ã°ã‚“ã˜ã‚‡ãƒ¼","ã‚ãã¦ãƒã³ã¦ãƒ","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãŒã£ã","ãŠã‚“ãŒã"], + "🎻": ["ã°ã„ãŠã‚Šã‚“","ã‚ãã¦ãƒã³ã¦ãƒ","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãŒã£ã","ãŠã‚“ãŒã"], + "🎲": ["ã•ã„ã“ã‚","ã•ã„","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã’ーむ"], + "🧩": ["ã±ãšã‚‹ã®ã´ãƒ¼ã™","ã¦ãŒã‹ã‚Š","ã‹ã¿ã‚ã†","ã´ãƒ¼ã™","ã±ãšã‚‹","ã˜ããー"], + "♟ï¸": ["ã¡ã‡ã™ã®ã½ãƒ¼ã‚“","ã¡ã‡ã™","ã“ã¾","ã’ーむ","ã™ã¦ã“ã¾"], + "🎯": ["ã¦ãã¡ã‚…ã†","ã‚ãã¦ãƒã³ã¦ãƒ","ã¶ã‚‹","ã¶ã‚‹ãšã‚ã„","ã ーã¤","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã‚","ã—ã‚ã„","ã²ã£ã¨","ã²ã‚‡ã†ã¦ã"], + "🎳": ["ã¼ã†ã‚Šã‚“ã","ã¼ãƒ¼ã‚‹","ã—ã‚ã„"], + "🪀": ["よーよー","ãŠã‚‚ã¡ã‚ƒ","ã˜ã‚‡ã†ã’"], + "ðŸª": ["ãŸã“","ãŠã‚‚ã¡ã‚ƒ","ã¨ã¶","ã¾ã†"], + "ðŸ›": ["ã™ã¹ã‚Šã ã„","ゆã†ãˆã‚“ã¡","ã‚ãã³"], + "🎮": ["ã¦ã‚Œã³ã’ーむ","ã“ã‚“ã¨ã‚ーらー","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã’ーむ","ã³ã§ãŠã’ーむ"], + "👾": ["ãˆã„ã‚Šã‚ã‚“","ã†ã¡ã‚…ã†ã˜ã‚“","ã‹ã„ã˜ã‚…ã†","ã„ã›ã„ã˜ã‚“","ã‹ãŠ","ãŠã¨ãŽã°ãªã—","ãµãã‚“ãŸã˜ãƒ¼","ã‚‚ã‚“ã™ãŸãƒ¼","ã†ã¡ã‚…ã†","UFO"], + "🎰": ["ã™ã‚ã£ã¨ã¾ã—ã‚“","ã‚ãã¦ãƒã³ã¦ãƒ","ã’ーむ","ã™ã‚ã£ã¨"], + "🚗": ["ã˜ã©ã†ã—ゃ","ãã‚‹ã¾","ã®ã‚Šã‚‚ã®"], + "🚙": ["ãゃんã´ã‚“ãã‹ãƒ¼","ã‚Œãã‚Šãˆãƒ¼ã—ょん","RV","ã®ã‚Šã‚‚ã®"], + "🚕": ["ãŸãã—ー","ã®ã‚Šã‚‚ã®"], + "🛺": ["ãŠãƒ¼ã¨ã‚Šãã—ゃ","ã˜ã‚“ã‚Šãã—ゃ","ã¨ã…ãã¨ã…ã"], + "🚌": ["ã°ã™","ã®ã‚Šã‚‚ã®"], + "🚎": ["ã¨ã‚りーã°ã™","ã°ã™","ã‚ã‚ã‚“ã§ã‚“ã—ゃ","ã—ãŒã„ã§ã‚“ã—ゃ","ã®ã‚Šã‚‚ã®"], + "ðŸŽ": ["れーã—ã‚“ãã‹ãƒ¼","ãã‚‹ã¾","ãょã†ãã†"], + "🚓": ["ã±ã¨ã‹ãƒ¼","ãã‚‹ã¾","ã±ã¨ã‚ーる","ã‘ã„ã•ã¤","ã®ã‚Šã‚‚ã®"], + "🚑": ["ãã‚…ã†ãã‚…ã†ã—ゃ","ã®ã‚Šã‚‚ã®"], + "🚒": ["ã—ょã†ã¼ã†ã—ゃ","ãˆã‚“ã˜ã‚“","ãˆã‚“","ã¨ã‚‰ã£ã","ã®ã‚Šã‚‚ã®"], + "ðŸš": ["ã¾ã„ãã‚ã°ã™","ã°ã™","ã®ã‚Šã‚‚ã®"], + "🛻": ["ã´ã£ãã‚ã£ã·ã¨ã‚‰ã£ã","ã´ã£ãã‚ã£ã·","ã¨ã‚‰ã£ã","ã®ã‚Šã‚‚ã®"], + "🚚": ["ã¯ã„ãŸã¤ã‚ˆã†ã¨ã‚‰ã£ã","ã¯ã„ãŸã¤","ã¨ã‚‰ã£ã","ã®ã‚Šã‚‚ã®"], + "🚛": ["ã¨ã‚Œãƒ¼ã‚‰ãƒ¼","ãŠãŠãŒãŸã¨ã‚‰ã£ã","ã›ã¿","ã¨ã‚‰ã£ã","ã®ã‚Šã‚‚ã®"], + "🚜": ["ã¨ã‚‰ããŸãƒ¼","ã®ã‚Šã‚‚ã®"], + "ðŸ": ["れーã™ã°ã„ã","ãŠãƒ¼ã¨ã°ã„","れーã™"], + "🛵": ["ã™ãーãŸãƒ¼","もーãŸãƒ¼"], + "🚲": ["ã˜ã¦ã‚“ã—ゃ","ã°ã„ã","ã®ã‚Šã‚‚ã®"], + "🦼": ["ã§ã‚“ã©ã†ãã‚‹ã¾ã„ã™","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ãã‚‹ã¾ã„ã™"], + "🦽": ["ã—ã‚…ã©ã†ãã‚‹ã¾ã„ã™","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ãã‚‹ã¾ã„ã™"], + "🛴": ["ãã£ãã¼ãƒ¼ã©","ãã£ã","ã™ãーãŸãƒ¼"], + "🛹": ["ã™ã‘ã¼ãƒ¼","ã™ã‘ーã¨","ã¼ãƒ¼ã©"], + "🛼": ["ã‚ーらーã™ã‘ーã¨","ã‚ーらー","ã™ã‘ーã¨"], + "🛞": ["ã—ゃりん","ãˆã‚“","ãŸã„ã‚„","ã‹ã„ã¦ã‚“"], + "🚨": ["ã±ã¨ã‚‰ã„ã¨","ãã‚‹ã¾","ã²ã‹ã‚Š","ã‘ã„ã•ã¤","ã‹ã„ã¦ã‚“","ã®ã‚Šã‚‚ã®","ã•ã„れん","ã‘ã„ã“ã"], + "🚔": ["ã±ã¨ã‹ãƒ¼","ãã‚‹ã¾","ãŸã„ã“ã†ã—ゃ","ã‘ã„ã•ã¤","ã®ã‚Šã‚‚ã®"], + "ðŸš": ["ã°ã™","ãŸã„ã“ã†ã—ゃ","ã®ã‚Šã‚‚ã®"], + "🚘": ["ãŸã„ã“ã†ã—ゃ","ã˜ã©ã†ã—ゃ","ãã‚‹ã¾","ã®ã‚Šã‚‚ã®"], + "🚖": ["ãŸãã—ー","ãŸã„ã“ã†ã—ゃ","ã®ã‚Šã‚‚ã®"], + "🚡": ["ã‚ーã·ã†ã‡ã„","ãã†ã¡ã‚…ã†","ã‘ーã¶ã‚‹","ãã‚‹ã¾","ã”ã‚“ã©ã‚‰","ã¨ã‚‰ã‚€ã†ã‡ã„","ã®ã‚Šã‚‚ã®"], + "🚠": ["ã‚ーã·ã†ã‡ã„","ã‘ーã¶ã‚‹","ã”ã‚“ã©ã‚‰","ã‚„ã¾","ã®ã‚Šã‚‚ã®"], + "🚟": ["ã“ã†ã‹ã¦ã¤ã©ã†","ã¦ã¤ã©ã†","ã®ã‚Šã‚‚ã®"], + "🚃": ["ã¦ã¤ã©ã†ã—ゃりょã†","ãã‚‹ã¾","ã§ã‚“ã","ã¦ã¤ã©ã†","ã‚Œã£ã—ゃ","ã‚ã‚ã‚“","ã¨ã‚りーã°ã™","ã®ã‚Šã‚‚ã®"], + "🚋": ["ã‚ã‚ã‚“ã§ã‚“ã—ゃ","ãã‚‹ã¾","ã‚ã‚ã‚“","ã¨ã‚りーã°ã™","ã®ã‚Šã‚‚ã®"], + "ðŸš": ["ã‚‚ã®ã‚Œãƒ¼ã‚‹","ã®ã‚Šã‚‚ã®"], + "🚄": ["ã—ã‚“ã‹ã‚“ã›ã‚“","ã¦ã¤ã©ã†","ã“ã†ãã","ã‚Œã£ã—ゃ","ã®ã‚Šã‚‚ã®"], + "🚅": ["ã—ã‚“ã‹ã‚“ã›ã‚“","ã ã‚“ãŒã‚“","ã¦ã¤ã©ã†","ã“ã†ãã","ã‚Œã£ã—ゃ","ã®ã‚Šã‚‚ã®"], + "🚈": ["らã„ã¨ã‚Œãƒ¼ã‚‹","ã¦ã¤ã©ã†","ã®ã‚Šã‚‚ã®"], + "🚞": ["ã•ã‚“ãŒãã¦ã¤ã©ã†","ãã‚‹ã¾","ã‚„ã¾","ã¦ã¤ã©ã†","ã®ã‚Šã‚‚ã®"], + "🚂": ["ã˜ã‚‡ã†ããã‹ã‚“ã—ゃ","ãˆã‚“ã˜ã‚“","ãã‹ã‚“ã—ゃ","ã¦ã¤ã©ã†","ã˜ã‚‡ã†ã","ã‚Œã£ã—ゃ","ã®ã‚Šã‚‚ã®"], + "🚆": ["ã§ã‚“ã—ゃ","ã›ã‚“ã‚","ã®ã‚Šã‚‚ã®"], + "🚇": ["ã¡ã‹ã¦ã¤","ã‚ã¨ã‚","ã®ã‚Šã‚‚ã®"], + "🚊": ["ã‚ã‚ã‚“ã§ã‚“ã—ゃ","ã¨ã‚りーã°ã™","ã®ã‚Šã‚‚ã®"], + "🚉": ["ãˆã","ã›ã‚“ã‚","ã§ã‚“ã—ゃ","ã®ã‚Šã‚‚ã®"], + "ðŸš": ["ã¸ã‚Šã“ã·ãŸãƒ¼","ã®ã‚Šã‚‚ã®"], + "🛩": ["ã“ãŒãŸã“ã†ãã†ã","ã²ã“ã†ã","ã®ã‚Šã‚‚ã®"], + "✈ï¸": ["ã²ã“ã†ã","ã®ã‚Šã‚‚ã®"], + "🛫": ["ã²ã“ã†ãã®ã‚Šã‚Šã","ã²ã“ã†ã","ã¡ã‡ã£ãã„ã‚“","ã—ã‚…ã£ã±ã¤","ã®ã‚Šã‚‚ã®"], + "🛬": ["ã²ã“ã†ãã®ã¡ã‚ƒãã‚Šã","ã²ã“ã†ã","ã¨ã†ã¡ã‚ƒã","ã¡ã‚ƒãã‚Šã","ã®ã‚Šã‚‚ã®"], + "🪂": ["ã±ã‚‰ã—ゅーã¨","ã±ã‚‰ã›ãƒ¼ã‚‹","ã™ã‹ã„ã ã„ã¶","ã¯ã‚“ããらã„ã ー"], + "💺": ["ã–ã›ã","ã„ã™"], + "🛰": ["ã•ã¦ã‚‰ã„ã¨","ãˆã„ã›ã„","ã†ã¡ã‚…ã†","ã®ã‚Šã‚‚ã®"], + "🚀": ["ã‚ã‘ã£ã¨","ã†ã¡ã‚…ã†","ã®ã‚Šã‚‚ã®"], + "🛸": ["ãらã¨ã¶ãˆã‚“ã°ã‚“","UFO","ã†ã¡ã‚…ã†ã˜ã‚“","ã„ã»ã—ã˜ã‚“","ã†ã¡ã‚…ã†","ãã†ãã†"], + "🛶": ["ã‹ã¬ãƒ¼","ã¼ãƒ¼ã¨"], + "⛵": ["よã£ã¨","ã¼ãƒ¼ã¨","ã‚Šãžãƒ¼ã¨","ã†ã¿","ã®ã‚Šã‚‚ã®"], + "🛥": ["もーãŸãƒ¼ã¼ãƒ¼ã¨","ã¼ãƒ¼ã¨","ã®ã‚Šã‚‚ã®"], + "🚤": ["ã™ã´ãƒ¼ã©ã¼ãƒ¼ã¨","ã¼ãƒ¼ã¨","ã®ã‚Šã‚‚ã®"], + "â›´": ["ãµã‡ã‚Šãƒ¼","ã¼ãƒ¼ã¨"], + "🛳": ["りょã‹ãã›ã‚“","りょã‹ã","ãµã","ã®ã‚Šã‚‚ã®"], + "🚢": ["ãµã","ã®ã‚Šã‚‚ã®"], + "🛟": ["ãã‚…ã†ã‚ã„ã†ãã‚","ã†ãã‚","らã„ãµã˜ã‚ƒã‘ã£ã¨","らã„ãµã›ãƒ¼ã°ãƒ¼","ãã‚…ã†ã˜ã‚‡","ã‚ã‚“ãœã‚“"], + "âš“": ["ã„ã‹ã‚Š","ãµã","ã¤ãƒ¼ã‚‹"], + "⛽": ["ãŒãã‚Šã‚“ã™ãŸã‚“ã©","ãんりょã†","ãŒãã‚Šã‚“","ãã‚…ã†ã‚†ã","ã•ãƒ¼ã³ã™ã™ã¦ãƒ¼ã—ょん"], + "🚧": ["ã“ã†ã˜ã¡ã‚…ã†","ã“ã†ã˜ã‚ˆã†ãµã‡ã‚“ã™","ã‘ã‚“ã›ã¤ã“ã†ã˜"], + "ðŸš": ["ã°ã™ã¦ã„","ã°ã™","ã¦ã„ã—"], + "🚦": ["ãŸã¦ã‚€ãã®ã—ã‚“ã”ã†ã","ã—ã‚“ã”ã†ã","ã—ã‚“ã”ã†","ã“ã†ã¤ã†"], + "🚥": ["よã“ã‚€ãã®ã—ã‚“ã”ã†ã","ã—ã‚“ã”ã†ã","ã—ã‚“ã”ã†","ã“ã†ã¤ã†"], + "🛑": ["ã„ã¡ã˜ã¦ã„ã—ã²ã‚‡ã†ã—ã","ã¯ã£ã‹ã£ã‘ã„","ã²ã‚‡ã†ã—ã","ã¦ã„ã—"], + "🎡": ["ã‹ã‚“らんã—ゃ","ã‚ãã¦ãƒã³ã¦ãƒ","ゆã†ãˆã‚“ã¡","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãµã‡ã‚Šã™"], + "🎢": ["ã˜ã‡ã£ã¨ã“ーã™ãŸãƒ¼","ã‚ãã¦ãƒã³ã¦ãƒ","ゆã†ãˆã‚“ã¡","ã“ーã™ãŸãƒ¼","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã‚ーらー"], + "🎠": ["ã‚りーã”ーらんã©","ã‚ãã¦ãƒã³ã¦ãƒ","ã‚りーã”ーらã†ã‚“ã©","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã†ã¾"], + "ðŸ—": ["ã‘ã‚“ã›ã¤ã¡ã‚…ã†","ãŸã¦ã‚‚ã®","ã‘ã‚“ã›ã¤"], + "ðŸŒ": ["ãã‚Š","ã¦ã‚“ã"], + "🗼": ["ã¨ã†ãょã†ãŸã‚ー","ã¨ã†ãょã†","ãŸã‚ー"], + "ðŸ": ["ã“ã†ã˜ã‚‡ã†","ãŸã¦ã‚‚ã®"], + "⛲": ["ãµã‚“ã™ã„"], + "🎑": ["ãŠã¤ãã¿","ã‚ãã¦ãƒã³ã¦ãƒ","ãŠã„ã‚ã„","ã˜ã‚…ã—ょã†ã—ã","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã¤ã"], + "â›°": ["ã‚„ã¾"], + "ðŸ”": ["ゆãã‚„ã¾","ã•ã‚€ã„","ã‚„ã¾","ゆã"], + "🗻": ["ãµã˜ã•ã‚“","ã‚„ã¾"], + "🌋": ["ã‹ã–ã‚“","ãµã‚“ã‹","ã‚„ã¾","ãã—ょã†"], + "🗾": ["ã«ã£ã½ã‚“ã‚Œã£ã¨ã†","ã«ã£ã½ã‚“","ã¡ãš"], + "ðŸ•": ["ãゃんã·"], + "⛺": ["ã¦ã‚“ã¨","ãゃんã·"], + "ðŸž": ["ã“ãã‚Šã¤ã“ã†ãˆã‚“","ã“ã†ãˆã‚“"], + "🛣": ["ã“ã†ããã©ã†ã‚","ã¯ã„ã†ã‡ã„","ã©ã†ã‚"], + "🛤": ["ã›ã‚“ã‚","ã¦ã¤ã©ã†","ã§ã‚“ã—ゃ"], + "🌅": ["ã²ã®ã§","ã‚ã•","ãŸã„よã†","ã¦ã‚“ã“ã†"], + "🌄": ["ã‚„ã¾ã‹ã‚‰ã®ã²ã®ã§","ã‚ã•","ã‚„ã¾","ãŸã„よã†","ã²ã®ã§","ã¦ã‚“ã“ã†"], + "ðŸœ": ["ã•ã°ã"], + "ðŸ–": ["ã³ãƒ¼ã¡ã¨ã‹ã•","ã³ãƒ¼ã¡","ã‹ã•","ã±ã‚‰ãã‚‹"], + "ðŸ": ["ã‚€ã˜ã‚“ã¨ã†","ã•ã°ã","ã—ã¾"], + "🌇": ["ã³ã‚‹ã«ã—ãšã‚€ã‚†ã†ã²","ãŸã¦ã‚‚ã®","ゆã†ãã‚Œ","ãŸã„よã†","ゆã†ã²","ã¦ã‚“ã"], + "🌆": ["ゆã†ãã‚Œã®ã¾ã¡ãªã¿","ãŸã¦ã‚‚ã®","ã¾ã¡","ゆã†ãã‚Œ","ã²ãã‚Œ","ãµã†ã‘ã„","ãŸã„よã†","ゆã†ã²","ã¦ã‚“ã"], + "ðŸ™": ["ã¾ã¡ãªã¿","ãŸã¦ã‚‚ã®","ã¾ã¡"], + "🌃": ["ã»ã—ãžã‚‰","よる","ã»ã—","ã¦ã‚“ã"], + "🌉": ["よるã®ã¯ã—","ã¯ã—","よる","ã¦ã‚“ã"], + "🌌": ["ã‚ã¾ã®ãŒã‚","ã†ã¡ã‚…ã†","ã¦ã‚“ã"], + "🌠": ["ãªãŒã‚Œã¼ã—","ã‚ãã¦ãƒã³ã¦ãƒ","らã£ã‹","ãªãŒã‚Œã‚‹","ã†ã¡ã‚…ã†","ã»ã—"], + "🎇": ["ã›ã‚“ã“ã†ã¯ãªã³","ã‚ãã¦ãƒã³ã¦ãƒ","ãŠã„ã‚ã„","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã¯ãªã³","ãらãら"], + "🎆": ["ã¯ãªã³","ã‚ãã¦ãƒã³ã¦ãƒ","ãŠã„ã‚ã„","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨"], + "🛖": ["ã“ã‚„","ã„ãˆ","ã›ã‚“ã‘ã„ã“","ã±ãŠ"], + "ðŸ˜": ["ã„ãˆ","ãŸã¦ã‚‚ã®"], + "ðŸ°": ["ã›ã„よã†ã®ã—ã‚","ãŸã¦ã‚‚ã®","ã—ã‚","よーã‚ã£ã±"], + "ðŸ¯": ["ã«ã£ã½ã‚“ã®ã—ã‚","ãŸã¦ã‚‚ã®","ã—ã‚","ã«ã£ã½ã‚“"], + "ðŸŸ": ["ã™ãŸã˜ã‚ã‚€"], + "🗽": ["ã˜ã‚†ã†ã®ã‚ãŒã¿","ã˜ã‚†ã†","ãžã†"], + "ðŸ ": ["ã„ãˆ","ãŸã¦ã‚‚ã®","ã˜ãŸã"], + "ðŸ¡": ["ã«ã‚ã¤ãã®ã„ãˆ","ãŸã¦ã‚‚ã®","ã«ã‚","ã˜ãŸã","ã„ãˆ"], + "ðŸš": ["ã¯ã„ãょ","ãŸã¦ã‚‚ã®","ã¯ã„ãŠã","ã„ãˆ"], + "ðŸ¢": ["ãŠãµãƒã™ã³ã‚‹","ãŸã¦ã‚‚ã®"], + "ðŸ¬": ["ã§ã±ãƒ¼ã¨","ãŸã¦ã‚‚ã®","ã¦ã‚“"], + "ðŸ£": ["ã«ã£ã½ã‚“ã®ã‚†ã†ã³ã‚“ãょã","ãŸã¦ã‚‚ã®","ã«ã£ã½ã‚“","ã½ã™ã¨"], + "ðŸ¤": ["よーã‚ã£ã±ã®ã‚†ã†ã³ã‚“ãょã","ãŸã¦ã‚‚ã®","よーã‚ã£ã±","ã½ã™ã¨"], + "ðŸ¥": ["ã³ã‚‡ã†ã„ã‚“","ãŸã¦ã‚‚ã®","ã„ã—","ãã™ã‚Š"], + "ðŸ¦": ["ãŽã‚“ã“ã†","ãŸã¦ã‚‚ã®"], + "ðŸ¨": ["ã»ã¦ã‚‹","ãŸã¦ã‚‚ã®"], + "ðŸª": ["ã“ã‚“ã³ã«ãˆã‚“ã™ã™ã¨ã‚","ãŸã¦ã‚‚ã®","ã“ã‚“ã³ã«ãˆã‚“ã™","ã™ã¨ã‚"], + "ðŸ«": ["ãŒã£ã“ã†","ãŸã¦ã‚‚ã®"], + "ðŸ©": ["らã¶ã»ã¦ã‚‹","ãŸã¦ã‚‚ã®","ã»ã¦ã‚‹","らã¶"], + "💒": ["ã‘ã£ã“ã‚“ã—ã","ã‚ãã¦ãƒã³ã¦ãƒ","ã¡ã‚ƒãºã‚‹","ã‚ã¾ã‚“ã™"], + "ðŸ›": ["ã‚Œãã—ã¦ããªãŸã¦ã‚‚ã®","ãŸã¦ã‚‚ã®","ã‚Œãã—ã¦ããª"], + "⛪": ["ãょã†ã‹ã„","ãŸã¦ã‚‚ã®","ãã‚Šã™ã¡ã‚ƒã‚“","ã˜ã‚…ã†ã˜ã‹","ã—ã‚…ã†ãょã†"], + "🕌": ["ã‚‚ã™ã","ã„ã™ã‚‰ã‚€","ã‚€ã™ã‚Šã‚€","ã—ã‚…ã†ãょã†"], + "🛕": ["ã²ã‚“ã©ã…ーãょã†ã˜ã„ã‚“","ã²ã‚“ã©ã…ーãょã†","ã˜ã„ã‚“","ã—ã‚…ã†ãょã†"], + "ðŸ•": ["ã—ãªã”ーã","ゆã ã‚„ã˜ã‚“","ゆã ã‚„ãょã†","ã—ã‚…ã†ãょã†","ã‹ã„ã©ã†"], + "🕋": ["ã‹ã‚ã°","ã„ã™ã‚‰ã‚€","ã‚€ã™ã‚Šã‚€","ã—ã‚…ã†ãょã†"], + "⛩": ["ã˜ã‚“ã˜ã‚ƒ","ã—ã‚…ã†ãょã†","ã—ã‚“ã¨ã†"], + "⌚": ["ã†ã§ã©ã‘ã„","ã¨ã‘ã„"], + "📱": ["ã‘ã„ãŸã„ã§ã‚“ã‚","ã‘ã„ãŸã„","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã‚‚ã°ã„ã‚‹","ã§ã‚“ã‚"], + "📲": ["ã¡ã‚ƒãã—ã‚“ã¡ã‚…ã†","ã‚„ã˜ã‚‹ã—","ã¤ã†ã‚","ã‘ã„ãŸã„","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã‚‚ã°ã„ã‚‹","ã‘ã„ãŸã„ã§ã‚“ã‚","ã˜ã‚…ã—ã‚“","ã§ã‚“ã‚"], + "💻": ["ã±ãã“ã‚“","ã®ãƒ¼ã¨ã±ãã“ã‚“","ã“ã‚“ã´ã‚…ーãŸãƒ¼","ã±ãƒ¼ããªã‚‹"], + "⌨": ["ãーã¼ãƒ¼ã©","ã“ã‚“ã´ã‚…ーãŸãƒ¼"], + "🖥": ["ã§ã™ãã¨ã£ã·ã±ãã“ã‚“","ã“ã‚“ã´ã‚…ーãŸãƒ¼","ã§ã™ãã¨ã£ã·"], + "🖨": ["ã·ã‚Šã‚“ãŸãƒ¼","ã“ã‚“ã´ã‚…ーãŸãƒ¼"], + "🖱": ["3ã¼ãŸã‚“ã¾ã†ã™","3","ã¼ãŸã‚“","ã“ã‚“ã´ã‚…ーãŸãƒ¼","ã¾ã†ã™","ã•ã‚“"], + "🖲": ["ã¨ã‚‰ã£ãã¼ãƒ¼ã‚‹","ã“ã‚“ã´ã‚…ーãŸãƒ¼"], + "🕹": ["ã˜ã‚‡ã„ã™ã¦ãƒã£ã","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã’ーむ","ã³ã§ãŠã’ーむ"], + "🗜": ["ã‚ã£ã—ã‚…ã","ã¤ãƒ¼ã‚‹","ã‘ã£ã‹ã‚“"], + "💽": ["MD","ã±ãã“ã‚“","ã²ã‹ã‚Šã§ãƒã™ã","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã¿ã«ã§ãƒã™ã","ã“ã†ãŒã"], + "💾": ["ãµã‚ã£ã´ãƒ¼ã§ãƒã™ã","ã“ã‚“ã´ã‚…ーãŸãƒ¼","ã§ãƒã™ã","ãµã‚ã£ã´ãƒ¼"], + "💿": ["CDã§ãƒã™ã","ã¶ã‚‹ãƒ¼ã‚Œã„","CD","ã“ã‚“ã´ã‚…ーãŸãƒ¼","ã§ãƒã™ã","DVD","ã“ã†ãŒã"], + "📀": ["DVD","ã¶ã‚‹ãƒ¼ã‚Œã„","CD","ã“ã‚“ã´ã‚…ーãŸãƒ¼","ã§ãƒã™ã","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã“ã†ãŒã"], + "📼": ["ã³ã§ãŠã¦ãƒ¼ã·","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã¦ãƒ¼ã·","VHS","ã³ã§ãŠ","ã³ã§ãŠã‹ã›ã£ã¨"], + "📷": ["ã‹ã‚ら","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã³ã§ãŠ"], + "📸": ["ãµã‚‰ã£ã—ã‚…ã‚’ãŸã„ãŸã‹ã‚ら","ã‹ã‚ら","ãµã‚‰ã£ã—ã‚…","ã³ã§ãŠ"], + "📹": ["ã³ã§ãŠã‹ã‚ら","ã‹ã‚ら","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã³ã§ãŠ"], + "🎥": ["ã³ã§ãŠã‹ã‚ら","ã‚ãã¦ãƒã³ã¦ãƒ","ã‹ã‚ら","ã—ãã¾","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãˆã„ãŒ"], + "📽": ["ãˆã„ã—ゃã","ã—ãã¾","ã”らã","ãµãƒã‚‹ã‚€","ãˆã„ãŒ","ã·ã‚ã˜ã‡ããŸãƒ¼","ã³ã§ãŠ"], + "🎞": ["ãµãƒã‚‹ã‚€ã®ãµã‚Œãƒ¼ã‚€","ã—ãã¾","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãµãƒã‚‹ã‚€","ãµã‚Œãƒ¼ã‚€","ãˆã„ãŒ"], + "📞": ["ã˜ã‚…ã‚ã","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã§ã‚“ã‚","ã˜ã‚…ã—ã‚“ã"], + "☎ï¸": ["ã§ã‚“ã‚","ã‘ã„ãŸã„ã§ã‚“ã‚"], + "📟": ["ã½ã‘ã£ã¨ã¹ã‚‹","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã½ã‘ã¹ã‚‹"], + "📠": ["FAX","ã“ã¿ã‚…ã«ã‘ーã—ょん; fAX"], + "📺": ["ã¦ã‚Œã³","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","TV","ã³ã§ãŠ"], + "📻": ["らã˜ãŠ","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã³ã§ãŠ"], + "🎙": ["ã™ãŸã˜ãŠã¾ã„ã","ã¾ã„ã","ãŠã‚“ãŒã","ã™ãŸã˜ãŠ"], + "🎚": ["ã¡ã‚‡ã†ã›ã¤ã°ãƒ¼","ã¡ã‚‡ã†ã›ã¤","ãŠã‚“ãŒã","ã°ãƒ¼"], + "🎛": ["ã“ã‚“ã¨ã‚ーるã®ã¶","ã“ã‚“ã¨ã‚ーる","ã¤ã¾ã¿","ãŠã‚“ãŒã"], + "â±": ["ã™ã¨ã£ã·ã†ã‰ã£ã¡","ã¨ã‘ã„"], + "â²": ["ãŸã„ã¾ãƒ¼ã¨ã‘ã„","ã¨ã‘ã„","ãŸã„ã¾ãƒ¼"], + "â°": ["ã‚ã–ã¾ã—ã¨ã‘ã„","ã‚らーむ","ã¨ã‘ã„"], + "🕰": ["ãŠãã©ã‘ã„","ã¨ã‘ã„"], + "â³": ["ã™ãªã©ã‘ã„","ã™ãª","ãŸã„ã¾ãƒ¼"], + "⌛": ["ã™ãªã©ã‘ã„","ã™ãª","ãŸã„ã¾ãƒ¼"], + "🧮": ["ãã‚ã°ã‚“","ã‘ã„ã•ã‚“","ã‹ã†ã‚“ã¨","ã—ã‚…ã†ã‘ã„ã²ã‚‡ã†","ã™ã†ãŒã"], + "📡": ["ãˆã„ã›ã„ã‚ã‚“ã¦ãª","ã‚ã‚“ã¦ãª","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã±ã‚‰ã¼ã‚‰ã‚ã‚“ã¦ãª","ãˆã„ã›ã„"], + "🔋": ["ã§ã‚“ã¡","ã°ã£ã¦ã‚Šãƒ¼","ã§ã‚“ã—","ã ã‹ãˆãã‚‹ãŽãƒ¼"], + "🪫": ["ã°ã£ã¦ã‚Šãƒ¼ã–んりょã†ã—ょã†","ã°ã£ã¦ã‚Šãƒ¼","ã§ã‚“ã—","ã¦ã„ãˆãã‚‹ãŽãƒ¼"], + "🔌": ["ã“ã‚“ã›ã‚“ã¨","ã§ã‚“ã","ã·ã‚‰ã"], + "💡": ["ã§ã‚“ãã‚…ã†","ã¾ã‚“ãŒ","ã§ã‚“ã","ã²ã‚‰ã‚ã","ã²ã‹ã‚Š"], + "🔦": ["ã‹ã„ã¡ã‚…ã†ã§ã‚“ã¨ã†","ã§ã‚“ã","ã²ã‹ã‚Š","ã©ã†ã","ãŸã„ã¾ã¤"], + "🕯": ["ã‚ã†ãã","ã²ã‹ã‚Š"], + "🧯": ["ã—ょã†ã‹ã","ã—ょã†ã‹","ã²","ã‘ã™"], + "🗑": ["ã”ã¿ã°ã“","ã”ã¿","ã‹ã‚“","ã³ã‚“"], + "🛢": ["ã©ã‚‰ã‚€ã‹ã‚“","ã©ã‚‰ã‚€","ãŠã„ã‚‹"], + "🛒": ["ã—ょã£ã´ã‚“ãã‹ãƒ¼ã¨","ã‹ãƒ¼ã¨","ã—ょã£ã´ã‚“ã","ã¨ã‚りー"], + "💸": ["ã¯ãã®ã¯ãˆãŸãŠã•ã¤","ãŽã‚“ã“ã†","ã—ã¸ã„","ã›ã„ãã‚…ã†ã—ょ","ã©ã‚‹","ã¨ã¶","ãŠã‹ã","ã¯ã"], + "💵": ["ã©ã‚‹ã•ã¤","ãŽã‚“ã“ã†","ã—ã¸ã„","ãŠã•ã¤","ã¤ã†ã‹","ã©ã‚‹","ãŠã‹ã"], + "💴": ["ãˆã‚“ãã”ã†ã®ã¯ã„ã£ãŸã“ãŽã£ã¦","ãŽã‚“ã“ã†","ã—ã¸ã„","ãŠã•ã¤","ã¤ã†ã‹","ãŠã‹ã","ãˆã‚“"], + "💶": ["ゆーã‚ã•ã¤","ãŽã‚“ã“ã†","ã—ã¸ã„","ãŠã•ã¤","ã¤ã†ã‹","ゆーã‚","ãŠã‹ã"], + "💷": ["ã½ã‚“ã©ã•ã¤","ãŽã‚“ã“ã†","ã—ã¸ã„","ãŠã•ã¤","ã¤ã†ã‹","ãŠã‹ã","ã½ã‚“ã©"], + "💰": ["ã©ã‚‹ã¶ãã‚","ã°ã£ã","ã©ã‚‹","ãŠã‹ã"], + "🪙": ["ã“ã„ã‚“","ãã‚“","ãã‚“ãžã","ãŠã‹ã","ãŽã‚“","ãŸã‹ã‚‰"], + "💳": ["ãã‚Œã˜ã£ã¨ã‹ãƒ¼ã©","ãŽã‚“ã“ã†","ã‹ãƒ¼ã©","ãã‚Œã˜ã£ã¨","ãŠã‹ã"], + "🪪": ["ã¿ã¶ã‚“ã—ょã†ã‚ã„ã—ょ","ã—ã‹ãã˜ã‚‡ã†ã»ã†","ID","らã„ã›ã‚“ã™","ã›ãã‚…ã‚Šã¦ãƒ"], + "🧾": ["りょã†ã—ã‚…ã†ã—ょ","ã‹ã„ã‘ã„","ã¼ã","ã—ょã†ã“","ã—ょã†ã‚ã„"], + "💎": ["ã»ã†ã›ã","ã ã„ã‚ã‚‚ã‚“ã©","ã˜ã‚…ãˆã‚‹","ã‚ã¾ã‚“ã™"], + "âš–": ["ã¯ã‹ã‚Š","ã¦ã‚“ã³ã‚“","ã“ã†ã›ã„","ã¦ã‚“ã³ã‚“ã–","ã‚‚ã®ã•ã—","ã©ã†ã","ã˜ã‚…ã†ã‚Šã‚‡ã†","ã›ã„ã–"], + "🦯": ["ã—ã‚ã¤ãˆ","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã‚ãŒãµã˜ã‚†ã†"], + "🧰": ["ã©ã†ãã°ã“","ã‚€ã","ã›ã„ã³ã—","ã“ã†ã"], + "🔧": ["れんã¡","ã©ã†ã"], + "🪛": ["ã©ã‚‰ã„ã°ãƒ¼","ãã˜","ã“ã†ã"], + "🔨": ["ã¯ã‚“ã¾ãƒ¼","ã©ã†ã"], + "âš’": ["ã¯ã‚“ã¾ãƒ¼ã¨ã¤ã‚‹ã¯ã—","ã¯ã‚“ã¾ãƒ¼","ã¤ã‚‹ã¯ã—","ã©ã†ã"], + "🛠": ["ã¯ã‚“ã¾ãƒ¼ã¨ã‚Œã‚“ã¡","ã¯ã‚“ã¾ãƒ¼","ã©ã†ã","れんã¡"], + "â›": ["ã¤ã‚‹ã¯ã—","ã•ã„ãã¤","ã©ã†ã"], + "🪓": ["ãŠã®","ãŸãŸããŽã‚Š","ã¦ãŠã®","ã‚れる","ã‚‚ãã–ã„","ã“ã†ã"], + "🪚": ["ã‚‚ã£ã“ã†ã‚ˆã†ã®ã“ãŽã‚Š","ã ã„ã","ã–ã„ã‚‚ã","ã®ã“ãŽã‚Š","ã“ã†ã"], + "🔩": ["ãªã£ã¨ã¨ã¼ã‚‹ã¨","ã¼ã‚‹ã¨","ãªã£ã¨","ã©ã†ã"], + "âš™": ["ã¯ãã‚‹ã¾","ãŽã‚","ã©ã†ã"], + "⛓": ["ãã•ã‚Š"], + "ðŸª": ["ãµã£ã","ã‚ãª","ã„ã‹ã•ã¾","ãºã¦ã‚“","ゆã†ã‚ã","ãµãƒã£ã—ã‚“ã","ã¤ãƒ¼ã‚‹"], + "🪜": ["ã¯ã—ã”","ã®ã¼ã‚‹","よã“ãŽ","ã ã‚“","ã“ã†ã"], + "🧱": ["れんãŒ","ãã‚“ã©","ã‘ã‚“ã›ã¤","ã‚‚ã‚‹ãŸã‚‹","ã‹ã¹"], + "🪨": ["ã‚ã£ã","ã„ã‚","ã‘ã‚“ãžã†ã¶ã¤","ãŠã‚‚ã„","ã“ãŸã„","ã„ã—"], + "🪵": ["ã‚‚ãã–ã„","ã‘ã‚“ãžã†ã¶ã¤","ã¾ã‚‹ãŸ","ã–ã„ã‚‚ã","ã¯ãŸ"], + "🔫": ["ã¿ãšã§ã£ã½ã†","ã¿ãš","ã´ã™ã¨ã‚‹","ãµã‚“ã—ゃã","ã˜ã‚…ã†"], + "🧨": ["ã°ãã¡ã","ã ã„ãªã¾ã„ã¨","ã‹ã‚„ã","ã¯ãªã³"], + "💣": ["ã°ãã ã‚“"], + "🔪": ["ã»ã†ã¡ã‚‡ã†","ãã£ã¡ã‚“ãªã„ãµ","ã¡ã‚‡ã†ã‚Š","ãªã„ãµ"], + "🗡": ["ãŸã‚“ã‘ã‚“","ãªã„ãµ"], + "âš”": ["ã“ã†ã•ã—ãŸã‘ã‚“","ã“ã†ã•","ã‘ã‚“"], + "🛡": ["ãŸã¦"], + "🚬": ["ãã¤ãˆã‚“ã¾ãƒ¼ã","ã‚ãã¦ãƒã³ã¦ãƒ","ãã¤ãˆã‚“"], + "âš°": ["ã‹ã‚“","ã—"], + "🪦": ["ã¯ã‹ã„ã—","ã¼ã¡","ã—","ã¼","ã¯ã‹ã°","ã¯ã‚ã†ãƒãƒ¼ã‚“"], + "âš±": ["ã“ã¤ã¤ã¼","ã—","ãã†ãŽ"], + "ðŸº": ["ã‚ã‚“ãµã‰ã‚‰","ã¿ãšãŒã‚ã–","りょã†ã‚Š","ã®ã¿ã‚‚ã®","ã¿ãšã•ã—","ã©ã†ã","ã›ã„ã–"], + "🔮": ["ã™ã„ã—ょã†ã ã¾","ãŸã¾","ã™ã„ã—ょã†","ãŠã¨ãŽã°ãªã—","ãµãã‚“ãŸã˜ãƒ¼","ã†ã‚‰ãªã„","ã©ã†ã"], + "🪄": ["ã¾ã»ã†ã®ã¤ãˆ","ã¾ã»ã†","ã¼ã†","ã¾ã˜ã‚‡","ã¾ã»ã†ã¤ã‹ã„"], + "📿": ["ã˜ã‚…ãšã˜ã‚‡ã†ã®ã„ã®ã‚Šã®ã‚ˆã†ã","ã˜ã‚…ãš","ã„ã‚‹ã„","ãã£ãã‚Œã™","ã„ã®ã‚Š","ã—ã‚…ã†ãょã†"], + "🧿": ["ãªã–ーるã®ãŠã¾ã‚‚ã‚Š","ã˜ã‚…ãšã ã¾","ãŠã¾ã‚‚ã‚Š","よã“ã—ã¾ã—","ãªã–ーる","ã”ãµ"], + "🪬": ["ã¯ã‚€ã•","ãŠã¾ã‚‚ã‚Š","ãµãã¦ãƒã¾","ã¦","ã‚ã‚りー","ã¿ã‚Šã‚ã‚€","ã»ã”"], + "💈": ["ã‚Šã¯ã¤ã¦ã‚“ã®ã‹ã‚“ã°ã‚“ã°ã—ら","ã‚Šã¯ã¤ã¦ã‚“","ã¨ã“ã‚„","ã•ã‚“ã±ã¤","ã‹ã‚“ã°ã‚“ã°ã—ら"], + "🧲": ["ã˜ã—ゃã","ã‚ã¨ã‚‰ãã—ょん","ã°ã¦ã„"], + "âš—": ["ã˜ã‚‡ã†ã‚Šã‚…ã†ã","ã‹ãŒã","ã˜ã£ã‘ã‚“","ã©ã†ã"], + "🧪": ["ã—ã‘ã‚“ã‹ã‚“","ã‹ãŒãã—ゃ","ã‹ãŒã","ã˜ã£ã‘ã‚“","ã˜ã£ã‘ã‚“ã—ã¤"], + "🧫": ["ãºã¨ã‚Šã•ã‚‰","ã°ãã¦ã‚Šã‚","ã›ã„ã¶ã¤ãŒãã—ゃ","ã›ã„ã¶ã¤ãŒã","ã¶ã‚“ã‹","ã˜ã£ã‘ã‚“ã—ã¤"], + "🧬": ["DNA","ã›ã„ã¶ã¤ãŒãã—ゃ","ã—ã‚“ã‹","ã„ã§ã‚“ã—","ã„ã§ã‚“ã—ãŒã","ã›ã„ã‚ã„"], + "ðŸ”": ["ã¼ã†ãˆã‚“ãょã†","ã¤ãƒ¼ã‚‹"], + "🔬": ["ã‘ã‚“ã³ãょã†","ã¤ãƒ¼ã‚‹"], + "🕳": ["ã‚ãª"], + "🩻": ["Xã›ã‚“","ã»ã","ã„ã—","ã„りょã†","ã“ã£ã‹ã"], + "💊": ["ãã™ã‚Š","ã„ã—","ã´ã‚‹","ã³ã‚‡ã†ã"], + "💉": ["ã¡ã‚…ã†ã—ゃã","ã„ã—","ãã™ã‚Š","ã¡ã‚…ã†ã—ゃã¯ã‚Š","ã¡ã‚…ã†ã—ゃ","ã³ã‚‡ã†ã","ã©ã†ã","ã‚ãã¡ã‚“"], + "🩸": ["ã¡1ã¦ã","ã„ã—","ãã™ã‚Š","ã‘ã¤ãˆã","ã›ã„ã‚Š"], + "🩹": ["ãŒãƒ¼ãœã¤ãã°ã‚“ãã†ã“ã†","ã„ã—","ãã™ã‚Š","ã°ã‚“ã©ãˆã„ã©","ã»ã†ãŸã„","ã°ã‚“ãã†ã“ã†"], + "🩺": ["ã¡ã‚‡ã†ã—ã‚“ã","ã„ã—","ãã™ã‚Š","ã—ã‚“ãžã†"], + "🌡": ["ãŠã‚“ã©ã‘ã„","ã¦ã‚“ã","ãŠã‚“ã©"], + "🩼": ["ã¾ã¤ã°ã¥ãˆ","ã¤ãˆ","ã—ょã†ãŒã„","ã‘ãŒ","ã„ã©ã†ã»ã˜ã‚‡","ã¼ã†"], + "ðŸ·": ["らã¹ã‚‹","ã«ãµã "], + "🔖": ["ã¶ã£ãã¾ãƒ¼ã","ã—ãŠã‚Š","ã—ã‚‹ã—"], + "🚽": ["ã¨ã„ã‚Œ"], + "🪠": ["ã·ã‚‰ã‚“ã˜ã‚ƒãƒ¼","ãµã‰ãƒ¼ã™ã‹ã£ã·","ã¯ã„ã‹ã‚“ã“ã†","ãã‚…ã†ã„ã‚“","ã¨ã„ã‚Œ"], + "🚿": ["ã—ゃã‚ー","ã¿ãš"], + "ðŸ›": ["ã°ã™ãŸã¶","ãµã‚","よããã†"], + "🛀": ["ãµã‚","よããã†"], + "🪮": ["ã¸ã‚ã´ã£ã","ã‚ãµã‚","ãã—","ã‹ã¿","ã´ã£ã"], + "🪥": ["ã¯ã¶ã‚‰ã—","ã°ã™ã‚‹ãƒ¼ã‚€","ã¶ã‚‰ã—","ãã‚Œã„","ã¯ã„ã—ゃ","ãˆã„ã›ã„","ã¯"], + "🪒": ["ã‹ã¿ãã‚Š","ã™ã‚‹ã©ã„","ã²ã’ã™ã‚Š"], + "🧴": ["ã‚ーã—ょんã¼ã¨ã‚‹","ã‚ーã—ょん","ã»ã—ã‚ã–ã„","ã—ゃんã·ãƒ¼","ã²ã‚„ã‘ã¨ã‚"], + "🧻": ["ãºãƒ¼ã±ãƒ¼ã‚ーる","ãºãƒ¼ã±ãƒ¼ãŸãŠã‚‹","ã¨ã„ã‚Œã£ã¨ãºãƒ¼ã±ãƒ¼"], + "🧼": ["ã›ã£ã‘ã‚“","ã¼ã†","ã¿ãšã‚ã³","ãりーã«ã‚“ã","ã‚ã‚","ã›ã£ã‘ã‚“ã„ã‚Œ"], + "🫧": ["ã°ã¶ã‚‹","ã’ã£ã·","ãã‚Œã„","ã›ã£ã‘ã‚“","ã™ã„ã¡ã‚…ã†"], + "🧽": ["ã™ã½ã‚“ã˜","ãã‚…ã†ã—ã‚…ã†","ãりーã«ã‚“ã","ãŸã“ã†ã›ã„"], + "🧹": ["ã»ã†ã","ãりーã«ã‚“ã","ãã†ã˜","ã¾ã˜ã‚‡"], + "🧺": ["ã°ã™ã‘ã£ã¨","ã®ã†ãŽã‚‡ã†","らんã©ã‚Šãƒ¼","ã´ãã«ã£ã"], + "🪣": ["ã°ã‘ã¤","ãŸã‚‹","ã¦ãŠã‘","ãŠãŠã ã‚‹"], + "🔑": ["ã‹ãŽ","ã˜ã‚‡ã†","ã±ã™ã‚ーã©"], + "ðŸ—": ["ãµã‚‹ã„ã‹ãŽ","ã‹ãŽ","ã˜ã‚‡ã†","ãµã‚‹ã„"], + "🪤": ["ããšã¿ã¨ã‚Šã","ãˆã•","ããšã¿","ã‹ã˜ã¯ã©ã†ã¶ã¤","ã‚ãªã‚","ã‚ãª"], + "🛋": ["ããµãーã¨ã‚‰ã‚“ã·","ããµãー","ã»ã¦ã‚‹","らんã·"], + "🪑": ["ã„ã™","ã–ã›ã","ã™ã‚ã‚‹"], + "🛌": ["ã—ã‚…ãã¯ãã—ã›ã¤","ãã‚‹","ã»ã¦ã‚‹","ã™ã„ã¿ã‚“","ã¹ã£ã©"], + "ðŸ›": ["ã¹ã£ã©","ã»ã¦ã‚‹","ã™ã„ã¿ã‚“"], + "🚪": ["ã©ã‚","ã¨ã³ã‚‰"], + "🪞": ["ã‹ãŒã¿","ã¯ã‚“ã—ゃ","ã¯ã‚“ã—ゃãŸã„","ã¯ã‚“ã—ゃãょã†"], + "🪟": ["ã¾ã©","ã‚ã","ã—ã‚“ã›ã‚“ãªãã†ã","ãŒã‚‰ã™","ã‹ã„ã“ã†ã¶","ã¨ã†ã‚ã„","ã—ã‹ã„"], + "🧳": ["ã¦ã«ã‚‚ã¤","ã±ã£ãã‚“ã","りょã“ã†","ã™ãƒ¼ã¤ã‘ーã™"], + "🛎": ["ãŸãã˜ã‚‡ã†ã¹ã‚‹","ã¹ã‚‹","ã»ã¦ã‚‹"], + "🖼": ["ãŒãã«ã¯ã„ã£ãŸã—ゃã—ã‚“","ã‚ーã¨","ãŒãã¶ã¡","ã³ã˜ã‚…ã¤ã‹ã‚“","ã‹ã„ãŒ","ã—ゃã—ã‚“"], + "ðŸ§": ["ã“ã‚“ã±ã™","ã˜ã—ゃã","ãªã³ã’ーã—ょん","ãŠã‚Šãˆã‚“ã¦ãƒ¼ã‚Šã‚“ã"], + "🗺": ["ã›ã‹ã„ã¡ãš","ã¡ãš","ã›ã‹ã„"], + "â›±": ["ãŸã¦ã‚‰ã‚ŒãŸã±ã‚‰ãã‚‹","ã‚ã‚","ã¯ã‚Œ","ã‹ã•","ã¦ã‚“ã"], + "ðŸª": ["ãŠã‚ŠãŸãŸã¿ã›ã‚“ã™","ã‚Œã„ãゃã","ãˆã‚“りょãŒã¡","ã ã‚“ã™","ãµãã‚“","ãµã‚‰ã£ãŸãƒ¼","ãã¤","ã‚ã¤ã„","ã†ã¡ã","ã²ã‚ãŒã‚‹"], + "🗿": ["ã‚‚ã‚„ã„ãžã†","ã‚‚ã‚ã„ãžã†","ã‹ãŠ","ãžã†"], + "ðŸ›": ["ã‹ã„ã‚‚ã®ã¶ãã‚","ã‹ã°ã‚“","ã»ã¦ã‚‹","ã‹ã„ã‚‚ã®"], + "🎈": ["ãµã†ã›ã‚“","ã‚ãã¦ãƒã³ã¦ãƒ","ãŠã„ã‚ã„","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨"], + "ðŸŽ": ["ã“ã„ã®ã¼ã‚Š","ã‚ãã¦ãƒã³ã¦ãƒ","ã“ã„","ãŠã„ã‚ã„","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã¯ãŸ","ãµããªãŒã—"], + "🎀": ["ã‚Šã¼ã‚“","ãŠã„ã‚ã„"], + "🧧": ["ã‚ã‹ã„ãµã†ã¨ã†","ãŽãµã¨","ã“ã†ã†ã‚“","ã»ã‚“ã°ãŠ","らã„ã—ー","ãŠã‹ã"], + "ðŸŽ": ["ã·ã‚Œãœã‚“ã¨","ã¯ã“","ãŠã„ã‚ã„","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãŠãã‚Šã‚‚ã®","ã»ã†ãã†"], + "🎊": ["ãã™ã ã¾","ã‚ãã¦ãƒã³ã¦ãƒ","ãŠã„ã‚ã„","ã‹ã¿ãµã¶ã","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨"], + "🎉": ["ãらã£ã‹ãƒ¼","ã‚ãã¦ãƒã³ã¦ãƒ","ãŠã„ã‚ã„","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã±ãƒ¼ã¦ãƒãƒ¼","ã˜ã‚ƒãƒ¼ã‚“"], + "🪅": ["ã´ã«ã‚ƒãƒ¼ãŸ","ãŠã„ã‚ã„","ã±ãƒ¼ã¦ãƒãƒ¼","ã´ãªãƒ¼ãŸ"], + "🪩": ["ã¿ã‚‰ãƒ¼ã¼ãƒ¼ã‚‹","ã ã‚“ã™","ã§ãƒã™ã“","ã‹ãŒã‚„ã","ã±ãƒ¼ã¦ãƒãƒ¼"], + "🪆": ["ã„ã‚Œã“ã«ã‚“ãŽã‚‡ã†","ã«ã‚“ãŽã‚‡ã†","ã„ã‚Œã“","ã‚ã—ã‚"], + "🎎": ["ã²ãªã¾ã¤ã‚Š","ã‚ãã¦ãƒã³ã¦ãƒ","ãŠã„ã‚ã„","ã«ã‚“ãŽã‚‡ã†","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã¾ã¤ã‚Š","ã«ã£ã½ã‚“"], + "ðŸŽ": ["ãµã†ã‚Šã‚“","ã‚ãã¦ãƒã³ã¦ãƒ","ã‹ã","ãŠã„ã‚ã„","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãµã†"], + "ðŸ®": ["ã„ã–ã‹ã‚„ã®ã¡ã‚‡ã†ã¡ã‚“","ã‚ã‹ã¡ã‚‡ã†ã¡ã‚“","ã„ã–ã‹ã‚„","ã«ã£ã½ã‚“","ã¡ã‚‡ã†ã¡ã‚“","ã‚ã‹ã‚Š","ã‚ã‹"], + "🪔": ["ã§ãƒã‚„らんã·","ã§ãƒã‚„","らんã·","ãŠã„ã‚‹"], + "✉ï¸": ["ãµã†ã¨ã†","Eã‚ーる","ã§ã‚“ã—ã‚ーる"], + "📩": ["ã‚ーるã˜ã‚…ã—ã‚“ã¡ã‚…ã†","ã‚„ã˜ã‚‹ã—","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã—ãŸ","Eã‚ーる","ã§ã‚“ã—ã‚ーる","ãµã†ã¨ã†","ã¦ãŒã¿","ã‚ーる","ãŠãã‚‹","ãã†ã—ã‚“"], + "📨": ["ã‚ーるã˜ã‚…ã—ã‚“","ã“ã¿ã‚…ã«ã‘ーã—ょん","Eã‚ーる","ã§ã‚“ã—ã‚ーる","ãµã†ã¨ã†","ã†ã‘ã¨ã‚‹","ã¦ãŒã¿","ã‚ーる","ã˜ã‚…ã—ã‚“"], + "📧": ["Eã‚ーる","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã§ã‚“ã—ã‚ーる","ã¦ãŒã¿","ã‚ーる"], + "💌": ["らã¶ã‚ŒãŸãƒ¼","ã¯ãƒ¼ã¨","ã¦ãŒã¿","ã‚ã„","ã‚ーる","ã‚ã¾ã‚“ã™"], + "📮": ["ã½ã™ã¨","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã‚ーる","ゆã†ã³ã‚“ã†ã‘"], + "📪": ["ã¯ãŸãŒã•ãŒã£ã¦ã„ã¦ã¨ã˜ã¦ã„ã‚‹ã˜ã‚‡ã†ãŸã„ã®ã‚†ã†ã³ã‚“ã†ã‘","ã¨ã˜ã‚‹","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã¯ãŸ","ã•ãŒã£ãŸ","ã‚ーる","ã½ã™ã¨","ゆã†ã³ã‚“ã†ã‘"], + "📫": ["ã¯ãŸãŒã‚ãŒã£ã¦ã„ã¦ã¨ã˜ã¦ã„ã‚‹ã˜ã‚‡ã†ãŸã„ã®ã‚†ã†ã³ã‚“ã†ã‘","ã¨ã˜ã‚‹","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã¯ãŸ","ã‚ーる","ゆã†ã³ã‚“ã†ã‘","ã½ã™ã¨"], + "📬": ["ã¯ãŸãŒã‚ãŒã£ã¦ã„ã¦ã²ã‚‰ã„ã¦ã„ã‚‹ã˜ã‚‡ã†ãŸã„ã®ã‚†ã†ã³ã‚“ã†ã‘","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã¯ãŸ","ã‚ーる","ã½ã™ã¨","ã‚ã‘ã‚‹","ゆã†ã³ã‚“ã†ã‘"], + "ðŸ“": ["ã¯ãŸãŒã•ãŒã£ã¦ã„ã¦ã²ã‚‰ã„ã¦ã„るゆã†ã³ã‚“ã†ã‘","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã¯ãŸ","ã•ã’","ã‚ーる","ã‚ーるã¼ã£ãã™","ã‚ã‘ã‚‹","ゆã†ã³ã‚“ã†ã‘"], + "📦": ["ã«ã‚‚ã¤","ã¯ã“","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã±ã£ã‘ーã˜","ã“ã¥ã¤ã¿"], + "📯": ["ゆã†ã³ã‚“らã£ã±","ã“ã¿ã‚…ã«ã‘ーã—ょん","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã‹ã","ã½ã™ã¨","ゆã†ã³ã‚“"], + "📥": ["ã˜ã‚…ã—ã‚“ã¨ã‚Œã„","ã¯ã“","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã¦ãŒã¿","ã‚ーる","ã˜ã‚…ã—ã‚“","ã¨ã‚Œã„"], + "📤": ["ãã†ã—ã‚“ã¨ã‚Œã„","ã¯ã“","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã¦ãŒã¿","ã‚ーる","ãã†ã—ã‚“","ã¨ã‚Œã„"], + "📜": ["ã¾ãã‚‚ã®","ã‹ã¿"], + "📃": ["ã’ã‚“ã“ã†","ã‹ãƒ¼ã‚‹","ã©ãã‚…ã‚ã‚“ã¨","ãºãƒ¼ã˜"], + "📑": ["ã¶ã£ãã¾ãƒ¼ããŸã¶","ã¶ã£ãã¾ãƒ¼ã","ã¾ãƒ¼ã","ã¾ãƒ¼ã‹ãƒ¼","ãŸã¶"], + "📊": ["ã¼ã†ãらãµ","ã°ãƒ¼","ã¡ã‚ƒãƒ¼ã¨","ãらãµ"], + "📈": ["ã˜ã‚‡ã†ã—ょã†ã™ã‚‹ãらãµ","ã˜ã‚‡ã†ã—ょã†ã¡ã‚ƒãƒ¼ã¨","ã¡ã‚ƒãƒ¼ã¨","ãらãµ","ã›ã„ã¡ã‚‡ã†","ã¨ã‚Œã‚“ã©","ã†ã‚ã‚€ã"], + "📉": ["ã‹ã“ã†ã™ã‚‹ãらãµ","ã‹ã“ã†ã¡ã‚ƒãƒ¼ã¨","ã¡ã‚ƒãƒ¼ã¨","ã†ãˆ","ãらãµ","ã¨ã‚Œã‚“ã©"], + "📄": ["ã¶ã‚“ã—ょ","ãºãƒ¼ã˜"], + "📅": ["ã‹ã‚Œã‚“ã ー","ã²ã¥ã‘"], + "📆": ["ã²ã‚ãã‚Šã‹ã‚Œã‚“ã ー","ã‹ã‚Œã‚“ã ー"], + "🗓": ["ã‚Šã‚“ãã‹ã‚Œã‚“ã ー","ã‹ã‚Œã‚“ã ー","ã±ã£ã©","らã›ã‚“ã˜ã‚‡ã†"], + "📇": ["ã‚ã„ã—ãµã‰ã‚‹ã ","ã‹ãƒ¼ã©","ã•ãã„ã‚“","ã‚ーらã§ã£ãã™"], + "🗃": ["ã‹ãƒ¼ã©ãµãã„ã‚‹","ã¯ã“","ã‹ãƒ¼ã©","ãµãã„ã‚‹"], + "🗳": ["ã¨ã†ã²ã‚‡ã†ã‚ˆã†ã—ã¨ã¨ã†ã²ã‚‡ã†ã°ã“","ã¨ã†ã²ã‚‡ã†ã‚ˆã†ã—","ã¯ã“","ã²ã‚‡ã†","ã¨ã†ã²ã‚‡ã†"], + "🗄": ["ãµãã„ã‚‹ã—ã‚…ã†ã®ã†ã“","ã—ã‚…ã†ã®ã†","ãµãã„ã‚‹"], + "📋": ["ãã‚Šã£ã·ã¼ãƒ¼ã©"], + "🗒": ["ã‚Šã‚“ãã®ãƒ¼ã¨","ã®ãƒ¼ã¨","ã±ã£ã©","らã›ã‚“ã˜ã‚‡ã†"], + "ðŸ“": ["ãµã‰ã‚‹ã ","ãµãã„ã‚‹"], + "📂": ["ã²ã‚‰ã„ãŸãµã‰ã‚‹ã ","ãµãã„ã‚‹","ãµã‰ã‚‹ã ","ã²ã‚‰ã„ãŸ"], + "🗂": ["ã—ãã‚Šã‹ãƒ¼ã©","ã‹ãƒ¼ã©","ã—ãã‚Š","ã•ãã„ã‚“"], + "🗞": ["ã¾ã‚‹ã‚ãŸã—ã‚“ã¶ã‚“","ã«ã‚…ーã™","ã—ã‚“ã¶ã‚“","ã‹ã¿","ã¾ã‚‹ã‚ãŸ"], + "📰": ["ã—ã‚“ã¶ã‚“","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã«ã‚…ーã™","ã‹ã¿"], + "🪧": ["ã·ã‚‰ã‹ãƒ¼ã©","ã§ã‚‚","ã—ãŒã‚‰ã¿","ã“ã†ãŽ","ã‹ã‚“ã°ã‚“"], + "📓": ["ã®ãƒ¼ã¨"], + "📕": ["ã¨ã˜ãŸã»ã‚“","ã»ã‚“","ã¨ã˜ã¦ã„ã‚‹"], + "📗": ["ã¿ã©ã‚Šã„ã‚ã®ã»ã‚“","ã»ã‚“","ã¿ã©ã‚Š"], + "📘": ["ã‚ãŠã„ã»ã‚“","ã‚ãŠ","ã»ã‚“"], + "📙": ["ãŠã‚Œã‚“ã˜ã„ã‚ã®ã»ã‚“","ã»ã‚“","ãŠã‚Œã‚“ã˜"], + "📔": ["ãã†ã—ょãã‹ã°ãƒ¼ã®ã®ãƒ¼ã¨","ã»ã‚“","ã‹ã°ãƒ¼","ãã†ã—ょã","ã®ãƒ¼ã¨"], + "📒": ["ã¡ã‚‡ã†ã¼","ã‚‚ã¨ã¡ã‚‡ã†","ã®ãƒ¼ã¨"], + "📚": ["ã—ょã›ã","ã»ã‚“"], + "📖": ["ã²ã‚‰ã„ãŸã»ã‚“","ã»ã‚“","ã²ã‚‰ã„ãŸ"], + "🔗": ["ã‚Šã‚“ã"], + "📎": ["ãã‚Šã£ã·","ãºãƒ¼ã±ãƒ¼ãã‚Šã£ã·"], + "🖇": ["ã¤ãªãŒã£ãŸãºãƒ¼ã±ãƒ¼ãã‚Šã£ã·","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã‚Šã‚“ã","ãºãƒ¼ã±ãƒ¼ãã‚Šã£ã·"], + "✂ï¸": ["ã¯ã•ã¿","ã©ã†ã"], + "ðŸ“": ["ã•ã‚“ã‹ãã˜ã‚‡ã†ãŽ","ã˜ã‚‡ã†ãŽ","ã¯ã„ã¡","ã•ã‚“ã‹ã"], + "ðŸ“": ["ã˜ã‚‡ã†ãŽ","ã¡ã‚‡ãã˜ã‚‡ã†ãŽ"], + "📌": ["ãŒã³ã‚‡ã†","ã´ã‚“"], + "ðŸ“": ["ãŒã³ã‚‡ã†","ã´ã‚“"], + "🧷": ["ã‚ã‚“ãœã‚“ã´ã‚“","ãŠã‚€ã¤","ã±ã‚“ãã‚ã£ã"], + "🪡": ["ã¬ã„ã¯ã‚Š","ã—ã—ã‚…ã†","ã•ã„ã»ã†","ã¬ã„ã‚","ã»ã†ã”ã†","ã—ãŸã¦"], + "🧵": ["ã™ã‚Œã£ã©","ã¬ã„ã‚ã¿","ã•ã„ã»ã†","ã„ã¨ã¾ã","ã„ã¨","ã—ã‚…ã“ã†ã’ã„"], + "🧶": ["ã„ã¨","ã¼ãƒ¼ã‚‹","ã‹ãŽã°ã‚Šã‚ã¿","ã«ã£ã¨","ã—ã‚…ã“ã†ã’ã„"], + "🪢": ["ã‚€ã™ã³ã‚","ã‚ーã·","ã‹ã‚‰ã‚“ã ","ã²ã‚‚","よりã„ã¨","ãã˜ã‚Œ"], + "ðŸ”": ["ã“ã„ã‚“ã‚ã£ã‹ãƒ¼","ã—ã¾ã£ã¦ã„ã‚‹","ã‹ãŽ","ã›ã˜ã‚‡ã†","ã¼ã†ã¯ã‚“"], + "🔒": ["ã‹ãŽ","ã¨ã˜ã‚‰ã‚ŒãŸ","ã›ã˜ã‚‡ã†"], + "🔓": ["ã‹ã„ã˜ã‚‡ã†","ã›ã˜ã‚‡ã†","ã‚ã‘ã‚‹"], + "ðŸ”": ["ã˜ã‚‡ã†ã¾ãˆã¨ãºã‚“","ã„ã‚“ã","ã˜ã‚‡ã†","ãºã‚“ã•ã","ãºã‚“","ã·ã‚‰ã„ã°ã—ー"], + "🖊": ["ã²ã ã‚Šã—ãŸã‚€ãã®ã¼ãƒ¼ã‚‹ãºã‚“","ã¼ãƒ¼ã‚‹ãºã‚“","ã“ã¿ã‚…ã«ã‘ーã—ょん","ãºã‚“"], + "🖋": ["ã²ã ã‚Šã—ãŸã‚€ãã®ã¾ã‚“ãã‚“ã²ã¤","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã¾ã‚“ãã‚“ã²ã¤","ãºã‚“"], + "✒ï¸": ["ãºã‚“ã•ã","ãºã‚“"], + "ðŸ“": ["ã‚ã‚‚","ã“ã¿ã‚…ã«ã‘ーã—ょん","ãˆã‚“ã´ã¤"], + "âœï¸": ["ãˆã‚“ã´ã¤"], + "ðŸ–": ["ã²ã ã‚Šã—ãŸã‚€ãã®ãれよん","ã“ã¿ã‚…ã«ã‘ーã—ょん","ãれよん"], + "🖌": ["ã²ã ã‚Šã—ãŸã‚€ãã®ã¶ã‚‰ã—","ã“ã¿ã‚…ã«ã‘ーã—ょん","ãºã„ã‚“ã¨ã¶ã‚‰ã—","ãˆ"], + "ðŸ”": ["ã²ã ã‚Šã‚€ãã‚€ã—ã‚ãŒã","ã‚ãŒã","ã‹ãã ã„","ã‘ã‚“ã•ã","ã¤ãƒ¼ã‚‹"], + "🔎": ["ã¿ãŽã‚€ãã‚€ã—ã‚ãŒã","ã‚ãŒã","ã‹ãã ã„","ã‘ã‚“ã•ã","ã¤ãƒ¼ã‚‹"], + "â¤ï¸": ["ã‚ã‹ã„ã‚ã®ã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨"], + "🧡": ["ãŠã‚Œã‚“ã˜ã„ã‚ã®ã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ãŠã‚Œã‚“ã˜ã„ã‚"], + "💛": ["ãã„ã‚ã®ã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ãã„ã‚"], + "💚": ["ã¿ã©ã‚Šã®ã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ã¿ã©ã‚Š"], + "💙": ["ã‚ãŠã®ã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ã‚ãŠ"], + "💜": ["むらã•ãã®ã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","むらã•ã"], + "🤎": ["ã¡ã‚ƒã„ã‚ã®ã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ã¡ã‚ƒã„ã‚"], + "🖤": ["ãã‚ã„ã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ãã‚","ã‚ã","ã‚ã‚‹ã‚‚ã®"], + "ðŸ¤": ["ã—ã‚ã®ã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ã—ã‚"], + "💔": ["ã‚ã‚ŒãŸã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ã“ã‚れる","ã¯ãょã"], + "â£": ["ã¯ãƒ¼ã¨ã®ã³ã£ãã‚Šã¾ãƒ¼ã","ã¯ãƒ¼ã¨","ã³ã£ãã‚Šã¾ãƒ¼ã","ãã”ã†"], + "💕": ["2ã¤ã®ã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ã‚ã„"], + "💞": ["ã‹ã„ã¦ã‚“ã™ã‚‹ã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ã‹ã„ã¦ã‚“"], + "💓": ["ã“ã©ã†ã™ã‚‹ã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ã“ã©ã†","ã©ãã©ã"], + "💗": ["ã²ã‹ã‚‹ã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ã‚ãã‚ã","ã²ã‹ã‚‹","ã“ã©ã†","ãã‚“ã¡ã‚‡ã†"], + "💖": ["ãらã‚ãã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ã‚ãã‚ã","ãらãら"], + "💘": ["ã„ã¬ã‹ã‚ŒãŸã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ã‚„","ãゅーã´ã£ã©","ã‚ã¾ã‚“ã™"], + "ðŸ’": ["ã‚Šã¼ã‚“ã¤ãã®ã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ã‚Šã¼ã‚“","ã°ã‚Œã‚“ãŸã„ã‚“"], + "â¤ï¸â€ðŸ”¥": ["ã‚‚ãˆã¦ã„ã‚‹ã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ã²","ã‚‚ãˆã‚‹","ã‚ã„","ãã¤ã˜ã‚‡ã†","ã—ã‚“ã›ã„ãªã¯ãƒ¼ã¨"], + "â¤ï¸â€ðŸ©¹": ["ã¦ã‚ã¦ã—ã¦ã„ã‚‹ã¯ãƒ¼ã¨","ã¯ãƒ¼ã¨","ã‘ã‚“ã“ã†ã«ãªã‚‹","ã‹ã„ãœã‚“ã—ã¦ã„ã‚‹","ã¦ã‚ã¦ã—ã¦ã„ã‚‹","ã‹ã„ãµãã—ã¦ã„ã‚‹","ã‚„ã¿ã‚ãŒã‚Š","ã’ã‚“ã"], + "💟": ["ã¯ãƒ¼ã¨ã®ã§ã“れーã—ょん","ã¯ãƒ¼ã¨"], + "☮": ["ã´ãƒ¼ã™ã¾ãƒ¼ã","ã¸ã„ã‚"], + "âœ": ["らã¦ã‚“ã˜ã‚…ã†ã˜","ãã‚Šã™ã¡ã‚ƒã‚“","ã˜ã‚…ã†ã˜ã‹","ã—ã‚…ã†ãょã†"], + "☪": ["ã»ã—ã¨ã¿ã‹ã¥ã","ã„ã™ã‚‰ã‚€","ã‚€ã™ã‚Šã‚€","ã—ã‚…ã†ãょã†"], + "🕉": ["ãŠãƒ¼ã‚€ã¾ãƒ¼ã","ã²ã‚“ã©ã…ーãょã†","ãŠãƒ¼ã‚€","ã—ã‚…ã†ãょã†"], + "☸": ["ã»ã†ã‚Šã‚“","ã¶ã£ãょã†ã¨","ã ーã¾","ã—ã‚…ã†ãょã†"], + "✡": ["ã ã³ã§ã®ã»ã—","ã ã³ã§","ゆã ã‚„ã˜ã‚“","ゆã ã‚„ãょã†","ã—ã‚…ã†ãょã†","ã»ã—"], + "🔯": ["ã‚ãã¼ã†ã›ã„","ã†ã‚‰ãªã„","ã»ã—"], + "🕎": ["ã¯ã¬ã£ãーやー","ã—ょãã ã„","ã‚ã®ãƒ¼ã‚‰ãƒ¼","ã—ã‚…ã†ãょã†"], + "☯": ["ã„んよã†","ã—ã‚…ã†ãょã†","ã©ã†","ã©ã†ã‹","ã²","ã‹ã’"], + "☦": ["ã¯ã£ãŸã‚“ã˜ã‚…ã†ã˜ã‹","ãã‚Šã™ã¡ã‚ƒã‚“","ã˜ã‚…ã†ã˜ã‹","ã—ã‚…ã†ãょã†"], + "🪯": ["ã‹ã‚“ã ","ã—ã‚…ã†ãょã†","ã—ーããょã†ã¨"], + "ðŸ›": ["ã‚Œã„ã¯ã„ã—ょ","ã—ã‚…ã†ãょã†","ã‚Œã„ã¯ã„"], + "⛎": ["ã¸ã³ã¤ã‹ã„ã–","ã†ã‚“ã±ã‚“ã«ã‚“","ã¸ã³","ã›ã„ã–"], + "♈": ["ãŠã²ã¤ã˜ã–","ã“ã²ã¤ã˜","ã›ã„ã–"], + "♉": ["ãŠã†ã—ã–","ãŠã™ã†ã—","ゆã†ã†ã—","ã›ã„ã–"], + "♊": ["ãµãŸã”ã–","ãµãŸã”","ã›ã„ã–"], + "♋": ["ãŒã‚“","ã‹ã«ã–","ã‹ã«","ã›ã„ã–"], + "♌": ["ã—ã—ã–","らã„ãŠã‚“","ã›ã„ã–"], + "â™": ["ãŠã¨ã‚ã–","ãŠã¨ã‚","ã—ょã˜ã‚‡","ã›ã„ã–"], + "♎": ["ã¦ã‚“ã³ã‚“ã–","ã¦ã‚“ã³ã‚“","ã“ã†ã›ã„","ã¯ã‹ã‚Š","ã›ã„ã–"], + "â™": ["ã•ãã‚Šã–","ã•ãã‚Š","ã›ã„ã–"], + "â™": ["ã„ã¦ã–","ã—ゃã—ã‚…","ã—ゃã—ã‚…ã–","ã›ã„ã–"], + "♑": ["ã‚„ãŽã–","ã‚„ãŽ","ã›ã„ã–"], + "â™’": ["ã¿ãšãŒã‚ã–","ã†ã‚“ã±ã‚“ã˜ã‚“","ã¿ãš","ã›ã„ã–"], + "♓": ["ã†ãŠã–","ã•ã‹ãª","ã›ã„ã–"], + "🆔": ["ã—ã‹ãã‹ã“ã¿ID","ID","ã—ãã¹ã¤"], + "âš›": ["ã’ã‚“ããã”ã†","ã‚€ã—ã‚“ã‚ã‚“ã—ゃ","ã’ã‚“ã—"], + "âš•ï¸": ["ã‚ã™ãã‚Œã´ãŠã™ã®ã¤ãˆ","ã‘ã‚“ã“ã†","ã›ã‚","ã„ã—","ãã™ã‚Š","ã¤ãˆ","ã¸ã³"], + "☢": ["ã»ã†ã—ゃã®ã†ã²ã‚‡ã†ã—ã","ã»ã†ã—ゃã®ã†"], + "☣": ["ã°ã„ãŠã¯ã–ーã©ã²ã‚‡ã†ã—ã","ã›ã„ã¶ã¤ã•ã„ãŒã„"], + "📴": ["ã‘ã„ãŸã„ã§ã‚“ã‚ã§ã‚“ã’ã‚“ãŠãµ","ã‘ã„ãŸã„","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã‚‚ã°ã„ã‚‹","ãŠãµ","ã‘ã„ãŸã„ã§ã‚“ã‚","ã§ã‚“ã‚"], + "📳": ["ã¾ãªãƒ¼ã‚‚ーã©","ã‘ã„ãŸã„","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã‚‚ã°ã„ã‚‹","もーã©","ã‘ã„ãŸã„ã§ã‚“ã‚","ã§ã‚“ã‚","ã°ã„ã¶ã‚Œãƒ¼ã—ょん"], + "🈶": ["ã—ã‹ãã‹ã“ã¿ã‚†ã†","ã«ã»ã‚“ã”","ã‚ã‚Š"], + "🈚": ["ã—ã‹ãã‹ã“ã¿ã‚€","ã—ã‹ãã‹ã“ã¿ã„ãª","ã«ã»ã‚“ã”","ãªã—"], + "🈸": ["ã—ã‹ãã‹ã“ã¿ã—ã‚“","ã—ã‹ãã‹ã“ã¿ã¦ã","ã¡ã‚…ã†ã”ãã”","ã—ã‚“ã›ã„"], + "🈺": ["ã—ã‹ãã‹ã“ã¿ãˆã„","ã¡ã‚…ã†ã”ãã”","ãˆã„ãŽã‚‡ã†"], + "🈷ï¸": ["ã—ã‹ãã‹ã“ã¿ã¤ã","ã«ã»ã‚“ã”","ã¤ããŽã‚"], + "✴ï¸": ["ã¯ã¡ã‚Šã‚‡ã†ã¼ã—","ã»ã—"], + "🆚": ["ã—ã‹ãã‹ã“ã¿VS","ãŸã„","VS"], + "🉑": ["ã¾ã‚‹ã‹ã“ã¿ãょã‹","ã¾ã‚‹ã‹ã“ã¿ã‹","ã¡ã‚…ã†ã”ãã”","ã‹ã®ã†"], + "💮": ["ã—ã‚ã„ã¯ãª","ã¯ãª","ãŸã„ã¸ã‚“よãã§ãã¾ã—ãŸ"], + "ðŸ‰": ["ã¾ã‚‹ã‹ã“ã¿ã¨ã","ã«ã»ã‚“ã”","ã¨ã"], + "㊙ï¸": ["ã¾ã‚‹ã‹ã“ã¿ã²","ã¡ã‚…ã†ã”ãã”","ã²ã‚‡ã†ã„ã‚‚ã˜","ã²"], + "㊗ï¸": ["ã¾ã‚‹ã‹ã“ã¿ã—ã‚…ã","ã¡ã‚…ã†ã”ãã”","ãŠã‚ã§ã¨ã†","ã—ã‚…ã"], + "🈴": ["ã—ã‹ãã‹ã“ã¿ã®ã”ã†","ã—ã‹ãã‹ã“ã¿ã”ã†","ã¡ã‚…ã†ã”ãã”","ã”ã†ã‹ã","ã¦ãã”ã†"], + "🈵": ["ã—ã‹ãã‹ã“ã¿ã¾ã‚“","ã¡ã‚…ã†ã”ãã”","ã¾ã‚“ã—ã¤","ã¾ã‚“ã—ゃ","ã¾ã‚“ãŸã‚“"], + "🈹": ["ã—ã‹ãã‹ã“ã¿ã‚ã‚Š","ã—ã‹ãã‹ã“ã¿ã®ã‚ã‚Š","ã«ã»ã‚“ã”","ã‚ã‚Šã³ã"], + "🈲": ["ã—ã‹ãã‹ã“ã¿ãã‚“","ã«ã»ã‚“ã”","ãã‚“ã—"], + "🅰ï¸": ["ãã‚ã—ã‹ãã‹ã“ã¿A","A","ã‘ã¤ãˆããŒãŸ"], + "🅱ï¸": ["ãã‚ã—ã‹ãã‹ã“ã¿B","B","ã‘ã¤ãˆããŒãŸ"], + "🆎": ["ãã‚ã—ã‹ãã‹ã“ã¿AB","AB","ã‘ã¤ãˆããŒãŸ"], + "🆑": ["ã—ã‹ãã‹ã“ã¿CL","CL"], + "🅾ï¸": ["ãã‚ã—ã‹ãã‹ã“ã¿O","ã‘ã¤ãˆããŒãŸ","O"], + "🆘": ["ã—ã‹ãã‹ã“ã¿SOS","ã¸ã‚‹ã·","SOS"], + "â›”": ["ãŸã¡ã„ã‚Šãã‚“ã—","ãŸã¡ã„ã‚Š","ãã‚“ã—","ã ã‚","ã§ããªã„","ãã‚“ã˜ã‚‹","ã“ã†ã¤ã†"], + "📛": ["ãªãµã ","ã°ã£ã˜","ãªã¾ãˆ"], + "🚫": ["ã—ã‚“ã«ã‚…ã†ãã‚“ã—","ãŸã¡ã„ã‚Š","ãã‚“ã—","ã ã‚","ã§ããªã„","ãã‚“ã˜ã‚‹"], + "âŒ": ["ã°ã¤ã—ã‚‹ã—","ãゃんã›ã‚‹","ãã”ã†","ã‹ã‘ã–ã‚“","ã˜ã‚‡ã†ã–ã‚“","x"], + "â•": ["ãµã¨ã„ãŠãŠããªã¾ã‚‹","ã¾ã‚‹","O"], + "💢": ["ã„ã‹ã‚Šã¾ãƒ¼ã","ã„ã‹ã‚Š","ã¾ã‚“ãŒ","ã’ãã©"], + "♨ï¸": ["ãŠã‚“ã›ã‚“","ã‚ãŸãŸã‹ã„","ã‚ãã§ã‚‹","ã˜ã‚‡ã†ã"], + "🚷": ["ã»ã“ã†ã—ゃãŸã¡ã„ã‚Šãã‚“ã—","ãã‚“ã—","ã ã‚","ãªã„","ã»ã“ã†ã—ゃ","ãã‚“ã˜ã‚‹"], + "🚯": ["ã½ã„ã™ã¦ãã‚“ã—","ãã‚“ã—","ã”ã¿","ã ã‚","ãªã„","ãã‚“ã—ã•ã‚Œã¦ã„ã‚‹"], + "🚳": ["ã˜ã¦ã‚“ã—ゃãã‚“ã—","ã˜ã¦ã‚“ã—ゃ","ã°ã„ã","ãã‚“ã—","ã ã‚","ã§ããªã„","ãã‚“ã˜ã‚‹","ã®ã‚Šã‚‚ã®"], + "🚱": ["ã„んよã†ãµã‹","ã²ã„んりょã†ã™ã„","ã„んりょã†","ãã‚“ã—","ã ã‚","ãªã„","ã„んよã†","ãã‚“ã—ã•ã‚Œã¦ã„ã‚‹","ã¿ãš"], + "🔞": ["18ã•ã„ã¿ã¾ã‚“ãã‚“ã—","18","ãã‚“ã‚Œã„ã›ã„ã’ã‚“","ã˜ã‚…ã†ã¯ã¡","ãã‚“ã—","ã ã‚","ãªã„","ãã‚“ã—ã—ãŸ","ã¿ã›ã„ãã‚“ã—ゃ"], + "📵": ["ã‘ã„ãŸã„ã§ã‚“ã‚ãã‚“ã—","ã‘ã„ãŸã„","ã¤ã†ã—ã‚“","ãã‚“ã—","ã‚‚ã°ã„ã‚‹","ã ã‚","ã§ããªã„","ã‘ã„ãŸã„ã§ã‚“ã‚","ãã‚“ã—ã•ã‚Œã¦ã„ã‚‹","ã§ã‚“ã‚"], + "ðŸš": ["ãã‚“ãˆã‚“","ãã‚“ã—","ã ã‚","ã§ããªã„","ãã‚“ã—ã•ã‚Œã¦ã„ã‚‹","ãã¤ãˆã‚“"], + "â—": ["ã‚ã‹ã„ã³ã£ãã‚Šã¾ãƒ¼ã","ã³ã£ãã‚Š","ã¾ãƒ¼ã","ãã”ã†"], + "â•": ["ã—ã‚ã„ã³ã£ãã‚Šã¾ãƒ¼ã","ã³ã£ãã‚Š","ã¾ãƒ¼ã","ã‹ã“ã¿","ãã”ã†"], + "â“": ["ã‚ã‹ã„ã¯ã¦ãªã¾ãƒ¼ã","ã¾ãƒ¼ã","ãã”ã†","ã¯ã¦ãª"], + "â”": ["ã—ã‚ã„ã¯ã¦ãªã¾ãƒ¼ã","ã¾ãƒ¼ã","ã‹ã“ã¿","ãã”ã†","ã¯ã¦ãª"], + "‼ï¸": ["!!ã¾ãƒ¼ã","ã°ã‚“ã°ã‚“","ã³ã£ãã‚Š","ã¾ãƒ¼ã","ãã”ã†"], + "â‰ï¸": ["!?","ã³ã£ãã‚Š","ã„ã‚“ã¦ã‚ã°ã‚“ã","ã¾ãƒ¼ã","ãã”ã†","ã¯ã¦ãª"], + "💯": ["100ã¦ã‚“","100","ãµã‚‹","ã²ã‚ƒã","ã™ã“ã‚"], + "🔅": ["ã¦ã„ãã©","ã‚ã‹ã‚‹ã•","ã†ã™ãらã„","ã¦ã„"], + "🔆": ["ã“ã†ãã©","ã‚ã‹ã‚‹ã„","ã‚ã‹ã‚‹ã•"], + "🔱": ["ã¨ã‚‰ã„ã§ã‚“ã¨","ã„ã‹ã‚Š","ãˆã‚“ã¶ã‚Œã‚€","ãµã","ã“ã†ã"], + "âšœ": ["ゆりã®ã‚‚ã‚“ã—ょã†"], + "〽ï¸": ["ã„ãŠã‚Šã¦ã‚“","ã—ã‚‹ã—","ã¶ã¶ã‚“"], + "âš ï¸": ["ã‘ã„ã“ã"], + "🚸": ["ã“ã†ã•ã¦ã‚“ã‚’ã‚ãŸã‚‹ã“ã©ã‚‚ãŸã¡","ã“ã©ã‚‚","ã“ã†ã•ã¦ã‚“","ã»ã“ã†ã—ゃ","ã“ã†ã¤ã†"], + "🔰": ["ã—ょã—ã‚“ã—ゃã¾ãƒ¼ã","ã—ょã—ã‚“ã—ゃ","ã¾ãƒ¼ã","ã¿ã©ã‚Š","ã«ã£ã½ã‚“","ã‚ã‹ã°","ã©ã†ã","ã"], + "â™»ï¸": ["ã‚Šã•ã„ãã‚‹ã¾ãƒ¼ã","ã‚Šã•ã„ãã‚‹"], + "🈯": ["ã—ã‹ãã‹ã“ã¿ã‚†ã³","ã«ã»ã‚“ã”"], + "💹": ["ã˜ã‚‡ã†ã—ょã†ã¨ã‚Œã‚“ã©ã®ã¡ã‚ƒãƒ¼ã¨ã¨ãˆã‚“ãã”ã†","ã˜ã‚‡ã†ã—ょã†ã¡ã‚…ã†ãˆã‚“ã¡ã‚ƒãƒ¼ã¨","ãŽã‚“ã“ã†","ã¡ã‚ƒãƒ¼ã¨","ã¤ã†ã‹","ãらãµ","ã›ã„ã¡ã‚‡ã†","ã—ã˜ã‚‡ã†","ãŠã‹ã","ã˜ã‚‡ã†ã—ょã†","ã¨ã‚Œã‚“ã©","ã†ã‚ã‚€ã","ãˆã‚“"], + "â‡ï¸": ["ãらãら"], + "✳ï¸": ["ã‚ã™ãŸã‚Šã™ã (8ã»ã‚“ã“ã†ã›ã„)","ã‚ã™ãŸã‚Šã™ã"], + "âŽ": ["ã—ã‹ãã§ã‹ã“ã¾ã‚ŒãŸã°ã¤ã—ã‚‹ã—","ã¾ãƒ¼ã","ã—ã‹ã"], + "✅": ["ã—ã‚ã„ãµã¨ã˜ã®ã¡ã‡ã£ãã¾ãƒ¼ã","ã¡ã‡ã£ã","ã¾ãƒ¼ã"], + "💠": ["ã©ã£ã¨ã‚‚よã†ã®ã ã„ã‚„","ã¾ã‚“ãŒ","ã ã„ã‚„ã‚‚ã‚“ã©","ãã‹ãŒã","ãªã„ã¶"], + "🌀": ["ã•ã„ãã‚ã‚“","ã¦ã„ãã‚ã¤","ã‚ã¾ã„","ãŸã¤ã¾ã","ãŸã„ãµã†","ã¦ã‚“ã"], + "âž¿": ["ã«ã˜ã‚…ã†ã®ã‹ãƒ¼ã‚‹ã˜ã‚‡ã†ã®ã‚‹ãƒ¼ã·","ã‹ãƒ¼ã‚‹","ã ã¶ã‚‹","るーã·"], + "ðŸŒ": ["ã—ã”ã›ã‚“・ã‘ã„ã›ã‚“ã®ã‚ã‚‹ã¡ãã‚…ã†","ã¡ãã‚…ã†","ã¡ãã‚…ã†ãŽ","ã‘ã„ã›ã‚“","ã›ã‹ã„"], + "♾": ["ã‚€ã’ã‚“","ãˆã„ãˆã‚“","ãµã¸ã‚“ã¦ã"], + "â“‚ï¸": ["ã¾ã‚‹ã‹ã“ã¿M","ãˆã‚“","M"], + "ðŸ§": ["ATM","ATMãã”ã†","ã˜ã©ã†","ãŽã‚“ã“ã†","ã™ã„ã¨ã†"], + "🚾": ["ã¨ã„ã‚Œ","ã‘ã—ょã†ã—ã¤","ãŠã¦ã‚らã„","ã¿ãš","WC"], + "♿": ["ãã‚‹ã¾ã„ã™","ã‚ãã›ã™"], + "🅿ï¸": ["ãã‚ã—ã‹ãã‹ã“ã¿P","ã¡ã‚…ã†ã—ゃã˜ã‚‡ã†"], + "🈳": ["ã—ã‹ãã‹ã“ã¿ãら","ã—ã‹ãã‹ã“ã¿ã®ãら","ã¡ã‚…ã†ã”ãã”","ãらã—ã¤","ã‚ã","ãã†ã—ゃ"], + "🈂ï¸": ["ã—ã‹ãã‹ã“ã¿ã•","ã«ã£ã½ã‚“ã˜ã‚“","ã•ãƒ¼ã³ã™"], + "🛂": ["ã«ã‚…ã†ã“ãã—ã‚“ã•","ã±ã™ã½ãƒ¼ã¨"], + "🛃": ["ãœã„ã‹ã‚“"], + "🛄": ["ã¦ã«ã‚‚ã¤ã†ã‘ã¨ã‚Šã—ょ","ã¦ã«ã‚‚ã¤","ã†ã‘ã¨ã‚Š"], + "🛅": ["ã¦ã«ã‚‚ã¤ã‚ãšã‹ã‚Šã—ょ","ã¦ã«ã‚‚ã¤","ã‚ã£ã‹ãƒ¼","ã‘ã„ã“ã†ã²ã‚“"], + "🚰": ["ã„んりょã†ã™ã„","ã®ã¿ã‚‚ã®","ã¿ãš"], + "🛗": ["ãˆã‚Œã¹ãƒ¼ãŸãƒ¼","ã‚ãã›ã—ã³ã‚Šã¦ãƒ","ã²ãã‚ã’","ã—ょã†ã“ã†ã"], + "🚹": ["ã ã‚“ã›ã„ã®ãã”ã†","ã ã‚“ã›ã„よã†","ã¨ã„ã‚Œ","ãŠã¨ã“","ã ã‚“ã›ã„"], + "♂ï¸": ["ã ã‚“ã›ã„ãã”ã†","ã ã‚“ã›ã„","ãŠã¨ã“"], + "🚺": ["ã˜ã‚‡ã›ã„ã®ãã”ã†","ã˜ã‚‡ã›ã„よã†","ã¨ã„ã‚Œ","ãŠã‚“ãª","ã˜ã‚‡ã›ã„"], + "♀ï¸": ["ã˜ã‚‡ã›ã„ãã”ã†","ã˜ã‚‡ã›ã„","ãŠã‚“ãª"], + "⚧ï¸": ["ã¨ã‚‰ã‚“ã™ã˜ã‡ã‚“ã ーã•ã„ã‚“","ã¨ã‚‰ã‚“ã™ã˜ã‡ã‚“ã ー","ã·ã‚‰ã„ã©","lgbt"], + "🚼": ["ã‚ã‹ã¡ã‚ƒã‚“ã¾ãƒ¼ã","ã‚ã‹ã¡ã‚ƒã‚“","ãŠã‚€ã¤ã‹ãˆ"], + "🚻": ["ã¨ã„ã‚Œ","ã‘ã—ょã†ã—ã¤","WC"], + "🚮": ["ã”ã¿ã™ã¦ã˜ã‚‡ã†","ã³ã‚“ã®ã”ã¿ã™ã¦ã˜ã‚‡ã†","ã”ã¿","ã”ã¿ã°ã“"], + "🎦": ["ãˆã„ãŒ","ã‚ãã¦ãƒã³ã¦ãƒ","ã‹ã‚ら","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãµãƒã‚‹ã‚€","ã©ã†ãŒ"], + "📶": ["ã‚ã‚“ã¦ãª","ã°ãƒ¼","ã‘ã„ãŸã„","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã‚‚ã°ã„ã‚‹","ã‘ã„ãŸã„ã§ã‚“ã‚","ã—ããªã‚‹","ã§ã‚“ã‚"], + "🛜": ["ã‚€ã›ã‚“","ã“ã‚“ã´ã‚…ーãŸ","ã„ã‚“ãŸãƒ¼ãã£ã¨","ãã£ã¨ã‚ーã","Wi-Fi","ã›ã¤ãžã"], + "ðŸˆ": ["ã—ã‹ãã‹ã“ã¿ã“ã“","ã«ã£ã½ã‚“ã˜ã‚“"], + "🆖": ["ã—ã‹ãã‹ã“ã¿NG","NG"], + "🆗": ["ã—ã‹ãã‹ã“ã¿OK","OK"], + "🆙": ["ã—ã‹ãã‹ã“ã¿UP!","ã¾ãƒ¼ã","ã†ãˆ"], + "🆒": ["COOL","ã‹ã£ã“ã„ã„","ãーる"], + "🆕": ["ã—ã‹ãã‹ã“ã¿new","ã—ã‚“"], + "🆓": ["ã—ã‹ãã‹ã“ã¿FREE","ãµã‚Šãƒ¼","むりょã†"], + "0ï¸âƒ£": ["0ãー","0","ãー","ãœã‚"], + "1ï¸âƒ£": ["1ãー","ã„ã¡","ãー"], + "2ï¸âƒ£": ["2ãー","2","ãー","ã«"], + "3ï¸âƒ£": ["3ãー","3","ãー","ã•ã‚“"], + "4ï¸âƒ£": ["4ãー","4","よん","ãー"], + "5ï¸âƒ£": ["5ãー","5","ã”","ãー"], + "6ï¸âƒ£": ["6ãー","6","ãー","ã‚ã"], + "7ï¸âƒ£": ["7ãー","7","ãー","ãªãª"], + "8ï¸âƒ£": ["8ãー","8","ã¯ã¡","ãー"], + "9ï¸âƒ£": ["9ãー","9","ãー","ãã‚…ã†"], + "🔟": ["10ãー","10","ãー","ã˜ã‚…ã†"], + "🔢": ["ã°ã‚“ã”ã†ã®ã«ã‚…ã†ã‚Šã‚‡ããã”ã†","1234","ã«ã‚…ã†ã‚Šã‚‡ã","ã™ã†ã˜"], + "â–¶ï¸": ["ã¿ãŽã‚€ãã•ã‚“ã‹ã","ã•ã„ã›ã„ã¼ãŸã‚“","ã‚„ã˜ã‚‹ã—","ã•ã„ã›ã„","ã¿ãŽ","ã•ã‚“ã‹ã£ã‘ã„"], + "â¸": ["2ã»ã‚“ã®ã™ã„ã¡ã‚‡ãã°ãƒ¼","ã„ã¡ã˜ã¦ã„ã—ã¼ãŸã‚“","ã°ãƒ¼","2ã°ã„","ã„ã¡ã˜ã¦ã„ã—","ã™ã„ã¡ã‚‡ã"], + "â¯": ["ã¿ãŽã‚€ãã®ã•ã‚“ã‹ã£ã‘ã„ã¨ã«ã˜ã‚…ã†ã™ã„ã¡ã‚‡ãã¼ã†","ã•ã„ã›ã„ã¾ãŸã¯ã„ã¡ã˜ã¦ã„ã—ã¼ãŸã‚“","ã‚„ã˜ã‚‹ã—","ã„ã¡ã˜ã¦ã„ã—","ã•ã„ã›ã„","ã¿ãŽ","ã•ã‚“ã‹ã£ã‘ã„"], + "â¹": ["ã¦ã„ã—","ã¦ã„ã—ã¼ãŸã‚“","ã—ã‹ã"], + "âº": ["ã‚ããŒ","ã‚ããŒã¼ãŸã‚“","ã¾ã‚‹"], + "âï¸": ["ã¨ã‚Šã ã—ã¾ãƒ¼ã","ã¨ã‚Šã ã—ã¼ãŸã‚“"], + "â": ["ã¿ãŽã‚€ãã®ã«ã˜ã‚…ã†ã•ã‚“ã‹ã£ã‘ã„ã¨ã™ã„ã¡ã‚‡ãã¼ã†","「ã¤ãŽã®ãょãã€ã¼ãŸã‚“","ã‚„ã˜ã‚‹ã—","ã¤ãŽã®ã°ã‚ã‚“","ã¤ãŽã®ãょã","ã•ã‚“ã‹ã£ã‘ã„"], + "â®": ["ã²ã ã‚Šã‚€ãã®ã«ã˜ã‚…ã†ã•ã‚“ã‹ã£ã‘ã„ã¨ã™ã„ã¡ã‚‡ãã¼ã†","「ã¾ãˆã®ãょãã€ã¼ãŸã‚“","ã‚„ã˜ã‚‹ã—","ã¾ãˆã®ã°ã‚ã‚“","ã¾ãˆã®ãょã","ã•ã‚“ã‹ã£ã‘ã„"], + "â©": ["ã¿ãŽã‚€ãã®ã«ã˜ã‚…ã†ã•ã‚“ã‹ã£ã‘ã„","ã¯ã‚„ãŠãã‚Šã¼ãŸã‚“","ã‚„ã˜ã‚‹ã—","2ã°ã„","ã“ã†ãã","ã™ã™ã‚€"], + "âª": ["ã²ã ã‚Šã‚€ãã®ã«ã˜ã‚…ã†ã•ã‚“ã‹ã£ã‘ã„","ã¯ã‚„ã‚‚ã©ã—ã¼ãŸã‚“","ã‚„ã˜ã‚‹ã—","2ã°ã„","ã¾ãã‚‚ã©ã—"], + "🔀": ["ãã˜ã‚Šã¿ãŽã‚€ãã‚„ã˜ã‚‹ã—ã®ãˆã‚‚ã˜","ã—ゃã£ãµã‚‹","ã‚„ã˜ã‚‹ã—","ã“ã†ã•"], + "ðŸ”": ["ã‚Šã´ãƒ¼ã¨","ã‚Šã´ãƒ¼ã¨ã¼ãŸã‚“","ã‚„ã˜ã‚‹ã—","ã¨ã‘ã„ã¾ã‚ã‚Š"], + "🔂": ["1ãょãã‚’ã‚Šã´ãƒ¼ã¨ã•ã„ã›ã„","ã‚Šã´ãƒ¼ã¨ã¼ãŸã‚“","ã‚„ã˜ã‚‹ã—","ã¨ã‘ã„ã¾ã‚ã‚Š","ã„ã¡ã©"], + "â—€ï¸": ["ã²ã ã‚Šã‚€ãã®ã•ã‚“ã‹ã£ã‘ã„","ã¯ã‚“ã¦ã‚“ã¼ãŸã‚“","ã‚„ã˜ã‚‹ã—","ã²ã ã‚Š","ã¯ã‚“ã¦ã‚“","ã•ã‚“ã‹ã£ã‘ã„"], + "🔼": ["ã†ã‚ã‚€ãã®ã•ã‚“ã‹ã£ã‘ã„","ã†ãˆã¼ãŸã‚“","ã‚„ã˜ã‚‹ã—","ã¼ãŸã‚“","ã†ãˆ"], + "🔽": ["ã—ãŸã‚€ãã®ã•ã‚“ã‹ã£ã‘ã„","ã—ãŸã¼ãŸã‚“","ã‚„ã˜ã‚‹ã—","ã¼ãŸã‚“","ã—ãŸ"], + "â«": ["ã†ã‚ã‚€ãã®ã«ã˜ã‚…ã†ã•ã‚“ã‹ã£ã‘ã„","ã“ã†ããã˜ã‚‡ã†ã—ょã†ã¼ãŸã‚“","ã‚„ã˜ã‚‹ã—","ã ã¶ã‚‹","ã†ãˆ"], + "â¬": ["ã—ãŸã‚€ãã®ã«ã˜ã‚…ã†ã•ã‚“ã‹ã£ã‘ã„","ã“ã†ããã ã†ã‚“ã¼ãŸã‚“","ã‚„ã˜ã‚‹ã—","ã ã¶ã‚‹","ã—ãŸ"], + "âž¡ï¸": ["ã¿ãŽã‚€ãã‚„ã˜ã‚‹ã—","ã¿ãŽã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã—ゅよã†","ã»ã†ã“ã†","ã²ãŒã—"], + "⬅ï¸": ["ã²ã ã‚Šã‚€ãã‚„ã˜ã‚‹ã—","ã²ã ã‚Šã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã—ゅよã†","ã»ã†ã“ã†","ã«ã—"], + "⬆ï¸": ["ã†ã‚ã‚€ãã‚„ã˜ã‚‹ã—","ã†ãˆã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã—ゅよã†","ã»ã†ã“ã†","ããŸ"], + "⬇ï¸": ["ã—ãŸã‚€ãã‚„ã˜ã‚‹ã—","ã—ãŸã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã—ゅよã†","ã»ã†ã“ã†","ã—ãŸ","ã¿ãªã¿"], + "↗ï¸": ["ã¿ãŽã†ãˆã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã»ã†ã“ã†","ãªãªã‚","ã»ãã¨ã†"], + "↘ï¸": ["ã¿ãŽã—ãŸã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã»ã†ã“ã†","ãªãªã‚","ãªã‚“ã¨ã†"], + "↙ï¸": ["ã²ã ã‚Šã—ãŸã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã»ã†ã“ã†","ãªãªã‚","ãªã‚“ã›ã„"], + "↖ï¸": ["ã²ã ã‚Šã†ãˆã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã»ã†ã“ã†","ãªãªã‚","ã»ãã›ã„"], + "↕ï¸": ["ã˜ã‚‡ã†ã’ã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã»ã†ã“ã†","ãªãªã‚","ã»ãã›ã„"], + "↔ï¸": ["ã•ã‚†ã†ã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—"], + "🔄": ["ã†ãšã¾ãã‚„ã˜ã‚‹ã—","ã¯ã‚“ã¨ã‘ã„ã¾ã‚ã‚Š","ã‚„ã˜ã‚‹ã—","ã²ã ã‚Šã¾ã‚ã‚Š"], + "↪ï¸": ["ã¿ãŽã‚€ãã ã‚“ã¤ãã‚„ã˜ã‚‹ã—","ã¿ãŽã«ã¾ãŒã£ãŸã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—"], + "↩ï¸": ["ã²ã ã‚Šã‚€ãã ã‚“ã¤ãã‚„ã˜ã‚‹ã—","ã²ã ã‚Šã«ã¾ãŒã£ãŸã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—"], + "🔃": ["るーã·ã‚„ã˜ã‚‹ã—","ã¨ã‘ã„ã®ã¯ã‚Š","ã‚„ã˜ã‚‹ã—","ã¨ã‘ã„ã¾ã‚ã‚Š","ã‚Šã‚ーã©"], + "⤴ï¸": ["ã¿ãŽã†ãˆã¸ã‹ãƒ¼ã¶ã™ã‚‹ã‚„ã˜ã‚‹ã—","ã†ãˆã¸ã‹ãƒ¼ã¶ã™ã‚‹ã¿ãŽã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—"], + "⤵ï¸": ["ã¿ãŽã—ãŸã¸ã‹ãƒ¼ã¶ã™ã‚‹ã‚„ã˜ã‚‹ã—","ã—ãŸã«ã‹ãƒ¼ã¶ã™ã‚‹ã¿ãŽã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã—ãŸ"], + "#ï¸âƒ£": ["#ãー","ã¯ã£ã—ã‚…","ãー","ã½ã‚“ã©"], + "*⃣": ["ã‚ã™ãŸã‚Šã™ããー","ã‚ã™ãŸã‚Šã™ã","ãー","ã»ã—"], + "ℹï¸": ["ã˜ã‚‡ã†ã»ã†ã’ã‚“","i","ã„ã‚“ãµã‰ã‚ーã—ょん"], + "🔤": ["ã‚ã‚‹ãµãã¹ã£ã¨ã«ã‚…ã†ã‚Šã‚‡ã","abc","ã‚ã‚‹ãµãã¹ã£ã¨","ã«ã‚…ã†ã‚Šã‚‡ã","らã¦ã‚“","ã‚‚ã˜"], + "🔡": ["ã‚ã‚‹ãµãã¹ã£ã¨ã“ã‚‚ã˜ã«ã‚…ã†ã‚Šã‚‡ã","abcd","ã«ã‚…ã†ã‚Šã‚‡ã","らã¦ã‚“","ã‚‚ã˜","ã“ã‚‚ã˜"], + "🔠": ["ã‚ã‚‹ãµãã¹ã£ã¨ãŠãŠã‚‚ã˜ã«ã‚…ã†ã‚Šã‚‡ã","ã«ã‚…ã†ã‚Šã‚‡ã","らã¦ã‚“","ã‚‚ã˜","ãŠãŠã‚‚ã˜"], + "🔣": ["ãã”ã†ã«ã‚…ã†ã‚Šã‚‡ã","ã«ã‚…ã†ã‚Šã‚‡ã"], + "🎵": ["ãŠã‚“ã·","ã‚ãã¦ãƒã³ã¦ãƒ","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãŠã‚“ãŒã"], + "🎶": ["ãµãã™ã†ã®ãŠã‚“ã·","ã‚ãã¦ãƒã³ã¦ãƒ","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãŠã‚“ãŒã","ãŠã‚“ã·"], + "〰ï¸": ["ã¯ã›ã‚“","ã ã£ã—ã‚…","ãã”ã†","ãªã¿"], + "âž°": ["ã‹ãƒ¼ã‚‹ã˜ã‚‡ã†ã®ã‚‹ãƒ¼ã·","ã‹ãƒ¼ã‚‹","るーã·"], + "✔ï¸": ["ãµã¨ã˜ã®ã¡ã‡ã£ãã¾ãƒ¼ã","ã¡ã‡ã£ã","ã¾ãƒ¼ã"], + "âž•": ["ãµã¨ã˜ã®+ãã”ã†","ã™ã†ãŒã","ã·ã‚‰ã™"], + "âž–": ["ãµã¨ã˜ã®ã¾ã„ãªã™ãã”ã†","ã™ã†ãŒã","ã¾ã„ãªã™"], + "âž—": ["ãµã¨ã˜ã®ã‚ã‚‹ãã”ã†","ã‚ã‚Šã–ã‚“","ã™ã†ãŒã"], + "✖ï¸": ["ãµã¨ã˜ã®ã‹ã‘ã‚‹ã—ã‚‹ã—","ãゃんã›ã‚‹","ã˜ã‚‡ã†ã–ã‚“","ã‹ã‘ã‚‹","x"], + "🟰": ["ãµã¨ã„ã¨ã†ã”ã†","ã¨ã†ã—ã","ã™ã†ãŒã","ã²ã¨ã—ã„"], + "💲": ["ãµã¨ã˜ã®ã©ã‚‹ãã”ã†","ã¤ã†ã‹","ã©ã‚‹","ãŠã‹ã"], + "💱": ["ãŒã„ã‹ã‚Šã‚‡ã†ãŒãˆ","ãŽã‚“ã“ã†","ã¤ã†ã‹","りょã†ãŒãˆ","ãŠã‹ã"], + "©ï¸": ["ã“ã´ãƒ¼ã‚‰ã„ã¨ã¾ãƒ¼ã","ã¡ã‚‡ã•ãã‘ã‚“"], + "®ï¸": ["ã¨ã†ã‚ãã—ょã†ã²ã‚‡ã†ã¾ãƒ¼ã","ã¨ã†ã‚ããšã¿","ã—ょã†ã²ã‚‡ã†"], + "â„¢ï¸": ["ã—ょã†ã²ã‚‡ã†ã¾ãƒ¼ã","ã¾ãƒ¼ã","tm","ã—ょã†ã²ã‚‡ã†"], + "🔚": ["ENDã¨ã²ã ã‚Šã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã¯ã˜"], + "🔙": ["BACKã¨ã²ã ã‚Šã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã‚‚ã©ã‚‹"], + "🔛": ["ON!ã¨ã•ã‚†ã†ã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã¾ãƒ¼ã","ãŠã‚“"], + "ðŸ”": ["TOPã¨ã†ãˆã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã¨ã£ã·","ã†ãˆ"], + "🔜": ["SOONã¨ã¿ãŽã‚„ã˜ã‚‹ã—","ã‚„ã˜ã‚‹ã—","ã¾ã‚‚ãªã"], + "☑ï¸": ["ã¡ã‡ã£ãã„ã‚Šã¡ã‡ã£ãã¼ã£ãã™","ã¨ã†ã²ã‚‡ã†","ã¼ã£ãã™","ã¡ã‡ã£ã"], + "🔘": ["らã˜ãŠã¼ãŸã‚“","ã¼ãŸã‚“","ãã‹ãŒã","らã˜ãŠ"], + "🔴": ["ã‚ã‹ã¾ã‚‹","ãˆã‚“","ãã‹ãŒã","ã‚ã‹"], + "🟠": ["ãŠã‚Œã‚“ã˜ã„ã‚ã®ãˆã‚“","ãˆã‚“","ãã‹ãŒã","ãŠã‚Œã‚“ã˜"], + "🟡": ["ãã„ã‚ã®ã¾ã‚‹","ãˆã‚“","ãã‹ãŒã","ã¡ã‚ƒã„ã‚"], + "🟢": ["ã¿ã©ã‚Šã¾ã‚‹","ãˆã‚“","ãã‹ãŒã","ã¿ã©ã‚Š"], + "🔵": ["ã‚ãŠã¾ã‚‹","ã‚ãŠ","ãˆã‚“","ãã‹ãŒã"], + "🟣": ["むらã•ãã®ã¾ã‚‹","ãˆã‚“","ãã‹ãŒã","むらã•ã"], + "🟤": ["ã¡ã‚ƒã„ã‚ã®ã¾ã‚‹","ãˆã‚“","ãã‹ãŒã","ã¡ã‚ƒã„ã‚"], + "âš«": ["ãã‚ã¾ã‚‹","ãˆã‚“","ãã‹ãŒã"], + "⚪": ["ã—ã‚ã¾ã‚‹","ãˆã‚“","ãã‹ãŒã"], + "🟥": ["ã‚ã‹ã®ã›ã„ã»ã†ã‘ã„","ã›ã„ã»ã†ã‘ã„","ãã‹ãŒã","ã‚ã‹"], + "🟧": ["ãŠã‚Œã‚“ã˜ã—ょãã®ã›ã„ã»ã†ã‘ã„","ã›ã„ã»ã†ã‘ã„","ãã‹ãŒã","ãŠã‚Œã‚“ã˜"], + "🟨": ["ãã„ã‚ã®ã›ã„ã»ã†ã‘ã„","ã›ã„ã»ã†ã‘ã„","ãã‹ãŒã","ãã„ã‚"], + "🟩": ["ã¿ã©ã‚Šã®ã›ã„ã»ã†ã‘ã„","ã›ã„ã»ã†ã‘ã„","ãã‹ãŒã","ã¿ã©ã‚Š"], + "🟦": ["ã‚ãŠã®ã›ã„ã»ã†ã‘ã„","ã›ã„ã»ã†ã‘ã„","ãã‹ãŒã","ã‚ãŠ"], + "🟪": ["むらã•ãã®ã›ã„ã»ã†ã‘ã„","ã›ã„ã»ã†ã‘ã„","ãã‹ãŒã","むらã•ã"], + "🟫": ["ã¡ã‚ƒã„ã‚ã®ã›ã„ã»ã†ã‘ã„","ã›ã„ã»ã†ã‘ã„","ãã‹ãŒã","ã¡ã‚ƒã„ã‚"], + "⬛": ["ãã‚ã„ãŠãŠããªã—ã‹ã","ãã‹ãŒã","ã›ã„ã»ã†ã‘ã„"], + "⬜": ["ã—ã‚ã„ãŠãŠããªã—ã‹ã","ãã‹ãŒã","ã›ã„ã»ã†ã‘ã„"], + "â—¼ï¸": ["ãã‚ã„ã¡ã‚…ã†ãらã„ã®ã—ã‹ã","ãã‹ãŒã","ã›ã„ã»ã†ã‘ã„"], + "â—»ï¸": ["ã—ã‚ãã¦ã¡ã‚…ã†ãらã„ã®ã—ã‹ã","ãã‹ãŒã","ã›ã„ã»ã†ã‘ã„"], + "â—¾": ["ãã‚ãã¦ã¡ã‚…ã†ãらã„ã®ã¡ã„ã•ã„ã—ã‹ã","ãã‹ãŒã","ã›ã„ã»ã†ã‘ã„"], + "â—½": ["ã—ã‚ã„ã¡ã‚…ã†ãらã„ã®ã¡ã„ã•ãªã—ã‹ã","ãã‹ãŒã","ã›ã„ã»ã†ã‘ã„"], + "â–ªï¸": ["ãã‚ã„ã¡ã„ã•ãªã—ã‹ã","ãã‹ãŒã","ã›ã„ã»ã†ã‘ã„"], + "â–«ï¸": ["ã—ã‚ã„ã¡ã„ã•ãªã—ã‹ã","ãã‹ãŒã","ã›ã„ã»ã†ã‘ã„"], + "🔸": ["ã¡ã„ã•ã„ãŠã‚Œã‚“ã˜ã®ã ã„ã‚„ã‚‚ã‚“ã©","ã ã„ã‚„ã‚‚ã‚“ã©","ãã‹ãŒã","ãŠã‚Œã‚“ã˜"], + "🔹": ["ã¡ã„ã•ãã¦ã‚ãŠã„ã ã„ã‚„ã‚‚ã‚“ã©","ã‚ãŠ","ã ã„ã‚„ã‚‚ã‚“ã©","ãã‹ãŒã"], + "🔶": ["ãŠãŠãã„ãŠã‚Œã‚“ã˜ã®ã ã„ã‚„","ã ã„ã‚„ã‚‚ã‚“ã©","ãã‹ãŒã","ãŠã‚Œã‚“ã˜"], + "🔷": ["ãŠãŠããã¦ã‚ãŠã„ã ã„ã‚„ã‚‚ã‚“ã©","ã‚ãŠ","ã ã„ã‚„ã‚‚ã‚“ã©","ãã‹ãŒã"], + "🔺": ["ã†ã‚ã‚€ãã®ã‚ã‹ã„ã•ã‚“ã‹ã£ã‘ã„","ã†ãˆ","ãã‹ãŒã","ã‚ã‹"], + "🔻": ["ã—ãŸã‚€ãã®ã•ã‚“ã‹ã£ã‘ã„","ã ã†ã‚“","ãã‹ãŒã","ã‚ã‹"], + "🔲": ["ãã‚ã„ã—ã‹ãã¼ãŸã‚“","ã¼ãŸã‚“","ãã‹ãŒã","ã›ã„ã»ã†ã‘ã„"], + "🔳": ["ã—ã‚ã„ã—ã‹ãã¼ãŸã‚“","ã¼ãŸã‚“","ãã‹ãŒã","ã‹ã“ã¿","ã—ã‹ã"], + "🔈": ["ã™ã´ãƒ¼ã‹ãƒ¼","ãŠã‚“りょã†"], + "🔉": ["ãŠã‚“りょã†ã—ょã†","ã§ã‚“ã’ã‚“ãŒã¯ã„ã£ãŸã™ã´ãƒ¼ã‹ãƒ¼","ã²ãã„","ã™ã´ãƒ¼ã‹ãƒ¼","ãŠã‚“りょã†","ãªã¿"], + "🔊": ["ãŠã‚“りょã†ã ã„","ã ã„ãŠã‚“りょã†ã®ã™ã´ãƒ¼ã‹ãƒ¼","3","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ãŸã‹ã„","ãŠã¨ã®ãŠãŠãã„","ã™ã´ãƒ¼ã‹ãƒ¼","ã¼ã‚Šã‚…ーむ"], + "🔇": ["ã‚€ãŠã‚“ã®ã™ã´ãƒ¼ã‹ãƒ¼","ã™ã´ãƒ¼ã‹ãƒ¼","ãŠãµ","ã¿ã‚…ーã¨","ã›ã„ãŠã‚“","ã‚€ãŠã‚“","ãŠã‚“りょã†"], + "📣": ["ã‚ãŒã»ã‚“","ãŠã†ãˆã‚“","ã“ã¿ã‚…ã«ã‘ーã—ょん","ã‹ãã›ã„ã"], + "📢": ["ã‹ãã›ã„ã","ã“ã¿ã‚…ã«ã‘ーã—ょん","ãŠãŠã”ãˆ","ã™ã´ãƒ¼ã‹ãƒ¼","ã±ã¶ã‚Šã£ãã‚ã©ã‚Œã™","ã‚ãŒã»ã‚“"], + "🔔": ["ã¹ã‚‹"], + "🔕": ["ã¿ã‚…ーã¨","ã™ã‚‰ã£ã—ã‚…ã¹ã‚‹","ã‹ã","ãã‚“ã˜ã‚‰ã‚ŒãŸ","ã ã‚","ãªã„","ãã‚“ã—","ã—ãšã‹"], + "ðŸƒ": ["ã¨ã‚‰ã‚“ã·ã®ã˜ã‚‡ãƒ¼ã‹ãƒ¼","ã‹ãƒ¼ã©","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã’ーむ","ã˜ã‚‡ãƒ¼ã‹ãƒ¼","ã·ã‚Œã„"], + "🀄": ["ã¾ãƒ¼ã˜ã‚ƒã‚“ã±ã„ã®ã¡ã‚…ã†","ã’ーむ","ã¾ãƒ¼ã˜ã‚ƒã‚“","ã‚ã‹"], + "â™ ï¸": ["ã¨ã‚‰ã‚“ã·ã®ã™ãºãƒ¼ã©","ã‹ãƒ¼ã©","ã’ーむ","ã™ãºãƒ¼ã©","ã™ãƒ¼ã¤"], + "♣ï¸": ["ã¨ã‚‰ã‚“ã·ã®ãらã¶","ã‹ãƒ¼ã©","ãらã¶","ã’ーむ","ã™ãƒ¼ã¤"], + "♥ï¸": ["ã¨ã‚‰ã‚“ã·ã®ã¯ãƒ¼ã¨","ã‹ãƒ¼ã©","ã’ーむ","ã¯ãƒ¼ã¨","ã™ãƒ¼ã¤"], + "♦ï¸": ["ã¨ã‚‰ã‚“ã·ã®ã ã„ã‚„","ã‹ãƒ¼ã©","ã ã„ã‚„","ã ã„ã‚„ã‚‚ã‚“ã©","ã’ーむ","ã™ãƒ¼ã¤"], + "🎴": ["ã¯ãªãµã ","ã‚ãã¦ãƒã³ã¦ãƒ","ã‹ãƒ¼ã©","ãˆã‚“ãŸãƒ¼ã¦ã„ã‚ã‚“ã¨","ã¯ãª","ã’ーむ","ã«ã£ã½ã‚“","ã·ã‚Œã„"], + "ðŸ‘â€ðŸ—¨": ["ãµãã ã—ã®ã‚","ãµãã ã—","ã‚","ã™ã´ãƒ¼ã¡","ã—ょã†ã«ã‚“"], + "🗨": ["ã²ã ã‚Šã‚€ãã®ãµãã ã—","ã›ã‚Šãµ","ã™ã´ãƒ¼ã¡"], + "ðŸ’": ["ã‹ã‚“ãŒãˆãµãã ã—","ãµãã ã—","ã‚ã‚","ã¾ã‚“ãŒ","ã‹ã‚“ãŒãˆ"], + "🗯": ["ã¿ãŽã‚€ãã®ã„ã‹ã‚Šã®ãµãã ã—","ã„ã‹ã‚Š","ãµãã ã—","ã‚ã‚","ã’ãã©"], + "💬": ["ãµãã ã—","ã‚ã‚","ã¾ã‚“ãŒ","ã›ã‚Šãµ","ã™ã´ãƒ¼ã¡"], + "ðŸ•": ["1ã˜","0ãµã‚“","1","ã¨ã‘ã„","ã¨ã","ã„ã¡"], + "🕑": ["2ã˜","0ãµã‚“","2","ã¨ã‘ã„","ã¨ã","ã«"], + "🕒": ["3ã˜","0ãµã‚“","3","ã¨ã‘ã„","ã¨ã","ã•ã‚“"], + "🕓": ["4ã˜","0ãµã‚“","4","ã¨ã‘ã„","よん","ã¨ã"], + "🕔": ["5ã˜","0ãµã‚“","5","ã¨ã‘ã„","ã”","ã¨ã"], + "🕕": ["6ã˜","0ãµã‚“","6","ã¨ã‘ã„","ã¨ã","ã‚ã"], + "🕖": ["7ã˜","0ãµã‚“","7","ã¨ã‘ã„","ã¨ã","ãªãª"], + "🕗": ["8ã˜","0ãµã‚“","8","ã¨ã‘ã„","ã¯ã¡","ã¨ã"], + "🕘": ["9ã˜","0ãµã‚“","9","ã¨ã‘ã„","ãã‚…ã†","ã¨ã"], + "🕙": ["10ã˜","0ãµã‚“","10","ã¨ã‘ã„","ã¨ã","ã˜ã‚…ã†"], + "🕚": ["11ã˜","0ãµã‚“","11","ã¨ã‘ã„","ã˜ã‚…ã†ã„ã¡","ã¨ã"], + "🕛": ["12ã˜","0ãµã‚“","12","ã¨ã‘ã„","ã˜ã‚…ã†ã«","ã¨ã"], + "🕜": ["1ã˜ã¯ã‚“","1ã˜","ã¯ã‚“","ã˜ã“ã","ã„ã¡","30"], + "ðŸ•": ["2ã˜ã¯ã‚“","2ã˜","ã¯ã‚“","ã˜ã“ã","30","ã«"], + "🕞": ["3ã˜ã¯ã‚“","3ã˜","ã¯ã‚“","ã˜ã“ã","30","ã•ã‚“"], + "🕟": ["4ã˜ã¯ã‚“","30","4ã˜","ã˜ã“ã","よん","ã¯ã‚“"], + "🕠": ["5ã˜ã¯ã‚“","30","5ã˜","ã˜ã“ã","ã”","ã¯ã‚“"], + "🕡": ["6ã˜ã¯ã‚“","30","6ã˜","ã˜ã“ã","ã‚ã","ã¯ã‚“"], + "🕢": ["7ã˜ã¯ã‚“","30","7ã˜","ã˜ã“ã","ãªãª","ã¯ã‚“"], + "🕣": ["8ã˜ã¯ã‚“","30","8ã˜","ã˜ã“ã","ã¯ã¡","ã¯ã‚“"], + "🕤": ["9ã˜ã¯ã‚“","30","9ã˜","ã˜ã“ã","ãã‚…ã†","ã¯ã‚“"], + "🕥": ["10ã˜ã¯ã‚“","10ã˜","ã¯ã‚“","ã˜ã“ã","ã˜ã‚…ã†","30"], + "🕦": ["11ã˜ã¯ã‚“","11ã˜","ã¯ã‚“","ã˜ã“ã","ã˜ã‚…ã†ã„ã¡","30"], + "🕧": ["12ã˜ã¯ã‚“","12ã˜","ã¯ã‚“","ã˜ã“ã","30","ã˜ã‚…ã†ã«"], + "ðŸ³": ["ãªã³ãã—ã‚ã¯ãŸ","ã¯ãŸ","ãªã³ã"], + "ðŸ´": ["ãªã³ããã‚ã¯ãŸ","ã¯ãŸ","ãªã³ã"], + "ðŸ": ["ã¡ã‡ã£ã‹ãƒ¼ãµã‚‰ã£ã","ã„ã¡ã¾ã¤ã‚‚よã†","ã¯ãŸ","れーã™"], + "🚩": ["ã•ã‚“ã‹ãã¯ãŸ","ã¯ãŸ","ã½ã™ã¨"], + "🎌": ["ã“ã†ã•ã","ã‚ãã¦ãƒã³ã¦ãƒ","ãŠã„ã‚ã„","ã“ã†ã•","ã“ã†ã•ã—ãŸ","ã¯ãŸ","ã«ã£ã½ã‚“"], + "ðŸ´â€â˜ ï¸": ["ã‹ã„ãžãã¯ãŸ","ã¯ãŸ","ã‹ã„ãžã"], + "ðŸ³ï¸â€ðŸŒˆ": ["ã‚Œã„ã‚“ã¼ãƒ¼ãµã‚‰ã£ã","ãµã‚‰ã£ã","ã‚Œã„ã‚“ã¼ãƒ¼","ã·ã‚‰ã„ã©","lgbt"], + "ðŸ³ï¸â€âš§ï¸": ["ã¨ã‚‰ã™ã˜ã‡ã‚“ã ーãµã‚‰ã£ã","ãµã‚‰ã£ã","ã¨ã‚‰ã‚“ã™ã˜ã‡ã‚“ã ー","ã·ã‚‰ã„ã©","lgbt"], + "🇦🇨": ["ã‚ã›ã‚“ã—ょんã¨ã†ã®ã¯ãŸ","ã‚ã›ã‚“ã—ょん","ã“ã£ã","ã—ã¾"], + "🇦🇩": ["ã‚ã‚“ã©ã‚‰ã“ã£ã","ã‚ã‚“ã©ã‚‰","ã“ã£ã"], + "🇦🇪": ["ã‚らã¶ã—ã‚…ã¡ã‚‡ã†ã“ãれんã½ã†ã“ã£ã","ã—ã‚…ã¡ã‚‡ã†ã“ã","ã“ã£ã","ã‚らã¶ã—ã‚…ã¡ã‚‡ã†ã“ãれんã½ã†","れんã½ã†"], + "🇦🇫": ["ã‚ãµãŒã«ã™ãŸã‚“ã“ã£ã","ã‚ãµãŒã«ã™ãŸã‚“","ã“ã£ã"], + "🇦🇬": ["ã‚ã‚“ã¦ãƒãã‚ã°ãƒ¼ã¶ãƒ¼ã ã“ã£ã","ã‚ã‚“ã¦ãƒãã‚","ã°ãƒ¼ã¶ãƒ¼ã ","ã“ã£ã"], + "🇦🇮": ["ã‚ã‚“ãŽã‚‰ã¨ã†ã®ã¯ãŸ","ã‚ã‚“ãŽã‚‰ã¨ã†","ã“ã£ã"], + "🇦🇱": ["ã‚ã‚‹ã°ã«ã‚ã“ã£ã","ã‚ã‚‹ã°ã«ã‚","ã“ã£ã"], + "🇦🇲": ["ã‚ã‚‹ã‚ã«ã‚ã“ã£ã","ã‚ã‚‹ã‚ã«ã‚","ã“ã£ã"], + "🇦🇴": ["ã‚ã‚“ã”らã“ã£ã","ã‚ã‚“ã”ら","ã“ã£ã"], + "🇦🇶": ["ãªã‚“ãょããŸã„ã‚Šãã®ã¯ãŸ","ãªã‚“ãょããŸã„ã‚Šã","ã“ã£ã"], + "🇦🇷": ["ã‚ã‚‹ãœã‚“ã¡ã‚“ã“ã£ã","ã‚ã‚‹ãœã‚“ã¡ã‚“","ã“ã£ã"], + "🇦🇸": ["ã‚ã‚ã‚Šã‹ã‚Šã‚‡ã†ã•ã‚‚ã‚ã®ã¯ãŸ","ã‚ã‚ã‚Šã‹ã‚Šã‚‡ã†","ã“ã£ã","ã•ã‚‚ã‚"], + "🇦🇹": ["ãŠãƒ¼ã™ã¨ã‚Šã‚ã“ã£ã","ãŠãƒ¼ã™ã¨ã‚Šã‚","ã“ã£ã"], + "🇦🇺": ["ãŠãƒ¼ã™ã¨ã‚‰ã‚Šã‚ã“ã£ã","ãŠãƒ¼ã™ã¨ã‚‰ã‚Šã‚","ã“ã£ã","ã¯ãƒ¼ã©","ã¾ãã©ãªã‚‹ã©"], + "🇦🇼": ["ã‚ã‚‹ã°ã“ã£ã","ã‚ã‚‹ã°","ã“ã£ã"], + "🇦🇽": ["ãŠãƒ¼ã‚‰ã‚“ã©ã—ょã¨ã†ã®ã¯ãŸ","ãŠãƒ¼ã‚‰ã‚“ã©ã—ょã¨ã†","ã“ã£ã"], + "🇦🇿": ["ã‚ãœã‚‹ã°ã„ã˜ã‚ƒã‚“ã“ã£ã","ã‚ãœã‚‹ã°ã„ã˜ã‚ƒã‚“","ã“ã£ã"], + "🇧🇦": ["ã¼ã™ã«ã‚ã¸ã‚‹ã¤ã‡ã”ã³ãªã“ã£ã","ã¼ã™ã«ã‚","ã“ã£ã","ã¸ã‚‹ã¤ã‡ã”ã³ãª"], + "🇧🇧": ["ã°ã‚‹ã°ã©ã™ã“ã£ã","ã°ã‚‹ã°ã©ã™","ã“ã£ã"], + "🇧🇩": ["ã°ã‚“ãらã§ã—ã‚…ã“ã£ã","ã°ã‚“ãらã§ã—ã‚…","ã“ã£ã"], + "🇧🇪": ["ã¹ã‚‹ãŽãƒ¼ã“ã£ã","ã¹ã‚‹ãŽãƒ¼","ã“ã£ã"], + "🇧🇫": ["ã¶ã‚‹ããªãµããã“ã£ã","ã¶ã‚‹ããªãµãã","ã“ã£ã"], + "🇧🇬": ["ã¶ã‚‹ãŒã‚Šã‚ã“ã£ã","ã¶ã‚‹ãŒã‚Šã‚","ã“ã£ã"], + "🇧ðŸ‡": ["ã°ãƒ¼ã‚Œãƒ¼ã‚“ã“ã£ã","ã°ãƒ¼ã‚Œãƒ¼ã‚“","ã“ã£ã"], + "🇧🇮": ["ã¶ã‚‹ã‚“ã˜ã“ã£ã","ã¶ã‚‹ã‚“ã˜","ã“ã£ã"], + "🇧🇯": ["ã¹ãªã‚“ã“ã£ã","ã¹ãªã‚“","ã“ã£ã"], + "🇧🇱": ["ã•ã‚“・ã°ã‚‹ã¦ã‚‹ã¿ãƒ¼ã¨ã†ã®ã¯ãŸ","ã°ã‚‹ã¦ã‚‹ã¿ãƒ¼","ã“ã£ã","ã•ã‚“"], + "🇧🇲": ["ã°ã¿ã‚…ーã ã—ょã¨ã†ã®ã¯ãŸ","ã°ã¿ã‚…ーã ã—ょã¨ã†","ã“ã£ã"], + "🇧🇳": ["ã¶ã‚‹ãã„ã“ã£ã","ã¶ã‚‹ãã„","ã ã‚‹ã•ã‚‰ãƒ¼ã‚€","ã“ã£ã"], + "🇧🇴": ["ã¼ã‚Šã³ã‚ã“ã£ã","ã¼ã‚Šã³ã‚","ã“ã£ã"], + "🇧🇶": ["ã‹ã‚Šã¶ã‹ã„ã®ãŠã‚‰ã‚“ã りょã†ã¨ã†ã®ã¯ãŸ","ã¼ãーるã¨ã†","ã‹ã‚Šã¶ã‹ã„","ゆーã™ãŸã¦ãƒã†ã™","ã“ã£ã","ãŠã‚‰ã‚“ã ","ã•ã°","ã—ã‚“ã¨"], + "🇧🇷": ["ã¶ã‚‰ã˜ã‚‹ã“ã£ã","ã¶ã‚‰ã˜ã‚‹","ã“ã£ã"], + "🇧🇸": ["ã°ã¯ã¾ã“ã£ã","ã°ã¯ã¾","ã“ã£ã"], + "🇧🇹": ["ã¶ãƒ¼ãŸã‚“ã“ã£ã","ã¶ãƒ¼ãŸã‚“","ã“ã£ã"], + "🇧🇼": ["ã¼ã¤ã‚ãªã“ã£ã","ã¼ã¤ã‚ãª","ã“ã£ã"], + "🇧🇾": ["ã¹ã‚‰ã‚‹ãƒ¼ã—ã“ã£ã","ã¹ã‚‰ã‚‹ãƒ¼ã—","ã“ã£ã"], + "🇧🇿": ["ã¹ã‚Šãƒ¼ãšã“ã£ã","ã¹ã‚Šãƒ¼ãš","ã“ã£ã"], + "🇨🇦": ["ã‹ãªã ã“ã£ã","ã‹ãªã ","ã“ã£ã"], + "🇨🇨": ["ã“ã“ã™ã—ょã¨ã†ã®ã¯ãŸ","ã“ã“ã™","ã“ã£ã","ã—ょã¨ã†","ãーりんã"], + "🇨🇩": ["ã“ã‚“ã”ã“ã£ã - ãã‚“ã—ゃã•","ã“ã‚“ã”","ã“ã‚“ã” - ãã‚“ã—ゃã•","ã“ã‚“ã”ã¿ã‚“ã—ã‚…ãょã†ã‚ã“ã","ã“ã£ã","ãã‚“ã—ゃã•","ãょã†ã‚ã“ã"], + "🇨🇫": ["ã¡ã‚…ã†ãŠã†ã‚ãµã‚Šã‹ã“ã£ã","ã¡ã‚…ã†ãŠã†ã‚ãµã‚Šã‹ãょã†ã‚ã“ã","ã“ã£ã","ãょã†ã‚ã“ã"], + "🇨🇬": ["ã“ã‚“ã”ã®ã¯ãŸ - ã¶ã‚‰ã–ã³ã‚‹","ã¶ã‚‰ã–ã³ã‚‹","ã“ã‚“ã”","ã“ã‚“ã”ãょã†ã‚ã“ã","ã“ã‚“ã” - ã¶ã‚‰ã–ã³ã‚‹","ã“ã£ã","ãょã†ã‚ã“ã"], + "🇨ðŸ‡": ["ã™ã„ã™ã“ã£ã","ã“ã£ã","ã™ã„ã™"], + "🇨🇮": ["ã“ーã¨ã˜ã¼ã‚ーるã“ã£ã","ã“ーã¨ã˜ã¼ã‚ーる","ã“ã£ã"], + "🇨🇰": ["ãã£ãã—ょã¨ã†ã“ã£ã","ãã£ã","ã“ã£ã","ã—ょã¨ã†"], + "🇨🇱": ["ã¡ã‚Šã“ã£ã","ã¡ã‚Š","ã“ã£ã"], + "🇨🇲": ["ã‹ã‚るーんã“ã£ã","ã‹ã‚るーん","ã“ã£ã"], + "🇨🇳": ["ã¡ã‚…ã†ã”ãã“ã£ã","ã¡ã‚…ã†ã”ã","ã“ã£ã"], + "🇨🇴": ["ã“ã‚ã‚“ã³ã‚ã“ã£ã","ã“ã‚ã‚“ã³ã‚","ã“ã£ã"], + "🇨🇷": ["ã“ã™ãŸã‚Šã‹ã“ã£ã","ã“ã™ãŸã‚Šã‹","ã“ã£ã"], + "🇨🇺": ["ãゅーã°ã“ã£ã","ãゅーã°","ã“ã£ã"], + "🇨🇻": ["ã‹ãƒ¼ã¼ã¹ã‚‹ã§ã“ã£ã","ã‹ãƒ¼ã¼","ã‘ーã·","ã“ã£ã","ã¹ã‚‹ã§"], + "🇨🇼": ["ãゅらãーã¨ã†ã®ã¯ãŸ","ã‚ã‚“ã¦ãƒã‚‹ã—ょã¨ã†","ãゅらãー","ã“ã£ã"], + "🇨🇽": ["ãã‚Šã™ã¾ã™ã¨ã†ã®ã¯ãŸ","ãã‚Šã™ã¾ã™","ã“ã£ã","ã—ã¾"], + "🇨🇾": ["ãã·ã‚ã™ã“ã£ã","ãã·ã‚ã™","ã“ã£ã"], + "🇨🇿": ["ã¡ã‡ã“ã“ã£ã","ã¡ã‡ã“ãょã†ã‚ã“ã","ã“ã£ã"], + "🇩🇪": ["ã©ã„ã¤ã“ã£ã","ã“ã£ã","ã©ã„ã¤"], + "🇩🇯": ["ã˜ã¶ã¡ã“ã£ã","ã˜ã¶ã¡","ã“ã£ã"], + "🇩🇰": ["ã§ã‚“ã¾ãƒ¼ãã“ã£ã","ã§ã‚“ã¾ãƒ¼ã","ã“ã£ã"], + "🇩🇲": ["ã©ã¿ã«ã‹ã“ã£ã","ã©ã¿ã«ã‹","ã“ã£ã"], + "🇩🇴": ["ã©ã¿ã«ã‹ãょã†ã‚ã“ãã“ã£ã","ã©ã¿ã«ã‹ãょã†ã‚ã“ã","ã“ã£ã"], + "🇩🇿": ["ã‚ã‚‹ã˜ã‡ã‚Šã‚ã“ã£ã","ã‚ã‚‹ã˜ã‡ã‚Šã‚","ã“ã£ã"], + "🇪🇨": ["ãˆãã‚ã©ã‚‹ã“ã£ã","ãˆãã‚ã©ã‚‹","ã“ã£ã"], + "ðŸ´ó §ó ¢ó ¥ó ®ó §ó ¿": ["ã„ã‚“ãらんã©ã®ã¯ãŸ","ã„ã‚“ãらんã©","ã“ã£ã"], + "🇪🇪": ["ãˆã™ã¨ã«ã‚ã“ã£ã","ãˆã™ã¨ã«ã‚","ã“ã£ã"], + "🇪🇬": ["ãˆã˜ã·ã¨ã“ã£ã","ãˆã˜ã·ã¨","ã“ã£ã"], + "🇪ðŸ‡": ["ã«ã—ã•ã¯ã‚‰ã®ã¯ãŸ","ã“ã£ã","ã•ã¯ã‚‰","ã«ã—","ã«ã—ã•ã¯ã‚‰"], + "🇪🇷": ["ãˆã‚Šã¨ã‚Šã‚ã“ã£ã","ãˆã‚Šã¨ã‚Šã‚","ã“ã£ã"], + "🇪🇸": ["ã™ãºã„ã‚“ã“ã£ã","ã“ã£ã","ã™ãºã„ã‚“","ã›ã†ãŸ","ã‚りりゃ"], + "🇪🇹": ["ãˆã¡ãŠã´ã‚ã“ã£ã","ãˆã¡ãŠã´ã‚","ã“ã£ã"], + "🇪🇺": ["ãŠã†ã—ã‚…ã†ã¯ãŸ","ãŠã†ã—ã‚…ã†ã‚Œã‚“ã”ã†","ã“ã£ã"], + "🇫🇮": ["ãµãƒã‚“らんã©ã“ã£ã","ãµãƒã‚“らんã©","ã“ã£ã"], + "🇫🇯": ["ãµãƒã˜ãƒ¼ã“ã£ã","ãµãƒã˜ãƒ¼","ã“ã£ã"], + "🇫🇰": ["ãµã‰ãƒ¼ãらんã©ã—ょã¨ã†ã®ã¯ãŸ","ãµã‰ãƒ¼ãらんã©","ãµã‰ãƒ¼ãらんã©ã—ょã¨ã†","ã“ã£ã","ã—ょã¨ã†","ã¾ã‚‹ã³ãªã™"], + "🇫🇲": ["ã¿ãã‚ãã—ã‚ã“ã£ã","ã“ã£ã","ã¿ãã‚ãã—ã‚"], + "🇫🇴": ["ãµã‡ã‚ーã—ょã¨ã†ã®ã¯ãŸ","ãµã‡ã‚ー","ã¯ãŸ","ã—ょã¨ã†"], + "🇫🇷": ["ãµã‚‰ã‚“ã™ã“ã£ã","ã“ã£ã","ãµã‚‰ã‚“ã™","ãã‚Šã£ã±ãƒ¼ã¨ã‚“ã¨ã†","ã›ã‚“ã¨ãƒ»ã¾ãƒ¼ã¡ã‚“","ã•ã‚“・ã¾ã‚‹ãŸã‚“"], + "🇬🇦": ["ãŒã¼ã‚“ã“ã£ã","ã“ã£ã","ãŒã¼ã‚“"], + "🇬🇧": ["ã„ãŽã‚Šã™ã“ã£ã","ã„ãŽã‚Šã™","ã„ãŽã‚Šã™ã‚Šã‚‡ã†","ã“ーんã†ã‰ãƒ¼ã‚‹","ã„ã‚“ãらんã©","ã“ã£ã","ãれーã¨ã¶ã‚Šã¦ã‚“","ã‚ã„るらんã©","ããŸã‚ã„るらんã©","ã™ã“ã£ã¨ã‚‰ã‚“ã©","UK","ゆã«ãŠã‚“ã˜ã‚ƒã£ã","れんã”ã†","れんã”ã†ãŠã†ã“ã","ã†ã‡ãƒ¼ã‚‹ãš"], + "🇬🇩": ["ãã‚Œãªã ã“ã£ã","ã“ã£ã","ãã‚Œãªã "], + "🇬🇪": ["ã˜ã‚‡ãƒ¼ã˜ã‚ã“ã£ã","ã“ã£ã","ã˜ã‚‡ãƒ¼ã˜ã‚"], + "🇬🇫": ["ãµã‚‰ã‚“ã™ã‚Šã‚‡ã†ãŽã‚ãªã®ã¯ãŸ","ã“ã£ã","ãµã‚‰ã‚“ã™ã‚Šã‚‡ã†","ãŽã‚ãª"], + "🇬🇬": ["ãŒãƒ¼ã‚“ã˜ãƒ¼ã“ã£ã","ã“ã£ã","ãŒãƒ¼ã‚“ã˜ãƒ¼"], + "🇬ðŸ‡": ["ãŒãƒ¼ãªã“ã£ã","ã“ã£ã","ãŒãƒ¼ãª"], + "🇬🇮": ["ã˜ã¶ã‚‰ã‚‹ãŸã‚‹ã“ã£ã","ã“ã£ã","ã˜ã¶ã‚‰ã‚‹ãŸã‚‹"], + "🇬🇱": ["ãりーんらんã©ã“ã£ã","ã“ã£ã","ãりーんらんã©"], + "🇬🇲": ["ãŒã‚“ã³ã‚ã“ã£ã","ã“ã£ã","ãŒã‚“ã³ã‚"], + "🇬🇳": ["ãŽã«ã‚ã“ã£ã","ã“ã£ã","ãŽã«ã‚"], + "🇬🇵": ["ãã‚ã©ã‚‹ãƒ¼ã·ã“ã£ã","ã“ã£ã","ãã‚ã©ã‚‹ãƒ¼ã·"], + "🇬🇶": ["ã›ãã©ã†ãŽã«ã‚ã“ã£ã","ã›ãã©ã†ãŽã«ã‚","ã“ã£ã","ãŽã«ã‚"], + "🇬🇷": ["ãŽã‚Šã—ゃã“ã£ã","ã“ã£ã","ãŽã‚Šã—ゃ"], + "🇬🇸": ["ã•ã†ã™ã˜ã‚‡ãƒ¼ã˜ã‚・ã•ã†ã™ã•ã‚“ã©ã†ãƒã£ã¡ã—ょã¨ã†ã“ã£ã","ã“ã£ã","ã˜ã‚‡ãƒ¼ã˜ã‚","ã—ょã¨ã†","ã•ã†ã™","ã•ã†ã™ã˜ã‚‡ãƒ¼ã˜ã‚","ã•ã†ã™ã•ã‚“ã©ã†ãƒã£ã¡"], + "🇬🇹": ["ãã‚ã¦ã¾ã‚‰ã“ã£ã","ã“ã£ã","ãã‚ã¦ã¾ã‚‰"], + "🇬🇺": ["ãã‚ã‚€ã¯ãŸ","ã“ã£ã","ãã‚ã‚€"], + "🇬🇼": ["ãŽã«ã‚ã³ã•ã†ã“ã£ã","ã³ã•ã†","ã“ã£ã","ãŽã«ã‚"], + "🇬🇾": ["ãŒã„ã‚ãªã“ã£ã","ã“ã£ã","ãŒã„ã‚ãª"], + "ðŸ‡ðŸ‡°": ["ã»ã‚“ã“ã‚“ã®ã¯ãŸ","ã¡ã‚…ã†ã”ã","ã“ã£ã","ã»ã‚“ã“ã‚“"], + "ðŸ‡ðŸ‡³": ["ã»ã‚“ã˜ã‚…らã™ã“ã£ã","ã“ã£ã","ã»ã‚“ã˜ã‚…らã™"], + "ðŸ‡ðŸ‡·": ["ãã‚ã‚ã¡ã‚ã“ã£ã","ãã‚ã‚ã¡ã‚","ã“ã£ã"], + "ðŸ‡ðŸ‡¹": ["ã¯ã„ã¡ã“ã£ã","ã“ã£ã","ã¯ã„ã¡"], + "ðŸ‡ðŸ‡º": ["ã¯ã‚“ãŒã‚Šãƒ¼ã“ã£ã","ã“ã£ã","ã¯ã‚“ãŒã‚Šãƒ¼"], + "🇮🇨": ["ã‹ãªã‚Šã‚ã—ょã¨ã†ã®ã¯ãŸ","ã‹ãªã‚Šã‚","ã“ã£ã","ã—ょã¨ã†"], + "🇮🇩": ["ã„ã‚“ã©ãã—ã‚ã“ã£ã","ã“ã£ã","ã„ã‚“ã©ãã—ã‚"], + "🇮🇪": ["ã‚ã„るらんã©ã“ã£ã","ã“ã£ã","ã‚ã„るらんã©"], + "🇮🇱": ["ã„ã™ã‚‰ãˆã‚‹ã“ã£ã","ã“ã£ã","ã„ã™ã‚‰ãˆã‚‹"], + "🇮🇲": ["ã¾ã‚“ã¨ã†ã®ã¯ãŸ","ã“ã£ã","ã¾ã‚“ã¨ã†"], + "🇮🇳": ["ã„ã‚“ã©ã“ã£ã","ã“ã£ã","ã„ã‚“ã©"], + "🇮🇴": ["ã„ãŽã‚Šã™ã‚Šã‚‡ã†ã„ã‚“ã©ã‚ˆã†ã¡ã„ãã®ã¯ãŸ","ã„ãŽã‚Šã™ã‚Šã‚‡ã†","ã¡ã‚ƒã”ã™","ã¯ãŸ","ã„ã‚“ã©ã‚ˆã†","ã—ã¾","ã§ãƒãˆã”ãŒã‚‹ã—ã‚"], + "🇮🇶": ["ã„らãã“ã£ã","ã“ã£ã","ã„らã"], + "🇮🇷": ["ã„らんã“ã£ã","ã“ã£ã","ã„らん"], + "🇮🇸": ["ã‚ã„ã™ã‚‰ã‚“ã©ã“ã£ã","ã“ã£ã","ã‚ã„ã™ã‚‰ã‚“ã©"], + "🇮🇹": ["ã„ãŸã‚Šã‚ã“ã£ã","ã“ã£ã","ã„ãŸã‚Šã‚"], + "🇯🇪": ["ã˜ã‚ƒãƒ¼ã˜ãƒ¼ã ã„ã‹ã‚“ã‹ã‚“ã‹ã¤ãã®ã¯ãŸ","ã“ã£ã","ã˜ã‚ƒãƒ¼ã˜ãƒ¼ã ã„ã‹ã‚“ã‹ã‚“ã‹ã¤ã"], + "🇯🇲": ["ã˜ã‚ƒã¾ã„ã‹ã“ã£ã","ã“ã£ã","ã˜ã‚ƒã¾ã„ã‹"], + "🇯🇴": ["よるã ã‚“ã“ã£ã","ã“ã£ã","よるã ã‚“"], + "🇯🇵": ["ã«ã£ã½ã‚“ã“ã£ã","ã“ã£ã","ã«ã£ã½ã‚“"], + "🇰🇪": ["ã‘ã«ã‚ã“ã£ã","ã“ã£ã","ã‘ã«ã‚"], + "🇰🇬": ["ãã‚‹ãŽã™ã“ã£ã","ã“ã£ã","ãã‚‹ãŽã™"], + "🇰ðŸ‡": ["ã‹ã‚“ã¼ã˜ã‚ã“ã£ã","ã‹ã‚“ã¼ã˜ã‚","ã“ã£ã"], + "🇰🇮": ["ãã‚Šã°ã™ã“ã£ã","ã“ã£ã","ãã‚Šã°ã™"], + "🇰🇲": ["ã“ã‚‚ã‚ã“ã£ã","ã“ã‚‚ã‚","ã“ã£ã"], + "🇰🇳": ["ã›ã‚“ã¨ãã‚Šã™ã¨ãµãーãã„ã³ã™ã“ã£ã","ã“ã£ã","ãã£ã¤","ãã„ã³ã™","ã›ã‚“ã¨"], + "🇰🇵": ["ããŸã¡ã‚‡ã†ã›ã‚“ã“ã£ã","ã“ã£ã","ã¡ã‚‡ã†ã›ã‚“","ããŸ","ããŸã¡ã‚‡ã†ã›ã‚“"], + "🇰🇷": ["ã‹ã‚“ã“ãã“ã£ã","ã“ã£ã","ã‹ã‚“ã“ã","ã¿ãªã¿","ã ã„ã‹ã‚“ã¿ã‚“ã“ã"], + "🇰🇼": ["ãã†ã‡ãƒ¼ã¨ã“ã£ã","ã“ã£ã","ãã†ã‡ãƒ¼ã¨"], + "🇰🇾": ["ã‘ã„ã¾ã‚“ã—ょã¨ã†ã®ã¯ãŸ","ã‘ã„ã¾ã‚“","ã“ã£ã","ã—ょã¨ã†"], + "🇰🇿": ["ã‹ã–ãµã™ãŸã‚“ã“ã£ã","ã“ã£ã","ã‹ã–ãµã™ãŸã‚“"], + "🇱🇦": ["らãŠã™ã“ã£ã","ã“ã£ã","らãŠã™"], + "🇱🇧": ["ã‚Œã°ã®ã‚“ã“ã£ã","ã“ã£ã","ã‚Œã°ã®ã‚“"], + "🇱🇨": ["ã›ã‚“ã¨ã‚‹ã—ã‚ã“ã£ã","ã“ã£ã","ã›ã‚“ã¨ã‚‹ã—ã‚"], + "🇱🇮": ["ã‚Šã²ã¦ã‚“ã—ã‚…ãŸã„ã‚“ã“ã£ã","ã“ã£ã","ã‚Šã²ã¦ã‚“ã—ã‚…ãŸã„ã‚“"], + "🇱🇰": ["ã™ã‚Šã‚‰ã‚“ã‹ã“ã£ã","ã“ã£ã","ã™ã‚Šã‚‰ã‚“ã‹"], + "🇱🇷": ["ã‚Šã¹ã‚Šã‚ã“ã£ã","ã“ã£ã","ã‚Šã¹ã‚Šã‚"], + "🇱🇸": ["ã‚Œãã¨ã“ã£ã","ã“ã£ã","ã‚Œãã¨"], + "🇱🇹": ["ã‚Šã¨ã‚ã«ã‚ã“ã£ã","ã“ã£ã","ã‚Šã¨ã‚ã«ã‚"], + "🇱🇺": ["ã‚‹ãã›ã‚“ã¶ã‚‹ãã“ã£ã","ã“ã£ã","ã‚‹ãã›ã‚“ã¶ã‚‹ã"], + "🇱🇻": ["らã¨ã³ã‚ã“ã£ã","ã“ã£ã","らã¨ã³ã‚"], + "🇱🇾": ["ã‚Šã³ã‚ã“ã£ã","ã“ã£ã","ã‚Šã³ã‚"], + "🇲🇦": ["ã‚‚ã‚ã£ã“ã“ã£ã","ã“ã£ã","ã‚‚ã‚ã£ã“"], + "🇲🇨": ["ã‚‚ãªã“ã“ã£ã","ã“ã£ã","ã‚‚ãªã“"], + "🇲🇩": ["ã‚‚ã‚‹ã©ã°ã“ã£ã","ã“ã£ã","ã‚‚ã‚‹ã©ã°"], + "🇲🇪": ["ã‚‚ã‚“ã¦ããã‚ã“ã£ã","ã“ã£ã","ã‚‚ã‚“ã¦ããã‚"], + "🇲🇬": ["ã¾ã ãŒã™ã‹ã‚‹ã“ã£ã","ã“ã£ã","ã¾ã ãŒã™ã‹ã‚‹"], + "🇲ðŸ‡": ["ã¾ãƒ¼ã—ゃるã—ょã¨ã†ã“ã£ã","ã“ã£ã","ã—ょã¨ã†","ã¾ãƒ¼ã—ゃる"], + "🇲🇰": ["ã¾ã‘ã©ã«ã‚ã“ã£ã","ã“ã£ã","ã¾ã‘ã©ã«ã‚"], + "🇲🇱": ["ã¾ã‚Šã“ã£ã","ã“ã£ã","ã¾ã‚Š"], + "🇲🇲": ["ã¿ã‚ƒã‚“ã¾ãƒ¼ã“ã£ã","ã³ã‚‹ã¾","ã“ã£ã","ã¿ã‚ƒã‚“ã¾ãƒ¼"], + "🇲🇳": ["ã‚‚ã‚“ã”ã‚‹ã“ã£ã","ã“ã£ã","ã‚‚ã‚“ã”ã‚‹"], + "🇲🇴": ["ã¾ã‹ãŠã®ã¯ãŸ","ã¡ã‚…ã†ã”ã","ã“ã£ã","ã¾ã‹ãŠ"], + "🇲🇵": ["ããŸã¾ã‚Šã‚ãªã—ょã¨ã†ã®ã¯ãŸ","ã“ã£ã","ã—ょã¨ã†","ã¾ã‚Šã‚ãª","ããŸ","ããŸã¾ã‚Šã‚ãª"], + "🇲🇶": ["ã¾ã‚‹ã¦ãƒã«ãƒ¼ãã®ã¯ãŸ","ã¯ãŸ","ã¾ã‚‹ã¦ãƒã«ãƒ¼ã"], + "🇲🇷": ["もーりãŸã«ã‚ã“ã£ã","ã“ã£ã","もーりãŸã«ã‚"], + "🇲🇸": ["ã‚‚ã‚“ã¨ã›ã‚‰ã¨ã®ã¯ãŸ","ã¯ãŸ","ã‚‚ã‚“ã¨ã›ã‚‰ã¨"], + "🇲🇹": ["ã¾ã‚‹ãŸã“ã£ã","ã“ã£ã","ã¾ã‚‹ãŸ"], + "🇲🇺": ["もーりã—ゃã™ã“ã£ã","ã“ã£ã","もーりã—ゃã™"], + "🇲🇻": ["ã‚‚ã‚‹ã§ãƒã¶ã“ã£ã","ã“ã£ã","ã‚‚ã‚‹ã§ãƒã¶"], + "🇲🇼": ["ã¾ã‚‰ã†ã„ã“ã£ã","ã“ã£ã","ã¾ã‚‰ã†ã„"], + "🇲🇽": ["ã‚ãã—ã“ã“ã£ã","ã“ã£ã","ã‚ãã—ã“"], + "🇲🇾": ["ã¾ã‚Œãƒ¼ã—ã‚ã“ã£ã","ã“ã£ã","ã¾ã‚Œãƒ¼ã—ã‚"], + "🇲🇿": ["ã‚‚ã–ã‚“ã³ãƒ¼ãã“ã£ã","ã“ã£ã","ã‚‚ã–ã‚“ã³ãƒ¼ã"], + "🇳🇦": ["ãªã¿ã³ã‚ã“ã£ã","ã“ã£ã","ãªã¿ã³ã‚"], + "🇳🇨": ["ã«ã‚…ーã‹ã‚Œã©ã«ã‚ã®ã¯ãŸ","ã“ã£ã","ã«ã‚…ー","ã«ã‚…ーã‹ã‚Œã©ã«ã‚"], + "🇳🇪": ["ã«ã˜ã‡ãƒ¼ã‚‹ã“ã£ã","ã“ã£ã","ã«ã˜ã‡ãƒ¼ã‚‹"], + "🇳🇫": ["ã®ãƒ¼ãµã‰ãƒ¼ãã¨ã†ã®ã¯ãŸ","ã¯ãŸ","ã—ã¾","ã®ãƒ¼ãµã‰ãƒ¼ã"], + "🇳🇬": ["ãªã„ã˜ã‡ã‚Šã‚ã“ã£ã","ã“ã£ã","ãªã„ã˜ã‡ã‚Šã‚"], + "🇳🇮": ["ã«ã‹ã‚‰ãã‚ã“ã£ã","ã“ã£ã","ã«ã‹ã‚‰ãã‚"], + "🇳🇱": ["ãŠã‚‰ã‚“ã ã“ã£ã","ã“ã£ã","ãŠã‚‰ã‚“ã "], + "🇳🇴": ["ã®ã‚‹ã†ã‡ãƒ¼ã“ã£ã","ã¯ãŸ","ã®ã‚‹ã†ã‡ãƒ¼","ã¶ãƒ¼ã¹","ã™ãƒ´ãーるã°ã‚‹","ã‚„ã‚“ã¾ã„ãˆã‚“"], + "🇳🇵": ["ãã±ãƒ¼ã‚‹ã“ã£ã","ã“ã£ã","ãã±ãƒ¼ã‚‹"], + "🇳🇷": ["ãªã†ã‚‹ã“ã£ã","ã“ã£ã","ãªã†ã‚‹"], + "🇳🇺": ["ã«ã†ãˆã“ã£ã","ã“ã£ã","ã«ã†ãˆ"], + "🇳🇿": ["ã«ã‚…ーã˜ãƒ¼ã‚‰ã‚“ã©ã“ã£ã","ã“ã£ã","ã«ã‚…ー","ã«ã‚…ーã˜ãƒ¼ã‚‰ã‚“ã©"], + "🇴🇲": ["ãŠã¾ãƒ¼ã‚“ã“ã£ã","ã“ã£ã","ãŠã¾ãƒ¼ã‚“"], + "🇵🇦": ["ã±ãªã¾ã“ã£ã","ã“ã£ã","ã±ãªã¾"], + "🇵🇪": ["ãºã‚‹ãƒ¼ã“ã£ã","ã“ã£ã","ãºã‚‹ãƒ¼"], + "🇵🇫": ["ãµã‚‰ã‚“ã™ã‚Šã‚‡ã†ã½ã‚Šãã—ã‚ã®ã¯ãŸ","ã“ã£ã","ãµã‚‰ã‚“ã™ã‚Šã‚‡ã†","ã½ã‚Šãã—ã‚"], + "🇵🇬": ["ã±ã·ã‚ã«ã‚…ーãŽã«ã‚ã“ã£ã","ã“ã£ã","ãŽã«ã‚","ã«ã‚…ー","ã±ã·ã‚ã«ã‚…ーãŽã«ã‚"], + "🇵ðŸ‡": ["ãµãƒã‚Šã´ã‚“ã“ã£ã","ã“ã£ã","ãµãƒã‚Šã´ã‚“"], + "🇵🇰": ["ã±ãã™ãŸã‚“ã“ã£ã","ã“ã£ã","ã±ãã™ãŸã‚“"], + "🇵🇱": ["ã½ãƒ¼ã‚‰ã‚“ã©ã“ã£ã","ã“ã£ã","ã½ãƒ¼ã‚‰ã‚“ã©"], + "🇵🇲": ["ã•ã‚“ã´ãˆãƒ¼ã‚‹ã¨ã†ãƒ»ã¿ãã‚ã‚“ã¨ã†ã®ã¯ãŸ","ã¯ãŸ","ã¿ãã‚ã‚“","ã´ãˆãƒ¼ã‚‹","ã•ã‚“"], + "🇵🇳": ["ã´ã¨ã‘ã‚ã‚“ã—ょã¨ã†ã®ã¯ãŸ","ã¯ãŸ","ã—ょã¨ã†","ã´ã¨ã‘ã‚ã‚“"], + "🇵🇷": ["ã·ãˆã‚‹ã¨ã‚Šã“ã®ã¯ãŸ","ã“ã£ã","ã·ãˆã‚‹ã¨ã‚Šã“"], + "🇵🇸": ["ã±ã‚Œã™ã¡ãªã˜ã¡ã›ã„ãµã®ã¯ãŸ","ã“ã£ã","ã±ã‚Œã™ã¡ãª"], + "🇵🇹": ["ã½ã‚‹ã¨ãŒã‚‹ã“ã£ã","ã“ã£ã","ã½ã‚‹ã¨ãŒã‚‹"], + "🇵🇼": ["ã±ã‚‰ãŠã“ã£ã","ã“ã£ã","ã±ã‚‰ãŠ"], + "🇵🇾": ["ã±ã‚‰ãã‚ã„ã“ã£ã","ã“ã£ã","ã±ã‚‰ãã‚ã„"], + "🇶🇦": ["ã‹ãŸãƒ¼ã‚‹ã“ã£ã","ã“ã£ã","ã‹ãŸãƒ¼ã‚‹"], + "🇷🇪": ["れゆã«ãŠã‚“ã®ã¯ãŸ","ã¯ãŸ","れゆã«ãŠã‚“"], + "🇷🇴": ["るーã¾ã«ã‚ã“ã£ã","ã“ã£ã","るーã¾ã«ã‚"], + "🇷🇸": ["ã›ã‚‹ã³ã‚ã“ã£ã","ã“ã£ã","ã›ã‚‹ã³ã‚"], + "🇷🇺": ["ã‚ã—ã‚ã“ã£ã","ã“ã£ã","ã‚ã—ã‚"], + "🇷🇼": ["ã‚‹ã‚ã‚“ã ã“ã£ã","ã“ã£ã","ã‚‹ã‚ã‚“ã "], + "🇸🇦": ["ã•ã†ã˜ã‚らã³ã‚ã“ã£ã","ã“ã£ã","ã•ã†ã˜ã‚らã³ã‚"], + "ðŸ´ó §ó ¢ó ³ó £ó ´ó ¿": ["ã™ã“ã£ã¨ã‚‰ã‚“ã©ã®ã¯ãŸ","ã™ã“ã£ã¨ã‚‰ã‚“ã©","ã¯ãŸ"], + "🇸🇧": ["ãã‚ã‚‚ã‚“ã—ょã¨ã†ã“ã£ã","ã¯ãŸ","ã—ょã¨ã†","ãã‚ã‚‚ã‚“"], + "🇸🇨": ["ã›ãƒ¼ã—ã‡ã‚‹ã“ã£ã","ã“ã£ã","ã›ãƒ¼ã—ã‡ã‚‹"], + "🇸🇩": ["ã™ãƒ¼ã ã‚“ã“ã£ã","ã“ã£ã","ã™ãƒ¼ã ã‚“"], + "🇸🇪": ["ã™ã†ã‡ãƒ¼ã§ã‚“ã“ã£ã","ã“ã£ã","ã™ã†ã‡ãƒ¼ã§ã‚“"], + "🇸🇬": ["ã—ã‚“ãŒã½ãƒ¼ã‚‹ã“ã£ã","ã“ã£ã","ã—ã‚“ãŒã½ãƒ¼ã‚‹"], + "🇸ðŸ‡": ["ã›ã‚“ã¨ã¸ã‚Œãªã¨ã†ã®ã¯ãŸ","ã¯ãŸ","ã¸ã‚Œãª","ã›ã‚“ã¨"], + "🇸🇮": ["ã™ã‚ã¹ã«ã‚ã“ã£ã","ã“ã£ã","ã™ã‚ã¹ã«ã‚"], + "🇸🇰": ["ã™ã‚ã°ãã‚ã“ã£ã","ã“ã£ã","ã™ã‚ã°ãã‚"], + "🇸🇱": ["ã—ãˆã‚‰ã‚ŒãŠãã“ã£ã","ã“ã£ã","ã—ãˆã‚‰ã‚ŒãŠã"], + "🇸🇲": ["ã•ã‚“ã¾ã‚Šã®ã“ã£ã","ã“ã£ã","ã•ã‚“ã¾ã‚Šã®"], + "🇸🇳": ["ã›ããŒã‚‹ã“ã£ã","ã“ã£ã","ã›ããŒã‚‹"], + "🇸🇴": ["ãã¾ã‚Šã‚ã“ã£ã","ã“ã£ã","ãã¾ã‚Šã‚"], + "🇸🇷": ["ã™ã‚Šãªã‚€ã“ã£ã","ã“ã£ã","ã™ã‚Šãªã‚€"], + "🇸🇸": ["ã¿ãªã¿ã™ãƒ¼ã ã‚“ã“ã£ã","ã“ã£ã","ã¿ãªã¿","ã¿ãªã¿ã™ãƒ¼ã ã‚“","ã™ãƒ¼ã ã‚“"], + "🇸🇹": ["ã•ã‚“ã¨ã‚ã·ã‚Šã‚“ã—ãºã“ã£ã","ã“ã£ã","ã·ã‚Šã‚“ã—ãº","ã·ã‚Šã‚“ã—ã´","ã•ã‚“ã¨ã‚","ã•ã‰ã‚“ã¨ã‚ー"], + "🇸🇻": ["ãˆã‚‹ã•ã‚‹ã°ã©ã‚‹ã“ã£ã","ãˆã‚‹ã•ã‚‹ã°ã©ã‚‹","ã“ã£ã"], + "🇸🇽": ["ã›ã‚“ã¨ãƒ»ã¾ãƒ¼ã¡ã‚“ã¨ã†ã®ã¯ãŸ","ã¯ãŸ","ã¾ãƒ¼ã¡ã‚“","ã›ã‚“ã¨"], + "🇸🇾": ["ã—ã‚Šã‚ã“ã£ã","ã“ã£ã","ã—ã‚Šã‚"], + "🇸🇿": ["ã™ã‚ã˜ã‚‰ã‚“ã©ã“ã£ã","ã“ã£ã","ã™ã‚ã˜ã‚‰ã‚“ã©"], + "🇹🇦": ["ã¨ã‚Šã™ãŸã‚“ã ãーã«ã‚ƒã®ã¯ãŸ","ã¯ãŸ","ã¨ã‚Šã™ãŸã‚“・ã ・ãーã«ã‚ƒ"], + "🇹🇨": ["ãŸãƒ¼ãã™ãƒ»ã‹ã„ã“ã™ã—ょã¨ã†ã®ã¯ãŸ","ã‹ã„ã“ã™","ã¯ãŸ","ã—ょã¨ã†","ãŸãƒ¼ãã™"], + "🇹🇩": ["ã¡ã‚ƒã©ã“ã£ã","ã¡ã‚ƒã©","ã“ã£ã"], + "🇹🇫": ["ãµã‚‰ã‚“ã™ã‚Šã‚‡ã†ãªã‚“ã½ã†ãƒ»ãªã‚“ãょãã¡ã„ãã®ã¯ãŸ","ãªã‚“ãょã","ã“ã£ã","ãµã‚‰ã‚“ã™ã‚Šã‚‡ã†"], + "🇹🇬": ["ã¨ãƒ¼ã”ã“ã£ã","ã“ã£ã","ã¨ãƒ¼ã”"], + "🇹ðŸ‡": ["ãŸã„ã“ã£ã","ã“ã£ã","ãŸã„"], + "🇹🇯": ["ãŸã˜ãã™ãŸã‚“ã“ã£ã","ã“ã£ã","ãŸã˜ãã™ãŸã‚“"], + "🇹🇰": ["ã¨ã‘らã†ã¯ãŸ","ã“ã£ã","ã¨ã‘らã†"], + "🇹🇱": ["ã²ãŒã—ã¦ãƒã‚‚ーるã“ã£ã","ã²ãŒã—","ã²ãŒã—ã¦ãƒã‚‚ーる","ã“ã£ã","ã¦ãƒã‚‚ーる・れã™ã¦"], + "🇹🇲": ["ã¨ã‚‹ãã‚ã«ã™ãŸã‚“ã“ã£ã","ã“ã£ã","ã¨ã‚‹ãã‚ã«ã™ãŸã‚“"], + "🇹🇳": ["ã¡ã‚…ã«ã˜ã‚ã“ã£ã","ã“ã£ã","ã¡ã‚…ã«ã˜ã‚"], + "🇹🇴": ["ã¨ã‚“ãŒã“ã£ã","ã“ã£ã","ã¨ã‚“ãŒ"], + "🇹🇷": ["ã¨ã‚‹ã“ã“ã£ã","ã“ã£ã","ã¨ã‚‹ã“"], + "🇹🇹": ["ã¨ã‚Šã«ã ーã©ã¨ã°ã”ã“ã£ã","ã“ã£ã","ã¨ã°ã”","ã¨ã‚Šã«ã ーã©"], + "🇹🇻": ["ã¤ã°ã‚‹ã“ã£ã","ã“ã£ã","ã¤ã°ã‚‹"], + "🇹🇼": ["ãŸã„ã‚ã‚“ã®ã¯ãŸ","ã¡ã‚…ã†ã”ã","ã“ã£ã","ãŸã„ã‚ã‚“"], + "🇹🇿": ["ãŸã‚“ã–ã«ã‚ã“ã£ã","ã“ã£ã","ãŸã‚“ã–ã«ã‚"], + "🇺🇦": ["ã†ãらã„ãªã“ã£ã","ã“ã£ã","ã†ãらã„ãª"], + "🇺🇬": ["ã†ãŒã‚“ã ã“ã£ã","ã“ã£ã","ã†ãŒã‚“ã "], + "🇺🇳": ["ã“ãれんã®ã¯ãŸ","ã¯ãŸ","ã“ãれん","れんã”ã†","ã“ãã•ã„"], + "🇺🇸": ["ã‚ã‚ã‚Šã‹ã“ã£ã","ã‚ã‚ã‚Šã‹","ã¯ãŸ","ã”ã†ã—ã‚…ã†","ãŒã£ã—ã‚…ã†ã“ã","ã‚ã‚ã‚Šã‹ãŒã£ã—ã‚…ã†ã“ã","ãŒã£ã—ã‚…ã†ã“ãりょã†ã‚†ã†ã—ょã†ã‚Šã¨ã†"], + "🇺🇾": ["ã†ã‚‹ãã‚ã„ã“ã£ã","ã“ã£ã","ã†ã‚‹ãã‚ã„"], + "🇺🇿": ["ã†ãšã¹ãã™ãŸã‚“ã“ã£ã","ã“ã£ã","ã†ãšã¹ãã™ãŸã‚“"], + "🇻🇦": ["ã°ã¡ã‹ã‚“ã—ã“ã£ã","ã“ã£ã","ã°ã¡ã‹ã‚“"], + "🇻🇨": ["ã›ã‚“ã¨ã³ã‚“ã›ã‚“ã¨ãƒ»ãã‚Œãªã§ãƒãƒ¼ã‚“ã“ã£ã","ã“ã£ã","ãã‚Œãªã§ãƒãƒ¼ã‚“ã—ょã¨ã†","ã›ã‚“ã¨","ã³ã‚“ã›ã‚“ã¨"], + "🇻🇪": ["ã¹ããšãˆã‚‰ã“ã£ã","ã“ã£ã","ã¹ããšãˆã‚‰"], + "🇻🇬": ["ã„ãŽã‚Šã™ã‚Šã‚‡ã†ãƒ´ããーã˜ã‚“ã—ょã¨ã†ã®ã¯ãŸ","ã„ãŽã‚Šã™ã‚Šã‚‡ã†","ã“ã£ã","ã—ã¾","ヴãーã˜ã‚“"], + "🇻🇮": ["ã‚ã‚ã‚Šã‹ã‚Šã‚‡ã†ãƒ´ãーã˜ã‚“ã—ょã¨ã†ã®ã¯ãŸ","ã‚ã‚ã‚Šã‹","ã“ã£ã","ã—ã¾","ã‚ã‚ã‚Šã‹ãŒã£ã—ã‚…ã†ã“ã","ãŒã£ã—ã‚…ã†ã“ã","ヴãーã˜ã‚“"], + "🇻🇳": ["ã¹ã¨ãªã‚€ã“ã£ã","ã“ã£ã","ã¹ã¨ãªã‚€","ヴã‡ã¨ãªã‚€"], + "🇻🇺": ["ã°ã¬ã‚ã¤ã“ã£ã","ã“ã£ã","ã°ã¬ã‚ã¤"], + "ðŸ´ó §ó ¢ó ·ó ¬ó ³ó ¿": ["ã†ã‡ãƒ¼ã‚‹ãšã®ã¯ãŸ","ã†ã‡ãƒ¼ã‚‹ãš","ã¯ãŸ"], + "🇼🇫": ["ã†ã‰ã‚Šã™ãƒ»ãµã¤ãªã®ã¯ãŸ","ã“ã£ã","ãµã¤ãª","ã†ã‰ã‚Šã™"], + "🇼🇸": ["ã•ã‚‚ã‚ã“ã£ã","ã“ã£ã","ã•ã‚‚ã‚"], + "🇽🇰": ["ã“ãã¼ã“ã£ã","ã“ã£ã","ã“ãã¼"], + "🇾🇪": ["ã„ãˆã‚ã‚“ã“ã£ã","ã“ã£ã","ã„ãˆã‚ã‚“"], + "🇾🇹": ["ã¾ã‚ˆã£ã¨ã®ã¯ãŸ","ã“ã£ã","ã¾ã‚ˆã£ã¨"], + "🇿🇦": ["ã¿ãªã¿ã‚ãµã‚Šã‹ã“ã£ã","ã“ã£ã","ã¿ãªã¿","ã¿ãªã¿ã‚ãµã‚Šã‹"], + "🇿🇲": ["ã–ã‚“ã³ã‚ã“ã£ã","ã“ã£ã","ã–ã‚“ã³ã‚"], + "🇿🇼": ["ã˜ã‚“ã°ã¶ãˆã“ã£ã","ã“ã£ã","ã˜ã‚“ã°ã¶ãˆ"], + "": ["ã—ã¶ã‚„109", "SHIBUYA109", "109"] +} diff --git a/packages/frontend/src/widgets/WidgetActivity.calendar.vue b/packages/frontend/src/widgets/WidgetActivity.calendar.vue index bb5a2676dd9990d956e3f0ef18e9afdfff280430..58d231d9d4960274615273b387293d14fd3524fd 100644 --- a/packages/frontend/src/widgets/WidgetActivity.calendar.vue +++ b/packages/frontend/src/widgets/WidgetActivity.calendar.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/widgets/WidgetActivity.chart.vue b/packages/frontend/src/widgets/WidgetActivity.chart.vue index 0e87ec3ec361352583c5b61232307df9c090aa8e..41c6126c72b09903b89090a37b73105eeed1daf6 100644 --- a/packages/frontend/src/widgets/WidgetActivity.chart.vue +++ b/packages/frontend/src/widgets/WidgetActivity.chart.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/widgets/WidgetActivity.vue b/packages/frontend/src/widgets/WidgetActivity.vue index d2842143b1657d490b7022a5491641f8a1340834..9b65ca5e4ad6576e9432a2ded9d1f4c50ea1eb38 100644 --- a/packages/frontend/src/widgets/WidgetActivity.vue +++ b/packages/frontend/src/widgets/WidgetActivity.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -25,7 +25,7 @@ import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, Wid import XCalendar from './WidgetActivity.calendar.vue'; import XChart from './WidgetActivity.chart.vue'; import { GetFormResultType } from '@/scripts/form.js'; -import * as os from '@/os.js'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; import MkContainer from '@/components/MkContainer.vue'; import { $i } from '@/account.js'; import { i18n } from '@/i18n.js'; @@ -76,7 +76,7 @@ const toggleView = () => { save(); }; -os.apiGet('charts/user/notes', { +misskeyApiGet('charts/user/notes', { userId: $i.id, span: 'day', limit: 7 * 21, diff --git a/packages/frontend/src/widgets/WidgetAichan.vue b/packages/frontend/src/widgets/WidgetAichan.vue index fef026244c88009c2c365c5b8e410220f6629512..00001005de34366b996cf6565642bdcfe48bf2d4 100644 --- a/packages/frontend/src/widgets/WidgetAichan.vue +++ b/packages/frontend/src/widgets/WidgetAichan.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/widgets/WidgetAiscript.vue b/packages/frontend/src/widgets/WidgetAiscript.vue index c17e9728a58417cc9e2fb687a90907ee96b51c68..70fac9ae55329c0cbc0c446e61b43793df725cb5 100644 --- a/packages/frontend/src/widgets/WidgetAiscript.vue +++ b/packages/frontend/src/widgets/WidgetAiscript.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -25,7 +25,7 @@ import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, Wid import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; import MkContainer from '@/components/MkContainer.vue'; -import { createAiScriptEnv } from '@/scripts/aiscript/api.js'; +import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js'; import { $i } from '@/account.js'; import { i18n } from '@/i18n.js'; @@ -69,19 +69,7 @@ const run = async () => { storageKey: 'widget', token: $i?.token, }), { - in: (q) => { - return new Promise(ok => { - os.inputText({ - title: q, - }).then(({ canceled, result: a }) => { - if (canceled) { - ok(''); - } else { - ok(a); - } - }); - }); - }, + in: aiScriptReadline, out: (value) => { logs.value.push({ id: Math.random().toString(), diff --git a/packages/frontend/src/widgets/WidgetAiscriptApp.vue b/packages/frontend/src/widgets/WidgetAiscriptApp.vue index 10248a840ad0076d4f68bb6ad351de5e2e310b62..fa79e4aeb73a508681ce1962a8d7dad5af8efc61 100644 --- a/packages/frontend/src/widgets/WidgetAiscriptApp.vue +++ b/packages/frontend/src/widgets/WidgetAiscriptApp.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -18,7 +18,7 @@ import { Interpreter, Parser } from '@syuilo/aiscript'; import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; -import { createAiScriptEnv } from '@/scripts/aiscript/api.js'; +import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js'; import { $i } from '@/account.js'; import MkAsUi from '@/components/MkAsUi.vue'; import MkContainer from '@/components/MkContainer.vue'; @@ -64,19 +64,7 @@ async function run() { root.value = _root.value; }), }, { - in: (q) => { - return new Promise(ok => { - os.inputText({ - title: q, - }).then(({ canceled, result: a }) => { - if (canceled) { - ok(''); - } else { - ok(a); - } - }); - }); - }, + in: aiScriptReadline, out: (value) => { // nop }, diff --git a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue index 0a83eba9c143898813889a0d4fc17a812ecafe8f..36ba9f825502cb6301a47487eb78778c040de263 100644 --- a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue +++ b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -27,7 +27,7 @@ import * as Misskey from 'misskey-js'; import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { useInterval } from '@/scripts/use-interval.js'; import { i18n } from '@/i18n.js'; import { infoImageUrl } from '@/instance.js'; @@ -70,7 +70,7 @@ const fetch = () => { now.setHours(0, 0, 0, 0); if (now > lfAtD) { - os.api('users/following', { + misskeyApi('users/following', { limit: 18, birthday: now.toISOString(), userId: $i.id, diff --git a/packages/frontend/src/widgets/WidgetButton.vue b/packages/frontend/src/widgets/WidgetButton.vue index 11082c1e3fa9edd4de6a3dcaeabeaa49f7633b3b..6080e120ec4192bbc1565dd7e8df153e10079fa2 100644 --- a/packages/frontend/src/widgets/WidgetButton.vue +++ b/packages/frontend/src/widgets/WidgetButton.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -16,7 +16,7 @@ import { Interpreter, Parser } from '@syuilo/aiscript'; import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; -import { createAiScriptEnv } from '@/scripts/aiscript/api.js'; +import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js'; import { $i } from '@/account.js'; import MkButton from '@/components/MkButton.vue'; @@ -56,19 +56,7 @@ const run = async () => { storageKey: 'widget', token: $i?.token, }), { - in: (q) => { - return new Promise(ok => { - os.inputText({ - title: q, - }).then(({ canceled, result: a }) => { - if (canceled) { - ok(''); - } else { - ok(a); - } - }); - }); - }, + in: aiScriptReadline, out: (value) => { // nop }, diff --git a/packages/frontend/src/widgets/WidgetCalendar.vue b/packages/frontend/src/widgets/WidgetCalendar.vue index b3f814a0a72adc77147fd98829ef25b413c9b6d3..06b71311c4e95e30174a13dc3b55ce6e49abeaca 100644 --- a/packages/frontend/src/widgets/WidgetCalendar.vue +++ b/packages/frontend/src/widgets/WidgetCalendar.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -7,11 +7,11 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="[$style.root, { _panel: !widgetProps.transparent }]" data-cy-mkw-calendar> <div :class="[$style.calendar, { [$style.isHoliday]: isHoliday }]"> <p :class="$style.monthAndYear"> - <span :class="$style.year">{{ i18n.t('yearX', { year }) }}</span> - <span :class="$style.month">{{ i18n.t('monthX', { month }) }}</span> + <span :class="$style.year">{{ i18n.tsx.yearX({ year }) }}</span> + <span :class="$style.month">{{ i18n.tsx.monthX({ month }) }}</span> </p> - <p v-if="month === 1 && day === 1" class="day">🎉{{ i18n.t('dayX', { day }) }}<span style="display: inline-block; transform: scaleX(-1);">🎉</span></p> - <p v-else :class="$style.day">{{ i18n.t('dayX', { day }) }}</p> + <p v-if="month === 1 && day === 1" class="day">🎉{{ i18n.tsx.dayX({ day }) }}<span style="display: inline-block; transform: scaleX(-1);">🎉</span></p> + <p v-else :class="$style.day">{{ i18n.tsx.dayX({ day }) }}</p> <p :class="$style.weekDay">{{ weekDay }}</p> </div> <div :class="$style.info"> diff --git a/packages/frontend/src/widgets/WidgetClicker.vue b/packages/frontend/src/widgets/WidgetClicker.vue index aa492690178ddeffc78a114a46e67d5928367d16..9d231ae7154e1a87b013f423fc29b552d689cf5c 100644 --- a/packages/frontend/src/widgets/WidgetClicker.vue +++ b/packages/frontend/src/widgets/WidgetClicker.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/widgets/WidgetClock.vue b/packages/frontend/src/widgets/WidgetClock.vue index 22f053db59a5f688f97f5ef75bb3942139c6904a..b3128ef27e00ce1b44c109e7f923113b52be79db 100644 --- a/packages/frontend/src/widgets/WidgetClock.vue +++ b/packages/frontend/src/widgets/WidgetClock.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/widgets/WidgetDigitalClock.vue b/packages/frontend/src/widgets/WidgetDigitalClock.vue index a4b90c49d34b027535a420ee46b8337fe58a5db4..fa9a98d57183b9d090cc126150f98ab97ebe787c 100644 --- a/packages/frontend/src/widgets/WidgetDigitalClock.vue +++ b/packages/frontend/src/widgets/WidgetDigitalClock.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/widgets/WidgetFederation.vue b/packages/frontend/src/widgets/WidgetFederation.vue index 9be7d084e935878057b397513aaece746830e964..ae770f9816a0dd8382aa193107060e6804f7ba5f 100644 --- a/packages/frontend/src/widgets/WidgetFederation.vue +++ b/packages/frontend/src/widgets/WidgetFederation.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -31,7 +31,7 @@ import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, Wid import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkMiniChart from '@/components/MkMiniChart.vue'; -import * as os from '@/os.js'; +import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js'; import { useInterval } from '@/scripts/use-interval.js'; import { i18n } from '@/i18n.js'; import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; @@ -62,11 +62,11 @@ const charts = ref<Misskey.entities.ChartsInstanceResponse[]>([]); const fetching = ref(true); const fetch = async () => { - const fetchedInstances = await os.api('federation/instances', { + const fetchedInstances = await misskeyApi('federation/instances', { sort: '+latestRequestReceivedAt', limit: 5, }); - const fetchedCharts = await Promise.all(fetchedInstances.map(i => os.apiGet('charts/instance', { host: i.host, limit: 16, span: 'hour' }))); + const fetchedCharts = await Promise.all(fetchedInstances.map(i => misskeyApiGet('charts/instance', { host: i.host, limit: 16, span: 'hour' }))); instances.value = fetchedInstances; charts.value = fetchedCharts; fetching.value = false; diff --git a/packages/frontend/src/widgets/WidgetInstanceCloud.vue b/packages/frontend/src/widgets/WidgetInstanceCloud.vue index 38323ed04024e307190abd7bab87c4a3da6be271..76ccdb397119a7e5516763a49008fd34b6e825e2 100644 --- a/packages/frontend/src/widgets/WidgetInstanceCloud.vue +++ b/packages/frontend/src/widgets/WidgetInstanceCloud.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -25,6 +25,7 @@ import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkTagCloud from '@/components/MkTagCloud.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { useInterval } from '@/scripts/use-interval.js'; import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; @@ -56,7 +57,7 @@ function onInstanceClick(i) { } useInterval(() => { - os.api('federation/instances', { + misskeyApi('federation/instances', { sort: '+latestRequestReceivedAt', limit: 25, }).then(res => { diff --git a/packages/frontend/src/widgets/WidgetInstanceInfo.vue b/packages/frontend/src/widgets/WidgetInstanceInfo.vue index 2133deb363893d56fc46b02d8576b2df881133da..962521b25cc65e6bf43030a3bec5e2195b1feac0 100644 --- a/packages/frontend/src/widgets/WidgetInstanceInfo.vue +++ b/packages/frontend/src/widgets/WidgetInstanceInfo.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/widgets/WidgetJobQueue.vue b/packages/frontend/src/widgets/WidgetJobQueue.vue index c54682bb87492802dbe536b0828eb44a5aaca32e..b3e364a6d7720d968bb2c936dab64f79d53c4e8b 100644 --- a/packages/frontend/src/widgets/WidgetJobQueue.vue +++ b/packages/frontend/src/widgets/WidgetJobQueue.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -10,19 +10,19 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="values"> <div> <div>Process</div> - <div :class="{ inc: current.inbox.activeSincePrevTick > prev.inbox.activeSincePrevTick, dec: current.inbox.activeSincePrevTick < prev.inbox.activeSincePrevTick }">{{ number(current.inbox.activeSincePrevTick) }}</div> + <div :class="{ inc: current.inbox.activeSincePrevTick > prev.inbox.activeSincePrevTick, dec: current.inbox.activeSincePrevTick < prev.inbox.activeSincePrevTick }" :title="`${current.inbox.activeSincePrevTick}`">{{ kmg(current.inbox.activeSincePrevTick, 2) }}</div> </div> <div> <div>Active</div> - <div :class="{ inc: current.inbox.active > prev.inbox.active, dec: current.inbox.active < prev.inbox.active }">{{ number(current.inbox.active) }}</div> + <div :class="{ inc: current.inbox.active > prev.inbox.active, dec: current.inbox.active < prev.inbox.active }" :title="`${current.inbox.active}`">{{ kmg(current.inbox.active, 2) }}</div> </div> <div> <div>Delayed</div> - <div :class="{ inc: current.inbox.delayed > prev.inbox.delayed, dec: current.inbox.delayed < prev.inbox.delayed }">{{ number(current.inbox.delayed) }}</div> + <div :class="{ inc: current.inbox.delayed > prev.inbox.delayed, dec: current.inbox.delayed < prev.inbox.delayed }" :title="`${current.inbox.delayed}`">{{ kmg(current.inbox.delayed, 2) }}</div> </div> <div> <div>Waiting</div> - <div :class="{ inc: current.inbox.waiting > prev.inbox.waiting, dec: current.inbox.waiting < prev.inbox.waiting }">{{ number(current.inbox.waiting) }}</div> + <div :class="{ inc: current.inbox.waiting > prev.inbox.waiting, dec: current.inbox.waiting < prev.inbox.waiting }" :title="`${current.inbox.waiting}`">{{ kmg(current.inbox.waiting, 2) }}</div> </div> </div> </div> @@ -31,19 +31,19 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="values"> <div> <div>Process</div> - <div :class="{ inc: current.deliver.activeSincePrevTick > prev.deliver.activeSincePrevTick, dec: current.deliver.activeSincePrevTick < prev.deliver.activeSincePrevTick }">{{ number(current.deliver.activeSincePrevTick) }}</div> + <div :class="{ inc: current.deliver.activeSincePrevTick > prev.deliver.activeSincePrevTick, dec: current.deliver.activeSincePrevTick < prev.deliver.activeSincePrevTick }" :title="`${current.deliver.activeSincePrevTick}`">{{ kmg(current.deliver.activeSincePrevTick, 2) }}</div> </div> <div> <div>Active</div> - <div :class="{ inc: current.deliver.active > prev.deliver.active, dec: current.deliver.active < prev.deliver.active }">{{ number(current.deliver.active) }}</div> + <div :class="{ inc: current.deliver.active > prev.deliver.active, dec: current.deliver.active < prev.deliver.active }" :title="`${current.deliver.active}`">{{ kmg(current.deliver.active, 2) }}</div> </div> <div> <div>Delayed</div> - <div :class="{ inc: current.deliver.delayed > prev.deliver.delayed, dec: current.deliver.delayed < prev.deliver.delayed }">{{ number(current.deliver.delayed) }}</div> + <div :class="{ inc: current.deliver.delayed > prev.deliver.delayed, dec: current.deliver.delayed < prev.deliver.delayed }" :title="`${current.deliver.delayed}`">{{ kmg(current.deliver.delayed, 2) }}</div> </div> <div> <div>Waiting</div> - <div :class="{ inc: current.deliver.waiting > prev.deliver.waiting, dec: current.deliver.waiting < prev.deliver.waiting }">{{ number(current.deliver.waiting) }}</div> + <div :class="{ inc: current.deliver.waiting > prev.deliver.waiting, dec: current.deliver.waiting < prev.deliver.waiting }" :title="`${current.deliver.waiting}`">{{ kmg(current.deliver.waiting, 2) }}</div> </div> </div> </div> @@ -55,7 +55,7 @@ import { onUnmounted, reactive, ref } from 'vue'; import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import { useStream } from '@/stream.js'; -import number from '@/filters/number.js'; +import kmg from '@/filters/kmg.js'; import * as sound from '@/scripts/sound.js'; import { deepClone } from '@/scripts/clone.js'; import { defaultStore } from '@/store.js'; @@ -104,10 +104,7 @@ const jammedAudioBuffer = ref<AudioBuffer | null>(null); const jammedSoundNodePlaying = ref<boolean>(false); if (defaultStore.state.sound_masterVolume) { - sound.loadAudio({ - type: 'syuilo/queue-jammed', - volume: 1, - }).then(buf => { + sound.loadAudio('/client-assets/sounds/syuilo/queue-jammed.mp3').then(buf => { if (!buf) throw new Error('[WidgetJobQueue] Failed to initialize AudioBuffer'); jammedAudioBuffer.value = buf; }); @@ -126,7 +123,7 @@ const onStats = (stats) => { current[domain].delayed = stats[domain].delayed; if (current[domain].waiting > 0 && widgetProps.sound && jammedAudioBuffer.value && !jammedSoundNodePlaying.value) { - const soundNode = sound.createSourceNode(jammedAudioBuffer.value, 1); + const soundNode = sound.createSourceNode(jammedAudioBuffer.value, {}).soundSource; if (soundNode) { jammedSoundNodePlaying.value = true; soundNode.onended = () => jammedSoundNodePlaying.value = false; diff --git a/packages/frontend/src/widgets/WidgetMemo.vue b/packages/frontend/src/widgets/WidgetMemo.vue index 8e9e67ade5026b016ed52b24de95ec6c1a2a649a..d9efe546232227810822a3aab275eccc76abd377 100644 --- a/packages/frontend/src/widgets/WidgetMemo.vue +++ b/packages/frontend/src/widgets/WidgetMemo.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/widgets/WidgetNotifications.vue b/packages/frontend/src/widgets/WidgetNotifications.vue index e858741aa143c732816ee59012ed70744728c359..d590e7768e89e0df2fa34f0d192b79d7a864fbf2 100644 --- a/packages/frontend/src/widgets/WidgetNotifications.vue +++ b/packages/frontend/src/widgets/WidgetNotifications.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/widgets/WidgetOnlineUsers.vue b/packages/frontend/src/widgets/WidgetOnlineUsers.vue index 0a6fec7f2e52c45a6f1bf1a92b2a908a77d61364..5c89a06c62062dd03f92c41d4ce768d09b3a1049 100644 --- a/packages/frontend/src/widgets/WidgetOnlineUsers.vue +++ b/packages/frontend/src/widgets/WidgetOnlineUsers.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref } from 'vue'; import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; -import * as os from '@/os.js'; +import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js'; import { useInterval } from '@/scripts/use-interval.js'; import { i18n } from '@/i18n.js'; import number from '@/filters/number.js'; @@ -45,7 +45,7 @@ const { widgetProps, configure } = useWidgetPropsManager(name, const onlineUsersCount = ref(0); const tick = () => { - os.apiGet('get-online-users-count').then(res => { + misskeyApiGet('get-online-users-count').then(res => { onlineUsersCount.value = res.count; }); }; diff --git a/packages/frontend/src/widgets/WidgetPhotos.vue b/packages/frontend/src/widgets/WidgetPhotos.vue index ff9b6e19f502ee7699ca2cda010c7562cc7f9627..e578ebe2c51c6573a226f85051e09481c5480275 100644 --- a/packages/frontend/src/widgets/WidgetPhotos.vue +++ b/packages/frontend/src/widgets/WidgetPhotos.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -28,7 +28,7 @@ import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, Wid import { GetFormResultType } from '@/scripts/form.js'; import { useStream } from '@/stream.js'; import { getStaticImageUrl } from '@/scripts/media-proxy.js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import MkContainer from '@/components/MkContainer.vue'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; @@ -74,7 +74,7 @@ const thumbnail = (image: any): string => { : image.thumbnailUrl; }; -os.api('drive/stream', { +misskeyApi('drive/stream', { type: 'image/*', limit: 9, }).then(res => { diff --git a/packages/frontend/src/widgets/WidgetPostForm.vue b/packages/frontend/src/widgets/WidgetPostForm.vue index 9979ae256ec69476e8a22327cfb7bea0eff9d6ab..7f344505d84be2160d8e17e5709b376a3fc085d2 100644 --- a/packages/frontend/src/widgets/WidgetPostForm.vue +++ b/packages/frontend/src/widgets/WidgetPostForm.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/widgets/WidgetProfile.vue b/packages/frontend/src/widgets/WidgetProfile.vue index 3ff57bab860b1f90026166a6c964185666d6d98c..a5578d4de62a63fe882df9ae376592043225126c 100644 --- a/packages/frontend/src/widgets/WidgetProfile.vue +++ b/packages/frontend/src/widgets/WidgetProfile.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/widgets/WidgetRss.vue b/packages/frontend/src/widgets/WidgetRss.vue index a7185487313f80f4a6ca1efdebca0e52d0b03e18..e0272bc7d7f47e3d63ef46a21d66b501b0dc3b79 100644 --- a/packages/frontend/src/widgets/WidgetRss.vue +++ b/packages/frontend/src/widgets/WidgetRss.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/widgets/WidgetRssTicker.vue b/packages/frontend/src/widgets/WidgetRssTicker.vue index 607bb2f0abf2716c2ceb388f6623f30321a4463e..7456f9d35f30f7373b1c02ad333a61769d8edae5 100644 --- a/packages/frontend/src/widgets/WidgetRssTicker.vue +++ b/packages/frontend/src/widgets/WidgetRssTicker.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/widgets/WidgetSearch.vue b/packages/frontend/src/widgets/WidgetSearch.vue index 99991397766f2335f74aa00f300cbdcff1b28368..cf91a8f089035cf7dc1114fb4f5cc5b6ab72a865 100644 --- a/packages/frontend/src/widgets/WidgetSearch.vue +++ b/packages/frontend/src/widgets/WidgetSearch.vue @@ -20,8 +20,9 @@ import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, Wid import MkInput from '@/components/MkInput.vue'; import MkContainer from '@/components/MkContainer.vue'; import { i18n } from '@/i18n.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import * as os from '@/os.js'; -import { useRouter } from '@/router.js'; +import { useRouter } from '@/router/supplier.js'; import { GetFormResultType } from '@/scripts/form.js'; const name = 'search'; @@ -100,7 +101,7 @@ async function search() { if (query == null || query === '') return; if (query.startsWith('https://')) { - const promise = os.api('ap/show', { + const promise = misskeyApi('ap/show', { uri: query, }); diff --git a/packages/frontend/src/widgets/WidgetSlideshow.vue b/packages/frontend/src/widgets/WidgetSlideshow.vue index 7e39a0588134fe8388d0df29167d701db19ca4ad..b8efd3bda9fc7808ab0a5bfe339da3b34d444b22 100644 --- a/packages/frontend/src/widgets/WidgetSlideshow.vue +++ b/packages/frontend/src/widgets/WidgetSlideshow.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <p v-if="widgetProps.folderId == null"> {{ i18n.ts.folder }} </p> - <p v-if="widgetProps.folderId != null && images.length === 0 && !fetching">{{ i18n.t('no-image') }}</p> + <p v-if="widgetProps.folderId != null && images.length === 0 && !fetching">{{ i18n.ts['no-image'] }}</p> <div ref="slideA" class="slide a"></div> <div ref="slideB" class="slide b"></div> </div> @@ -22,6 +22,7 @@ import * as Misskey from 'misskey-js'; import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { useInterval } from '@/scripts/use-interval.js'; import { i18n } from '@/i18n.js'; @@ -77,7 +78,7 @@ const change = () => { const fetch = () => { fetching.value = true; - os.api('drive/files', { + misskeyApi('drive/files', { folderId: widgetProps.folderId, type: 'image/*', limit: 100, @@ -92,10 +93,10 @@ const fetch = () => { const choose = () => { os.selectDriveFolder(false).then(folder => { - if (folder == null) { + if (folder[0] == null) { return; } - widgetProps.folderId = folder.id; + widgetProps.folderId = folder[0].id; save(); fetch(); }); diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue index 070466f476956df7e58d13d93a57163ca8ad25cd..f6cf13290f192186df5ce698f676a307de35cf2e 100644 --- a/packages/frontend/src/widgets/WidgetTimeline.vue +++ b/packages/frontend/src/widgets/WidgetTimeline.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <template #header> <button class="_button" @click="choose"> - <span>{{ widgetProps.src === 'list' ? widgetProps.list.name : widgetProps.src === 'antenna' ? widgetProps.antenna.name : i18n.t('_timelines.' + widgetProps.src) }}</span> + <span>{{ widgetProps.src === 'list' ? widgetProps.list.name : widgetProps.src === 'antenna' ? widgetProps.antenna.name : i18n.ts._timelines[widgetProps.src] }}</span> <i :class="menuOpened ? 'ph-caret-up ph-bold ph-lg' : 'ph-caret-down ph-bold ph-lg'" style="margin-left: 8px;"></i> </button> </template> @@ -39,6 +39,7 @@ import { ref } from 'vue'; import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import MkContainer from '@/components/MkContainer.vue'; import MkTimeline from '@/components/MkTimeline.vue'; import { i18n } from '@/i18n.js'; @@ -97,8 +98,8 @@ const setSrc = (src) => { const choose = async (ev) => { menuOpened.value = true; const [antennas, lists] = await Promise.all([ - os.api('antennas/list'), - os.api('users/lists/list'), + misskeyApi('antennas/list'), + misskeyApi('users/lists/list'), ]); const antennaItems = antennas.map(antenna => ({ text: antenna.name, diff --git a/packages/frontend/src/widgets/WidgetTrends.vue b/packages/frontend/src/widgets/WidgetTrends.vue index 3416a1c0a7487f21569ba13f006ecf2fd71b2bd4..978a1a86f70562b1999a88eeff6f261588e8c2c4 100644 --- a/packages/frontend/src/widgets/WidgetTrends.vue +++ b/packages/frontend/src/widgets/WidgetTrends.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-for="stat in stats" :key="stat.tag"> <div class="tag"> <MkA class="a" :to="`/tags/${ encodeURIComponent(stat.tag) }`" :title="stat.tag">#{{ stat.tag }}</MkA> - <p>{{ i18n.t('nUsersMentioned', { n: stat.usersCount }) }}</p> + <p>{{ i18n.tsx.nUsersMentioned({ n: stat.usersCount }) }}</p> </div> <MkMiniChart class="chart" :src="stat.chart"/> </div> @@ -30,7 +30,7 @@ import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, Wid import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkMiniChart from '@/components/MkMiniChart.vue'; -import * as os from '@/os.js'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; import { useInterval } from '@/scripts/use-interval.js'; import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; @@ -59,7 +59,7 @@ const stats = ref<Misskey.entities.HashtagsTrendResponse>([]); const fetching = ref(true); const fetch = () => { - os.apiGet('hashtags/trend').then(res => { + misskeyApiGet('hashtags/trend').then(res => { stats.value = res; fetching.value = false; }); diff --git a/packages/frontend/src/widgets/WidgetUnixClock.vue b/packages/frontend/src/widgets/WidgetUnixClock.vue index 35f29b5e2147be57a74c86af3b4f3bd0742cf1e5..2ac7d1c7810dc1d907b11d93c279a6f745ecfdcf 100644 --- a/packages/frontend/src/widgets/WidgetUnixClock.vue +++ b/packages/frontend/src/widgets/WidgetUnixClock.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/widgets/WidgetUserList.vue b/packages/frontend/src/widgets/WidgetUserList.vue index c40328d2faa73634405cee40872702af7d226124..0e4fe2fbd38544132cb1896a914dbe16ab9f32fd 100644 --- a/packages/frontend/src/widgets/WidgetUserList.vue +++ b/packages/frontend/src/widgets/WidgetUserList.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -30,6 +30,7 @@ import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, Wid import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { useInterval } from '@/scripts/use-interval.js'; import { i18n } from '@/i18n.js'; import MkButton from '@/components/MkButton.vue'; @@ -64,7 +65,7 @@ const users = ref<Misskey.entities.UserDetailed[]>([]); const fetching = ref(true); async function chooseList() { - const lists = await os.api('users/lists/list'); + const lists = await misskeyApi('users/lists/list'); const { canceled, result: list } = await os.select({ title: i18n.ts.selectList, items: lists.map(x => ({ @@ -85,11 +86,11 @@ const fetch = () => { return; } - os.api('users/lists/show', { + misskeyApi('users/lists/show', { listId: widgetProps.listId, }).then(_list => { list.value = _list; - os.api('users/show', { + misskeyApi('users/show', { userIds: list.value.userIds, }).then(_users => { users.value = _users; diff --git a/packages/frontend/src/widgets/index.ts b/packages/frontend/src/widgets/index.ts index b783d783bc2a91c308214d067c0337833dfdc5aa..29e4558f1edf61ff3f567347accb54d7f88e571c 100644 --- a/packages/frontend/src/widgets/index.ts +++ b/packages/frontend/src/widgets/index.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/widgets/server-metric/cpu-mem.vue b/packages/frontend/src/widgets/server-metric/cpu-mem.vue index f13b6a370d724b6a01f1b33306437237f036640c..27d32342073526dd6c9769566ce4c30a9de13060 100644 --- a/packages/frontend/src/widgets/server-metric/cpu-mem.vue +++ b/packages/frontend/src/widgets/server-metric/cpu-mem.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -80,13 +80,13 @@ import * as Misskey from 'misskey-js'; import { v4 as uuid } from 'uuid'; const props = defineProps<{ - connection: any, + connection: Misskey.ChannelConnection<Misskey.Channels['serverStats']>, meta: Misskey.entities.ServerInfoResponse }>(); const viewBoxX = ref<number>(50); const viewBoxY = ref<number>(30); -const stats = ref<any[]>([]); +const stats = ref<Misskey.entities.ServerStats[]>([]); const cpuGradientId = uuid(); const cpuMaskId = uuid(); const memGradientId = uuid(); @@ -107,6 +107,7 @@ onMounted(() => { props.connection.on('statsLog', onStatsLog); props.connection.send('requestLog', { id: Math.random().toString().substring(2, 10), + length: 50, }); }); @@ -115,7 +116,7 @@ onBeforeUnmount(() => { props.connection.off('statsLog', onStatsLog); }); -function onStats(connStats) { +function onStats(connStats: Misskey.entities.ServerStats) { stats.value.push(connStats); if (stats.value.length > 50) stats.value.shift(); @@ -136,8 +137,8 @@ function onStats(connStats) { memP.value = (connStats.mem.active / props.meta.mem.total * 100).toFixed(0); } -function onStatsLog(statsLog) { - for (const revStats of [...statsLog].reverse()) { +function onStatsLog(statsLog: Misskey.entities.ServerStatsLog) { + for (const revStats of statsLog.reverse()) { onStats(revStats); } } diff --git a/packages/frontend/src/widgets/server-metric/cpu.vue b/packages/frontend/src/widgets/server-metric/cpu.vue index 35c20c893592d44b97dcc7ecb5bba4892aadf997..e00ef187f3a1023f8770ce8544b357f7000f65cf 100644 --- a/packages/frontend/src/widgets/server-metric/cpu.vue +++ b/packages/frontend/src/widgets/server-metric/cpu.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -20,13 +20,13 @@ import * as Misskey from 'misskey-js'; import XPie from './pie.vue'; const props = defineProps<{ - connection: any, + connection: Misskey.ChannelConnection<Misskey.Channels['serverStats']>, meta: Misskey.entities.ServerInfoResponse }>(); const usage = ref<number>(0); -function onStats(stats) { +function onStats(stats: Misskey.entities.ServerStats) { usage.value = stats.cpu; } diff --git a/packages/frontend/src/widgets/server-metric/disk.vue b/packages/frontend/src/widgets/server-metric/disk.vue index 0704854878549d505a94873162bbf421d837d248..e94a8b684844f72f379790b0b4f39990b6f68f8e 100644 --- a/packages/frontend/src/widgets/server-metric/disk.vue +++ b/packages/frontend/src/widgets/server-metric/disk.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/widgets/server-metric/index.vue b/packages/frontend/src/widgets/server-metric/index.vue index 9a785d9112ffc9279ad6afeaa67edf4090af5c80..1180a2a059fba4e61096b50bbe53332937a02ef2 100644 --- a/packages/frontend/src/widgets/server-metric/index.vue +++ b/packages/frontend/src/widgets/server-metric/index.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onUnmounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; -import { useWidgetPropsManager, Widget, WidgetComponentExpose } from '../widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from '../widget.js'; import XCpuMemory from './cpu-mem.vue'; import XNet from './net.vue'; import XCpu from './cpu.vue'; @@ -30,7 +30,7 @@ import XMemory from './mem.vue'; import XDisk from './disk.vue'; import MkContainer from '@/components/MkContainer.vue'; import { GetFormResultType } from '@/scripts/form.js'; -import * as os from '@/os.js'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; import { useStream } from '@/stream.js'; import { i18n } from '@/i18n.js'; @@ -54,11 +54,8 @@ const widgetPropsDef = { type WidgetProps = GetFormResultType<typeof widgetPropsDef>; -// ç¾æ™‚点ã§ã¯vueã®åˆ¶é™ã«ã‚ˆã‚Šimportã—ãŸtypeをジェãƒãƒªãƒƒã‚¯ã«æ¸¡ã›ãªã„ -//const props = defineProps<WidgetComponentProps<WidgetProps>>(); -//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>(); -const props = defineProps<{ widget?: Widget<WidgetProps>; }>(); -const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>(); +const props = defineProps<WidgetComponentProps<WidgetProps>>(); +const emit = defineEmits<WidgetComponentEmits<WidgetProps>>(); const { widgetProps, configure, save } = useWidgetPropsManager(name, widgetPropsDef, @@ -68,7 +65,7 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name, const meta = ref<Misskey.entities.ServerInfoResponse | null>(null); -os.apiGet('server-info', {}).then(res => { +misskeyApiGet('server-info', {}).then(res => { meta.value = res; }); diff --git a/packages/frontend/src/widgets/server-metric/mem.vue b/packages/frontend/src/widgets/server-metric/mem.vue index 34a1f1ae3db8a9e3d545973c4f50d25f0d20f34f..ba56d14211c64819430c512793d2a1191dcba36c 100644 --- a/packages/frontend/src/widgets/server-metric/mem.vue +++ b/packages/frontend/src/widgets/server-metric/mem.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -22,7 +22,7 @@ import XPie from './pie.vue'; import bytes from '@/filters/bytes.js'; const props = defineProps<{ - connection: any, + connection: Misskey.ChannelConnection<Misskey.Channels['serverStats']>, meta: Misskey.entities.ServerInfoResponse }>(); @@ -31,7 +31,7 @@ const total = ref<number>(0); const used = ref<number>(0); const free = ref<number>(0); -function onStats(stats) { +function onStats(stats: Misskey.entities.ServerStats) { usage.value = stats.mem.active / props.meta.mem.total; total.value = props.meta.mem.total; used.value = stats.mem.active; diff --git a/packages/frontend/src/widgets/server-metric/net.vue b/packages/frontend/src/widgets/server-metric/net.vue index 7af88a94eb6b806fe9fe309c886ed34f4c9d4c26..d46aaa5f69372aa6ec312c4da07cb45d0fd75409 100644 --- a/packages/frontend/src/widgets/server-metric/net.vue +++ b/packages/frontend/src/widgets/server-metric/net.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> @@ -54,13 +54,13 @@ import * as Misskey from 'misskey-js'; import bytes from '@/filters/bytes.js'; const props = defineProps<{ - connection: any, + connection: Misskey.ChannelConnection<Misskey.Channels['serverStats']>, meta: Misskey.entities.ServerInfoResponse }>(); const viewBoxX = ref<number>(50); const viewBoxY = ref<number>(30); -const stats = ref<any[]>([]); +const stats = ref<Misskey.entities.ServerStats[]>([]); const inPolylinePoints = ref<string>(''); const outPolylinePoints = ref<string>(''); const inPolygonPoints = ref<string>(''); @@ -77,6 +77,7 @@ onMounted(() => { props.connection.on('statsLog', onStatsLog); props.connection.send('requestLog', { id: Math.random().toString().substring(2, 10), + length: 50, }); }); @@ -85,7 +86,7 @@ onBeforeUnmount(() => { props.connection.off('statsLog', onStatsLog); }); -function onStats(connStats) { +function onStats(connStats: Misskey.entities.ServerStats) { stats.value.push(connStats); if (stats.value.length > 50) stats.value.shift(); @@ -109,8 +110,8 @@ function onStats(connStats) { outRecent.value = connStats.net.tx; } -function onStatsLog(statsLog) { - for (const revStats of [...statsLog].reverse()) { +function onStatsLog(statsLog: Misskey.entities.ServerStatsLog) { + for (const revStats of statsLog.reverse()) { onStats(revStats); } } diff --git a/packages/frontend/src/widgets/server-metric/pie.vue b/packages/frontend/src/widgets/server-metric/pie.vue index fd18a6a4f21ea31cb6c6f05aceafd0af952a94f9..400cbe9fa28c809068fa87e64146c865a48e2b22 100644 --- a/packages/frontend/src/widgets/server-metric/pie.vue +++ b/packages/frontend/src/widgets/server-metric/pie.vue @@ -1,5 +1,5 @@ <!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-FileCopyrightText: syuilo and misskey-project SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/widgets/widget.ts b/packages/frontend/src/widgets/widget.ts index 9c7632fc9b110242509217ebc4bfd59421c11488..bfe8067adfe59418384c2f9cc8fee63205837a52 100644 --- a/packages/frontend/src/widgets/widget.ts +++ b/packages/frontend/src/widgets/widget.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/workers/draw-blurhash.ts b/packages/frontend/src/workers/draw-blurhash.ts index b9190922233a4cdb523c5010d3ae1f54affa1e48..22de6cd3a8c5aaa227949ce755437ee70441cd6e 100644 --- a/packages/frontend/src/workers/draw-blurhash.ts +++ b/packages/frontend/src/workers/draw-blurhash.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/src/workers/test-webgl2.ts b/packages/frontend/src/workers/test-webgl2.ts index 8f57e5039bfc2e5babdad38f6ef2e3413f56e727..b203ebe666b8ab1718738c6cb5fb11b2e3d5985a 100644 --- a/packages/frontend/src/workers/test-webgl2.ts +++ b/packages/frontend/src/workers/test-webgl2.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/test/autocomplete.test.ts b/packages/frontend/test/autocomplete.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..394ac3a821687c5e9eacff80dd0d8146b2b899e4 --- /dev/null +++ b/packages/frontend/test/autocomplete.test.ts @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { assert, describe, test } from 'vitest'; +import { searchEmoji } from '@/scripts/search-emoji.js'; + +describe('emoji autocomplete', () => { + test('åå‰ã®å®Œå…¨ä¸€è‡´ã¯åå‰ã®å‰æ–¹ä¸€è‡´ã‚ˆã‚Šå„ªå…ˆã•ã‚Œã‚‹', async () => { + const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]); + assert.equal(result[0].emoji, ':foooo:'); + }); + + test('åå‰ã®å‰æ–¹ä¸€è‡´ã¯åå‰ã®éƒ¨åˆ†ä¸€è‡´ã‚ˆã‚Šå„ªå…ˆã•ã‚Œã‚‹', async () => { + const result = searchEmoji('baaa', [{ emoji: ':baaar:', name: 'baaar' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]); + assert.equal(result[0].emoji, ':baaar:'); + }); + + test('åå‰ã®å®Œå…¨ä¸€è‡´ã¯ã‚¿ã‚°ã®å®Œå…¨ä¸€è‡´ã‚ˆã‚Šå„ªå…ˆã•ã‚Œã‚‹', async () => { + const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]); + assert.equal(result[0].emoji, ':foooo:'); + }); + + test('åå‰ã®å‰æ–¹ä¸€è‡´ã¯ã‚¿ã‚°ã®å‰æ–¹ä¸€è‡´ã‚ˆã‚Šå„ªå…ˆã•ã‚Œã‚‹', async () => { + const result = searchEmoji('foo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]); + assert.equal(result[0].emoji, ':foooo:'); + }); + + test('åå‰ã®éƒ¨åˆ†ä¸€è‡´ã¯ã‚¿ã‚°ã®éƒ¨åˆ†ä¸€è‡´ã‚ˆã‚Šå„ªå…ˆã•ã‚Œã‚‹', async () => { + const result = searchEmoji('oooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]); + assert.equal(result[0].emoji, ':foooo:'); + }); +}); diff --git a/packages/frontend/test/emoji.test.ts b/packages/frontend/test/emoji.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..9a2989b37348794c5347efab6eb85d4cc23cb4f5 --- /dev/null +++ b/packages/frontend/test/emoji.test.ts @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { describe, test, assert, afterEach } from 'vitest'; +import { render, cleanup, type RenderResult } from '@testing-library/vue'; +import { defaultStoreState } from './init.js'; +import { getEmojiName } from '@/scripts/emojilist.js'; +import { components } from '@/components/index.js'; +import { directives } from '@/directives/index.js'; +import MkEmoji from '@/components/global/MkEmoji.vue'; + +describe('Emoji', () => { + const renderEmoji = (emoji: string): RenderResult => { + return render(MkEmoji, { + props: { emoji }, + global: { directives, components }, + }); + }; + + afterEach(() => { + cleanup(); + defaultStoreState.emojiStyle = ''; + }); + + describe('MkEmoji', () => { + test('Should render selector-less heart with color in native mode', async () => { + defaultStoreState.emojiStyle = 'native'; + const mkEmoji = await renderEmoji('\u2764'); // monochrome heart + assert.ok(mkEmoji.queryByText('\u2764\uFE0F')); // colored heart + assert.ok(!mkEmoji.queryByText('\u2764')); + }); + }); + + describe('Emoji list', () => { + test('Should get the name of the heart', () => { + assert.strictEqual(getEmojiName('\u2764'), 'heart'); + }); + }); +}); diff --git a/packages/frontend/test/home.test.ts b/packages/frontend/test/home.test.ts index 094ea071b927f1055fea6fdfcd14bd8e955cbb24..b3a4e8ff3a9be8a37ba36f174c48d019a12f94a0 100644 --- a/packages/frontend/test/home.test.ts +++ b/packages/frontend/test/home.test.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/test/init.ts b/packages/frontend/test/init.ts index 6d93ff8cb0012fc36da5d38da7e5bfee53f0ff99..0cde571dcbf15e0ec624a6997b8e0ef0edf4cd19 100644 --- a/packages/frontend/test/init.ts +++ b/packages/frontend/test/init.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -17,21 +17,23 @@ updateI18n(locales['en-US']); // XXX: misskey-js panics if WebSocket is not defined vi.stubGlobal('WebSocket', class WebSocket extends EventTarget { static CLOSING = 2; }); +export const defaultStoreState: Record<string, unknown> = { + + // ãªã‚“ã‹testãŒã†ã¾ã„ã“ã¨å‹•ã‹ãªã„ã®ã§ã“ã“ã«æ›¸ã + dataSaver: { + media: false, + avatar: false, + urlPreview: false, + code: false, + }, + +}; + // XXX: defaultStore somehow becomes undefined in vitest? vi.mock('@/store.js', () => { return { defaultStore: { - state: { - - // ãªã‚“ã‹testãŒã†ã¾ã„ã“ã¨å‹•ã‹ãªã„ã®ã§ã“ã“ã«æ›¸ã - dataSaver: { - media: false, - avatar: false, - urlPreview: false, - code: false, - }, - - }, + state: defaultStoreState, }, }; }); diff --git a/packages/frontend/test/note.test.ts b/packages/frontend/test/note.test.ts index 8ccc05ff3e745bb6ce725d5a9a849e3511632c90..7ce5f23e22d434da1e20985a019d08774fdd2405 100644 --- a/packages/frontend/test/note.test.ts +++ b/packages/frontend/test/note.test.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/frontend/test/nyaize.test.ts b/packages/frontend/test/nyaize.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..d4f0fff1db49e0c28161efd2130795d9cfd1ee83 --- /dev/null +++ b/packages/frontend/test/nyaize.test.ts @@ -0,0 +1,32 @@ +import { describe, test, assert, afterEach } from 'vitest'; +import { nyaize } from '@/scripts/nyaize.js'; + +function runTests(cases) { + for (const c of cases) { + const [input,expected] = c; + const got = nyaize(input); + assert.strictEqual(got, expected); + } +} + +describe('nyaize', () => { + test('ja-JP', () => { + runTests([ + ['ãã‚Œã„ãª','ãã‚Œã„ã«ã‚ƒ'], + ['ナナナ', 'ニャニャニャ'], + ['ï¾…ï¾…','ニャニャ'], + ]); + }); + test('en-US', () => { + runTests([ + ['bar','bar'], + ['banana','banyanya'], + ['booting','booting'], + ['morning','mornyan'], + ['mmmorning','mmmornyan'], + ['someone','someone'], + ['everyone','everynyan'], + ['foreveryone','foreverynyan'], + ]); + }); +}); diff --git a/packages/frontend/test/scroll.test.ts b/packages/frontend/test/scroll.test.ts index 2334268d43f494a3e5b01e29884d5781ff674f65..a0b56b7221bbadb5cd63b7af51e5bbf425072ad2 100644 --- a/packages/frontend/test/scroll.test.ts +++ b/packages/frontend/test/scroll.test.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -9,6 +9,7 @@ import { onScrollBottom, onScrollTop } from '@/scripts/scroll.js'; describe('Scroll', () => { describe('onScrollTop', () => { + /* 動作ã—ãªã„(happy-domã®ãƒã‚°ï¼Ÿ) test('Initial onScrollTop callback for connected elements', () => { const { document } = new Window(); const div = document.createElement('div'); @@ -21,6 +22,7 @@ describe('Scroll', () => { assert.ok(called); }); + */ test('No onScrollTop callback for disconnected elements', () => { const { document } = new Window(); @@ -35,11 +37,11 @@ describe('Scroll', () => { }); describe('onScrollBottom', () => { + /* 動作ã—ãªã„(happy-domã®ãƒã‚°ï¼Ÿ) test('Initial onScrollBottom callback for connected elements', () => { const { document } = new Window(); const div = document.createElement('div'); assert.strictEqual(div.scrollTop, 0); - (div as any).scrollHeight = 100; // happy-dom has no scrollHeight document.body.append(div); @@ -48,12 +50,12 @@ describe('Scroll', () => { assert.ok(called); }); + */ test('No onScrollBottom callback for disconnected elements', () => { const { document } = new Window(); const div = document.createElement('div'); assert.strictEqual(div.scrollTop, 0); - (div as any).scrollHeight = 100; // happy-dom has no scrollHeight let called = false; onScrollBottom(div as any as HTMLElement, () => called = true); diff --git a/packages/frontend/test/url-preview.test.ts b/packages/frontend/test/url-preview.test.ts index f760de927453140a11bf741271c80bedd438916a..4b79d33348c66583bdb5233d234c7569f9045a91 100644 --- a/packages/frontend/test/url-preview.test.ts +++ b/packages/frontend/test/url-preview.test.ts @@ -1,12 +1,12 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { describe, test, assert, afterEach } from 'vitest'; import { render, cleanup, type RenderResult } from '@testing-library/vue'; import './init'; -import type { summaly } from 'summaly'; +import type { summaly } from '@misskey-dev/summaly'; import { components } from '@/components/index.js'; import { directives } from '@/directives/index.js'; import MkUrlPreview from '@/components/MkUrlPreview.vue'; @@ -116,6 +116,34 @@ describe('MkUrlPreview', () => { assert.strictEqual(iframe?.allow, 'fullscreen;web-share'); }); + test('A Summaly proxy response without allow falls back to the default', async () => { + const iframe = await renderAndOpenPreview({ + url: 'https://example.local', + player: { + url: 'https://example.local/player', + width: null, + height: null, + allow: undefined as any, + }, + }); + assert.exists(iframe, 'iframe should exist'); + assert.strictEqual(iframe?.allow, 'autoplay;encrypted-media;fullscreen'); + }); + + test('Filtering the allow list from the Summaly proxy', async () => { + const iframe = await renderAndOpenPreview({ + url: 'https://example.local', + player: { + url: 'https://example.local/player', + width: null, + height: null, + allow: ['autoplay', 'camera', 'fullscreen'], + }, + }); + assert.exists(iframe, 'iframe should exist'); + assert.strictEqual(iframe?.allow, 'autoplay;fullscreen'); + }); + test('Having a player width should keep the fixed aspect ratio', async () => { const iframe = await renderAndOpenPreview({ url: 'https://example.local', diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json index 5d451c878c00749486f09a3c818d3bce2008c44b..819629a9cf8aafe01b2473a2b7a5c2cb72377141 100644 --- a/packages/frontend/tsconfig.json +++ b/packages/frontend/tsconfig.json @@ -33,6 +33,7 @@ ], "types": [ "vite/client", + "vitest/importMeta", ], "lib": [ "esnext", diff --git a/packages/frontend/vite.config.local-dev.ts b/packages/frontend/vite.config.local-dev.ts index 5a6f511c66080a24b33a9435862e14e0f1e3fe31..4c19dfbc669168ca541bf2183ea4c0c097799676 100644 --- a/packages/frontend/vite.config.local-dev.ts +++ b/packages/frontend/vite.config.local-dev.ts @@ -1,5 +1,7 @@ import dns from 'dns'; +import { readFile } from 'node:fs/promises'; import { defineConfig } from 'vite'; +import * as yaml from 'js-yaml'; import locales from '../../locales/index.js'; import { getConfig } from './vite.config.js'; @@ -7,6 +9,11 @@ dns.setDefaultResultOrder('ipv4first'); const defaultConfig = getConfig(); +const { port } = yaml.load(await readFile('../../.config/default.yml', 'utf-8')); + +const httpUrl = `http://localhost:${port}/`; +const websocketUrl = `ws://localhost:${port}/`; + const devConfig = { // 基本ã®è¨å®šã¯ vite.config.js ã‹ã‚‰å¼•ã継ã ...defaultConfig, @@ -19,28 +26,29 @@ const devConfig = { proxy: { '/api': { changeOrigin: true, - target: 'http://localhost:3000/', + target: httpUrl, }, - '/assets': 'http://localhost:3000/', - '/static-assets': 'http://localhost:3000/', - '/client-assets': 'http://localhost:3000/', - '/files': 'http://localhost:3000/', - '/twemoji': 'http://localhost:3000/', - '/fluent-emoji': 'http://localhost:3000/', - '/sw.js': 'http://localhost:3000/', + '/assets': httpUrl, + '/static-assets': httpUrl, + '/client-assets': httpUrl, + '/files': httpUrl, + '/twemoji': httpUrl, + '/fluent-emoji': httpUrl, + '/tossface': httpUrl, + '/sw.js': httpUrl, '/streaming': { - target: 'ws://localhost:3000/', + target: websocketUrl, ws: true, }, - '/favicon.ico': 'http://localhost:3000/', + '/favicon.ico': httpUrl, '/identicon': { - target: 'http://localhost:3000/', + target: httpUrl, rewrite(path) { return path.replace('@localhost:5173', ''); }, }, - '/url': 'http://localhost:3000', - '/proxy': 'http://localhost:3000', + '/url': httpUrl, + '/proxy': httpUrl, }, }, build: { diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index ea8c4ced6082c198e60063c8576d0f0c9f5a8125..657f6002c6fd4f746e0bfee3b765fe0d95843be7 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -71,6 +71,7 @@ export function getConfig(): UserConfig { '/client-assets/': __dirname + '/assets/', '/static-assets/': __dirname + '/../backend/assets/', '/fluent-emojis/': __dirname + '/../../fluent-emojis/dist/', + '/tossface/': __dirname + '/../../tossface-emojis/dist/', '/fluent-emoji/': __dirname + '/../../fluent-emojis/dist/', }, }, @@ -98,11 +99,6 @@ export function getConfig(): UserConfig { __VUE_PROD_DEVTOOLS__: false, }, - // https://vitejs.dev/guide/dep-pre-bundling.html#monorepos-and-linked-dependencies - optimizeDeps: { - include: ['misskey-js'], - }, - build: { target: [ 'chrome116', @@ -132,7 +128,7 @@ export function getConfig(): UserConfig { // https://vitejs.dev/guide/dep-pre-bundling.html#monorepos-and-linked-dependencies commonjsOptions: { - include: [/misskey-js/, /node_modules/], + include: [/misskey-js/, /misskey-reversi/, /misskey-bubble-game/, /node_modules/], }, }, @@ -152,6 +148,7 @@ export function getConfig(): UserConfig { }, }, }, + includeSource: ['src/**/*.ts'], }, }; } diff --git a/packages/misskey-bubble-game/.eslintignore b/packages/misskey-bubble-game/.eslintignore new file mode 100644 index 0000000000000000000000000000000000000000..f22128f047fd7d41ba245801483111d0e5862b76 --- /dev/null +++ b/packages/misskey-bubble-game/.eslintignore @@ -0,0 +1,7 @@ +node_modules +/built +/coverage +/.eslintrc.js +/jest.config.ts +/test +/test-d diff --git a/packages/misskey-bubble-game/.eslintrc.cjs b/packages/misskey-bubble-game/.eslintrc.cjs new file mode 100644 index 0000000000000000000000000000000000000000..e2e31e9e331e0b135f991e53df66781ab4fbd15e --- /dev/null +++ b/packages/misskey-bubble-game/.eslintrc.cjs @@ -0,0 +1,9 @@ +module.exports = { + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + }, + extends: [ + '../shared/.eslintrc.js', + ], +}; diff --git a/packages/misskey-bubble-game/build.js b/packages/misskey-bubble-game/build.js new file mode 100644 index 0000000000000000000000000000000000000000..4744dfaf7b44de2f4a8b6bc814c35ec00491540a --- /dev/null +++ b/packages/misskey-bubble-game/build.js @@ -0,0 +1,31 @@ +import { build } from "esbuild"; +import { globSync } from "glob"; + +const entryPoints = globSync("./src/**/**.{ts,tsx}"); + +/** @type {import('esbuild').BuildOptions} */ +const options = { + entryPoints, + minify: true, + outdir: "./built/esm", + target: "es2022", + platform: "browser", + format: "esm", +}; + +if (process.env.WATCH === "true") { + options.watch = { + onRebuild(error, result) { + if (error) { + console.error("watch build failed:", error); + } else { + console.log("watch build succeeded:", result); + } + }, + }; +} + +build(options).catch((err) => { + process.stderr.write(err.stderr); + process.exit(1); +}); diff --git a/packages/misskey-bubble-game/package.json b/packages/misskey-bubble-game/package.json new file mode 100644 index 0000000000000000000000000000000000000000..ddc4c2134bcf685e4fae67bd4093f9a6e541a395 --- /dev/null +++ b/packages/misskey-bubble-game/package.json @@ -0,0 +1,48 @@ +{ + "type": "module", + "name": "misskey-bubble-game", + "version": "0.0.1", + "types": "./built/dts/index.d.ts", + "exports": { + ".": { + "import": "./built/esm/index.js", + "types": "./built/dts/index.d.ts" + }, + "./*": { + "import": "./built/esm/*", + "types": "./built/dts/*" + } + }, + "scripts": { + "build": "node ./build.js", + "build:tsc": "npm run tsc", + "tsc": "npm run tsc-esm && npm run tsc-dts", + "tsc-esm": "tsc --outDir built/esm", + "tsc-dts": "tsc --outDir built/dts --declaration true --emitDeclarationOnly true --declarationMap true", + "watch": "nodemon -w src -e ts,js,cjs,mjs,json --exec \"pnpm run build:tsc\"", + "eslint": "eslint . --ext .js,.jsx,.ts,.tsx", + "typecheck": "tsc --noEmit", + "lint": "pnpm typecheck && pnpm eslint" + }, + "devDependencies": { + "@misskey-dev/eslint-plugin": "1.0.0", + "@types/matter-js": "0.19.6", + "@types/node": "20.11.5", + "@types/seedrandom": "3.0.8", + "@typescript-eslint/eslint-plugin": "7.1.0", + "@typescript-eslint/parser": "7.1.0", + "eslint": "8.57.0", + "nodemon": "3.0.2", + "typescript": "5.3.3" + }, + "files": [ + "built" + ], + "dependencies": { + "esbuild": "0.19.11", + "eventemitter3": "5.0.1", + "glob": "^10.3.10", + "matter-js": "0.19.0", + "seedrandom": "3.0.5" + } +} diff --git a/packages/misskey-bubble-game/src/game.ts b/packages/misskey-bubble-game/src/game.ts new file mode 100644 index 0000000000000000000000000000000000000000..3bce4b1dcfe8dd2e15ad8645889dcf039c5d9959 --- /dev/null +++ b/packages/misskey-bubble-game/src/game.ts @@ -0,0 +1,495 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { EventEmitter } from 'eventemitter3'; +import * as Matter from 'matter-js'; +import seedrandom from 'seedrandom'; +import { NORAML_MONOS, SQUARE_MONOS, SWEETS_MONOS, YEN_MONOS } from './monos.js'; + +export type Mono = { + id: string; + level: number; + sizeX: number; + sizeY: number; + shape: 'circle' | 'rectangle' | 'custom'; + vertices?: Matter.Vector[][]; + verticesSize?: number; + score: number; + dropCandidate: boolean; +}; + +type Log = { + frame: number; + operation: 'drop'; + x: number; +} | { + frame: number; + operation: 'hold'; +} | { + frame: number; + operation: 'surrender'; +}; + +export class DropAndFusionGame extends EventEmitter<{ + changeScore: (newScore: number) => void; + changeCombo: (newCombo: number) => void; + changeStock: (newStock: { id: string; mono: Mono }[]) => void; + changeHolding: (newHolding: { id: string; mono: Mono } | null) => void; + dropped: (x: number) => void; + fusioned: (x: number, y: number, nextMono: Mono | null, scoreDelta: number) => void; + collision: (energy: number, bodyA: Matter.Body, bodyB: Matter.Body) => void; + monoAdded: (mono: Mono) => void; + gameOver: () => void; +}> { + private PHYSICS_QUALITY_FACTOR = 16; // 低ã„ã»ã©ãƒ‘フォーマンスãŒé«˜ã„ãŒã‚¬ã‚¿ã‚¬ã‚¿ã—ã¦å®‰å®šã—ãªããªã‚‹ã€é€†ã«é«˜ã™ãŽã¦ã‚‚何故ã‹ä¸å®‰å®šã«ãªã‚‹ + private COMBO_INTERVAL = 60; // frame + public readonly GAME_VERSION = 3; + public readonly GAME_WIDTH = 450; + public readonly GAME_HEIGHT = 600; + public readonly DROP_COOLTIME = 30; // frame + public readonly PLAYAREA_MARGIN = 25; + private STOCK_MAX = 4; + private TICK_DELTA = 1000 / 60; // 60fps + + public frame = 0; + public engine: Matter.Engine; + private tickCallbackQueue: { frame: number; callback: () => void; }[] = []; + private overflowCollider: Matter.Body; + private isGameOver = false; + private gameMode: 'normal' | 'yen' | 'square' | 'sweets' | 'space'; + private rng: () => number; + private logs: Log[] = []; + + /** + * フィールドã«å‡ºã¦ã„ã¦ã€ã‹ã¤åˆä½“ã®å¯¾è±¡ã¨ãªã‚‹ã‚¢ã‚¤ãƒ†ãƒ + */ + private fusionReadyBodyIds: Matter.Body['id'][] = []; + + private gameOverReadyBodyIds: Matter.Body['id'][] = []; + + /** + * fusion予約アイテムã®ãƒšã‚¢ + * TODO: ã“れらã®ãƒ¢ãƒŽã¯å…‰ã‚‰ã›ã‚‹ãªã©ã®æ¼”出をã™ã‚‹ã¨è¦–覚的ã«æ¥½ã—ãㆠ+ */ + private fusionReservedPairs: { bodyA: Matter.Body; bodyB: Matter.Body }[] = []; + + private latestDroppedAt = 0; // frame + private latestFusionedAt = 0; // frame + private stock: { id: string; mono: Mono }[] = []; + private holding: { id: string; mono: Mono } | null = null; + + public get monoDefinitions() { + switch (this.gameMode) { + case 'normal': return NORAML_MONOS; + case 'yen': return YEN_MONOS; + case 'square': return SQUARE_MONOS; + case 'sweets': return SWEETS_MONOS; + case 'space': return NORAML_MONOS; + } + } + + private _combo = 0; + private get combo() { + return this._combo; + } + private set combo(value: number) { + this._combo = value; + this.emit('changeCombo', value); + } + + private _score = 0; + private get score() { + return this._score; + } + private set score(value: number) { + this._score = value; + this.emit('changeScore', value); + } + + private getMonoRenderOptions: null | ((mono: Mono) => Partial<Matter.IBodyRenderOptions>) = null; + + public replayPlaybackRate = 1; + + constructor(env: { + seed: string; + gameMode: DropAndFusionGame['gameMode']; + getMonoRenderOptions?: (mono: Mono) => Partial<Matter.IBodyRenderOptions>; + }) { + super(); + + //#region BIND + this.tick = this.tick.bind(this); + //#endregion + + this.gameMode = env.gameMode; + this.getMonoRenderOptions = env.getMonoRenderOptions ?? null; + this.rng = seedrandom(env.seed); + + // sweetsモードã¯é‡ã„ãŸã‚ + const physicsQualityFactor = this.gameMode === 'sweets' ? 4 : this.PHYSICS_QUALITY_FACTOR; + this.engine = Matter.Engine.create({ + constraintIterations: 2 * physicsQualityFactor, + positionIterations: 6 * physicsQualityFactor, + velocityIterations: 4 * physicsQualityFactor, + gravity: { + x: 0, + y: this.gameMode === 'space' ? 0.0125 : 1, + }, + timing: { + timeScale: 2, + }, + enableSleeping: false, + }); + + this.engine.world.bodies = []; + + //#region walls + const WALL_OPTIONS: Matter.IChamferableBodyDefinition = { + label: '_wall_', + isStatic: true, + friction: 0.7, + slop: this.gameMode === 'space' ? 0.01 : 0.7, + render: { + strokeStyle: 'transparent', + fillStyle: 'transparent', + }, + }; + + const thickness = 100; + Matter.Composite.add(this.engine.world, [ + Matter.Bodies.rectangle(this.GAME_WIDTH / 2, this.GAME_HEIGHT + (thickness / 2) - this.PLAYAREA_MARGIN, this.GAME_WIDTH, thickness, WALL_OPTIONS), + Matter.Bodies.rectangle(this.GAME_WIDTH + (thickness / 2) - this.PLAYAREA_MARGIN, this.GAME_HEIGHT / 2, thickness, this.GAME_HEIGHT, WALL_OPTIONS), + Matter.Bodies.rectangle(-((thickness / 2) - this.PLAYAREA_MARGIN), this.GAME_HEIGHT / 2, thickness, this.GAME_HEIGHT, WALL_OPTIONS), + ]); + //#endregion + + this.overflowCollider = Matter.Bodies.rectangle(this.GAME_WIDTH / 2, 0, this.GAME_WIDTH, 200, { + label: '_overflow_', + isStatic: true, + isSensor: true, + render: { + strokeStyle: 'transparent', + fillStyle: 'transparent', + }, + }); + Matter.Composite.add(this.engine.world, this.overflowCollider); + } + + public msToFrame(ms: number) { + return Math.round(ms / this.TICK_DELTA); + } + + public frameToMs(frame: number) { + return frame * this.TICK_DELTA; + } + + private createBody(mono: Mono, x: number, y: number) { + const options: Matter.IBodyDefinition = { + label: mono.id, + density: this.gameMode === 'space' ? 0.01 : ((mono.sizeX * mono.sizeY) / 10000), + restitution: this.gameMode === 'space' ? 0.5 : 0.2, + frictionAir: this.gameMode === 'space' ? 0 : 0.01, + friction: this.gameMode === 'space' ? 0.5 : 0.7, + frictionStatic: this.gameMode === 'space' ? 0 : 5, + slop: this.gameMode === 'space' ? 0.01 : 0.7, + //mass: 0, + render: this.getMonoRenderOptions ? this.getMonoRenderOptions(mono) : undefined, + }; + if (mono.shape === 'circle') { + return Matter.Bodies.circle(x, y, mono.sizeX / 2, options); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + } else if (mono.shape === 'rectangle') { + return Matter.Bodies.rectangle(x, y, mono.sizeX, mono.sizeY, options); + } else if (mono.shape === 'custom') { + return Matter.Bodies.fromVertices(x, y, mono.vertices!.map(i => i.map(j => ({ + x: (j.x / mono.verticesSize!) * mono.sizeX, + y: (j.y / mono.verticesSize!) * mono.sizeY, + }))), options); + } else { + throw new Error('unrecognized shape'); + } + } + + private fusion(bodyA: Matter.Body, bodyB: Matter.Body) { + if (this.latestFusionedAt > this.frame - this.COMBO_INTERVAL) { + this.combo++; + } else { + this.combo = 1; + } + this.latestFusionedAt = this.frame; + + const newX = (bodyA.position.x + bodyB.position.x) / 2; + const newY = (bodyA.position.y + bodyB.position.y) / 2; + + this.fusionReadyBodyIds = this.fusionReadyBodyIds.filter(x => x !== bodyA.id && x !== bodyB.id); + this.gameOverReadyBodyIds = this.gameOverReadyBodyIds.filter(x => x !== bodyA.id && x !== bodyB.id); + Matter.Composite.remove(this.engine.world, [bodyA, bodyB]); + + const currentMono = this.monoDefinitions.find(y => y.id === bodyA.label)!; + const nextMono = this.monoDefinitions.find(x => x.level === currentMono.level + 1) ?? null; + + if (nextMono) { + const body = this.createBody(nextMono, newX, newY); + Matter.Composite.add(this.engine.world, body); + + // 連鎖ã—ã¦fusionã—ãŸå ´åˆã®åˆ†ã‹ã‚Šã‚„ã™ã•ã®ãŸã‚å°‘ã—é–“ã‚’ç½®ã„ã¦ã‹ã‚‰fusion対象ã«ãªã‚‹ã‚ˆã†ã«ã™ã‚‹ + this.tickCallbackQueue.push({ + frame: this.frame + this.msToFrame(100), + callback: () => { + this.fusionReadyBodyIds.push(body.id); + }, + }); + + this.emit('monoAdded', nextMono); + } + + const hasComboBonus = this.gameMode !== 'yen' && this.gameMode !== 'sweets'; + const comboBonus = hasComboBonus ? 1 + ((this.combo - 1) / 5) : 1; + const additionalScore = Math.round(currentMono.score * comboBonus); + this.score += additionalScore; + + this.emit('fusioned', newX, newY, nextMono, additionalScore); + } + + private onCollision(event: Matter.IEventCollision<Matter.Engine>) { + for (const pairs of event.pairs) { + const { bodyA, bodyB } = pairs; + + const shouldFusion = (bodyA.label === bodyB.label) && + !this.fusionReservedPairs.some(x => + x.bodyA.id === bodyA.id || + x.bodyA.id === bodyB.id || + x.bodyB.id === bodyA.id || + x.bodyB.id === bodyB.id); + + if (shouldFusion) { + if (this.fusionReadyBodyIds.includes(bodyA.id) && this.fusionReadyBodyIds.includes(bodyB.id)) { + this.fusion(bodyA, bodyB); + } else { + this.fusionReservedPairs.push({ bodyA, bodyB }); + this.tickCallbackQueue.push({ + frame: this.frame + this.msToFrame(100), + callback: () => { + this.fusionReservedPairs = this.fusionReservedPairs.filter(x => x.bodyA.id !== bodyA.id && x.bodyB.id !== bodyB.id); + this.fusion(bodyA, bodyB); + }, + }); + } + } else { + const energy = pairs.collision.depth; + + if (bodyA.label === '_overflow_' || bodyB.label === '_overflow_') continue; + + if (bodyA.label !== '_wall_' && bodyB.label !== '_wall_') { + if (!this.gameOverReadyBodyIds.includes(bodyA.id)) this.gameOverReadyBodyIds.push(bodyA.id); + if (!this.gameOverReadyBodyIds.includes(bodyB.id)) this.gameOverReadyBodyIds.push(bodyB.id); + } + + this.emit('collision', energy, bodyA, bodyB); + } + } + } + + private onCollisionActive(event: Matter.IEventCollision<Matter.Engine>) { + for (const pairs of event.pairs) { + const { bodyA, bodyB } = pairs; + + // ãƒã‚³ã‹ã‚‰ã‚ãµã‚ŒãŸã‹ã©ã†ã‹ã®åˆ¤å®š + if (bodyA.id === this.overflowCollider.id || bodyB.id === this.overflowCollider.id) { + if (this.gameOverReadyBodyIds.includes(bodyA.id) || this.gameOverReadyBodyIds.includes(bodyB.id)) { + this.gameOver(); + break; + } + continue; + } + } + } + + public surrender() { + this.logs.push({ + frame: this.frame, + operation: 'surrender', + }); + + this.gameOver(); + } + + private gameOver() { + this.isGameOver = true; + this.emit('gameOver'); + } + + public start() { + for (let i = 0; i < this.STOCK_MAX; i++) { + this.stock.push({ + id: this.rng().toString(), + mono: this.monoDefinitions.filter(x => x.dropCandidate)[Math.floor(this.rng() * this.monoDefinitions.filter(x => x.dropCandidate).length)], + }); + } + this.emit('changeStock', this.stock); + + Matter.Events.on(this.engine, 'collisionStart', this.onCollision.bind(this)); + Matter.Events.on(this.engine, 'collisionActive', this.onCollisionActive.bind(this)); + } + + public getLogs() { + return this.logs; + } + + public tick() { + this.frame++; + + if (this.latestFusionedAt < this.frame - this.COMBO_INTERVAL) { + this.combo = 0; + } + + this.tickCallbackQueue = this.tickCallbackQueue.filter(x => { + if (x.frame === this.frame) { + x.callback(); + return false; + } else { + return true; + } + }); + + Matter.Engine.update(this.engine, this.TICK_DELTA); + + const hasNextTick = !this.isGameOver; + + return hasNextTick; + } + + public getActiveMonos() { + return this.engine.world.bodies.map(x => this.monoDefinitions.find((mono) => mono.id === x.label)!).filter(x => x !== undefined); + } + + public drop(_x: number) { + if (this.isGameOver) return; + if (this.frame - this.latestDroppedAt < this.DROP_COOLTIME) return; + + const head = this.stock.shift()!; + this.stock.push({ + id: this.rng().toString(), + mono: this.monoDefinitions.filter(x => x.dropCandidate)[Math.floor(this.rng() * this.monoDefinitions.filter(x => x.dropCandidate).length)], + }); + this.emit('changeStock', this.stock); + + const inputX = Math.round(_x); + const x = Math.min(this.GAME_WIDTH - this.PLAYAREA_MARGIN - (head.mono.sizeX / 2), Math.max(this.PLAYAREA_MARGIN + (head.mono.sizeX / 2), inputX)); + const body = this.createBody(head.mono, x, 50 + head.mono.sizeY / 2); + this.logs.push({ + frame: this.frame, + operation: 'drop', + x: inputX, + }); + + // add force + if (this.gameMode === 'space') { + Matter.Body.applyForce(body, body.position, { + x: 0, + y: (Math.PI * head.mono.sizeX * head.mono.sizeY) / 65536, + }); + } + + Matter.Composite.add(this.engine.world, body); + + this.fusionReadyBodyIds.push(body.id); + this.latestDroppedAt = this.frame; + + this.emit('dropped', x); + this.emit('monoAdded', head.mono); + } + + public hold() { + if (this.isGameOver) return; + + this.logs.push({ + frame: this.frame, + operation: 'hold', + }); + + if (this.holding) { + const head = this.stock.shift()!; + this.stock.unshift(this.holding); + this.holding = head; + this.emit('changeHolding', this.holding); + this.emit('changeStock', this.stock); + } else { + const head = this.stock.shift()!; + this.holding = head; + this.stock.push({ + id: this.rng().toString(), + mono: this.monoDefinitions.filter(x => x.dropCandidate)[Math.floor(this.rng() * this.monoDefinitions.filter(x => x.dropCandidate).length)], + }); + this.emit('changeHolding', this.holding); + this.emit('changeStock', this.stock); + } + } + + public static serializeLogs(logs: Log[]) { + const _logs: number[][] = []; + + for (let i = 0; i < logs.length; i++) { + const log = logs[i]; + const frameDelta = i === 0 ? log.frame : log.frame - logs[i - 1].frame; + + switch (log.operation) { + case 'drop': + _logs.push([frameDelta, 0, log.x]); + break; + case 'hold': + _logs.push([frameDelta, 1]); + break; + case 'surrender': + _logs.push([frameDelta, 2]); + break; + } + } + + return _logs; + } + + public static deserializeLogs(logs: number[][]) { + const _logs: Log[] = []; + + let frame = 0; + + for (const log of logs) { + const frameDelta = log[0]; + frame += frameDelta; + + const operation = log[1]; + + switch (operation) { + case 0: + _logs.push({ + frame, + operation: 'drop', + x: log[2], + }); + break; + case 1: + _logs.push({ + frame, + operation: 'hold', + }); + break; + case 2: + _logs.push({ + frame, + operation: 'surrender', + }); + break; + } + } + + return _logs; + } + + public dispose() { + Matter.World.clear(this.engine.world, false); + Matter.Engine.clear(this.engine); + } +} diff --git a/packages/misskey-bubble-game/src/index.ts b/packages/misskey-bubble-game/src/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..004a7d008ef4336240ade4c77af65289444550bf --- /dev/null +++ b/packages/misskey-bubble-game/src/index.ts @@ -0,0 +1,10 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { DropAndFusionGame, Mono } from './game.js'; + +export { + DropAndFusionGame, Mono, +}; diff --git a/packages/misskey-bubble-game/src/monos.ts b/packages/misskey-bubble-game/src/monos.ts new file mode 100644 index 0000000000000000000000000000000000000000..41ab2358c479df04336fb25e65510e87d1ae4920 --- /dev/null +++ b/packages/misskey-bubble-game/src/monos.ts @@ -0,0 +1,952 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Mono } from './game.js'; + +const NORMAL_BASE_SIZE = 32; +export const NORAML_MONOS: Mono[] = [{ + id: '9377076d-c980-4d83-bdaf-175bc58275b7', + level: 10, + sizeX: NORMAL_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: NORMAL_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'circle', + score: 512, + dropCandidate: false, +}, { + id: 'be9f38d2-b267-4b1a-b420-904e22e80568', + level: 9, + sizeX: NORMAL_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: NORMAL_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'circle', + score: 256, + dropCandidate: false, +}, { + id: 'beb30459-b064-4888-926b-f572e4e72e0c', + level: 8, + sizeX: NORMAL_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: NORMAL_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'circle', + score: 128, + dropCandidate: false, +}, { + id: 'feab6426-d9d8-49ae-849c-048cdbb6cdf0', + level: 7, + sizeX: NORMAL_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: NORMAL_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'circle', + score: 64, + dropCandidate: false, +}, { + id: 'd6d8fed6-6d18-4726-81a1-6cf2c974df8a', + level: 6, + sizeX: NORMAL_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: NORMAL_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'circle', + score: 32, + dropCandidate: false, +}, { + id: '249c728e-230f-4332-bbbf-281c271c75b2', + level: 5, + sizeX: NORMAL_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: NORMAL_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'circle', + score: 16, + dropCandidate: true, +}, { + id: '23d67613-d484-4a93-b71e-3e81b19d6186', + level: 4, + sizeX: NORMAL_BASE_SIZE * 1.25 * 1.25 * 1.25, + sizeY: NORMAL_BASE_SIZE * 1.25 * 1.25 * 1.25, + shape: 'circle', + score: 8, + dropCandidate: true, +}, { + id: '3cbd0add-ad7d-4685-bad0-29f6dddc0b99', + level: 3, + sizeX: NORMAL_BASE_SIZE * 1.25 * 1.25, + sizeY: NORMAL_BASE_SIZE * 1.25 * 1.25, + shape: 'circle', + score: 4, + dropCandidate: true, +}, { + id: '8f86d4f4-ee02-41bf-ad38-1ce0ae457fb5', + level: 2, + sizeX: NORMAL_BASE_SIZE * 1.25, + sizeY: NORMAL_BASE_SIZE * 1.25, + shape: 'circle', + score: 2, + dropCandidate: true, +}, { + id: '64ec4add-ce39-42b4-96cb-33908f3f118d', + level: 1, + sizeX: NORMAL_BASE_SIZE, + sizeY: NORMAL_BASE_SIZE, + shape: 'circle', + score: 1, + dropCandidate: true, +}]; + +const YEN_BASE_SIZE = 32; +const YEN_SATSU_BASE_SIZE = 70; +export const YEN_MONOS: Mono[] = [{ + id: '880f9bd9-802f-4135-a7e1-fd0e0331f726', + level: 10, + sizeX: (YEN_SATSU_BASE_SIZE * 2) * 1.25 * 1.25 * 1.25, + sizeY: YEN_SATSU_BASE_SIZE * 1.25 * 1.25 * 1.25, + shape: 'rectangle', + score: 10000, + dropCandidate: false, +}, { + id: 'e807beb6-374a-4314-9cc2-aa5f17d96b6b', + level: 9, + sizeX: (YEN_SATSU_BASE_SIZE * 2) * 1.25 * 1.25, + sizeY: YEN_SATSU_BASE_SIZE * 1.25 * 1.25, + shape: 'rectangle', + score: 5000, + dropCandidate: false, +}, { + id: '033445b7-8f90-4fc9-beca-71a9e87cb530', + level: 8, + sizeX: (YEN_SATSU_BASE_SIZE * 2) * 1.25, + sizeY: YEN_SATSU_BASE_SIZE * 1.25, + shape: 'rectangle', + score: 2000, + dropCandidate: false, +}, { + id: '410a09ec-5f7f-46f6-b26f-cbca4ccbd091', + level: 7, + sizeX: YEN_SATSU_BASE_SIZE * 2, + sizeY: YEN_SATSU_BASE_SIZE, + shape: 'rectangle', + score: 1000, + dropCandidate: false, +}, { + id: '2aae82bc-3fa4-49ad-a6b5-94d888e809f5', + level: 6, + sizeX: YEN_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: YEN_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'circle', + score: 500, + dropCandidate: false, +}, { + id: 'a619bd67-d08f-4cc0-8c7e-c8072a4950cd', + level: 5, + sizeX: YEN_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: YEN_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'circle', + score: 100, + dropCandidate: true, +}, { + id: 'c1c5d8e4-17d6-4455-befd-12154d731faa', + level: 4, + sizeX: YEN_BASE_SIZE * 1.25 * 1.25 * 1.25, + sizeY: YEN_BASE_SIZE * 1.25 * 1.25 * 1.25, + shape: 'circle', + score: 50, + dropCandidate: true, +}, { + id: '7082648c-e428-44c4-887a-25c07a8ebdd5', + level: 3, + sizeX: YEN_BASE_SIZE * 1.25 * 1.25, + sizeY: YEN_BASE_SIZE * 1.25 * 1.25, + shape: 'circle', + score: 10, + dropCandidate: true, +}, { + id: '0d8d40d5-e6e0-4d26-8a95-b8d842363379', + level: 2, + sizeX: YEN_BASE_SIZE * 1.25, + sizeY: YEN_BASE_SIZE * 1.25, + shape: 'circle', + score: 5, + dropCandidate: true, +}, { + id: '9dec1b38-d99d-40de-8288-37367b983d0d', + level: 1, + sizeX: YEN_BASE_SIZE, + sizeY: YEN_BASE_SIZE, + shape: 'circle', + score: 1, + dropCandidate: true, +}]; + +const SQUARE_BASE_SIZE = 28; +export const SQUARE_MONOS: Mono[] = [{ + id: 'f75fd0ba-d3d4-40a4-9712-b470e45b0525', + level: 10, + sizeX: SQUARE_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: SQUARE_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'rectangle', + score: 512, + dropCandidate: false, +}, { + id: '7b70f4af-1c01-45fd-af72-61b1f01e03d1', + level: 9, + sizeX: SQUARE_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: SQUARE_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'rectangle', + score: 256, + dropCandidate: false, +}, { + id: '41607ef3-b6d6-4829-95b6-3737bf8bb956', + level: 8, + sizeX: SQUARE_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: SQUARE_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'rectangle', + score: 128, + dropCandidate: false, +}, { + id: '8a8310d2-0374-460f-bb50-ca9cd3ee3416', + level: 7, + sizeX: SQUARE_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: SQUARE_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'rectangle', + score: 64, + dropCandidate: false, +}, { + id: '1092e069-fe1a-450b-be97-b5d477ec398c', + level: 6, + sizeX: SQUARE_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: SQUARE_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'rectangle', + score: 32, + dropCandidate: false, +}, { + id: '2294734d-7bb8-4781-bb7b-ef3820abf3d0', + level: 5, + sizeX: SQUARE_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: SQUARE_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'rectangle', + score: 16, + dropCandidate: true, +}, { + id: 'ea8a61af-e350-45f7-ba6a-366fcd65692a', + level: 4, + sizeX: SQUARE_BASE_SIZE * 1.25 * 1.25 * 1.25, + sizeY: SQUARE_BASE_SIZE * 1.25 * 1.25 * 1.25, + shape: 'rectangle', + score: 8, + dropCandidate: true, +}, { + id: 'd0c74815-fc1c-4fbe-9953-c92e4b20f919', + level: 3, + sizeX: SQUARE_BASE_SIZE * 1.25 * 1.25, + sizeY: SQUARE_BASE_SIZE * 1.25 * 1.25, + shape: 'rectangle', + score: 4, + dropCandidate: true, +}, { + id: 'd8fbd70e-611d-402d-87da-1a7fd8cd2c8d', + level: 2, + sizeX: SQUARE_BASE_SIZE * 1.25, + sizeY: SQUARE_BASE_SIZE * 1.25, + shape: 'rectangle', + score: 2, + dropCandidate: true, +}, { + id: '35e476ee-44bd-4711-ad42-87be245d3efd', + level: 1, + sizeX: SQUARE_BASE_SIZE, + sizeY: SQUARE_BASE_SIZE, + shape: 'rectangle', + score: 1, + dropCandidate: true, +}]; + +const SWEETS_BASE_SIZE = 40; +export const SWEETS_MONOS: Mono[] = [{ + id: '77f724c0-88be-4aeb-8e1a-a00ed18e3844', + level: 10, + sizeX: SWEETS_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: SWEETS_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'custom', + vertices: [ + [ + { + 'x': 14, + 'y': 2, + }, + { + 'x': 2, + 'y': 13, + }, + { + 'x': 2, + 'y': 31, + }, + { + 'x': 30, + 'y': 23, + }, + { + 'x': 30, + 'y': 7, + }, + { + 'x': 29, + 'y': 6, + }, + { + 'x': 20, + 'y': 4, + }, + { + 'x': 17, + 'y': 3, + }, + { + 'x': 16, + 'y': 2, + }, + ], + ], + verticesSize: 32, + score: 400, + dropCandidate: false, +}, { + id: 'f3468ef4-2e1e-4906-8795-f147f39f7e1f', + level: 9, + sizeX: SWEETS_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: SWEETS_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'custom', + vertices: [ + [ + { + 'x': 15, + 'y': 2, + }, + { + 'x': 14, + 'y': 3, + }, + { + 'x': 8, + 'y': 4, + }, + { + 'x': 6, + 'y': 5, + }, + { + 'x': 4, + 'y': 8, + }, + { + 'x': 4, + 'y': 15, + }, + { + 'x': 2, + 'y': 19, + }, + { + 'x': 2, + 'y': 22.36, + }, + { + 'x': 3, + 'y': 25, + }, + { + 'x': 5, + 'y': 28, + }, + { + 'x': 10, + 'y': 30, + }, + { + 'x': 22, + 'y': 30, + }, + { + 'x': 27, + 'y': 28, + }, + { + 'x': 29, + 'y': 25, + }, + { + 'x': 30, + 'y': 22, + }, + { + 'x': 30, + 'y': 19, + }, + { + 'x': 28, + 'y': 15, + }, + { + 'x': 28, + 'y': 8, + }, + { + 'x': 26, + 'y': 5, + }, + { + 'x': 24, + 'y': 4, + }, + { + 'x': 18, + 'y': 3, + }, + { + 'x': 17, + 'y': 2, + }, + ], + ], + verticesSize: 32, + score: 380, + dropCandidate: false, +}, { + id: 'bcb41129-6f2d-44ee-89d3-86eb2df564ba', + level: 8, + sizeX: SWEETS_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: SWEETS_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'custom', + vertices: [ + [ + { + 'x': 15, + 'y': 2, + }, + { + 'x': 11, + 'y': 3, + }, + { + 'x': 8, + 'y': 6, + }, + { + 'x': 7, + 'y': 8, + }, + { + 'x': 6, + 'y': 11, + }, + { + 'x': 6, + 'y': 13, + }, + { + 'x': 7, + 'y': 16, + }, + { + 'x': 8, + 'y': 18, + }, + { + 'x': 15, + 'y': 30, + }, + { + 'x': 17, + 'y': 30, + }, + { + 'x': 24, + 'y': 18, + }, + { + 'x': 25, + 'y': 16, + }, + { + 'x': 26, + 'y': 13, + }, + { + 'x': 26, + 'y': 11, + }, + { + 'x': 25, + 'y': 8, + }, + { + 'x': 24, + 'y': 6, + }, + { + 'x': 21, + 'y': 3, + }, + { + 'x': 17, + 'y': 2, + }, + ], + ], + verticesSize: 32, + score: 300, + dropCandidate: false, +}, { + id: 'f058e1ad-1981-409b-b3a7-302de0a43744', + level: 7, + sizeX: SWEETS_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: SWEETS_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'custom', + vertices: [ + [ + { + 'x': 17, + 'y': 2.541, + }, + { + 'x': 14, + 'y': 5.402, + }, + { + 'x': 10, + 'y': 7, + }, + { + 'x': 10, + 'y': 10.367, + }, + { + 'x': 8, + 'y': 11, + }, + { + 'x': 8, + 'y': 14, + }, + { + 'x': 5.781, + 'y': 16.265, + }, + { + 'x': 6.594, + 'y': 19.627, + }, + { + 'x': 9.414, + 'y': 21, + }, + { + 'x': 12, + 'y': 29.988, + }, + { + 'x': 21, + 'y': 29.988, + }, + { + 'x': 22.016, + 'y': 22.629, + }, + { + 'x': 23, + 'y': 21.772, + }, + { + 'x': 23, + 'y': 19.202, + }, + { + 'x': 25.783, + 'y': 17.473, + }, + { + 'x': 25.783, + 'y': 14.727, + }, + { + 'x': 24, + 'y': 13.173, + }, + { + 'x': 24, + 'y': 10.367, + }, + { + 'x': 22, + 'y': 9.233, + }, + { + 'x': 22, + 'y': 6.454, + }, + { + 'x': 18, + 'y': 5, + }, + ], + ], + verticesSize: 32, + score: 300, + dropCandidate: false, +}, { + id: 'd22cfe38-5a3b-4b9c-a1a6-907930a3d732', + level: 6, + sizeX: SWEETS_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: SWEETS_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'custom', + vertices: [ + [ + { + 'x': 15, + 'y': 2, + }, + { + 'x': 11, + 'y': 3, + }, + { + 'x': 8, + 'y': 5, + }, + { + 'x': 7, + 'y': 6, + }, + { + 'x': 5, + 'y': 9, + }, + { + 'x': 4, + 'y': 12, + }, + { + 'x': 4, + 'y': 20, + }, + { + 'x': 5, + 'y': 23, + }, + { + 'x': 7, + 'y': 26, + }, + { + 'x': 11, + 'y': 29, + }, + { + 'x': 14, + 'y': 30, + }, + { + 'x': 18, + 'y': 30, + }, + { + 'x': 21, + 'y': 29, + }, + { + 'x': 25, + 'y': 26, + }, + { + 'x': 27, + 'y': 23, + }, + { + 'x': 28, + 'y': 20, + }, + { + 'x': 28, + 'y': 12, + }, + { + 'x': 27, + 'y': 9, + }, + { + 'x': 25, + 'y': 6, + }, + { + 'x': 24, + 'y': 5, + }, + { + 'x': 21, + 'y': 3, + }, + { + 'x': 17, + 'y': 2, + }, + ], + ], + verticesSize: 32, + score: 250, + dropCandidate: false, +}, { + id: '79867083-a073-427e-ae82-07a70d9f3b4f', + level: 5, + sizeX: SWEETS_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: SWEETS_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'custom', + vertices: [ + [ + { + 'x': 9, + 'y': 15, + }, + { + 'x': 23, + 'y': 15, + }, + { + 'x': 30, + 'y': 27, + }, + { + 'x': 25.7, + 'y': 30, + }, + { + 'x': 6.34, + 'y': 30, + }, + { + 'x': 2, + 'y': 27, + }, + ], + ], + verticesSize: 32, + score: 200, + dropCandidate: true, +}, { + id: '2e152a12-a567-4100-b4d4-d15d81ba47b1', + level: 4, + sizeX: SWEETS_BASE_SIZE * 1.25 * 1.25 * 1.25, + sizeY: SWEETS_BASE_SIZE * 1.25 * 1.25 * 1.25, + shape: 'custom', + vertices: [ + [ + { + 'x': 12, + 'y': 2, + }, + { + 'x': 2, + 'y': 12, + }, + { + 'x': 2, + 'y': 14, + }, + { + 'x': 18, + 'y': 30, + }, + { + 'x': 20, + 'y': 30, + }, + { + 'x': 30, + 'y': 20, + }, + { + 'x': 30, + 'y': 18, + }, + { + 'x': 14, + 'y': 2, + }, + ], + ], + verticesSize: 32, + score: 200, + dropCandidate: true, +}, { + id: '12250376-2258-4716-8eec-b3a7239461fc', + level: 3, + sizeX: SWEETS_BASE_SIZE * 1.25 * 1.25, + sizeY: SWEETS_BASE_SIZE * 1.25 * 1.25, + shape: 'custom', + vertices: [ + [ + { + 'x': 12, + 'y': 2, + }, + { + 'x': 7, + 'y': 3, + }, + { + 'x': 3, + 'y': 7, + }, + { + 'x': 2, + 'y': 12, + }, + { + 'x': 3, + 'y': 16, + }, + { + 'x': 5, + 'y': 19, + }, + { + 'x': 8, + 'y': 21, + }, + { + 'x': 12, + 'y': 22, + }, + { + 'x': 18, + 'y': 21, + }, + { + 'x': 27, + 'y': 30, + }, + { + 'x': 30, + 'y': 30, + }, + { + 'x': 30, + 'y': 27, + }, + { + 'x': 21, + 'y': 18, + }, + { + 'x': 22, + 'y': 14.25, + }, + { + 'x': 22, + 'y': 11, + }, + { + 'x': 21, + 'y': 8, + }, + { + 'x': 19, + 'y': 5, + }, + { + 'x': 16.5, + 'y': 3, + }, + ], + ], + verticesSize: 32, + score: 120, + dropCandidate: true, +}, { + id: '4d4f2668-4be7-44a3-aa3a-856df6e25aa6', + level: 2, + sizeX: SWEETS_BASE_SIZE * 1.25, + sizeY: SWEETS_BASE_SIZE * 1.25, + shape: 'custom', + vertices: [ + [ + { + 'x': 12, + 'y': 1.9, + }, + { + 'x': 4, + 'y': 4, + }, + { + 'x': 2, + 'y': 12, + }, + { + 'x': 6, + 'y': 13.375, + }, + { + 'x': 6, + 'y': 18, + }, + { + 'x': 8, + 'y': 22, + }, + { + 'x': 12, + 'y': 25.372, + }, + { + 'x': 16, + 'y': 26, + }, + { + 'x': 19, + 'y': 25.372, + }, + { + 'x': 20, + 'y': 30, + }, + { + 'x': 28, + 'y': 27, + }, + { + 'x': 30, + 'y': 20, + }, + { + 'x': 25.473, + 'y': 19, + }, + { + 'x': 26, + 'y': 15, + }, + { + 'x': 24, + 'y': 10, + }, + { + 'x': 20, + 'y': 7, + }, + { + 'x': 16, + 'y': 6, + }, + { + 'x': 13, + 'y': 6, + }, + ], + ], + verticesSize: 32, + score: 20, + dropCandidate: true, +}, { + id: 'c9984b40-4045-44c3-b260-d47b7b4625b2', + level: 1, + sizeX: SWEETS_BASE_SIZE, + sizeY: SWEETS_BASE_SIZE, + shape: 'circle', + score: 30, + dropCandidate: true, +}]; diff --git a/packages/misskey-bubble-game/tsconfig.json b/packages/misskey-bubble-game/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..f56b65e86802158634e10482fe7a47eee39cf5cf --- /dev/null +++ b/packages/misskey-bubble-game/tsconfig.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "target": "ES2022", + "module": "nodenext", + "moduleResolution": "nodenext", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./built/", + "removeComments": true, + "strict": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "experimentalDecorators": true, + "noImplicitReturns": true, + "esModuleInterop": true, + "typeRoots": [ + "./node_modules/@types" + ], + "lib": [ + "esnext", + "dom" + ] + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "test/**/*" + ] +} diff --git a/packages/misskey-js/LICENSE b/packages/misskey-js/LICENSE index 11c1f9ce22c95f243d991f4247a7d7c086dcec68..63762b85d863722ce5a7f18a4294327aaaa88662 100644 --- a/packages/misskey-js/LICENSE +++ b/packages/misskey-js/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2022 syuilo and other contributors +Copyright (c) 2021-2024 syuilo and other contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/packages/misskey-js/api-extractor.json b/packages/misskey-js/api-extractor.json index a95281a6d559a8e54f617a45901be7a96e7e69fc..f80d0f20a821a45c0c3551b2e9c6e68ca23ce785 100644 --- a/packages/misskey-js/api-extractor.json +++ b/packages/misskey-js/api-extractor.json @@ -45,7 +45,7 @@ * * SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName> */ - "mainEntryPointFilePath": "<projectFolder>/built/index.d.ts", + "mainEntryPointFilePath": "<projectFolder>/built/dts/index.d.ts", /** * A list of NPM package names whose exports should be treated as part of this package. diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index d4c43f207c2c7d9c6fe348964b7af6b84b334997..b17776e8dd636fe63f7db9349f3327451bae4f00 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -85,6 +85,9 @@ type AdminAnnouncementsListResponse = operations['admin/announcements/list']['re // @public (undocumented) type AdminAnnouncementsUpdateRequest = operations['admin/announcements/update']['requestBody']['content']['application/json']; +// @public (undocumented) +type AdminApproveUserRequest = operations['admin/approve-user']['requestBody']['content']['application/json']; + // @public (undocumented) type AdminAvatarDecorationsCreateRequest = operations['admin/avatar-decorations/create']['requestBody']['content']['application/json']; @@ -103,9 +106,6 @@ type AdminAvatarDecorationsUpdateRequest = operations['admin/avatar-decorations/ // @public (undocumented) type AdminDeleteAccountRequest = operations['admin/delete-account']['requestBody']['content']['application/json']; -// @public (undocumented) -type AdminDeleteAccountResponse = operations['admin/delete-account']['responses']['200']['content']['application/json']; - // @public (undocumented) type AdminDeleteAllFilesOfAUserRequest = operations['admin/delete-all-files-of-a-user']['requestBody']['content']['application/json']; @@ -127,6 +127,9 @@ type AdminEmojiAddAliasesBulkRequest = operations['admin/emoji/add-aliases-bulk' // @public (undocumented) type AdminEmojiAddRequest = operations['admin/emoji/add']['requestBody']['content']['application/json']; +// @public (undocumented) +type AdminEmojiAddResponse = operations['admin/emoji/add']['responses']['200']['content']['application/json']; + // @public (undocumented) type AdminEmojiCopyRequest = operations['admin/emoji/copy']['requestBody']['content']['application/json']; @@ -208,6 +211,9 @@ type AdminInviteListResponse = operations['admin/invite/list']['responses']['200 // @public (undocumented) type AdminMetaResponse = operations['admin/meta']['responses']['200']['content']['application/json']; +// @public (undocumented) +type AdminNsfwUserRequest = operations['admin/nsfw-user']['requestBody']['content']['application/json']; + // @public (undocumented) type AdminPromoCreateRequest = operations['admin/promo/create']['requestBody']['content']['application/json']; @@ -304,15 +310,24 @@ type AdminShowUsersRequest = operations['admin/show-users']['requestBody']['cont // @public (undocumented) type AdminShowUsersResponse = operations['admin/show-users']['responses']['200']['content']['application/json']; +// @public (undocumented) +type AdminSilenceUserRequest = operations['admin/silence-user']['requestBody']['content']['application/json']; + // @public (undocumented) type AdminSuspendUserRequest = operations['admin/suspend-user']['requestBody']['content']['application/json']; +// @public (undocumented) +type AdminUnnsfwUserRequest = operations['admin/unnsfw-user']['requestBody']['content']['application/json']; + // @public (undocumented) type AdminUnsetUserAvatarRequest = operations['admin/unset-user-avatar']['requestBody']['content']['application/json']; // @public (undocumented) type AdminUnsetUserBannerRequest = operations['admin/unset-user-banner']['requestBody']['content']['application/json']; +// @public (undocumented) +type AdminUnsilenceUserRequest = operations['admin/unsilence-user']['requestBody']['content']['application/json']; + // @public (undocumented) type AdminUnsuspendUserRequest = operations['admin/unsuspend-user']['requestBody']['content']['application/json']; @@ -473,6 +488,15 @@ type BlockingListRequest = operations['blocking/list']['requestBody']['content'] // @public (undocumented) type BlockingListResponse = operations['blocking/list']['responses']['200']['content']['application/json']; +// @public (undocumented) +type BubbleGameRankingRequest = operations['bubble-game/ranking']['requestBody']['content']['application/json']; + +// @public (undocumented) +type BubbleGameRankingResponse = operations['bubble-game/ranking']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type BubbleGameRegisterRequest = operations['bubble-game/register']['requestBody']['content']['application/json']; + // @public (undocumented) type Channel = components['schemas']['Channel']; @@ -508,10 +532,10 @@ export type Channels = { mention: (payload: Note) => void; reply: (payload: Note) => void; renote: (payload: Note) => void; - follow: (payload: User) => void; - followed: (payload: User) => void; - unfollow: (payload: User) => void; - meUpdated: (payload: MeDetailed) => void; + follow: (payload: UserDetailedNotMe) => void; + followed: (payload: UserDetailed | UserLite) => void; + unfollow: (payload: UserDetailed) => void; + meUpdated: (payload: UserDetailed) => void; pageEvent: (payload: PageEvent) => void; urlUploadFinished: (payload: { marker: string; @@ -521,6 +545,7 @@ export type Channels = { unreadNotification: (payload: Notification_2) => void; unreadMention: (payload: Note['id']) => void; readAllUnreadMentions: () => void; + notificationFlushed: () => void; unreadSpecifiedNote: (payload: Note['id']) => void; readAllUnreadSpecifiedNotes: () => void; readAllAntennas: () => void; @@ -537,6 +562,7 @@ export type Channels = { readAntenna: (payload: Antenna) => void; receiveFollowRequest: (payload: User) => void; announcementCreated: (payload: AnnouncementCreated) => void; + edited: (payload: Note) => void; }; receives: null; }; @@ -544,6 +570,7 @@ export type Channels = { params: { withRenotes?: boolean; withFiles?: boolean; + withBots?: boolean; }; events: { note: (payload: Note) => void; @@ -555,6 +582,7 @@ export type Channels = { withRenotes?: boolean; withReplies?: boolean; withFiles?: boolean; + withBots?: boolean; }; events: { note: (payload: Note) => void; @@ -566,6 +594,7 @@ export type Channels = { withRenotes?: boolean; withReplies?: boolean; withFiles?: boolean; + withBots?: boolean; }; events: { note: (payload: Note) => void; @@ -576,6 +605,18 @@ export type Channels = { params: { withRenotes?: boolean; withFiles?: boolean; + withBots?: boolean; + }; + events: { + note: (payload: Note) => void; + }; + receives: null; + }; + bubbleTimeline: { + params: { + withRenotes?: boolean; + withFiles?: boolean; + withBots?: boolean; }; events: { note: (payload: Note) => void; @@ -586,6 +627,7 @@ export type Channels = { params: { listId: string; withFiles?: boolean; + withRenotes?: boolean; }; events: { note: (payload: Note) => void; @@ -636,7 +678,7 @@ export type Channels = { fileUpdated: (payload: DriveFile) => void; folderCreated: (payload: DriveFolder) => void; folderDeleted: (payload: DriveFolder['id']) => void; - folderUpdated: (payload: DriveFile) => void; + folderUpdated: (payload: DriveFolder) => void; }; receives: null; }; @@ -678,6 +720,46 @@ export type Channels = { }; receives: null; }; + reversiGame: { + params: { + gameId: string; + }; + events: { + started: (payload: { + game: ReversiGameDetailed; + }) => void; + ended: (payload: { + winnerId: User['id'] | null; + game: ReversiGameDetailed; + }) => void; + canceled: (payload: { + userId: User['id']; + }) => void; + changeReadyStates: (payload: { + user1: boolean; + user2: boolean; + }) => void; + updateSettings: (payload: { + userId: User['id']; + key: string; + value: any; + }) => void; + log: (payload: Record<string, any>) => void; + }; + receives: { + putStone: { + pos: number; + id: string; + }; + ready: boolean; + cancel: null | Record<string, never>; + updateSettings: { + key: string; + value: any; + }; + claimTimeIsUp: null | Record<string, never>; + }; + }; }; // @public (undocumented) @@ -1034,6 +1116,18 @@ export type Endpoints = Overwrite<Endpoints_2, { }; }; }; + 'signup': { + req: SignupRequest; + res: SignupResponse; + }; + 'signup-pending': { + req: SignupPendingRequest; + res: SignupPendingResponse; + }; + 'signin': { + req: SigninRequest; + res: SigninResponse; + }; }>; // @public (undocumented) @@ -1053,6 +1147,12 @@ declare namespace entities { EmojiUpdated, EmojiDeleted, AnnouncementCreated, + SignupRequest, + SignupResponse, + SignupPendingRequest, + SignupPendingResponse, + SigninRequest, + SigninResponse, EmptyRequest, EmptyResponse, AdminMetaResponse, @@ -1089,6 +1189,7 @@ declare namespace entities { AdminDriveShowFileResponse, AdminEmojiAddAliasesBulkRequest, AdminEmojiAddRequest, + AdminEmojiAddResponse, AdminEmojiCopyRequest, AdminEmojiCopyResponse, AdminEmojiDeleteBulkRequest, @@ -1135,11 +1236,15 @@ declare namespace entities { AdminShowUserResponse, AdminShowUsersRequest, AdminShowUsersResponse, + AdminNsfwUserRequest, + AdminUnnsfwUserRequest, + AdminSilenceUserRequest, + AdminUnsilenceUserRequest, AdminSuspendUserRequest, + AdminApproveUserRequest, AdminUnsuspendUserRequest, AdminUpdateMetaRequest, AdminDeleteAccountRequest, - AdminDeleteAccountResponse, AdminUpdateUserNoteRequest, AdminRolesCreateRequest, AdminRolesCreateResponse, @@ -1337,6 +1442,7 @@ declare namespace entities { HashtagsUsersResponse, IResponse, I2faDoneRequest, + I2faDoneResponse, I2faKeyDoneRequest, I2faKeyDoneResponse, I2faPasswordLessRequest, @@ -1363,6 +1469,7 @@ declare namespace entities { IGalleryPostsResponse, IImportBlockingRequest, IImportFollowingRequest, + IImportNotesRequest, IImportMutingRequest, IImportUserListsRequest, IImportAntennasRequest, @@ -1380,6 +1487,7 @@ declare namespace entities { IRegenerateTokenRequest, IRegistryGetAllRequest, IRegistryGetAllResponse, + IRegistryGetUnsecureRequest, IRegistryGetDetailRequest, IRegistryGetDetailResponse, IRegistryGetRequest, @@ -1387,6 +1495,7 @@ declare namespace entities { IRegistryKeysWithTypeRequest, IRegistryKeysWithTypeResponse, IRegistryKeysRequest, + IRegistryKeysResponse, IRegistryRemoveRequest, IRegistryScopesWithDomainResponse, IRegistrySetRequest, @@ -1447,6 +1556,8 @@ declare namespace entities { NotesFeaturedResponse, NotesGlobalTimelineRequest, NotesGlobalTimelineResponse, + NotesBubbleTimelineRequest, + NotesBubbleTimelineResponse, NotesHybridTimelineRequest, NotesHybridTimelineResponse, NotesLocalTimelineRequest, @@ -1460,6 +1571,7 @@ declare namespace entities { NotesReactionsResponse, NotesReactionsCreateRequest, NotesReactionsDeleteRequest, + NotesLikeRequest, NotesRenotesRequest, NotesRenotesResponse, NotesRepliesRequest, @@ -1481,6 +1593,10 @@ declare namespace entities { NotesUnrenoteRequest, NotesUserListTimelineRequest, NotesUserListTimelineResponse, + NotesEditRequest, + NotesEditResponse, + NotesVersionsRequest, + NotesVersionsResponse, NotificationsCreateRequest, PagePushRequest, PagesCreateRequest, @@ -1589,6 +1705,21 @@ declare namespace entities { FetchExternalResourcesRequest, FetchExternalResourcesResponse, RetentionResponse, + SponsorsRequest, + BubbleGameRegisterRequest, + BubbleGameRankingRequest, + BubbleGameRankingResponse, + ReversiCancelMatchRequest, + ReversiGamesRequest, + ReversiGamesResponse, + ReversiMatchRequest, + ReversiMatchResponse, + ReversiInvitationsResponse, + ReversiShowGameRequest, + ReversiShowGameResponse, + ReversiSurrenderRequest, + ReversiVerifyRequest, + ReversiVerifyResponse, Error_2 as Error, UserLite, UserDetailedNotMeOnly, @@ -1614,6 +1745,7 @@ declare namespace entities { Hashtag, InviteCode, Page, + PageBlock, Channel, QueueCount, Antenna, @@ -1624,8 +1756,21 @@ declare namespace entities { EmojiDetailed, Flash, Signin, + RoleCondFormulaLogics, + RoleCondFormulaValueNot, + RoleCondFormulaValueIsLocalOrRemote, + RoleCondFormulaValueAssignedRole, + RoleCondFormulaValueCreated, + RoleCondFormulaFollowersOrFollowingOrNotes, + RoleCondFormulaValue, RoleLite, - Role + Role, + RolePolicies, + ReversiGameLite, + ReversiGameDetailed, + MetaLite, + MetaDetailedOnly, + MetaDetailed } } export { entities } @@ -1881,6 +2026,9 @@ type HashtagsUsersResponse = operations['hashtags/users']['responses']['200']['c // @public (undocumented) type I2faDoneRequest = operations['i/2fa/done']['requestBody']['content']['application/json']; +// @public (undocumented) +type I2faDoneResponse = operations['i/2fa/done']['responses']['200']['content']['application/json']; + // @public (undocumented) type I2faKeyDoneRequest = operations['i/2fa/key-done']['requestBody']['content']['application/json']; @@ -1968,6 +2116,9 @@ type IImportFollowingRequest = operations['i/import-following']['requestBody'][' // @public (undocumented) type IImportMutingRequest = operations['i/import-muting']['requestBody']['content']['application/json']; +// @public (undocumented) +type IImportNotesRequest = operations['i/import-notes']['requestBody']['content']['application/json']; + // @public (undocumented) type IImportUserListsRequest = operations['i/import-user-lists']['requestBody']['content']['application/json']; @@ -2049,9 +2200,15 @@ type IRegistryGetRequest = operations['i/registry/get']['requestBody']['content' // @public (undocumented) type IRegistryGetResponse = operations['i/registry/get']['responses']['200']['content']['application/json']; +// @public (undocumented) +type IRegistryGetUnsecureRequest = operations['i/registry/get-unsecure']['requestBody']['content']['application/json']; + // @public (undocumented) type IRegistryKeysRequest = operations['i/registry/keys']['requestBody']['content']['application/json']; +// @public (undocumented) +type IRegistryKeysResponse = operations['i/registry/keys']['responses']['200']['content']['application/json']; + // @public (undocumented) type IRegistryKeysWithTypeRequest = operations['i/registry/keys-with-type']['requestBody']['content']['application/json']; @@ -2074,7 +2231,7 @@ type IResponse = operations['i']['responses']['200']['content']['application/jso type IRevokeTokenRequest = operations['i/revoke-token']['requestBody']['content']['application/json']; // @public (undocumented) -function isAPIError(reason: any): reason is APIError; +function isAPIError(reason: Record<PropertyKey, unknown>): reason is APIError; // @public (undocumented) type ISigninHistoryRequest = operations['i/signin-history']['requestBody']['content']['application/json']; @@ -2127,6 +2284,15 @@ type MeDetailed = components['schemas']['MeDetailed']; // @public (undocumented) type MeDetailedOnly = components['schemas']['MeDetailedOnly']; +// @public (undocumented) +type MetaDetailed = components['schemas']['MetaDetailed']; + +// @public (undocumented) +type MetaDetailedOnly = components['schemas']['MetaDetailedOnly']; + +// @public (undocumented) +type MetaLite = components['schemas']['MetaLite']; + // @public (undocumented) type MetaRequest = operations['meta']['requestBody']['content']['application/json']; @@ -2144,10 +2310,13 @@ type ModerationLog = { id: ID; createdAt: DateString; userId: User['id']; - user: UserDetailed | null; + user: UserDetailedNotMe | null; } & ({ type: 'updateServerSettings'; info: ModerationLogPayloads['updateServerSettings']; +} | { + type: 'approve'; + info: ModerationLogPayloads['approve']; } | { type: 'suspend'; info: ModerationLogPayloads['suspend']; @@ -2220,6 +2389,9 @@ type ModerationLog = { } | { type: 'unsuspendRemoteInstance'; info: ModerationLogPayloads['unsuspendRemoteInstance']; +} | { + type: 'updateRemoteInstanceNote'; + info: ModerationLogPayloads['updateRemoteInstanceNote']; } | { type: 'markSensitiveDriveFile'; info: ModerationLogPayloads['markSensitiveDriveFile']; @@ -2259,7 +2431,7 @@ type ModerationLog = { }); // @public (undocumented) -export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"]; +export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "approve", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "updateRemoteInstanceNote", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"]; // @public (undocumented) type MuteCreateRequest = operations['mute/create']['requestBody']['content']['application/json']; @@ -2294,6 +2466,12 @@ type NoteFavorite = components['schemas']['NoteFavorite']; // @public (undocumented) type NoteReaction = components['schemas']['NoteReaction']; +// @public (undocumented) +type NotesBubbleTimelineRequest = operations['notes/bubble-timeline']['requestBody']['content']['application/json']; + +// @public (undocumented) +type NotesBubbleTimelineResponse = operations['notes/bubble-timeline']['responses']['200']['content']['application/json']; + // @public (undocumented) type NotesChildrenRequest = operations['notes/children']['requestBody']['content']['application/json']; @@ -2321,6 +2499,12 @@ type NotesCreateResponse = operations['notes/create']['responses']['200']['conte // @public (undocumented) type NotesDeleteRequest = operations['notes/delete']['requestBody']['content']['application/json']; +// @public (undocumented) +type NotesEditRequest = operations['notes/edit']['requestBody']['content']['application/json']; + +// @public (undocumented) +type NotesEditResponse = operations['notes/edit']['responses']['200']['content']['application/json']; + // @public (undocumented) type NotesFavoritesCreateRequest = operations['notes/favorites/create']['requestBody']['content']['application/json']; @@ -2345,6 +2529,9 @@ type NotesHybridTimelineRequest = operations['notes/hybrid-timeline']['requestBo // @public (undocumented) type NotesHybridTimelineResponse = operations['notes/hybrid-timeline']['responses']['200']['content']['application/json']; +// @public (undocumented) +type NotesLikeRequest = operations['notes/like']['requestBody']['content']['application/json']; + // @public (undocumented) type NotesLocalTimelineRequest = operations['notes/local-timeline']['requestBody']['content']['application/json']; @@ -2447,6 +2634,12 @@ type NotesUserListTimelineRequest = operations['notes/user-list-timeline']['requ // @public (undocumented) type NotesUserListTimelineResponse = operations['notes/user-list-timeline']['responses']['200']['content']['application/json']; +// @public (undocumented) +type NotesVersionsRequest = operations['notes/versions']['requestBody']['content']['application/json']; + +// @public (undocumented) +type NotesVersionsResponse = operations['notes/versions']['responses']['200']['content']['application/json']; + // @public (undocumented) export const noteVisibilities: readonly ["public", "home", "followers", "specified"]; @@ -2457,11 +2650,14 @@ type Notification_2 = components['schemas']['Notification']; type NotificationsCreateRequest = operations['notifications/create']['requestBody']['content']['application/json']; // @public (undocumented) -export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app", "roleAssigned", "achievementEarned"]; +export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app", "roleAssigned", "achievementEarned", "edited"]; // @public (undocumented) type Page = components['schemas']['Page']; +// @public (undocumented) +type PageBlock = components['schemas']['PageBlock']; + // @public (undocumented) type PageEvent = { pageId: Page['id']; @@ -2505,7 +2701,7 @@ type PagesUpdateRequest = operations['pages/update']['requestBody']['content'][' function parse(acct: string): Acct; // @public (undocumented) -export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "write:admin:delete-account", "write:admin:delete-all-files-of-a-user", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-user", "read:admin:show-users", "write:admin:suspend-user", "write:admin:unset-user-avatar", "write:admin:unset-user-banner", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-note", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"]; +export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "write:admin:delete-account", "write:admin:delete-all-files-of-a-user", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-user", "read:admin:show-users", "write:admin:suspend-user", "write:admin:approve-user", "write:admin:nsfw-user", "write:admin:unnsfw-user", "write:admin:silence-user", "write:admin:unsilence-user", "write:admin:unset-user-avatar", "write:admin:unset-user-banner", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-note", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"]; // @public (undocumented) type PingResponse = operations['ping']['responses']['200']['content']['application/json']; @@ -2536,7 +2732,7 @@ type QueueStats = { }; // @public (undocumented) -type QueueStatsLog = string[]; +type QueueStatsLog = QueueStats[]; // @public (undocumented) type RenoteMuteCreateRequest = operations['renote-mute/create']['requestBody']['content']['application/json']; @@ -2562,12 +2758,75 @@ type ResetPasswordRequest = operations['reset-password']['requestBody']['content // @public (undocumented) type RetentionResponse = operations['retention']['responses']['200']['content']['application/json']; +// @public (undocumented) +type ReversiCancelMatchRequest = operations['reversi/cancel-match']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ReversiGameDetailed = components['schemas']['ReversiGameDetailed']; + +// @public (undocumented) +type ReversiGameLite = components['schemas']['ReversiGameLite']; + +// @public (undocumented) +type ReversiGamesRequest = operations['reversi/games']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ReversiGamesResponse = operations['reversi/games']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ReversiInvitationsResponse = operations['reversi/invitations']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ReversiMatchRequest = operations['reversi/match']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ReversiMatchResponse = operations['reversi/match']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ReversiShowGameRequest = operations['reversi/show-game']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ReversiShowGameResponse = operations['reversi/show-game']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ReversiSurrenderRequest = operations['reversi/surrender']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ReversiVerifyRequest = operations['reversi/verify']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ReversiVerifyResponse = operations['reversi/verify']['responses']['200']['content']['application/json']; + // @public (undocumented) type Role = components['schemas']['Role']; +// @public (undocumented) +type RoleCondFormulaFollowersOrFollowingOrNotes = components['schemas']['RoleCondFormulaFollowersOrFollowingOrNotes']; + +// @public (undocumented) +type RoleCondFormulaLogics = components['schemas']['RoleCondFormulaLogics']; + +// @public (undocumented) +type RoleCondFormulaValue = components['schemas']['RoleCondFormulaValue']; + +// @public (undocumented) +type RoleCondFormulaValueAssignedRole = components['schemas']['RoleCondFormulaValueAssignedRole']; + +// @public (undocumented) +type RoleCondFormulaValueCreated = components['schemas']['RoleCondFormulaValueCreated']; + +// @public (undocumented) +type RoleCondFormulaValueIsLocalOrRemote = components['schemas']['RoleCondFormulaValueIsLocalOrRemote']; + +// @public (undocumented) +type RoleCondFormulaValueNot = components['schemas']['RoleCondFormulaValueNot']; + // @public (undocumented) type RoleLite = components['schemas']['RoleLite']; +// @public (undocumented) +type RolePolicies = components['schemas']['RolePolicies']; + // @public (undocumented) type RolesListResponse = operations['roles/list']['responses']['200']['content']['application/json']; @@ -2610,11 +2869,55 @@ type ServerStats = { }; // @public (undocumented) -type ServerStatsLog = string[]; +type ServerStatsLog = ServerStats[]; // @public (undocumented) type Signin = components['schemas']['Signin']; +// @public (undocumented) +type SigninRequest = { + username: string; + password: string; + token?: string; +}; + +// @public (undocumented) +type SigninResponse = { + id: User['id']; + i: string; +}; + +// @public (undocumented) +type SignupPendingRequest = { + code: string; +}; + +// @public (undocumented) +type SignupPendingResponse = { + id: User['id']; + i: string; +}; + +// @public (undocumented) +type SignupRequest = { + username: string; + password: string; + host?: string; + invitationCode?: string; + emailAddress?: string; + 'hcaptcha-response'?: string | null; + 'g-recaptcha-response'?: string | null; + 'turnstile-response'?: string | null; +}; + +// @public (undocumented) +type SignupResponse = MeDetailed & { + token: string; +}; + +// @public (undocumented) +type SponsorsRequest = operations['sponsors']['requestBody']['content']['application/json']; + // @public (undocumented) type StatsResponse = operations['stats']['responses']['200']['content']['application/json']; diff --git a/packages/misskey-js/generator/package.json b/packages/misskey-js/generator/package.json index 50b23f5792a1cddd4bc8d5595ad6bb6027b49395..a1c0f41cb28526946b06d2276decf9edc3afc5e4 100644 --- a/packages/misskey-js/generator/package.json +++ b/packages/misskey-js/generator/package.json @@ -7,16 +7,17 @@ "generate": "tsx src/generator.ts && eslint ./built/**/* --ext .ts --fix" }, "devDependencies": { - "@apidevtools/swagger-parser": "10.1.0", + "@misskey-dev/eslint-plugin": "^1.0.0", + "@readme/openapi-parser": "2.5.0", "@types/node": "20.9.1", "@typescript-eslint/eslint-plugin": "6.11.0", "@typescript-eslint/parser": "6.11.0", "eslint": "8.53.0", - "typescript": "5.3.3", - "tsx": "4.4.0", - "ts-case-convert": "2.0.2", "openapi-types": "12.1.3", - "openapi-typescript": "6.7.1" + "openapi-typescript": "6.7.3", + "ts-case-convert": "2.0.2", + "tsx": "4.4.0", + "typescript": "5.3.3" }, "files": [ "built" diff --git a/packages/misskey-js/generator/src/generator.ts b/packages/misskey-js/generator/src/generator.ts index f12ed945132467c5a2fc67cf61c89d3297e1eb1e..f091e599a921e9088a136926d43e6c945c5fa6d0 100644 --- a/packages/misskey-js/generator/src/generator.ts +++ b/packages/misskey-js/generator/src/generator.ts @@ -1,27 +1,11 @@ import { mkdir, writeFile } from 'fs/promises'; -import { OpenAPIV3 } from 'openapi-types'; +import { OpenAPIV3_1 } from 'openapi-types'; import { toPascal } from 'ts-case-convert'; -import SwaggerParser from '@apidevtools/swagger-parser'; +import OpenAPIParser from '@readme/openapi-parser'; import openapiTS from 'openapi-typescript'; -function generateVersionHeaderComment(openApiDocs: OpenAPIV3.Document): string { - const contents = { - version: openApiDocs.info.version, - generatedAt: new Date().toISOString(), - }; - - const lines: string[] = []; - lines.push('/*'); - for (const [key, value] of Object.entries(contents)) { - lines.push(` * ${key}: ${value}`); - } - lines.push(' */'); - - return lines.join('\n'); -} - async function generateBaseTypes( - openApiDocs: OpenAPIV3.Document, + openApiDocs: OpenAPIV3_1.Document, openApiJsonPath: string, typeFileName: string, ) { @@ -36,9 +20,6 @@ async function generateBaseTypes( } lines.push(''); - lines.push(generateVersionHeaderComment(openApiDocs)); - lines.push(''); - const generatedTypes = await openapiTS(openApiJsonPath, { exportType: true }); lines.push(generatedTypes); lines.push(''); @@ -47,7 +28,7 @@ async function generateBaseTypes( } async function generateSchemaEntities( - openApiDocs: OpenAPIV3.Document, + openApiDocs: OpenAPIV3_1.Document, typeFileName: string, outputPath: string, ) { @@ -59,8 +40,6 @@ async function generateSchemaEntities( const schemaNames = Object.keys(schemas); const typeAliasLines: string[] = []; - typeAliasLines.push(generateVersionHeaderComment(openApiDocs)); - typeAliasLines.push(''); typeAliasLines.push(`import { components } from '${toImportPath(typeFileName)}';`); typeAliasLines.push( ...schemaNames.map(it => `export type ${it} = components['schemas']['${it}'];`), @@ -71,7 +50,7 @@ async function generateSchemaEntities( } async function generateEndpoints( - openApiDocs: OpenAPIV3.Document, + openApiDocs: OpenAPIV3_1.Document, typeFileName: string, entitiesOutputPath: string, endpointOutputPath: string, @@ -79,7 +58,7 @@ async function generateEndpoints( const endpoints: Endpoint[] = []; // misskey-jsã¯POST固定ã§é€ã£ã¦ã„ã‚‹ã®ã§ã€ã“ã¡ã‚‰ã‚‚決ã‚打ã¡ã™ã‚‹ã€‚別メソッドã«å¯¾å¿œã™ã‚‹ã“ã¨ãŒã‚ã‚Œã°ã“ã¡ã‚‰ã‚‚ç›´ã™å¿…è¦ã‚ã‚Š - const paths = openApiDocs.paths; + const paths = openApiDocs.paths ?? {}; const postPathItems = Object.keys(paths) .map(it => paths[it]?.post) .filter(filterUndefined); @@ -119,9 +98,6 @@ async function generateEndpoints( const entitiesOutputLine: string[] = []; - entitiesOutputLine.push(generateVersionHeaderComment(openApiDocs)); - entitiesOutputLine.push(''); - entitiesOutputLine.push(`import { operations } from '${toImportPath(typeFileName)}';`); entitiesOutputLine.push(''); @@ -139,9 +115,6 @@ async function generateEndpoints( const endpointOutputLine: string[] = []; - endpointOutputLine.push(generateVersionHeaderComment(openApiDocs)); - endpointOutputLine.push(''); - endpointOutputLine.push('import type {'); endpointOutputLine.push( ...[emptyRequest, emptyResponse, ...entities].map(it => '\t' + it.generateName() + ','), @@ -160,7 +133,7 @@ async function generateEndpoints( } async function generateApiClientJSDoc( - openApiDocs: OpenAPIV3.Document, + openApiDocs: OpenAPIV3_1.Document, apiClientFileName: string, endpointsFileName: string, warningsOutputPath: string, @@ -168,7 +141,7 @@ async function generateApiClientJSDoc( const endpoints: { operationId: string; description: string; }[] = []; // misskey-jsã¯POST固定ã§é€ã£ã¦ã„ã‚‹ã®ã§ã€ã“ã¡ã‚‰ã‚‚決ã‚打ã¡ã™ã‚‹ã€‚別メソッドã«å¯¾å¿œã™ã‚‹ã“ã¨ãŒã‚ã‚Œã°ã“ã¡ã‚‰ã‚‚ç›´ã™å¿…è¦ã‚ã‚Š - const paths = openApiDocs.paths; + const paths = openApiDocs.paths ?? {}; const postPathItems = Object.keys(paths) .map(it => paths[it]?.post) .filter(filterUndefined); @@ -187,9 +160,6 @@ async function generateApiClientJSDoc( const endpointOutputLine: string[] = []; - endpointOutputLine.push(generateVersionHeaderComment(openApiDocs)); - endpointOutputLine.push(''); - endpointOutputLine.push(`import type { SwitchCaseResponseType } from '${toImportPath(apiClientFileName)}';`); endpointOutputLine.push(`import type { Endpoints } from '${toImportPath(endpointsFileName)}';`); endpointOutputLine.push(''); @@ -221,21 +191,21 @@ async function generateApiClientJSDoc( await writeFile(warningsOutputPath, endpointOutputLine.join('\n')); } -function isRequestBodyObject(value: unknown): value is OpenAPIV3.RequestBodyObject { +function isRequestBodyObject(value: unknown): value is OpenAPIV3_1.RequestBodyObject { if (!value) { return false; } - const { content } = value as Record<keyof OpenAPIV3.RequestBodyObject, unknown>; + const { content } = value as Record<keyof OpenAPIV3_1.RequestBodyObject, unknown>; return content !== undefined; } -function isResponseObject(value: unknown): value is OpenAPIV3.ResponseObject { +function isResponseObject(value: unknown): value is OpenAPIV3_1.ResponseObject { if (!value) { return false; } - const { description } = value as Record<keyof OpenAPIV3.ResponseObject, unknown>; + const { description } = value as Record<keyof OpenAPIV3_1.ResponseObject, unknown>; return description !== undefined; } @@ -330,7 +300,7 @@ async function main() { await mkdir(generatePath, { recursive: true }); const openApiJsonPath = './api.json'; - const openApiDocs = await SwaggerParser.validate(openApiJsonPath) as OpenAPIV3.Document; + const openApiDocs = await OpenAPIParser.parse(openApiJsonPath) as OpenAPIV3_1.Document; const typeFileName = './built/autogen/types.ts'; await generateBaseTypes(openApiDocs, openApiJsonPath, typeFileName); diff --git a/packages/misskey-js/jest.config.cjs b/packages/misskey-js/jest.config.cjs index e5a74170ea0bcaa854a146dfe819cdacc296831a..1230a4b5e28eae02082bd361ea1bdb402ea614d4 100644 --- a/packages/misskey-js/jest.config.cjs +++ b/packages/misskey-js/jest.config.cjs @@ -81,7 +81,17 @@ module.exports = { // ], // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module - // moduleNameMapper: {}, + moduleNameMapper: { + // Do not resolve .wasm.js to .wasm by the rule below + '^(.+)\\.wasm\\.js$': '$1.wasm.js', + // SWC converts @/foo/bar.js to `../../src/foo/bar.js`, and then this rule + // converts it again to `../../src/foo/bar` which then can be resolved to + // `.ts` files. + // See https://github.com/swc-project/jest/issues/64#issuecomment-1029753225 + // TODO: Use `--allowImportingTsExtensions` on TypeScript 5.0 so that we can + // directly import `.ts` files without this hack. + '^((?:\\.{1,2}|[A-Z:])*/.*)\\.js$': '$1', + }, // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader // modulePathIgnorePatterns: [], diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 53d5044d68a0eeba932e916a4622cd61918372de..772f001c0734eb4af94538ed102266b693bf3963 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,12 +1,25 @@ { + "type": "module", "name": "misskey-js", - "version": "0.0.16", + "version": "2024.3.1", "description": "Misskey SDK for JavaScript", - "main": "./built/index.js", - "types": "./built/index.d.ts", + "types": "./built/dts/index.d.ts", + "exports": { + ".": { + "import": "./built/esm/index.js", + "types": "./built/dts/index.d.ts" + }, + "./*": { + "import": "./built/esm/*", + "types": "./built/dts/*" + } + }, "scripts": { - "build": "tsc", - "watch": "nodemon -w src -e ts,js,cjs,mjs,json --exec \"pnpm run build\"", + "build": "npm run ts", + "ts": "npm run ts-esm && npm run ts-dts", + "ts-esm": "tsc --outDir built/esm", + "ts-dts": "tsc --outDir built/dts --declaration true --emitDeclarationOnly true --declarationMap true", + "watch": "nodemon -w src -e ts,js,cjs,mjs,json --exec \"pnpm run ts\"", "tsd": "tsd", "api": "pnpm api-extractor run --local --verbose", "api-prod": "pnpm api-extractor run --verbose", @@ -22,28 +35,31 @@ "url": "git+https://github.com/misskey-dev/misskey.js.git" }, "devDependencies": { - "@microsoft/api-extractor": "7.38.5", - "@swc/jest": "0.2.29", - "@types/jest": "29.5.11", - "@types/node": "20.10.5", - "@typescript-eslint/eslint-plugin": "6.14.0", - "@typescript-eslint/parser": "6.14.0", - "eslint": "8.56.0", + "@microsoft/api-extractor": "7.39.1", + "@misskey-dev/eslint-plugin": "1.0.0", + "@swc/jest": "0.2.31", + "@types/jest": "29.5.12", + "@types/node": "20.11.22", + "@typescript-eslint/eslint-plugin": "7.1.0", + "@typescript-eslint/parser": "7.1.0", + "eslint": "8.57.0", "jest": "29.7.0", "jest-fetch-mock": "3.0.3", "jest-websocket-mock": "2.5.0", "mock-socket": "9.3.1", "ncp": "2.0.0", - "nodemon": "3.0.2", - "tsd": "0.30.0", + "nodemon": "3.1.0", + "tsd": "0.30.7", "typescript": "5.3.3" }, "files": [ - "built" + "built", + "built/esm", + "built/dts" ], "dependencies": { "@swc/cli": "0.1.63", - "@swc/core": "1.3.100", + "@swc/core": "1.3.105", "eventemitter3": "5.0.1", "reconnecting-websocket": "4.4.0" } diff --git a/packages/misskey-js/src/api.ts b/packages/misskey-js/src/api.ts index 0d10faaadadae146a9cfde88e5306fd59af9a660..134ead0d795939b1bec8339d47f9b7c102d95e3e 100644 --- a/packages/misskey-js/src/api.ts +++ b/packages/misskey-js/src/api.ts @@ -1,11 +1,11 @@ -import './autogen/apiClientJSDoc'; +import './autogen/apiClientJSDoc.js'; -import { SwitchCaseResponseType } from './api.types'; -import type { Endpoints } from './api.types'; +import { SwitchCaseResponseType } from './api.types.js'; +import type { Endpoints } from './api.types.js'; export { SwitchCaseResponseType, -} from './api.types'; +} from './api.types.js'; const MK_API_ERROR = Symbol(); @@ -17,7 +17,7 @@ export type APIError = { info: Record<string, any>; }; -export function isAPIError(reason: any): reason is APIError { +export function isAPIError(reason: Record<PropertyKey, unknown>): reason is APIError { return reason[MK_API_ERROR] === true; } diff --git a/packages/misskey-js/src/api.types.ts b/packages/misskey-js/src/api.types.ts index d97646b7ccebd73573018c21b7fd08e8a4e10f5a..af0bade5b3b41861aca32adcf71975dd8a3b694f 100644 --- a/packages/misskey-js/src/api.types.ts +++ b/packages/misskey-js/src/api.types.ts @@ -1,16 +1,24 @@ -import { Endpoints as Gen } from './autogen/endpoint'; -import { UserDetailed } from './autogen/models'; -import { UsersShowRequest } from './autogen/entities'; +import { Endpoints as Gen } from './autogen/endpoint.js'; +import { UserDetailed } from './autogen/models.js'; +import { UsersShowRequest } from './autogen/entities.js'; +import { + SigninRequest, + SigninResponse, + SignupPendingRequest, + SignupPendingResponse, + SignupRequest, + SignupResponse, +} from './entities.js'; type Overwrite<T, U extends { [Key in keyof T]?: unknown }> = Omit< T, keyof U > & U; -type SwitchCase = { +type SwitchCase<Condition = unknown, Result = unknown> = { $switch: { - $cases: [any, any][], - $default: any; + $cases: [Condition, Result][], + $default: Result; }; }; @@ -55,6 +63,21 @@ export type Endpoints = Overwrite< $default: UserDetailed; }; }; - } + }, + // api.jsonã«ã¯è¼‰ã›ãªã„ã‚‚ã®ãªã®ã§ã“ã“ã§å®šç¾© + 'signup': { + req: SignupRequest; + res: SignupResponse; + }, + // api.jsonã«ã¯è¼‰ã›ãªã„ã‚‚ã®ãªã®ã§ã“ã“ã§å®šç¾© + 'signup-pending': { + req: SignupPendingRequest; + res: SignupPendingResponse; + }, + // api.jsonã«ã¯è¼‰ã›ãªã„ã‚‚ã®ãªã®ã§ã“ã“ã§å®šç¾© + 'signin': { + req: SigninRequest; + res: SigninResponse; + }, } > diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts index 758beaf3a022d94ea5d713a3c602e9688016bca5..1796148530e949c7d2b958ceeb68d9dc8fc2e260 100644 --- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts +++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts @@ -1,8 +1,3 @@ -/* - * version: 2023.12.0 - * generatedAt: 2023-12-26T23:35:09.494Z - */ - import type { SwitchCaseResponseType } from '../api.js'; import type { Endpoints } from './endpoint.js'; @@ -33,7 +28,6 @@ declare module '../api.js' { /** * No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. * **Credential required**: *No* */ request<E extends 'admin/accounts/create', P extends Endpoints[E]['req']>( @@ -692,6 +686,50 @@ declare module '../api.js' { credential?: string | null, ): Promise<SwitchCaseResponseType<E, P>>; + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:nsfw-user* + */ + request<E extends 'admin/nsfw-user', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:unnsfw-user* + */ + request<E extends 'admin/unnsfw-user', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:silence-user* + */ + request<E extends 'admin/silence-user', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:unsilence-user* + */ + request<E extends 'admin/unsilence-user', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + /** * No description provided. * @@ -703,6 +741,17 @@ declare module '../api.js' { credential?: string | null, ): Promise<SwitchCaseResponseType<E, P>>; + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:approve-user* + */ + request<E extends 'admin/approve-user', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + /** * No description provided. * @@ -2202,6 +2251,18 @@ declare module '../api.js' { credential?: string | null, ): Promise<SwitchCaseResponseType<E, P>>; + /** + * No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + request<E extends 'i/export-data', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + /** * No description provided. * @@ -2250,6 +2311,18 @@ declare module '../api.js' { credential?: string | null, ): Promise<SwitchCaseResponseType<E, P>>; + /** + * No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + request<E extends 'i/export-clips', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + /** * No description provided. * @@ -2343,6 +2416,18 @@ declare module '../api.js' { credential?: string | null, ): Promise<SwitchCaseResponseType<E, P>>; + /** + * No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + request<E extends 'i/import-notes', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + /** * No description provided. * @@ -2479,6 +2564,17 @@ declare module '../api.js' { credential?: string | null, ): Promise<SwitchCaseResponseType<E, P>>; + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:account* + */ + request<E extends 'i/registry/get-unsecure', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + /** * No description provided. * @@ -2958,6 +3054,17 @@ declare module '../api.js' { credential?: string | null, ): Promise<SwitchCaseResponseType<E, P>>; + /** + * No description provided. + * + * **Credential required**: *No* + */ + request<E extends 'notes/bubble-timeline', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + /** * No description provided. * @@ -3046,6 +3153,17 @@ declare module '../api.js' { credential?: string | null, ): Promise<SwitchCaseResponseType<E, P>>; + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:reactions* + */ + request<E extends 'notes/like', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + /** * No description provided. * @@ -3178,6 +3296,28 @@ declare module '../api.js' { credential?: string | null, ): Promise<SwitchCaseResponseType<E, P>>; + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:notes* + */ + request<E extends 'notes/edit', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *No* + */ + request<E extends 'notes/versions', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + /** * No description provided. * @@ -3189,6 +3329,17 @@ declare module '../api.js' { credential?: string | null, ): Promise<SwitchCaseResponseType<E, P>>; + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:notifications* + */ + request<E extends 'notifications/flush', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + /** * No description provided. * @@ -3974,5 +4125,115 @@ declare module '../api.js' { params: P, credential?: string | null, ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * Get Sharkey GH Sponsors + * + * **Credential required**: *No* + */ + request<E extends 'sponsors', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:account* + */ + request<E extends 'bubble-game/register', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *No* + */ + request<E extends 'bubble-game/ranking', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:account* + */ + request<E extends 'reversi/cancel-match', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *No* + */ + request<E extends 'reversi/games', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:account* + */ + request<E extends 'reversi/match', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:account* + */ + request<E extends 'reversi/invitations', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *No* + */ + request<E extends 'reversi/show-game', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:account* + */ + request<E extends 'reversi/surrender', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; + + /** + * No description provided. + * + * **Credential required**: *No* + */ + request<E extends 'reversi/verify', P extends Endpoints[E]['req']>( + endpoint: E, + params: P, + credential?: string | null, + ): Promise<SwitchCaseResponseType<E, P>>; } } diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index 2ed76a22f96416339b116535216084e8ecf04cc9..e223f5faf75a3003dbfe6ceaa302cdee0a0bec93 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -1,8 +1,3 @@ -/* - * version: 2023.12.0 - * generatedAt: 2023-12-26T23:35:09.491Z - */ - import type { EmptyRequest, EmptyResponse, @@ -40,6 +35,7 @@ import type { AdminDriveShowFileResponse, AdminEmojiAddAliasesBulkRequest, AdminEmojiAddRequest, + AdminEmojiAddResponse, AdminEmojiCopyRequest, AdminEmojiCopyResponse, AdminEmojiDeleteBulkRequest, @@ -86,11 +82,15 @@ import type { AdminShowUserResponse, AdminShowUsersRequest, AdminShowUsersResponse, + AdminNsfwUserRequest, + AdminUnnsfwUserRequest, + AdminSilenceUserRequest, + AdminUnsilenceUserRequest, AdminSuspendUserRequest, + AdminApproveUserRequest, AdminUnsuspendUserRequest, AdminUpdateMetaRequest, AdminDeleteAccountRequest, - AdminDeleteAccountResponse, AdminUpdateUserNoteRequest, AdminRolesCreateRequest, AdminRolesCreateResponse, @@ -288,6 +288,7 @@ import type { HashtagsUsersResponse, IResponse, I2faDoneRequest, + I2faDoneResponse, I2faKeyDoneRequest, I2faKeyDoneResponse, I2faPasswordLessRequest, @@ -314,6 +315,7 @@ import type { IGalleryPostsResponse, IImportBlockingRequest, IImportFollowingRequest, + IImportNotesRequest, IImportMutingRequest, IImportUserListsRequest, IImportAntennasRequest, @@ -331,6 +333,7 @@ import type { IRegenerateTokenRequest, IRegistryGetAllRequest, IRegistryGetAllResponse, + IRegistryGetUnsecureRequest, IRegistryGetDetailRequest, IRegistryGetDetailResponse, IRegistryGetRequest, @@ -338,6 +341,7 @@ import type { IRegistryKeysWithTypeRequest, IRegistryKeysWithTypeResponse, IRegistryKeysRequest, + IRegistryKeysResponse, IRegistryRemoveRequest, IRegistryScopesWithDomainResponse, IRegistrySetRequest, @@ -398,6 +402,8 @@ import type { NotesFeaturedResponse, NotesGlobalTimelineRequest, NotesGlobalTimelineResponse, + NotesBubbleTimelineRequest, + NotesBubbleTimelineResponse, NotesHybridTimelineRequest, NotesHybridTimelineResponse, NotesLocalTimelineRequest, @@ -411,6 +417,7 @@ import type { NotesReactionsResponse, NotesReactionsCreateRequest, NotesReactionsDeleteRequest, + NotesLikeRequest, NotesRenotesRequest, NotesRenotesResponse, NotesRepliesRequest, @@ -432,6 +439,10 @@ import type { NotesUnrenoteRequest, NotesUserListTimelineRequest, NotesUserListTimelineResponse, + NotesEditRequest, + NotesEditResponse, + NotesVersionsRequest, + NotesVersionsResponse, NotificationsCreateRequest, PagePushRequest, PagesCreateRequest, @@ -540,6 +551,21 @@ import type { FetchExternalResourcesRequest, FetchExternalResourcesResponse, RetentionResponse, + SponsorsRequest, + BubbleGameRegisterRequest, + BubbleGameRankingRequest, + BubbleGameRankingResponse, + ReversiCancelMatchRequest, + ReversiGamesRequest, + ReversiGamesResponse, + ReversiMatchRequest, + ReversiMatchResponse, + ReversiInvitationsResponse, + ReversiShowGameRequest, + ReversiShowGameResponse, + ReversiSurrenderRequest, + ReversiVerifyRequest, + ReversiVerifyResponse, } from './entities.js'; export type Endpoints = { @@ -568,7 +594,7 @@ export type Endpoints = { 'admin/drive/files': { req: AdminDriveFilesRequest; res: AdminDriveFilesResponse }; 'admin/drive/show-file': { req: AdminDriveShowFileRequest; res: AdminDriveShowFileResponse }; 'admin/emoji/add-aliases-bulk': { req: AdminEmojiAddAliasesBulkRequest; res: EmptyResponse }; - 'admin/emoji/add': { req: AdminEmojiAddRequest; res: EmptyResponse }; + 'admin/emoji/add': { req: AdminEmojiAddRequest; res: AdminEmojiAddResponse }; 'admin/emoji/copy': { req: AdminEmojiCopyRequest; res: AdminEmojiCopyResponse }; 'admin/emoji/delete-bulk': { req: AdminEmojiDeleteBulkRequest; res: EmptyResponse }; 'admin/emoji/delete': { req: AdminEmojiDeleteRequest; res: EmptyResponse }; @@ -605,10 +631,15 @@ export type Endpoints = { 'admin/show-moderation-logs': { req: AdminShowModerationLogsRequest; res: AdminShowModerationLogsResponse }; 'admin/show-user': { req: AdminShowUserRequest; res: AdminShowUserResponse }; 'admin/show-users': { req: AdminShowUsersRequest; res: AdminShowUsersResponse }; + 'admin/nsfw-user': { req: AdminNsfwUserRequest; res: EmptyResponse }; + 'admin/unnsfw-user': { req: AdminUnnsfwUserRequest; res: EmptyResponse }; + 'admin/silence-user': { req: AdminSilenceUserRequest; res: EmptyResponse }; + 'admin/unsilence-user': { req: AdminUnsilenceUserRequest; res: EmptyResponse }; 'admin/suspend-user': { req: AdminSuspendUserRequest; res: EmptyResponse }; + 'admin/approve-user': { req: AdminApproveUserRequest; res: EmptyResponse }; 'admin/unsuspend-user': { req: AdminUnsuspendUserRequest; res: EmptyResponse }; 'admin/update-meta': { req: AdminUpdateMetaRequest; res: EmptyResponse }; - 'admin/delete-account': { req: AdminDeleteAccountRequest; res: AdminDeleteAccountResponse }; + 'admin/delete-account': { req: AdminDeleteAccountRequest; res: EmptyResponse }; 'admin/update-user-note': { req: AdminUpdateUserNoteRequest; res: EmptyResponse }; 'admin/roles/create': { req: AdminRolesCreateRequest; res: AdminRolesCreateResponse }; 'admin/roles/delete': { req: AdminRolesDeleteRequest; res: EmptyResponse }; @@ -728,7 +759,7 @@ export type Endpoints = { 'hashtags/trend': { req: EmptyRequest; res: HashtagsTrendResponse }; 'hashtags/users': { req: HashtagsUsersRequest; res: HashtagsUsersResponse }; 'i': { req: EmptyRequest; res: IResponse }; - 'i/2fa/done': { req: I2faDoneRequest; res: EmptyResponse }; + 'i/2fa/done': { req: I2faDoneRequest; res: I2faDoneResponse }; 'i/2fa/key-done': { req: I2faKeyDoneRequest; res: I2faKeyDoneResponse }; 'i/2fa/password-less': { req: I2faPasswordLessRequest; res: EmptyResponse }; 'i/2fa/register-key': { req: I2faRegisterKeyRequest; res: I2faRegisterKeyResponse }; @@ -741,10 +772,12 @@ export type Endpoints = { 'i/claim-achievement': { req: IClaimAchievementRequest; res: EmptyResponse }; 'i/change-password': { req: IChangePasswordRequest; res: EmptyResponse }; 'i/delete-account': { req: IDeleteAccountRequest; res: EmptyResponse }; + 'i/export-data': { req: EmptyRequest; res: EmptyResponse }; 'i/export-blocking': { req: EmptyRequest; res: EmptyResponse }; 'i/export-following': { req: IExportFollowingRequest; res: EmptyResponse }; 'i/export-mute': { req: EmptyRequest; res: EmptyResponse }; 'i/export-notes': { req: EmptyRequest; res: EmptyResponse }; + 'i/export-clips': { req: EmptyRequest; res: EmptyResponse }; 'i/export-favorites': { req: EmptyRequest; res: EmptyResponse }; 'i/export-user-lists': { req: EmptyRequest; res: EmptyResponse }; 'i/export-antennas': { req: EmptyRequest; res: EmptyResponse }; @@ -753,6 +786,7 @@ export type Endpoints = { 'i/gallery/posts': { req: IGalleryPostsRequest; res: IGalleryPostsResponse }; 'i/import-blocking': { req: IImportBlockingRequest; res: EmptyResponse }; 'i/import-following': { req: IImportFollowingRequest; res: EmptyResponse }; + 'i/import-notes': { req: IImportNotesRequest; res: EmptyResponse }; 'i/import-muting': { req: IImportMutingRequest; res: EmptyResponse }; 'i/import-user-lists': { req: IImportUserListsRequest; res: EmptyResponse }; 'i/import-antennas': { req: IImportAntennasRequest; res: EmptyResponse }; @@ -765,10 +799,11 @@ export type Endpoints = { 'i/read-announcement': { req: IReadAnnouncementRequest; res: EmptyResponse }; 'i/regenerate-token': { req: IRegenerateTokenRequest; res: EmptyResponse }; 'i/registry/get-all': { req: IRegistryGetAllRequest; res: IRegistryGetAllResponse }; + 'i/registry/get-unsecure': { req: IRegistryGetUnsecureRequest; res: EmptyResponse }; 'i/registry/get-detail': { req: IRegistryGetDetailRequest; res: IRegistryGetDetailResponse }; 'i/registry/get': { req: IRegistryGetRequest; res: IRegistryGetResponse }; 'i/registry/keys-with-type': { req: IRegistryKeysWithTypeRequest; res: IRegistryKeysWithTypeResponse }; - 'i/registry/keys': { req: IRegistryKeysRequest; res: EmptyResponse }; + 'i/registry/keys': { req: IRegistryKeysRequest; res: IRegistryKeysResponse }; 'i/registry/remove': { req: IRegistryRemoveRequest; res: EmptyResponse }; 'i/registry/scopes-with-domain': { req: EmptyRequest; res: IRegistryScopesWithDomainResponse }; 'i/registry/set': { req: IRegistrySetRequest; res: EmptyResponse }; @@ -808,6 +843,7 @@ export type Endpoints = { 'notes/favorites/delete': { req: NotesFavoritesDeleteRequest; res: EmptyResponse }; 'notes/featured': { req: NotesFeaturedRequest; res: NotesFeaturedResponse }; 'notes/global-timeline': { req: NotesGlobalTimelineRequest; res: NotesGlobalTimelineResponse }; + 'notes/bubble-timeline': { req: NotesBubbleTimelineRequest; res: NotesBubbleTimelineResponse }; 'notes/hybrid-timeline': { req: NotesHybridTimelineRequest; res: NotesHybridTimelineResponse }; 'notes/local-timeline': { req: NotesLocalTimelineRequest; res: NotesLocalTimelineResponse }; 'notes/mentions': { req: NotesMentionsRequest; res: NotesMentionsResponse }; @@ -816,6 +852,7 @@ export type Endpoints = { 'notes/reactions': { req: NotesReactionsRequest; res: NotesReactionsResponse }; 'notes/reactions/create': { req: NotesReactionsCreateRequest; res: EmptyResponse }; 'notes/reactions/delete': { req: NotesReactionsDeleteRequest; res: EmptyResponse }; + 'notes/like': { req: NotesLikeRequest; res: EmptyResponse }; 'notes/renotes': { req: NotesRenotesRequest; res: NotesRenotesResponse }; 'notes/replies': { req: NotesRepliesRequest; res: NotesRepliesResponse }; 'notes/search-by-tag': { req: NotesSearchByTagRequest; res: NotesSearchByTagResponse }; @@ -828,7 +865,10 @@ export type Endpoints = { 'notes/translate': { req: NotesTranslateRequest; res: NotesTranslateResponse }; 'notes/unrenote': { req: NotesUnrenoteRequest; res: EmptyResponse }; 'notes/user-list-timeline': { req: NotesUserListTimelineRequest; res: NotesUserListTimelineResponse }; + 'notes/edit': { req: NotesEditRequest; res: NotesEditResponse }; + 'notes/versions': { req: NotesVersionsRequest; res: NotesVersionsResponse }; 'notifications/create': { req: NotificationsCreateRequest; res: EmptyResponse }; + 'notifications/flush': { req: EmptyRequest; res: EmptyResponse }; 'notifications/mark-all-as-read': { req: EmptyRequest; res: EmptyResponse }; 'notifications/test-notification': { req: EmptyRequest; res: EmptyResponse }; 'page-push': { req: PagePushRequest; res: EmptyResponse }; @@ -900,4 +940,14 @@ export type Endpoints = { 'fetch-rss': { req: FetchRssRequest; res: FetchRssResponse }; 'fetch-external-resources': { req: FetchExternalResourcesRequest; res: FetchExternalResourcesResponse }; 'retention': { req: EmptyRequest; res: RetentionResponse }; + 'sponsors': { req: SponsorsRequest; res: EmptyResponse }; + 'bubble-game/register': { req: BubbleGameRegisterRequest; res: EmptyResponse }; + 'bubble-game/ranking': { req: BubbleGameRankingRequest; res: BubbleGameRankingResponse }; + 'reversi/cancel-match': { req: ReversiCancelMatchRequest; res: EmptyResponse }; + 'reversi/games': { req: ReversiGamesRequest; res: ReversiGamesResponse }; + 'reversi/match': { req: ReversiMatchRequest; res: ReversiMatchResponse }; + 'reversi/invitations': { req: EmptyRequest; res: ReversiInvitationsResponse }; + 'reversi/show-game': { req: ReversiShowGameRequest; res: ReversiShowGameResponse }; + 'reversi/surrender': { req: ReversiSurrenderRequest; res: EmptyResponse }; + 'reversi/verify': { req: ReversiVerifyRequest; res: ReversiVerifyResponse }; } diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index c857e8e370065db0f10ce6b42e614986cfc22df0..70568ac6908f8cde92851ff41e1c3c0294a0d372 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -1,8 +1,3 @@ -/* - * version: 2023.12.0 - * generatedAt: 2023-12-26T23:35:09.489Z - */ - import { operations } from './types.js'; export type EmptyRequest = Record<string, unknown> | undefined; @@ -42,6 +37,7 @@ export type AdminDriveShowFileRequest = operations['admin/drive/show-file']['req export type AdminDriveShowFileResponse = operations['admin/drive/show-file']['responses']['200']['content']['application/json']; export type AdminEmojiAddAliasesBulkRequest = operations['admin/emoji/add-aliases-bulk']['requestBody']['content']['application/json']; export type AdminEmojiAddRequest = operations['admin/emoji/add']['requestBody']['content']['application/json']; +export type AdminEmojiAddResponse = operations['admin/emoji/add']['responses']['200']['content']['application/json']; export type AdminEmojiCopyRequest = operations['admin/emoji/copy']['requestBody']['content']['application/json']; export type AdminEmojiCopyResponse = operations['admin/emoji/copy']['responses']['200']['content']['application/json']; export type AdminEmojiDeleteBulkRequest = operations['admin/emoji/delete-bulk']['requestBody']['content']['application/json']; @@ -88,11 +84,15 @@ export type AdminShowUserRequest = operations['admin/show-user']['requestBody'][ export type AdminShowUserResponse = operations['admin/show-user']['responses']['200']['content']['application/json']; export type AdminShowUsersRequest = operations['admin/show-users']['requestBody']['content']['application/json']; export type AdminShowUsersResponse = operations['admin/show-users']['responses']['200']['content']['application/json']; +export type AdminNsfwUserRequest = operations['admin/nsfw-user']['requestBody']['content']['application/json']; +export type AdminUnnsfwUserRequest = operations['admin/unnsfw-user']['requestBody']['content']['application/json']; +export type AdminSilenceUserRequest = operations['admin/silence-user']['requestBody']['content']['application/json']; +export type AdminUnsilenceUserRequest = operations['admin/unsilence-user']['requestBody']['content']['application/json']; export type AdminSuspendUserRequest = operations['admin/suspend-user']['requestBody']['content']['application/json']; +export type AdminApproveUserRequest = operations['admin/approve-user']['requestBody']['content']['application/json']; export type AdminUnsuspendUserRequest = operations['admin/unsuspend-user']['requestBody']['content']['application/json']; export type AdminUpdateMetaRequest = operations['admin/update-meta']['requestBody']['content']['application/json']; export type AdminDeleteAccountRequest = operations['admin/delete-account']['requestBody']['content']['application/json']; -export type AdminDeleteAccountResponse = operations['admin/delete-account']['responses']['200']['content']['application/json']; export type AdminUpdateUserNoteRequest = operations['admin/update-user-note']['requestBody']['content']['application/json']; export type AdminRolesCreateRequest = operations['admin/roles/create']['requestBody']['content']['application/json']; export type AdminRolesCreateResponse = operations['admin/roles/create']['responses']['200']['content']['application/json']; @@ -290,6 +290,7 @@ export type HashtagsUsersRequest = operations['hashtags/users']['requestBody'][' export type HashtagsUsersResponse = operations['hashtags/users']['responses']['200']['content']['application/json']; export type IResponse = operations['i']['responses']['200']['content']['application/json']; export type I2faDoneRequest = operations['i/2fa/done']['requestBody']['content']['application/json']; +export type I2faDoneResponse = operations['i/2fa/done']['responses']['200']['content']['application/json']; export type I2faKeyDoneRequest = operations['i/2fa/key-done']['requestBody']['content']['application/json']; export type I2faKeyDoneResponse = operations['i/2fa/key-done']['responses']['200']['content']['application/json']; export type I2faPasswordLessRequest = operations['i/2fa/password-less']['requestBody']['content']['application/json']; @@ -316,6 +317,7 @@ export type IGalleryPostsRequest = operations['i/gallery/posts']['requestBody'][ export type IGalleryPostsResponse = operations['i/gallery/posts']['responses']['200']['content']['application/json']; export type IImportBlockingRequest = operations['i/import-blocking']['requestBody']['content']['application/json']; export type IImportFollowingRequest = operations['i/import-following']['requestBody']['content']['application/json']; +export type IImportNotesRequest = operations['i/import-notes']['requestBody']['content']['application/json']; export type IImportMutingRequest = operations['i/import-muting']['requestBody']['content']['application/json']; export type IImportUserListsRequest = operations['i/import-user-lists']['requestBody']['content']['application/json']; export type IImportAntennasRequest = operations['i/import-antennas']['requestBody']['content']['application/json']; @@ -333,6 +335,7 @@ export type IReadAnnouncementRequest = operations['i/read-announcement']['reques export type IRegenerateTokenRequest = operations['i/regenerate-token']['requestBody']['content']['application/json']; export type IRegistryGetAllRequest = operations['i/registry/get-all']['requestBody']['content']['application/json']; export type IRegistryGetAllResponse = operations['i/registry/get-all']['responses']['200']['content']['application/json']; +export type IRegistryGetUnsecureRequest = operations['i/registry/get-unsecure']['requestBody']['content']['application/json']; export type IRegistryGetDetailRequest = operations['i/registry/get-detail']['requestBody']['content']['application/json']; export type IRegistryGetDetailResponse = operations['i/registry/get-detail']['responses']['200']['content']['application/json']; export type IRegistryGetRequest = operations['i/registry/get']['requestBody']['content']['application/json']; @@ -340,6 +343,7 @@ export type IRegistryGetResponse = operations['i/registry/get']['responses']['20 export type IRegistryKeysWithTypeRequest = operations['i/registry/keys-with-type']['requestBody']['content']['application/json']; export type IRegistryKeysWithTypeResponse = operations['i/registry/keys-with-type']['responses']['200']['content']['application/json']; export type IRegistryKeysRequest = operations['i/registry/keys']['requestBody']['content']['application/json']; +export type IRegistryKeysResponse = operations['i/registry/keys']['responses']['200']['content']['application/json']; export type IRegistryRemoveRequest = operations['i/registry/remove']['requestBody']['content']['application/json']; export type IRegistryScopesWithDomainResponse = operations['i/registry/scopes-with-domain']['responses']['200']['content']['application/json']; export type IRegistrySetRequest = operations['i/registry/set']['requestBody']['content']['application/json']; @@ -400,6 +404,8 @@ export type NotesFeaturedRequest = operations['notes/featured']['requestBody'][' export type NotesFeaturedResponse = operations['notes/featured']['responses']['200']['content']['application/json']; export type NotesGlobalTimelineRequest = operations['notes/global-timeline']['requestBody']['content']['application/json']; export type NotesGlobalTimelineResponse = operations['notes/global-timeline']['responses']['200']['content']['application/json']; +export type NotesBubbleTimelineRequest = operations['notes/bubble-timeline']['requestBody']['content']['application/json']; +export type NotesBubbleTimelineResponse = operations['notes/bubble-timeline']['responses']['200']['content']['application/json']; export type NotesHybridTimelineRequest = operations['notes/hybrid-timeline']['requestBody']['content']['application/json']; export type NotesHybridTimelineResponse = operations['notes/hybrid-timeline']['responses']['200']['content']['application/json']; export type NotesLocalTimelineRequest = operations['notes/local-timeline']['requestBody']['content']['application/json']; @@ -413,6 +419,7 @@ export type NotesReactionsRequest = operations['notes/reactions']['requestBody'] export type NotesReactionsResponse = operations['notes/reactions']['responses']['200']['content']['application/json']; export type NotesReactionsCreateRequest = operations['notes/reactions/create']['requestBody']['content']['application/json']; export type NotesReactionsDeleteRequest = operations['notes/reactions/delete']['requestBody']['content']['application/json']; +export type NotesLikeRequest = operations['notes/like']['requestBody']['content']['application/json']; export type NotesRenotesRequest = operations['notes/renotes']['requestBody']['content']['application/json']; export type NotesRenotesResponse = operations['notes/renotes']['responses']['200']['content']['application/json']; export type NotesRepliesRequest = operations['notes/replies']['requestBody']['content']['application/json']; @@ -434,6 +441,10 @@ export type NotesTranslateResponse = operations['notes/translate']['responses'][ export type NotesUnrenoteRequest = operations['notes/unrenote']['requestBody']['content']['application/json']; export type NotesUserListTimelineRequest = operations['notes/user-list-timeline']['requestBody']['content']['application/json']; export type NotesUserListTimelineResponse = operations['notes/user-list-timeline']['responses']['200']['content']['application/json']; +export type NotesEditRequest = operations['notes/edit']['requestBody']['content']['application/json']; +export type NotesEditResponse = operations['notes/edit']['responses']['200']['content']['application/json']; +export type NotesVersionsRequest = operations['notes/versions']['requestBody']['content']['application/json']; +export type NotesVersionsResponse = operations['notes/versions']['responses']['200']['content']['application/json']; export type NotificationsCreateRequest = operations['notifications/create']['requestBody']['content']['application/json']; export type PagePushRequest = operations['page-push']['requestBody']['content']['application/json']; export type PagesCreateRequest = operations['pages/create']['requestBody']['content']['application/json']; @@ -542,3 +553,18 @@ export type FetchRssResponse = operations['fetch-rss']['responses']['200']['cont export type FetchExternalResourcesRequest = operations['fetch-external-resources']['requestBody']['content']['application/json']; export type FetchExternalResourcesResponse = operations['fetch-external-resources']['responses']['200']['content']['application/json']; export type RetentionResponse = operations['retention']['responses']['200']['content']['application/json']; +export type SponsorsRequest = operations['sponsors']['requestBody']['content']['application/json']; +export type BubbleGameRegisterRequest = operations['bubble-game/register']['requestBody']['content']['application/json']; +export type BubbleGameRankingRequest = operations['bubble-game/ranking']['requestBody']['content']['application/json']; +export type BubbleGameRankingResponse = operations['bubble-game/ranking']['responses']['200']['content']['application/json']; +export type ReversiCancelMatchRequest = operations['reversi/cancel-match']['requestBody']['content']['application/json']; +export type ReversiGamesRequest = operations['reversi/games']['requestBody']['content']['application/json']; +export type ReversiGamesResponse = operations['reversi/games']['responses']['200']['content']['application/json']; +export type ReversiMatchRequest = operations['reversi/match']['requestBody']['content']['application/json']; +export type ReversiMatchResponse = operations['reversi/match']['responses']['200']['content']['application/json']; +export type ReversiInvitationsResponse = operations['reversi/invitations']['responses']['200']['content']['application/json']; +export type ReversiShowGameRequest = operations['reversi/show-game']['requestBody']['content']['application/json']; +export type ReversiShowGameResponse = operations['reversi/show-game']['responses']['200']['content']['application/json']; +export type ReversiSurrenderRequest = operations['reversi/surrender']['requestBody']['content']['application/json']; +export type ReversiVerifyRequest = operations['reversi/verify']['requestBody']['content']['application/json']; +export type ReversiVerifyResponse = operations['reversi/verify']['responses']['200']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts index c5b81a6b41caff098570e51ab07202973bc1e23e..6f61458600f4f560ef95f5b8519c86a268f3b786 100644 --- a/packages/misskey-js/src/autogen/models.ts +++ b/packages/misskey-js/src/autogen/models.ts @@ -1,8 +1,3 @@ -/* - * version: 2023.12.0 - * generatedAt: 2023-12-26T23:35:09.485Z - */ - import { components } from './types.js'; export type Error = components['schemas']['Error']; export type UserLite = components['schemas']['UserLite']; @@ -29,6 +24,7 @@ export type Blocking = components['schemas']['Blocking']; export type Hashtag = components['schemas']['Hashtag']; export type InviteCode = components['schemas']['InviteCode']; export type Page = components['schemas']['Page']; +export type PageBlock = components['schemas']['PageBlock']; export type Channel = components['schemas']['Channel']; export type QueueCount = components['schemas']['QueueCount']; export type Antenna = components['schemas']['Antenna']; @@ -39,5 +35,18 @@ export type EmojiSimple = components['schemas']['EmojiSimple']; export type EmojiDetailed = components['schemas']['EmojiDetailed']; export type Flash = components['schemas']['Flash']; export type Signin = components['schemas']['Signin']; +export type RoleCondFormulaLogics = components['schemas']['RoleCondFormulaLogics']; +export type RoleCondFormulaValueNot = components['schemas']['RoleCondFormulaValueNot']; +export type RoleCondFormulaValueIsLocalOrRemote = components['schemas']['RoleCondFormulaValueIsLocalOrRemote']; +export type RoleCondFormulaValueAssignedRole = components['schemas']['RoleCondFormulaValueAssignedRole']; +export type RoleCondFormulaValueCreated = components['schemas']['RoleCondFormulaValueCreated']; +export type RoleCondFormulaFollowersOrFollowingOrNotes = components['schemas']['RoleCondFormulaFollowersOrFollowingOrNotes']; +export type RoleCondFormulaValue = components['schemas']['RoleCondFormulaValue']; export type RoleLite = components['schemas']['RoleLite']; export type Role = components['schemas']['Role']; +export type RolePolicies = components['schemas']['RolePolicies']; +export type ReversiGameLite = components['schemas']['ReversiGameLite']; +export type ReversiGameDetailed = components['schemas']['ReversiGameDetailed']; +export type MetaLite = components['schemas']['MetaLite']; +export type MetaDetailedOnly = components['schemas']['MetaDetailedOnly']; +export type MetaDetailed = components['schemas']['MetaDetailed']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 94bb2639809f7549435563d5b3c7c5847bd2f581..6b4ee9361e14a2949f55a04a7b8b62581562e6ed 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -1,11 +1,6 @@ /* eslint @typescript-eslint/naming-convention: 0 */ /* eslint @typescript-eslint/no-explicit-any: 0 */ -/* - * version: 2023.12.0 - * generatedAt: 2023-12-26T23:35:09.389Z - */ - /** * This file was auto-generated by openapi-typescript. * Do not make direct changes to the file. @@ -40,7 +35,6 @@ export type paths = { * admin/accounts/create * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. * **Credential required**: *No* */ post: operations['admin/accounts/create']; @@ -577,6 +571,42 @@ export type paths = { */ post: operations['admin/show-users']; }; + '/admin/nsfw-user': { + /** + * admin/nsfw-user + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:nsfw-user* + */ + post: operations['admin/nsfw-user']; + }; + '/admin/unnsfw-user': { + /** + * admin/unnsfw-user + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:unnsfw-user* + */ + post: operations['admin/unnsfw-user']; + }; + '/admin/silence-user': { + /** + * admin/silence-user + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:silence-user* + */ + post: operations['admin/silence-user']; + }; + '/admin/unsilence-user': { + /** + * admin/unsilence-user + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:unsilence-user* + */ + post: operations['admin/unsilence-user']; + }; '/admin/suspend-user': { /** * admin/suspend-user @@ -586,6 +616,15 @@ export type paths = { */ post: operations['admin/suspend-user']; }; + '/admin/approve-user': { + /** + * admin/approve-user + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:approve-user* + */ + post: operations['admin/approve-user']; + }; '/admin/unsuspend-user': { /** * admin/unsuspend-user @@ -1927,6 +1966,16 @@ export type paths = { */ post: operations['i/delete-account']; }; + '/i/export-data': { + /** + * i/export-data + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + post: operations['i/export-data']; + }; '/i/export-blocking': { /** * i/export-blocking @@ -1967,6 +2016,16 @@ export type paths = { */ post: operations['i/export-notes']; }; + '/i/export-clips': { + /** + * i/export-clips + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + post: operations['i/export-clips']; + }; '/i/export-favorites': { /** * i/export-favorites @@ -2044,6 +2103,16 @@ export type paths = { */ post: operations['i/import-following']; }; + '/i/import-notes': { + /** + * i/import-notes + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + post: operations['i/import-notes']; + }; '/i/import-muting': { /** * i/import-muting @@ -2156,6 +2225,15 @@ export type paths = { */ post: operations['i/registry/get-all']; }; + '/i/registry/get-unsecure': { + /** + * i/registry/get-unsecure + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:account* + */ + post: operations['i/registry/get-unsecure']; + }; '/i/registry/get-detail': { /** * i/registry/get-detail @@ -2570,6 +2648,15 @@ export type paths = { */ post: operations['notes/global-timeline']; }; + '/notes/bubble-timeline': { + /** + * notes/bubble-timeline + * @description No description provided. + * + * **Credential required**: *No* + */ + post: operations['notes/bubble-timeline']; + }; '/notes/hybrid-timeline': { /** * notes/hybrid-timeline @@ -2649,6 +2736,15 @@ export type paths = { */ post: operations['notes/reactions/delete']; }; + '/notes/like': { + /** + * notes/like + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:reactions* + */ + post: operations['notes/like']; + }; '/notes/renotes': { /** * notes/renotes @@ -2757,6 +2853,24 @@ export type paths = { */ post: operations['notes/user-list-timeline']; }; + '/notes/edit': { + /** + * notes/edit + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:notes* + */ + post: operations['notes/edit']; + }; + '/notes/versions': { + /** + * notes/versions + * @description No description provided. + * + * **Credential required**: *No* + */ + post: operations['notes/versions']; + }; '/notifications/create': { /** * notifications/create @@ -2766,6 +2880,15 @@ export type paths = { */ post: operations['notifications/create']; }; + '/notifications/flush': { + /** + * notifications/flush + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:notifications* + */ + post: operations['notifications/flush']; + }; '/notifications/mark-all-as-read': { /** * notifications/mark-all-as-read @@ -3438,6 +3561,103 @@ export type paths = { */ post: operations['retention']; }; + '/sponsors': { + /** + * sponsors + * @description Get Sharkey GH Sponsors + * + * **Credential required**: *No* + */ + post: operations['sponsors']; + }; + '/bubble-game/register': { + /** + * bubble-game/register + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:account* + */ + post: operations['bubble-game/register']; + }; + '/bubble-game/ranking': { + /** + * bubble-game/ranking + * @description No description provided. + * + * **Credential required**: *No* + */ + get: operations['bubble-game/ranking']; + /** + * bubble-game/ranking + * @description No description provided. + * + * **Credential required**: *No* + */ + post: operations['bubble-game/ranking']; + }; + '/reversi/cancel-match': { + /** + * reversi/cancel-match + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:account* + */ + post: operations['reversi/cancel-match']; + }; + '/reversi/games': { + /** + * reversi/games + * @description No description provided. + * + * **Credential required**: *No* + */ + post: operations['reversi/games']; + }; + '/reversi/match': { + /** + * reversi/match + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:account* + */ + post: operations['reversi/match']; + }; + '/reversi/invitations': { + /** + * reversi/invitations + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:account* + */ + post: operations['reversi/invitations']; + }; + '/reversi/show-game': { + /** + * reversi/show-game + * @description No description provided. + * + * **Credential required**: *No* + */ + post: operations['reversi/show-game']; + }; + '/reversi/surrender': { + /** + * reversi/surrender + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:account* + */ + post: operations['reversi/surrender']; + }; + '/reversi/verify': { + /** + * reversi/verify + * @description No description provided. + * + * **Credential required**: *No* + */ + post: operations['reversi/verify']; + }; }; export type webhooks = Record<string, never>; @@ -3486,8 +3706,15 @@ export type components = { offsetX?: number; offsetY?: number; }[]; + /** @default false */ + isAdmin?: boolean; + /** @default false */ + isModerator?: boolean; + isSilenced: boolean; + noindex: boolean; isBot?: boolean; isCat?: boolean; + speakAsCat?: boolean; instance?: { name: string | null; softwareName: string | null; @@ -3496,7 +3723,9 @@ export type components = { faviconUrl: string | null; themeColor: string | null; }; - emojis: Record<string, never>; + emojis: { + [key: string]: string; + }; /** @enum {string} */ onlineStatus: 'unknown' | 'online' | 'active' | 'offline'; badgeRoles?: ({ @@ -3522,8 +3751,10 @@ export type components = { /** Format: url */ bannerUrl: string | null; bannerBlurhash: string | null; + /** Format: url */ + backgroundUrl: string | null; + backgroundBlurhash: string | null; isLocked: boolean; - isSilenced: boolean; /** @example false */ isSuspended: boolean; /** @example Hi masters, I am Ai! */ @@ -3531,6 +3762,8 @@ export type components = { location: string | null; /** @example 2018-03-12 */ birthday: string | null; + /** @example Steve */ + ListenBrainz: string | null; /** @example ja-JP */ lang: string | null; fields: { @@ -3576,6 +3809,8 @@ export type components = { avatarId: string | null; /** Format: id */ bannerId: string | null; + /** Format: id */ + backgroundId: string | null; isModerator: boolean | null; isAdmin: boolean | null; injectFeaturedNote: boolean; @@ -3604,42 +3839,132 @@ export type components = { hardMutedWords: string[][]; mutedInstances: string[] | null; notificationRecieveConfig: { - app?: { + note?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never'; - }; - quote?: { + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never'; - }; - reply?: { + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + follow?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never'; - }; - follow?: { + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never'; - }; - renote?: { + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + mention?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never'; - }; - mention?: { + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never'; - }; - reaction?: { + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + reply?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never'; - }; - pollEnded?: { + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never'; - }; - receiveFollowRequest?: { + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + renote?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never'; - }; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + quote?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + reaction?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + pollEnded?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + receiveFollowRequest?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + followRequestAccepted?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + roleAssigned?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + achievementEarned?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + app?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + test?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; }; emailNotificationTypes: string[]; achievements: { @@ -3647,32 +3972,7 @@ export type components = { unlockedAt: number; }[]; loggedInDays: number; - policies: { - gtlAvailable: boolean; - ltlAvailable: boolean; - canPublicNote: boolean; - canInvite: boolean; - inviteLimit: number; - inviteLimitCycle: number; - inviteExpirationTime: number; - canManageCustomEmojis: boolean; - canManageAvatarDecorations: boolean; - canSearchNotes: boolean; - canUseTranslator: boolean; - canHideAds: boolean; - driveCapacityMb: number; - alwaysMarkNsfw: boolean; - pinLimit: number; - antennaLimit: number; - wordMuteLimit: number; - webhookLimit: number; - clipLimit: number; - noteEachClipsLimit: number; - userListLimit: number; - userEachUserListsLimit: number; - rateLimitFactor: number; - avatarDecorationLimit: number; - }; + policies: components['schemas']['RolePolicies']; email?: string | null; emailVerified?: boolean | null; securityKeysList?: { @@ -3689,7 +3989,7 @@ export type components = { UserDetailedNotMe: components['schemas']['UserLite'] & components['schemas']['UserDetailedNotMeOnly']; MeDetailed: components['schemas']['UserLite'] & components['schemas']['UserDetailedNotMeOnly'] & components['schemas']['MeDetailedOnly']; UserDetailed: components['schemas']['UserDetailedNotMe'] | components['schemas']['MeDetailed']; - User: components['schemas']['UserLite'] | components['schemas']['UserDetailed'] | components['schemas']['UserDetailedNotMe'] | components['schemas']['MeDetailed']; + User: components['schemas']['UserLite'] | components['schemas']['UserDetailed']; UserList: { /** * Format: id @@ -3733,8 +4033,10 @@ export type components = { text: string; title: string; imageUrl: string | null; - icon: string; - display: string; + /** @enum {string} */ + icon: 'info' | 'warning' | 'error' | 'success'; + /** @enum {string} */ + display: 'dialog' | 'normal' | 'banner'; needConfirmationToRead: boolean; silence: boolean; forYou: boolean; @@ -3776,35 +4078,54 @@ export type components = { reply?: components['schemas']['Note'] | null; renote?: components['schemas']['Note'] | null; isHidden?: boolean; - visibility: string; + /** @enum {string} */ + visibility: 'public' | 'home' | 'followers' | 'specified'; mentions?: string[]; visibleUserIds?: string[]; fileIds?: string[]; files?: components['schemas']['DriveFile'][]; tags?: string[]; - poll?: Record<string, unknown> | null; + poll?: ({ + /** Format: date-time */ + expiresAt?: string | null; + multiple: boolean; + choices: { + isVoted: boolean; + text: string; + votes: number; + }[]; + }) | null; + emojis?: { + [key: string]: string; + }; /** * Format: id * @example xxxxxxxxxx */ channelId?: string | null; - channel?: { + channel?: ({ id: string; name: string; color: string; isSensitive: boolean; allowRenoteToExternal: boolean; - } | null; + userId: string | null; + }) | null; localOnly?: boolean; reactionAcceptance: string | null; - reactions: Record<string, never>; + reactionEmojis: { + [key: string]: string; + }; + reactions: { + [key: string]: number; + }; renoteCount: number; repliesCount: number; uri?: string; url?: string; reactionAndUserPairCache?: string[]; clippedCount?: number; - myReaction?: Record<string, unknown> | null; + myReaction?: string | null; }; NoteReaction: { /** @@ -3835,65 +4156,217 @@ export type components = { /** Format: date-time */ createdAt: string; /** @enum {string} */ - type: 'note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped'; - user?: components['schemas']['UserLite'] | null; + type: 'note'; + user: components['schemas']['UserLite']; + /** Format: id */ + userId: string; + note: components['schemas']['Note']; + } | { /** Format: id */ - userId?: string | null; - note?: components['schemas']['Note'] | null; - reaction?: string | null; - achievement?: string; - body?: string | null; - header?: string | null; - icon?: string | null; - reactions?: { - user: components['schemas']['UserLite']; - reaction: string; - }[] | null; - users?: components['schemas']['UserLite'][] | null; - }; - DriveFile: { - /** - * Format: id - * @example xxxxxxxxxx - */ id: string; /** Format: date-time */ createdAt: string; - /** @example lenna.jpg */ - name: string; - /** @example image/jpeg */ - type: string; - /** - * Format: md5 - * @example 15eca7fba0480996e2245f5185bf39f2 - */ - md5: string; - /** @example 51469 */ - size: number; - isSensitive: boolean; - blurhash: string | null; - properties: { - /** @example 1280 */ - width?: number; - /** @example 720 */ - height?: number; - /** @example 8 */ - orientation?: number; - /** @example rgb(40,65,87) */ - avgColor?: string; - }; - /** Format: url */ - url: string; - /** Format: url */ - thumbnailUrl: string | null; - comment: string | null; - /** - * Format: id - * @example xxxxxxxxxx - */ - folderId: string | null; - folder?: components['schemas']['DriveFolder'] | null; - /** + /** @enum {string} */ + type: 'mention'; + user: components['schemas']['UserLite']; + /** Format: id */ + userId: string; + note: components['schemas']['Note']; + } | { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @enum {string} */ + type: 'reply'; + user: components['schemas']['UserLite']; + /** Format: id */ + userId: string; + note: components['schemas']['Note']; + } | { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @enum {string} */ + type: 'renote'; + user: components['schemas']['UserLite']; + /** Format: id */ + userId: string; + note: components['schemas']['Note']; + } | { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @enum {string} */ + type: 'quote'; + user: components['schemas']['UserLite']; + /** Format: id */ + userId: string; + note: components['schemas']['Note']; + } | { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @enum {string} */ + type: 'reaction'; + user: components['schemas']['UserLite']; + /** Format: id */ + userId: string; + note: components['schemas']['Note']; + reaction: string; + } | { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @enum {string} */ + type: 'pollEnded'; + user: components['schemas']['UserLite']; + /** Format: id */ + userId: string; + note: components['schemas']['Note']; + } | { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @enum {string} */ + type: 'follow'; + user: components['schemas']['UserLite']; + /** Format: id */ + userId: string; + } | { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @enum {string} */ + type: 'receiveFollowRequest'; + user: components['schemas']['UserLite']; + /** Format: id */ + userId: string; + } | { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @enum {string} */ + type: 'followRequestAccepted'; + user: components['schemas']['UserLite']; + /** Format: id */ + userId: string; + } | { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @enum {string} */ + type: 'roleAssigned'; + role: components['schemas']['Role']; + } | { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @enum {string} */ + type: 'achievementEarned'; + achievement: string; + } | { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @enum {string} */ + type: 'app'; + body: string; + header: string; + icon: string; + } | { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @enum {string} */ + type: 'edited'; + user: components['schemas']['UserLite']; + /** Format: id */ + userId: string; + note: components['schemas']['Note']; + } | { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @enum {string} */ + type: 'reaction:grouped'; + note: components['schemas']['Note']; + reactions: { + user: components['schemas']['UserLite']; + reaction: string; + }[]; + } | { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @enum {string} */ + type: 'renote:grouped'; + note: components['schemas']['Note']; + users: components['schemas']['UserLite'][]; + } | { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @enum {string} */ + type: 'test'; + }; + DriveFile: { + /** + * Format: id + * @example xxxxxxxxxx + */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @example lenna.jpg */ + name: string; + /** @example image/jpeg */ + type: string; + /** + * Format: md5 + * @example 15eca7fba0480996e2245f5185bf39f2 + */ + md5: string; + /** @example 51469 */ + size: number; + isSensitive: boolean; + blurhash: string | null; + properties: { + /** @example 1280 */ + width?: number; + /** @example 720 */ + height?: number; + /** @example 8 */ + orientation?: number; + /** @example rgb(40,65,87) */ + avgColor?: string; + }; + /** Format: url */ + url: string; + /** Format: url */ + thumbnailUrl: string | null; + comment: string | null; + /** + * Format: id + * @example xxxxxxxxxx + */ + folderId: string | null; + folder?: components['schemas']['DriveFolder'] | null; + /** * Format: id * @example xxxxxxxxxx */ @@ -3930,8 +4403,8 @@ export type components = { followeeId: string; /** Format: id */ followerId: string; - followee?: components['schemas']['UserDetailed']; - follower?: components['schemas']['UserDetailed']; + followee?: components['schemas']['UserDetailedNotMe']; + follower?: components['schemas']['UserDetailedNotMe']; }; Muting: { /** @@ -3945,7 +4418,7 @@ export type components = { expiresAt: string | null; /** Format: id */ muteeId: string; - mutee: components['schemas']['UserDetailed']; + mutee: components['schemas']['UserDetailedNotMe']; }; RenoteMuting: { /** @@ -3957,7 +4430,7 @@ export type components = { createdAt: string; /** Format: id */ muteeId: string; - mutee: components['schemas']['UserDetailed']; + mutee: components['schemas']['UserDetailedNotMe']; }; Blocking: { /** @@ -3969,7 +4442,7 @@ export type components = { createdAt: string; /** Format: id */ blockeeId: string; - blockee: components['schemas']['UserDetailed']; + blockee: components['schemas']['UserDetailedNotMe']; }; Hashtag: { /** @example misskey */ @@ -4012,7 +4485,7 @@ export type components = { /** Format: id */ userId: string; user: components['schemas']['UserLite']; - content: Record<string, never>[]; + content: components['schemas']['PageBlock'][]; variables: Record<string, never>[]; title: string; name: string; @@ -4027,6 +4500,29 @@ export type components = { likedCount: number; isLiked?: boolean; }; + PageBlock: OneOf<[{ + id: string; + /** @enum {string} */ + type: 'text'; + text: string; + }, { + id: string; + /** @enum {string} */ + type: 'section'; + title: string; + children: components['schemas']['PageBlock'][]; + }, { + id: string; + /** @enum {string} */ + type: 'image'; + fileId: string | null; + }, { + id: string; + /** @enum {string} */ + type: 'note'; + detailed: boolean; + note: string | null; + }]>; Channel: { /** * Format: id @@ -4138,6 +4634,8 @@ export type components = { infoUpdatedAt: string | null; /** Format: date-time */ latestRequestReceivedAt: string | null; + isNSFW: boolean; + moderationNote?: string | null; }; GalleryPost: { /** @@ -4166,6 +4664,7 @@ export type components = { name: string; category: string | null; url: string; + localOnly?: boolean; isSensitive?: boolean; roleIdsThatCanBeUsedThisEmojiAsReaction?: string[]; }; @@ -4210,6 +4709,46 @@ export type components = { headers: Record<string, never>; success: boolean; }; + RoleCondFormulaLogics: { + id: string; + /** @enum {string} */ + type: 'and' | 'or'; + values: components['schemas']['RoleCondFormulaValue'][]; + }; + RoleCondFormulaValueNot: { + id: string; + /** @enum {string} */ + type: 'not'; + value: components['schemas']['RoleCondFormulaValue']; + }; + RoleCondFormulaValueIsLocalOrRemote: { + id: string; + /** @enum {string} */ + type: 'isLocal' | 'isRemote'; + }; + RoleCondFormulaValueAssignedRole: { + id: string; + /** @enum {string} */ + type: 'roleAssignedTo'; + /** + * Format: id + * @example xxxxxxxxxx + */ + roleId: string; + }; + RoleCondFormulaValueCreated: { + id: string; + /** @enum {string} */ + type: 'createdLessThan' | 'createdMoreThan'; + sec: number; + }; + RoleCondFormulaFollowersOrFollowingOrNotes: { + id: string; + /** @enum {string} */ + type: 'followersLessThanOrEq' | 'followersMoreThanOrEq' | 'followingLessThanOrEq' | 'followingMoreThanOrEq' | 'notesLessThanOrEq' | 'notesMoreThanOrEq'; + value: number; + }; + RoleCondFormulaValue: components['schemas']['RoleCondFormulaLogics'] | components['schemas']['RoleCondFormulaValueNot'] | components['schemas']['RoleCondFormulaValueIsLocalOrRemote'] | components['schemas']['RoleCondFormulaValueAssignedRole'] | components['schemas']['RoleCondFormulaValueCreated'] | components['schemas']['RoleCondFormulaFollowersOrFollowingOrNotes']; RoleLite: { /** * Format: id @@ -4236,7 +4775,7 @@ export type components = { updatedAt: string; /** @enum {string} */ target: 'manual' | 'conditional'; - condFormula: Record<string, never>; + condFormula: components['schemas']['RoleCondFormulaValue']; /** @example false */ isPublic: boolean; /** @example false */ @@ -4246,129 +4785,206 @@ export type components = { /** @example false */ canEditMembersByModerator: boolean; policies: { - pinLimit: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - canInvite: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - clipLimit: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - canHideAds: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - inviteLimit: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - antennaLimit: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - gtlAvailable: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - ltlAvailable: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - webhookLimit: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - canPublicNote: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - userListLimit: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - wordMuteLimit: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - alwaysMarkNsfw: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - canSearchNotes: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - driveCapacityMb: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - rateLimitFactor: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - inviteLimitCycle: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - noteEachClipsLimit: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - inviteExpirationTime: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - canManageCustomEmojis: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - userEachUserListsLimit: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - canManageAvatarDecorations: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - canUseTranslator: { - value: number | boolean; - priority: number; - useDefault: boolean; - }; - avatarDecorationLimit: { - value: number | boolean; - priority: number; - useDefault: boolean; + [key: string]: { + value?: number | boolean; + priority?: number; + useDefault?: boolean; }; }; usersCount: number; }); + RolePolicies: { + gtlAvailable: boolean; + ltlAvailable: boolean; + btlAvailable: boolean; + canPublicNote: boolean; + mentionLimit: number; + canInvite: boolean; + inviteLimit: number; + inviteLimitCycle: number; + inviteExpirationTime: number; + canManageCustomEmojis: boolean; + canManageAvatarDecorations: boolean; + canSearchNotes: boolean; + canUseTranslator: boolean; + canHideAds: boolean; + driveCapacityMb: number; + alwaysMarkNsfw: boolean; + pinLimit: number; + antennaLimit: number; + wordMuteLimit: number; + webhookLimit: number; + clipLimit: number; + noteEachClipsLimit: number; + userListLimit: number; + userEachUserListsLimit: number; + rateLimitFactor: number; + avatarDecorationLimit: number; + }; + ReversiGameLite: { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + startedAt: string | null; + /** Format: date-time */ + endedAt: string | null; + isStarted: boolean; + isEnded: boolean; + /** Format: id */ + user1Id: string; + /** Format: id */ + user2Id: string; + user1: components['schemas']['UserLite']; + user2: components['schemas']['UserLite']; + /** Format: id */ + winnerId: string | null; + winner: components['schemas']['UserLite'] | null; + /** Format: id */ + surrenderedUserId: string | null; + /** Format: id */ + timeoutUserId: string | null; + black: number | null; + bw: string; + noIrregularRules: boolean; + isLlotheo: boolean; + canPutEverywhere: boolean; + loopedBoard: boolean; + timeLimitForEachTurn: number; + }; + ReversiGameDetailed: { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + startedAt: string | null; + /** Format: date-time */ + endedAt: string | null; + isStarted: boolean; + isEnded: boolean; + form1: Record<string, never> | null; + form2: Record<string, never> | null; + user1Ready: boolean; + user2Ready: boolean; + /** Format: id */ + user1Id: string; + /** Format: id */ + user2Id: string; + user1: components['schemas']['UserLite']; + user2: components['schemas']['UserLite']; + /** Format: id */ + winnerId: string | null; + winner: components['schemas']['UserLite'] | null; + /** Format: id */ + surrenderedUserId: string | null; + /** Format: id */ + timeoutUserId: string | null; + black: number | null; + bw: string; + noIrregularRules: boolean; + isLlotheo: boolean; + canPutEverywhere: boolean; + loopedBoard: boolean; + timeLimitForEachTurn: number; + logs: number[][]; + map: string[]; + }; + MetaLite: { + maintainerName: string | null; + maintainerEmail: string | null; + version: string; + providesTarball: boolean; + name: string | null; + shortName: string | null; + /** + * Format: url + * @example https://misskey.example.com + */ + uri: string; + description: string | null; + langs: string[]; + tosUrl: string | null; + /** @default https://github.com/misskey-dev/misskey */ + repositoryUrl: string | null; + /** @default https://github.com/misskey-dev/misskey/issues/new */ + feedbackUrl: string | null; + donationUrl: string | null; + defaultDarkTheme: string | null; + defaultLightTheme: string | null; + defaultLike: string | null; + disableRegistration: boolean; + emailRequiredForSignup: boolean; + approvalRequiredForSignup: boolean; + enableHcaptcha: boolean; + hcaptchaSiteKey: string | null; + enableMcaptcha: boolean; + mcaptchaSiteKey: string | null; + mcaptchaInstanceUrl: string | null; + enableRecaptcha: boolean; + recaptchaSiteKey: string | null; + enableTurnstile: boolean; + turnstileSiteKey: string | null; + enableAchievements: boolean | null; + swPublickey: string | null; + /** @default /assets/ai.png */ + mascotImageUrl: string; + bannerUrl: string | null; + serverErrorImageUrl: string | null; + infoImageUrl: string | null; + notFoundImageUrl: string | null; + iconUrl: string | null; + maxNoteTextLength: number; + ads: { + /** + * Format: id + * @example xxxxxxxxxx + */ + id: string; + /** Format: url */ + url: string; + place: string; + ratio: number; + /** Format: url */ + imageUrl: string; + dayOfWeek: number; + }[]; + /** @default 0 */ + notesPerOneAd: number; + enableEmail: boolean; + enableServiceWorker: boolean; + translatorAvailable: boolean; + mediaProxy: string; + backgroundImageUrl: string | null; + impressumUrl: string | null; + logoImageUrl: string | null; + privacyPolicyUrl: string | null; + serverRules: string[]; + themeColor: string | null; + policies: components['schemas']['RolePolicies']; + }; + MetaDetailedOnly: { + features?: { + registration: boolean; + emailRequiredForSignup: boolean; + localTimeline: boolean; + globalTimeline: boolean; + hcaptcha: boolean; + turnstile: boolean; + recaptcha: boolean; + objectStorage: boolean; + serviceWorker: boolean; + /** @default true */ + miauth?: boolean; + }; + proxyAccountName: string | null; + /** @example false */ + requireSetup: boolean; + cacheRemoteFiles: boolean; + cacheRemoteSensitiveFiles: boolean; + }; + MetaDetailed: components['schemas']['MetaLite'] & components['schemas']['MetaDetailedOnly']; }; responses: never; parameters: never; @@ -4398,8 +5014,12 @@ export type operations = { cacheRemoteFiles: boolean; cacheRemoteSensitiveFiles: boolean; emailRequiredForSignup: boolean; + approvalRequiredForSignup: boolean; enableHcaptcha: boolean; hcaptchaSiteKey: string | null; + enableMcaptcha: boolean; + mcaptchaSiteKey: string | null; + mcaptchaInstanceUrl: string | null; enableRecaptcha: boolean; recaptchaSiteKey: string | null; enableTurnstile: boolean; @@ -4422,15 +5042,19 @@ export type operations = { hiddenTags: string[]; blockedHosts: string[]; sensitiveWords: string[]; + prohibitedWords: string[]; bannedEmailDomains?: string[]; preservedUsernames: string[]; + bubbleInstances: string[]; hcaptchaSecretKey: string | null; + mcaptchaSecretKey: string | null; recaptchaSecretKey: string | null; turnstileSecretKey: string | null; sensitiveMediaDetection: string; sensitiveMediaDetectionSensitivity: string; setSensitiveFlagAutomatically: boolean; enableSensitiveMediaDetectionForVideos: boolean; + enableBotTrending: boolean; /** Format: id */ proxyAccountId: string | null; email: string | null; @@ -4456,9 +5080,13 @@ export type operations = { enableActiveEmailValidation: boolean; enableVerifymailApi: boolean; verifymailAuthKey: string | null; + enableTruemailApi: boolean; + truemailInstance: string | null; + truemailAuthKey: string | null; enableChartsForRemoteUser: boolean; enableChartsForFederatedInstances: boolean; enableServerMachineStats: boolean; + enableAchievements: boolean; enableIdenticonGeneration: boolean; manifestJsonOverride: string; policies: Record<string, never>; @@ -4472,18 +5100,21 @@ export type operations = { backgroundImageUrl: string | null; deeplAuthKey: string | null; deeplIsPro: boolean; + deeplFreeMode: boolean; + deeplFreeInstance: string | null; defaultDarkTheme: string | null; defaultLightTheme: string | null; description: string | null; disableRegistration: boolean; impressumUrl: string | null; + donationUrl: string | null; maintainerEmail: string | null; maintainerName: string | null; name: string | null; shortName: string | null; objectStorageS3ForcePathStyle: boolean; privacyPolicyUrl: string | null; - repositoryUrl: string; + repositoryUrl: string | null; summalyProxy: string | null; themeColor: string | null; tosUrl: string | null; @@ -4578,9 +5209,9 @@ export type operations = { targetUserId: string; /** Format: id */ assigneeId: string | null; - reporter: components['schemas']['User']; - targetUser: components['schemas']['User']; - assignee?: components['schemas']['User'] | null; + reporter: components['schemas']['UserDetailedNotMe']; + targetUser: components['schemas']['UserDetailedNotMe']; + assignee?: components['schemas']['UserDetailedNotMe'] | null; })[]; }; }; @@ -4620,7 +5251,6 @@ export type operations = { * admin/accounts/create * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. * **Credential required**: *No* */ 'admin/accounts/create': { @@ -4636,7 +5266,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['User']; + 'application/json': components['schemas']['MeDetailed']; }; }; /** @description Client error */ @@ -4741,7 +5371,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['User']; + 'application/json': components['schemas']['UserDetailedNotMe']; }; }; /** @description Client error */ @@ -5887,7 +6517,12 @@ export type operations = { size: number; comment: string | null; blurhash: string | null; - properties: Record<string, never>; + properties: { + width?: number; + height?: number; + orientation?: number; + avgColor?: string; + }; /** @example true */ storedInternal: boolean | null; /** Format: url */ @@ -6019,9 +6654,11 @@ export type operations = { }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['EmojiDetailed']; + }; }; /** @description Client error */ 400: { @@ -6632,13 +7269,13 @@ export type operations = { content: { 'application/json': { /** Format: misskey:id */ - id: string; - name: string; + id?: string; + name?: string; /** Format: misskey:id */ fileId?: string; /** @description Use `null` to reset the category. */ category?: string | null; - aliases: string[]; + aliases?: string[]; license?: string | null; isSensitive?: boolean; localOnly?: boolean; @@ -6847,7 +7484,9 @@ export type operations = { content: { 'application/json': { host: string; - isSuspended: boolean; + isSuspended?: boolean; + isNSFW?: boolean; + moderationNote?: string; }; }; }; @@ -6948,7 +7587,12 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': Record<string, never>; + 'application/json': { + [key: string]: { + count: number; + size: number; + }; + }; }; }; /** @description Client error */ @@ -7889,7 +8533,7 @@ export type operations = { info: Record<string, never>; /** Format: id */ userId: string; - user: components['schemas']['UserDetailed']; + user: components['schemas']['UserDetailedNotMe']; }[]; }; }; @@ -7944,7 +8588,162 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': Record<string, never>; + 'application/json': { + email: string | null; + emailVerified: boolean; + autoAcceptFollowed: boolean; + noCrawle: boolean; + preventAiLearning: boolean; + alwaysMarkNsfw: boolean; + autoSensitive: boolean; + carefulBot: boolean; + injectFeaturedNote: boolean; + receiveAnnouncementEmail: boolean; + mutedWords: (string | string[])[]; + mutedInstances: string[]; + notificationRecieveConfig: { + note?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + follow?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + mention?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + reply?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + renote?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + quote?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + reaction?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + pollEnded?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + receiveFollowRequest?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + followRequestAccepted?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + roleAssigned?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + achievementEarned?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + app?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + test?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + }; + isModerator: boolean; + isSilenced: boolean; + isSuspended: boolean; + isHibernated: boolean; + lastActiveDate: string | null; + moderationNote: string; + signins: components['schemas']['Signin'][]; + policies: components['schemas']['RolePolicies']; + roles: components['schemas']['Role'][]; + roleAssigns: ({ + createdAt: string; + expiresAt: string | null; + roleId: string; + })[]; + }; }; }; /** @description Client error */ @@ -7999,7 +8798,7 @@ export type operations = { * @default all * @enum {string} */ - state?: 'all' | 'alive' | 'available' | 'admin' | 'moderator' | 'adminOrModerator' | 'suspended'; + state?: 'all' | 'alive' | 'available' | 'admin' | 'moderator' | 'adminOrModerator' | 'suspended' | 'approved'; /** * @default combined * @enum {string} @@ -8055,12 +8854,12 @@ export type operations = { }; }; /** - * admin/suspend-user + * admin/nsfw-user * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:admin:suspend-user* + * **Credential required**: *Yes* / **Permission**: *write:admin:nsfw-user* */ - 'admin/suspend-user': { + 'admin/nsfw-user': { requestBody: { content: { 'application/json': { @@ -8107,12 +8906,12 @@ export type operations = { }; }; /** - * admin/unsuspend-user + * admin/unnsfw-user * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:admin:unsuspend-user* + * **Credential required**: *Yes* / **Permission**: *write:admin:unnsfw-user* */ - 'admin/unsuspend-user': { + 'admin/unnsfw-user': { requestBody: { content: { 'application/json': { @@ -8159,110 +8958,17 @@ export type operations = { }; }; /** - * admin/update-meta + * admin/silence-user * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:admin:meta* + * **Credential required**: *Yes* / **Permission**: *write:admin:silence-user* */ - 'admin/update-meta': { + 'admin/silence-user': { requestBody: { content: { 'application/json': { - disableRegistration?: boolean | null; - pinnedUsers?: string[] | null; - hiddenTags?: string[] | null; - blockedHosts?: string[] | null; - sensitiveWords?: string[] | null; - themeColor?: string | null; - mascotImageUrl?: string | null; - bannerUrl?: string | null; - serverErrorImageUrl?: string | null; - infoImageUrl?: string | null; - notFoundImageUrl?: string | null; - iconUrl?: string | null; - app192IconUrl?: string | null; - app512IconUrl?: string | null; - backgroundImageUrl?: string | null; - logoImageUrl?: string | null; - name?: string | null; - shortName?: string | null; - description?: string | null; - defaultLightTheme?: string | null; - defaultDarkTheme?: string | null; - cacheRemoteFiles?: boolean; - cacheRemoteSensitiveFiles?: boolean; - emailRequiredForSignup?: boolean; - enableHcaptcha?: boolean; - hcaptchaSiteKey?: string | null; - hcaptchaSecretKey?: string | null; - enableRecaptcha?: boolean; - recaptchaSiteKey?: string | null; - recaptchaSecretKey?: string | null; - enableTurnstile?: boolean; - turnstileSiteKey?: string | null; - turnstileSecretKey?: string | null; - /** @enum {string} */ - sensitiveMediaDetection?: 'none' | 'all' | 'local' | 'remote'; - /** @enum {string} */ - sensitiveMediaDetectionSensitivity?: 'medium' | 'low' | 'high' | 'veryLow' | 'veryHigh'; - setSensitiveFlagAutomatically?: boolean; - enableSensitiveMediaDetectionForVideos?: boolean; /** Format: misskey:id */ - proxyAccountId?: string | null; - maintainerName?: string | null; - maintainerEmail?: string | null; - langs?: string[]; - summalyProxy?: string | null; - deeplAuthKey?: string | null; - deeplIsPro?: boolean; - enableEmail?: boolean; - email?: string | null; - smtpSecure?: boolean; - smtpHost?: string | null; - smtpPort?: number | null; - smtpUser?: string | null; - smtpPass?: string | null; - enableServiceWorker?: boolean; - swPublicKey?: string | null; - swPrivateKey?: string | null; - tosUrl?: string | null; - repositoryUrl?: string; - feedbackUrl?: string; - impressumUrl?: string | null; - privacyPolicyUrl?: string | null; - useObjectStorage?: boolean; - objectStorageBaseUrl?: string | null; - objectStorageBucket?: string | null; - objectStoragePrefix?: string | null; - objectStorageEndpoint?: string | null; - objectStorageRegion?: string | null; - objectStoragePort?: number | null; - objectStorageAccessKey?: string | null; - objectStorageSecretKey?: string | null; - objectStorageUseSSL?: boolean; - objectStorageUseProxy?: boolean; - objectStorageSetPublicRead?: boolean; - objectStorageS3ForcePathStyle?: boolean; - enableIpLogging?: boolean; - enableActiveEmailValidation?: boolean; - enableVerifymailApi?: boolean; - verifymailAuthKey?: string | null; - enableChartsForRemoteUser?: boolean; - enableChartsForFederatedInstances?: boolean; - enableServerMachineStats?: boolean; - enableIdenticonGeneration?: boolean; - serverRules?: string[]; - bannedEmailDomains?: string[]; - preservedUsernames?: string[]; - manifestJsonOverride?: string; - enableFanoutTimeline?: boolean; - enableFanoutTimelineDbFallback?: boolean; - perLocalUserUserTimelineCacheMax?: number; - perRemoteUserUserTimelineCacheMax?: number; - perUserHomeTimelineCacheMax?: number; - perUserListTimelineCacheMax?: number; - notesPerOneAd?: number; - silencedHosts?: string[] | null; + userId: string; }; }; }; @@ -8304,12 +9010,12 @@ export type operations = { }; }; /** - * admin/delete-account + * admin/unsilence-user * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:admin:delete-account* + * **Credential required**: *Yes* / **Permission**: *write:admin:unsilence-user* */ - 'admin/delete-account': { + 'admin/unsilence-user': { requestBody: { content: { 'application/json': { @@ -8319,11 +9025,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -8358,18 +9062,17 @@ export type operations = { }; }; /** - * admin/update-user-note + * admin/suspend-user * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:admin:user-note* + * **Credential required**: *Yes* / **Permission**: *write:admin:suspend-user* */ - 'admin/update-user-note': { + 'admin/suspend-user': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ userId: string; - text: string; }; }; }; @@ -8411,40 +9114,24 @@ export type operations = { }; }; /** - * admin/roles/create + * admin/approve-user * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:admin:roles* + * **Credential required**: *Yes* / **Permission**: *write:admin:approve-user* */ - 'admin/roles/create': { + 'admin/approve-user': { requestBody: { content: { 'application/json': { - name: string; - description: string; - color: string | null; - iconUrl: string | null; - /** @enum {string} */ - target: 'manual' | 'conditional'; - condFormula: Record<string, never>; - isPublic: boolean; - isModerator: boolean; - isAdministrator: boolean; - /** @default false */ - isExplorable?: boolean; - asBadge: boolean; - canEditMembersByModerator: boolean; - displayOrder: number; - policies: Record<string, never>; + /** Format: misskey:id */ + userId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Role']; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -8479,17 +9166,17 @@ export type operations = { }; }; /** - * admin/roles/delete + * admin/unsuspend-user * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:admin:roles* + * **Credential required**: *Yes* / **Permission**: *write:admin:unsuspend-user* */ - 'admin/roles/delete': { + 'admin/unsuspend-user': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - roleId: string; + userId: string; }; }; }; @@ -8531,132 +9218,126 @@ export type operations = { }; }; /** - * admin/roles/list - * @description No description provided. - * - * **Credential required**: *Yes* / **Permission**: *read:admin:roles* - */ - 'admin/roles/list': { - responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Role'][]; - }; - }; - /** @description Client error */ - 400: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description Authentication error */ - 401: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description Forbidden error */ - 403: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description I'm Ai */ - 418: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description Internal server error */ - 500: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - }; - }; - /** - * admin/roles/show + * admin/update-meta * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:admin:roles* + * **Credential required**: *Yes* / **Permission**: *write:admin:meta* */ - 'admin/roles/show': { + 'admin/update-meta': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - roleId: string; - }; - }; - }; - responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Role']; - }; - }; - /** @description Client error */ - 400: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description Authentication error */ - 401: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description Forbidden error */ - 403: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description I'm Ai */ - 418: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description Internal server error */ - 500: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - }; - }; - /** - * admin/roles/update - * @description No description provided. - * - * **Credential required**: *Yes* / **Permission**: *write:admin:roles* - */ - 'admin/roles/update': { - requestBody: { - content: { - 'application/json': { - /** Format: misskey:id */ - roleId: string; - name: string; - description: string; - color: string | null; - iconUrl: string | null; + disableRegistration?: boolean | null; + pinnedUsers?: string[] | null; + hiddenTags?: string[] | null; + blockedHosts?: string[] | null; + sensitiveWords?: string[] | null; + prohibitedWords?: string[] | null; + themeColor?: string | null; + mascotImageUrl?: string | null; + bannerUrl?: string | null; + serverErrorImageUrl?: string | null; + infoImageUrl?: string | null; + notFoundImageUrl?: string | null; + iconUrl?: string | null; + app192IconUrl?: string | null; + app512IconUrl?: string | null; + backgroundImageUrl?: string | null; + logoImageUrl?: string | null; + name?: string | null; + shortName?: string | null; + description?: string | null; + defaultLightTheme?: string | null; + defaultDarkTheme?: string | null; + defaultLike?: string | null; + cacheRemoteFiles?: boolean; + cacheRemoteSensitiveFiles?: boolean; + emailRequiredForSignup?: boolean; + approvalRequiredForSignup?: boolean; + enableHcaptcha?: boolean; + hcaptchaSiteKey?: string | null; + hcaptchaSecretKey?: string | null; + enableMcaptcha?: boolean; + mcaptchaSiteKey?: string | null; + mcaptchaInstanceUrl?: string | null; + mcaptchaSecretKey?: string | null; + enableRecaptcha?: boolean; + recaptchaSiteKey?: string | null; + recaptchaSecretKey?: string | null; + enableTurnstile?: boolean; + turnstileSiteKey?: string | null; + turnstileSecretKey?: string | null; /** @enum {string} */ - target: 'manual' | 'conditional'; - condFormula: Record<string, never>; - isPublic: boolean; - isModerator: boolean; - isAdministrator: boolean; - isExplorable?: boolean; - asBadge: boolean; - canEditMembersByModerator: boolean; - displayOrder: number; - policies: Record<string, never>; + sensitiveMediaDetection?: 'none' | 'all' | 'local' | 'remote'; + /** @enum {string} */ + sensitiveMediaDetectionSensitivity?: 'medium' | 'low' | 'high' | 'veryLow' | 'veryHigh'; + setSensitiveFlagAutomatically?: boolean; + enableSensitiveMediaDetectionForVideos?: boolean; + enableBotTrending?: boolean; + /** Format: misskey:id */ + proxyAccountId?: string | null; + maintainerName?: string | null; + maintainerEmail?: string | null; + langs?: string[]; + summalyProxy?: string | null; + deeplAuthKey?: string | null; + deeplIsPro?: boolean; + deeplFreeMode?: boolean; + deeplFreeInstance?: string | null; + enableEmail?: boolean; + email?: string | null; + smtpSecure?: boolean; + smtpHost?: string | null; + smtpPort?: number | null; + smtpUser?: string | null; + smtpPass?: string | null; + enableServiceWorker?: boolean; + swPublicKey?: string | null; + swPrivateKey?: string | null; + tosUrl?: string | null; + repositoryUrl?: string | null; + feedbackUrl?: string | null; + impressumUrl?: string | null; + donationUrl?: string | null; + privacyPolicyUrl?: string | null; + useObjectStorage?: boolean; + objectStorageBaseUrl?: string | null; + objectStorageBucket?: string | null; + objectStoragePrefix?: string | null; + objectStorageEndpoint?: string | null; + objectStorageRegion?: string | null; + objectStoragePort?: number | null; + objectStorageAccessKey?: string | null; + objectStorageSecretKey?: string | null; + objectStorageUseSSL?: boolean; + objectStorageUseProxy?: boolean; + objectStorageSetPublicRead?: boolean; + objectStorageS3ForcePathStyle?: boolean; + enableIpLogging?: boolean; + enableActiveEmailValidation?: boolean; + enableVerifymailApi?: boolean; + verifymailAuthKey?: string | null; + enableTruemailApi?: boolean; + truemailInstance?: string | null; + truemailAuthKey?: string | null; + enableChartsForRemoteUser?: boolean; + enableChartsForFederatedInstances?: boolean; + enableServerMachineStats?: boolean; + enableAchievements?: boolean; + enableIdenticonGeneration?: boolean; + serverRules?: string[]; + bannedEmailDomains?: string[]; + preservedUsernames?: string[]; + bubbleInstances?: string[]; + manifestJsonOverride?: string; + enableFanoutTimeline?: boolean; + enableFanoutTimelineDbFallback?: boolean; + perLocalUserUserTimelineCacheMax?: number; + perRemoteUserUserTimelineCacheMax?: number; + perUserHomeTimelineCacheMax?: number; + perUserListTimelineCacheMax?: number; + notesPerOneAd?: number; + silencedHosts?: string[] | null; }; }; }; @@ -8698,20 +9379,17 @@ export type operations = { }; }; /** - * admin/roles/assign + * admin/delete-account * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:admin:roles* + * **Credential required**: *Yes* / **Permission**: *write:admin:delete-account* */ - 'admin/roles/assign': { + 'admin/delete-account': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - roleId: string; /** Format: misskey:id */ userId: string; - expiresAt?: number | null; }; }; }; @@ -8753,19 +9431,18 @@ export type operations = { }; }; /** - * admin/roles/unassign + * admin/update-user-note * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:admin:roles* + * **Credential required**: *Yes* / **Permission**: *write:admin:user-note* */ - 'admin/roles/unassign': { + 'admin/update-user-note': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - roleId: string; /** Format: misskey:id */ userId: string; + text: string; }; }; }; @@ -8807,23 +9484,40 @@ export type operations = { }; }; /** - * admin/roles/update-default-policies + * admin/roles/create * @description No description provided. * * **Credential required**: *Yes* / **Permission**: *write:admin:roles* */ - 'admin/roles/update-default-policies': { + 'admin/roles/create': { requestBody: { content: { 'application/json': { + name: string; + description: string; + color: string | null; + iconUrl: string | null; + /** @enum {string} */ + target: 'manual' | 'conditional'; + condFormula: Record<string, never>; + isPublic: boolean; + isModerator: boolean; + isAdministrator: boolean; + /** @default false */ + isExplorable?: boolean; + asBadge: boolean; + canEditMembersByModerator: boolean; + displayOrder: number; policies: Record<string, never>; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Role']; + }; }; /** @description Client error */ 400: { @@ -8858,40 +9552,24 @@ export type operations = { }; }; /** - * admin/roles/users + * admin/roles/delete * @description No description provided. * - * **Credential required**: *No* / **Permission**: *read:admin:roles* + * **Credential required**: *Yes* / **Permission**: *write:admin:roles* */ - 'admin/roles/users': { + 'admin/roles/delete': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ roleId: string; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - /** @default 10 */ - limit?: number; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': ({ - /** Format: misskey:id */ - id: string; - /** Format: date-time */ - createdAt: string; - user: components['schemas']['UserDetailed']; - /** Format: date-time */ - expiresAt: string | null; - })[]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -8926,31 +9604,17 @@ export type operations = { }; }; /** - * announcements + * admin/roles/list * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *read:admin:roles* */ - announcements: { - requestBody: { - content: { - 'application/json': { - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - /** @default true */ - isActive?: boolean; - }; - }; - }; + 'admin/roles/list': { responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Announcement'][]; + 'application/json': components['schemas']['Role'][]; }; }; /** @description Client error */ @@ -8986,28 +9650,17 @@ export type operations = { }; }; /** - * antennas/create + * admin/roles/show * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *Yes* / **Permission**: *read:admin:roles* */ - 'antennas/create': { + 'admin/roles/show': { requestBody: { content: { 'application/json': { - name: string; - /** @enum {string} */ - src: 'home' | 'all' | 'users' | 'list' | 'users_blacklist'; /** Format: misskey:id */ - userListId?: string | null; - keywords: string[][]; - excludeKeywords: string[][]; - users: string[]; - caseSensitive: boolean; - localOnly?: boolean; - withReplies: boolean; - withFile: boolean; - notify: boolean; + roleId: string; }; }; }; @@ -9015,7 +9668,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Antenna']; + 'application/json': components['schemas']['Role']; }; }; /** @description Client error */ @@ -9051,21 +9704,36 @@ export type operations = { }; }; /** - * antennas/delete + * admin/roles/update * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *Yes* / **Permission**: *write:admin:roles* */ - 'antennas/delete': { + 'admin/roles/update': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - antennaId: string; - }; - }; - }; - responses: { + roleId: string; + name: string; + description: string; + color: string | null; + iconUrl: string | null; + /** @enum {string} */ + target: 'manual' | 'conditional'; + condFormula: Record<string, never>; + isPublic: boolean; + isModerator: boolean; + isAdministrator: boolean; + isExplorable?: boolean; + asBadge: boolean; + canEditMembersByModerator: boolean; + displayOrder: number; + policies: Record<string, never>; + }; + }; + }; + responses: { /** @description OK (without any results) */ 204: { content: never; @@ -9103,19 +9771,28 @@ export type operations = { }; }; /** - * antennas/list + * admin/roles/assign * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *Yes* / **Permission**: *write:admin:roles* */ - 'antennas/list': { - responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Antenna'][]; + 'admin/roles/assign': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + roleId: string; + /** Format: misskey:id */ + userId: string; + expiresAt?: number | null; }; }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; /** @description Client error */ 400: { content: { @@ -9149,34 +9826,26 @@ export type operations = { }; }; /** - * antennas/notes + * admin/roles/unassign * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *Yes* / **Permission**: *write:admin:roles* */ - 'antennas/notes': { + 'admin/roles/unassign': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - antennaId: string; - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; + roleId: string; /** Format: misskey:id */ - untilId?: string; - sinceDate?: number; - untilDate?: number; + userId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Note'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -9211,26 +9880,23 @@ export type operations = { }; }; /** - * antennas/show + * admin/roles/update-default-policies * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *Yes* / **Permission**: *write:admin:roles* */ - 'antennas/show': { + 'admin/roles/update-default-policies': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - antennaId: string; + policies: Record<string, never>; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Antenna']; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -9265,30 +9931,23 @@ export type operations = { }; }; /** - * antennas/update + * admin/roles/users * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *No* / **Permission**: *read:admin:roles* */ - 'antennas/update': { + 'admin/roles/users': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - antennaId: string; - name: string; - /** @enum {string} */ - src: 'home' | 'all' | 'users' | 'list' | 'users_blacklist'; + roleId: string; /** Format: misskey:id */ - userListId?: string | null; - keywords: string[][]; - excludeKeywords: string[][]; - users: string[]; - caseSensitive: boolean; - localOnly?: boolean; - withReplies: boolean; - withFile: boolean; - notify: boolean; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default 10 */ + limit?: number; }; }; }; @@ -9296,7 +9955,15 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Antenna']; + 'application/json': ({ + /** Format: misskey:id */ + id: string; + /** Format: date-time */ + createdAt: string; + user: components['schemas']['UserDetailed']; + /** Format: date-time */ + expiresAt: string | null; + })[]; }; }; /** @description Client error */ @@ -9332,16 +9999,23 @@ export type operations = { }; }; /** - * ap/get + * announcements * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:federation* + * **Credential required**: *No* */ - 'ap/get': { + announcements: { requestBody: { content: { 'application/json': { - uri: string; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default true */ + isActive?: boolean; }; }; }; @@ -9349,7 +10023,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': Record<string, never>; + 'application/json': components['schemas']['Announcement'][]; }; }; /** @description Client error */ @@ -9376,12 +10050,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -9391,16 +10059,28 @@ export type operations = { }; }; /** - * ap/show + * antennas/create * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'ap/show': { + 'antennas/create': { requestBody: { content: { 'application/json': { - uri: string; + name: string; + /** @enum {string} */ + src: 'home' | 'all' | 'users' | 'list' | 'users_blacklist'; + /** Format: misskey:id */ + userListId?: string | null; + keywords: string[][]; + excludeKeywords: string[][]; + users: string[]; + caseSensitive: boolean; + localOnly?: boolean; + withReplies: boolean; + withFile: boolean; + notify: boolean; }; }; }; @@ -9408,15 +10088,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': OneOf<[{ - /** @enum {string} */ - type: 'User'; - object: components['schemas']['UserDetailedNotMe']; - }, { - /** @enum {string} */ - type: 'Note'; - object: components['schemas']['Note']; - }]>; + 'application/json': components['schemas']['Antenna']; }; }; /** @description Client error */ @@ -9443,12 +10115,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -9458,28 +10124,24 @@ export type operations = { }; }; /** - * app/create + * antennas/delete * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'app/create': { + 'antennas/delete': { requestBody: { content: { 'application/json': { - name: string; - description: string; - permission: string[]; - callbackUrl?: string | null; + /** Format: misskey:id */ + antennaId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['App']; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -9514,25 +10176,17 @@ export type operations = { }; }; /** - * app/show + * antennas/list * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'app/show': { - requestBody: { - content: { - 'application/json': { - /** Format: misskey:id */ - appId: string; - }; - }; - }; + 'antennas/list': { responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['App']; + 'application/json': components['schemas']['Antenna'][]; }; }; /** @description Client error */ @@ -9568,24 +10222,34 @@ export type operations = { }; }; /** - * auth/accept + * antennas/notes * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'auth/accept': { + 'antennas/notes': { requestBody: { content: { 'application/json': { - token: string; + /** Format: misskey:id */ + antennaId: string; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + sinceDate?: number; + untilDate?: number; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Note'][]; + }; }; /** @description Client error */ 400: { @@ -9620,16 +10284,17 @@ export type operations = { }; }; /** - * auth/session/generate + * antennas/show * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'auth/session/generate': { + 'antennas/show': { requestBody: { content: { 'application/json': { - appSecret: string; + /** Format: misskey:id */ + antennaId: string; }; }; }; @@ -9637,11 +10302,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - token: string; - /** Format: url */ - url: string; - }; + 'application/json': components['schemas']['Antenna']; }; }; /** @description Client error */ @@ -9677,16 +10338,30 @@ export type operations = { }; }; /** - * auth/session/show + * antennas/update * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'auth/session/show': { + 'antennas/update': { requestBody: { content: { 'application/json': { - token: string; + /** Format: misskey:id */ + antennaId: string; + name: string; + /** @enum {string} */ + src: 'home' | 'all' | 'users' | 'list' | 'users_blacklist'; + /** Format: misskey:id */ + userListId?: string | null; + keywords: string[][]; + excludeKeywords: string[][]; + users: string[]; + caseSensitive: boolean; + localOnly?: boolean; + withReplies: boolean; + withFile: boolean; + notify: boolean; }; }; }; @@ -9694,12 +10369,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - /** Format: id */ - id: string; - app: components['schemas']['App']; - token: string; - }; + 'application/json': components['schemas']['Antenna']; }; }; /** @description Client error */ @@ -9735,17 +10405,16 @@ export type operations = { }; }; /** - * auth/session/userkey + * ap/get * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *read:federation* */ - 'auth/session/userkey': { + 'ap/get': { requestBody: { content: { 'application/json': { - appSecret: string; - token: string; + uri: string; }; }; }; @@ -9753,10 +10422,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - accessToken: string; - user: components['schemas']['UserDetailedNotMe']; - }; + 'application/json': Record<string, never>; }; }; /** @description Client error */ @@ -9783,6 +10449,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -9792,17 +10464,16 @@ export type operations = { }; }; /** - * blocking/create + * ap/show * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:blocks* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'blocking/create': { + 'ap/show': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - userId: string; + uri: string; }; }; }; @@ -9810,7 +10481,15 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['UserDetailedNotMe']; + 'application/json': OneOf<[{ + /** @enum {string} */ + type: 'User'; + object: components['schemas']['UserDetailedNotMe']; + }, { + /** @enum {string} */ + type: 'Note'; + object: components['schemas']['Note']; + }]>; }; }; /** @description Client error */ @@ -9852,17 +10531,19 @@ export type operations = { }; }; /** - * blocking/delete + * app/create * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:blocks* + * **Credential required**: *No* */ - 'blocking/delete': { + 'app/create': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - userId: string; + name: string; + description: string; + permission: string[]; + callbackUrl?: string | null; }; }; }; @@ -9870,7 +10551,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['UserDetailedNotMe']; + 'application/json': components['schemas']['App']; }; }; /** @description Client error */ @@ -9897,12 +10578,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -9912,21 +10587,17 @@ export type operations = { }; }; /** - * blocking/list + * app/show * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:blocks* + * **Credential required**: *No* */ - 'blocking/list': { + 'app/show': { requestBody: { content: { 'application/json': { - /** @default 30 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; /** Format: misskey:id */ - untilId?: string; + appId: string; }; }; }; @@ -9934,7 +10605,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Blocking'][]; + 'application/json': components['schemas']['App']; }; }; /** @description Client error */ @@ -9970,31 +10641,24 @@ export type operations = { }; }; /** - * channels/create + * auth/accept * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:channels* + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* */ - 'channels/create': { + 'auth/accept': { requestBody: { content: { 'application/json': { - name: string; - description?: string | null; - /** Format: misskey:id */ - bannerId?: string | null; - color?: string; - isSensitive?: boolean | null; - allowRenoteToExternal?: boolean | null; + token: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Channel']; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -10020,12 +10684,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -10035,17 +10693,28 @@ export type operations = { }; }; /** - * channels/featured + * auth/session/generate * @description No description provided. * * **Credential required**: *No* */ - 'channels/featured': { + 'auth/session/generate': { + requestBody: { + content: { + 'application/json': { + appSecret: string; + }; + }; + }; responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Channel'][]; + 'application/json': { + token: string; + /** Format: url */ + url: string; + }; }; }; /** @description Client error */ @@ -10081,24 +10750,30 @@ export type operations = { }; }; /** - * channels/follow + * auth/session/show * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:channels* + * **Credential required**: *No* */ - 'channels/follow': { + 'auth/session/show': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - channelId: string; + token: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + /** Format: id */ + id: string; + app: components['schemas']['App']; + token: string; + }; + }; }; /** @description Client error */ 400: { @@ -10133,21 +10808,17 @@ export type operations = { }; }; /** - * channels/followed + * auth/session/userkey * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:channels* + * **Credential required**: *No* */ - 'channels/followed': { + 'auth/session/userkey': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - /** @default 5 */ - limit?: number; + appSecret: string; + token: string; }; }; }; @@ -10155,7 +10826,10 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Channel'][]; + 'application/json': { + accessToken: string; + user: components['schemas']['UserDetailedNotMe']; + }; }; }; /** @description Client error */ @@ -10191,21 +10865,17 @@ export type operations = { }; }; /** - * channels/owned + * blocking/create * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:channels* + * **Credential required**: *Yes* / **Permission**: *write:blocks* */ - 'channels/owned': { + 'blocking/create': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - /** @default 5 */ - limit?: number; + userId: string; }; }; }; @@ -10213,7 +10883,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Channel'][]; + 'application/json': components['schemas']['UserDetailedNotMe']; }; }; /** @description Client error */ @@ -10240,6 +10910,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -10249,17 +10925,17 @@ export type operations = { }; }; /** - * channels/show + * blocking/delete * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:blocks* */ - 'channels/show': { + 'blocking/delete': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - channelId: string; + userId: string; }; }; }; @@ -10267,7 +10943,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Channel']; + 'application/json': components['schemas']['UserDetailedNotMe']; }; }; /** @description Client error */ @@ -10294,6 +10970,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -10303,27 +10985,21 @@ export type operations = { }; }; /** - * channels/timeline + * blocking/list * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *read:blocks* */ - 'channels/timeline': { + 'blocking/list': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - channelId: string; - /** @default 10 */ + /** @default 30 */ limit?: number; /** Format: misskey:id */ sinceId?: string; /** Format: misskey:id */ untilId?: string; - sinceDate?: number; - untilDate?: number; - /** @default false */ - allowPartial?: boolean; }; }; }; @@ -10331,7 +11007,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Note'][]; + 'application/json': components['schemas']['Blocking'][]; }; }; /** @description Client error */ @@ -10367,24 +11043,31 @@ export type operations = { }; }; /** - * channels/unfollow + * channels/create * @description No description provided. * * **Credential required**: *Yes* / **Permission**: *write:channels* */ - 'channels/unfollow': { + 'channels/create': { requestBody: { content: { 'application/json': { + name: string; + description?: string | null; /** Format: misskey:id */ - channelId: string; + bannerId?: string | null; + color?: string; + isSensitive?: boolean | null; + allowRenoteToExternal?: boolean | null; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Channel']; + }; }; /** @description Client error */ 400: { @@ -10410,6 +11093,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -10419,34 +11108,17 @@ export type operations = { }; }; /** - * channels/update + * channels/featured * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:channels* + * **Credential required**: *No* */ - 'channels/update': { - requestBody: { - content: { - 'application/json': { - /** Format: misskey:id */ - channelId: string; - name?: string; - description?: string | null; - /** Format: misskey:id */ - bannerId?: string | null; - isArchived?: boolean | null; - pinnedNoteIds?: string[]; - color?: string; - isSensitive?: boolean | null; - allowRenoteToExternal?: boolean | null; - }; - }; - }; + 'channels/featured': { responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Channel']; + 'application/json': components['schemas']['Channel'][]; }; }; /** @description Client error */ @@ -10482,12 +11154,12 @@ export type operations = { }; }; /** - * channels/favorite + * channels/follow * @description No description provided. * * **Credential required**: *Yes* / **Permission**: *write:channels* */ - 'channels/favorite': { + 'channels/follow': { requestBody: { content: { 'application/json': { @@ -10534,24 +11206,30 @@ export type operations = { }; }; /** - * channels/unfavorite + * channels/followed * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:channels* + * **Credential required**: *Yes* / **Permission**: *read:channels* */ - 'channels/unfavorite': { + 'channels/followed': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - channelId: string; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default 5 */ + limit?: number; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Channel'][]; + }; }; /** @description Client error */ 400: { @@ -10586,12 +11264,24 @@ export type operations = { }; }; /** - * channels/my-favorites + * channels/owned * @description No description provided. * * **Credential required**: *Yes* / **Permission**: *read:channels* */ - 'channels/my-favorites': { + 'channels/owned': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default 5 */ + limit?: number; + }; + }; + }; responses: { /** @description OK (with results) */ 200: { @@ -10632,27 +11322,17 @@ export type operations = { }; }; /** - * channels/search + * channels/show * @description No description provided. * * **Credential required**: *No* */ - 'channels/search': { + 'channels/show': { requestBody: { content: { 'application/json': { - query: string; - /** - * @default nameAndDescription - * @enum {string} - */ - type?: 'nameAndDescription' | 'nameOnly'; - /** Format: misskey:id */ - sinceId?: string; /** Format: misskey:id */ - untilId?: string; - /** @default 5 */ - limit?: number; + channelId: string; }; }; }; @@ -10660,7 +11340,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Channel'][]; + 'application/json': components['schemas']['Channel']; }; }; /** @description Client error */ @@ -10696,21 +11376,27 @@ export type operations = { }; }; /** - * charts/active-users + * channels/timeline * @description No description provided. * * **Credential required**: *No* */ - 'charts/active-users': { + 'channels/timeline': { requestBody: { content: { 'application/json': { - /** @enum {string} */ - span: 'day' | 'hour'; - /** @default 30 */ + /** Format: misskey:id */ + channelId: string; + /** @default 10 */ limit?: number; - /** @default null */ - offset?: number | null; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + sinceDate?: number; + untilDate?: number; + /** @default false */ + allowPartial?: boolean; }; }; }; @@ -10718,17 +11404,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - readWrite: number[]; - read: number[]; - write: number[]; - registeredWithinWeek: number[]; - registeredWithinMonth: number[]; - registeredWithinYear: number[]; - registeredOutsideWeek: number[]; - registeredOutsideMonth: number[]; - registeredOutsideYear: number[]; - }; + 'application/json': components['schemas']['Note'][]; }; }; /** @description Client error */ @@ -10764,34 +11440,24 @@ export type operations = { }; }; /** - * charts/ap-request + * channels/unfollow * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:channels* */ - 'charts/ap-request': { + 'channels/unfollow': { requestBody: { content: { 'application/json': { - /** @enum {string} */ - span: 'day' | 'hour'; - /** @default 30 */ - limit?: number; - /** @default null */ - offset?: number | null; + /** Format: misskey:id */ + channelId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': { - deliverFailed: number[]; - deliverSucceeded: number[]; - inboxReceived: number[]; - }; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -10826,21 +11492,26 @@ export type operations = { }; }; /** - * charts/drive + * channels/update * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:channels* */ - 'charts/drive': { + 'channels/update': { requestBody: { content: { 'application/json': { - /** @enum {string} */ - span: 'day' | 'hour'; - /** @default 30 */ - limit?: number; - /** @default null */ - offset?: number | null; + /** Format: misskey:id */ + channelId: string; + name?: string; + description?: string | null; + /** Format: misskey:id */ + bannerId?: string | null; + isArchived?: boolean | null; + pinnedNoteIds?: string[]; + color?: string; + isSensitive?: boolean | null; + allowRenoteToExternal?: boolean | null; }; }; }; @@ -10848,16 +11519,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - 'local.incCount': number[]; - 'local.incSize': number[]; - 'local.decCount': number[]; - 'local.decSize': number[]; - 'remote.incCount': number[]; - 'remote.incSize': number[]; - 'remote.decCount': number[]; - 'remote.decSize': number[]; - }; + 'application/json': components['schemas']['Channel']; }; }; /** @description Client error */ @@ -10893,39 +11555,24 @@ export type operations = { }; }; /** - * charts/federation + * channels/favorite * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:channels* */ - 'charts/federation': { + 'channels/favorite': { requestBody: { content: { 'application/json': { - /** @enum {string} */ - span: 'day' | 'hour'; - /** @default 30 */ - limit?: number; - /** @default null */ - offset?: number | null; + /** Format: misskey:id */ + channelId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': { - deliveredInstances: number[]; - inboxInstances: number[]; - stalled: number[]; - sub: number[]; - pub: number[]; - pubsub: number[]; - subActive: number[]; - pubActive: number[]; - }; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -10960,56 +11607,24 @@ export type operations = { }; }; /** - * charts/instance + * channels/unfavorite * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:channels* */ - 'charts/instance': { + 'channels/unfavorite': { requestBody: { content: { 'application/json': { - /** @enum {string} */ - span: 'day' | 'hour'; - /** @default 30 */ - limit?: number; - /** @default null */ - offset?: number | null; - host: string; + /** Format: misskey:id */ + channelId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': { - 'requests.failed': number[]; - 'requests.succeeded': number[]; - 'requests.received': number[]; - 'notes.total': number[]; - 'notes.inc': number[]; - 'notes.dec': number[]; - 'notes.diffs.normal': number[]; - 'notes.diffs.reply': number[]; - 'notes.diffs.renote': number[]; - 'notes.diffs.withFile': number[]; - 'users.total': number[]; - 'users.inc': number[]; - 'users.dec': number[]; - 'following.total': number[]; - 'following.inc': number[]; - 'following.dec': number[]; - 'followers.total': number[]; - 'followers.inc': number[]; - 'followers.dec': number[]; - 'drive.totalFiles': number[]; - 'drive.incFiles': number[]; - 'drive.decFiles': number[]; - 'drive.incUsage': number[]; - 'drive.decUsage': number[]; - }; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -11044,44 +11659,17 @@ export type operations = { }; }; /** - * charts/notes + * channels/my-favorites * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *read:channels* */ - 'charts/notes': { - requestBody: { - content: { - 'application/json': { - /** @enum {string} */ - span: 'day' | 'hour'; - /** @default 30 */ - limit?: number; - /** @default null */ - offset?: number | null; - }; - }; - }; + 'channels/my-favorites': { responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': { - 'local.total': number[]; - 'local.inc': number[]; - 'local.dec': number[]; - 'local.diffs.normal': number[]; - 'local.diffs.reply': number[]; - 'local.diffs.renote': number[]; - 'local.diffs.withFile': number[]; - 'remote.total': number[]; - 'remote.inc': number[]; - 'remote.dec': number[]; - 'remote.diffs.normal': number[]; - 'remote.diffs.reply': number[]; - 'remote.diffs.renote': number[]; - 'remote.diffs.withFile': number[]; - }; + 'application/json': components['schemas']['Channel'][]; }; }; /** @description Client error */ @@ -11117,23 +11705,27 @@ export type operations = { }; }; /** - * charts/user/drive + * channels/search * @description No description provided. * * **Credential required**: *No* */ - 'charts/user/drive': { + 'channels/search': { requestBody: { content: { 'application/json': { - /** @enum {string} */ - span: 'day' | 'hour'; - /** @default 30 */ - limit?: number; - /** @default null */ - offset?: number | null; + query: string; + /** + * @default nameAndDescription + * @enum {string} + */ + type?: 'nameAndDescription' | 'nameOnly'; /** Format: misskey:id */ - userId: string; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default 5 */ + limit?: number; }; }; }; @@ -11141,14 +11733,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - totalCount: number[]; - totalSize: number[]; - incCount: number[]; - incSize: number[]; - decCount: number[]; - decSize: number[]; - }; + 'application/json': components['schemas']['Channel'][]; }; }; /** @description Client error */ @@ -11184,12 +11769,12 @@ export type operations = { }; }; /** - * charts/user/following + * charts/active-users * @description No description provided. * * **Credential required**: *No* */ - 'charts/user/following': { + 'charts/active-users': { requestBody: { content: { 'application/json': { @@ -11199,8 +11784,6 @@ export type operations = { limit?: number; /** @default null */ offset?: number | null; - /** Format: misskey:id */ - userId: string; }; }; }; @@ -11209,18 +11792,15 @@ export type operations = { 200: { content: { 'application/json': { - 'local.followings.total': number[]; - 'local.followings.inc': number[]; - 'local.followings.dec': number[]; - 'local.followers.total': number[]; - 'local.followers.inc': number[]; - 'local.followers.dec': number[]; - 'remote.followings.total': number[]; - 'remote.followings.inc': number[]; - 'remote.followings.dec': number[]; - 'remote.followers.total': number[]; - 'remote.followers.inc': number[]; - 'remote.followers.dec': number[]; + readWrite: number[]; + read: number[]; + write: number[]; + registeredWithinWeek: number[]; + registeredWithinMonth: number[]; + registeredWithinYear: number[]; + registeredOutsideWeek: number[]; + registeredOutsideMonth: number[]; + registeredOutsideYear: number[]; }; }; }; @@ -11257,12 +11837,12 @@ export type operations = { }; }; /** - * charts/user/notes + * charts/ap-request * @description No description provided. * * **Credential required**: *No* */ - 'charts/user/notes': { + 'charts/ap-request': { requestBody: { content: { 'application/json': { @@ -11272,8 +11852,6 @@ export type operations = { limit?: number; /** @default null */ offset?: number | null; - /** Format: misskey:id */ - userId: string; }; }; }; @@ -11282,13 +11860,9 @@ export type operations = { 200: { content: { 'application/json': { - total: number[]; - inc: number[]; - dec: number[]; - 'diffs.normal': number[]; - 'diffs.reply': number[]; - 'diffs.renote': number[]; - 'diffs.withFile': number[]; + deliverFailed: number[]; + deliverSucceeded: number[]; + inboxReceived: number[]; }; }; }; @@ -11325,12 +11899,12 @@ export type operations = { }; }; /** - * charts/user/pv + * charts/drive * @description No description provided. * * **Credential required**: *No* */ - 'charts/user/pv': { + 'charts/drive': { requestBody: { content: { 'application/json': { @@ -11340,8 +11914,6 @@ export type operations = { limit?: number; /** @default null */ offset?: number | null; - /** Format: misskey:id */ - userId: string; }; }; }; @@ -11350,10 +11922,18 @@ export type operations = { 200: { content: { 'application/json': { - 'upv.user': number[]; - 'pv.user': number[]; - 'upv.visitor': number[]; - 'pv.visitor': number[]; + local: { + incCount: number[]; + incSize: number[]; + decCount: number[]; + decSize: number[]; + }; + remote: { + incCount: number[]; + incSize: number[]; + decCount: number[]; + decSize: number[]; + }; }; }; }; @@ -11390,12 +11970,12 @@ export type operations = { }; }; /** - * charts/user/reactions + * charts/federation * @description No description provided. * * **Credential required**: *No* */ - 'charts/user/reactions': { + 'charts/federation': { requestBody: { content: { 'application/json': { @@ -11405,8 +11985,6 @@ export type operations = { limit?: number; /** @default null */ offset?: number | null; - /** Format: misskey:id */ - userId: string; }; }; }; @@ -11415,8 +11993,14 @@ export type operations = { 200: { content: { 'application/json': { - 'local.count': number[]; - 'remote.count': number[]; + deliveredInstances: number[]; + inboxInstances: number[]; + stalled: number[]; + sub: number[]; + pub: number[]; + pubsub: number[]; + subActive: number[]; + pubActive: number[]; }; }; }; @@ -11453,12 +12037,12 @@ export type operations = { }; }; /** - * charts/users + * charts/instance * @description No description provided. * * **Credential required**: *No* */ - 'charts/users': { + 'charts/instance': { requestBody: { content: { 'application/json': { @@ -11468,6 +12052,7 @@ export type operations = { limit?: number; /** @default null */ offset?: number | null; + host: string; }; }; }; @@ -11476,12 +12061,44 @@ export type operations = { 200: { content: { 'application/json': { - 'local.total': number[]; - 'local.inc': number[]; - 'local.dec': number[]; - 'remote.total': number[]; - 'remote.inc': number[]; - 'remote.dec': number[]; + requests: { + failed: number[]; + succeeded: number[]; + received: number[]; + }; + notes: { + total: number[]; + inc: number[]; + dec: number[]; + diffs: { + normal: number[]; + reply: number[]; + renote: number[]; + withFile: number[]; + }; + }; + users: { + total: number[]; + inc: number[]; + dec: number[]; + }; + following: { + total: number[]; + inc: number[]; + dec: number[]; + }; + followers: { + total: number[]; + inc: number[]; + dec: number[]; + }; + drive: { + totalFiles: number[]; + incFiles: number[]; + decFiles: number[]; + incUsage: number[]; + decUsage: number[]; + }; }; }; }; @@ -11518,26 +12135,53 @@ export type operations = { }; }; /** - * clips/add-note + * charts/notes * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *No* */ - 'clips/add-note': { + 'charts/notes': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - clipId: string; - /** Format: misskey:id */ - noteId: string; + /** @enum {string} */ + span: 'day' | 'hour'; + /** @default 30 */ + limit?: number; + /** @default null */ + offset?: number | null; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + local: { + total: number[]; + inc: number[]; + dec: number[]; + diffs: { + normal: number[]; + reply: number[]; + renote: number[]; + withFile: number[]; + }; + }; + remote: { + total: number[]; + inc: number[]; + dec: number[]; + diffs: { + normal: number[]; + reply: number[]; + renote: number[]; + withFile: number[]; + }; + }; + }; + }; }; /** @description Client error */ 400: { @@ -11563,12 +12207,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -11578,26 +12216,39 @@ export type operations = { }; }; /** - * clips/remove-note + * charts/user/drive * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *No* */ - 'clips/remove-note': { + 'charts/user/drive': { requestBody: { content: { 'application/json': { + /** @enum {string} */ + span: 'day' | 'hour'; + /** @default 30 */ + limit?: number; + /** @default null */ + offset?: number | null; /** Format: misskey:id */ - clipId: string; - /** Format: misskey:id */ - noteId: string; + userId: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + totalCount: number[]; + totalSize: number[]; + incCount: number[]; + incSize: number[]; + decCount: number[]; + decSize: number[]; + }; + }; }; /** @description Client error */ 400: { @@ -11632,19 +12283,23 @@ export type operations = { }; }; /** - * clips/create + * charts/user/following * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *No* */ - 'clips/create': { + 'charts/user/following': { requestBody: { content: { 'application/json': { - name: string; - /** @default false */ - isPublic?: boolean; - description?: string | null; + /** @enum {string} */ + span: 'day' | 'hour'; + /** @default 30 */ + limit?: number; + /** @default null */ + offset?: number | null; + /** Format: misskey:id */ + userId: string; }; }; }; @@ -11652,7 +12307,32 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Clip']; + 'application/json': { + local: { + followings: { + total: number[]; + inc: number[]; + dec: number[]; + }; + followers: { + total: number[]; + inc: number[]; + dec: number[]; + }; + }; + remote: { + followings: { + total: number[]; + inc: number[]; + dec: number[]; + }; + followers: { + total: number[]; + inc: number[]; + dec: number[]; + }; + }; + }; }; }; /** @description Client error */ @@ -11688,24 +12368,42 @@ export type operations = { }; }; /** - * clips/delete + * charts/user/notes * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *No* */ - 'clips/delete': { + 'charts/user/notes': { requestBody: { content: { 'application/json': { + /** @enum {string} */ + span: 'day' | 'hour'; + /** @default 30 */ + limit?: number; + /** @default null */ + offset?: number | null; /** Format: misskey:id */ - clipId: string; + userId: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + total: number[]; + inc: number[]; + dec: number[]; + diffs: { + normal: number[]; + reply: number[]; + renote: number[]; + withFile: number[]; + }; + }; + }; }; /** @description Client error */ 400: { @@ -11740,17 +12438,40 @@ export type operations = { }; }; /** - * clips/list + * charts/user/pv * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *No* */ - 'clips/list': { + 'charts/user/pv': { + requestBody: { + content: { + 'application/json': { + /** @enum {string} */ + span: 'day' | 'hour'; + /** @default 30 */ + limit?: number; + /** @default null */ + offset?: number | null; + /** Format: misskey:id */ + userId: string; + }; + }; + }; responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Clip'][]; + 'application/json': { + upv: { + user: number[]; + visitor: number[]; + }; + pv: { + user: number[]; + visitor: number[]; + }; + }; }; }; /** @description Client error */ @@ -11786,23 +12507,23 @@ export type operations = { }; }; /** - * clips/notes + * charts/user/reactions * @description No description provided. * - * **Credential required**: *No* / **Permission**: *read:account* + * **Credential required**: *No* */ - 'clips/notes': { + 'charts/user/reactions': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - clipId: string; - /** @default 10 */ + /** @enum {string} */ + span: 'day' | 'hour'; + /** @default 30 */ limit?: number; + /** @default null */ + offset?: number | null; /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; + userId: string; }; }; }; @@ -11810,7 +12531,14 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Note'][]; + 'application/json': { + local: { + count: number[]; + }; + remote: { + count: number[]; + }; + }; }; }; /** @description Client error */ @@ -11846,17 +12574,21 @@ export type operations = { }; }; /** - * clips/show + * charts/users * @description No description provided. * - * **Credential required**: *No* / **Permission**: *read:account* + * **Credential required**: *No* */ - 'clips/show': { + 'charts/users': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - clipId: string; + /** @enum {string} */ + span: 'day' | 'hour'; + /** @default 30 */ + limit?: number; + /** @default null */ + offset?: number | null; }; }; }; @@ -11864,7 +12596,18 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Clip']; + 'application/json': { + local: { + total: number[]; + inc: number[]; + dec: number[]; + }; + remote: { + total: number[]; + inc: number[]; + dec: number[]; + }; + }; }; }; /** @description Client error */ @@ -11900,29 +12643,26 @@ export type operations = { }; }; /** - * clips/update + * clips/add-note * @description No description provided. * * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'clips/update': { + 'clips/add-note': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ clipId: string; - name: string; - isPublic?: boolean; - description?: string | null; + /** Format: misskey:id */ + noteId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Clip']; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -11948,6 +12688,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -11957,17 +12703,19 @@ export type operations = { }; }; /** - * clips/favorite + * clips/remove-note * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:clip-favorite* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'clips/favorite': { + 'clips/remove-note': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ clipId: string; + /** Format: misskey:id */ + noteId: string; }; }; }; @@ -12009,24 +12757,28 @@ export type operations = { }; }; /** - * clips/unfavorite + * clips/create * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:clip-favorite* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'clips/unfavorite': { + 'clips/create': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - clipId: string; + name: string; + /** @default false */ + isPublic?: boolean; + description?: string | null; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Clip']; + }; }; /** @description Client error */ 400: { @@ -12061,19 +12813,25 @@ export type operations = { }; }; /** - * clips/my-favorites + * clips/delete * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:clip-favorite* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'clips/my-favorites': { - responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Clip'][]; + 'clips/delete': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + clipId: string; }; }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; /** @description Client error */ 400: { content: { @@ -12107,20 +12865,17 @@ export type operations = { }; }; /** - * drive + * clips/list * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:drive* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - drive: { + 'clips/list': { responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': { - capacity: number; - usage: number; - }; + 'application/json': components['schemas']['Clip'][]; }; }; /** @description Client error */ @@ -12156,29 +12911,23 @@ export type operations = { }; }; /** - * drive/files + * clips/notes * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:drive* + * **Credential required**: *No* / **Permission**: *read:account* */ - 'drive/files': { + 'clips/notes': { requestBody: { content: { 'application/json': { + /** Format: misskey:id */ + clipId: string; /** @default 10 */ limit?: number; /** Format: misskey:id */ sinceId?: string; /** Format: misskey:id */ untilId?: string; - /** - * Format: misskey:id - * @default null - */ - folderId?: string | null; - type?: string | null; - /** @enum {string|null} */ - sort?: '+createdAt' | '-createdAt' | '+name' | '-name' | '+size' | '-size' | null; }; }; }; @@ -12186,7 +12935,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['DriveFile'][]; + 'application/json': components['schemas']['Note'][]; }; }; /** @description Client error */ @@ -12222,23 +12971,17 @@ export type operations = { }; }; /** - * drive/files/attached-notes - * @description Find the notes to which the given file is attached. + * clips/show + * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:drive* + * **Credential required**: *No* / **Permission**: *read:account* */ - 'drive/files/attached-notes': { + 'clips/show': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - fileId: string; + clipId: string; }; }; }; @@ -12246,7 +12989,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Note'][]; + 'application/json': components['schemas']['Clip']; }; }; /** @description Client error */ @@ -12282,16 +13025,20 @@ export type operations = { }; }; /** - * drive/files/check-existence - * @description Check if a given file exists. + * clips/update + * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:drive* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'drive/files/check-existence': { + 'clips/update': { requestBody: { content: { 'application/json': { - md5: string; + /** Format: misskey:id */ + clipId: string; + name: string; + isPublic?: boolean; + description?: string | null; }; }; }; @@ -12299,7 +13046,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': boolean; + 'application/json': components['schemas']['Clip']; }; }; /** @description Client error */ @@ -12335,42 +13082,24 @@ export type operations = { }; }; /** - * drive/files/create - * @description Upload a new drive file. + * clips/favorite + * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:drive* + * **Credential required**: *Yes* / **Permission**: *write:clip-favorite* */ - 'drive/files/create': { + 'clips/favorite': { requestBody: { content: { - 'multipart/form-data': { - /** - * Format: misskey:id - * @default null - */ - folderId?: string | null; - /** @default null */ - name?: string | null; - /** @default null */ - comment?: string | null; - /** @default false */ - isSensitive?: boolean; - /** @default false */ - force?: boolean; - /** - * Format: binary - * @description The file contents. - */ - file: string; + 'application/json': { + /** Format: misskey:id */ + clipId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['DriveFile']; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -12396,12 +13125,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -12411,17 +13134,17 @@ export type operations = { }; }; /** - * drive/files/delete - * @description Delete an existing drive file. + * clips/unfavorite + * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:drive* + * **Credential required**: *Yes* / **Permission**: *write:clip-favorite* */ - 'drive/files/delete': { + 'clips/unfavorite': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - fileId: string; + clipId: string; }; }; }; @@ -12463,24 +13186,17 @@ export type operations = { }; }; /** - * drive/files/find-by-hash - * @description Search for a drive file by a hash of the contents. + * clips/my-favorites + * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:drive* + * **Credential required**: *Yes* / **Permission**: *read:clip-favorite* */ - 'drive/files/find-by-hash': { - requestBody: { - content: { - 'application/json': { - md5: string; - }; - }; - }; + 'clips/my-favorites': { responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['DriveFile'][]; + 'application/json': components['schemas']['Clip'][]; }; }; /** @description Client error */ @@ -12516,29 +13232,20 @@ export type operations = { }; }; /** - * drive/files/find - * @description Search for a drive file by the given parameters. + * drive + * @description No description provided. * * **Credential required**: *Yes* / **Permission**: *read:drive* */ - 'drive/files/find': { - requestBody: { - content: { - 'application/json': { - name: string; - /** - * Format: misskey:id - * @default null - */ - folderId?: string | null; - }; - }; - }; + drive: { responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['DriveFile'][]; + 'application/json': { + capacity: number; + usage: number; + }; }; }; /** @description Client error */ @@ -12574,26 +13281,37 @@ export type operations = { }; }; /** - * drive/files/show - * @description Show the properties of a drive file. + * drive/files + * @description No description provided. * * **Credential required**: *Yes* / **Permission**: *read:drive* */ - 'drive/files/show': { + 'drive/files': { requestBody: { content: { 'application/json': { + /** @default 10 */ + limit?: number; /** Format: misskey:id */ - fileId?: string; - url?: string; - }; - }; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** + * Format: misskey:id + * @default null + */ + folderId?: string | null; + type?: string | null; + /** @enum {string|null} */ + sort?: '+createdAt' | '-createdAt' | '+name' | '-name' | '+size' | '-size' | null; + }; + }; }; responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['DriveFile']; + 'application/json': components['schemas']['DriveFile'][]; }; }; /** @description Client error */ @@ -12629,22 +13347,23 @@ export type operations = { }; }; /** - * drive/files/update - * @description Update the properties of a drive file. + * drive/files/attached-notes + * @description Find the notes to which the given file is attached. * - * **Credential required**: *Yes* / **Permission**: *write:drive* + * **Credential required**: *Yes* / **Permission**: *read:drive* */ - 'drive/files/update': { + 'drive/files/attached-notes': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - fileId: string; + sinceId?: string; /** Format: misskey:id */ - folderId?: string | null; - name?: string; - isSensitive?: boolean; - comment?: string | null; + untilId?: string; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + fileId: string; }; }; }; @@ -12652,7 +13371,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['DriveFile']; + 'application/json': components['schemas']['Note'][]; }; }; /** @description Client error */ @@ -12688,36 +13407,25 @@ export type operations = { }; }; /** - * drive/files/upload-from-url - * @description Request the server to download a new drive file from the specified URL. + * drive/files/check-existence + * @description Check if a given file exists. * - * **Credential required**: *Yes* / **Permission**: *write:drive* + * **Credential required**: *Yes* / **Permission**: *read:drive* */ - 'drive/files/upload-from-url': { + 'drive/files/check-existence': { requestBody: { content: { 'application/json': { - url: string; - /** - * Format: misskey:id - * @default null - */ - folderId?: string | null; - /** @default false */ - isSensitive?: boolean; - /** @default null */ - comment?: string | null; - /** @default null */ - marker?: string | null; - /** @default false */ - force?: boolean; + md5: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': boolean; + }; }; /** @description Client error */ 400: { @@ -12743,12 +13451,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -12758,26 +13460,33 @@ export type operations = { }; }; /** - * drive/folders - * @description No description provided. + * drive/files/create + * @description Upload a new drive file. * - * **Credential required**: *Yes* / **Permission**: *read:drive* + * **Credential required**: *Yes* / **Permission**: *write:drive* */ - 'drive/folders': { + 'drive/files/create': { requestBody: { content: { - 'application/json': { - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; + 'multipart/form-data': { /** * Format: misskey:id * @default null */ folderId?: string | null; + /** @default null */ + name?: string | null; + /** @default null */ + comment?: string | null; + /** @default false */ + isSensitive?: boolean; + /** @default false */ + force?: boolean; + /** + * Format: binary + * @description The file contents. + */ + file: string; }; }; }; @@ -12785,7 +13494,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['DriveFolder'][]; + 'application/json': components['schemas']['DriveFile']; }; }; /** @description Client error */ @@ -12812,6 +13521,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -12821,28 +13536,24 @@ export type operations = { }; }; /** - * drive/folders/create - * @description No description provided. + * drive/files/delete + * @description Delete an existing drive file. * * **Credential required**: *Yes* / **Permission**: *write:drive* */ - 'drive/folders/create': { + 'drive/files/delete': { requestBody: { content: { 'application/json': { - /** @default Untitled */ - name?: string; /** Format: misskey:id */ - parentId?: string | null; + fileId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['DriveFolder']; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -12868,12 +13579,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -12883,24 +13588,25 @@ export type operations = { }; }; /** - * drive/folders/delete - * @description No description provided. + * drive/files/find-by-hash + * @description Search for a drive file by a hash of the contents. * - * **Credential required**: *Yes* / **Permission**: *write:drive* + * **Credential required**: *Yes* / **Permission**: *read:drive* */ - 'drive/folders/delete': { + 'drive/files/find-by-hash': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - folderId: string; + md5: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['DriveFile'][]; + }; }; /** @description Client error */ 400: { @@ -12935,12 +13641,12 @@ export type operations = { }; }; /** - * drive/folders/find - * @description No description provided. + * drive/files/find + * @description Search for a drive file by the given parameters. * * **Credential required**: *Yes* / **Permission**: *read:drive* */ - 'drive/folders/find': { + 'drive/files/find': { requestBody: { content: { 'application/json': { @@ -12949,7 +13655,7 @@ export type operations = { * Format: misskey:id * @default null */ - parentId?: string | null; + folderId?: string | null; }; }; }; @@ -12957,7 +13663,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['DriveFolder'][]; + 'application/json': components['schemas']['DriveFile'][]; }; }; /** @description Client error */ @@ -12993,17 +13699,18 @@ export type operations = { }; }; /** - * drive/folders/show - * @description No description provided. + * drive/files/show + * @description Show the properties of a drive file. * * **Credential required**: *Yes* / **Permission**: *read:drive* */ - 'drive/folders/show': { + 'drive/files/show': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - folderId: string; + fileId?: string; + url?: string; }; }; }; @@ -13011,7 +13718,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['DriveFolder']; + 'application/json': components['schemas']['DriveFile']; }; }; /** @description Client error */ @@ -13047,20 +13754,22 @@ export type operations = { }; }; /** - * drive/folders/update - * @description No description provided. + * drive/files/update + * @description Update the properties of a drive file. * * **Credential required**: *Yes* / **Permission**: *write:drive* */ - 'drive/folders/update': { + 'drive/files/update': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - folderId: string; - name?: string; + fileId: string; /** Format: misskey:id */ - parentId?: string | null; + folderId?: string | null; + name?: string; + isSensitive?: boolean; + comment?: string | null; }; }; }; @@ -13068,7 +13777,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['DriveFolder']; + 'application/json': components['schemas']['DriveFile']; }; }; /** @description Client error */ @@ -13104,31 +13813,36 @@ export type operations = { }; }; /** - * drive/stream - * @description No description provided. + * drive/files/upload-from-url + * @description Request the server to download a new drive file from the specified URL. * - * **Credential required**: *Yes* / **Permission**: *read:drive* + * **Credential required**: *Yes* / **Permission**: *write:drive* */ - 'drive/stream': { + 'drive/files/upload-from-url': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - type?: string; + url: string; + /** + * Format: misskey:id + * @default null + */ + folderId?: string | null; + /** @default false */ + isSensitive?: boolean; + /** @default null */ + comment?: string | null; + /** @default null */ + marker?: string | null; + /** @default false */ + force?: boolean; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['DriveFile'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -13154,6 +13868,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -13163,16 +13883,26 @@ export type operations = { }; }; /** - * email-address/available + * drive/folders * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *read:drive* */ - 'email-address/available': { + 'drive/folders': { requestBody: { content: { 'application/json': { - emailAddress: string; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** + * Format: misskey:id + * @default null + */ + folderId?: string | null; }; }; }; @@ -13180,10 +13910,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - available: boolean; - reason: string | null; - }; + 'application/json': components['schemas']['DriveFolder'][]; }; }; /** @description Client error */ @@ -13219,16 +13946,19 @@ export type operations = { }; }; /** - * endpoint + * drive/folders/create * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:drive* */ - endpoint: { + 'drive/folders/create': { requestBody: { content: { 'application/json': { - endpoint: string; + /** @default Untitled */ + name?: string; + /** Format: misskey:id */ + parentId?: string | null; }; }; }; @@ -13236,18 +13966,9 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - params: { - name: string; - type: string; - }[]; - } | null; + 'application/json': components['schemas']['DriveFolder']; }; }; - /** @description OK (without any results) */ - 204: { - content: never; - }; /** @description Client error */ 400: { content: { @@ -13272,6 +13993,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -13281,19 +14008,25 @@ export type operations = { }; }; /** - * endpoints + * drive/folders/delete * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:drive* */ - endpoints: { - responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': string[]; + 'drive/folders/delete': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + folderId: string; }; }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; /** @description Client error */ 400: { content: { @@ -13327,17 +14060,30 @@ export type operations = { }; }; /** - * export-custom-emojis + * drive/folders/find * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *read:drive* */ - 'export-custom-emojis': { + 'drive/folders/find': { + requestBody: { + content: { + 'application/json': { + name: string; + /** + * Format: misskey:id + * @default null + */ + parentId?: string | null; + }; + }; + }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['DriveFolder'][]; + }; }; /** @description Client error */ 400: { @@ -13363,12 +14109,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -13378,22 +14118,17 @@ export type operations = { }; }; /** - * federation/followers + * drive/folders/show * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *read:drive* */ - 'federation/followers': { + 'drive/folders/show': { requestBody: { content: { 'application/json': { - host: string; - /** Format: misskey:id */ - sinceId?: string; /** Format: misskey:id */ - untilId?: string; - /** @default 10 */ - limit?: number; + folderId: string; }; }; }; @@ -13401,7 +14136,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Following'][]; + 'application/json': components['schemas']['DriveFolder']; }; }; /** @description Client error */ @@ -13437,22 +14172,20 @@ export type operations = { }; }; /** - * federation/following + * drive/folders/update * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:drive* */ - 'federation/following': { + 'drive/folders/update': { requestBody: { content: { 'application/json': { - host: string; /** Format: misskey:id */ - sinceId?: string; + folderId: string; + name?: string; /** Format: misskey:id */ - untilId?: string; - /** @default 10 */ - limit?: number; + parentId?: string | null; }; }; }; @@ -13460,7 +14193,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Following'][]; + 'application/json': components['schemas']['DriveFolder']; }; }; /** @description Client error */ @@ -13496,30 +14229,22 @@ export type operations = { }; }; /** - * federation/instances + * drive/stream * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *read:drive* */ - 'federation/instances': { + 'drive/stream': { requestBody: { content: { 'application/json': { - /** @description Omit or use `null` to not filter by host. */ - host?: string | null; - blocked?: boolean | null; - notResponding?: boolean | null; - suspended?: boolean | null; - silenced?: boolean | null; - federating?: boolean | null; - subscribing?: boolean | null; - publishing?: boolean | null; - /** @default 30 */ + /** @default 10 */ limit?: number; - /** @default 0 */ - offset?: number; - /** @enum {string|null} */ - sort?: '+pubSub' | '-pubSub' | '+notes' | '-notes' | '+users' | '-users' | '+following' | '-following' | '+followers' | '-followers' | '+firstRetrievedAt' | '-firstRetrievedAt' | '+latestRequestReceivedAt' | '-latestRequestReceivedAt' | null; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + type?: string; }; }; }; @@ -13527,7 +14252,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['FederationInstance'][]; + 'application/json': components['schemas']['DriveFile'][]; }; }; /** @description Client error */ @@ -13563,16 +14288,16 @@ export type operations = { }; }; /** - * federation/show-instance + * email-address/available * @description No description provided. * * **Credential required**: *No* */ - 'federation/show-instance': { + 'email-address/available': { requestBody: { content: { 'application/json': { - host: string; + emailAddress: string; }; }; }; @@ -13580,13 +14305,12 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['FederationInstance'] | null; + 'application/json': { + available: boolean; + reason: string | null; + }; }; }; - /** @description OK (without any results) */ - 204: { - content: never; - }; /** @description Client error */ 400: { content: { @@ -13620,21 +14344,31 @@ export type operations = { }; }; /** - * federation/update-remote-user + * endpoint * @description No description provided. * * **Credential required**: *No* */ - 'federation/update-remote-user': { + endpoint: { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - userId: string; + endpoint: string; }; }; }; responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + params: { + name: string; + type: string; + }[]; + } | null; + }; + }; /** @description OK (without any results) */ 204: { content: never; @@ -13672,30 +14406,17 @@ export type operations = { }; }; /** - * federation/users + * endpoints * @description No description provided. * * **Credential required**: *No* */ - 'federation/users': { - requestBody: { - content: { - 'application/json': { - host: string; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - /** @default 10 */ - limit?: number; - }; - }; - }; + endpoints: { responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['UserDetailedNotMe'][]; + 'application/json': string[]; }; }; /** @description Client error */ @@ -13731,31 +14452,17 @@ export type operations = { }; }; /** - * federation/stats + * export-custom-emojis * @description No description provided. * - * **Credential required**: *No* + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* */ - 'federation/stats': { - requestBody: { - content: { - 'application/json': { - /** @default 10 */ - limit?: number; - }; - }; - }; + 'export-custom-emojis': { responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': { - topSubInstances: components['schemas']['FederationInstance'][]; - otherFollowersCount: number; - topPubInstances: components['schemas']['FederationInstance'][]; - otherFollowingCount: number; - }; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -13781,6 +14488,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -13790,18 +14503,22 @@ export type operations = { }; }; /** - * following/create + * federation/followers * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:following* + * **Credential required**: *No* */ - 'following/create': { + 'federation/followers': { requestBody: { content: { 'application/json': { + host: string; /** Format: misskey:id */ - userId: string; - withReplies?: boolean; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default 10 */ + limit?: number; }; }; }; @@ -13809,7 +14526,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['UserLite']; + 'application/json': components['schemas']['Following'][]; }; }; /** @description Client error */ @@ -13836,12 +14553,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -13851,17 +14562,22 @@ export type operations = { }; }; /** - * following/delete + * federation/following * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:following* + * **Credential required**: *No* */ - 'following/delete': { + 'federation/following': { requestBody: { content: { 'application/json': { + host: string; /** Format: misskey:id */ - userId: string; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default 10 */ + limit?: number; }; }; }; @@ -13869,7 +14585,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['UserLite']; + 'application/json': components['schemas']['Following'][]; }; }; /** @description Client error */ @@ -13896,12 +14612,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -13911,20 +14621,32 @@ export type operations = { }; }; /** - * following/update + * federation/instances * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:following* + * **Credential required**: *No* */ - 'following/update': { + 'federation/instances': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - userId: string; - /** @enum {string} */ - notify?: 'normal' | 'none'; - withReplies?: boolean; + /** @description Omit or use `null` to not filter by host. */ + host?: string | null; + blocked?: boolean | null; + notResponding?: boolean | null; + suspended?: boolean | null; + silenced?: boolean | null; + federating?: boolean | null; + subscribing?: boolean | null; + publishing?: boolean | null; + nsfw?: boolean | null; + bubble?: boolean | null; + /** @default 30 */ + limit?: number; + /** @default 0 */ + offset?: number; + /** @enum {string|null} */ + sort?: '+pubSub' | '-pubSub' | '+notes' | '-notes' | '+users' | '-users' | '+following' | '-following' | '+followers' | '-followers' | '+firstRetrievedAt' | '-firstRetrievedAt' | '+latestRequestReceivedAt' | '-latestRequestReceivedAt' | null; }; }; }; @@ -13932,7 +14654,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['UserLite']; + 'application/json': components['schemas']['FederationInstance'][]; }; }; /** @description Client error */ @@ -13959,12 +14681,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -13974,22 +14690,26 @@ export type operations = { }; }; /** - * following/update-all + * federation/show-instance * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:following* + * **Credential required**: *No* */ - 'following/update-all': { + 'federation/show-instance': { requestBody: { content: { 'application/json': { - /** @enum {string} */ - notify?: 'normal' | 'none'; - withReplies?: boolean; + host: string; }; }; }; responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['FederationInstance'] | null; + }; + }; /** @description OK (without any results) */ 204: { content: never; @@ -14018,12 +14738,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -14033,12 +14747,12 @@ export type operations = { }; }; /** - * following/invalidate + * federation/update-remote-user * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:following* + * **Credential required**: *No* */ - 'following/invalidate': { + 'federation/update-remote-user': { requestBody: { content: { 'application/json': { @@ -14048,11 +14762,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['UserLite']; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -14078,12 +14790,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -14093,24 +14799,31 @@ export type operations = { }; }; /** - * following/requests/accept + * federation/users * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:following* + * **Credential required**: *No* */ - 'following/requests/accept': { + 'federation/users': { requestBody: { content: { 'application/json': { + host: string; /** Format: misskey:id */ - userId: string; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default 10 */ + limit?: number; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['UserDetailedNotMe'][]; + }; }; /** @description Client error */ 400: { @@ -14145,17 +14858,17 @@ export type operations = { }; }; /** - * following/requests/cancel + * federation/stats * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:following* + * **Credential required**: *No* */ - 'following/requests/cancel': { + 'federation/stats': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - userId: string; + /** @default 10 */ + limit?: number; }; }; }; @@ -14163,7 +14876,12 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['UserLite']; + 'application/json': { + topSubInstances: components['schemas']['FederationInstance'][]; + otherFollowersCount: number; + topPubInstances: components['schemas']['FederationInstance'][]; + otherFollowingCount: number; + }; }; }; /** @description Client error */ @@ -14199,21 +14917,18 @@ export type operations = { }; }; /** - * following/requests/list + * following/create * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:following* + * **Credential required**: *Yes* / **Permission**: *write:following* */ - 'following/requests/list': { + 'following/create': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - /** @default 10 */ - limit?: number; + userId: string; + withReplies?: boolean; }; }; }; @@ -14221,12 +14936,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - /** Format: id */ - id: string; - follower: components['schemas']['UserLite']; - followee: components['schemas']['UserLite']; - }[]; + 'application/json': components['schemas']['UserLite']; }; }; /** @description Client error */ @@ -14253,6 +14963,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -14262,12 +14978,12 @@ export type operations = { }; }; /** - * following/requests/reject + * following/delete * @description No description provided. * * **Credential required**: *Yes* / **Permission**: *write:following* */ - 'following/requests/reject': { + 'following/delete': { requestBody: { content: { 'application/json': { @@ -14277,9 +14993,11 @@ export type operations = { }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['UserLite']; + }; }; /** @description Client error */ 400: { @@ -14305,6 +15023,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -14314,19 +15038,20 @@ export type operations = { }; }; /** - * gallery/featured + * following/update * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:following* */ - 'gallery/featured': { + 'following/update': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; /** Format: misskey:id */ - untilId?: string; + userId: string; + /** @enum {string} */ + notify?: 'normal' | 'none'; + withReplies?: boolean; }; }; }; @@ -14334,7 +15059,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['GalleryPost'][]; + 'application/json': components['schemas']['UserLite']; }; }; /** @description Client error */ @@ -14361,6 +15086,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -14370,19 +15101,26 @@ export type operations = { }; }; /** - * gallery/popular + * following/update-all * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:following* */ - 'gallery/popular': { - responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['GalleryPost'][]; + 'following/update-all': { + requestBody: { + content: { + 'application/json': { + /** @enum {string} */ + notify?: 'normal' | 'none'; + withReplies?: boolean; }; }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; /** @description Client error */ 400: { content: { @@ -14407,6 +15145,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -14416,21 +15160,17 @@ export type operations = { }; }; /** - * gallery/posts + * following/invalidate * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:following* */ - 'gallery/posts': { + 'following/invalidate': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; /** Format: misskey:id */ - untilId?: string; + userId: string; }; }; }; @@ -14438,7 +15178,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['GalleryPost'][]; + 'application/json': components['schemas']['UserLite']; }; }; /** @description Client error */ @@ -14465,6 +15205,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -14474,29 +15220,24 @@ export type operations = { }; }; /** - * gallery/posts/create + * following/requests/accept * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:gallery* + * **Credential required**: *Yes* / **Permission**: *write:following* */ - 'gallery/posts/create': { + 'following/requests/accept': { requestBody: { content: { 'application/json': { - title: string; - description?: string | null; - fileIds: string[]; - /** @default false */ - isSensitive?: boolean; + /** Format: misskey:id */ + userId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['GalleryPost']; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -14522,12 +15263,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -14537,24 +15272,26 @@ export type operations = { }; }; /** - * gallery/posts/delete + * following/requests/cancel * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:gallery* + * **Credential required**: *Yes* / **Permission**: *write:following* */ - 'gallery/posts/delete': { + 'following/requests/cancel': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - postId: string; + userId: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['UserLite']; + }; }; /** @description Client error */ 400: { @@ -14589,24 +15326,35 @@ export type operations = { }; }; /** - * gallery/posts/like + * following/requests/list * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:gallery-likes* + * **Credential required**: *Yes* / **Permission**: *read:following* */ - 'gallery/posts/like': { + 'following/requests/list': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - postId: string; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default 10 */ + limit?: number; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + /** Format: id */ + id: string; + follower: components['schemas']['UserLite']; + followee: components['schemas']['UserLite']; + }[]; + }; }; /** @description Client error */ 400: { @@ -14641,26 +15389,24 @@ export type operations = { }; }; /** - * gallery/posts/show + * following/requests/reject * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:following* */ - 'gallery/posts/show': { + 'following/requests/reject': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - postId: string; + userId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['GalleryPost']; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -14695,24 +15441,28 @@ export type operations = { }; }; /** - * gallery/posts/unlike + * gallery/featured * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:gallery-likes* + * **Credential required**: *No* */ - 'gallery/posts/unlike': { + 'gallery/featured': { requestBody: { content: { 'application/json': { + /** @default 10 */ + limit?: number; /** Format: misskey:id */ - postId: string; + untilId?: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['GalleryPost'][]; + }; }; /** @description Client error */ 400: { @@ -14747,30 +15497,17 @@ export type operations = { }; }; /** - * gallery/posts/update + * gallery/popular * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:gallery* + * **Credential required**: *No* */ - 'gallery/posts/update': { - requestBody: { - content: { - 'application/json': { - /** Format: misskey:id */ - postId: string; - title: string; - description?: string | null; - fileIds: string[]; - /** @default false */ - isSensitive?: boolean; - }; - }; - }; + 'gallery/popular': { responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['GalleryPost']; + 'application/json': components['schemas']['GalleryPost'][]; }; }; /** @description Client error */ @@ -14797,12 +15534,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -14812,19 +15543,29 @@ export type operations = { }; }; /** - * get-online-users-count + * gallery/posts * @description No description provided. * * **Credential required**: *No* */ - 'get-online-users-count': { + 'gallery/posts': { + requestBody: { + content: { + 'application/json': { + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + }; + }; + }; responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': { - count: number; - }; + 'application/json': components['schemas']['GalleryPost'][]; }; }; /** @description Client error */ @@ -14860,27 +15601,28 @@ export type operations = { }; }; /** - * get-avatar-decorations + * gallery/posts/create * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:gallery* */ - 'get-avatar-decorations': { + 'gallery/posts/create': { + requestBody: { + content: { + 'application/json': { + title: string; + description?: string | null; + fileIds: string[]; + /** @default false */ + isSensitive?: boolean; + }; + }; + }; responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': { - /** - * Format: id - * @example xxxxxxxxxx - */ - id: string; - name: string; - description: string; - url: string; - roleIdsThatCanBeUsedThisDecoration: string[]; - }[]; + 'application/json': components['schemas']['GalleryPost']; }; }; /** @description Client error */ @@ -14907,6 +15649,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -14916,34 +15664,24 @@ export type operations = { }; }; /** - * hashtags/list + * gallery/posts/delete * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:gallery* */ - 'hashtags/list': { + 'gallery/posts/delete': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** @default false */ - attachedToUserOnly?: boolean; - /** @default false */ - attachedToLocalUserOnly?: boolean; - /** @default false */ - attachedToRemoteUserOnly?: boolean; - /** @enum {string} */ - sort: '+mentionedUsers' | '-mentionedUsers' | '+mentionedLocalUsers' | '-mentionedLocalUsers' | '+mentionedRemoteUsers' | '-mentionedRemoteUsers' | '+attachedUsers' | '-attachedUsers' | '+attachedLocalUsers' | '-attachedLocalUsers' | '+attachedRemoteUsers' | '-attachedRemoteUsers'; + /** Format: misskey:id */ + postId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Hashtag'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -14978,29 +15716,24 @@ export type operations = { }; }; /** - * hashtags/search + * gallery/posts/like * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:gallery-likes* */ - 'hashtags/search': { + 'gallery/posts/like': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - query: string; - /** @default 0 */ - offset?: number; + /** Format: misskey:id */ + postId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': string[]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -15035,16 +15768,17 @@ export type operations = { }; }; /** - * hashtags/show + * gallery/posts/show * @description No description provided. * * **Credential required**: *No* */ - 'hashtags/show': { + 'gallery/posts/show': { requestBody: { content: { 'application/json': { - tag: string; + /** Format: misskey:id */ + postId: string; }; }; }; @@ -15052,7 +15786,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Hashtag']; + 'application/json': components['schemas']['GalleryPost']; }; }; /** @description Client error */ @@ -15088,23 +15822,25 @@ export type operations = { }; }; /** - * hashtags/trend + * gallery/posts/unlike * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:gallery-likes* */ - 'hashtags/trend': { - responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': { - tag: string; - chart: number[]; - usersCount: number; - }[]; + 'gallery/posts/unlike': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + postId: string; }; }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; /** @description Client error */ 400: { content: { @@ -15138,30 +15874,22 @@ export type operations = { }; }; /** - * hashtags/users + * gallery/posts/update * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:gallery* */ - 'hashtags/users': { + 'gallery/posts/update': { requestBody: { content: { 'application/json': { - tag: string; - /** @default 10 */ - limit?: number; - /** @enum {string} */ - sort: '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+updatedAt' | '-updatedAt'; - /** - * @default all - * @enum {string} - */ - state?: 'all' | 'alive'; - /** - * @default local - * @enum {string} - */ - origin?: 'combined' | 'local' | 'remote'; + /** Format: misskey:id */ + postId: string; + title: string; + description?: string | null; + fileIds: string[]; + /** @default false */ + isSensitive?: boolean; }; }; }; @@ -15169,7 +15897,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['UserDetailed'][]; + 'application/json': components['schemas']['GalleryPost']; }; }; /** @description Client error */ @@ -15196,6 +15924,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -15205,17 +15939,19 @@ export type operations = { }; }; /** - * i + * get-online-users-count * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *No* */ - i: { + 'get-online-users-count': { responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['MeDetailed']; + 'application/json': { + count: number; + }; }; }; /** @description Client error */ @@ -15251,24 +15987,90 @@ export type operations = { }; }; /** - * i/2fa/done + * get-avatar-decorations * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *No* */ - 'i/2fa/done': { + 'get-avatar-decorations': { + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + /** + * Format: id + * @example xxxxxxxxxx + */ + id: string; + name: string; + description: string; + url: string; + roleIdsThatCanBeUsedThisDecoration: string[]; + }[]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * hashtags/list + * @description No description provided. + * + * **Credential required**: *No* + */ + 'hashtags/list': { requestBody: { content: { 'application/json': { - token: string; + /** @default 10 */ + limit?: number; + /** @default false */ + attachedToUserOnly?: boolean; + /** @default false */ + attachedToLocalUserOnly?: boolean; + /** @default false */ + attachedToRemoteUserOnly?: boolean; + /** @enum {string} */ + sort: '+mentionedUsers' | '-mentionedUsers' | '+mentionedLocalUsers' | '-mentionedLocalUsers' | '+mentionedRemoteUsers' | '-mentionedRemoteUsers' | '+attachedUsers' | '-attachedUsers' | '+attachedLocalUsers' | '-attachedLocalUsers' | '+attachedRemoteUsers' | '-attachedRemoteUsers'; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Hashtag'][]; + }; }; /** @description Client error */ 400: { @@ -15303,20 +16105,20 @@ export type operations = { }; }; /** - * i/2fa/key-done + * hashtags/search * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *No* */ - 'i/2fa/key-done': { + 'hashtags/search': { requestBody: { content: { 'application/json': { - password: string; - token?: string | null; - name: string; - credential: Record<string, never>; + /** @default 10 */ + limit?: number; + query: string; + /** @default 0 */ + offset?: number; }; }; }; @@ -15324,10 +16126,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - id: string; - name: string; - }; + 'application/json': string[]; }; }; /** @description Client error */ @@ -15363,24 +16162,25 @@ export type operations = { }; }; /** - * i/2fa/password-less + * hashtags/show * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *No* */ - 'i/2fa/password-less': { + 'hashtags/show': { requestBody: { content: { 'application/json': { - value: boolean; + tag: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Hashtag']; + }; }; /** @description Client error */ 400: { @@ -15415,18 +16215,80 @@ export type operations = { }; }; /** - * i/2fa/register-key + * hashtags/trend * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *No* */ - 'i/2fa/register-key': { + 'hashtags/trend': { + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + tag: string; + chart: number[]; + usersCount: number; + }[]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * hashtags/users + * @description No description provided. + * + * **Credential required**: *No* + */ + 'hashtags/users': { requestBody: { content: { 'application/json': { - password: string; - token?: string | null; + tag: string; + /** @default 10 */ + limit?: number; + /** @enum {string} */ + sort: '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+updatedAt' | '-updatedAt'; + /** + * @default all + * @enum {string} + */ + state?: 'all' | 'alive'; + /** + * @default local + * @enum {string} + */ + origin?: 'combined' | 'local' | 'remote'; }; }; }; @@ -15434,40 +16296,168 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - rp: { - id: string | null; - }; - user: { - id: string; - name: string; - displayName: string; - }; - challenge: string; - pubKeyCredParams: { - type: string; - alg: number; - }[]; - timeout: number | null; - excludeCredentials: (({ - id: string; - type: string; - transports: ('ble' | 'cable' | 'hybrid' | 'internal' | 'nfc' | 'smart-card' | 'usb')[]; - })[]) | null; - authenticatorSelection: ({ - /** @enum {string} */ - authenticatorAttachment: 'cross-platform' | 'platform'; - requireResidentKey: boolean; - /** @enum {string} */ - userVerification: 'discouraged' | 'preferred' | 'required'; - }) | null; - /** @enum {string|null} */ - attestation: 'direct' | 'enterprise' | 'indirect' | 'none' | null; - extensions: ({ - appid: string | null; - credProps: boolean | null; - hmacCreateSecret: boolean | null; - }) | null; + 'application/json': components['schemas']['UserDetailed'][]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:account* + */ + i: { + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['MeDetailed']; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/2fa/done + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/2fa/done': { + requestBody: { + content: { + 'application/json': { + token: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + backupCodes: string[]; + }; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/2fa/key-done + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/2fa/key-done': { + requestBody: { + content: { + 'application/json': { + password: string; + token?: string | null; + name: string; + credential: Record<string, never>; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + id: string; + name: string; }; }; }; @@ -15504,18 +16494,1264 @@ export type operations = { }; }; /** - * i/2fa/register + * i/2fa/password-less + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/2fa/password-less': { + requestBody: { + content: { + 'application/json': { + value: boolean; + }; + }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/2fa/register-key + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/2fa/register-key': { + requestBody: { + content: { + 'application/json': { + password: string; + token?: string | null; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + rp: { + id?: string; + }; + user: { + id: string; + name: string; + displayName: string; + }; + challenge: string; + pubKeyCredParams: { + type: string; + alg: number; + }[]; + timeout: number | null; + excludeCredentials: (({ + id: string; + type: string; + transports: ('ble' | 'cable' | 'hybrid' | 'internal' | 'nfc' | 'smart-card' | 'usb')[]; + })[]) | null; + authenticatorSelection: ({ + /** @enum {string} */ + authenticatorAttachment: 'cross-platform' | 'platform'; + requireResidentKey: boolean; + /** @enum {string} */ + userVerification: 'discouraged' | 'preferred' | 'required'; + }) | null; + /** @enum {string|null} */ + attestation: 'direct' | 'enterprise' | 'indirect' | 'none' | null; + extensions: ({ + appid: string | null; + credProps: boolean | null; + hmacCreateSecret: boolean | null; + }) | null; + }; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/2fa/register + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/2fa/register': { + requestBody: { + content: { + 'application/json': { + password: string; + token?: string | null; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + qr: string; + url: string; + secret: string; + label: string; + issuer: string; + }; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/2fa/update-key + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/2fa/update-key': { + requestBody: { + content: { + 'application/json': { + name: string; + credentialId: string; + }; + }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/2fa/remove-key + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/2fa/remove-key': { + requestBody: { + content: { + 'application/json': { + password: string; + token?: string | null; + credentialId: string; + }; + }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/2fa/unregister + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/2fa/unregister': { + requestBody: { + content: { + 'application/json': { + password: string; + token?: string | null; + }; + }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/apps + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/apps': { + requestBody: { + content: { + 'application/json': { + /** @enum {string} */ + sort?: '+createdAt' | '-createdAt' | '+lastUsedAt' | '-lastUsedAt'; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + /** Format: misskey:id */ + id: string; + name?: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + lastUsedAt?: string; + permission: string[]; + }[]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/authorized-apps + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/authorized-apps': { + requestBody: { + content: { + 'application/json': { + /** @default 10 */ + limit?: number; + /** @default 0 */ + offset?: number; + /** + * @default desc + * @enum {string} + */ + sort?: 'desc' | 'asc'; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': ({ + /** Format: misskey:id */ + id: string; + name: string; + callbackUrl: string | null; + permission: string[]; + isAuthorized?: boolean; + })[]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/claim-achievement + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:account* + */ + 'i/claim-achievement': { + requestBody: { + content: { + 'application/json': { + /** @enum {string} */ + name: 'notes1' | 'notes10' | 'notes100' | 'notes500' | 'notes1000' | 'notes5000' | 'notes10000' | 'notes20000' | 'notes30000' | 'notes40000' | 'notes50000' | 'notes60000' | 'notes70000' | 'notes80000' | 'notes90000' | 'notes100000' | 'login3' | 'login7' | 'login15' | 'login30' | 'login60' | 'login100' | 'login200' | 'login300' | 'login400' | 'login500' | 'login600' | 'login700' | 'login800' | 'login900' | 'login1000' | 'passedSinceAccountCreated1' | 'passedSinceAccountCreated2' | 'passedSinceAccountCreated3' | 'loggedInOnBirthday' | 'loggedInOnNewYearsDay' | 'noteClipped1' | 'noteFavorited1' | 'myNoteFavorited1' | 'profileFilled' | 'markedAsCat' | 'following1' | 'following10' | 'following50' | 'following100' | 'following300' | 'followers1' | 'followers10' | 'followers50' | 'followers100' | 'followers300' | 'followers500' | 'followers1000' | 'collectAchievements30' | 'viewAchievements3min' | 'iLoveMisskey' | 'foundTreasure' | 'client30min' | 'client60min' | 'noteDeletedWithin1min' | 'postedAtLateNight' | 'postedAt0min0sec' | 'selfQuote' | 'htl20npm' | 'viewInstanceChart' | 'outputHelloWorldOnScratchpad' | 'open3windows' | 'driveFolderCircularReference' | 'reactWithoutRead' | 'clickedClickHere' | 'justPlainLucky' | 'setNameToSyuilo' | 'cookieClicked' | 'brainDiver' | 'smashTestNotificationButton' | 'tutorialCompleted' | 'bubbleGameExplodingHead' | 'bubbleGameDoubleExplodingHead'; + }; + }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/change-password + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/change-password': { + requestBody: { + content: { + 'application/json': { + currentPassword: string; + newPassword: string; + token?: string | null; + }; + }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/delete-account + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/delete-account': { + requestBody: { + content: { + 'application/json': { + password: string; + token?: string | null; + }; + }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/export-data + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/export-data': { + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/export-blocking + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/export-blocking': { + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/export-following + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/export-following': { + requestBody: { + content: { + 'application/json': { + /** @default false */ + excludeMuting?: boolean; + /** @default false */ + excludeInactive?: boolean; + }; + }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/export-mute + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/export-mute': { + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/export-notes + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/export-notes': { + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/export-clips + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/export-clips': { + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/export-favorites + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/export-favorites': { + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/export-user-lists + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/export-user-lists': { + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/export-antennas + * @description No description provided. + * + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* + */ + 'i/export-antennas': { + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/favorites + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:favorites* + */ + 'i/favorites': { + requestBody: { + content: { + 'application/json': { + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['NoteFavorite'][]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/gallery/likes + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:gallery-likes* + */ + 'i/gallery/likes': { + requestBody: { + content: { + 'application/json': { + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + /** Format: id */ + id: string; + post: components['schemas']['GalleryPost']; + }[]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * i/gallery/posts * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *read:gallery* */ - 'i/2fa/register': { + 'i/gallery/posts': { requestBody: { content: { 'application/json': { - password: string; - token?: string | null; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; }; }; }; @@ -15523,13 +17759,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - qr: string; - url: string; - secret: string; - label: string; - issuer: string; - }; + 'application/json': components['schemas']['GalleryPost'][]; }; }; /** @description Client error */ @@ -15565,18 +17795,18 @@ export type operations = { }; }; /** - * i/2fa/update-key + * i/import-blocking * @description No description provided. * * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. * **Credential required**: *Yes* */ - 'i/2fa/update-key': { + 'i/import-blocking': { requestBody: { content: { 'application/json': { - name: string; - credentialId: string; + /** Format: misskey:id */ + fileId: string; }; }; }; @@ -15609,6 +17839,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -15618,19 +17854,19 @@ export type operations = { }; }; /** - * i/2fa/remove-key + * i/import-following * @description No description provided. * * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. * **Credential required**: *Yes* */ - 'i/2fa/remove-key': { + 'i/import-following': { requestBody: { content: { 'application/json': { - password: string; - token?: string | null; - credentialId: string; + /** Format: misskey:id */ + fileId: string; + withReplies?: boolean; }; }; }; @@ -15663,6 +17899,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -15672,18 +17914,19 @@ export type operations = { }; }; /** - * i/2fa/unregister + * i/import-notes * @description No description provided. * * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. * **Credential required**: *Yes* */ - 'i/2fa/unregister': { + 'i/import-notes': { requestBody: { content: { 'application/json': { - password: string; - token?: string | null; + /** Format: misskey:id */ + fileId: string; + type?: string | null; }; }; }; @@ -15716,6 +17959,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -15725,36 +17974,25 @@ export type operations = { }; }; /** - * i/apps + * i/import-muting * @description No description provided. * * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. * **Credential required**: *Yes* */ - 'i/apps': { + 'i/import-muting': { requestBody: { content: { 'application/json': { - /** @enum {string} */ - sort?: '+createdAt' | '-createdAt' | '+lastUsedAt' | '-lastUsedAt'; + /** Format: misskey:id */ + fileId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': { - /** Format: misskey:id */ - id: string; - name: string; - /** Format: date-time */ - createdAt: string; - /** Format: date-time */ - lastUsedAt: string; - permission: string[]; - }[]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -15780,6 +18018,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -15789,41 +18033,25 @@ export type operations = { }; }; /** - * i/authorized-apps + * i/import-user-lists * @description No description provided. * * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. * **Credential required**: *Yes* */ - 'i/authorized-apps': { + 'i/import-user-lists': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** @default 0 */ - offset?: number; - /** - * @default desc - * @enum {string} - */ - sort?: 'desc' | 'asc'; + /** Format: misskey:id */ + fileId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': ({ - /** Format: misskey:id */ - id: string; - name: string; - callbackUrl: string | null; - permission: string[]; - isAuthorized: boolean; - })[]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -15849,6 +18077,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -15858,17 +18092,18 @@ export type operations = { }; }; /** - * i/claim-achievement + * i/import-antennas * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* */ - 'i/claim-achievement': { + 'i/import-antennas': { requestBody: { content: { 'application/json': { - /** @enum {string} */ - name: 'notes1' | 'notes10' | 'notes100' | 'notes500' | 'notes1000' | 'notes5000' | 'notes10000' | 'notes20000' | 'notes30000' | 'notes40000' | 'notes50000' | 'notes60000' | 'notes70000' | 'notes80000' | 'notes90000' | 'notes100000' | 'login3' | 'login7' | 'login15' | 'login30' | 'login60' | 'login100' | 'login200' | 'login300' | 'login400' | 'login500' | 'login600' | 'login700' | 'login800' | 'login900' | 'login1000' | 'passedSinceAccountCreated1' | 'passedSinceAccountCreated2' | 'passedSinceAccountCreated3' | 'loggedInOnBirthday' | 'loggedInOnNewYearsDay' | 'noteClipped1' | 'noteFavorited1' | 'myNoteFavorited1' | 'profileFilled' | 'markedAsCat' | 'following1' | 'following10' | 'following50' | 'following100' | 'following300' | 'followers1' | 'followers10' | 'followers50' | 'followers100' | 'followers300' | 'followers500' | 'followers1000' | 'collectAchievements30' | 'viewAchievements3min' | 'iLoveMisskey' | 'foundTreasure' | 'client30min' | 'client60min' | 'noteDeletedWithin1min' | 'postedAtLateNight' | 'postedAt0min0sec' | 'selfQuote' | 'htl20npm' | 'viewInstanceChart' | 'outputHelloWorldOnScratchpad' | 'open3windows' | 'driveFolderCircularReference' | 'reactWithoutRead' | 'clickedClickHere' | 'justPlainLucky' | 'setNameToSyuilo' | 'cookieClicked' | 'brainDiver' | 'smashTestNotificationButton' | 'tutorialCompleted'; + /** Format: misskey:id */ + fileId: string; }; }; }; @@ -15901,6 +18136,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -15910,26 +18151,34 @@ export type operations = { }; }; /** - * i/change-password + * i/notifications * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *read:notifications* */ - 'i/change-password': { + 'i/notifications': { requestBody: { content: { 'application/json': { - currentPassword: string; - newPassword: string; - token?: string | null; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default true */ + markAsRead?: boolean; + includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'edited' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[]; + excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'edited' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[]; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Notification'][]; + }; }; /** @description Client error */ 400: { @@ -15955,6 +18204,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -15964,25 +18219,34 @@ export type operations = { }; }; /** - * i/delete-account + * i/notifications-grouped * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *read:notifications* */ - 'i/delete-account': { + 'i/notifications-grouped': { requestBody: { content: { 'application/json': { - password: string; - token?: string | null; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default true */ + markAsRead?: boolean; + includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'edited' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[]; + excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'edited' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[]; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Notification'][]; + }; }; /** @description Client error */ 400: { @@ -16008,6 +18272,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -16017,17 +18287,34 @@ export type operations = { }; }; /** - * i/export-blocking + * i/page-likes * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *read:page-likes* */ - 'i/export-blocking': { + 'i/page-likes': { + requestBody: { + content: { + 'application/json': { + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + }; + }; + }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + /** Format: id */ + id: string; + page: components['schemas']['Page']; + }[]; + }; }; /** @description Client error */ 400: { @@ -16053,12 +18340,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -16068,27 +18349,30 @@ export type operations = { }; }; /** - * i/export-following + * i/pages * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *read:pages* */ - 'i/export-following': { + 'i/pages': { requestBody: { content: { 'application/json': { - /** @default false */ - excludeMuting?: boolean; - /** @default false */ - excludeInactive?: boolean; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Page'][]; + }; }; /** @description Client error */ 400: { @@ -16114,12 +18398,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -16129,17 +18407,26 @@ export type operations = { }; }; /** - * i/export-mute + * i/pin * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'i/export-mute': { + 'i/pin': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + noteId: string; + }; + }; + }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['MeDetailed']; + }; }; /** @description Client error */ 400: { @@ -16165,12 +18452,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -16180,13 +18461,12 @@ export type operations = { }; }; /** - * i/export-notes + * i/read-all-unread-notes * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'i/export-notes': { + 'i/read-all-unread-notes': { responses: { /** @description OK (without any results) */ 204: { @@ -16216,12 +18496,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -16231,13 +18505,20 @@ export type operations = { }; }; /** - * i/export-favorites + * i/read-announcement * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'i/export-favorites': { + 'i/read-announcement': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + announcementId: string; + }; + }; + }; responses: { /** @description OK (without any results) */ 204: { @@ -16267,12 +18548,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -16282,13 +18557,20 @@ export type operations = { }; }; /** - * i/export-user-lists + * i/regenerate-token * @description No description provided. * * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. * **Credential required**: *Yes* */ - 'i/export-user-lists': { + 'i/regenerate-token': { + requestBody: { + content: { + 'application/json': { + password: string; + }; + }; + }; responses: { /** @description OK (without any results) */ 204: { @@ -16318,12 +18600,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -16333,17 +18609,27 @@ export type operations = { }; }; /** - * i/export-antennas + * i/registry/get-all * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'i/export-antennas': { + 'i/registry/get-all': { + requestBody: { + content: { + 'application/json': { + /** @default [] */ + scope: string[]; + domain?: string | null; + }; + }; + }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': Record<string, never>; + }; }; /** @description Client error */ 400: { @@ -16369,12 +18655,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -16384,30 +18664,25 @@ export type operations = { }; }; /** - * i/favorites + * i/registry/get-unsecure * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:favorites* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'i/favorites': { + 'i/registry/get-unsecure': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; + key: string; + /** @default [] */ + scope?: string[]; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['NoteFavorite'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -16442,21 +18717,19 @@ export type operations = { }; }; /** - * i/gallery/likes + * i/registry/get-detail * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:gallery-likes* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'i/gallery/likes': { + 'i/registry/get-detail': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; + key: string; + /** @default [] */ + scope: string[]; + domain?: string | null; }; }; }; @@ -16465,10 +18738,9 @@ export type operations = { 200: { content: { 'application/json': { - /** Format: id */ - id: string; - post: components['schemas']['GalleryPost']; - }[]; + updatedAt: string; + value: unknown; + }; }; }; /** @description Client error */ @@ -16504,21 +18776,19 @@ export type operations = { }; }; /** - * i/gallery/posts + * i/registry/get * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:gallery* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'i/gallery/posts': { + 'i/registry/get': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; + key: string; + /** @default [] */ + scope: string[]; + domain?: string | null; }; }; }; @@ -16526,7 +18796,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['GalleryPost'][]; + 'application/json': Record<string, never>; }; }; /** @description Client error */ @@ -16562,25 +18832,29 @@ export type operations = { }; }; /** - * i/import-blocking + * i/registry/keys-with-type * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'i/import-blocking': { + 'i/registry/keys-with-type': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - fileId: string; + /** @default [] */ + scope: string[]; + domain?: string | null; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + [key: string]: string; + }; + }; }; /** @description Client error */ 400: { @@ -16606,12 +18880,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -16621,26 +18889,27 @@ export type operations = { }; }; /** - * i/import-following + * i/registry/keys * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'i/import-following': { + 'i/registry/keys': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - fileId: string; - withReplies?: boolean; + /** @default [] */ + scope: string[]; + domain?: string | null; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': string[]; + }; }; /** @description Client error */ 400: { @@ -16666,12 +18935,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -16681,18 +18944,19 @@ export type operations = { }; }; /** - * i/import-muting + * i/registry/remove * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'i/import-muting': { + 'i/registry/remove': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - fileId: string; + key: string; + /** @default [] */ + scope: string[]; + domain?: string | null; }; }; }; @@ -16725,12 +18989,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -16740,25 +18998,22 @@ export type operations = { }; }; /** - * i/import-user-lists + * i/registry/scopes-with-domain * @description No description provided. * * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. * **Credential required**: *Yes* */ - 'i/import-user-lists': { - requestBody: { - content: { - 'application/json': { - /** Format: misskey:id */ - fileId: string; - }; - }; - }; + 'i/registry/scopes-with-domain': { responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': ({ + scopes: string[][]; + domain: string | null; + })[]; + }; }; /** @description Client error */ 400: { @@ -16784,12 +19039,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -16799,18 +19048,20 @@ export type operations = { }; }; /** - * i/import-antennas + * i/registry/set * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'i/import-antennas': { + 'i/registry/set': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - fileId: string; + key: string; + value: unknown; + /** @default [] */ + scope: string[]; + domain?: string | null; }; }; }; @@ -16843,12 +19094,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -16858,34 +19103,26 @@ export type operations = { }; }; /** - * i/notifications + * i/revoke-token * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:notifications* + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* */ - 'i/notifications': { + 'i/revoke-token': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - /** @default true */ - markAsRead?: boolean; - includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[]; - excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[]; + tokenId?: string; + token?: string | null; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Notification'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -16911,12 +19148,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -16926,12 +19157,13 @@ export type operations = { }; }; /** - * i/notifications-grouped + * i/signin-history * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:notifications* + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* */ - 'i/notifications-grouped': { + 'i/signin-history': { requestBody: { content: { 'application/json': { @@ -16941,10 +19173,6 @@ export type operations = { sinceId?: string; /** Format: misskey:id */ untilId?: string; - /** @default true */ - markAsRead?: boolean; - includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[]; - excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[]; }; }; }; @@ -16952,7 +19180,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Notification'][]; + 'application/json': components['schemas']['Signin'][]; }; }; /** @description Client error */ @@ -16979,12 +19207,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -16994,21 +19216,17 @@ export type operations = { }; }; /** - * i/page-likes + * i/unpin * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:page-likes* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'i/page-likes': { + 'i/unpin': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; /** Format: misskey:id */ - untilId?: string; + noteId: string; }; }; }; @@ -17016,11 +19234,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - /** Format: id */ - id: string; - page: components['schemas']['Page']; - }[]; + 'application/json': components['schemas']['MeDetailed']; }; }; /** @description Client error */ @@ -17056,21 +19270,19 @@ export type operations = { }; }; /** - * i/pages + * i/update-email * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:pages* + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* */ - 'i/pages': { + 'i/update-email': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; + password: string; + email?: string | null; + token?: string | null; }; }; }; @@ -17078,7 +19290,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Page'][]; + 'application/json': components['schemas']['MeDetailed']; }; }; /** @description Client error */ @@ -17105,6 +19317,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -17114,17 +19332,195 @@ export type operations = { }; }; /** - * i/pin + * i/update * @description No description provided. * * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'i/pin': { + 'i/update': { requestBody: { content: { 'application/json': { + name?: string | null; + description?: string | null; + location?: string | null; + birthday?: string | null; + listenbrainz?: string | null; + /** @enum {string|null} */ + lang?: null | 'ach' | 'ady' | 'af' | 'af-NA' | 'af-ZA' | 'ak' | 'ar' | 'ar-AR' | 'ar-MA' | 'ar-SA' | 'ay-BO' | 'az' | 'az-AZ' | 'be-BY' | 'bg' | 'bg-BG' | 'bn' | 'bn-IN' | 'bn-BD' | 'br' | 'bs-BA' | 'ca' | 'ca-ES' | 'cak' | 'ck-US' | 'cs' | 'cs-CZ' | 'cy' | 'cy-GB' | 'da' | 'da-DK' | 'de' | 'de-AT' | 'de-DE' | 'de-CH' | 'dsb' | 'el' | 'el-GR' | 'en' | 'en-GB' | 'en-AU' | 'en-CA' | 'en-IE' | 'en-IN' | 'en-PI' | 'en-SG' | 'en-UD' | 'en-US' | 'en-ZA' | 'en@pirate' | 'eo' | 'eo-EO' | 'es' | 'es-AR' | 'es-419' | 'es-CL' | 'es-CO' | 'es-EC' | 'es-ES' | 'es-LA' | 'es-NI' | 'es-MX' | 'es-US' | 'es-VE' | 'et' | 'et-EE' | 'eu' | 'eu-ES' | 'fa' | 'fa-IR' | 'fb-LT' | 'ff' | 'fi' | 'fi-FI' | 'fo' | 'fo-FO' | 'fr' | 'fr-CA' | 'fr-FR' | 'fr-BE' | 'fr-CH' | 'fy-NL' | 'ga' | 'ga-IE' | 'gd' | 'gl' | 'gl-ES' | 'gn-PY' | 'gu-IN' | 'gv' | 'gx-GR' | 'he' | 'he-IL' | 'hi' | 'hi-IN' | 'hr' | 'hr-HR' | 'hsb' | 'ht' | 'hu' | 'hu-HU' | 'hy' | 'hy-AM' | 'id' | 'id-ID' | 'is' | 'is-IS' | 'it' | 'it-IT' | 'ja' | 'ja-JP' | 'jv-ID' | 'ka-GE' | 'kk-KZ' | 'km' | 'kl' | 'km-KH' | 'kab' | 'kn' | 'kn-IN' | 'ko' | 'ko-KR' | 'ku-TR' | 'kw' | 'la' | 'la-VA' | 'lb' | 'li-NL' | 'lt' | 'lt-LT' | 'lv' | 'lv-LV' | 'mai' | 'mg-MG' | 'mk' | 'mk-MK' | 'ml' | 'ml-IN' | 'mn-MN' | 'mr' | 'mr-IN' | 'ms' | 'ms-MY' | 'mt' | 'mt-MT' | 'my' | 'no' | 'nb' | 'nb-NO' | 'ne' | 'ne-NP' | 'nl' | 'nl-BE' | 'nl-NL' | 'nn-NO' | 'oc' | 'or-IN' | 'pa' | 'pa-IN' | 'pl' | 'pl-PL' | 'ps-AF' | 'pt' | 'pt-BR' | 'pt-PT' | 'qu-PE' | 'rm-CH' | 'ro' | 'ro-RO' | 'ru' | 'ru-RU' | 'sa-IN' | 'se-NO' | 'sh' | 'si-LK' | 'sk' | 'sk-SK' | 'sl' | 'sl-SI' | 'so-SO' | 'sq' | 'sq-AL' | 'sr' | 'sr-RS' | 'su' | 'sv' | 'sv-SE' | 'sw' | 'sw-KE' | 'ta' | 'ta-IN' | 'te' | 'te-IN' | 'tg' | 'tg-TJ' | 'th' | 'th-TH' | 'fil' | 'tlh' | 'tr' | 'tr-TR' | 'tt-RU' | 'uk' | 'uk-UA' | 'ur' | 'ur-PK' | 'uz' | 'uz-UZ' | 'vi' | 'vi-VN' | 'xh-ZA' | 'yi' | 'yi-DE' | 'zh' | 'zh-Hans' | 'zh-Hant' | 'zh-CN' | 'zh-HK' | 'zh-SG' | 'zh-TW' | 'zu-ZA'; /** Format: misskey:id */ - noteId: string; + avatarId?: string | null; + avatarDecorations?: ({ + /** Format: misskey:id */ + id: string; + angle?: number | null; + flipH?: boolean | null; + offsetX?: number | null; + offsetY?: number | null; + })[]; + /** Format: misskey:id */ + bannerId?: string | null; + /** Format: misskey:id */ + backgroundId?: string | null; + fields?: { + name: string; + value: string; + }[]; + isLocked?: boolean; + isExplorable?: boolean; + hideOnlineStatus?: boolean; + publicReactions?: boolean; + carefulBot?: boolean; + autoAcceptFollowed?: boolean; + noCrawle?: boolean; + preventAiLearning?: boolean; + noindex?: boolean; + isBot?: boolean; + isCat?: boolean; + speakAsCat?: boolean; + injectFeaturedNote?: boolean; + receiveAnnouncementEmail?: boolean; + alwaysMarkNsfw?: boolean; + autoSensitive?: boolean; + /** @enum {string} */ + followingVisibility?: 'public' | 'followers' | 'private'; + /** @enum {string} */ + followersVisibility?: 'public' | 'followers' | 'private'; + /** Format: misskey:id */ + pinnedPageId?: string | null; + mutedWords?: (string[] | string)[]; + hardMutedWords?: (string[] | string)[]; + mutedInstances?: string[]; + notificationRecieveConfig?: { + note?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + follow?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + mention?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + reply?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + renote?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + quote?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + reaction?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + pollEnded?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + receiveFollowRequest?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + followRequestAccepted?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + roleAssigned?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + achievementEarned?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + app?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + test?: OneOf<[{ + /** @enum {string} */ + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; + }, { + /** @enum {string} */ + type: 'list'; + /** Format: misskey:id */ + userListId: string; + }]>; + }; + emailNotificationTypes?: string[]; + alsoKnownAs?: string[]; }; }; }; @@ -17159,6 +19555,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -17168,16 +19570,26 @@ export type operations = { }; }; /** - * i/read-all-unread-notes + * i/move * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* */ - 'i/read-all-unread-notes': { + 'i/move': { + requestBody: { + content: { + 'application/json': { + moveToAccount: string; + }; + }; + }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': Record<string, never>; + }; }; /** @description Client error */ 400: { @@ -17203,6 +19615,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -17212,24 +19630,42 @@ export type operations = { }; }; /** - * i/read-announcement + * i/webhooks/create * @description No description provided. * * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'i/read-announcement': { + 'i/webhooks/create': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - announcementId: string; + name: string; + url: string; + /** @default */ + secret?: string; + on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction' | 'edited')[]; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + /** Format: misskey:id */ + id: string; + /** Format: misskey:id */ + userId: string; + name: string; + on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction' | 'edited')[]; + url: string; + secret: string; + active: boolean; + /** Format: date-time */ + latestSentAt: string | null; + latestStatus: number | null; + }; + }; }; /** @description Client error */ 400: { @@ -17264,24 +19700,31 @@ export type operations = { }; }; /** - * i/regenerate-token + * i/webhooks/list * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'i/regenerate-token': { - requestBody: { - content: { - 'application/json': { - password: string; - }; - }; - }; + 'i/webhooks/list': { responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': ({ + /** Format: misskey:id */ + id: string; + /** Format: misskey:id */ + userId: string; + name: string; + on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction' | 'edited')[]; + url: string; + secret: string; + active: boolean; + /** Format: date-time */ + latestSentAt: string | null; + latestStatus: number | null; + })[]; + }; }; /** @description Client error */ 400: { @@ -17316,18 +19759,17 @@ export type operations = { }; }; /** - * i/registry/get-all + * i/webhooks/show * @description No description provided. * * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'i/registry/get-all': { + 'i/webhooks/show': { requestBody: { content: { 'application/json': { - /** @default [] */ - scope: string[]; - domain?: string | null; + /** Format: misskey:id */ + webhookId: string; }; }; }; @@ -17335,7 +19777,20 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': Record<string, never>; + 'application/json': { + /** Format: misskey:id */ + id: string; + /** Format: misskey:id */ + userId: string; + name: string; + on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction' | 'edited')[]; + url: string; + secret: string; + active: boolean; + /** Format: date-time */ + latestSentAt: string | null; + latestStatus: number | null; + }; }; }; /** @description Client error */ @@ -17371,28 +19826,30 @@ export type operations = { }; }; /** - * i/registry/get-detail + * i/webhooks/update * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'i/registry/get-detail': { + 'i/webhooks/update': { requestBody: { content: { 'application/json': { - key: string; - /** @default [] */ - scope: string[]; - domain?: string | null; + /** Format: misskey:id */ + webhookId: string; + name: string; + url: string; + /** @default */ + secret?: string; + on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction' | 'edited')[]; + active: boolean; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': Record<string, never>; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -17427,28 +19884,24 @@ export type operations = { }; }; /** - * i/registry/get + * i/webhooks/delete * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'i/registry/get': { + 'i/webhooks/delete': { requestBody: { content: { 'application/json': { - key: string; - /** @default [] */ - scope: string[]; - domain?: string | null; + /** Format: misskey:id */ + webhookId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': Record<string, never>; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -17483,26 +19936,17 @@ export type operations = { }; }; /** - * i/registry/keys-with-type + * invite/create * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *Yes* / **Permission**: *write:invite-codes* */ - 'i/registry/keys-with-type': { - requestBody: { - content: { - 'application/json': { - /** @default [] */ - scope: string[]; - domain?: string | null; - }; - }; - }; + 'invite/create': { responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': Record<string, never>; + 'application/json': components['schemas']['InviteCode']; }; }; /** @description Client error */ @@ -17538,18 +19982,17 @@ export type operations = { }; }; /** - * i/registry/keys + * invite/delete * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *Yes* / **Permission**: *write:invite-codes* */ - 'i/registry/keys': { + 'invite/delete': { requestBody: { content: { 'application/json': { - /** @default [] */ - scope: string[]; - domain?: string | null; + /** Format: misskey:id */ + inviteId: string; }; }; }; @@ -17591,26 +20034,30 @@ export type operations = { }; }; /** - * i/registry/remove + * invite/list * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *Yes* / **Permission**: *read:invite-codes* */ - 'i/registry/remove': { + 'invite/list': { requestBody: { content: { 'application/json': { - key: string; - /** @default [] */ - scope: string[]; - domain?: string | null; + /** @default 30 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['InviteCode'][]; + }; }; /** @description Client error */ 400: { @@ -17645,21 +20092,19 @@ export type operations = { }; }; /** - * i/registry/scopes-with-domain + * invite/limit * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *read:invite-codes* */ - 'i/registry/scopes-with-domain': { + 'invite/limit': { responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': ({ - scopes: string[][]; - domain: string | null; - })[]; + 'application/json': { + remaining: number | null; + }; }; }; /** @description Client error */ @@ -17695,27 +20140,26 @@ export type operations = { }; }; /** - * i/registry/set + * meta * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *No* */ - 'i/registry/set': { + meta: { requestBody: { content: { 'application/json': { - key: string; - value: unknown; - /** @default [] */ - scope: string[]; - domain?: string | null; + /** @default true */ + detail?: boolean; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['MetaLite'] | components['schemas']['MetaDetailed']; + }; }; /** @description Client error */ 400: { @@ -17750,26 +20194,20 @@ export type operations = { }; }; /** - * i/revoke-token + * emojis * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *No* */ - 'i/revoke-token': { - requestBody: { - content: { - 'application/json': { - /** Format: misskey:id */ - tokenId?: string; - token?: string | null; - }; - }; - }; + emojis: { responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + emojis: components['schemas']['EmojiSimple'][]; + }; + }; }; /** @description Client error */ 400: { @@ -17804,22 +20242,16 @@ export type operations = { }; }; /** - * i/signin-history + * emoji * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *No* */ - 'i/signin-history': { + emoji: { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; + name: string; }; }; }; @@ -17827,7 +20259,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Signin'][]; + 'application/json': components['schemas']['EmojiDetailed']; }; }; /** @description Client error */ @@ -17863,17 +20295,21 @@ export type operations = { }; }; /** - * i/unpin + * miauth/gen-token * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* */ - 'i/unpin': { + 'miauth/gen-token': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - noteId: string; + session: string | null; + name?: string | null; + description?: string | null; + iconUrl?: string | null; + permission: string[]; }; }; }; @@ -17881,7 +20317,9 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['MeDetailed']; + 'application/json': { + token: string; + }; }; }; /** @description Client error */ @@ -17917,28 +20355,26 @@ export type operations = { }; }; /** - * i/update-email + * mute/create * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *write:mutes* */ - 'i/update-email': { + 'mute/create': { requestBody: { content: { 'application/json': { - password: string; - email?: string | null; - token?: string | null; + /** Format: misskey:id */ + userId: string; + /** @description A Unix Epoch timestamp that must lie in the future. `null` means an indefinite mute. */ + expiresAt?: number | null; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['UserDetailed']; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -17979,72 +20415,24 @@ export type operations = { }; }; /** - * i/update + * mute/delete * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *Yes* / **Permission**: *write:mutes* */ - 'i/update': { + 'mute/delete': { requestBody: { content: { 'application/json': { - name?: string | null; - description?: string | null; - location?: string | null; - birthday?: string | null; - /** @enum {string|null} */ - lang?: null | 'ach' | 'ady' | 'af' | 'af-NA' | 'af-ZA' | 'ak' | 'ar' | 'ar-AR' | 'ar-MA' | 'ar-SA' | 'ay-BO' | 'az' | 'az-AZ' | 'be-BY' | 'bg' | 'bg-BG' | 'bn' | 'bn-IN' | 'bn-BD' | 'br' | 'bs-BA' | 'ca' | 'ca-ES' | 'cak' | 'ck-US' | 'cs' | 'cs-CZ' | 'cy' | 'cy-GB' | 'da' | 'da-DK' | 'de' | 'de-AT' | 'de-DE' | 'de-CH' | 'dsb' | 'el' | 'el-GR' | 'en' | 'en-GB' | 'en-AU' | 'en-CA' | 'en-IE' | 'en-IN' | 'en-PI' | 'en-SG' | 'en-UD' | 'en-US' | 'en-ZA' | 'en@pirate' | 'eo' | 'eo-EO' | 'es' | 'es-AR' | 'es-419' | 'es-CL' | 'es-CO' | 'es-EC' | 'es-ES' | 'es-LA' | 'es-NI' | 'es-MX' | 'es-US' | 'es-VE' | 'et' | 'et-EE' | 'eu' | 'eu-ES' | 'fa' | 'fa-IR' | 'fb-LT' | 'ff' | 'fi' | 'fi-FI' | 'fo' | 'fo-FO' | 'fr' | 'fr-CA' | 'fr-FR' | 'fr-BE' | 'fr-CH' | 'fy-NL' | 'ga' | 'ga-IE' | 'gd' | 'gl' | 'gl-ES' | 'gn-PY' | 'gu-IN' | 'gv' | 'gx-GR' | 'he' | 'he-IL' | 'hi' | 'hi-IN' | 'hr' | 'hr-HR' | 'hsb' | 'ht' | 'hu' | 'hu-HU' | 'hy' | 'hy-AM' | 'id' | 'id-ID' | 'is' | 'is-IS' | 'it' | 'it-IT' | 'ja' | 'ja-JP' | 'jv-ID' | 'ka-GE' | 'kk-KZ' | 'km' | 'kl' | 'km-KH' | 'kab' | 'kn' | 'kn-IN' | 'ko' | 'ko-KR' | 'ku-TR' | 'kw' | 'la' | 'la-VA' | 'lb' | 'li-NL' | 'lt' | 'lt-LT' | 'lv' | 'lv-LV' | 'mai' | 'mg-MG' | 'mk' | 'mk-MK' | 'ml' | 'ml-IN' | 'mn-MN' | 'mr' | 'mr-IN' | 'ms' | 'ms-MY' | 'mt' | 'mt-MT' | 'my' | 'no' | 'nb' | 'nb-NO' | 'ne' | 'ne-NP' | 'nl' | 'nl-BE' | 'nl-NL' | 'nn-NO' | 'oc' | 'or-IN' | 'pa' | 'pa-IN' | 'pl' | 'pl-PL' | 'ps-AF' | 'pt' | 'pt-BR' | 'pt-PT' | 'qu-PE' | 'rm-CH' | 'ro' | 'ro-RO' | 'ru' | 'ru-RU' | 'sa-IN' | 'se-NO' | 'sh' | 'si-LK' | 'sk' | 'sk-SK' | 'sl' | 'sl-SI' | 'so-SO' | 'sq' | 'sq-AL' | 'sr' | 'sr-RS' | 'su' | 'sv' | 'sv-SE' | 'sw' | 'sw-KE' | 'ta' | 'ta-IN' | 'te' | 'te-IN' | 'tg' | 'tg-TJ' | 'th' | 'th-TH' | 'fil' | 'tlh' | 'tr' | 'tr-TR' | 'tt-RU' | 'uk' | 'uk-UA' | 'ur' | 'ur-PK' | 'uz' | 'uz-UZ' | 'vi' | 'vi-VN' | 'xh-ZA' | 'yi' | 'yi-DE' | 'zh' | 'zh-Hans' | 'zh-Hant' | 'zh-CN' | 'zh-HK' | 'zh-SG' | 'zh-TW' | 'zu-ZA'; - /** Format: misskey:id */ - avatarId?: string | null; - avatarDecorations?: ({ - /** Format: misskey:id */ - id: string; - angle?: number | null; - flipH?: boolean | null; - offsetX?: number | null; - offsetY?: number | null; - })[]; - /** Format: misskey:id */ - bannerId?: string | null; - fields?: { - name: string; - value: string; - }[]; - isLocked?: boolean; - isExplorable?: boolean; - hideOnlineStatus?: boolean; - publicReactions?: boolean; - carefulBot?: boolean; - autoAcceptFollowed?: boolean; - noCrawle?: boolean; - preventAiLearning?: boolean; - isBot?: boolean; - isCat?: boolean; - injectFeaturedNote?: boolean; - receiveAnnouncementEmail?: boolean; - alwaysMarkNsfw?: boolean; - autoSensitive?: boolean; - /** @enum {string} */ - followingVisibility?: 'public' | 'followers' | 'private'; - /** @enum {string} */ - followersVisibility?: 'public' | 'followers' | 'private'; /** Format: misskey:id */ - pinnedPageId?: string | null; - mutedWords?: (string[] | string)[]; - hardMutedWords?: (string[] | string)[]; - mutedInstances?: string[]; - notificationRecieveConfig?: Record<string, never>; - emailNotificationTypes?: string[]; - alsoKnownAs?: string[]; + userId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['MeDetailed']; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -18070,12 +20458,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -18085,17 +20467,21 @@ export type operations = { }; }; /** - * i/move + * mute/list * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *read:mutes* */ - 'i/move': { + 'mute/list': { requestBody: { content: { 'application/json': { - moveToAccount: string; + /** @default 30 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; }; }; }; @@ -18103,7 +20489,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': Record<string, never>; + 'application/json': components['schemas']['Muting'][]; }; }; /** @description Client error */ @@ -18130,12 +20516,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -18145,42 +20525,24 @@ export type operations = { }; }; /** - * i/webhooks/create + * renote-mute/create * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *Yes* / **Permission**: *write:mutes* */ - 'i/webhooks/create': { + 'renote-mute/create': { requestBody: { content: { 'application/json': { - name: string; - url: string; - /** @default */ - secret?: string; - on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[]; + /** Format: misskey:id */ + userId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': { - /** Format: misskey:id */ - id: string; - /** Format: misskey:id */ - userId: string; - name: string; - on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[]; - url: string; - secret: string; - active: boolean; - /** Format: date-time */ - latestSentAt: string | null; - latestStatus: number | null; - }; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -18206,6 +20568,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -18215,32 +20583,25 @@ export type operations = { }; }; /** - * i/webhooks/list + * renote-mute/delete * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *Yes* / **Permission**: *write:mutes* */ - 'i/webhooks/list': { - responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': ({ - /** Format: misskey:id */ - id: string; - /** Format: misskey:id */ - userId: string; - name: string; - on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[]; - url: string; - secret: string; - active: boolean; - /** Format: date-time */ - latestSentAt: string | null; - latestStatus: number | null; - })[]; + 'renote-mute/delete': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + userId: string; }; }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; /** @description Client error */ 400: { content: { @@ -18274,17 +20635,21 @@ export type operations = { }; }; /** - * i/webhooks/show + * renote-mute/list * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *Yes* / **Permission**: *read:mutes* */ - 'i/webhooks/show': { + 'renote-mute/list': { requestBody: { content: { 'application/json': { + /** @default 30 */ + limit?: number; /** Format: misskey:id */ - webhookId: string; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; }; }; }; @@ -18292,20 +20657,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - /** Format: misskey:id */ - id: string; - /** Format: misskey:id */ - userId: string; - name: string; - on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[]; - url: string; - secret: string; - active: boolean; - /** Format: date-time */ - latestSentAt: string | null; - latestStatus: number | null; - }; + 'application/json': components['schemas']['RenoteMuting'][]; }; }; /** @description Client error */ @@ -18341,30 +20693,28 @@ export type operations = { }; }; /** - * i/webhooks/update + * my/apps * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'i/webhooks/update': { + 'my/apps': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - webhookId: string; - name: string; - url: string; - /** @default */ - secret?: string; - on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[]; - active: boolean; + /** @default 10 */ + limit?: number; + /** @default 0 */ + offset?: number; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['App'][]; + }; }; /** @description Client error */ 400: { @@ -18399,24 +20749,36 @@ export type operations = { }; }; /** - * i/webhooks/delete + * notes * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *No* */ - 'i/webhooks/delete': { + notes: { requestBody: { content: { 'application/json': { + /** @default false */ + local?: boolean; + reply?: boolean; + renote?: boolean; + withFiles?: boolean; + poll?: boolean; + /** @default 10 */ + limit?: number; /** Format: misskey:id */ - webhookId: string; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Note'][]; + }; }; /** @description Client error */ 400: { @@ -18451,17 +20813,33 @@ export type operations = { }; }; /** - * invite/create + * notes/children * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:invite-codes* + * **Credential required**: *No* */ - 'invite/create': { + 'notes/children': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + noteId: string; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default true */ + showQuotes?: boolean; + }; + }; + }; responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['InviteCode']; + 'application/json': components['schemas']['Note'][]; }; }; /** @description Client error */ @@ -18497,24 +20875,26 @@ export type operations = { }; }; /** - * invite/delete + * notes/clips * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:invite-codes* + * **Credential required**: *No* */ - 'invite/delete': { + 'notes/clips': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - inviteId: string; + noteId: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Clip'][]; + }; }; /** @description Client error */ 400: { @@ -18549,21 +20929,21 @@ export type operations = { }; }; /** - * invite/list + * notes/conversation * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:invite-codes* + * **Credential required**: *No* */ - 'invite/list': { + 'notes/conversation': { requestBody: { content: { 'application/json': { - /** @default 30 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; /** Format: misskey:id */ - untilId?: string; + noteId: string; + /** @default 10 */ + limit?: number; + /** @default 0 */ + offset?: number; }; }; }; @@ -18571,7 +20951,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['InviteCode'][]; + 'application/json': components['schemas']['Note'][]; }; }; /** @description Client error */ @@ -18607,18 +20987,59 @@ export type operations = { }; }; /** - * invite/limit + * notes/create * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:invite-codes* + * **Credential required**: *Yes* / **Permission**: *write:notes* */ - 'invite/limit': { + 'notes/create': { + requestBody: { + content: { + 'application/json': { + /** + * @default public + * @enum {string} + */ + visibility?: 'public' | 'home' | 'followers' | 'specified'; + visibleUserIds?: string[]; + cw?: string | null; + /** @default false */ + localOnly?: boolean; + /** + * @default null + * @enum {string|null} + */ + reactionAcceptance?: null | 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote'; + /** @default false */ + noExtractMentions?: boolean; + /** @default false */ + noExtractHashtags?: boolean; + /** @default false */ + noExtractEmojis?: boolean; + /** Format: misskey:id */ + replyId?: string | null; + /** Format: misskey:id */ + renoteId?: string | null; + /** Format: misskey:id */ + channelId?: string | null; + text?: string | null; + fileIds?: string[]; + mediaIds?: string[]; + poll?: ({ + choices: string[]; + multiple?: boolean; + expiresAt?: number | null; + expiredAfter?: number | null; + }) | null; + }; + }; + }; responses: { /** @description OK (with results) */ 200: { content: { 'application/json': { - remaining: number | null; + createdNote: components['schemas']['Note']; }; }; }; @@ -18646,6 +21067,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -18655,105 +21082,24 @@ export type operations = { }; }; /** - * meta + * notes/delete * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:notes* */ - meta: { + 'notes/delete': { requestBody: { content: { 'application/json': { - /** @default true */ - detail?: boolean; + /** Format: misskey:id */ + noteId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': { - maintainerName: string | null; - maintainerEmail: string | null; - version: string; - name: string; - shortName: string | null; - /** - * Format: url - * @example https://misskey.example.com - */ - uri: string; - description: string | null; - langs: string[]; - tosUrl: string | null; - /** @default https://github.com/misskey-dev/misskey */ - repositoryUrl: string; - /** @default https://github.com/misskey-dev/misskey/issues/new */ - feedbackUrl: string; - defaultDarkTheme: string | null; - defaultLightTheme: string | null; - disableRegistration: boolean; - cacheRemoteFiles: boolean; - cacheRemoteSensitiveFiles: boolean; - emailRequiredForSignup: boolean; - enableHcaptcha: boolean; - hcaptchaSiteKey: string | null; - enableRecaptcha: boolean; - recaptchaSiteKey: string | null; - enableTurnstile: boolean; - turnstileSiteKey: string | null; - swPublickey: string | null; - /** @default /assets/ai.png */ - mascotImageUrl: string; - bannerUrl: string; - serverErrorImageUrl: string | null; - infoImageUrl: string | null; - notFoundImageUrl: string | null; - iconUrl: string | null; - maxNoteTextLength: number; - ads: { - /** - * Format: id - * @example xxxxxxxxxx - */ - id: string; - /** Format: url */ - url: string; - place: string; - ratio: number; - /** Format: url */ - imageUrl: string; - dayOfWeek: number; - }[]; - /** @default 0 */ - notesPerOneAd: number; - /** @example false */ - requireSetup: boolean; - enableEmail: boolean; - enableServiceWorker: boolean; - translatorAvailable: boolean; - proxyAccountName: string | null; - mediaProxy: string; - features?: { - registration: boolean; - localTimeline: boolean; - globalTimeline: boolean; - hcaptcha: boolean; - recaptcha: boolean; - objectStorage: boolean; - serviceWorker: boolean; - /** @default true */ - miauth?: boolean; - }; - backgroundImageUrl: string | null; - impressumUrl: string | null; - logoImageUrl: string | null; - privacyPolicyUrl: string | null; - serverRules: string[]; - themeColor: string | null; - }; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -18779,6 +21125,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -18788,21 +21140,25 @@ export type operations = { }; }; /** - * emojis - * @description No description provided. - * - * **Credential required**: *No* - */ - emojis: { - responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': { - emojis: components['schemas']['EmojiSimple'][]; - }; + * notes/favorites/create + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:favorites* + */ + 'notes/favorites/create': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + noteId: string; }; }; + }; + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; /** @description Client error */ 400: { content: { @@ -18827,6 +21183,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -18836,25 +21198,24 @@ export type operations = { }; }; /** - * emoji + * notes/favorites/delete * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:favorites* */ - emoji: { + 'notes/favorites/delete': { requestBody: { content: { 'application/json': { - name: string; + /** Format: misskey:id */ + noteId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['EmojiDetailed']; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -18889,21 +21250,21 @@ export type operations = { }; }; /** - * miauth/gen-token + * notes/featured * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *No* */ - 'miauth/gen-token': { + 'notes/featured': { requestBody: { content: { 'application/json': { - session: string | null; - name?: string | null; - description?: string | null; - iconUrl?: string | null; - permission: string[]; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + untilId?: string; + /** Format: misskey:id */ + channelId?: string | null; }; }; }; @@ -18911,9 +21272,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - token: string; - }; + 'application/json': components['schemas']['Note'][]; }; }; /** @description Client error */ @@ -18949,26 +21308,38 @@ export type operations = { }; }; /** - * mute/create + * notes/global-timeline * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:mutes* + * **Credential required**: *No* */ - 'mute/create': { + 'notes/global-timeline': { requestBody: { content: { 'application/json': { + /** @default false */ + withFiles?: boolean; + /** @default true */ + withBots?: boolean; + /** @default true */ + withRenotes?: boolean; + /** @default 10 */ + limit?: number; /** Format: misskey:id */ - userId: string; - /** @description A Unix Epoch timestamp that must lie in the future. `null` means an indefinite mute. */ - expiresAt?: number | null; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + sinceDate?: number; + untilDate?: number; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Note'][]; + }; }; /** @description Client error */ 400: { @@ -18994,12 +21365,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -19009,24 +21374,38 @@ export type operations = { }; }; /** - * mute/delete + * notes/bubble-timeline * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:mutes* + * **Credential required**: *No* */ - 'mute/delete': { + 'notes/bubble-timeline': { requestBody: { content: { 'application/json': { + /** @default false */ + withFiles?: boolean; + /** @default true */ + withBots?: boolean; + /** @default true */ + withRenotes?: boolean; + /** @default 10 */ + limit?: number; /** Format: misskey:id */ - userId: string; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + sinceDate?: number; + untilDate?: number; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Note'][]; + }; }; /** @description Client error */ 400: { @@ -19061,21 +21440,39 @@ export type operations = { }; }; /** - * mute/list + * notes/hybrid-timeline * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:mutes* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'mute/list': { + 'notes/hybrid-timeline': { requestBody: { content: { 'application/json': { - /** @default 30 */ + /** @default 10 */ limit?: number; /** Format: misskey:id */ sinceId?: string; /** Format: misskey:id */ untilId?: string; + sinceDate?: number; + untilDate?: number; + /** @default false */ + allowPartial?: boolean; + /** @default true */ + includeMyRenotes?: boolean; + /** @default true */ + includeRenotedMyNotes?: boolean; + /** @default true */ + includeLocalRenotes?: boolean; + /** @default false */ + withFiles?: boolean; + /** @default true */ + withRenotes?: boolean; + /** @default false */ + withReplies?: boolean; + /** @default true */ + withBots?: boolean; }; }; }; @@ -19083,7 +21480,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Muting'][]; + 'application/json': components['schemas']['Note'][]; }; }; /** @description Client error */ @@ -19119,24 +21516,42 @@ export type operations = { }; }; /** - * renote-mute/create + * notes/local-timeline * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:mutes* + * **Credential required**: *No* */ - 'renote-mute/create': { + 'notes/local-timeline': { requestBody: { content: { 'application/json': { + /** @default false */ + withFiles?: boolean; + /** @default true */ + withRenotes?: boolean; + /** @default false */ + withReplies?: boolean; + /** @default true */ + withBots?: boolean; + /** @default 10 */ + limit?: number; /** Format: misskey:id */ - userId: string; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default false */ + allowPartial?: boolean; + sinceDate?: number; + untilDate?: number; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Note'][]; + }; }; /** @description Client error */ 400: { @@ -19162,12 +21577,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -19177,24 +21586,33 @@ export type operations = { }; }; /** - * renote-mute/delete + * notes/mentions * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:mutes* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'renote-mute/delete': { + 'notes/mentions': { requestBody: { content: { 'application/json': { + /** @default false */ + following?: boolean; + /** @default 10 */ + limit?: number; /** Format: misskey:id */ - userId: string; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + visibility?: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Note'][]; + }; }; /** @description Client error */ 400: { @@ -19229,21 +21647,19 @@ export type operations = { }; }; /** - * renote-mute/list + * notes/polls/recommendation * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:mutes* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'renote-mute/list': { + 'notes/polls/recommendation': { requestBody: { content: { 'application/json': { - /** @default 30 */ + /** @default 10 */ limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; + /** @default 0 */ + offset?: number; }; }; }; @@ -19251,7 +21667,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['RenoteMuting'][]; + 'application/json': components['schemas']['Note'][]; }; }; /** @description Client error */ @@ -19287,28 +21703,25 @@ export type operations = { }; }; /** - * my/apps + * notes/polls/vote * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *Yes* / **Permission**: *write:votes* */ - 'my/apps': { + 'notes/polls/vote': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** @default 0 */ - offset?: number; + /** Format: misskey:id */ + noteId: string; + choice: number; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['App'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -19343,21 +21756,18 @@ export type operations = { }; }; /** - * notes + * notes/reactions * @description No description provided. * * **Credential required**: *No* */ - notes: { + 'notes/reactions': { requestBody: { content: { - 'application/json': { - /** @default false */ - local?: boolean; - reply?: boolean; - renote?: boolean; - withFiles?: boolean; - poll?: boolean; + 'application/json': { + /** Format: misskey:id */ + noteId: string; + type?: string | null; /** @default 10 */ limit?: number; /** Format: misskey:id */ @@ -19371,7 +21781,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Note'][]; + 'application/json': components['schemas']['NoteReaction'][]; }; }; /** @description Client error */ @@ -19407,32 +21817,25 @@ export type operations = { }; }; /** - * notes/children + * notes/reactions/create * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:reactions* */ - 'notes/children': { + 'notes/reactions/create': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ noteId: string; - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; + reaction: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Note'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -19467,12 +21870,12 @@ export type operations = { }; }; /** - * notes/clips + * notes/reactions/delete * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:reactions* */ - 'notes/clips': { + 'notes/reactions/delete': { requestBody: { content: { 'application/json': { @@ -19482,11 +21885,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Clip'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -19512,6 +21913,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -19521,30 +21928,25 @@ export type operations = { }; }; /** - * notes/conversation + * notes/like * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:reactions* */ - 'notes/conversation': { + 'notes/like': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ noteId: string; - /** @default 10 */ - limit?: number; - /** @default 0 */ - offset?: number; + override?: string | null; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Note'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -19579,50 +21981,27 @@ export type operations = { }; }; /** - * notes/create + * notes/renotes * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:notes* + * **Credential required**: *No* */ - 'notes/create': { + 'notes/renotes': { requestBody: { content: { 'application/json': { - /** - * @default public - * @enum {string} - */ - visibility?: 'public' | 'home' | 'followers' | 'specified'; - visibleUserIds?: string[]; - cw?: string | null; - /** @default false */ - localOnly?: boolean; - /** - * @default null - * @enum {string|null} - */ - reactionAcceptance?: null | 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote'; - /** @default false */ - noExtractMentions?: boolean; - /** @default false */ - noExtractHashtags?: boolean; - /** @default false */ - noExtractEmojis?: boolean; /** Format: misskey:id */ - replyId?: string | null; + noteId: string; /** Format: misskey:id */ - renoteId?: string | null; + userId?: string; + /** @default 10 */ + limit?: number; /** Format: misskey:id */ - channelId?: string | null; - text?: string | null; - fileIds?: string[]; - mediaIds?: string[]; - poll?: ({ - choices: string[]; - multiple?: boolean; - expiresAt?: number | null; - expiredAfter?: number | null; - }) | null; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default false */ + quote?: boolean; }; }; }; @@ -19630,9 +22009,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - createdNote: components['schemas']['Note']; - }; + 'application/json': components['schemas']['Note'][]; }; }; /** @description Client error */ @@ -19659,12 +22036,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -19674,24 +22045,32 @@ export type operations = { }; }; /** - * notes/delete + * notes/replies * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:notes* + * **Credential required**: *No* */ - 'notes/delete': { + 'notes/replies': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ noteId: string; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default 10 */ + limit?: number; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Note'][]; + }; }; /** @description Client error */ 400: { @@ -19717,12 +22096,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -19732,24 +22105,44 @@ export type operations = { }; }; /** - * notes/favorites/create + * notes/search-by-tag * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:favorites* + * **Credential required**: *No* */ - 'notes/favorites/create': { + 'notes/search-by-tag': { requestBody: { content: { 'application/json': { + /** @default null */ + reply?: boolean | null; + /** @default null */ + renote?: boolean | null; + /** + * @description Only show notes that have attached files. + * @default false + */ + withFiles?: boolean; + /** @default null */ + poll?: boolean | null; /** Format: misskey:id */ - noteId: string; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default 10 */ + limit?: number; + tag?: string; + /** @description The outer arrays are chained with OR, the inner arrays are chained with AND. */ + query?: string[][]; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Note'][]; + }; }; /** @description Client error */ 400: { @@ -19775,12 +22168,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -19790,24 +22177,47 @@ export type operations = { }; }; /** - * notes/favorites/delete + * notes/search * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:favorites* + * **Credential required**: *No* */ - 'notes/favorites/delete': { + 'notes/search': { requestBody: { content: { 'application/json': { + query: string; /** Format: misskey:id */ - noteId: string; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default 10 */ + limit?: number; + /** @default 0 */ + offset?: number; + /** @description The local host is represented with `.`. */ + host?: string; + filetype?: string | null; + /** + * Format: misskey:id + * @default null + */ + userId?: string | null; + /** + * Format: misskey:id + * @default null + */ + channelId?: string | null; + order?: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Note'][]; + }; }; /** @description Client error */ 400: { @@ -19842,21 +22252,17 @@ export type operations = { }; }; /** - * notes/featured + * notes/show * @description No description provided. * * **Credential required**: *No* */ - 'notes/featured': { + 'notes/show': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - untilId?: string; /** Format: misskey:id */ - channelId?: string | null; + noteId: string; }; }; }; @@ -19864,7 +22270,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Note'][]; + 'application/json': components['schemas']['Note']; }; }; /** @description Client error */ @@ -19900,27 +22306,17 @@ export type operations = { }; }; /** - * notes/global-timeline + * notes/state * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'notes/global-timeline': { + 'notes/state': { requestBody: { content: { 'application/json': { - /** @default false */ - withFiles?: boolean; - /** @default true */ - withRenotes?: boolean; - /** @default 10 */ - limit?: number; /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - sinceDate?: number; - untilDate?: number; + noteId: string; }; }; }; @@ -19928,7 +22324,10 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Note'][]; + 'application/json': { + isFavorited: boolean; + isMutedThread: boolean; + }; }; }; /** @description Client error */ @@ -19964,46 +22363,24 @@ export type operations = { }; }; /** - * notes/hybrid-timeline + * notes/thread-muting/create * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'notes/hybrid-timeline': { + 'notes/thread-muting/create': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; /** Format: misskey:id */ - untilId?: string; - sinceDate?: number; - untilDate?: number; - /** @default false */ - allowPartial?: boolean; - /** @default true */ - includeMyRenotes?: boolean; - /** @default true */ - includeRenotedMyNotes?: boolean; - /** @default true */ - includeLocalRenotes?: boolean; - /** @default false */ - withFiles?: boolean; - /** @default true */ - withRenotes?: boolean; - /** @default false */ - withReplies?: boolean; + noteId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Note'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -20029,6 +22406,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -20038,40 +22421,24 @@ export type operations = { }; }; /** - * notes/local-timeline + * notes/thread-muting/delete * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'notes/local-timeline': { + 'notes/thread-muting/delete': { requestBody: { content: { 'application/json': { - /** @default false */ - withFiles?: boolean; - /** @default true */ - withRenotes?: boolean; - /** @default false */ - withReplies?: boolean; - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; /** Format: misskey:id */ - untilId?: string; - /** @default false */ - allowPartial?: boolean; - sinceDate?: number; - untilDate?: number; + noteId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Note'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -20106,24 +22473,37 @@ export type operations = { }; }; /** - * notes/mentions + * notes/timeline * @description No description provided. * * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'notes/mentions': { + 'notes/timeline': { requestBody: { content: { 'application/json': { - /** @default false */ - following?: boolean; /** @default 10 */ limit?: number; /** Format: misskey:id */ sinceId?: string; /** Format: misskey:id */ untilId?: string; - visibility?: string; + sinceDate?: number; + untilDate?: number; + /** @default false */ + allowPartial?: boolean; + /** @default true */ + includeMyRenotes?: boolean; + /** @default true */ + includeRenotedMyNotes?: boolean; + /** @default true */ + includeLocalRenotes?: boolean; + /** @default false */ + withFiles?: boolean; + /** @default true */ + withRenotes?: boolean; + /** @default true */ + withBots?: boolean; }; }; }; @@ -20167,19 +22547,18 @@ export type operations = { }; }; /** - * notes/polls/recommendation + * notes/translate * @description No description provided. * * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'notes/polls/recommendation': { + 'notes/translate': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** @default 0 */ - offset?: number; + /** Format: misskey:id */ + noteId: string; + targetLang: string; }; }; }; @@ -20187,7 +22566,10 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Note'][]; + 'application/json': { + sourceLang: string; + text: string; + }; }; }; /** @description Client error */ @@ -20223,18 +22605,19 @@ export type operations = { }; }; /** - * notes/polls/vote + * notes/unrenote * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:votes* + * **Credential required**: *Yes* / **Permission**: *write:notes* */ - 'notes/polls/vote': { + 'notes/unrenote': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ noteId: string; - choice: number; + /** @default false */ + quote?: boolean; }; }; }; @@ -20267,6 +22650,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -20276,24 +22665,40 @@ export type operations = { }; }; /** - * notes/reactions + * notes/user-list-timeline * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'notes/reactions': { + 'notes/user-list-timeline': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - noteId: string; - type?: string | null; + listId: string; /** @default 10 */ limit?: number; /** Format: misskey:id */ sinceId?: string; /** Format: misskey:id */ untilId?: string; + sinceDate?: number; + untilDate?: number; + /** @default false */ + allowPartial?: boolean; + /** @default true */ + includeMyRenotes?: boolean; + /** @default true */ + includeRenotedMyNotes?: boolean; + /** @default true */ + includeLocalRenotes?: boolean; + /** @default true */ + withRenotes?: boolean; + /** + * @description Only show notes that have attached files. + * @default false + */ + withFiles?: boolean; }; }; }; @@ -20301,7 +22706,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['NoteReaction'][]; + 'application/json': components['schemas']['Note'][]; }; }; /** @description Client error */ @@ -20337,25 +22742,63 @@ export type operations = { }; }; /** - * notes/reactions/create + * notes/edit * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:reactions* + * **Credential required**: *Yes* / **Permission**: *write:notes* */ - 'notes/reactions/create': { + 'notes/edit': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - noteId: string; - reaction: string; + editId?: string; + /** + * @default public + * @enum {string} + */ + visibility?: 'public' | 'home' | 'followers' | 'specified'; + visibleUserIds?: string[]; + cw?: string | null; + /** @default false */ + localOnly?: boolean; + /** + * @default null + * @enum {string|null} + */ + reactionAcceptance?: null | 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote'; + /** @default false */ + noExtractMentions?: boolean; + /** @default false */ + noExtractHashtags?: boolean; + /** @default false */ + noExtractEmojis?: boolean; + /** Format: misskey:id */ + replyId?: string | null; + /** Format: misskey:id */ + renoteId?: string | null; + /** Format: misskey:id */ + channelId?: string | null; + text?: string | null; + fileIds?: string[]; + mediaIds?: string[]; + poll?: ({ + choices: string[]; + multiple?: boolean; + expiresAt?: number | null; + expiredAfter?: number | null; + }) | null; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + createdNote: components['schemas']['Note']; + }; + }; }; /** @description Client error */ 400: { @@ -20381,6 +22824,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -20390,12 +22839,12 @@ export type operations = { }; }; /** - * notes/reactions/delete + * notes/versions * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:reactions* + * **Credential required**: *No* */ - 'notes/reactions/delete': { + 'notes/versions': { requestBody: { content: { 'application/json': { @@ -20405,9 +22854,11 @@ export type operations = { }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': Record<string, never>; + }; }; /** @description Client error */ 400: { @@ -20433,12 +22884,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -20448,32 +22893,25 @@ export type operations = { }; }; /** - * notes/renotes + * notifications/create * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:notifications* */ - 'notes/renotes': { + 'notifications/create': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - noteId: string; - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; + body: string; + header?: string | null; + icon?: string | null; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Note'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -20499,6 +22937,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -20508,32 +22952,16 @@ export type operations = { }; }; /** - * notes/replies + * notifications/flush * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:notifications* */ - 'notes/replies': { - requestBody: { - content: { - 'application/json': { - /** Format: misskey:id */ - noteId: string; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - /** @default 10 */ - limit?: number; - }; - }; - }; + 'notifications/flush': { responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Note'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -20568,44 +22996,16 @@ export type operations = { }; }; /** - * notes/search-by-tag + * notifications/mark-all-as-read * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:notifications* */ - 'notes/search-by-tag': { - requestBody: { - content: { - 'application/json': { - /** @default null */ - reply?: boolean | null; - /** @default null */ - renote?: boolean | null; - /** - * @description Only show notes that have attached files. - * @default false - */ - withFiles?: boolean; - /** @default null */ - poll?: boolean | null; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - /** @default 10 */ - limit?: number; - tag?: string; - /** @description The outer arrays are chained with OR, the inner arrays are chained with AND. */ - query?: string[][]; - }; - }; - }; + 'notifications/mark-all-as-read': { responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Note'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -20640,45 +23040,16 @@ export type operations = { }; }; /** - * notes/search + * notifications/test-notification * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:notifications* */ - 'notes/search': { - requestBody: { - content: { - 'application/json': { - query: string; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - /** @default 10 */ - limit?: number; - /** @default 0 */ - offset?: number; - /** @description The local host is represented with `.`. */ - host?: string; - /** - * Format: misskey:id - * @default null - */ - userId?: string | null; - /** - * Format: misskey:id - * @default null - */ - channelId?: string | null; - }; - }; - }; + 'notifications/test-notification': { responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Note'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -20704,6 +23075,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -20713,26 +23090,27 @@ export type operations = { }; }; /** - * notes/show + * page-push * @description No description provided. * - * **Credential required**: *No* + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* */ - 'notes/show': { + 'page-push': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - noteId: string; + pageId: string; + event: string; + var?: unknown; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Note']; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -20767,17 +23145,36 @@ export type operations = { }; }; /** - * notes/state + * pages/create * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *Yes* / **Permission**: *write:pages* */ - 'notes/state': { + 'pages/create': { requestBody: { content: { 'application/json': { + title: string; + name: string; + summary?: string | null; + content: { + [key: string]: unknown; + }[]; + variables: { + [key: string]: unknown; + }[]; + script: string; /** Format: misskey:id */ - noteId: string; + eyeCatchingImageId?: string | null; + /** + * @default sans-serif + * @enum {string} + */ + font?: 'serif' | 'sans-serif'; + /** @default false */ + alignCenter?: boolean; + /** @default false */ + hideTitleWhenPinned?: boolean; }; }; }; @@ -20785,10 +23182,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - isFavorited: boolean; - isMutedThread: boolean; - }; + 'application/json': components['schemas']['Page']; }; }; /** @description Client error */ @@ -20815,6 +23209,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -20824,17 +23224,17 @@ export type operations = { }; }; /** - * notes/thread-muting/create + * pages/delete * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *Yes* / **Permission**: *write:pages* */ - 'notes/thread-muting/create': { + 'pages/delete': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - noteId: string; + pageId: string; }; }; }; @@ -20867,12 +23267,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -20882,24 +23276,18 @@ export type operations = { }; }; /** - * notes/thread-muting/delete + * pages/featured * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *No* */ - 'notes/thread-muting/delete': { - requestBody: { - content: { - 'application/json': { - /** Format: misskey:id */ - noteId: string; - }; - }; - }; + 'pages/featured': { responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Page'][]; + }; }; /** @description Client error */ 400: { @@ -20934,44 +23322,24 @@ export type operations = { }; }; /** - * notes/timeline + * pages/like * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *Yes* / **Permission**: *write:page-likes* */ - 'notes/timeline': { + 'pages/like': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; /** Format: misskey:id */ - untilId?: string; - sinceDate?: number; - untilDate?: number; - /** @default false */ - allowPartial?: boolean; - /** @default true */ - includeMyRenotes?: boolean; - /** @default true */ - includeRenotedMyNotes?: boolean; - /** @default true */ - includeLocalRenotes?: boolean; - /** @default false */ - withFiles?: boolean; - /** @default true */ - withRenotes?: boolean; + pageId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Note'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -21006,18 +23374,19 @@ export type operations = { }; }; /** - * notes/translate + * pages/show * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *No* */ - 'notes/translate': { + 'pages/show': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - noteId: string; - targetLang: string; + pageId?: string; + name?: string; + username?: string; }; }; }; @@ -21025,10 +23394,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - sourceLang: string; - text: string; - }; + 'application/json': components['schemas']['Page']; }; }; /** @description Client error */ @@ -21064,17 +23430,17 @@ export type operations = { }; }; /** - * notes/unrenote + * pages/unlike * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:notes* + * **Credential required**: *Yes* / **Permission**: *write:page-likes* */ - 'notes/unrenote': { + 'pages/unlike': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - noteId: string; + pageId: string; }; }; }; @@ -21107,12 +23473,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -21122,49 +23482,40 @@ export type operations = { }; }; /** - * notes/user-list-timeline + * pages/update * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *Yes* / **Permission**: *write:pages* */ - 'notes/user-list-timeline': { + 'pages/update': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - listId: string; - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; + pageId: string; + title: string; + name: string; + summary?: string | null; + content: { + [key: string]: unknown; + }[]; + variables: { + [key: string]: unknown; + }[]; + script: string; /** Format: misskey:id */ - untilId?: string; - sinceDate?: number; - untilDate?: number; - /** @default false */ - allowPartial?: boolean; - /** @default true */ - includeMyRenotes?: boolean; - /** @default true */ - includeRenotedMyNotes?: boolean; - /** @default true */ - includeLocalRenotes?: boolean; - /** @default true */ - withRenotes?: boolean; - /** - * @description Only show notes that have attached files. - * @default false - */ - withFiles?: boolean; + eyeCatchingImageId?: string | null; + /** @enum {string} */ + font?: 'serif' | 'sans-serif'; + alignCenter?: boolean; + hideTitleWhenPinned?: boolean; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Note'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -21190,6 +23541,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -21199,25 +23556,28 @@ export type operations = { }; }; /** - * notifications/create + * flash/create * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:notifications* + * **Credential required**: *Yes* / **Permission**: *write:flash* */ - 'notifications/create': { + 'flash/create': { requestBody: { content: { 'application/json': { - body: string; - header?: string | null; - icon?: string | null; + title: string; + summary: string; + script: string; + permissions: string[]; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Flash']; + }; }; /** @description Client error */ 400: { @@ -21258,12 +23618,20 @@ export type operations = { }; }; /** - * notifications/mark-all-as-read + * flash/delete * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:notifications* + * **Credential required**: *Yes* / **Permission**: *write:flash* */ - 'notifications/mark-all-as-read': { + 'flash/delete': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + flashId: string; + }; + }; + }; responses: { /** @description OK (without any results) */ 204: { @@ -21302,16 +23670,18 @@ export type operations = { }; }; /** - * notifications/test-notification + * flash/featured * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:notifications* + * **Credential required**: *No* */ - 'notifications/test-notification': { + 'flash/featured': { responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Flash'][]; + }; }; /** @description Client error */ 400: { @@ -21337,12 +23707,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -21352,20 +23716,17 @@ export type operations = { }; }; /** - * page-push + * flash/like * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *write:flash-likes* */ - 'page-push': { + 'flash/like': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - pageId: string; - event: string; - var?: unknown; + flashId: string; }; }; }; @@ -21407,36 +23768,17 @@ export type operations = { }; }; /** - * pages/create + * flash/show * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:pages* + * **Credential required**: *No* */ - 'pages/create': { + 'flash/show': { requestBody: { content: { 'application/json': { - title: string; - name: string; - summary?: string | null; - content: { - [key: string]: unknown; - }[]; - variables: { - [key: string]: unknown; - }[]; - script: string; /** Format: misskey:id */ - eyeCatchingImageId?: string | null; - /** - * @default sans-serif - * @enum {string} - */ - font?: 'serif' | 'sans-serif'; - /** @default false */ - alignCenter?: boolean; - /** @default false */ - hideTitleWhenPinned?: boolean; + flashId: string; }; }; }; @@ -21444,7 +23786,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Page']; + 'application/json': components['schemas']['Flash']; }; }; /** @description Client error */ @@ -21471,12 +23813,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -21486,17 +23822,17 @@ export type operations = { }; }; /** - * pages/delete + * flash/unlike * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:pages* + * **Credential required**: *Yes* / **Permission**: *write:flash-likes* */ - 'pages/delete': { + 'flash/unlike': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - pageId: string; + flashId: string; }; }; }; @@ -21538,63 +23874,23 @@ export type operations = { }; }; /** - * pages/featured - * @description No description provided. - * - * **Credential required**: *No* - */ - 'pages/featured': { - responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Page'][]; - }; - }; - /** @description Client error */ - 400: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description Authentication error */ - 401: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description Forbidden error */ - 403: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description I'm Ai */ - 418: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description Internal server error */ - 500: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - }; - }; - /** - * pages/like + * flash/update * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:page-likes* + * **Credential required**: *Yes* / **Permission**: *write:flash* */ - 'pages/like': { + 'flash/update': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - pageId: string; + flashId: string; + title?: string; + summary?: string; + script?: string; + permissions?: string[]; + /** @enum {string} */ + visibility?: 'public' | 'private'; }; }; }; @@ -21627,6 +23923,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -21636,19 +23938,21 @@ export type operations = { }; }; /** - * pages/show + * flash/my * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *read:flash* */ - 'pages/show': { + 'flash/my': { requestBody: { content: { 'application/json': { + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; /** Format: misskey:id */ - pageId?: string; - name?: string; - username?: string; + untilId?: string; }; }; }; @@ -21656,7 +23960,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Page']; + 'application/json': components['schemas']['Flash'][]; }; }; /** @description Client error */ @@ -21692,24 +23996,34 @@ export type operations = { }; }; /** - * pages/unlike + * flash/my-likes * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:page-likes* + * **Credential required**: *Yes* / **Permission**: *read:flash-likes* */ - 'pages/unlike': { + 'flash/my-likes': { requestBody: { content: { 'application/json': { + /** @default 10 */ + limit?: number; /** Format: misskey:id */ - pageId: string; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + /** Format: id */ + id: string; + flash: components['schemas']['Flash']; + }[]; + }; }; /** @description Client error */ 400: { @@ -21744,40 +24058,20 @@ export type operations = { }; }; /** - * pages/update + * ping * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:pages* + * **Credential required**: *No* */ - 'pages/update': { - requestBody: { - content: { - 'application/json': { - /** Format: misskey:id */ - pageId: string; - title: string; - name: string; - summary?: string | null; - content: { - [key: string]: unknown; - }[]; - variables: { - [key: string]: unknown; - }[]; - script: string; - /** Format: misskey:id */ - eyeCatchingImageId?: string | null; - /** @enum {string} */ - font?: 'serif' | 'sans-serif'; - alignCenter?: boolean; - hideTitleWhenPinned?: boolean; - }; - }; - }; + ping: { responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + pong: number; + }; + }; }; /** @description Client error */ 400: { @@ -21803,12 +24097,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -21818,27 +24106,17 @@ export type operations = { }; }; /** - * flash/create + * pinned-users * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:flash* + * **Credential required**: *No* */ - 'flash/create': { - requestBody: { - content: { - 'application/json': { - title: string; - summary: string; - script: string; - permissions: string[]; - }; - }; - }; + 'pinned-users': { responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Flash']; + 'application/json': components['schemas']['UserDetailed'][]; }; }; /** @description Client error */ @@ -21865,12 +24143,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -21880,17 +24152,17 @@ export type operations = { }; }; /** - * flash/delete + * promo/read * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:flash* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'flash/delete': { + 'promo/read': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - flashId: string; + noteId: string; }; }; }; @@ -21932,17 +24204,17 @@ export type operations = { }; }; /** - * flash/featured + * roles/list * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'flash/featured': { + 'roles/list': { responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Flash'][]; + 'application/json': components['schemas']['Role'][]; }; }; /** @description Client error */ @@ -21978,24 +24250,26 @@ export type operations = { }; }; /** - * flash/like + * roles/show * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:flash-likes* + * **Credential required**: *No* */ - 'flash/like': { + 'roles/show': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - flashId: string; + roleId: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Role']; + }; }; /** @description Client error */ 400: { @@ -22030,17 +24304,23 @@ export type operations = { }; }; /** - * flash/show + * roles/users * @description No description provided. * * **Credential required**: *No* */ - 'flash/show': { + 'roles/users': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - flashId: string; + roleId: string; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default 10 */ + limit?: number; }; }; }; @@ -22048,7 +24328,11 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Flash']; + 'application/json': { + /** Format: misskey:id */ + id: string; + user: components['schemas']['UserDetailed']; + }[]; }; }; /** @description Client error */ @@ -22084,24 +24368,34 @@ export type operations = { }; }; /** - * flash/unlike + * roles/notes * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:flash-likes* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'flash/unlike': { + 'roles/notes': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - flashId: string; + roleId: string; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + sinceDate?: number; + untilDate?: number; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Note'][]; + }; }; /** @description Client error */ 400: { @@ -22136,23 +24430,17 @@ export type operations = { }; }; /** - * flash/update - * @description No description provided. + * request-reset-password + * @description Request a users password to be reset. * - * **Credential required**: *Yes* / **Permission**: *write:flash* + * **Credential required**: *No* */ - 'flash/update': { + 'request-reset-password': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - flashId: string; - title: string; - summary: string; - script: string; - permissions: string[]; - /** @enum {string} */ - visibility?: 'public' | 'private'; + username: string; + email: string; }; }; }; @@ -22200,30 +24488,16 @@ export type operations = { }; }; /** - * flash/my - * @description No description provided. + * reset-db + * @description Only available when running with <code>NODE_ENV=testing</code>. Reset the database and flush Redis. * - * **Credential required**: *Yes* / **Permission**: *read:flash* + * **Credential required**: *No* */ - 'flash/my': { - requestBody: { - content: { - 'application/json': { - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - }; - }; - }; + 'reset-db': { responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Flash'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -22258,34 +24532,24 @@ export type operations = { }; }; /** - * flash/my-likes - * @description No description provided. + * reset-password + * @description Complete the password reset that was previously requested. * - * **Credential required**: *Yes* / **Permission**: *read:flash-likes* + * **Credential required**: *No* */ - 'flash/my-likes': { + 'reset-password': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; + token: string; + password: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': { - /** Format: id */ - id: string; - flash: components['schemas']['Flash']; - }[]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -22320,18 +24584,29 @@ export type operations = { }; }; /** - * ping + * server-info * @description No description provided. * * **Credential required**: *No* */ - ping: { + 'server-info': { responses: { /** @description OK (with results) */ 200: { content: { 'application/json': { - pong: number; + machine: string; + cpu: { + model: string; + cores: number; + }; + mem: { + total: number; + }; + fs: { + total: number; + used: number; + }; }; }; }; @@ -22368,17 +24643,25 @@ export type operations = { }; }; /** - * pinned-users + * stats * @description No description provided. * * **Credential required**: *No* */ - 'pinned-users': { + stats: { responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['UserDetailed'][]; + 'application/json': { + notesCount: number; + originalNotesCount: number; + usersCount: number; + originalUsersCount: number; + instances: number; + driveUsageLocal: number; + driveUsageRemote: number; + }; }; }; /** @description Client error */ @@ -22414,21 +24697,31 @@ export type operations = { }; }; /** - * promo/read - * @description No description provided. + * sw/show-registration + * @description Check push notification registration exists. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* */ - 'promo/read': { + 'sw/show-registration': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - noteId: string; + endpoint: string; }; }; }; responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + userId: string; + endpoint: string; + sendReadMessage: boolean; + } | null; + }; + }; /** @description OK (without any results) */ 204: { content: never; @@ -22466,17 +24759,30 @@ export type operations = { }; }; /** - * roles/list - * @description No description provided. + * sw/update-registration + * @description Update push notification registration. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* */ - 'roles/list': { + 'sw/update-registration': { + requestBody: { + content: { + 'application/json': { + endpoint: string; + sendReadMessage?: boolean; + }; + }; + }; responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Role'][]; + 'application/json': { + userId: string; + endpoint: string; + sendReadMessage: boolean; + }; }; }; /** @description Client error */ @@ -22512,17 +24818,21 @@ export type operations = { }; }; /** - * roles/show - * @description No description provided. + * sw/register + * @description Register to receive push notifications. * - * **Credential required**: *No* + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* */ - 'roles/show': { + 'sw/register': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - roleId: string; + endpoint: string; + auth: string; + publickey: string; + /** @default false */ + sendReadMessage?: boolean; }; }; }; @@ -22530,7 +24840,14 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Role']; + 'application/json': { + /** @enum {string} */ + state?: 'already-subscribed' | 'subscribed'; + key: string | null; + userId: string; + endpoint: string; + sendReadMessage: boolean; + }; }; }; /** @description Client error */ @@ -22566,36 +24883,23 @@ export type operations = { }; }; /** - * roles/users - * @description No description provided. + * sw/unregister + * @description Unregister from receiving push notifications. * * **Credential required**: *No* */ - 'roles/users': { + 'sw/unregister': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - roleId: string; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - /** @default 10 */ - limit?: number; + endpoint: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': { - /** Format: misskey:id */ - id: string; - user: components['schemas']['User']; - }[]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -22630,25 +24934,23 @@ export type operations = { }; }; /** - * roles/notes - * @description No description provided. + * test + * @description Endpoint for testing input validation. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *No* */ - 'roles/notes': { + test: { requestBody: { content: { 'application/json': { + required: boolean; + string?: string; + /** @default hello */ + default?: string; + /** @default hello */ + nullableDefault?: string | null; /** Format: misskey:id */ - roleId: string; - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - sinceDate?: number; - untilDate?: number; + id?: string; }; }; }; @@ -22656,7 +24958,15 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Note'][]; + 'application/json': { + /** Format: misskey:id */ + id?: string; + required: boolean; + string?: string; + default?: string; + /** @default hello */ + nullableDefault?: string | null; + }; }; }; /** @description Client error */ @@ -22692,24 +25002,27 @@ export type operations = { }; }; /** - * request-reset-password - * @description Request a users password to be reset. + * username/available + * @description No description provided. * * **Credential required**: *No* */ - 'request-reset-password': { + 'username/available': { requestBody: { content: { 'application/json': { username: string; - email: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + available: boolean; + }; + }; }; /** @description Client error */ 400: { @@ -22735,12 +25048,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -22750,16 +25057,45 @@ export type operations = { }; }; /** - * reset-db - * @description Only available when running with <code>NODE_ENV=testing</code>. Reset the database and flush Redis. + * users + * @description No description provided. * * **Credential required**: *No* */ - 'reset-db': { + users: { + requestBody: { + content: { + 'application/json': { + /** @default 10 */ + limit?: number; + /** @default 0 */ + offset?: number; + /** @enum {string} */ + sort?: '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+updatedAt' | '-updatedAt'; + /** + * @default all + * @enum {string} + */ + state?: 'all' | 'alive'; + /** + * @default local + * @enum {string} + */ + origin?: 'combined' | 'local' | 'remote'; + /** + * @description The local host is represented with `null`. + * @default null + */ + hostname?: string | null; + }; + }; + }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['UserDetailed'][]; + }; }; /** @description Client error */ 400: { @@ -22794,24 +25130,32 @@ export type operations = { }; }; /** - * reset-password - * @description Complete the password reset that was previously requested. + * users/clips + * @description Show all clips this user owns. * * **Credential required**: *No* */ - 'reset-password': { + 'users/clips': { requestBody: { content: { 'application/json': { - token: string; - password: string; + /** Format: misskey:id */ + userId: string; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Clip'][]; + }; }; /** @description Client error */ 400: { @@ -22846,30 +25190,34 @@ export type operations = { }; }; /** - * server-info - * @description No description provided. + * users/followers + * @description Show everyone that follows this user. * * **Credential required**: *No* */ - 'server-info': { + 'users/followers': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + userId?: string; + username?: string; + /** @description The local host is represented with `null`. */ + host?: string | null; + }; + }; + }; responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': { - machine: string; - cpu: { - model: string; - cores: number; - }; - mem: { - total: number; - }; - fs: { - total: number; - used: number; - }; - }; + 'application/json': components['schemas']['Following'][]; }; }; /** @description Client error */ @@ -22905,25 +25253,35 @@ export type operations = { }; }; /** - * stats - * @description No description provided. + * users/following + * @description Show everyone that this user is following. * * **Credential required**: *No* */ - stats: { + 'users/following': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + userId?: string; + username?: string; + /** @description The local host is represented with `null`. */ + host?: string | null; + birthday?: string | null; + }; + }; + }; responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': { - notesCount: number; - originalNotesCount: number; - usersCount: number; - originalUsersCount: number; - instances: number; - driveUsageLocal: number; - driveUsageRemote: number; - }; + 'application/json': components['schemas']['Following'][]; }; }; /** @description Client error */ @@ -22959,17 +25317,23 @@ export type operations = { }; }; /** - * sw/show-registration - * @description Check push notification registration exists. + * users/gallery/posts + * @description Show all gallery posts by the given user. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *No* */ - 'sw/show-registration': { + 'users/gallery/posts': { requestBody: { content: { 'application/json': { - endpoint: string; + /** Format: misskey:id */ + userId: string; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; }; }; }; @@ -22977,17 +25341,9 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - userId: string; - endpoint: string; - sendReadMessage: boolean; - } | null; + 'application/json': components['schemas']['GalleryPost'][]; }; }; - /** @description OK (without any results) */ - 204: { - content: never; - }; /** @description Client error */ 400: { content: { @@ -23021,18 +25377,19 @@ export type operations = { }; }; /** - * sw/update-registration - * @description Update push notification registration. + * users/get-frequently-replied-users + * @description Get a list of other users that the specified user frequently replies to. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *No* */ - 'sw/update-registration': { + 'users/get-frequently-replied-users': { requestBody: { content: { 'application/json': { - endpoint: string; - sendReadMessage?: boolean; + /** Format: misskey:id */ + userId: string; + /** @default 10 */ + limit?: number; }; }; }; @@ -23041,10 +25398,9 @@ export type operations = { 200: { content: { 'application/json': { - userId: string; - endpoint: string; - sendReadMessage: boolean; - }; + user: components['schemas']['UserDetailed']; + weight: number; + }[]; }; }; /** @description Client error */ @@ -23080,21 +25436,21 @@ export type operations = { }; }; /** - * sw/register - * @description Register to receive push notifications. + * users/featured-notes + * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *No* */ - 'sw/register': { + 'users/featured-notes': { requestBody: { content: { 'application/json': { - endpoint: string; - auth: string; - publickey: string; - /** @default false */ - sendReadMessage?: boolean; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + untilId?: string; + /** Format: misskey:id */ + userId: string; }; }; }; @@ -23102,14 +25458,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - /** @enum {string} */ - state?: 'already-subscribed' | 'subscribed'; - key: string | null; - userId: string; - endpoint: string; - sendReadMessage: boolean; - }; + 'application/json': components['schemas']['Note'][]; }; }; /** @description Client error */ @@ -23145,23 +25494,25 @@ export type operations = { }; }; /** - * sw/unregister - * @description Unregister from receiving push notifications. + * users/lists/create + * @description Create a new list of users. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'sw/unregister': { + 'users/lists/create': { requestBody: { content: { 'application/json': { - endpoint: string; + name: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['UserList']; + }; }; /** @description Client error */ 400: { @@ -23196,40 +25547,24 @@ export type operations = { }; }; /** - * test - * @description Endpoint for testing input validation. + * users/lists/delete + * @description Delete an existing list of users. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - test: { + 'users/lists/delete': { requestBody: { content: { 'application/json': { - required: boolean; - string?: string; - /** @default hello */ - default?: string; - /** @default hello */ - nullableDefault?: string | null; /** Format: misskey:id */ - id?: string; + listId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': { - /** Format: misskey:id */ - id: string; - required: boolean; - string: string; - default: string; - /** @default hello */ - nullableDefault: string | null; - }; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -23264,16 +25599,17 @@ export type operations = { }; }; /** - * username/available - * @description No description provided. + * users/lists/list + * @description Show all lists that the authenticated user has created. * - * **Credential required**: *No* + * **Credential required**: *No* / **Permission**: *read:account* */ - 'username/available': { + 'users/lists/list': { requestBody: { content: { 'application/json': { - username: string; + /** Format: misskey:id */ + userId?: string; }; }; }; @@ -23281,9 +25617,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - available: boolean; - }; + 'application/json': components['schemas']['UserList'][]; }; }; /** @description Client error */ @@ -23319,45 +25653,26 @@ export type operations = { }; }; /** - * users - * @description No description provided. + * users/lists/pull + * @description Remove a user from a list. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - users: { + 'users/lists/pull': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** @default 0 */ - offset?: number; - /** @enum {string} */ - sort?: '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+updatedAt' | '-updatedAt'; - /** - * @default all - * @enum {string} - */ - state?: 'all' | 'alive'; - /** - * @default local - * @enum {string} - */ - origin?: 'combined' | 'local' | 'remote'; - /** - * @description The local host is represented with `null`. - * @default null - */ - hostname?: string | null; + /** Format: misskey:id */ + listId: string; + /** Format: misskey:id */ + userId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['UserDetailed'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -23392,32 +25707,26 @@ export type operations = { }; }; /** - * users/clips - * @description Show all clips this user owns. + * users/lists/push + * @description Add a user to an existing list. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'users/clips': { + 'users/lists/push': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - userId: string; - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; + listId: string; /** Format: misskey:id */ - untilId?: string; + userId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Clip'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -23443,6 +25752,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -23452,26 +25767,19 @@ export type operations = { }; }; /** - * users/followers - * @description Show everyone that follows this user. + * users/lists/show + * @description Show the properties of a list. * - * **Credential required**: *No* + * **Credential required**: *No* / **Permission**: *read:account* */ - 'users/followers': { + 'users/lists/show': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - userId?: string; - username?: string; - /** @description The local host is represented with `null`. */ - host?: string | null; + listId: string; + /** @default false */ + forPublic?: boolean; }; }; }; @@ -23479,7 +25787,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Following'][]; + 'application/json': components['schemas']['UserList']; }; }; /** @description Client error */ @@ -23515,36 +25823,24 @@ export type operations = { }; }; /** - * users/following - * @description Show everyone that this user is following. + * users/lists/favorite + * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'users/following': { + 'users/lists/favorite': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - userId?: string; - username?: string; - /** @description The local host is represented with `null`. */ - host?: string | null; - birthday?: string | null; + listId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Following'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -23579,32 +25875,24 @@ export type operations = { }; }; /** - * users/gallery/posts - * @description Show all gallery posts by the given user. + * users/lists/unfavorite + * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'users/gallery/posts': { + 'users/lists/unfavorite': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - userId: string; - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; + listId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['GalleryPost'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -23639,19 +25927,19 @@ export type operations = { }; }; /** - * users/get-frequently-replied-users - * @description Get a list of other users that the specified user frequently replies to. + * users/lists/update + * @description Update the properties of a list. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'users/get-frequently-replied-users': { + 'users/lists/update': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - userId: string; - /** @default 10 */ - limit?: number; + listId: string; + name?: string; + isPublic?: boolean; }; }; }; @@ -23659,10 +25947,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - user: components['schemas']['UserDetailed']; - weight: number; - }[]; + 'application/json': components['schemas']['UserList']; }; }; /** @description Client error */ @@ -23698,21 +25983,18 @@ export type operations = { }; }; /** - * users/featured-notes + * users/lists/create-from-public * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'users/featured-notes': { + 'users/lists/create-from-public': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - untilId?: string; + name: string; /** Format: misskey:id */ - userId: string; + listId: string; }; }; }; @@ -23720,7 +26002,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Note'][]; + 'application/json': components['schemas']['UserList']; }; }; /** @description Client error */ @@ -23756,25 +26038,27 @@ export type operations = { }; }; /** - * users/lists/create - * @description Create a new list of users. + * users/lists/update-membership + * @description No description provided. * * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'users/lists/create': { + 'users/lists/update-membership': { requestBody: { content: { 'application/json': { - name: string; + /** Format: misskey:id */ + listId: string; + /** Format: misskey:id */ + userId: string; + withReplies?: boolean; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['UserList']; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -23809,24 +26093,43 @@ export type operations = { }; }; /** - * users/lists/delete - * @description Delete an existing list of users. + * users/lists/get-memberships + * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *No* / **Permission**: *read:account* */ - 'users/lists/delete': { + 'users/lists/get-memberships': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ listId: string; + /** @default false */ + forPublic?: boolean; + /** @default 30 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + /** Format: misskey:id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: misskey:id */ + userId: string; + user: components['schemas']['UserLite']; + withReplies: boolean; + }[]; + }; }; /** @description Client error */ 400: { @@ -23861,17 +26164,35 @@ export type operations = { }; }; /** - * users/lists/list - * @description Show all lists that the authenticated user has created. + * users/notes + * @description No description provided. * - * **Credential required**: *No* / **Permission**: *read:account* + * **Credential required**: *No* */ - 'users/lists/list': { + 'users/notes': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - userId?: string; + userId: string; + /** @default false */ + withReplies?: boolean; + /** @default true */ + withRenotes?: boolean; + /** @default false */ + withChannelNotes?: boolean; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + sinceDate?: number; + untilDate?: number; + /** @default false */ + allowPartial?: boolean; + /** @default false */ + withFiles?: boolean; }; }; }; @@ -23879,7 +26200,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['UserList'][]; + 'application/json': components['schemas']['Note'][]; }; }; /** @description Client error */ @@ -23915,26 +26236,32 @@ export type operations = { }; }; /** - * users/lists/pull - * @description Remove a user from a list. + * users/pages + * @description Show all pages this user created. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *No* */ - 'users/lists/pull': { + 'users/pages': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - listId: string; /** Format: misskey:id */ userId: string; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Page'][]; + }; }; /** @description Client error */ 400: { @@ -23969,26 +26296,32 @@ export type operations = { }; }; /** - * users/lists/push - * @description Add a user to an existing list. + * users/flashs + * @description Show all flashs this user created. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *No* */ - 'users/lists/push': { + 'users/flashs': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - listId: string; /** Format: misskey:id */ userId: string; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['Flash'][]; + }; }; /** @description Client error */ 400: { @@ -24014,12 +26347,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -24029,19 +26356,25 @@ export type operations = { }; }; /** - * users/lists/show - * @description Show the properties of a list. + * users/reactions + * @description Show all reactions this user made. * - * **Credential required**: *No* / **Permission**: *read:account* + * **Credential required**: *No* */ - 'users/lists/show': { + 'users/reactions': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - listId: string; - /** @default false */ - forPublic?: boolean; + userId: string; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + sinceDate?: number; + untilDate?: number; }; }; }; @@ -24049,7 +26382,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['UserList']; + 'application/json': components['schemas']['NoteReaction'][]; }; }; /** @description Client error */ @@ -24085,24 +26418,28 @@ export type operations = { }; }; /** - * users/lists/favorite - * @description No description provided. + * users/recommendation + * @description Show users that the authenticated user might be interested to follow. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'users/lists/favorite': { + 'users/recommendation': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - listId: string; + /** @default 10 */ + limit?: number; + /** @default 0 */ + offset?: number; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['UserDetailed'][]; + }; }; /** @description Client error */ 400: { @@ -24137,24 +26474,47 @@ export type operations = { }; }; /** - * users/lists/unfavorite - * @description No description provided. + * users/relation + * @description Show the different kinds of relations between the authenticated user and the specified user(s). * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'users/lists/unfavorite': { + 'users/relation': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - listId: string; + userId: string | string[]; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': OneOf<[{ + /** Format: id */ + id: string; + isFollowing: boolean; + hasPendingFollowRequestFromYou: boolean; + hasPendingFollowRequestToYou: boolean; + isFollowed: boolean; + isBlocking: boolean; + isBlocked: boolean; + isMuted: boolean; + isRenoteMuted: boolean; + }, { + /** Format: id */ + id: string; + isFollowing: boolean; + hasPendingFollowRequestFromYou: boolean; + hasPendingFollowRequestToYou: boolean; + isFollowed: boolean; + isBlocking: boolean; + isBlocked: boolean; + isMuted: boolean; + isRenoteMuted: boolean; + }[]]>; + }; }; /** @description Client error */ 400: { @@ -24189,28 +26549,25 @@ export type operations = { }; }; /** - * users/lists/update - * @description Update the properties of a list. + * users/report-abuse + * @description File a report. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *Yes* / **Permission**: *write:report-abuse* */ - 'users/lists/update': { + 'users/report-abuse': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - listId: string; - name?: string; - isPublic?: boolean; + userId: string; + comment: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['UserList']; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -24245,18 +26602,21 @@ export type operations = { }; }; /** - * users/lists/create-from-public - * @description No description provided. + * users/search-by-username-and-host + * @description Search for a user by username and/or host. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *No* */ - 'users/lists/create-from-public': { + 'users/search-by-username-and-host': { requestBody: { content: { 'application/json': { - name: string; - /** Format: misskey:id */ - listId: string; + /** @default 10 */ + limit?: number; + /** @default true */ + detail?: boolean; + username?: string | null; + host?: string | null; }; }; }; @@ -24264,7 +26624,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['UserList']; + 'application/json': components['schemas']['User'][]; }; }; /** @description Client error */ @@ -24300,27 +26660,36 @@ export type operations = { }; }; /** - * users/lists/update-membership - * @description No description provided. + * users/search + * @description Search for users. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *No* */ - 'users/lists/update-membership': { + 'users/search': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - listId: string; - /** Format: misskey:id */ - userId: string; - withReplies?: boolean; + query: string; + /** @default 0 */ + offset?: number; + /** @default 10 */ + limit?: number; + /** + * @default combined + * @enum {string} + */ + origin?: 'local' | 'remote' | 'combined'; + /** @default true */ + detail?: boolean; }; }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['User'][]; + }; }; /** @description Client error */ 400: { @@ -24355,25 +26724,21 @@ export type operations = { }; }; /** - * users/lists/get-memberships - * @description No description provided. + * users/show + * @description Show the properties of a user. * - * **Credential required**: *No* / **Permission**: *read:account* + * **Credential required**: *No* */ - 'users/lists/get-memberships': { + 'users/show': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - listId: string; - /** @default false */ - forPublic?: boolean; - /** @default 30 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; + userId?: string; + userIds?: string[]; + username?: string; + /** @description The local host is represented with `null`. */ + host?: string | null; }; }; }; @@ -24381,16 +26746,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - /** Format: misskey:id */ - id: string; - /** Format: date-time */ - createdAt: string; - /** Format: misskey:id */ - userId: string; - user: components['schemas']['User']; - withReplies: boolean; - }[]; + 'application/json': components['schemas']['UserDetailed'] | components['schemas']['UserDetailed'][]; }; }; /** @description Client error */ @@ -24426,35 +26782,17 @@ export type operations = { }; }; /** - * users/notes + * users/achievements * @description No description provided. * * **Credential required**: *No* */ - 'users/notes': { + 'users/achievements': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ userId: string; - /** @default false */ - withReplies?: boolean; - /** @default true */ - withRenotes?: boolean; - /** @default false */ - withChannelNotes?: boolean; - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - sinceDate?: number; - untilDate?: number; - /** @default false */ - allowPartial?: boolean; - /** @default false */ - withFiles?: boolean; }; }; }; @@ -24462,7 +26800,10 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Note'][]; + 'application/json': { + name: string; + unlockedAt: number; + }[]; }; }; /** @description Client error */ @@ -24498,32 +26839,26 @@ export type operations = { }; }; /** - * users/pages - * @description Show all pages this user created. + * users/update-memo + * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'users/pages': { + 'users/update-memo': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ userId: string; - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; + /** @description A personal memo for the target user. If null or empty, delete the memo. */ + memo: string | null; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['Page'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -24558,23 +26893,16 @@ export type operations = { }; }; /** - * users/flashs - * @description Show all flashs this user created. + * fetch-rss + * @description No description provided. * * **Credential required**: *No* - */ - 'users/flashs': { - requestBody: { - content: { - 'application/json': { - /** Format: misskey:id */ - userId: string; - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; + */ + 'fetch-rss': { + requestBody: { + content: { + 'application/json': { + url: string; }; }; }; @@ -24582,7 +26910,9 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['Flash'][]; + 'application/json': { + items: Record<string, never>[]; + }; }; }; /** @description Client error */ @@ -24618,25 +26948,18 @@ export type operations = { }; }; /** - * users/reactions - * @description Show all reactions this user made. + * fetch-external-resources + * @description No description provided. * - * **Credential required**: *No* + * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. + * **Credential required**: *Yes* */ - 'users/reactions': { + 'fetch-external-resources': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - userId: string; - /** @default 10 */ - limit?: number; - /** Format: misskey:id */ - sinceId?: string; - /** Format: misskey:id */ - untilId?: string; - sinceDate?: number; - untilDate?: number; + url: string; + hash: string; }; }; }; @@ -24644,7 +26967,10 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['NoteReaction'][]; + 'application/json': { + type: string; + data: string; + }; }; }; /** @description Client error */ @@ -24671,6 +26997,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -24680,27 +27012,24 @@ export type operations = { }; }; /** - * users/recommendation - * @description Show users that the authenticated user might be interested to follow. + * retention + * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *No* */ - 'users/recommendation': { - requestBody: { - content: { - 'application/json': { - /** @default 10 */ - limit?: number; - /** @default 0 */ - offset?: number; - }; - }; - }; + retention: { responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['UserDetailed'][]; + 'application/json': { + /** Format: date-time */ + createdAt: string; + users: number; + data: { + [key: string]: number; + }; + }[]; }; }; /** @description Client error */ @@ -24736,47 +27065,24 @@ export type operations = { }; }; /** - * users/relation - * @description Show the different kinds of relations between the authenticated user and the specified user(s). + * sponsors + * @description Get Sharkey GH Sponsors * - * **Credential required**: *Yes* / **Permission**: *read:account* + * **Credential required**: *No* */ - 'users/relation': { + sponsors: { requestBody: { content: { 'application/json': { - userId: string | string[]; + /** @default false */ + forceUpdate?: boolean; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': OneOf<[{ - /** Format: id */ - id: string; - isFollowing: boolean; - hasPendingFollowRequestFromYou: boolean; - hasPendingFollowRequestToYou: boolean; - isFollowed: boolean; - isBlocking: boolean; - isBlocked: boolean; - isMuted: boolean; - isRenoteMuted: boolean; - }, { - /** Format: id */ - id: string; - isFollowing: boolean; - hasPendingFollowRequestFromYou: boolean; - hasPendingFollowRequestToYou: boolean; - isFollowed: boolean; - isBlocking: boolean; - isBlocked: boolean; - isMuted: boolean; - isRenoteMuted: boolean; - }[]]>; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -24811,18 +27117,20 @@ export type operations = { }; }; /** - * users/report-abuse - * @description File a report. + * bubble-game/register + * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:report-abuse* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'users/report-abuse': { + 'bubble-game/register': { requestBody: { content: { 'application/json': { - /** Format: misskey:id */ - userId: string; - comment: string; + score: number; + seed: string; + logs: number[][]; + gameMode: string; + gameVersion: number; }; }; }; @@ -24855,6 +27163,12 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; + /** @description To many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; /** @description Internal server error */ 500: { content: { @@ -24864,21 +27178,16 @@ export type operations = { }; }; /** - * users/search-by-username-and-host - * @description Search for a user by username and/or host. + * bubble-game/ranking + * @description No description provided. * * **Credential required**: *No* */ - 'users/search-by-username-and-host': { + 'bubble-game/ranking': { requestBody: { content: { 'application/json': { - /** @default 10 */ - limit?: number; - /** @default true */ - detail?: boolean; - username?: string | null; - host?: string | null; + gameMode: string; }; }; }; @@ -24886,7 +27195,12 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['User'][]; + 'application/json': { + /** Format: misskey:id */ + id: string; + score: number; + user?: components['schemas']['UserLite']; + }[]; }; }; /** @description Client error */ @@ -24922,36 +27236,24 @@ export type operations = { }; }; /** - * users/search - * @description Search for users. + * reversi/cancel-match + * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'users/search': { + 'reversi/cancel-match': { requestBody: { content: { 'application/json': { - query: string; - /** @default 0 */ - offset?: number; - /** @default 10 */ - limit?: number; - /** - * @default combined - * @enum {string} - */ - origin?: 'local' | 'remote' | 'combined'; - /** @default true */ - detail?: boolean; + /** Format: misskey:id */ + userId?: string | null; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': components['schemas']['User'][]; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -24986,21 +27288,23 @@ export type operations = { }; }; /** - * users/show - * @description Show the properties of a user. + * reversi/games + * @description No description provided. * * **Credential required**: *No* */ - 'users/show': { + 'reversi/games': { requestBody: { content: { 'application/json': { + /** @default 10 */ + limit?: number; /** Format: misskey:id */ - userId?: string; - userIds?: string[]; - username?: string; - /** @description The local host is represented with `null`. */ - host?: string | null; + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** @default false */ + my?: boolean; }; }; }; @@ -25008,7 +27312,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': components['schemas']['UserDetailed'] | components['schemas']['UserDetailed'][]; + 'application/json': components['schemas']['ReversiGameLite'][]; }; }; /** @description Client error */ @@ -25044,17 +27348,21 @@ export type operations = { }; }; /** - * users/achievements + * reversi/match * @description No description provided. * - * **Credential required**: *No* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'users/achievements': { + 'reversi/match': { requestBody: { content: { 'application/json': { /** Format: misskey:id */ - userId: string; + userId?: string | null; + /** @default false */ + noIrregularRules?: boolean; + /** @default false */ + multiple?: boolean; }; }; }; @@ -25062,12 +27370,13 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - name: string; - unlockedAt: number; - }[]; + 'application/json': components['schemas']['ReversiGameDetailed']; }; }; + /** @description OK (without any results) */ + 204: { + content: never; + }; /** @description Client error */ 400: { content: { @@ -25101,26 +27410,18 @@ export type operations = { }; }; /** - * users/update-memo + * reversi/invitations * @description No description provided. * - * **Credential required**: *Yes* / **Permission**: *write:account* + * **Credential required**: *Yes* / **Permission**: *read:account* */ - 'users/update-memo': { - requestBody: { - content: { - 'application/json': { - /** Format: misskey:id */ - userId: string; - /** @description A personal memo for the target user. If null or empty, delete the memo. */ - memo: string | null; - }; - }; - }; + 'reversi/invitations': { responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['UserLite'][]; + }; }; /** @description Client error */ 400: { @@ -25155,16 +27456,17 @@ export type operations = { }; }; /** - * fetch-rss + * reversi/show-game * @description No description provided. * * **Credential required**: *No* */ - 'fetch-rss': { + 'reversi/show-game': { requestBody: { content: { 'application/json': { - url: string; + /** Format: misskey:id */ + gameId: string; }; }; }; @@ -25172,9 +27474,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - items: Record<string, never>[]; - }; + 'application/json': components['schemas']['ReversiGameDetailed']; }; }; /** @description Client error */ @@ -25210,30 +27510,24 @@ export type operations = { }; }; /** - * fetch-external-resources + * reversi/surrender * @description No description provided. * - * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties. - * **Credential required**: *Yes* + * **Credential required**: *Yes* / **Permission**: *write:account* */ - 'fetch-external-resources': { + 'reversi/surrender': { requestBody: { content: { 'application/json': { - url: string; - hash: string; + /** Format: misskey:id */ + gameId: string; }; }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': { - type: string; - data: string; - }; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -25259,12 +27553,6 @@ export type operations = { 'application/json': components['schemas']['Error']; }; }; - /** @description To many requests */ - 429: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; /** @description Internal server error */ 500: { content: { @@ -25274,17 +27562,29 @@ export type operations = { }; }; /** - * retention + * reversi/verify * @description No description provided. * * **Credential required**: *No* */ - retention: { + 'reversi/verify': { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + gameId: string; + crc32: string; + }; + }; + }; responses: { /** @description OK (with results) */ 200: { content: { - 'application/json': unknown; + 'application/json': { + desynced: boolean; + game?: components['schemas']['ReversiGameDetailed'] | null; + }; }; }; /** @description Client error */ diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts index 0748d9863ed7db725384580efef343013cec939f..4de567e6d40d258ddb6f8861cab9129c307de56a 100644 --- a/packages/misskey-js/src/consts.ts +++ b/packages/misskey-js/src/consts.ts @@ -1,4 +1,4 @@ -export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'roleAssigned', 'achievementEarned'] as const; +export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'roleAssigned', 'achievementEarned', 'edited'] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; @@ -127,6 +127,7 @@ export const moderationLogTypes = [ 'resetPassword', 'suspendRemoteInstance', 'unsuspendRemoteInstance', + 'updateRemoteInstanceNote', 'markSensitiveDriveFile', 'unmarkSensitiveDriveFile', 'resolveAbuseReport', @@ -272,6 +273,12 @@ export type ModerationLogPayloads = { id: string; host: string; }; + updateRemoteInstanceNote: { + id: string; + host: string; + before: string | null; + after: string | null; + }; markSensitiveDriveFile: { fileId: string; fileUserId: string | null; diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index 2c490fa55e1e0a7dd3d16f20138b8a9fca89c56a..5a19bf74461bd84310153e40443a4c9a08ad6b7d 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -1,8 +1,8 @@ import { ModerationLogPayloads } from './consts.js'; -import { Announcement, EmojiDetailed, Page, User, UserDetailed } from './autogen/models'; +import { Announcement, EmojiDetailed, MeDetailed, Page, User, UserDetailedNotMe } from './autogen/models.js'; -export * from './autogen/entities'; -export * from './autogen/models'; +export * from './autogen/entities.js'; +export * from './autogen/models.js'; export type ID = string; export type DateString = string; @@ -19,7 +19,7 @@ export type ModerationLog = { id: ID; createdAt: DateString; userId: User['id']; - user: UserDetailed | null; + user: UserDetailedNotMe | null; } & ({ type: 'updateServerSettings'; info: ModerationLogPayloads['updateServerSettings']; @@ -98,6 +98,9 @@ export type ModerationLog = { } | { type: 'unsuspendRemoteInstance'; info: ModerationLogPayloads['unsuspendRemoteInstance']; +} | { + type: 'updateRemoteInstanceNote'; + info: ModerationLogPayloads['updateRemoteInstanceNote']; } | { type: 'markSensitiveDriveFile'; info: ModerationLogPayloads['markSensitiveDriveFile']; @@ -152,7 +155,7 @@ export type ServerStats = { } }; -export type ServerStatsLog = string[]; +export type ServerStatsLog = ServerStats[]; export type QueueStats = { deliver: { @@ -169,7 +172,7 @@ export type QueueStats = { }; }; -export type QueueStatsLog = string[]; +export type QueueStatsLog = QueueStats[]; export type EmojiAdded = { emoji: EmojiDetailed @@ -186,3 +189,38 @@ export type EmojiDeleted = { export type AnnouncementCreated = { announcement: Announcement; }; + +export type SignupRequest = { + username: string; + password: string; + host?: string; + invitationCode?: string; + emailAddress?: string; + 'hcaptcha-response'?: string | null; + 'g-recaptcha-response'?: string | null; + 'turnstile-response'?: string | null; +} + +export type SignupResponse = MeDetailed & { + token: string; +} + +export type SignupPendingRequest = { + code: string; +}; + +export type SignupPendingResponse = { + id: User['id'], + i: string, +}; + +export type SigninRequest = { + username: string; + password: string; + token?: string; +}; + +export type SigninResponse = { + id: User['id'], + i: string, +}; diff --git a/packages/misskey-js/src/streaming.ts b/packages/misskey-js/src/streaming.ts index c641706a4bfa37505bd8046e1633844f724836c1..0f2685778205caaa435f56c8ce40b01ad3aa8ff4 100644 --- a/packages/misskey-js/src/streaming.ts +++ b/packages/misskey-js/src/streaming.ts @@ -1,7 +1,9 @@ import { EventEmitter } from 'eventemitter3'; -import ReconnectingWebsocket from 'reconnecting-websocket'; +import _ReconnectingWebsocket from 'reconnecting-websocket'; import type { BroadcastEvents, Channels } from './streaming.types.js'; +const ReconnectingWebsocket = _ReconnectingWebsocket as unknown as typeof _ReconnectingWebsocket['default']; + export function urlQuery(obj: Record<string, string | number | boolean | undefined>): string { const params = Object.entries(obj) .filter(([, v]) => Array.isArray(v) ? v.length : v !== undefined) @@ -24,7 +26,7 @@ type StreamEvents = { * Misskey stream connection */ export default class Stream extends EventEmitter<StreamEvents> { - private stream: ReconnectingWebsocket; + private stream: _ReconnectingWebsocket.default; public state: 'initializing' | 'reconnecting' | 'connected' = 'initializing'; private sharedConnectionPools: Pool[] = []; private sharedConnections: SharedConnection[] = []; diff --git a/packages/misskey-js/src/streaming.types.ts b/packages/misskey-js/src/streaming.types.ts index e2b550ab7aad6d613c2038a737bdca29e1e74e66..912ad56f63ec2e5c7107f16a2611af1260f554fe 100644 --- a/packages/misskey-js/src/streaming.types.ts +++ b/packages/misskey-js/src/streaming.types.ts @@ -2,11 +2,13 @@ import { Antenna, DriveFile, DriveFolder, - MeDetailed, Note, Notification, Signin, User, + UserDetailed, + UserDetailedNotMe, + UserLite, } from './autogen/models.js'; import { AnnouncementCreated, @@ -17,6 +19,7 @@ import { QueueStatsLog, ServerStats, ServerStatsLog, + ReversiGameDetailed, } from './entities.js'; export type Channels = { @@ -27,16 +30,17 @@ export type Channels = { mention: (payload: Note) => void; reply: (payload: Note) => void; renote: (payload: Note) => void; - follow: (payload: User) => void; // 自分ãŒä»–人をフォãƒãƒ¼ã—ãŸã¨ã - followed: (payload: User) => void; // 他人ãŒè‡ªåˆ†ã‚’フォãƒãƒ¼ã—ãŸã¨ã - unfollow: (payload: User) => void; // 自分ãŒä»–人をフォãƒãƒ¼è§£é™¤ã—ãŸã¨ã - meUpdated: (payload: MeDetailed) => void; + follow: (payload: UserDetailedNotMe) => void; // 自分ãŒä»–人をフォãƒãƒ¼ã—ãŸã¨ã + followed: (payload: UserDetailed | UserLite) => void; // 他人ãŒè‡ªåˆ†ã‚’フォãƒãƒ¼ã—ãŸã¨ã + unfollow: (payload: UserDetailed) => void; // 自分ãŒä»–人をフォãƒãƒ¼è§£é™¤ã—ãŸã¨ã + meUpdated: (payload: UserDetailed) => void; pageEvent: (payload: PageEvent) => void; urlUploadFinished: (payload: { marker: string; file: DriveFile; }) => void; readAllNotifications: () => void; unreadNotification: (payload: Notification) => void; unreadMention: (payload: Note['id']) => void; readAllUnreadMentions: () => void; + notificationFlushed: () => void; unreadSpecifiedNote: (payload: Note['id']) => void; readAllUnreadSpecifiedNotes: () => void; readAllAntennas: () => void; @@ -53,6 +57,7 @@ export type Channels = { readAntenna: (payload: Antenna) => void; receiveFollowRequest: (payload: User) => void; announcementCreated: (payload: AnnouncementCreated) => void; + edited: (payload: Note) => void; }; receives: null; }; @@ -60,6 +65,7 @@ export type Channels = { params: { withRenotes?: boolean; withFiles?: boolean; + withBots?: boolean; }; events: { note: (payload: Note) => void; @@ -71,6 +77,7 @@ export type Channels = { withRenotes?: boolean; withReplies?: boolean; withFiles?: boolean; + withBots?: boolean; }; events: { note: (payload: Note) => void; @@ -82,6 +89,7 @@ export type Channels = { withRenotes?: boolean; withReplies?: boolean; withFiles?: boolean; + withBots?: boolean; }; events: { note: (payload: Note) => void; @@ -92,6 +100,7 @@ export type Channels = { params: { withRenotes?: boolean; withFiles?: boolean; + withBots?: boolean; }; events: { note: (payload: Note) => void; @@ -99,7 +108,11 @@ export type Channels = { receives: null; }; bubbleTimeline: { - params: null; + params: { + withRenotes?: boolean; + withFiles?: boolean; + withBots?: boolean; + }; events: { note: (payload: Note) => void; }; @@ -109,6 +122,7 @@ export type Channels = { params: { listId: string; withFiles?: boolean; + withRenotes?: boolean; }; events: { note: (payload: Note) => void; @@ -159,7 +173,7 @@ export type Channels = { fileUpdated: (payload: DriveFile) => void; folderCreated: (payload: DriveFolder) => void; folderDeleted: (payload: DriveFolder['id']) => void; - folderUpdated: (payload: DriveFile) => void; + folderUpdated: (payload: DriveFolder) => void; }; receives: null; }; @@ -200,6 +214,32 @@ export type Channels = { } }; receives: null; + }; + reversiGame: { + params: { + gameId: string; + }; + events: { + started: (payload: { game: ReversiGameDetailed; }) => void; + ended: (payload: { winnerId: User['id'] | null; game: ReversiGameDetailed; }) => void; + canceled: (payload: { userId: User['id']; }) => void; + changeReadyStates: (payload: { user1: boolean; user2: boolean; }) => void; + updateSettings: (payload: { userId: User['id']; key: string; value: any; }) => void; + log: (payload: Record<string, any>) => void; + }; + receives: { + putStone: { + pos: number; + id: string; + }; + ready: boolean; + cancel: null | Record<string, never>; + updateSettings: { + key: string; + value: any; + }; + claimTimeIsUp: null | Record<string, never>; + } } }; diff --git a/packages/misskey-js/test-d/api.ts b/packages/misskey-js/test-d/api.ts index 1c2a142e8bf3c5b90554ee2df965b9244579d9cb..4b72ff4e9da51db010c8060567124f628dfb9c8c 100644 --- a/packages/misskey-js/test-d/api.ts +++ b/packages/misskey-js/test-d/api.ts @@ -1,5 +1,5 @@ import { expectType } from 'tsd'; -import * as Misskey from '../src'; +import * as Misskey from '../src/index.js'; describe('API', () => { test('success', async () => { diff --git a/packages/misskey-js/test-d/streaming.ts b/packages/misskey-js/test-d/streaming.ts index 6b186bd45af9bcab432e407d4755ff1db499f050..b46b06e4dfb78826c4035ea5b91ace273f071238 100644 --- a/packages/misskey-js/test-d/streaming.ts +++ b/packages/misskey-js/test-d/streaming.ts @@ -1,5 +1,5 @@ import { expectType } from 'tsd'; -import * as Misskey from '../src'; +import * as Misskey from '../src/index.js'; describe('Streaming', () => { test('emit type', async () => { diff --git a/packages/misskey-js/test/api.ts b/packages/misskey-js/test/api.ts index 6f9e656fefae4e9b75df570dcf5944af41ccac79..fa31d23faab752ada4bb37d2bc924cbf4d21bd24 100644 --- a/packages/misskey-js/test/api.ts +++ b/packages/misskey-js/test/api.ts @@ -1,5 +1,5 @@ import { enableFetchMocks } from 'jest-fetch-mock'; -import { APIClient, isAPIError } from '../src/api'; +import { APIClient, isAPIError } from '../src/api.js'; enableFetchMocks(); diff --git a/packages/misskey-js/test/streaming.ts b/packages/misskey-js/test/streaming.ts index 9f6615a8d89c59b9bd66a06c848bc3964d80c084..06b55cd8afbec60cb09aa53e0419c83baa93d297 100644 --- a/packages/misskey-js/test/streaming.ts +++ b/packages/misskey-js/test/streaming.ts @@ -1,5 +1,5 @@ import WS from 'jest-websocket-mock'; -import Stream from '../src/streaming'; +import Stream from '../src/streaming.js'; describe('Streaming', () => { test('useChannel', async () => { diff --git a/packages/misskey-reversi/.eslintignore b/packages/misskey-reversi/.eslintignore new file mode 100644 index 0000000000000000000000000000000000000000..f22128f047fd7d41ba245801483111d0e5862b76 --- /dev/null +++ b/packages/misskey-reversi/.eslintignore @@ -0,0 +1,7 @@ +node_modules +/built +/coverage +/.eslintrc.js +/jest.config.ts +/test +/test-d diff --git a/packages/misskey-reversi/.eslintrc.cjs b/packages/misskey-reversi/.eslintrc.cjs new file mode 100644 index 0000000000000000000000000000000000000000..db37a0109871fba27cae540702f545531e9d36e4 --- /dev/null +++ b/packages/misskey-reversi/.eslintrc.cjs @@ -0,0 +1,10 @@ +module.exports = { + root: true, + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + }, + extends: [ + '../shared/.eslintrc.js', + ], +}; diff --git a/packages/misskey-reversi/build.js b/packages/misskey-reversi/build.js new file mode 100644 index 0000000000000000000000000000000000000000..4744dfaf7b44de2f4a8b6bc814c35ec00491540a --- /dev/null +++ b/packages/misskey-reversi/build.js @@ -0,0 +1,31 @@ +import { build } from "esbuild"; +import { globSync } from "glob"; + +const entryPoints = globSync("./src/**/**.{ts,tsx}"); + +/** @type {import('esbuild').BuildOptions} */ +const options = { + entryPoints, + minify: true, + outdir: "./built/esm", + target: "es2022", + platform: "browser", + format: "esm", +}; + +if (process.env.WATCH === "true") { + options.watch = { + onRebuild(error, result) { + if (error) { + console.error("watch build failed:", error); + } else { + console.log("watch build succeeded:", result); + } + }, + }; +} + +build(options).catch((err) => { + process.stderr.write(err.stderr); + process.exit(1); +}); diff --git a/packages/misskey-reversi/package.json b/packages/misskey-reversi/package.json new file mode 100644 index 0000000000000000000000000000000000000000..7bfc890fef852949bf95e34f4211b902a2d5dcbd --- /dev/null +++ b/packages/misskey-reversi/package.json @@ -0,0 +1,44 @@ +{ + "type": "module", + "name": "misskey-reversi", + "version": "0.0.1", + "types": "./built/dts/index.d.ts", + "exports": { + ".": { + "import": "./built/esm/index.js", + "types": "./built/dts/index.d.ts" + }, + "./*": { + "import": "./built/esm/*", + "types": "./built/dts/*" + } + }, + "scripts": { + "build": "node ./build.js", + "build:tsc": "npm run tsc", + "tsc": "npm run tsc-esm && npm run tsc-dts", + "tsc-esm": "tsc --outDir built/esm", + "tsc-dts": "tsc --outDir built/dts --declaration true --emitDeclarationOnly true --declarationMap true", + "watch": "nodemon -w src -e ts,js,cjs,mjs,json --exec \"pnpm run build:tsc\"", + "eslint": "eslint . --ext .js,.jsx,.ts,.tsx", + "typecheck": "tsc --noEmit", + "lint": "pnpm typecheck && pnpm eslint" + }, + "devDependencies": { + "@misskey-dev/eslint-plugin": "1.0.0", + "@types/node": "20.11.5", + "@typescript-eslint/eslint-plugin": "7.1.0", + "@typescript-eslint/parser": "7.1.0", + "eslint": "8.57.0", + "nodemon": "3.0.2", + "typescript": "5.3.3" + }, + "files": [ + "built" + ], + "dependencies": { + "crc-32": "1.2.2", + "esbuild": "0.19.11", + "glob": "10.3.10" + } +} diff --git a/packages/misskey-reversi/src/game.ts b/packages/misskey-reversi/src/game.ts new file mode 100644 index 0000000000000000000000000000000000000000..4afca9898cf092901017afb9f53ec279d3fe0001 --- /dev/null +++ b/packages/misskey-reversi/src/game.ts @@ -0,0 +1,226 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import CRC32 from 'crc-32'; + +/** + * true ... é»’ + * false ... 白 + */ +export type Color = boolean; +const BLACK = true; +const WHITE = false; + +export type MapCell = 'null' | 'empty'; + +export type Options = { + isLlotheo: boolean; + canPutEverywhere: boolean; + loopedBoard: boolean; +}; + +export type Undo = { + color: Color; + pos: number; + + /** + * å転ã—ãŸçŸ³ã®ä½ç½®ã®é…列 + */ + effects: number[]; + + turn: Color | null; +}; + +export class Game { + public map: MapCell[]; + public mapWidth: number; + public mapHeight: number; + public board: (Color | null | undefined)[]; + public turn: Color | null = BLACK; + public opts: Options; + + public prevPos = -1; + public prevColor: Color | null = null; + + private logs: Undo[] = []; + + constructor(map: string[], opts: Options) { + //#region binds + this.putStone = this.putStone.bind(this); + //#endregion + + //#region Options + this.opts = opts; + if (this.opts.isLlotheo == null) this.opts.isLlotheo = false; + if (this.opts.canPutEverywhere == null) this.opts.canPutEverywhere = false; + if (this.opts.loopedBoard == null) this.opts.loopedBoard = false; + //#endregion + + //#region Parse map data + this.mapWidth = map[0].length; + this.mapHeight = map.length; + const mapData = map.join(''); + + this.board = mapData.split('').map(d => d === '-' ? null : d === 'b' ? BLACK : d === 'w' ? WHITE : undefined); + + this.map = mapData.split('').map(d => d === '-' || d === 'b' || d === 'w' ? 'empty' : 'null'); + //#endregion + + // ゲームãŒå§‹ã¾ã£ãŸæ™‚点ã§ç‰‡æ–¹ã®è‰²ã®çŸ³ã—ã‹ãªã„ã‹ã€å§‹ã¾ã£ãŸæ™‚点ã§å‹æ•—ãŒæ±ºå®šã™ã‚‹ã‚ˆã†ãªãƒžãƒƒãƒ—ã®å ´åˆãŒã‚ã‚‹ + if (!this.canPutSomewhere(BLACK)) this.turn = this.canPutSomewhere(WHITE) ? WHITE : null; + } + + public get blackCount() { + return this.board.filter(x => x === BLACK).length; + } + + public get whiteCount() { + return this.board.filter(x => x === WHITE).length; + } + + public posToXy(pos: number): number[] { + const x = pos % this.mapWidth; + const y = Math.floor(pos / this.mapWidth); + return [x, y]; + } + + public xyToPos(x: number, y: number): number { + return x + (y * this.mapWidth); + } + + public putStone(pos: number) { + const color = this.turn; + if (color == null) return; + + this.prevPos = pos; + this.prevColor = color; + + this.board[pos] = color; + + // å転ã•ã›ã‚‰ã‚Œã‚‹çŸ³ã‚’å–å¾— + const effects = this.effects(color, pos); + + // å転ã•ã›ã‚‹ + for (const pos of effects) { + this.board[pos] = color; + } + + const turn = this.turn; + + this.logs.push({ + color, + pos, + effects, + turn, + }); + + this.calcTurn(); + } + + private calcTurn() { + // ターン計算 + this.turn = + this.canPutSomewhere(!this.prevColor) ? !this.prevColor : + this.canPutSomewhere(this.prevColor!) ? this.prevColor : + null; + } + + public undo() { + const undo = this.logs.pop()!; + this.prevColor = undo.color; + this.prevPos = undo.pos; + this.board[undo.pos] = null; + for (const pos of undo.effects) { + const color = this.board[pos]; + this.board[pos] = !color; + } + this.turn = undo.turn; + } + + public mapDataGet(pos: number): MapCell { + const [x, y] = this.posToXy(pos); + return x < 0 || y < 0 || x >= this.mapWidth || y >= this.mapHeight ? 'null' : this.map[pos]; + } + + public getPuttablePlaces(color: Color): number[] { + return Array.from(this.board.keys()).filter(i => this.canPut(color, i)); + } + + public canPutSomewhere(color: Color): boolean { + return this.getPuttablePlaces(color).length > 0; + } + + public canPut(color: Color, pos: number): boolean { + return ( + this.board[pos] !== null ? false : // æ—¢ã«çŸ³ãŒç½®ã„ã¦ã‚ã‚‹å ´æ‰€ã«ã¯æ‰“ã¦ãªã„ + this.opts.canPutEverywhere ? this.mapDataGet(pos) === 'empty' : // 挟んã§ãªãã¦ã‚‚ç½®ã‘るモード + this.effects(color, pos).length !== 0); // 相手ã®çŸ³ã‚’1ã¤ã§ã‚‚å転ã•ã›ã‚‰ã‚Œã‚‹ã‹ + } + + /** + * 指定ã®ãƒžã‚¹ã«çŸ³ã‚’ç½®ã„ãŸæ™‚ã®ã€å転ã•ã›ã‚‰ã‚Œã‚‹çŸ³ã‚’å–å¾—ã—ã¾ã™ + * @param color 自分ã®è‰² + * @param initPos ä½ç½® + */ + public effects(color: Color, initPos: number): number[] { + const enemyColor = !color; + + const diffVectors: [number, number][] = [ + [0, -1], // 上 + [+1, -1], // å³ä¸Š + [+1, 0], // å³ + [+1, +1], // å³ä¸‹ + [0, +1], // 下 + [-1, +1], // 左下 + [-1, 0], // å·¦ + [-1, -1], // 左上 + ]; + + const effectsInLine = ([dx, dy]: [number, number]): number[] => { + const nextPos = (x: number, y: number): [number, number] => [x + dx, y + dy]; + + const found: number[] = []; // 挟ã‚ã‚‹ã‹ã‚‚ã—ã‚Œãªã„相手ã®çŸ³ã‚’入れã¦ãŠãé…列 + let [x, y] = this.posToXy(initPos); + while (true) { + [x, y] = nextPos(x, y); + + // 座標ãŒæŒ‡ã—示ã™ä½ç½®ãŒãƒœãƒ¼ãƒ‰å¤–ã«å‡ºãŸã¨ã + if (this.opts.loopedBoard && this.xyToPos( + (x = ((x % this.mapWidth) + this.mapWidth) % this.mapWidth), + (y = ((y % this.mapHeight) + this.mapHeight) % this.mapHeight)) === initPos) { + // 盤é¢ã®å¢ƒç•Œã§ãƒ«ãƒ¼ãƒ—ã—ã€è‡ªåˆ†ãŒçŸ³ã‚’ç½®ãä½ç½®ã«æˆ»ã£ã¦ããŸã¨ãã€æŒŸã‚るよã†ã«ã—ã¦ã„ã‚‹ (ref: Test4ã®ãƒžãƒƒãƒ—) + return found; + } else if (x === -1 || y === -1 || x === this.mapWidth || y === this.mapHeight) return []; // 挟ã‚ãªã„ã“ã¨ãŒç¢ºå®š (盤é¢å¤–ã«åˆ°é”) + + const pos = this.xyToPos(x, y); + if (this.mapDataGet(pos) === 'null') return []; // 挟ã‚ãªã„ã“ã¨ãŒç¢ºå®š (é…ç½®ä¸å¯èƒ½ãªãƒžã‚¹ã«åˆ°é”) + const stone = this.board[pos]; + if (stone === null) return []; // 挟ã‚ãªã„ã“ã¨ãŒç¢ºå®š (石ãŒç½®ã‹ã‚Œã¦ã„ãªã„マスã«åˆ°é”) + if (stone === enemyColor) found.push(pos); // 挟ã‚ã‚‹ã‹ã‚‚ã—ã‚Œãªã„ (相手ã®çŸ³ã‚’発見) + if (stone === color) return found; // 挟ã‚ã‚‹ã“ã¨ãŒç¢ºå®š (対ã¨ãªã‚‹è‡ªåˆ†ã®çŸ³ã‚’発見) + } + }; + + return ([] as number[]).concat(...diffVectors.map(effectsInLine)); + } + + public calcCrc32(): number { + return CRC32.str(JSON.stringify({ + board: this.board, + turn: this.turn, + })); + } + + public get isEnded(): boolean { + return this.turn === null; + } + + public get winner(): Color | null { + return this.isEnded ? + this.blackCount === this.whiteCount ? null : + this.opts.isLlotheo === this.blackCount > this.whiteCount ? WHITE : BLACK : + undefined as never; + } +} diff --git a/packages/misskey-reversi/src/index.ts b/packages/misskey-reversi/src/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..cfd27d48cb5f60951f8351be57908ea347529d35 --- /dev/null +++ b/packages/misskey-reversi/src/index.ts @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export { Game } from './game.js'; +export * as Serializer from './serializer.js'; +export * as maps from './maps.js'; diff --git a/packages/misskey-reversi/src/maps.ts b/packages/misskey-reversi/src/maps.ts new file mode 100644 index 0000000000000000000000000000000000000000..29ea3591c2641739a6e0bc977c4111afa767042f --- /dev/null +++ b/packages/misskey-reversi/src/maps.ts @@ -0,0 +1,697 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +/** + * 組ã¿è¾¼ã¿ãƒžãƒƒãƒ—定義 + * + * データ値: + * (スペース) ... マス無㗠+ * - ... マス + * b ... åˆæœŸé…ç½®ã•ã‚Œã‚‹é»’石 + * w ... åˆæœŸé…ç½®ã•ã‚Œã‚‹ç™½çŸ³ + */ + +export type Map = { + name?: string; + category?: string; + author?: string; + data: string[]; +}; + +export const fourfour: Map = { + name: '4x4', + category: '4x4', + data: [ + '----', + '-wb-', + '-bw-', + '----', + ], +}; + +export const sixsix: Map = { + name: '6x6', + category: '6x6', + data: [ + '------', + '------', + '--wb--', + '--bw--', + '------', + '------', + ], +}; + +export const roundedSixsix: Map = { + name: '6x6 rounded', + category: '6x6', + author: 'syuilo', + data: [ + ' ---- ', + '------', + '--wb--', + '--bw--', + '------', + ' ---- ', + ], +}; + +export const roundedSixsix2: Map = { + name: '6x6 rounded 2', + category: '6x6', + author: 'syuilo', + data: [ + ' -- ', + ' ---- ', + '--wb--', + '--bw--', + ' ---- ', + ' -- ', + ], +}; + +export const eighteight: Map = { + name: '8x8', + category: '8x8', + data: [ + '--------', + '--------', + '--------', + '---wb---', + '---bw---', + '--------', + '--------', + '--------', + ], +}; + +export const eighteightH28: Map = { + name: '8x8 handicap 28', + category: '8x8', + data: [ + 'bbbbbbbb', + 'b------b', + 'b------b', + 'b--wb--b', + 'b--bw--b', + 'b------b', + 'b------b', + 'bbbbbbbb', + ], +}; + +export const roundedEighteight: Map = { + name: '8x8 rounded', + category: '8x8', + author: 'syuilo', + data: [ + ' ------ ', + '--------', + '--------', + '---wb---', + '---bw---', + '--------', + '--------', + ' ------ ', + ], +}; + +export const roundedEighteight2: Map = { + name: '8x8 rounded 2', + category: '8x8', + author: 'syuilo', + data: [ + ' ---- ', + ' ------ ', + '--------', + '---wb---', + '---bw---', + '--------', + ' ------ ', + ' ---- ', + ], +}; + +export const roundedEighteight3: Map = { + name: '8x8 rounded 3', + category: '8x8', + author: 'syuilo', + data: [ + ' -- ', + ' ---- ', + ' ------ ', + '---wb---', + '---bw---', + ' ------ ', + ' ---- ', + ' -- ', + ], +}; + +export const eighteightWithNotch: Map = { + name: '8x8 with notch', + category: '8x8', + author: 'syuilo', + data: [ + '--- ---', + '--------', + '--------', + ' --wb-- ', + ' --bw-- ', + '--------', + '--------', + '--- ---', + ], +}; + +export const eighteightWithSomeHoles: Map = { + name: '8x8 with some holes', + category: '8x8', + author: 'syuilo', + data: [ + '--- ----', + '----- --', + '-- -----', + '---wb---', + '---bw- -', + ' -------', + '--- ----', + '--------', + ], +}; + +export const circle: Map = { + name: 'Circle', + category: '8x8', + author: 'syuilo', + data: [ + ' -- ', + ' ------ ', + ' ------ ', + '---wb---', + '---bw---', + ' ------ ', + ' ------ ', + ' -- ', + ], +}; + +export const smile: Map = { + name: 'Smile', + category: '8x8', + author: 'syuilo', + data: [ + ' ------ ', + '--------', + '-- -- --', + '---wb---', + '-- bw --', + '--- ---', + '--------', + ' ------ ', + ], +}; + +export const window: Map = { + name: 'Window', + category: '8x8', + author: 'syuilo', + data: [ + '--------', + '- -- -', + '- -- -', + '---wb---', + '---bw---', + '- -- -', + '- -- -', + '--------', + ], +}; + +export const reserved: Map = { + name: 'Reserved', + category: '8x8', + author: 'Aya', + data: [ + 'w------b', + '--------', + '--------', + '---wb---', + '---bw---', + '--------', + '--------', + 'b------w', + ], +}; + +export const x: Map = { + name: 'X', + category: '8x8', + author: 'Aya', + data: [ + 'w------b', + '-w----b-', + '--w--b--', + '---wb---', + '---bw---', + '--b--w--', + '-b----w-', + 'b------w', + ], +}; + +export const parallel: Map = { + name: 'Parallel', + category: '8x8', + author: 'Aya', + data: [ + '--------', + '--------', + '--------', + '---bb---', + '---ww---', + '--------', + '--------', + '--------', + ], +}; + +export const lackOfBlack: Map = { + name: 'Lack of Black', + category: '8x8', + data: [ + '--------', + '--------', + '--------', + '---w----', + '---bw---', + '--------', + '--------', + '--------', + ], +}; + +export const squareParty: Map = { + name: 'Square Party', + category: '8x8', + author: 'syuilo', + data: [ + '--------', + '-wwwbbb-', + '-w-wb-b-', + '-wwwbbb-', + '-bbbwww-', + '-b-bw-w-', + '-bbbwww-', + '--------', + ], +}; + +export const minesweeper: Map = { + name: 'Minesweeper', + category: '8x8', + author: 'syuilo', + data: [ + 'b-b--w-w', + '-w-wb-b-', + 'w-b--w-b', + '-b-wb-w-', + '-w-bw-b-', + 'b-w--b-w', + '-b-bw-w-', + 'w-w--b-b', + ], +}; + +export const tenthtenth: Map = { + name: '10x10', + category: '10x10', + data: [ + '----------', + '----------', + '----------', + '----------', + '----wb----', + '----bw----', + '----------', + '----------', + '----------', + '----------', + ], +}; + +export const hole: Map = { + name: 'The Hole', + category: '10x10', + author: 'syuilo', + data: [ + '----------', + '----------', + '--wb--wb--', + '--bw--bw--', + '---- ----', + '---- ----', + '--wb--wb--', + '--bw--bw--', + '----------', + '----------', + ], +}; + +export const grid: Map = { + name: 'Grid', + category: '10x10', + author: 'syuilo', + data: [ + '----------', + '- - -- - -', + '----------', + '- - -- - -', + '----wb----', + '----bw----', + '- - -- - -', + '----------', + '- - -- - -', + '----------', + ], +}; + +export const cross: Map = { + name: 'Cross', + category: '10x10', + author: 'Aya', + data: [ + ' ---- ', + ' ---- ', + ' ---- ', + '----------', + '----wb----', + '----bw----', + '----------', + ' ---- ', + ' ---- ', + ' ---- ', + ], +}; + +export const charX: Map = { + name: 'Char X', + category: '10x10', + author: 'syuilo', + data: [ + '--- ---', + '---- ----', + '----------', + ' -------- ', + ' --wb-- ', + ' --bw-- ', + ' -------- ', + '----------', + '---- ----', + '--- ---', + ], +}; + +export const charY: Map = { + name: 'Char Y', + category: '10x10', + author: 'syuilo', + data: [ + '--- ---', + '---- ----', + '----------', + ' -------- ', + ' --wb-- ', + ' --bw-- ', + ' ------ ', + ' ------ ', + ' ------ ', + ' ------ ', + ], +}; + +export const walls: Map = { + name: 'Walls', + category: '10x10', + author: 'Aya', + data: [ + ' bbbbbbbb ', + 'w--------w', + 'w--------w', + 'w--------w', + 'w---wb---w', + 'w---bw---w', + 'w--------w', + 'w--------w', + 'w--------w', + ' bbbbbbbb ', + ], +}; + +export const cpu: Map = { + name: 'CPU', + category: '10x10', + author: 'syuilo', + data: [ + ' b b b b ', + 'w--------w', + ' -------- ', + 'w--------w', + ' ---wb--- ', + ' ---bw--- ', + 'w--------w', + ' -------- ', + 'w--------w', + ' b b b b ', + ], +}; + +export const checker: Map = { + name: 'Checker', + category: '10x10', + author: 'Aya', + data: [ + '----------', + '----------', + '----------', + '---wbwb---', + '---bwbw---', + '---wbwb---', + '---bwbw---', + '----------', + '----------', + '----------', + ], +}; + +export const japaneseCurry: Map = { + name: 'Japanese curry', + category: '10x10', + author: 'syuilo', + data: [ + 'w-b-b-b-b-', + '-w-b-b-b-b', + 'w-w-b-b-b-', + '-w-w-b-b-b', + 'w-w-wwb-b-', + '-w-wbb-b-b', + 'w-w-w-b-b-', + '-w-w-w-b-b', + 'w-w-w-w-b-', + '-w-w-w-w-b', + ], +}; + +export const mosaic: Map = { + name: 'Mosaic', + category: '10x10', + author: 'syuilo', + data: [ + '- - - - - ', + ' - - - - -', + '- - - - - ', + ' - w w - -', + '- - b b - ', + ' - w w - -', + '- - b b - ', + ' - - - - -', + '- - - - - ', + ' - - - - -', + ], +}; + +export const arena: Map = { + name: 'Arena', + category: '10x10', + author: 'syuilo', + data: [ + '- - -- - -', + ' - - - - ', + '- ------ -', + ' -------- ', + '- --wb-- -', + '- --bw-- -', + ' -------- ', + '- ------ -', + ' - - - - ', + '- - -- - -', + ], +}; + +export const reactor: Map = { + name: 'Reactor', + category: '10x10', + author: 'syuilo', + data: [ + '-w------b-', + 'b- - - -w', + '- --wb-- -', + '---b w---', + '- b wb w -', + '- w bw b -', + '---w b---', + '- --bw-- -', + 'w- - - -b', + '-b------w-', + ], +}; + +export const sixeight: Map = { + name: '6x8', + category: 'Special', + data: [ + '------', + '------', + '------', + '--wb--', + '--bw--', + '------', + '------', + '------', + ], +}; + +export const spark: Map = { + name: 'Spark', + category: 'Special', + author: 'syuilo', + data: [ + ' - - ', + '----------', + ' -------- ', + ' -------- ', + ' ---wb--- ', + ' ---bw--- ', + ' -------- ', + ' -------- ', + '----------', + ' - - ', + ], +}; + +export const islands: Map = { + name: 'Islands', + category: 'Special', + author: 'syuilo', + data: [ + '-------- ', + '---wb--- ', + '---bw--- ', + '-------- ', + ' - - ', + ' - - ', + ' --------', + ' --------', + ' --------', + ' --------', + ], +}; + +export const galaxy: Map = { + name: 'Galaxy', + category: 'Special', + author: 'syuilo', + data: [ + ' ------ ', + ' --www--- ', + ' ------w--- ', + '---bbb--w---', + '--b---b-w-b-', + '-b--wwb-w-b-', + '-b-w-bww--b-', + '-b-w-b---b--', + '---w--bbb---', + ' ---w------ ', + ' ---www-- ', + ' ------ ', + ], +}; + +export const triangle: Map = { + name: 'Triangle', + category: 'Special', + author: 'syuilo', + data: [ + ' -- ', + ' -- ', + ' ---- ', + ' ---- ', + ' --wb-- ', + ' --bw-- ', + ' -------- ', + ' -------- ', + '----------', + '----------', + ], +}; + +export const iphonex: Map = { + name: 'iPhone X', + category: 'Special', + author: 'syuilo', + data: [ + ' -- -- ', + '--------', + '--------', + '--------', + '--------', + '---wb---', + '---bw---', + '--------', + '--------', + '--------', + '--------', + ' ------ ', + ], +}; + +export const dealWithIt: Map = { + name: 'Deal with it!', + category: 'Special', + author: 'syuilo', + data: [ + '------------', + '--w-b-------', + ' --b-w------', + ' --w-b---- ', + ' ------- ', + ], +}; + +export const twoBoard: Map = { + name: 'Two board', + category: 'Special', + author: 'Aya', + data: [ + '-------- --------', + '-------- --------', + '-------- --------', + '---wb--- ---wb---', + '---bw--- ---bw---', + '-------- --------', + '-------- --------', + '-------- --------', + ], +}; diff --git a/packages/misskey-reversi/src/serializer.ts b/packages/misskey-reversi/src/serializer.ts new file mode 100644 index 0000000000000000000000000000000000000000..aa5987a1e5e128c03402cc14658cdf465b3e7074 --- /dev/null +++ b/packages/misskey-reversi/src/serializer.ts @@ -0,0 +1,98 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Game } from './game.js'; + +export type Log = { + time: number; + player: boolean; + operation: 'put'; + pos: number; +}; + +export type SerializedLog = number[]; + +export function serializeLogs(logs: Log[]) { + const _logs: number[][] = []; + + for (let i = 0; i < logs.length; i++) { + const log = logs[i]; + const timeDelta = i === 0 ? log.time : log.time - logs[i - 1].time; + + switch (log.operation) { + case 'put': + _logs.push([timeDelta, log.player ? 1 : 0, 0, log.pos]); + break; + //case 'surrender': + // _logs.push([timeDelta, log.player, 1]); + // break; + } + } + + return _logs; +} + +export function deserializeLogs(logs: SerializedLog[]) { + const _logs: Log[] = []; + + let time = 0; + + for (const log of logs) { + const timeDelta = log[0]; + time += timeDelta; + + const player = log[1]; + const operation = log[2]; + + switch (operation) { + case 0: + _logs.push({ + time, + player: player === 1, + operation: 'put', + pos: log[3], + }); + break; + //case 1: + // _logs.push({ + // time, + // player: player === 1, + // operation: 'surrender', + // }); + // break; + } + } + + return _logs; +} + +export function restoreGame(env: { + map: string[]; + isLlotheo: boolean; + canPutEverywhere: boolean; + loopedBoard: boolean; + logs: SerializedLog[]; +}) { + const logs = deserializeLogs(env.logs); + + const game = new Game(env.map, { + isLlotheo: env.isLlotheo, + canPutEverywhere: env.canPutEverywhere, + loopedBoard: env.loopedBoard, + }); + + for (const log of logs) { + switch (log.operation) { + case 'put': + game.putStone(log.pos); + break; + //case 'surrender': + // game.surrender(log.player); + // break; + } + } + + return game; +} diff --git a/packages/misskey-reversi/tsconfig.json b/packages/misskey-reversi/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..f56b65e86802158634e10482fe7a47eee39cf5cf --- /dev/null +++ b/packages/misskey-reversi/tsconfig.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "target": "ES2022", + "module": "nodenext", + "moduleResolution": "nodenext", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./built/", + "removeComments": true, + "strict": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "experimentalDecorators": true, + "noImplicitReturns": true, + "esModuleInterop": true, + "typeRoots": [ + "./node_modules/@types" + ], + "lib": [ + "esnext", + "dom" + ] + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "test/**/*" + ] +} diff --git a/packages/shared/.eslintrc.js b/packages/shared/.eslintrc.js index b3c7626a39b24b0e02faa7b5e4bb29ba8cae663a..58247877ae275e7b1c81cef33998e7856a5548be 100644 --- a/packages/shared/.eslintrc.js +++ b/packages/shared/.eslintrc.js @@ -1,118 +1,7 @@ module.exports = { root: true, - parser: '@typescript-eslint/parser', - plugins: [ - '@typescript-eslint', - 'import' - ], + ignorePatterns: ['**/.eslintrc.cjs'], extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:import/recommended', - 'plugin:import/typescript' + 'plugin:@misskey-dev/recommended', ], - rules: { - 'indent': ['warn', 'tab', { - 'SwitchCase': 1, - 'MemberExpression': 1, - 'flatTernaryExpressions': true, - 'ArrayExpression': 'first', - 'ObjectExpression': 'first', - }], - 'eol-last': ['error', 'always'], - 'semi': ['error', 'always'], - 'semi-spacing': ['error', { 'before': false, 'after': true }], - 'quotes': ['warn', 'single'], - 'comma-dangle': ['warn', 'always-multiline'], - 'comma-spacing': ['error', { 'before': false, 'after': true }], - 'array-bracket-spacing': ['error', 'never'], - 'keyword-spacing': ['error', { - 'before': true, - 'after': true, - }], - 'key-spacing': ['error', { - 'beforeColon': false, - 'afterColon': true, - }], - 'arrow-spacing': ['error', { - 'before': true, - 'after': true, - }], - 'brace-style': ['error', '1tbs', { - 'allowSingleLine': true, - }], - 'padded-blocks': ['error', 'never'], - /* TODO: path aliasを使ã‚ãªã„ã¨warnã™ã‚‹ - 'no-restricted-imports': ['warn', { - 'patterns': [ - ] - }], - */ - 'eqeqeq': ['error', 'always', { 'null': 'ignore' }], - 'no-multi-spaces': ['error'], - 'no-var': ['error'], - 'prefer-arrow-callback': ['error'], - 'no-throw-literal': ['error'], - 'no-param-reassign': ['warn'], - 'no-constant-condition': ['warn'], - 'no-empty-pattern': ['warn'], - 'no-async-promise-executor': ['off'], - 'no-useless-escape': ['off'], - 'no-multiple-empty-lines': ['error', { 'max': 1 }], - 'no-control-regex': ['warn'], - 'no-empty': ['warn'], - 'no-inner-declarations': ['off'], - 'no-sparse-arrays': ['off'], - 'nonblock-statement-body-position': ['error', 'beside'], - 'object-curly-spacing': ['error', 'always'], - 'space-infix-ops': ['error'], - 'space-before-blocks': ['error', 'always'], - 'padding-line-between-statements': [ - 'error', - { 'blankLine': 'always', 'prev': 'function', 'next': '*' }, - { 'blankLine': 'always', 'prev': '*', 'next': 'function' }, - ], - "lines-between-class-members": "off", - /* typescript-eslint ã§ã¯ enforce ã«å¯¾å¿œã—ã¦ãªã„ã£ã½ã„ - '@typescript-eslint/lines-between-class-members': ['error', { - enforce: [{ - blankLine: 'always', - prev: 'method', - next: '*', - }] - }], - */ - '@typescript-eslint/func-call-spacing': ['error', 'never'], - '@typescript-eslint/no-explicit-any': ['warn'], - '@typescript-eslint/no-unused-vars': ['warn'], - '@typescript-eslint/no-unnecessary-condition': ['warn'], - '@typescript-eslint/no-var-requires': ['warn'], - '@typescript-eslint/no-inferrable-types': ['warn'], - '@typescript-eslint/no-empty-function': ['off'], - '@typescript-eslint/no-non-null-assertion': ['warn'], - '@typescript-eslint/explicit-function-return-type': ['off'], - '@typescript-eslint/no-misused-promises': ['error', { - 'checksVoidReturn': false, - }], - '@typescript-eslint/consistent-type-imports': 'off', - '@typescript-eslint/prefer-nullish-coalescing': [ - 'warn', - ], - '@typescript-eslint/naming-convention': [ - 'error', - { - "selector": "typeLike", - "format": ["PascalCase"] - }, - { - "selector": "typeParameter", - "format": [] - } - ], - 'import/no-unresolved': ['off'], - 'import/no-default-export': ['warn'], - 'import/order': ['warn', { - 'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'], - }] - }, }; diff --git a/packages/sw/build.js b/packages/sw/build.js index ee190079b96b712f133645ce8b45b0ee066e89db..eb9a944f4765e809f5f03b9965f287ce3c5b57b3 100644 --- a/packages/sw/build.js +++ b/packages/sw/build.js @@ -1,7 +1,7 @@ // @ts-check /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/sw/package.json b/packages/sw/package.json index c48efd6ea6dedbf20d77302a241a87a484e7d3c2..bac0cc1ff50173e681e88ed25328d77414783452 100644 --- a/packages/sw/package.json +++ b/packages/sw/package.json @@ -9,16 +9,17 @@ "lint": "pnpm typecheck && pnpm eslint" }, "dependencies": { - "esbuild": "0.19.9", + "esbuild": "0.19.11", "idb-keyval": "6.2.1", "misskey-js": "workspace:*" }, "devDependencies": { - "@typescript-eslint/parser": "6.14.0", + "@misskey-dev/eslint-plugin": "1.0.0", + "@typescript-eslint/parser": "7.1.0", "@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67", - "eslint": "8.56.0", + "eslint": "8.57.0", "eslint-plugin-import": "2.29.1", - "nodemon": "3.0.2", + "nodemon": "3.1.0", "typescript": "5.3.3" }, "type": "module" diff --git a/packages/sw/src/@types/global.d.ts b/packages/sw/src/@types/global.d.ts index 80d7b02fe5ff6ac54747c8a8910de64a4e55ef0c..bf63810e6d6e3847f8b7ad3e5d7402aca8c1b66f 100644 --- a/packages/sw/src/@types/global.d.ts +++ b/packages/sw/src/@types/global.d.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts index 2e1ab719ac2c013f7aadd65470304124f8f19ada..32b12f4b4fa926e984615003c7303d593f6849a8 100644 --- a/packages/sw/src/scripts/create-notification.ts +++ b/packages/sw/src/scripts/create-notification.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -231,7 +231,15 @@ async function composeNotification(data: PushNotificationDataMap[keyof PushNotif badge: iconUrl('bell'), data, }]; - + + case 'edited': + return [t('_notification.edited', { name: getUserName(data.body.user) }), { + body: data.body.note.text ?? '', + icon: data.body.user.avatarUrl, + badge: iconUrl('messages'), + data, + }]; + default: return null; } diff --git a/packages/sw/src/scripts/get-account-from-id.ts b/packages/sw/src/scripts/get-account-from-id.ts index bbd306374e09c65a8355034f76bd0653c93c850e..19bfe052eea2b0456a1e1f7d48226e2878a34e5b 100644 --- a/packages/sw/src/scripts/get-account-from-id.ts +++ b/packages/sw/src/scripts/get-account-from-id.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/sw/src/scripts/get-user-name.ts b/packages/sw/src/scripts/get-user-name.ts index 2acdb91159f6d25c28e92ed4bbf99005e21f6ca7..6472a6c4e643aea7034627c3b59c5fea3e4c852b 100644 --- a/packages/sw/src/scripts/get-user-name.ts +++ b/packages/sw/src/scripts/get-user-name.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/sw/src/scripts/i18n.ts b/packages/sw/src/scripts/i18n.ts index 2c7feccc4449f8c62cd201d9a7a66ff7f1f86122..77b955dbe837ef3c20ff332def463713702a115d 100644 --- a/packages/sw/src/scripts/i18n.ts +++ b/packages/sw/src/scripts/i18n.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/sw/src/scripts/lang.ts b/packages/sw/src/scripts/lang.ts index a2b99ff6b1331ad81b8833c6d9b17de21638bd75..6fccedd7466ce37e1a3e4e09558c39d60c0a6a0c 100644 --- a/packages/sw/src/scripts/lang.ts +++ b/packages/sw/src/scripts/lang.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/sw/src/scripts/login-id.ts b/packages/sw/src/scripts/login-id.ts index 7002485925db2c3c2ebbfcc89f1f58e92a1b6e9c..084b52d1e436a7ca079df57c4f0b13f76e2a49e3 100644 --- a/packages/sw/src/scripts/login-id.ts +++ b/packages/sw/src/scripts/login-id.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/sw/src/scripts/operations.ts b/packages/sw/src/scripts/operations.ts index 0cbf4c7953dacb4d639bb79197bf1fb209223c85..24eea0623146ea5136a554b86a7476e725198f78 100644 --- a/packages/sw/src/scripts/operations.ts +++ b/packages/sw/src/scripts/operations.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/sw/src/scripts/twemoji-base.ts b/packages/sw/src/scripts/twemoji-base.ts index 32c5172131c044a70a3ef79800ebf3e7cf26dac6..e5b0603660bd9b71b4c2289e743817c398777fb5 100644 --- a/packages/sw/src/scripts/twemoji-base.ts +++ b/packages/sw/src/scripts/twemoji-base.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts index b79fd8ce7a14605c6437c6bfa8c1c367ea964e31..c38419eadc65c4c75cdc4740700f29738603fd79 100644 --- a/packages/sw/src/sw.ts +++ b/packages/sw/src/sw.ts @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { get } from 'idb-keyval'; import * as Misskey from 'misskey-js'; import type { PushNotificationDataMap } from '@/types.js'; +import type { I18n, Locale } from '@/scripts/i18n.js'; import { createEmptyNotification, createNotification } from '@/scripts/create-notification.js'; import { swLang } from '@/scripts/lang.js'; import * as swos from '@/scripts/operations.js'; @@ -26,8 +27,15 @@ globalThis.addEventListener('activate', ev => { ); }); -function offlineContentHTML(): string { - return `<!doctype html>Offline. Service Worker @${_VERSION_} <button onclick="location.reload()">reload</button>`; +async function offlineContentHTML() { + const i18n = await (swLang.i18n ?? swLang.fetchLocale()) as Partial<I18n<Locale>>; + const messages = { + title: i18n.ts?._offlineScreen?.title ?? 'Offline - Could not connect to server', + header: i18n.ts?._offlineScreen?.header ?? 'Could not connect to server', + reload: i18n.ts?.reload ?? 'Reload', + }; + + return `<!DOCTYPE html><html lang="ja"><head><meta charset="UTF-8"><meta content="width=device-width,initial-scale=1"name="viewport"><title>${messages.title}</title><style>body{background-color:#0c1210;color:#dee7e4;font-family:Hiragino Maru Gothic Pro,BIZ UDGothic,Roboto,HelveticaNeue,Arial,sans-serif;line-height:1.35;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;margin:0;padding:24px;box-sizing:border-box}.icon{max-width:120px;width:100%;height:auto;margin-bottom:20px;}.message{text-align:center;font-size:20px;font-weight:700;margin-bottom:20px}.version{text-align:center;font-size:90%;margin-bottom:20px}button{padding:7px 14px;min-width:100px;font-weight:700;font-family:Hiragino Maru Gothic Pro,BIZ UDGothic,Roboto,HelveticaNeue,Arial,sans-serif;line-height:1.35;border-radius:99rem;background-color:#b4e900;color:#192320;border:none;cursor:pointer;-webkit-tap-highlight-color:transparent}button:hover{background-color:#c6ff03}</style></head><body><svg class="icon"fill="none"height="24"stroke="currentColor"stroke-linecap="round"stroke-linejoin="round"stroke-width="2"viewBox="0 0 24 24"width="24"xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z"fill="none"stroke="none"/><path d="M9.58 5.548c.24 -.11 .492 -.207 .752 -.286c1.88 -.572 3.956 -.193 5.444 1c1.488 1.19 2.162 3.007 1.77 4.769h.99c1.913 0 3.464 1.56 3.464 3.486c0 .957 -.383 1.824 -1.003 2.454m-2.997 1.033h-11.343c-2.572 -.004 -4.657 -2.011 -4.657 -4.487c0 -2.475 2.085 -4.482 4.657 -4.482c.13 -.582 .37 -1.128 .7 -1.62"/><path d="M3 3l18 18"/></svg><div class="message">${messages.header}</div><div class="version">v${_VERSION_}</div><button onclick="reloadPage()">${messages.reload}</button><script>function reloadPage(){location.reload(!0)}</script></body></html>`; } globalThis.addEventListener('fetch', ev => { @@ -43,8 +51,9 @@ globalThis.addEventListener('fetch', ev => { if (!isHTMLRequest) return; ev.respondWith( fetch(ev.request) - .catch(() => { - return new Response(offlineContentHTML(), { + .catch(async () => { + const html = await offlineContentHTML(); + return new Response(html, { status: 200, headers: { 'content-type': 'text/html', @@ -124,6 +133,9 @@ globalThis.addEventListener('notificationclick', (ev: ServiceWorkerGlobalScopeEv case 'showFollowRequests': client = await swos.openClient('push', '/my/follow-requests', loginId); break; + case 'edited': + if ('note' in data.body) client = await swos.openPost({ reply: data.body.note }, loginId); + break; default: switch (data.body.type) { case 'receiveFollowRequest': diff --git a/packages/sw/src/types.ts b/packages/sw/src/types.ts index c63e489c71d332dd574a58ebedaa82f23f9943a9..fac3e707d811067cf9615888d8fea768f39a67f4 100644 --- a/packages/sw/src/types.ts +++ b/packages/sw/src/types.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index badbc43294f90654d02fa9851e174b21e4375a1e..b54383a88f42ad34c7c4caf34e2101ff2baba74b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,39 +13,48 @@ importers: .: dependencies: cssnano: - specifier: 6.0.2 - version: 6.0.2(postcss@8.4.32) + specifier: 6.0.5 + version: 6.0.5(postcss@8.4.35) execa: specifier: 8.0.1 version: 8.0.1 + fast-glob: + specifier: 3.3.2 + version: 3.3.2 + ignore-walk: + specifier: 6.0.4 + version: 6.0.4 js-yaml: specifier: 4.1.0 version: 4.1.0 postcss: - specifier: 8.4.32 - version: 8.4.32 + specifier: 8.4.35 + version: 8.4.35 + tar: + specifier: 6.2.0 + version: 6.2.0 terser: - specifier: 5.26.0 - version: 5.26.0 + specifier: 5.28.1 + version: 5.28.1 typescript: specifier: 5.3.3 version: 5.3.3 devDependencies: '@typescript-eslint/eslint-plugin': - specifier: 6.14.0 - version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/parser': - specifier: 6.14.0 - version: 6.14.0(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(eslint@8.57.0)(typescript@5.3.3) cross-env: specifier: 7.0.3 version: 7.0.3 cypress: - specifier: 13.6.1 - version: 13.6.1 + specifier: 13.6.6 + version: 13.6.6 eslint: - specifier: 8.56.0 - version: 8.56.0 + specifier: 8.57.0 + version: 8.57.0 ncp: specifier: 2.0.0 version: 2.0.0 @@ -62,14 +71,14 @@ importers: specifier: 3.412.0 version: 3.412.0(@aws-sdk/client-s3@3.412.0) '@bull-board/api': - specifier: 5.10.2 - version: 5.10.2(@bull-board/ui@5.10.2) + specifier: 5.14.2 + version: 5.14.2(@bull-board/ui@5.14.2) '@bull-board/fastify': - specifier: 5.10.2 - version: 5.10.2 + specifier: 5.14.2 + version: 5.14.2 '@bull-board/ui': - specifier: 5.10.2 - version: 5.10.2 + specifier: 5.14.2 + version: 5.14.2 '@discordapp/twemoji': specifier: 15.0.2 version: 15.0.2 @@ -77,8 +86,8 @@ importers: specifier: 4.3.0 version: 4.3.0 '@fastify/cookie': - specifier: 9.2.0 - version: 9.2.0 + specifier: 9.3.1 + version: 9.3.1 '@fastify/cors': specifier: 8.5.0 version: 8.5.0 @@ -89,32 +98,35 @@ importers: specifier: 9.3.0 version: 9.3.0(bufferutil@4.0.7)(utf-8-validate@6.0.3) '@fastify/multipart': - specifier: 8.0.0 - version: 8.0.0 + specifier: 8.1.0 + version: 8.1.0 '@fastify/static': specifier: 6.12.0 version: 6.12.0 '@fastify/view': specifier: 8.2.0 version: 8.2.0 + '@misskey-dev/sharp-read-bmp': + specifier: 1.2.0 + version: 1.2.0 + '@misskey-dev/summaly': + specifier: 5.0.3 + version: 5.0.3 '@nestjs/common': - specifier: 10.2.10 - version: 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1) + specifier: 10.3.3 + version: 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1) '@nestjs/core': - specifier: 10.2.10 - version: 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.14)(rxjs@7.8.1) + specifier: 10.3.3 + version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) '@nestjs/testing': - specifier: 10.2.10 - version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10) + specifier: 10.3.3 + version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(@nestjs/platform-express@10.3.3) '@peertube/http-signature': specifier: 1.7.0 version: 1.7.0 - '@sharkey/sfm-js': - specifier: 0.24.3 - version: 0.24.3 '@simplewebauthn/server': - specifier: 8.3.5 - version: 8.3.5 + specifier: 9.0.3 + version: 9.0.3 '@sinonjs/fake-timers': specifier: 11.2.2 version: 11.2.2 @@ -123,10 +135,13 @@ importers: version: 2.1.10 '@swc/cli': specifier: 0.1.63 - version: 0.1.63(@swc/core@1.3.100)(chokidar@3.5.3) + version: 0.1.63(@swc/core@1.3.107)(chokidar@3.5.3) '@swc/core': - specifier: 1.3.100 - version: 1.3.100 + specifier: 1.3.107 + version: 1.3.107 + '@transfem-org/sfm-js': + specifier: 0.24.4 + version: 0.24.4 '@twemoji/parser': specifier: 15.0.0 version: 15.0.0 @@ -140,11 +155,11 @@ importers: specifier: 6.0.1 version: 6.0.1 argon2: - specifier: ^0.31.1 - version: 0.31.1 + specifier: ^0.40.1 + version: 0.40.1 async-mutex: - specifier: 0.4.0 - version: 0.4.0 + specifier: 0.4.1 + version: 0.4.1 bcryptjs: specifier: 2.4.3 version: 2.4.3 @@ -155,14 +170,14 @@ importers: specifier: 1.20.2 version: 1.20.2 bullmq: - specifier: 4.15.4 - version: 4.15.4 + specifier: 5.4.0 + version: 5.4.0 cacheable-lookup: specifier: 7.0.0 version: 7.0.0 cbor: - specifier: 9.0.1 - version: 9.0.1 + specifier: 9.0.2 + version: 9.0.2 chalk: specifier: 5.3.0 version: 5.3.0 @@ -188,8 +203,8 @@ importers: specifier: 0.1.21 version: 0.1.21 fastify: - specifier: 4.24.3 - version: 4.24.3 + specifier: 4.25.2 + version: 4.25.2 fastify-multer: specifier: ^2.0.3 version: 2.0.3 @@ -200,8 +215,8 @@ importers: specifier: 4.2.2 version: 4.2.2 file-type: - specifier: 18.7.0 - version: 18.7.0 + specifier: 19.0.0 + version: 19.0.0 fluent-ffmpeg: specifier: 2.1.2 version: 2.1.2 @@ -212,17 +227,20 @@ importers: specifier: 10.3.10 version: 10.3.10 got: - specifier: 14.0.0 - version: 14.0.0 + specifier: 14.2.0 + version: 14.2.0 happy-dom: specifier: 10.0.3 version: 10.0.3 hpagent: specifier: 1.2.0 version: 1.2.0 - http-link-header: + htmlescape: specifier: 1.1.1 version: 1.1.1 + http-link-header: + specifier: 1.1.2 + version: 1.1.2 ioredis: specifier: 5.3.2 version: 5.3.2 @@ -239,8 +257,8 @@ importers: specifier: 4.1.0 version: 4.1.0 jsdom: - specifier: 23.0.1 - version: 23.0.1(bufferutil@4.0.7)(utf-8-validate@6.0.3) + specifier: 23.2.0 + version: 23.2.0(bufferutil@4.0.7)(utf-8-validate@6.0.3) json5: specifier: 2.2.3 version: 2.2.3 @@ -248,14 +266,14 @@ importers: specifier: 8.3.2 version: 8.3.2 jsrsasign: - specifier: 10.9.0 - version: 10.9.0 + specifier: 11.1.0 + version: 11.1.0 megalodon: specifier: workspace:* version: link:../megalodon meilisearch: - specifier: 0.36.0 - version: 0.36.0 + specifier: 0.37.0 + version: 0.37.0 microformats-parser: specifier: 2.0.2 version: 2.0.2 @@ -265,12 +283,15 @@ importers: misskey-js: specifier: workspace:* version: link:../misskey-js + misskey-reversi: + specifier: workspace:* + version: link:../misskey-reversi ms: specifier: 3.0.0-canary.1 version: 3.0.0-canary.1 nanoid: - specifier: 5.0.4 - version: 5.0.4 + specifier: 5.0.6 + version: 5.0.6 nested-property: specifier: 4.0.0 version: 4.0.0 @@ -278,8 +299,8 @@ importers: specifier: 3.3.2 version: 3.3.2 nodemailer: - specifier: 6.9.7 - version: 6.9.7 + specifier: 6.9.10 + version: 6.9.10 oauth: specifier: 0.10.0 version: 0.10.0 @@ -293,8 +314,8 @@ importers: specifier: 0.0.14 version: 0.0.14 otpauth: - specifier: 9.2.1 - version: 9.2.1 + specifier: 9.2.2 + version: 9.2.2 parse5: specifier: 7.1.2 version: 7.1.2 @@ -302,8 +323,8 @@ importers: specifier: 8.11.3 version: 8.11.3 pkce-challenge: - specifier: 4.0.1 - version: 4.0.1 + specifier: 4.1.0 + version: 4.1.0 probe-image-size: specifier: 7.2.3 version: 7.2.3 @@ -335,8 +356,8 @@ importers: specifier: 0.1.4 version: 0.1.4 reflect-metadata: - specifier: 0.1.14 - version: 0.1.14 + specifier: 0.2.1 + version: 0.2.1 rename: specifier: 1.0.4 version: 1.0.4 @@ -347,17 +368,14 @@ importers: specifier: 7.8.1 version: 7.8.1 sanitize-html: - specifier: 2.11.0 - version: 2.11.0 + specifier: 2.12.1 + version: 2.12.1 secure-json-parse: specifier: 2.7.0 version: 2.7.0 sharp: - specifier: 0.32.6 - version: 0.32.6 - sharp-read-bmp: - specifier: github:misskey-dev/sharp-read-bmp - version: github.com/misskey-dev/sharp-read-bmp/02d9dc189fa7df0c4bea09330be26741772dac01 + specifier: 0.33.2 + version: 0.33.2 slacc: specifier: 0.0.10 version: 0.0.10 @@ -367,18 +385,15 @@ importers: stringz: specifier: 2.1.0 version: 2.1.0 - summaly: - specifier: github:misskey-dev/summaly - version: github.com/misskey-dev/summaly/d2a3e07205c3c9769bc5a7b42031c8884b5a25c8 systeminformation: - specifier: 5.21.20 - version: 5.21.20 + specifier: 5.22.0 + version: 5.22.0 tinycolor2: specifier: 1.6.0 version: 1.6.0 tmp: - specifier: 0.2.1 - version: 0.2.1 + specifier: 0.2.2 + version: 0.2.2 tsc-alias: specifier: 1.8.8 version: 1.8.8 @@ -386,8 +401,8 @@ importers: specifier: 4.2.0 version: 4.2.0 typeorm: - specifier: 0.3.17 - version: 0.3.17(ioredis@5.3.2)(pg@8.11.3) + specifier: 0.3.20 + version: 0.3.20(ioredis@5.3.2)(pg@8.11.3) typescript: specifier: 5.3.3 version: 5.3.3 @@ -401,11 +416,11 @@ importers: specifier: 1.1.2 version: 1.1.2 web-push: - specifier: 3.6.6 - version: 3.6.6 + specifier: 3.6.7 + version: 3.6.7 ws: - specifier: 8.15.1 - version: 8.15.1(bufferutil@4.0.7)(utf-8-validate@6.0.3) + specifier: 8.16.0 + version: 8.16.0(bufferutil@4.0.7)(utf-8-validate@6.0.3) xev: specifier: 3.0.2 version: 3.0.2 @@ -495,12 +510,18 @@ importers: '@jest/globals': specifier: 29.7.0 version: 29.7.0 - '@simplewebauthn/typescript-types': - specifier: 8.3.4 - version: 8.3.4 + '@misskey-dev/eslint-plugin': + specifier: 1.0.0 + version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + '@nestjs/platform-express': + specifier: 10.3.3 + version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3) + '@simplewebauthn/types': + specifier: 9.0.1 + version: 9.0.1 '@swc/jest': - specifier: 0.2.29 - version: 0.2.29(@swc/core@1.3.100) + specifier: 0.2.31 + version: 0.2.31(@swc/core@1.3.107) '@types/accepts': specifier: 1.3.7 version: 1.3.7 @@ -513,9 +534,6 @@ importers: '@types/body-parser': specifier: 1.19.5 version: 1.19.5 - '@types/cbor': - specifier: 6.0.0 - version: 6.0.0 '@types/color-convert': specifier: 2.0.3 version: 2.0.3 @@ -525,6 +543,9 @@ importers: '@types/fluent-ffmpeg': specifier: 2.1.24 version: 2.1.24 + '@types/htmlescape': + specifier: ^1.1.3 + version: 1.1.3 '@types/http-link-header': specifier: 1.0.5 version: 1.0.5 @@ -550,8 +571,8 @@ importers: specifier: 0.7.34 version: 0.7.34 '@types/node': - specifier: 20.10.5 - version: 20.10.5 + specifier: 20.11.22 + version: 20.11.22 '@types/node-fetch': specifier: 3.0.3 version: 3.0.3 @@ -568,14 +589,14 @@ importers: specifier: 0.1.2 version: 0.1.2 '@types/pg': - specifier: 8.10.9 - version: 8.10.9 + specifier: 8.11.2 + version: 8.11.2 '@types/pug': specifier: 2.0.10 version: 2.0.10 '@types/punycode': - specifier: 2.1.3 - version: 2.1.3 + specifier: 2.1.4 + version: 2.1.4 '@types/qrcode': specifier: 1.5.5 version: 1.5.5 @@ -589,14 +610,11 @@ importers: specifier: 1.0.7 version: 1.0.7 '@types/sanitize-html': - specifier: 2.9.5 - version: 2.9.5 + specifier: 2.11.0 + version: 2.11.0 '@types/semver': - specifier: 7.5.6 - version: 7.5.6 - '@types/sharp': - specifier: 0.32.0 - version: 0.32.0 + specifier: 7.5.8 + version: 7.5.8 '@types/simple-oauth2': specifier: 5.0.7 version: 5.0.7 @@ -622,35 +640,41 @@ importers: specifier: 8.5.10 version: 8.5.10 '@typescript-eslint/eslint-plugin': - specifier: 6.14.0 - version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/parser': - specifier: 6.14.0 - version: 6.14.0(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(eslint@8.57.0)(typescript@5.3.3) aws-sdk-client-mock: - specifier: 3.0.0 - version: 3.0.0 + specifier: 3.0.1 + version: 3.0.1 cross-env: specifier: 7.0.3 version: 7.0.3 eslint: - specifier: 8.56.0 - version: 8.56.0 + specifier: 8.57.0 + version: 8.57.0 eslint-plugin-import: specifier: 2.29.1 - version: 2.29.1(@typescript-eslint/parser@6.14.0)(eslint@8.56.0) + version: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0) execa: specifier: 8.0.1 version: 8.0.1 + fkill: + specifier: ^9.0.0 + version: 9.0.0 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.10.5) + version: 29.7.0(@types/node@20.11.22) jest-mock: specifier: 29.7.0 version: 29.7.0 nodemon: - specifier: 3.0.2 - version: 3.0.2 + specifier: 3.1.0 + version: 3.1.0 + pid-port: + specifier: 1.0.0 + version: 1.0.0 simple-oauth2: specifier: 5.0.0 version: 5.0.0 @@ -663,69 +687,72 @@ importers: '@github/webauthn-json': specifier: 2.1.1 version: 2.1.1 + '@mcaptcha/vanilla-glue': + specifier: 0.1.0-alpha-3 + version: 0.1.0-alpha-3 + '@misskey-dev/browser-image-resizer': + specifier: 2024.1.0 + version: 2024.1.0 '@phosphor-icons/web': specifier: ^2.0.3 version: 2.0.3 '@rollup/plugin-json': specifier: 6.1.0 - version: 6.1.0(rollup@4.9.1) + version: 6.1.0(rollup@4.12.0) '@rollup/plugin-replace': specifier: 5.0.5 - version: 5.0.5(rollup@4.9.1) + version: 5.0.5(rollup@4.12.0) '@rollup/pluginutils': specifier: 5.1.0 - version: 5.1.0(rollup@4.9.1) - '@sharkey/sfm-js': - specifier: 0.24.3 - version: 0.24.3 + version: 5.1.0(rollup@4.12.0) '@syuilo/aiscript': - specifier: 0.16.0 - version: 0.16.0 + specifier: 0.17.0 + version: 0.17.0 + '@transfem-org/sfm-js': + specifier: 0.24.4 + version: 0.24.4 '@twemoji/parser': specifier: 15.0.0 version: 15.0.0 '@vitejs/plugin-vue': - specifier: 4.5.2 - version: 4.5.2(vite@5.0.10)(vue@3.3.12) + specifier: 5.0.4 + version: 5.0.4(vite@5.1.4)(vue@3.4.21) '@vue/compiler-sfc': - specifier: 3.3.12 - version: 3.3.12 + specifier: 3.4.21 + version: 3.4.21 aiscript-vscode: - specifier: github:aiscript-dev/aiscript-vscode#v0.0.6 - version: github.com/aiscript-dev/aiscript-vscode/b5a8aa0ad927831a0b867d1c183460a14e6c48cd + specifier: github:aiscript-dev/aiscript-vscode#v0.1.2 + version: github.com/aiscript-dev/aiscript-vscode/793211d40243c8775f6b85f015c221c82cbffb07 astring: specifier: 1.8.6 version: 1.8.6 broadcast-channel: specifier: 7.0.0 version: 7.0.0 - browser-image-resizer: - specifier: github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3 - version: github.com/misskey-dev/browser-image-resizer/0227e860621e55cbed0aabe6dc601096a7748c4a buraha: specifier: 0.0.1 version: 0.0.1 canvas-confetti: - specifier: 1.6.1 - version: 1.6.1 + specifier: 1.9.2 + version: 1.9.2 chart.js: - specifier: 4.4.1 - version: 4.4.1 + specifier: 4.4.2 + version: 4.4.2 chartjs-adapter-date-fns: specifier: 3.0.0 - version: 3.0.0(chart.js@4.4.1)(date-fns@2.30.0) + version: 3.0.0(chart.js@4.4.2)(date-fns@2.30.0) chartjs-chart-matrix: specifier: 2.0.1 - version: 2.0.1(chart.js@4.4.1) + version: 2.0.1(chart.js@4.4.2) chartjs-plugin-gradient: specifier: 0.6.1 - version: 0.6.1(chart.js@4.4.1) + version: 0.6.1(chart.js@4.4.2) chartjs-plugin-zoom: specifier: 2.0.1 - version: 2.0.1(chart.js@4.4.1) + version: 2.0.1(chart.js@4.4.2) chromatic: - specifier: 10.1.0 - version: 10.1.0 + specifier: 11.0.0 + version: 11.0.0 compare-versions: specifier: 6.1.0 version: 6.1.0 @@ -744,9 +771,6 @@ importers: eventemitter3: specifier: 5.0.1 version: 5.0.1 - gsap: - specifier: 3.12.4 - version: 3.12.4 idb-keyval: specifier: 6.2.1 version: 6.2.1 @@ -765,9 +789,15 @@ importers: matter-js: specifier: 0.19.0 version: 0.19.0 + misskey-bubble-game: + specifier: workspace:* + version: link:../misskey-bubble-game misskey-js: specifier: workspace:* version: link:../misskey-js + misskey-reversi: + specifier: workspace:* + version: link:../misskey-reversi photoswipe: specifier: 5.4.3 version: 5.4.3 @@ -775,17 +805,17 @@ importers: specifier: 2.3.1 version: 2.3.1 rollup: - specifier: 4.9.1 - version: 4.9.1 + specifier: 4.12.0 + version: 4.12.0 sanitize-html: - specifier: 2.11.0 - version: 2.11.0 + specifier: 2.12.1 + version: 2.12.1 sass: - specifier: 1.69.5 - version: 1.69.5 + specifier: 1.71.1 + version: 1.71.1 shiki: - specifier: 0.14.7 - version: 0.14.7 + specifier: 1.1.7 + version: 1.1.7 strict-event-emitter-types: specifier: 2.0.0 version: 2.0.0 @@ -793,8 +823,8 @@ importers: specifier: 3.1.0 version: 3.1.0 three: - specifier: 0.159.0 - version: 0.159.0 + specifier: 0.162.0 + version: 0.162.0 throttle-debounce: specifier: 5.0.0 version: 5.0.0 @@ -814,75 +844,81 @@ importers: specifier: 9.0.1 version: 9.0.1 v-code-diff: - specifier: 1.7.2 - version: 1.7.2(vue@3.3.12) + specifier: 1.9.0 + version: 1.9.0(vue@3.4.21) vite: - specifier: 5.0.10 - version: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0) + specifier: 5.1.4 + version: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) vue: - specifier: 3.3.12 - version: 3.3.12(typescript@5.3.3) + specifier: 3.4.21 + version: 3.4.21(typescript@5.3.3) vuedraggable: specifier: next - version: 4.1.0(vue@3.3.12) + version: 4.1.0(vue@3.4.21) devDependencies: + '@misskey-dev/eslint-plugin': + specifier: 1.0.0 + version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + '@misskey-dev/summaly': + specifier: 5.0.3 + version: 5.0.3 '@storybook/addon-actions': - specifier: 7.6.5 - version: 7.6.5 + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6 '@storybook/addon-essentials': - specifier: 7.6.5 - version: 7.6.5(react-dom@18.2.0)(react@18.2.0) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) '@storybook/addon-interactions': - specifier: 7.6.5 - version: 7.6.5 + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6 '@storybook/addon-links': - specifier: 7.6.5 - version: 7.6.5(react@18.2.0) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(react@18.2.0) + '@storybook/addon-mdx-gfm': + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6 '@storybook/addon-storysource': - specifier: 7.6.5 - version: 7.6.5 - '@storybook/addons': - specifier: 7.6.5 - version: 7.6.5(react-dom@18.2.0)(react@18.2.0) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6 '@storybook/blocks': - specifier: 7.6.5 - version: 7.6.5(react-dom@18.2.0)(react@18.2.0) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + '@storybook/components': + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) '@storybook/core-events': - specifier: 7.6.5 - version: 7.6.5 - '@storybook/jest': - specifier: 0.2.3 - version: 0.2.3(vitest@0.34.6) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6 '@storybook/manager-api': - specifier: 7.6.5 - version: 7.6.5(react-dom@18.2.0)(react@18.2.0) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) '@storybook/preview-api': - specifier: 7.6.5 - version: 7.6.5 + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6 '@storybook/react': - specifier: 7.6.5 - version: 7.6.5(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3) '@storybook/react-vite': - specifier: 7.6.5 - version: 7.6.5(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.1)(typescript@5.3.3)(vite@5.0.10) - '@storybook/testing-library': - specifier: 0.2.2 - version: 0.2.2 + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(rollup@4.12.0)(typescript@5.3.3)(vite@5.1.4) + '@storybook/test': + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(vitest@0.34.6) '@storybook/theming': - specifier: 7.6.5 - version: 7.6.5(react-dom@18.2.0)(react@18.2.0) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) '@storybook/types': - specifier: 7.6.5 - version: 7.6.5 + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6 '@storybook/vue3': - specifier: 7.6.5 - version: 7.6.5(@vue/compiler-core@3.3.12)(vue@3.3.12) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(vue@3.4.21) '@storybook/vue3-vite': - specifier: 7.6.5 - version: 7.6.5(@vue/compiler-core@3.3.12)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.12) + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(vite@5.1.4)(vue@3.4.21) '@testing-library/vue': - specifier: 8.0.1 - version: 8.0.1(@vue/compiler-sfc@3.3.12)(vue@3.3.12) + specifier: 8.0.2 + version: 8.0.2(@vue/compiler-sfc@3.4.21)(vue@3.4.21) '@types/escape-regexp': specifier: 0.0.3 version: 0.0.3 @@ -890,20 +926,20 @@ importers: specifier: 1.0.5 version: 1.0.5 '@types/matter-js': - specifier: 0.19.5 - version: 0.19.5 + specifier: 0.19.6 + version: 0.19.6 '@types/micromatch': specifier: 4.0.6 version: 4.0.6 '@types/node': - specifier: 20.10.5 - version: 20.10.5 + specifier: 20.11.22 + version: 20.11.22 '@types/punycode': - specifier: 2.1.3 - version: 2.1.3 + specifier: 2.1.4 + version: 2.1.4 '@types/sanitize-html': - specifier: 2.9.5 - version: 2.9.5 + specifier: 2.11.0 + version: 2.11.0 '@types/throttle-debounce': specifier: 5.0.2 version: 5.0.2 @@ -911,47 +947,47 @@ importers: specifier: 1.4.6 version: 1.4.6 '@types/uuid': - specifier: 9.0.7 - version: 9.0.7 + specifier: 9.0.8 + version: 9.0.8 '@types/ws': specifier: 8.5.10 version: 8.5.10 '@typescript-eslint/eslint-plugin': - specifier: 6.14.0 - version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/parser': - specifier: 6.14.0 - version: 6.14.0(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(eslint@8.57.0)(typescript@5.3.3) '@vitest/coverage-v8': specifier: 0.34.6 version: 0.34.6(vitest@0.34.6) '@vue/runtime-core': - specifier: 3.3.12 - version: 3.3.12 + specifier: 3.4.21 + version: 3.4.21 acorn: - specifier: 8.11.2 - version: 8.11.2 + specifier: 8.11.3 + version: 8.11.3 cross-env: specifier: 7.0.3 version: 7.0.3 cypress: - specifier: 13.6.1 - version: 13.6.1 + specifier: 13.6.6 + version: 13.6.6 eslint: - specifier: 8.56.0 - version: 8.56.0 + specifier: 8.57.0 + version: 8.57.0 eslint-plugin-import: specifier: 2.29.1 - version: 2.29.1(@typescript-eslint/parser@6.14.0)(eslint@8.56.0) + version: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0) eslint-plugin-vue: - specifier: 9.19.2 - version: 9.19.2(eslint@8.56.0) + specifier: 9.22.0 + version: 9.22.0(eslint@8.57.0) fast-glob: specifier: 3.3.2 version: 3.3.2 happy-dom: - specifier: 10.0.3 - version: 10.0.3 + specifier: 13.6.2 + version: 13.6.2 intersection-observer: specifier: 0.12.2 version: 0.12.2 @@ -959,17 +995,17 @@ importers: specifier: 4.0.5 version: 4.0.5 msw: - specifier: 1.3.2 - version: 1.3.2(typescript@5.3.3) + specifier: 2.1.7 + version: 2.1.7(typescript@5.3.3) msw-storybook-addon: - specifier: 1.10.0 - version: 1.10.0(msw@1.3.2) + specifier: 2.0.0-beta.1 + version: 2.0.0-beta.1(msw@2.1.7) nodemon: - specifier: 3.0.2 - version: 3.0.2 + specifier: 3.1.0 + version: 3.1.0 prettier: - specifier: 3.1.1 - version: 3.1.1 + specifier: 3.2.5 + version: 3.2.5 react: specifier: 18.2.0 version: 18.2.0 @@ -980,29 +1016,29 @@ importers: specifier: 2.0.3 version: 2.0.3 storybook: - specifier: 7.6.5 - version: 7.6.5 + specifier: 8.0.0-beta.6 + version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) storybook-addon-misskey-theme: specifier: github:misskey-dev/storybook-addon-misskey-theme - version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.5)(@storybook/components@7.6.5)(@storybook/core-events@7.6.5)(@storybook/manager-api@7.6.5)(@storybook/preview-api@7.6.5)(@storybook/theming@7.6.5)(@storybook/types@7.6.5)(react-dom@18.2.0)(react@18.2.0) - summaly: - specifier: github:misskey-dev/summaly - version: github.com/misskey-dev/summaly/d2a3e07205c3c9769bc5a7b42031c8884b5a25c8 + version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.0-beta.6)(@storybook/components@8.0.0-beta.6)(@storybook/core-events@8.0.0-beta.6)(@storybook/manager-api@8.0.0-beta.6)(@storybook/preview-api@8.0.0-beta.6)(@storybook/theming@8.0.0-beta.6)(@storybook/types@8.0.0-beta.6)(react-dom@18.2.0)(react@18.2.0) vite-plugin-turbosnap: specifier: 1.0.3 version: 1.0.3 vitest: specifier: 0.34.6 - version: 0.34.6(happy-dom@10.0.3)(sass@1.69.5)(terser@5.26.0) + version: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1) vitest-fetch-mock: specifier: 0.2.2 version: 0.2.2(vitest@0.34.6) + vue-component-type-helpers: + specifier: 1.8.27 + version: 1.8.27 vue-eslint-parser: - specifier: 9.3.2 - version: 9.3.2(eslint@8.56.0) + specifier: 9.4.2 + version: 9.4.2(eslint@8.57.0) vue-tsc: - specifier: 1.8.25 - version: 1.8.25(typescript@5.3.3) + specifier: 1.8.27 + version: 1.8.27(typescript@5.3.3) packages/megalodon: dependencies: @@ -1066,10 +1102,10 @@ importers: devDependencies: '@typescript-eslint/eslint-plugin': specifier: ^6.12.0 - version: 6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.1.6) + version: 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.54.0)(typescript@5.1.6) '@typescript-eslint/parser': specifier: ^6.12.0 - version: 6.12.0(eslint@8.54.0)(typescript@5.1.6) + version: 6.21.0(eslint@8.54.0)(typescript@5.1.6) eslint: specifier: ^8.54.0 version: 8.54.0 @@ -1078,7 +1114,7 @@ importers: version: 9.0.0(eslint@8.54.0) jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.10.5) + version: 29.7.0(@types/node@20.11.22) jest-worker: specifier: ^29.7.0 version: 29.7.0 @@ -1090,19 +1126,65 @@ importers: version: 3.1.0 ts-jest: specifier: ^29.1.1 - version: 29.1.1(@babel/core@7.23.3)(jest@29.7.0)(typescript@5.1.6) + version: 29.1.1(@babel/core@7.24.0)(jest@29.7.0)(typescript@5.1.6) typedoc: specifier: ^0.25.3 version: 0.25.3(typescript@5.1.6) + packages/misskey-bubble-game: + dependencies: + esbuild: + specifier: 0.19.11 + version: 0.19.11 + eventemitter3: + specifier: 5.0.1 + version: 5.0.1 + glob: + specifier: ^10.3.10 + version: 10.3.10 + matter-js: + specifier: 0.19.0 + version: 0.19.0 + seedrandom: + specifier: 3.0.5 + version: 3.0.5 + devDependencies: + '@misskey-dev/eslint-plugin': + specifier: 1.0.0 + version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + '@types/matter-js': + specifier: 0.19.6 + version: 0.19.6 + '@types/node': + specifier: 20.11.5 + version: 20.11.5 + '@types/seedrandom': + specifier: 3.0.8 + version: 3.0.8 + '@typescript-eslint/eslint-plugin': + specifier: 7.1.0 + version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/parser': + specifier: 7.1.0 + version: 7.1.0(eslint@8.57.0)(typescript@5.3.3) + eslint: + specifier: 8.57.0 + version: 8.57.0 + nodemon: + specifier: 3.0.2 + version: 3.0.2 + typescript: + specifier: 5.3.3 + version: 5.3.3 + packages/misskey-js: dependencies: '@swc/cli': specifier: 0.1.63 - version: 0.1.63(@swc/core@1.3.100)(chokidar@3.5.3) + version: 0.1.63(@swc/core@1.3.105) '@swc/core': - specifier: 1.3.100 - version: 1.3.100 + specifier: 1.3.105 + version: 1.3.105 eventemitter3: specifier: 5.0.1 version: 5.0.1 @@ -1111,29 +1193,32 @@ importers: version: 4.4.0 devDependencies: '@microsoft/api-extractor': - specifier: 7.38.5 - version: 7.38.5(@types/node@20.10.5) + specifier: 7.39.1 + version: 7.39.1(@types/node@20.11.22) + '@misskey-dev/eslint-plugin': + specifier: 1.0.0 + version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) '@swc/jest': - specifier: 0.2.29 - version: 0.2.29(@swc/core@1.3.100) + specifier: 0.2.31 + version: 0.2.31(@swc/core@1.3.105) '@types/jest': - specifier: 29.5.11 - version: 29.5.11 + specifier: 29.5.12 + version: 29.5.12 '@types/node': - specifier: 20.10.5 - version: 20.10.5 + specifier: 20.11.22 + version: 20.11.22 '@typescript-eslint/eslint-plugin': - specifier: 6.14.0 - version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/parser': - specifier: 6.14.0 - version: 6.14.0(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(eslint@8.57.0)(typescript@5.3.3) eslint: - specifier: 8.56.0 - version: 8.56.0 + specifier: 8.57.0 + version: 8.57.0 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.10.5) + version: 29.7.0(@types/node@20.11.22) jest-fetch-mock: specifier: 3.0.3 version: 3.0.3 @@ -1147,20 +1232,23 @@ importers: specifier: 2.0.0 version: 2.0.0 nodemon: - specifier: 3.0.2 - version: 3.0.2 + specifier: 3.1.0 + version: 3.1.0 tsd: - specifier: 0.30.0 - version: 0.30.0 + specifier: 0.30.7 + version: 0.30.7 typescript: specifier: 5.3.3 version: 5.3.3 packages/misskey-js/generator: devDependencies: - '@apidevtools/swagger-parser': - specifier: 10.1.0 - version: 10.1.0(openapi-types@12.1.3) + '@misskey-dev/eslint-plugin': + specifier: ^1.0.0 + version: 1.0.0(@typescript-eslint/eslint-plugin@6.11.0)(@typescript-eslint/parser@6.11.0)(eslint-plugin-import@2.29.1)(eslint@8.53.0) + '@readme/openapi-parser': + specifier: 2.5.0 + version: 2.5.0(openapi-types@12.1.3) '@types/node': specifier: 20.9.1 version: 20.9.1 @@ -1177,8 +1265,8 @@ importers: specifier: 12.1.3 version: 12.1.3 openapi-typescript: - specifier: 6.7.1 - version: 6.7.1 + specifier: 6.7.3 + version: 6.7.3 ts-case-convert: specifier: 2.0.2 version: 2.0.2 @@ -1189,11 +1277,45 @@ importers: specifier: 5.3.3 version: 5.3.3 + packages/misskey-reversi: + dependencies: + crc-32: + specifier: 1.2.2 + version: 1.2.2 + esbuild: + specifier: 0.19.11 + version: 0.19.11 + glob: + specifier: 10.3.10 + version: 10.3.10 + devDependencies: + '@misskey-dev/eslint-plugin': + specifier: 1.0.0 + version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + '@types/node': + specifier: 20.11.5 + version: 20.11.5 + '@typescript-eslint/eslint-plugin': + specifier: 7.1.0 + version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/parser': + specifier: 7.1.0 + version: 7.1.0(eslint@8.57.0)(typescript@5.3.3) + eslint: + specifier: 8.57.0 + version: 8.57.0 + nodemon: + specifier: 3.0.2 + version: 3.0.2 + typescript: + specifier: 5.3.3 + version: 5.3.3 + packages/sw: dependencies: esbuild: - specifier: 0.19.9 - version: 0.19.9 + specifier: 0.19.11 + version: 0.19.11 idb-keyval: specifier: 6.2.1 version: 6.2.1 @@ -1201,21 +1323,24 @@ importers: specifier: workspace:* version: link:../misskey-js devDependencies: + '@misskey-dev/eslint-plugin': + specifier: 1.0.0 + version: 1.0.0(@typescript-eslint/eslint-plugin@6.21.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) '@typescript-eslint/parser': - specifier: 6.14.0 - version: 6.14.0(eslint@8.56.0)(typescript@5.3.3) + specifier: 7.1.0 + version: 7.1.0(eslint@8.57.0)(typescript@5.3.3) '@typescript/lib-webworker': specifier: npm:@types/serviceworker@0.0.67 version: /@types/serviceworker@0.0.67 eslint: - specifier: 8.56.0 - version: 8.56.0 + specifier: 8.57.0 + version: 8.57.0 eslint-plugin-import: specifier: 2.29.1 - version: 2.29.1(@typescript-eslint/parser@6.14.0)(eslint@8.56.0) + version: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0) nodemon: - specifier: 3.0.2 - version: 3.0.2 + specifier: 3.1.0 + version: 3.1.0 typescript: specifier: 5.3.3 version: 5.3.3 @@ -1227,24 +1352,16 @@ packages: engines: {node: '>=0.10.0'} dev: true - /@adobe/css-tools@4.3.1: - resolution: {integrity: sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==} + /@adobe/css-tools@4.3.3: + resolution: {integrity: sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==} dev: true /@ampproject/remapping@2.2.1: resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} engines: {node: '>=6.0.0'} dependencies: - '@jridgewell/gen-mapping': 0.3.2 - '@jridgewell/trace-mapping': 0.3.18 - dev: true - - /@apidevtools/json-schema-ref-parser@9.0.6: - resolution: {integrity: sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==} - dependencies: - '@jsdevtools/ono': 7.1.3 - call-me-maybe: 1.0.2 - js-yaml: 3.14.1 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 dev: true /@apidevtools/openapi-schemas@2.1.0: @@ -1256,20 +1373,13 @@ packages: resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} dev: true - /@apidevtools/swagger-parser@10.1.0(openapi-types@12.1.3): - resolution: {integrity: sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==} - peerDependencies: - openapi-types: '>=7' + /@asamuzakjp/dom-selector@2.0.2: + resolution: {integrity: sha512-x1KXOatwofR6ZAYzXRBL5wrdV0vwNxlTCK9NCuLqAzQYARqGcvFwiJA6A1ERuh+dgeA4Dxm3JBYictIes+SqUQ==} dependencies: - '@apidevtools/json-schema-ref-parser': 9.0.6 - '@apidevtools/openapi-schemas': 2.1.0 - '@apidevtools/swagger-methods': 3.0.2 - '@jsdevtools/ono': 7.1.3 - ajv: 8.12.0 - ajv-draft-04: 1.0.0(ajv@8.12.0) - call-me-maybe: 1.0.2 - openapi-types: 12.1.3 - dev: true + bidi-js: 1.0.3 + css-tree: 2.3.1 + is-potential-custom-element-name: 1.0.1 + dev: false /@aw-web-design/x-default-browser@1.4.126: resolution: {integrity: sha512-Xk1sIhyNC/esHGGVjL/niHLowM0csl/kFO5uawBy4IrWwy0o1G8LGt3jP6nmWGz+USxeeqbihAmp/oVZju6wug==} @@ -1852,20 +1962,20 @@ packages: tslib: 2.6.2 dev: false - /@babel/code-frame@7.22.13: - resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} + /@babel/code-frame@7.23.4: + resolution: {integrity: sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/highlight': 7.22.13 + '@babel/highlight': 7.23.4 chalk: 2.4.2 - dev: true - /@babel/code-frame@7.23.4: - resolution: {integrity: sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA==} + /@babel/code-frame@7.23.5: + resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} engines: {node: '>=6.9.0'} dependencies: '@babel/highlight': 7.23.4 chalk: 2.4.2 + dev: true /@babel/compat-data@7.23.3: resolution: {integrity: sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==} @@ -1883,14 +1993,37 @@ packages: dependencies: '@ampproject/remapping': 2.2.1 '@babel/code-frame': 7.23.4 - '@babel/generator': 7.23.4 + '@babel/generator': 7.23.6 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) '@babel/helpers': 7.23.4 '@babel/parser': 7.23.4 '@babel/template': 7.22.15 - '@babel/traverse': 7.23.4 - '@babel/types': 7.23.4 + '@babel/traverse': 7.24.0 + '@babel/types': 7.24.0 + convert-source-map: 2.0.0 + debug: 4.3.4(supports-color@8.1.1) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/core@7.24.0: + resolution: {integrity: sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.6 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) + '@babel/helpers': 7.24.0 + '@babel/parser': 7.24.0 + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.0 + '@babel/types': 7.24.0 convert-source-map: 2.0.0 debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 @@ -1900,11 +2033,11 @@ packages: - supports-color dev: true - /@babel/generator@7.23.4: - resolution: {integrity: sha512-esuS49Cga3HcThFNebGhlgsrVLkvhqvYDTzgjfFFlHJcIfLe5jFmRRfCQ1KuBfc4Jrtn3ndLgKWAKjBE+IraYQ==} + /@babel/generator@7.23.6: + resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 '@jridgewell/gen-mapping': 0.3.3 '@jridgewell/trace-mapping': 0.3.20 jsesc: 2.5.2 @@ -1914,14 +2047,14 @@ packages: resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 dev: true /@babel/helper-builder-binary-assignment-operator-visitor@7.22.15: resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 dev: true /@babel/helper-compilation-targets@7.22.15: @@ -1941,77 +2074,65 @@ packages: dependencies: '@babel/compat-data': 7.23.5 '@babel/helper-validator-option': 7.23.5 - browserslist: 4.22.2 + browserslist: 4.23.0 lru-cache: 5.1.1 semver: 6.3.1 dev: true - /@babel/helper-create-class-features-plugin@7.22.9(@babel/core@7.23.3): - resolution: {integrity: sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ==} + /@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.24.0): + resolution: {integrity: sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 - '@babel/helper-member-expression-to-functions': 7.22.5 + '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-replace-supers': 7.22.9(@babel/core@7.23.3) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0) '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 semver: 6.3.1 dev: true - /@babel/helper-create-class-features-plugin@7.23.6(@babel/core@7.23.3): - resolution: {integrity: sha512-cBXU1vZni/CpGF29iTu4YRbOZt3Wat6zCoMDxRF1MayiEc4URxOj31tT65HUM0CRpMowA3HCJaAOVOUnMf96cw==} + /@babel/helper-create-class-features-plugin@7.24.0(@babel/core@7.24.0): + resolution: {integrity: sha512-QAH+vfvts51BCsNZ2PhY6HAggnlS6omLLFTsIpeqZk/MmJ6cW7tgz5yRv0fMJThcr6FmbMrENh1RgrWPTYA76g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.3) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0) '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 semver: 6.3.1 dev: true - /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.23.3): + /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.24.0): resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-annotate-as-pure': 7.22.5 - regexpu-core: 5.3.2 - semver: 6.3.1 - dev: true - - /@babel/helper-create-regexp-features-plugin@7.22.9(@babel/core@7.23.3): - resolution: {integrity: sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-annotate-as-pure': 7.22.5 regexpu-core: 5.3.2 semver: 6.3.1 dev: true - /@babel/helper-define-polyfill-provider@0.4.4(@babel/core@7.23.3): + /@babel/helper-define-polyfill-provider@0.4.4(@babel/core@7.24.0): resolution: {integrity: sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-plugin-utils': 7.22.5 debug: 4.3.4(supports-color@8.1.1) @@ -2030,36 +2151,29 @@ packages: resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.22.15 - '@babel/types': 7.23.4 + '@babel/template': 7.24.0 + '@babel/types': 7.24.0 dev: true /@babel/helper-hoist-variables@7.22.5: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.4 - dev: true - - /@babel/helper-member-expression-to-functions@7.22.5: - resolution: {integrity: sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 dev: true /@babel/helper-member-expression-to-functions@7.23.0: resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 dev: true /@babel/helper-module-imports@7.22.15: resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 dev: true /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.3): @@ -2076,11 +2190,25 @@ packages: '@babel/helper-validator-identifier': 7.22.20 dev: true + /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.0): + resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + dev: true + /@babel/helper-optimise-call-expression@7.22.5: resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 dev: true /@babel/helper-plugin-utils@7.22.5: @@ -2088,75 +2216,55 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.23.3): + /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.24.0): resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-wrap-function': 7.22.20 dev: true - /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.3): + /@babel/helper-replace-supers@7.22.20(@babel/core@7.24.0): resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 dev: true - /@babel/helper-replace-supers@7.22.9(@babel/core@7.23.3): - resolution: {integrity: sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.3 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-member-expression-to-functions': 7.22.5 - '@babel/helper-optimise-call-expression': 7.22.5 - dev: true - /@babel/helper-simple-access@7.22.5: resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 dev: true /@babel/helper-skip-transparent-expression-wrappers@7.22.5: resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 dev: true /@babel/helper-split-export-declaration@7.22.6: resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 dev: true - /@babel/helper-string-parser@7.22.5: - resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} - engines: {node: '>=6.9.0'} - /@babel/helper-string-parser@7.23.4: resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-identifier@7.22.15: - resolution: {integrity: sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==} - engines: {node: '>=6.9.0'} - /@babel/helper-validator-identifier@7.22.20: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} @@ -2176,8 +2284,8 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/helper-function-name': 7.23.0 - '@babel/template': 7.22.15 - '@babel/types': 7.23.4 + '@babel/template': 7.24.0 + '@babel/types': 7.24.0 dev: true /@babel/helpers@7.23.4: @@ -2185,19 +2293,21 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.22.15 - '@babel/traverse': 7.23.4 - '@babel/types': 7.23.4 + '@babel/traverse': 7.24.0 + '@babel/types': 7.24.0 transitivePeerDependencies: - supports-color dev: true - /@babel/highlight@7.22.13: - resolution: {integrity: sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==} + /@babel/helpers@7.24.0: + resolution: {integrity: sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-validator-identifier': 7.22.15 - chalk: 2.4.2 - js-tokens: 4.0.0 + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.0 + '@babel/types': 7.24.0 + transitivePeerDependencies: + - supports-color dev: true /@babel/highlight@7.23.4: @@ -2213,14 +2323,14 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.22.17 + '@babel/types': 7.24.0 /@babel/parser@7.23.4: resolution: {integrity: sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==} engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 dev: true /@babel/parser@7.23.6: @@ -2228,48 +2338,64 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 + dev: true + + /@babel/parser@7.23.9: + resolution: {integrity: sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.24.0 + + /@babel/parser@7.24.0: + resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.24.0 + dev: true - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.23.3): + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3(@babel/core@7.23.3): + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.24.0) dev: true - /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.3(@babel/core@7.23.3): + /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.3): + /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.0): resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 dev: true /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.3): @@ -2281,6 +2407,15 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.0): + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.3): resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: @@ -2299,61 +2434,70 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.23.3): + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.0): + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.0): resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.23.3): + /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.0): resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.23.3): + /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.0): resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-flow@7.23.3(@babel/core@7.23.3): + /@babel/plugin-syntax-flow@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-import-assertions@7.23.3(@babel/core@7.23.3): + /@babel/plugin-syntax-import-assertions@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-import-attributes@7.23.3(@babel/core@7.23.3): + /@babel/plugin-syntax-import-attributes@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true @@ -2366,18 +2510,17 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.3): - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.0): + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.3): - resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} - engines: {node: '>=6.9.0'} + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: @@ -2385,7 +2528,36 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.3): + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.0): + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.3): + resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.24.0): + resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.3): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -2394,6 +2566,15 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.0): + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.3): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: @@ -2403,6 +2584,15 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.0): + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.3): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: @@ -2412,6 +2602,15 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.0): + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.3): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: @@ -2421,6 +2620,15 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.0): + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.3): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: @@ -2430,6 +2638,15 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.0): + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.3): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: @@ -2439,13 +2656,22 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.23.3): + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.0): + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.0): resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true @@ -2459,6 +2685,16 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.0): + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} engines: {node: '>=6.9.0'} @@ -2469,751 +2705,719 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.23.3): + /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.24.0): + resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.23.3) + '@babel/core': 7.24.0 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-async-generator-functions@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-async-generator-functions@7.23.4(@babel/core@7.24.0): resolution: {integrity: sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.3) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.3) + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.24.0) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-async-to-generator@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-async-to-generator@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-module-imports': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.3) + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.24.0): resolution: {integrity: sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-transform-class-properties@7.22.5(@babel/core@7.23.3): - resolution: {integrity: sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-class-features-plugin': 7.22.9(@babel/core@7.23.3) + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.23.3) + '@babel/core': 7.24.0 + '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-class-static-block@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-class-static-block@7.23.4(@babel/core@7.24.0): resolution: {integrity: sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.23.3) + '@babel/core': 7.24.0 + '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.3) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-classes@7.23.5(@babel/core@7.23.3): + /@babel/plugin-transform-classes@7.23.5(@babel/core@7.24.0): resolution: {integrity: sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.3) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0) '@babel/helper-split-export-declaration': 7.22.6 globals: 11.12.0 dev: true - /@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/template': 7.22.15 + '@babel/template': 7.24.0 dev: true - /@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.3) + '@babel/core': 7.24.0 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-duplicate-keys@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-duplicate-keys@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-dynamic-import@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-dynamic-import@7.23.4(@babel/core@7.24.0): resolution: {integrity: sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-exponentiation-operator@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-exponentiation-operator@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-export-namespace-from@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-export-namespace-from@7.23.4(@babel/core@7.24.0): resolution: {integrity: sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-flow-strip-types@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-flow-strip-types@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-flow': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-syntax-flow': 7.23.3(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-for-of@7.23.6(@babel/core@7.23.3): + /@babel/plugin-transform-for-of@7.23.6(@babel/core@7.24.0): resolution: {integrity: sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 dev: true - /@babel/plugin-transform-function-name@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-function-name@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-function-name': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.24.0): resolution: {integrity: sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-literals@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-literals@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.24.0): resolution: {integrity: sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.3) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-modules-amd@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-modules-amd@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) + '@babel/core': 7.24.0 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) + '@babel/core': 7.24.0 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-simple-access': 7.22.5 dev: true - /@babel/plugin-transform-modules-systemjs@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-modules-systemjs@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-identifier': 7.22.20 dev: true - /@babel/plugin-transform-modules-umd@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-modules-umd@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) + '@babel/core': 7.24.0 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.23.3): + /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.24.0): resolution: {integrity: sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.23.3) + '@babel/core': 7.24.0 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-new-target@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-new-target@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-nullish-coalescing-operator@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-nullish-coalescing-operator@7.23.4(@babel/core@7.24.0): resolution: {integrity: sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-numeric-separator@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-numeric-separator@7.23.4(@babel/core@7.24.0): resolution: {integrity: sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.3) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-object-rest-spread@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-object-rest-spread@7.23.4(@babel/core@7.24.0): resolution: {integrity: sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/compat-data': 7.23.5 - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.3) - '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-object-super@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-object-super@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.3) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-optional-catch-binding@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-optional-catch-binding@7.23.4(@babel/core@7.24.0): resolution: {integrity: sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-optional-chaining@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-optional-chaining@7.23.4(@babel/core@7.24.0): resolution: {integrity: sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-parameters@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-parameters@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-transform-private-methods@7.22.5(@babel/core@7.23.3): - resolution: {integrity: sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-class-features-plugin': 7.22.9(@babel/core@7.23.3) + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.23.3) + '@babel/core': 7.24.0 + '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-private-property-in-object@7.23.4(@babel/core@7.23.3): + /@babel/plugin-transform-private-property-in-object@7.23.4(@babel/core@7.24.0): resolution: {integrity: sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.23.3) + '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.3) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-transform-react-jsx-self@7.21.0(@babel/core@7.23.3): - resolution: {integrity: sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-transform-react-jsx-source@7.19.6(@babel/core@7.23.3): - resolution: {integrity: sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-regenerator@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-regenerator@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 regenerator-transform: 0.15.2 dev: true - /@babel/plugin-transform-reserved-words@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-reserved-words@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-spread@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-spread@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 dev: true - /@babel/plugin-transform-sticky-regex@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-sticky-regex@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-typeof-symbol@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-typeof-symbol@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-typescript@7.23.6(@babel/core@7.23.3): + /@babel/plugin-transform-typescript@7.23.6(@babel/core@7.24.0): resolution: {integrity: sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.23.3) + '@babel/helper-create-class-features-plugin': 7.24.0(@babel/core@7.24.0) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-unicode-escapes@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-unicode-escapes@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-unicode-property-regex@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-unicode-property-regex@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.3) + '@babel/core': 7.24.0 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-unicode-regex@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-unicode-regex@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.3) + '@babel/core': 7.24.0 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-unicode-sets-regex@7.23.3(@babel/core@7.23.3): + /@babel/plugin-transform-unicode-sets-regex@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.3) + '@babel/core': 7.24.0 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/preset-env@7.23.6(@babel/core@7.23.3): + /@babel/preset-env@7.23.6(@babel/core@7.24.0): resolution: {integrity: sha512-2XPn/BqKkZCpzYhUUNZ1ssXw7DcXfKQEjv/uXZUXgaebCMYmkEsfZ2yY+vv+xtXv50WmL5SGhyB6/xsWxIvvOQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/compat-data': 7.23.5 - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-option': 7.23.5 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.3) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.3) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.3) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.3) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.3) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.3) - '@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-syntax-import-attributes': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.3) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.3) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.3) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.3) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.3) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.3) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.3) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.3) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.3) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.3) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.23.3) - '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-async-generator-functions': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-async-to-generator': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-block-scoped-functions': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-class-static-block': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-classes': 7.23.5(@babel/core@7.23.3) - '@babel/plugin-transform-computed-properties': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-dotall-regex': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-duplicate-keys': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-dynamic-import': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-exponentiation-operator': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-export-namespace-from': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-for-of': 7.23.6(@babel/core@7.23.3) - '@babel/plugin-transform-function-name': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-json-strings': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-literals': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-logical-assignment-operators': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-member-expression-literals': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-modules-amd': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-modules-systemjs': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-modules-umd': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.23.3) - '@babel/plugin-transform-new-target': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-numeric-separator': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-object-rest-spread': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-object-super': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-optional-catch-binding': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-private-property-in-object': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-property-literals': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-regenerator': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-reserved-words': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-sticky-regex': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-typeof-symbol': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-unicode-escapes': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-unicode-property-regex': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-unicode-regex': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-unicode-sets-regex': 7.23.3(@babel/core@7.23.3) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.3) - babel-plugin-polyfill-corejs2: 0.4.7(@babel/core@7.23.3) - babel-plugin-polyfill-corejs3: 0.8.7(@babel/core@7.23.3) - babel-plugin-polyfill-regenerator: 0.5.4(@babel/core@7.23.3) - core-js-compat: 3.31.1 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.0) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.0) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-syntax-import-attributes': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.0) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-async-generator-functions': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-async-to-generator': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-block-scoped-functions': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-class-static-block': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-classes': 7.23.5(@babel/core@7.24.0) + '@babel/plugin-transform-computed-properties': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-dotall-regex': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-duplicate-keys': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-dynamic-import': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-exponentiation-operator': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-export-namespace-from': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-for-of': 7.23.6(@babel/core@7.24.0) + '@babel/plugin-transform-function-name': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-json-strings': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-literals': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-logical-assignment-operators': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-member-expression-literals': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-modules-amd': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-modules-systemjs': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-modules-umd': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.24.0) + '@babel/plugin-transform-new-target': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-numeric-separator': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-object-rest-spread': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-object-super': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-optional-catch-binding': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-private-property-in-object': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-property-literals': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-regenerator': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-reserved-words': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-sticky-regex': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-typeof-symbol': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-unicode-escapes': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-unicode-property-regex': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-unicode-regex': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-unicode-sets-regex': 7.23.3(@babel/core@7.24.0) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.0) + babel-plugin-polyfill-corejs2: 0.4.7(@babel/core@7.24.0) + babel-plugin-polyfill-corejs3: 0.8.7(@babel/core@7.24.0) + babel-plugin-polyfill-regenerator: 0.5.4(@babel/core@7.24.0) + core-js-compat: 3.34.0 semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /@babel/preset-flow@7.23.3(@babel/core@7.23.3): + /@babel/preset-flow@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-7yn6hl8RIv+KNk6iIrGZ+D06VhVY35wLVf23Cz/mMu1zOr7u4MMP4j0nZ9tLf8+4ZFpnib8cFYgB/oYg9hfswA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-validator-option': 7.22.15 - '@babel/plugin-transform-flow-strip-types': 7.23.3(@babel/core@7.23.3) + '@babel/helper-validator-option': 7.23.5 + '@babel/plugin-transform-flow-strip-types': 7.23.3(@babel/core@7.24.0) dev: true - /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.23.3): + /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.0): resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 esutils: 2.0.3 dev: true - /@babel/preset-typescript@7.23.3(@babel/core@7.23.3): + /@babel/preset-typescript@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-validator-option': 7.22.15 - '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-typescript': 7.23.6(@babel/core@7.23.3) + '@babel/helper-validator-option': 7.23.5 + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-typescript': 7.23.6(@babel/core@7.24.0) dev: true - /@babel/register@7.22.15(@babel/core@7.23.3): + /@babel/register@7.22.15(@babel/core@7.24.0): resolution: {integrity: sha512-V3Q3EqoQdn65RCgTLwauZaTfd1ShhwPmbBv+1dkZV/HpCGMKVyn6oFcRlI7RaKqiDQjX2Qd3AuoEguBgdjIKlg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 clone-deep: 4.0.1 find-cache-dir: 2.1.0 make-dir: 2.1.0 @@ -3230,20 +3434,13 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.13.11 - - /@babel/runtime@7.23.2: - resolution: {integrity: sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.0 - dev: true + dev: false /@babel/runtime@7.23.4: resolution: {integrity: sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==} engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.0 - dev: false /@babel/template@7.22.15: resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} @@ -3251,37 +3448,38 @@ packages: dependencies: '@babel/code-frame': 7.23.4 '@babel/parser': 7.23.4 - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 dev: true - /@babel/traverse@7.23.4: - resolution: {integrity: sha512-IYM8wSUwunWTB6tFC2dkKZhxbIjHoWemdK+3f8/wq8aKhbUscxD5MX72ubd90fxvFknaLPeGw5ycU84V1obHJg==} + /@babel/template@7.24.0: + resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.23.4 - '@babel/generator': 7.23.4 + '@babel/code-frame': 7.23.5 + '@babel/parser': 7.24.0 + '@babel/types': 7.24.0 + dev: true + + /@babel/traverse@7.24.0: + resolution: {integrity: sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.6 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 '@babel/helper-hoist-variables': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.23.4 - '@babel/types': 7.23.4 + '@babel/parser': 7.24.0 + '@babel/types': 7.24.0 debug: 4.3.4(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color dev: true - /@babel/types@7.22.17: - resolution: {integrity: sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.22.5 - '@babel/helper-validator-identifier': 7.22.15 - to-fast-properties: 2.0.0 - - /@babel/types@7.23.4: - resolution: {integrity: sha512-7uIFwVYpoplT5jp/kVv6EF93VaJ8H+Yn5IczYiaAi98ajzjfoZfslet/e0sLh+wVBjb2qqIut1b0S26VSafsSQ==} + /@babel/types@7.24.0: + resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-string-parser': 7.23.4 @@ -3296,102 +3494,66 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true - /@bull-board/api@5.10.2(@bull-board/ui@5.10.2): - resolution: {integrity: sha512-Gx98cqN0cryJB35mVKjYsEnD3NxArWY3Xi2E5Wrr17QTVOzWEP4jyDQ/riiapVdnYqc9RSsxCCmdIaNdNPcXlQ==} + /@bull-board/api@5.14.2(@bull-board/ui@5.14.2): + resolution: {integrity: sha512-0wppAGPU7ZMwWMpzkmtrlmm7ySI5immymyaRS1cVNJ54rUiGOZP5tnm+Sj7MwPdf63rxqIM843un8+PvQyARGg==} peerDependencies: - '@bull-board/ui': 5.10.2 + '@bull-board/ui': 5.14.2 dependencies: - '@bull-board/ui': 5.10.2 + '@bull-board/ui': 5.14.2 redis-info: 3.1.0 dev: false - /@bull-board/fastify@5.10.2: - resolution: {integrity: sha512-NrV1PBu1jwXMBnLslxWLjmt4Qb0oPDSngcUXRll5B8Lvm6E8jtecmnVuNb2X1EtpIGVqhgwlGZ+Q7AC+3ZBMFg==} + /@bull-board/fastify@5.14.2: + resolution: {integrity: sha512-GQMK70tKOu2gjBi2pjWXMXcftzWRvQNSm+deLmGlJUgqUUbNlzIGRyvaTk7giT4CFzgKcP+hT+lphcAsGTKBQw==} dependencies: - '@bull-board/api': 5.10.2(@bull-board/ui@5.10.2) - '@bull-board/ui': 5.10.2 + '@bull-board/api': 5.14.2(@bull-board/ui@5.14.2) + '@bull-board/ui': 5.14.2 '@fastify/static': 6.12.0 '@fastify/view': 8.2.0 ejs: 3.1.9 dev: false - /@bull-board/ui@5.10.2: - resolution: {integrity: sha512-wU9XmrX/COISZ3+sn3VEDB1UtPt7szu4QSKTw1O0q+U1JLM4Kxfs3tH9ZAIulzMrY+CQtkJXd+dKZPuRqy4rfQ==} + /@bull-board/ui@5.14.2: + resolution: {integrity: sha512-NiyKWLjKjy29I6ySCnSYbzGX4ZJyPE4xlS5/Z5dVsF2bJLoAV+yD1obflxteJMt60FiEgLV7tfs6tMSVa+Htew==} dependencies: - '@bull-board/api': 5.10.2(@bull-board/ui@5.10.2) + '@bull-board/api': 5.14.2(@bull-board/ui@5.14.2) dev: false + /@bundled-es-modules/cookie@2.0.0: + resolution: {integrity: sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==} + dependencies: + cookie: 0.5.0 + dev: true + + /@bundled-es-modules/statuses@1.0.1: + resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} + dependencies: + statuses: 2.0.1 + dev: true + /@canvas/image-data@1.0.0: resolution: {integrity: sha512-BxOqI5LgsIQP1odU5KMwV9yoijleOPzHL18/YvNqF9KFSGF2K/DLlYAbDQsWqd/1nbaFuSkYD/191dpMtNh4vw==} dev: false - /@cbor-extract/cbor-extract-darwin-arm64@2.1.1: - resolution: {integrity: sha512-blVBy5MXz6m36Vx0DfLd7PChOQKEs8lK2bD1WJn/vVgG4FXZiZmZb2GECHFvVPA5T7OnODd9xZiL3nMCv6QUhA==} - cpu: [arm64] - os: [darwin] + /@colors/colors@1.5.0: + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} requiresBuild: true - dev: false + dev: true optional: true - /@cbor-extract/cbor-extract-darwin-x64@2.1.1: - resolution: {integrity: sha512-h6KFOzqk8jXTvkOftyRIWGrd7sKQzQv2jVdTL9nKSf3D2drCvQB/LHUxAOpPXo3pv2clDtKs3xnHalpEh3rDsw==} - cpu: [x64] - os: [darwin] - requiresBuild: true + /@cropper/element-canvas@2.0.0-beta.4: + resolution: {integrity: sha512-xL7k5YgtbCLdR/QEj81An4HpPcBTJXf1lq+2xisyHALGeUKQXjA9cJQL7bldYscHAKjmFgNZ5xOMrNaYM++qZw==} + dependencies: + '@cropper/element': 2.0.0-beta.4 + '@cropper/utils': 2.0.0-beta.4 dev: false - optional: true - /@cbor-extract/cbor-extract-linux-arm64@2.1.1: - resolution: {integrity: sha512-SxAaRcYf8S0QHaMc7gvRSiTSr7nUYMqbUdErBEu+HYA4Q6UNydx1VwFE68hGcp1qvxcy9yT5U7gA+a5XikfwSQ==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@cbor-extract/cbor-extract-linux-arm@2.1.1: - resolution: {integrity: sha512-ds0uikdcIGUjPyraV4oJqyVE5gl/qYBpa/Wnh6l6xLE2lj/hwnjT2XcZCChdXwW/YFZ1LUHs6waoYN8PmK0nKQ==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@cbor-extract/cbor-extract-linux-x64@2.1.1: - resolution: {integrity: sha512-GVK+8fNIE9lJQHAlhOROYiI0Yd4bAZ4u++C2ZjlkS3YmO6hi+FUxe6Dqm+OKWTcMpL/l71N6CQAmaRcb4zyJuA==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@cbor-extract/cbor-extract-win32-x64@2.1.1: - resolution: {integrity: sha512-2Niq1C41dCRIDeD8LddiH+mxGlO7HJ612Ll3D/E73ZWBmycued+8ghTr/Ho3CMOWPUEr08XtyBMVXAjqF+TcKw==} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: false - optional: true - - /@colors/colors@1.5.0: - resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} - engines: {node: '>=0.1.90'} - requiresBuild: true - dev: true - optional: true - - /@cropper/element-canvas@2.0.0-beta.4: - resolution: {integrity: sha512-xL7k5YgtbCLdR/QEj81An4HpPcBTJXf1lq+2xisyHALGeUKQXjA9cJQL7bldYscHAKjmFgNZ5xOMrNaYM++qZw==} - dependencies: - '@cropper/element': 2.0.0-beta.4 - '@cropper/utils': 2.0.0-beta.4 - dev: false - - /@cropper/element-crosshair@2.0.0-beta.4: - resolution: {integrity: sha512-NiwIQZFh963i3E3QbXFiU9oNqs+P1cLJur3+e+DK0E3oLTa7rEfcigP/ZoMj/3DZ9Et0LPhKKRDY2SJ8ZszyPA==} - dependencies: - '@cropper/element': 2.0.0-beta.4 - '@cropper/utils': 2.0.0-beta.4 + /@cropper/element-crosshair@2.0.0-beta.4: + resolution: {integrity: sha512-NiwIQZFh963i3E3QbXFiU9oNqs+P1cLJur3+e+DK0E3oLTa7rEfcigP/ZoMj/3DZ9Et0LPhKKRDY2SJ8ZszyPA==} + dependencies: + '@cropper/element': 2.0.0-beta.4 + '@cropper/utils': 2.0.0-beta.4 dev: false /@cropper/element-grid@2.0.0-beta.4: @@ -3526,21 +3688,28 @@ packages: engines: {node: '>=10.0.0'} dev: true - /@emotion/use-insertion-effect-with-fallbacks@1.0.0(react@18.2.0): - resolution: {integrity: sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==} + /@emnapi/runtime@0.45.0: + resolution: {integrity: sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==} + requiresBuild: true + dependencies: + tslib: 2.6.2 + dev: false + optional: true + + /@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.2.0): + resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==} peerDependencies: react: '>=16.8.0' dependencies: react: 18.2.0 dev: true - /@esbuild/android-arm64@0.18.17: - resolution: {integrity: sha512-9np+YYdNDed5+Jgr1TdWBsozZ85U1Oa3xW0c7TWqH0y2aGghXtZsuT8nYRbzOMcl0bXZXjOGbksoTtVOlWrRZg==} + /@esbuild/aix-ppc64@0.19.11: + resolution: {integrity: sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==} engines: {node: '>=12'} - cpu: [arm64] - os: [android] + cpu: [ppc64] + os: [aix] requiresBuild: true - dev: true optional: true /@esbuild/android-arm64@0.18.20: @@ -3552,23 +3721,14 @@ packages: dev: true optional: true - /@esbuild/android-arm64@0.19.9: - resolution: {integrity: sha512-q4cR+6ZD0938R19MyEW3jEsMzbb/1rulLXiNAJQADD/XYp7pT+rOS5JGxvpRW8dFDEfjW4wLgC/3FXIw4zYglQ==} + /@esbuild/android-arm64@0.19.11: + resolution: {integrity: sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==} engines: {node: '>=12'} cpu: [arm64] os: [android] requiresBuild: true optional: true - /@esbuild/android-arm@0.18.17: - resolution: {integrity: sha512-wHsmJG/dnL3OkpAcwbgoBTTMHVi4Uyou3F5mf58ZtmUyIKfcdA7TROav/6tCzET4A3QW2Q2FC+eFneMU+iyOxg==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-arm@0.18.20: resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} engines: {node: '>=12'} @@ -3578,23 +3738,14 @@ packages: dev: true optional: true - /@esbuild/android-arm@0.19.9: - resolution: {integrity: sha512-jkYjjq7SdsWuNI6b5quymW0oC83NN5FdRPuCbs9HZ02mfVdAP8B8eeqLSYU3gb6OJEaY5CQabtTFbqBf26H3GA==} + /@esbuild/android-arm@0.19.11: + resolution: {integrity: sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==} engines: {node: '>=12'} cpu: [arm] os: [android] requiresBuild: true optional: true - /@esbuild/android-x64@0.18.17: - resolution: {integrity: sha512-O+FeWB/+xya0aLg23hHEM2E3hbfwZzjqumKMSIqcHbNvDa+dza2D0yLuymRBQQnC34CWrsJUXyH2MG5VnLd6uw==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-x64@0.18.20: resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} engines: {node: '>=12'} @@ -3604,23 +3755,14 @@ packages: dev: true optional: true - /@esbuild/android-x64@0.19.9: - resolution: {integrity: sha512-KOqoPntWAH6ZxDwx1D6mRntIgZh9KodzgNOy5Ebt9ghzffOk9X2c1sPwtM9P+0eXbefnDhqYfkh5PLP5ULtWFA==} + /@esbuild/android-x64@0.19.11: + resolution: {integrity: sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==} engines: {node: '>=12'} cpu: [x64] os: [android] requiresBuild: true optional: true - /@esbuild/darwin-arm64@0.18.17: - resolution: {integrity: sha512-M9uJ9VSB1oli2BE/dJs3zVr9kcCBBsE883prage1NWz6pBS++1oNn/7soPNS3+1DGj0FrkSvnED4Bmlu1VAE9g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-arm64@0.18.20: resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} engines: {node: '>=12'} @@ -3630,23 +3772,14 @@ packages: dev: true optional: true - /@esbuild/darwin-arm64@0.19.9: - resolution: {integrity: sha512-KBJ9S0AFyLVx2E5D8W0vExqRW01WqRtczUZ8NRu+Pi+87opZn5tL4Y0xT0mA4FtHctd0ZgwNoN639fUUGlNIWw==} + /@esbuild/darwin-arm64@0.19.11: + resolution: {integrity: sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] requiresBuild: true optional: true - /@esbuild/darwin-x64@0.18.17: - resolution: {integrity: sha512-XDre+J5YeIJDMfp3n0279DFNrGCXlxOuGsWIkRb1NThMZ0BsrWXoTg23Jer7fEXQ9Ye5QjrvXpxnhzl3bHtk0g==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-x64@0.18.20: resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} engines: {node: '>=12'} @@ -3656,23 +3789,14 @@ packages: dev: true optional: true - /@esbuild/darwin-x64@0.19.9: - resolution: {integrity: sha512-vE0VotmNTQaTdX0Q9dOHmMTao6ObjyPm58CHZr1UK7qpNleQyxlFlNCaHsHx6Uqv86VgPmR4o2wdNq3dP1qyDQ==} + /@esbuild/darwin-x64@0.19.11: + resolution: {integrity: sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==} engines: {node: '>=12'} cpu: [x64] os: [darwin] requiresBuild: true optional: true - /@esbuild/freebsd-arm64@0.18.17: - resolution: {integrity: sha512-cjTzGa3QlNfERa0+ptykyxs5A6FEUQQF0MuilYXYBGdBxD3vxJcKnzDlhDCa1VAJCmAxed6mYhA2KaJIbtiNuQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-arm64@0.18.20: resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} engines: {node: '>=12'} @@ -3682,23 +3806,14 @@ packages: dev: true optional: true - /@esbuild/freebsd-arm64@0.19.9: - resolution: {integrity: sha512-uFQyd/o1IjiEk3rUHSwUKkqZwqdvuD8GevWF065eqgYfexcVkxh+IJgwTaGZVu59XczZGcN/YMh9uF1fWD8j1g==} + /@esbuild/freebsd-arm64@0.19.11: + resolution: {integrity: sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] requiresBuild: true optional: true - /@esbuild/freebsd-x64@0.18.17: - resolution: {integrity: sha512-sOxEvR8d7V7Kw8QqzxWc7bFfnWnGdaFBut1dRUYtu+EIRXefBc/eIsiUiShnW0hM3FmQ5Zf27suDuHsKgZ5QrA==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-x64@0.18.20: resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} engines: {node: '>=12'} @@ -3708,23 +3823,14 @@ packages: dev: true optional: true - /@esbuild/freebsd-x64@0.19.9: - resolution: {integrity: sha512-WMLgWAtkdTbTu1AWacY7uoj/YtHthgqrqhf1OaEWnZb7PQgpt8eaA/F3LkV0E6K/Lc0cUr/uaVP/49iE4M4asA==} + /@esbuild/freebsd-x64@0.19.11: + resolution: {integrity: sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] requiresBuild: true optional: true - /@esbuild/linux-arm64@0.18.17: - resolution: {integrity: sha512-c9w3tE7qA3CYWjT+M3BMbwMt+0JYOp3vCMKgVBrCl1nwjAlOMYzEo+gG7QaZ9AtqZFj5MbUc885wuBBmu6aADQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm64@0.18.20: resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} engines: {node: '>=12'} @@ -3734,23 +3840,14 @@ packages: dev: true optional: true - /@esbuild/linux-arm64@0.19.9: - resolution: {integrity: sha512-PiPblfe1BjK7WDAKR1Cr9O7VVPqVNpwFcPWgfn4xu0eMemzRp442hXyzF/fSwgrufI66FpHOEJk0yYdPInsmyQ==} + /@esbuild/linux-arm64@0.19.11: + resolution: {integrity: sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==} engines: {node: '>=12'} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-arm@0.18.17: - resolution: {integrity: sha512-2d3Lw6wkwgSLC2fIvXKoMNGVaeY8qdN0IC3rfuVxJp89CRfA3e3VqWifGDfuakPmp90+ZirmTfye1n4ncjv2lg==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm@0.18.20: resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} engines: {node: '>=12'} @@ -3760,23 +3857,14 @@ packages: dev: true optional: true - /@esbuild/linux-arm@0.19.9: - resolution: {integrity: sha512-C/ChPohUYoyUaqn1h17m/6yt6OB14hbXvT8EgM1ZWaiiTYz7nWZR0SYmMnB5BzQA4GXl3BgBO1l8MYqL/He3qw==} + /@esbuild/linux-arm@0.19.11: + resolution: {integrity: sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==} engines: {node: '>=12'} cpu: [arm] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-ia32@0.18.17: - resolution: {integrity: sha512-1DS9F966pn5pPnqXYz16dQqWIB0dmDfAQZd6jSSpiT9eX1NzKh07J6VKR3AoXXXEk6CqZMojiVDSZi1SlmKVdg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ia32@0.18.20: resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} engines: {node: '>=12'} @@ -3786,23 +3874,14 @@ packages: dev: true optional: true - /@esbuild/linux-ia32@0.19.9: - resolution: {integrity: sha512-f37i/0zE0MjDxijkPSQw1CO/7C27Eojqb+r3BbHVxMLkj8GCa78TrBZzvPyA/FNLUMzP3eyHCVkAopkKVja+6Q==} + /@esbuild/linux-ia32@0.19.11: + resolution: {integrity: sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==} engines: {node: '>=12'} cpu: [ia32] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-loong64@0.18.17: - resolution: {integrity: sha512-EvLsxCk6ZF0fpCB6w6eOI2Fc8KW5N6sHlIovNe8uOFObL2O+Mr0bflPHyHwLT6rwMg9r77WOAWb2FqCQrVnwFg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-loong64@0.18.20: resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} engines: {node: '>=12'} @@ -3812,23 +3891,14 @@ packages: dev: true optional: true - /@esbuild/linux-loong64@0.19.9: - resolution: {integrity: sha512-t6mN147pUIf3t6wUt3FeumoOTPfmv9Cc6DQlsVBpB7eCpLOqQDyWBP1ymXn1lDw4fNUSb/gBcKAmvTP49oIkaA==} + /@esbuild/linux-loong64@0.19.11: + resolution: {integrity: sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==} engines: {node: '>=12'} cpu: [loong64] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-mips64el@0.18.17: - resolution: {integrity: sha512-e0bIdHA5p6l+lwqTE36NAW5hHtw2tNRmHlGBygZC14QObsA3bD4C6sXLJjvnDIjSKhW1/0S3eDy+QmX/uZWEYQ==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-mips64el@0.18.20: resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} engines: {node: '>=12'} @@ -3838,23 +3908,14 @@ packages: dev: true optional: true - /@esbuild/linux-mips64el@0.19.9: - resolution: {integrity: sha512-jg9fujJTNTQBuDXdmAg1eeJUL4Jds7BklOTkkH80ZgQIoCTdQrDaHYgbFZyeTq8zbY+axgptncko3v9p5hLZtw==} + /@esbuild/linux-mips64el@0.19.11: + resolution: {integrity: sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-ppc64@0.18.17: - resolution: {integrity: sha512-BAAilJ0M5O2uMxHYGjFKn4nJKF6fNCdP1E0o5t5fvMYYzeIqy2JdAP88Az5LHt9qBoUa4tDaRpfWt21ep5/WqQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ppc64@0.18.20: resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} engines: {node: '>=12'} @@ -3864,23 +3925,14 @@ packages: dev: true optional: true - /@esbuild/linux-ppc64@0.19.9: - resolution: {integrity: sha512-tkV0xUX0pUUgY4ha7z5BbDS85uI7ABw3V1d0RNTii7E9lbmV8Z37Pup2tsLV46SQWzjOeyDi1Q7Wx2+QM8WaCQ==} + /@esbuild/linux-ppc64@0.19.11: + resolution: {integrity: sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-riscv64@0.18.17: - resolution: {integrity: sha512-Wh/HW2MPnC3b8BqRSIme/9Zhab36PPH+3zam5pqGRH4pE+4xTrVLx2+XdGp6fVS3L2x+DrsIcsbMleex8fbE6g==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-riscv64@0.18.20: resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} engines: {node: '>=12'} @@ -3890,23 +3942,14 @@ packages: dev: true optional: true - /@esbuild/linux-riscv64@0.19.9: - resolution: {integrity: sha512-DfLp8dj91cufgPZDXr9p3FoR++m3ZJ6uIXsXrIvJdOjXVREtXuQCjfMfvmc3LScAVmLjcfloyVtpn43D56JFHg==} + /@esbuild/linux-riscv64@0.19.11: + resolution: {integrity: sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-s390x@0.18.17: - resolution: {integrity: sha512-j/34jAl3ul3PNcK3pfI0NSlBANduT2UO5kZ7FCaK33XFv3chDhICLY8wJJWIhiQ+YNdQ9dxqQctRg2bvrMlYgg==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-s390x@0.18.20: resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} engines: {node: '>=12'} @@ -3916,23 +3959,14 @@ packages: dev: true optional: true - /@esbuild/linux-s390x@0.19.9: - resolution: {integrity: sha512-zHbglfEdC88KMgCWpOl/zc6dDYJvWGLiUtmPRsr1OgCViu3z5GncvNVdf+6/56O2Ca8jUU+t1BW261V6kp8qdw==} + /@esbuild/linux-s390x@0.19.11: + resolution: {integrity: sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==} engines: {node: '>=12'} cpu: [s390x] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-x64@0.18.17: - resolution: {integrity: sha512-QM50vJ/y+8I60qEmFxMoxIx4de03pGo2HwxdBeFd4nMh364X6TIBZ6VQ5UQmPbQWUVWHWws5MmJXlHAXvJEmpQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-x64@0.18.20: resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} engines: {node: '>=12'} @@ -3942,23 +3976,14 @@ packages: dev: true optional: true - /@esbuild/linux-x64@0.19.9: - resolution: {integrity: sha512-JUjpystGFFmNrEHQnIVG8hKwvA2DN5o7RqiO1CVX8EN/F/gkCjkUMgVn6hzScpwnJtl2mPR6I9XV1oW8k9O+0A==} + /@esbuild/linux-x64@0.19.11: + resolution: {integrity: sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==} engines: {node: '>=12'} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@esbuild/netbsd-x64@0.18.17: - resolution: {integrity: sha512-/jGlhWR7Sj9JPZHzXyyMZ1RFMkNPjC6QIAan0sDOtIo2TYk3tZn5UDrkE0XgsTQCxWTTOcMPf9p6Rh2hXtl5TQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/netbsd-x64@0.18.20: resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} engines: {node: '>=12'} @@ -3968,23 +3993,14 @@ packages: dev: true optional: true - /@esbuild/netbsd-x64@0.19.9: - resolution: {integrity: sha512-GThgZPAwOBOsheA2RUlW5UeroRfESwMq/guy8uEe3wJlAOjpOXuSevLRd70NZ37ZrpO6RHGHgEHvPg1h3S1Jug==} + /@esbuild/netbsd-x64@0.19.11: + resolution: {integrity: sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] requiresBuild: true optional: true - /@esbuild/openbsd-x64@0.18.17: - resolution: {integrity: sha512-rSEeYaGgyGGf4qZM2NonMhMOP/5EHp4u9ehFiBrg7stH6BYEEjlkVREuDEcQ0LfIl53OXLxNbfuIj7mr5m29TA==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/openbsd-x64@0.18.20: resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} engines: {node: '>=12'} @@ -3994,23 +4010,14 @@ packages: dev: true optional: true - /@esbuild/openbsd-x64@0.19.9: - resolution: {integrity: sha512-Ki6PlzppaFVbLnD8PtlVQfsYw4S9n3eQl87cqgeIw+O3sRr9IghpfSKY62mggdt1yCSZ8QWvTZ9jo9fjDSg9uw==} + /@esbuild/openbsd-x64@0.19.11: + resolution: {integrity: sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] requiresBuild: true optional: true - /@esbuild/sunos-x64@0.18.17: - resolution: {integrity: sha512-Y7ZBbkLqlSgn4+zot4KUNYst0bFoO68tRgI6mY2FIM+b7ZbyNVtNbDP5y8qlu4/knZZ73fgJDlXID+ohY5zt5g==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true - optional: true - /@esbuild/sunos-x64@0.18.20: resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} engines: {node: '>=12'} @@ -4020,23 +4027,14 @@ packages: dev: true optional: true - /@esbuild/sunos-x64@0.19.9: - resolution: {integrity: sha512-MLHj7k9hWh4y1ddkBpvRj2b9NCBhfgBt3VpWbHQnXRedVun/hC7sIyTGDGTfsGuXo4ebik2+3ShjcPbhtFwWDw==} + /@esbuild/sunos-x64@0.19.11: + resolution: {integrity: sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==} engines: {node: '>=12'} cpu: [x64] os: [sunos] requiresBuild: true optional: true - /@esbuild/win32-arm64@0.18.17: - resolution: {integrity: sha512-bwPmTJsEQcbZk26oYpc4c/8PvTY3J5/QK8jM19DVlEsAB41M39aWovWoHtNm78sd6ip6prilxeHosPADXtEJFw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-arm64@0.18.20: resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} engines: {node: '>=12'} @@ -4046,23 +4044,14 @@ packages: dev: true optional: true - /@esbuild/win32-arm64@0.19.9: - resolution: {integrity: sha512-GQoa6OrQ8G08guMFgeXPH7yE/8Dt0IfOGWJSfSH4uafwdC7rWwrfE6P9N8AtPGIjUzdo2+7bN8Xo3qC578olhg==} + /@esbuild/win32-arm64@0.19.11: + resolution: {integrity: sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==} engines: {node: '>=12'} cpu: [arm64] os: [win32] requiresBuild: true optional: true - /@esbuild/win32-ia32@0.18.17: - resolution: {integrity: sha512-H/XaPtPKli2MhW+3CQueo6Ni3Avggi6hP/YvgkEe1aSaxw+AeO8MFjq8DlgfTd9Iz4Yih3QCZI6YLMoyccnPRg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-ia32@0.18.20: resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} engines: {node: '>=12'} @@ -4072,23 +4061,14 @@ packages: dev: true optional: true - /@esbuild/win32-ia32@0.19.9: - resolution: {integrity: sha512-UOozV7Ntykvr5tSOlGCrqU3NBr3d8JqPes0QWN2WOXfvkWVGRajC+Ym0/Wj88fUgecUCLDdJPDF0Nna2UK3Qtg==} + /@esbuild/win32-ia32@0.19.11: + resolution: {integrity: sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==} engines: {node: '>=12'} cpu: [ia32] os: [win32] requiresBuild: true optional: true - /@esbuild/win32-x64@0.18.17: - resolution: {integrity: sha512-fGEb8f2BSA3CW7riJVurug65ACLuQAzKq0SSqkY2b2yHHH0MzDfbLyKIGzHwOI/gkHcxM/leuSW6D5w/LMNitA==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-x64@0.18.20: resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} engines: {node: '>=12'} @@ -4098,8 +4078,8 @@ packages: dev: true optional: true - /@esbuild/win32-x64@0.19.9: - resolution: {integrity: sha512-oxoQgglOP7RH6iasDrhY+R/3cHrfwIDvRlT4CGChflq6twk8iENeVvMJjmvBb94Ik1Z+93iGO27err7w6l54GQ==} + /@esbuild/win32-x64@0.19.11: + resolution: {integrity: sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==} engines: {node: '>=12'} cpu: [x64] os: [win32] @@ -4126,13 +4106,13 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.56.0 + eslint: 8.57.0 eslint-visitor-keys: 3.4.3 dev: true @@ -4185,8 +4165,8 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@eslint/js@8.56.0: - resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==} + /@eslint/js@8.57.0: + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true @@ -4226,8 +4206,8 @@ packages: engines: {node: '>=14'} dev: true - /@fastify/cookie@9.2.0: - resolution: {integrity: sha512-fkg1yjjQRHPFAxSHeLC8CqYuNzvR6Lwlj/KjrzQcGjNBK+K82nW+UfCjfN71g1GkoVoc1GTOgIWkFJpcMfMkHQ==} + /@fastify/cookie@9.3.1: + resolution: {integrity: sha512-h1NAEhB266+ZbZ0e9qUE6NnNR07i7DnNXWG9VbbZ8uC6O/hxHpl+Zoe5sw1yfdZ2U6XhToUGDnzQtWJdCaPwfg==} dependencies: cookie-signature: 1.2.1 fastify-plugin: 4.5.0 @@ -4244,10 +4224,6 @@ packages: resolution: {integrity: sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==} dev: false - /@fastify/error@3.2.0: - resolution: {integrity: sha512-KAfcLa+CnknwVi5fWogrLXgidLic+GXnLjijXdpl8pvkvbXU5BGa37iZO9FGvsh9ZL4y+oFi5cbHBm5UOG+dmQ==} - dev: false - /@fastify/error@3.4.0: resolution: {integrity: sha512-e/mafFwbK3MNqxUcFBLgHhgxsF8UT1m8aj0dAlqEa2nJEgPsRtpHTZ3ObgrgkZ2M1eJHPTwgyUl/tXkvabsZdQ==} dev: false @@ -4273,25 +4249,21 @@ packages: '@fastify/reply-from': 9.0.1 fast-querystring: 1.1.2 fastify-plugin: 4.5.0 - ws: 8.15.1(bufferutil@4.0.7)(utf-8-validate@6.0.3) + ws: 8.16.0(bufferutil@4.0.7)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - utf-8-validate dev: false - /@fastify/multipart@8.0.0: - resolution: {integrity: sha512-xaH1pGIqYnIJjYs5qG6ryhPSFnWuJIfSXYqEUtzmcyREkMk0SwONd2y+SZ9JXfDmETAC/Ogtc/SRbz+AjZhCkw==} + /@fastify/multipart@8.1.0: + resolution: {integrity: sha512-sRX9X4ZhAqRbe2kDvXY2NK7i6Wf1Rm2g/CjpGYYM7+Np8E6uWQXcj761j08qPfPO8PJXM+vJ7yrKbK1GPB+OeQ==} dependencies: '@fastify/busboy': 1.1.0 '@fastify/deepmerge': 1.3.0 - '@fastify/error': 3.2.0 - '@fastify/swagger': 8.6.0 - '@fastify/swagger-ui': 1.9.0 + '@fastify/error': 3.4.0 fastify-plugin: 4.5.0 secure-json-parse: 2.7.0 stream-wormhole: 1.1.0 - transitivePeerDependencies: - - supports-color dev: false /@fastify/reply-from@9.0.1: @@ -4327,28 +4299,6 @@ packages: p-limit: 3.1.0 dev: false - /@fastify/swagger-ui@1.9.0: - resolution: {integrity: sha512-7RTq2bI2cg4k6WsY69k8MZ8GnH6VUSbczJGnTotUKH+fOY9Cg3y8NEvPUREfwRzguI+3N+v8gp6H0UAohayldA==} - dependencies: - '@fastify/static': 6.12.0 - fastify-plugin: 4.5.0 - openapi-types: 12.1.3 - rfdc: 1.3.0 - yaml: 2.3.1 - dev: false - - /@fastify/swagger@8.6.0: - resolution: {integrity: sha512-PGde7ryn0nsX/BpSrjP4Ade8RK2M0uBIU4Iow3Qt3kWa/70p1fM7AW28kS3dKERnwT0VwrUdxU3ftrRI+DsNTw==} - dependencies: - fastify-plugin: 4.5.0 - json-schema-resolver: 2.0.0 - openapi-types: 12.1.3 - rfdc: 1.3.0 - yaml: 2.3.1 - transitivePeerDependencies: - - supports-color - dev: false - /@fastify/view@8.2.0: resolution: {integrity: sha512-hBSiBofCnJNlPHEMZWpO1SL84eqOaqujJ1hR3jntFyZZCkweH5jMs12DKYyGesjVll7SJFRRxPUBB8kmUmneRQ==} dependencies: @@ -4356,34 +4306,6 @@ packages: hashlru: 2.3.0 dev: false - /@floating-ui/core@1.4.1: - resolution: {integrity: sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==} - dependencies: - '@floating-ui/utils': 0.1.1 - dev: true - - /@floating-ui/dom@1.5.1: - resolution: {integrity: sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw==} - dependencies: - '@floating-ui/core': 1.4.1 - '@floating-ui/utils': 0.1.1 - dev: true - - /@floating-ui/react-dom@2.0.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-5qhlDvjaLmAst/rKb3VdlCinwTF4EYMiVxuuc/HVUjs46W0zgtbMmAZ1UTsDrRTxRmUEzl92mOtWbeeXL26lSQ==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - dependencies: - '@floating-ui/dom': 1.5.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@floating-ui/utils@0.1.1: - resolution: {integrity: sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==} - dev: true - /@github/webauthn-json@2.1.1: resolution: {integrity: sha512-XrftRn4z75SnaJOmZQbt7Mk+IIjqVHw+glDGOxuHwXkZBZh/MBoRS7MHjSZMDaLhT4RjN2VqiEU7EOYleuJWSQ==} hasBin: true @@ -4440,25 +4362,233 @@ packages: - supports-color dev: true + /@humanwhocodes/config-array@0.11.14: + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 2.0.2 + debug: 4.3.4(supports-color@8.1.1) + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + /@humanwhocodes/module-importer@1.0.1: resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} dev: true + /@humanwhocodes/momoa@2.0.4: + resolution: {integrity: sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==} + engines: {node: '>=10.10.0'} + dev: true + /@humanwhocodes/object-schema@2.0.1: resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} dev: true - /@ioredis/commands@1.2.0: - resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} + /@humanwhocodes/object-schema@2.0.2: + resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} + dev: true + + /@img/sharp-darwin-arm64@0.33.2: + resolution: {integrity: sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.1 dev: false + optional: true - /@isaacs/cliui@8.0.2: - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - dependencies: - string-width: 5.1.2 - string-width-cjs: /string-width@4.2.3 + /@img/sharp-darwin-x64@0.33.2: + resolution: {integrity: sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [darwin] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.1 + dev: false + optional: true + + /@img/sharp-libvips-darwin-arm64@1.0.1: + resolution: {integrity: sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==} + engines: {macos: '>=11', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-darwin-x64@1.0.1: + resolution: {integrity: sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==} + engines: {macos: '>=10.13', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-arm64@1.0.1: + resolution: {integrity: sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==} + engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-arm@1.0.1: + resolution: {integrity: sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==} + engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-s390x@1.0.1: + resolution: {integrity: sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==} + engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-x64@1.0.1: + resolution: {integrity: sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==} + engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linuxmusl-arm64@1.0.1: + resolution: {integrity: sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==} + engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linuxmusl-x64@1.0.1: + resolution: {integrity: sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==} + engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-linux-arm64@0.33.2: + resolution: {integrity: sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.1 + dev: false + optional: true + + /@img/sharp-linux-arm@0.33.2: + resolution: {integrity: sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==} + engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.1 + dev: false + optional: true + + /@img/sharp-linux-s390x@0.33.2: + resolution: {integrity: sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==} + engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [s390x] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.1 + dev: false + optional: true + + /@img/sharp-linux-x64@0.33.2: + resolution: {integrity: sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.1 + dev: false + optional: true + + /@img/sharp-linuxmusl-arm64@0.33.2: + resolution: {integrity: sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==} + engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.1 + dev: false + optional: true + + /@img/sharp-linuxmusl-x64@0.33.2: + resolution: {integrity: sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==} + engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.1 + dev: false + optional: true + + /@img/sharp-wasm32@0.33.2: + resolution: {integrity: sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [wasm32] + requiresBuild: true + dependencies: + '@emnapi/runtime': 0.45.0 + dev: false + optional: true + + /@img/sharp-win32-ia32@0.33.2: + resolution: {integrity: sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-win32-x64@0.33.2: + resolution: {integrity: sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@ioredis/commands@1.2.0: + resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} + dev: false + + /@isaacs/cliui@8.0.2: + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 strip-ansi: 7.1.0 strip-ansi-cjs: /strip-ansi@6.0.1 wrap-ansi: 8.1.0 @@ -4485,7 +4615,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.10.5 + '@types/node': 20.11.22 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -4506,14 +4636,14 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.10.5 + '@types/node': 20.11.22 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.10.5) + jest-config: 29.7.0(@types/node@20.11.22) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -4535,11 +4665,11 @@ packages: - ts-node dev: true - /@jest/create-cache-key-function@27.5.1: - resolution: {integrity: sha512-dmH1yW+makpTSURTy8VzdUwFnfQh1G8R+DxO2Ho2FFmBbKFEVm+3jWdvFhE2VqB/LATCTokkP0dotjyQyw5/AQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /@jest/create-cache-key-function@29.7.0: + resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 27.5.1 + '@jest/types': 29.6.3 dev: true /@jest/environment@29.7.0: @@ -4548,7 +4678,7 @@ packages: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.10.5 + '@types/node': 20.11.22 jest-mock: 29.7.0 dev: true @@ -4574,7 +4704,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.10.5 + '@types/node': 20.11.22 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -4607,7 +4737,7 @@ packages: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.20 - '@types/node': 20.10.5 + '@types/node': 20.11.22 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -4629,13 +4759,6 @@ packages: - supports-color dev: true - /@jest/schemas@28.1.3: - resolution: {integrity: sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@sinclair/typebox': 0.24.51 - dev: true - /@jest/schemas@29.6.3: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4675,7 +4798,7 @@ packages: resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.20 babel-plugin-istanbul: 6.1.1 @@ -4698,9 +4821,9 @@ packages: resolution: {integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 20.10.5 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 20.11.22 '@types/yargs': 16.0.5 chalk: 4.1.2 dev: true @@ -4712,11 +4835,11 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.10.5 + '@types/node': 20.11.22 '@types/yargs': 17.0.32 chalk: 4.1.2 - /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.3)(vite@5.0.10): + /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.3)(vite@5.1.4): resolution: {integrity: sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==} peerDependencies: typescript: '>= 4.3.x' @@ -4730,16 +4853,7 @@ packages: magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.3.3) typescript: 5.3.3 - vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0) - dev: true - - /@jridgewell/gen-mapping@0.3.2: - resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.18 + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) dev: true /@jridgewell/gen-mapping@0.3.3: @@ -4750,11 +4864,6 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/trace-mapping': 0.3.20 - /@jridgewell/resolve-uri@3.1.0: - resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} - engines: {node: '>=6.0.0'} - dev: true - /@jridgewell/resolve-uri@3.1.1: resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} engines: {node: '>=6.0.0'} @@ -4769,20 +4878,9 @@ packages: '@jridgewell/gen-mapping': 0.3.3 '@jridgewell/trace-mapping': 0.3.20 - /@jridgewell/sourcemap-codec@1.4.14: - resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} - dev: true - /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - /@jridgewell/trace-mapping@0.3.18: - resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} - dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 - dev: true - /@jridgewell/trace-mapping@0.3.20: resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} dependencies: @@ -4793,45 +4891,37 @@ packages: resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} dev: true - /@juggle/resize-observer@3.4.0: - resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} - dev: true - /@kurkle/color@0.3.2: resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==} dev: false + /@levischuck/tiny-cbor@0.2.2: + resolution: {integrity: sha512-f5CnPw997Y2GQ8FAvtuVVC19FX8mwNNC+1XJcIi16n/LTJifKO6QBgGLgN3YEmqtGMk17SKSuoWES3imJVxAVw==} + dev: false + /@lukeed/csprng@1.0.1: resolution: {integrity: sha512-uSvJdwQU5nK+Vdf6zxcWAY2A8r7uqe+gePwLWzJ+fsQehq18pc0I2hJKwypZ2aLM90+Er9u1xn4iLJPZ+xlL4g==} engines: {node: '>=8'} - dev: false /@lukeed/ms@2.0.1: resolution: {integrity: sha512-Xs/4RZltsAL7pkvaNStUQt7netTkyxrS0K+RILcVr3TRMS/ToOg4I6uNfhB9SlGsnWBym4U+EaXq0f0cEMNkHA==} engines: {node: '>=8'} dev: false - /@mapbox/node-pre-gyp@1.0.11: - resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} - hasBin: true + /@mcaptcha/core-glue@0.1.0-alpha-5: + resolution: {integrity: sha512-16qWm5O5X0Y9LXULULaAks8Vf9FNlUUBcR5KDt49aWhFhG5++JzxNmCwQM9EJSHNU7y0U+FdyAWcGmjfKlkRLA==} + dev: false + + /@mcaptcha/vanilla-glue@0.1.0-alpha-3: + resolution: {integrity: sha512-GT6TJBgmViGXcXiT5VOr+h/6iOnThSlZuCoOWncubyTZU9R3cgU5vWPkF7G6Ob6ee2CBe3yqBxxk24CFVGTVXw==} dependencies: - detect-libc: 2.0.2 - https-proxy-agent: 5.0.1 - make-dir: 3.1.0 - node-fetch: 2.7.0 - nopt: 5.0.0 - npmlog: 5.0.1 - rimraf: 3.0.2 - semver: 7.5.4 - tar: 6.1.13 - transitivePeerDependencies: - - encoding - - supports-color + '@mcaptcha/core-glue': 0.1.0-alpha-5 dev: false - /@mdx-js/react@2.3.0(react@18.2.0): - resolution: {integrity: sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==} + /@mdx-js/react@3.0.1(@types/react@18.0.28)(react@18.2.0): + resolution: {integrity: sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==} peerDependencies: + '@types/react': '>=16' react: '>=16' dependencies: '@types/mdx': 2.0.3 @@ -4839,24 +4929,24 @@ packages: react: 18.2.0 dev: true - /@microsoft/api-extractor-model@7.28.3(@types/node@20.10.5): - resolution: {integrity: sha512-wT/kB2oDbdZXITyDh2SQLzaWwTOFbV326fP0pUwNW00WeliARs0qjmXBWmGWardEzp2U3/axkO3Lboqun6vrig==} + /@microsoft/api-extractor-model@7.28.4(@types/node@20.11.22): + resolution: {integrity: sha512-vucgyPmgHrJ/D4/xQywAmjTmSfxAx2/aDmD6TkIoLu51FdsAfuWRbijWA48AePy60OO+l+mmy9p2P/CEeBZqig==} dependencies: '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.62.0(@types/node@20.10.5) + '@rushstack/node-core-library': 3.63.0(@types/node@20.11.22) transitivePeerDependencies: - '@types/node' dev: true - /@microsoft/api-extractor@7.38.5(@types/node@20.10.5): - resolution: {integrity: sha512-c/w2zfqBcBJxaCzpJNvFoouWewcYrUOfeu5ZkWCCIXTF9a/gXM85RGevEzlMAIEGM/kssAAZSXRJIZ3Q5vLFow==} + /@microsoft/api-extractor@7.39.1(@types/node@20.11.22): + resolution: {integrity: sha512-V0HtCufWa8hZZvSmlEzQZfINcJkHAU/bmpyJQj6w+zpI87EkR8DuBOW6RWrO9c7mUYFZoDaNgUTyKo83ytv+QQ==} hasBin: true dependencies: - '@microsoft/api-extractor-model': 7.28.3(@types/node@20.10.5) + '@microsoft/api-extractor-model': 7.28.4(@types/node@20.11.22) '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.62.0(@types/node@20.10.5) + '@rushstack/node-core-library': 3.63.0(@types/node@20.11.22) '@rushstack/rig-package': 0.5.1 '@rushstack/ts-command-line': 4.17.1 colors: 1.2.5 @@ -4864,7 +4954,7 @@ packages: resolve: 1.22.8 semver: 7.5.4 source-map: 0.6.1 - typescript: 5.0.4 + typescript: 5.3.3 transitivePeerDependencies: - '@types/node' dev: true @@ -4882,6 +4972,72 @@ packages: resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} dev: true + /@misskey-dev/browser-image-resizer@2024.1.0: + resolution: {integrity: sha512-4EnO0zLW5NDtng3Gaz5MuT761uiuoOuplwX18wBqgj8w56LTU5BjLn/vbHwDIIe0j2gwqDYhMb7bDjmr1/Fomg==} + dev: false + + /@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@6.11.0)(@typescript-eslint/parser@6.11.0)(eslint-plugin-import@2.29.1)(eslint@8.53.0): + resolution: {integrity: sha512-dh6UbcrNDVg5DD8k8Qh4ab30OPpuEYIlJCqaBV/lkIV8wNN/AfCJ2V7iTP8V8KjryM4t+sf5IqzQLQnT0mWI4A==} + peerDependencies: + '@typescript-eslint/eslint-plugin': '>= 6' + '@typescript-eslint/parser': '>= 6' + eslint: '>= 3' + eslint-plugin-import: '>= 2' + dependencies: + '@typescript-eslint/eslint-plugin': 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.3) + eslint: 8.53.0 + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.11.0)(eslint@8.53.0) + dev: true + + /@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@6.21.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0): + resolution: {integrity: sha512-dh6UbcrNDVg5DD8k8Qh4ab30OPpuEYIlJCqaBV/lkIV8wNN/AfCJ2V7iTP8V8KjryM4t+sf5IqzQLQnT0mWI4A==} + peerDependencies: + '@typescript-eslint/eslint-plugin': '>= 6' + '@typescript-eslint/parser': '>= 6' + eslint: '>= 3' + eslint-plugin-import: '>= 2' + dependencies: + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3) + eslint: 8.57.0 + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0) + dev: true + + /@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0): + resolution: {integrity: sha512-dh6UbcrNDVg5DD8k8Qh4ab30OPpuEYIlJCqaBV/lkIV8wNN/AfCJ2V7iTP8V8KjryM4t+sf5IqzQLQnT0mWI4A==} + peerDependencies: + '@typescript-eslint/eslint-plugin': '>= 6' + '@typescript-eslint/parser': '>= 6' + eslint: '>= 3' + eslint-plugin-import: '>= 2' + dependencies: + '@typescript-eslint/eslint-plugin': 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3) + eslint: 8.57.0 + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0) + dev: true + + /@misskey-dev/sharp-read-bmp@1.2.0: + resolution: {integrity: sha512-er4pRakXzHYfEgOFAFfQagqDouG+wLm+kwNq1I30oSdIHDa0wM3KjFpfIGQ25Fks4GcmOl1s7Zh6xoQu5dNjTw==} + dependencies: + decode-bmp: 0.2.1 + decode-ico: 0.4.1 + sharp: 0.33.2 + dev: false + + /@misskey-dev/summaly@5.0.3: + resolution: {integrity: sha512-jVkuLEDrq2FaeHL8VY51LTqB6j0Jv5L7s0nmKGKMnE0jPBpSj6flswnZgntGmz5mbdCj47utEqu8FY43kH7PVg==} + dependencies: + cheerio: 1.0.0-rc.12 + escape-regexp: 0.0.1 + got: 12.6.1 + html-entities: 2.3.2 + iconv-lite: 0.6.3 + jschardet: 3.0.0 + private-ip: 2.3.3 + trace-redirect: 1.0.6 + /@mole-inc/bin-wrapper@8.0.1: resolution: {integrity: sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -4944,28 +5100,21 @@ packages: dev: false optional: true - /@mswjs/cookies@0.2.2: - resolution: {integrity: sha512-mlN83YSrcFgk7Dm1Mys40DLssI1KdJji2CMKN8eOlBqsTADYzj2+jWzsANsUTFbxDMWPD5e9bfA1RGqBpS3O1g==} - engines: {node: '>=14'} - dependencies: - '@types/set-cookie-parser': 2.4.3 - set-cookie-parser: 2.6.0 + /@mswjs/cookies@1.1.0: + resolution: {integrity: sha512-0ZcCVQxifZmhwNBoQIrystCb+2sWBY2Zw8lpfJBPCHGCA/HWqehITeCRVIv4VMy8MPlaHo2w2pTHFV2pFfqKPw==} + engines: {node: '>=18'} dev: true - /@mswjs/interceptors@0.17.10: - resolution: {integrity: sha512-N8x7eSLGcmUFNWZRxT1vsHvypzIRgQYdG0rJey/rZCy6zT/30qDt8Joj7FxzGNLSwXbeZqJOMqDurp7ra4hgbw==} - engines: {node: '>=14'} + /@mswjs/interceptors@0.25.16: + resolution: {integrity: sha512-8QC8JyKztvoGAdPgyZy49c9vSHHAZjHagwl4RY9E8carULk8ym3iTaiawrT1YoLF/qb449h48f71XDPgkUSOUg==} + engines: {node: '>=18'} dependencies: - '@open-draft/until': 1.0.3 - '@types/debug': 4.1.7 - '@xmldom/xmldom': 0.8.6 - debug: 4.3.4(supports-color@8.1.1) - headers-polyfill: 3.2.5 - outvariant: 1.4.0 - strict-event-emitter: 0.2.8 - web-encoding: 1.1.5 - transitivePeerDependencies: - - supports-color + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.2 + strict-event-emitter: 0.5.1 dev: true /@ndelangen/get-tarball@3.0.7: @@ -4976,12 +5125,12 @@ packages: tar-fs: 2.1.1 dev: true - /@nestjs/common@10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1): - resolution: {integrity: sha512-fwAk931rjW8CNH2Mgwawq/7HWHH1dxkOLdcgs7U52ddLk8CtHXjejm1cbNahewlSbNhvlOl7y1STLHutE6sUqw==} + /@nestjs/common@10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1): + resolution: {integrity: sha512-LAkTe8/CF0uNWM0ecuDwUNTHCi1lVSITmmR4FQ6Ftz1E7ujQCnJ5pMRzd8JRN14vdBkxZZ8VbVF0BDUKoKNxMQ==} peerDependencies: class-transformer: '*' class-validator: '*' - reflect-metadata: ^0.1.12 + reflect-metadata: ^0.1.12 || ^0.2.0 rxjs: ^7.1.0 peerDependenciesMeta: class-transformer: @@ -4990,21 +5139,20 @@ packages: optional: true dependencies: iterare: 1.2.1 - reflect-metadata: 0.1.14 + reflect-metadata: 0.2.1 rxjs: 7.8.1 tslib: 2.6.2 uid: 2.0.2 - dev: false - /@nestjs/core@10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.14)(rxjs@7.8.1): - resolution: {integrity: sha512-+ckOI6BPi2ZMHikT9MCG4ctHDc4OnjhoIytrn7f2AYMMXI4bnutJhqyQKc30VDka5x3Wq6QAD57pgSP7y+JjJg==} + /@nestjs/core@10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1): + resolution: {integrity: sha512-kxJWggQAPX3RuZx9JVec69eSLaYLNIox2emkZJpfBJ5Qq7cAq7edQIt1r4LGjTKq6kFubNTPsqhWf5y7yFRBPw==} requiresBuild: true peerDependencies: '@nestjs/common': ^10.0.0 '@nestjs/microservices': ^10.0.0 '@nestjs/platform-express': ^10.0.0 '@nestjs/websockets': ^10.0.0 - reflect-metadata: ^0.1.12 + reflect-metadata: ^0.1.12 || ^0.2.0 rxjs: ^7.1.0 peerDependenciesMeta: '@nestjs/microservices': @@ -5014,21 +5162,37 @@ packages: '@nestjs/websockets': optional: true dependencies: - '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/platform-express': 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3) '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 iterare: 1.2.1 path-to-regexp: 3.2.0 - reflect-metadata: 0.1.14 + reflect-metadata: 0.2.1 rxjs: 7.8.1 tslib: 2.6.2 uid: 2.0.2 transitivePeerDependencies: - encoding - dev: false - /@nestjs/testing@10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10): - resolution: {integrity: sha512-IVLUnPz/+fkBtPATYfqTIP+phN9yjkXejmj+JyhmcfPJZpxBmD1i9VSMqa4u54l37j0xkGPscQ0IXpbhqMYUKw==} + /@nestjs/platform-express@10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3): + resolution: {integrity: sha512-GGKSEU48Os7nYFIsUM0nutuFUGn5AbeP8gzFBiBCAtiuJWrXZXpZ58pMBYxAbMf7IrcOZFInHEukjHGAQU0OZw==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/core': ^10.0.0 + dependencies: + '@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + body-parser: 1.20.2 + cors: 2.8.5 + express: 4.18.2 + multer: 1.4.4-lts.1 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + + /@nestjs/testing@10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(@nestjs/platform-express@10.3.3): + resolution: {integrity: sha512-kX20GfjAImL5grd/i69uD/x7sc00BaqGcP2dRG3ilqshQUuy5DOmspLCr3a2C8xmVU7kzK4spT0oTxhe6WcCAA==} peerDependencies: '@nestjs/common': ^10.0.0 '@nestjs/core': ^10.0.0 @@ -5040,8 +5204,9 @@ packages: '@nestjs/platform-express': optional: true dependencies: - '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/platform-express': 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3) tslib: 2.6.2 dev: false @@ -5093,54 +5258,64 @@ packages: node-fetch: 2.7.0 transitivePeerDependencies: - encoding - dev: false /@one-ini/wasm@0.1.1: resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} dev: true - /@open-draft/until@1.0.3: - resolution: {integrity: sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==} + /@open-draft/deferred-promise@2.2.0: + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} dev: true - /@peculiar/asn1-android@2.3.6: - resolution: {integrity: sha512-zkYh4DsiRhiNfg6tWaUuRc+huwlb9XJbmeZLrjTz9v76UK1Ehq3EnfJFED6P3sdznW/nqWe46LoM9JrqxcD58g==} + /@open-draft/logger@0.3.0: + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} dependencies: - '@peculiar/asn1-schema': 2.3.6 + is-node-process: 1.2.0 + outvariant: 1.4.2 + dev: true + + /@open-draft/until@2.1.0: + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + dev: true + + /@peculiar/asn1-android@2.3.10: + resolution: {integrity: sha512-z9Rx9cFJv7UUablZISe7uksNbFJCq13hO0yEAOoIpAymALTLlvUOSLnGiQS7okPaM5dP42oTLhezH6XDXRXjGw==} + dependencies: + '@peculiar/asn1-schema': 2.3.8 asn1js: 3.0.5 tslib: 2.6.2 dev: false - /@peculiar/asn1-ecc@2.3.6: - resolution: {integrity: sha512-Hu1xzMJQWv8/GvzOiinaE6XiD1/kEhq2C/V89UEoWeZ2fLUcGNIvMxOr/pMyL0OmpRWj/mhCTXOZp4PP+a0aTg==} + /@peculiar/asn1-ecc@2.3.8: + resolution: {integrity: sha512-Ah/Q15y3A/CtxbPibiLM/LKcMbnLTdUdLHUgdpB5f60sSvGkXzxJCu5ezGTFHogZXWNX3KSmYqilCrfdmBc6pQ==} dependencies: - '@peculiar/asn1-schema': 2.3.6 - '@peculiar/asn1-x509': 2.3.6 + '@peculiar/asn1-schema': 2.3.8 + '@peculiar/asn1-x509': 2.3.8 asn1js: 3.0.5 tslib: 2.6.2 dev: false - /@peculiar/asn1-rsa@2.3.6: - resolution: {integrity: sha512-DswjJyAXZnvESuImGNTvbNKvh1XApBVqU+r3UmrFFTAI23gv62byl0f5OFKWTNhCf66WQrd3sklpsCZc/4+jwA==} + /@peculiar/asn1-rsa@2.3.8: + resolution: {integrity: sha512-ES/RVEHu8VMYXgrg3gjb1m/XG0KJWnV4qyZZ7mAg7rrF3VTmRbLxO8mk+uy0Hme7geSMebp+Wvi2U6RLLEs12Q==} dependencies: - '@peculiar/asn1-schema': 2.3.6 - '@peculiar/asn1-x509': 2.3.6 + '@peculiar/asn1-schema': 2.3.8 + '@peculiar/asn1-x509': 2.3.8 asn1js: 3.0.5 tslib: 2.6.2 dev: false - /@peculiar/asn1-schema@2.3.6: - resolution: {integrity: sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA==} + /@peculiar/asn1-schema@2.3.8: + resolution: {integrity: sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==} dependencies: asn1js: 3.0.5 pvtsutils: 1.3.5 tslib: 2.6.2 dev: false - /@peculiar/asn1-x509@2.3.6: - resolution: {integrity: sha512-dRwX31R1lcbIdzbztiMvLNTDoGptxdV7HocNx87LfKU0fEWh7fTWJjx4oV+glETSy6heF/hJHB2J4RGB3vVSYg==} + /@peculiar/asn1-x509@2.3.8: + resolution: {integrity: sha512-voKxGfDU1c6r9mKiN5ZUsZWh3Dy1BABvTM3cimf0tztNwyMJPhiXY94eRTgsMQe6ViLfT6EoXxkWVzcm3mFAFw==} dependencies: - '@peculiar/asn1-schema': 2.3.6 + '@peculiar/asn1-schema': 2.3.8 asn1js: 3.0.5 ipaddr.js: 2.1.0 pvtsutils: 1.3.5 @@ -5171,60 +5346,7 @@ packages: requiresBuild: true optional: true - /@radix-ui/number@1.0.1: - resolution: {integrity: sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==} - dependencies: - '@babel/runtime': 7.23.2 - dev: true - - /@radix-ui/primitive@1.0.1: - resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} - dependencies: - '@babel/runtime': 7.23.2 - dev: true - - /@radix-ui/react-arrow@1.0.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@radix-ui/react-collection@1.0.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/react-compose-refs': 1.0.1(react@18.2.0) - '@radix-ui/react-context': 1.0.1(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@radix-ui/react-compose-refs@1.0.1(react@18.2.0): + /@radix-ui/react-compose-refs@1.0.1(@types/react@18.0.28)(react@18.2.0): resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} peerDependencies: '@types/react': '*' @@ -5233,259 +5355,12 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.23.2 - react: 18.2.0 - dev: true - - /@radix-ui/react-context@1.0.1(react@18.2.0): - resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - react: 18.2.0 - dev: true - - /@radix-ui/react-direction@1.0.1(react@18.2.0): - resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - react: 18.2.0 - dev: true - - /@radix-ui/react-dismissable-layer@1.0.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(react@18.2.0) - '@radix-ui/react-use-escape-keydown': 1.0.3(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@radix-ui/react-focus-guards@1.0.1(react@18.2.0): - resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - react: 18.2.0 - dev: true - - /@radix-ui/react-focus-scope@1.0.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/react-compose-refs': 1.0.1(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@radix-ui/react-id@1.0.1(react@18.2.0): - resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/react-use-layout-effect': 1.0.1(react@18.2.0) - react: 18.2.0 - dev: true - - /@radix-ui/react-popper@1.1.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@floating-ui/react-dom': 2.0.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-arrow': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(react@18.2.0) - '@radix-ui/react-context': 1.0.1(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(react@18.2.0) - '@radix-ui/react-use-rect': 1.0.1(react@18.2.0) - '@radix-ui/react-use-size': 1.0.1(react@18.2.0) - '@radix-ui/rect': 1.0.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@radix-ui/react-portal@1.0.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@radix-ui/react-primitive@1.0.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/react-slot': 1.0.2(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@radix-ui/react-roving-focus@1.0.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(react@18.2.0) - '@radix-ui/react-context': 1.0.1(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(react@18.2.0) - '@radix-ui/react-id': 1.0.1(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@radix-ui/react-select@1.2.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-zI7McXr8fNaSrUY9mZe4x/HC0jTLY9fWNhO1oLWYMQGDXuV4UCivIGTxwioSzO0ZCYX9iSLyWmAh/1TOmX3Cnw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/number': 1.0.1 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-compose-refs': 1.0.1(react@18.2.0) - '@radix-ui/react-context': 1.0.1(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.4(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-focus-guards': 1.0.1(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-id': 1.0.1(react@18.2.0) - '@radix-ui/react-popper': 1.1.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-slot': 1.0.2(react@18.2.0) - '@radix-ui/react-use-callback-ref': 1.0.1(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(react@18.2.0) - '@radix-ui/react-use-layout-effect': 1.0.1(react@18.2.0) - '@radix-ui/react-use-previous': 1.0.1(react@18.2.0) - '@radix-ui/react-visually-hidden': 1.0.3(react-dom@18.2.0)(react@18.2.0) - aria-hidden: 1.2.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.5(react@18.2.0) - dev: true - - /@radix-ui/react-separator@1.0.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0) + '@babel/runtime': 7.23.4 + '@types/react': 18.0.28 react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) dev: true - /@radix-ui/react-slot@1.0.2(react@18.2.0): + /@radix-ui/react-slot@1.0.2(@types/react@18.0.28)(react@18.2.0): resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} peerDependencies: '@types/react': '*' @@ -5494,203 +5369,55 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/react-compose-refs': 1.0.1(react@18.2.0) - react: 18.2.0 - dev: true - - /@radix-ui/react-toggle-group@1.0.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-context': 1.0.1(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-roving-focus': 1.0.4(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-toggle': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@radix-ui/react-toggle@1.0.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Pkqg3+Bc98ftZGsl60CLANXQBBQ4W3mTFS9EJvNxKMZ7magklKV69/id1mlAlOFDDfHvlCms0fx8fA4CMKDJHg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.0.1(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@radix-ui/react-toolbar@1.0.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-tBgmM/O7a07xbaEkYJWYTXkIdU/1pW4/KZORR43toC/4XWyBCURK0ei9kMUdp+gTPPKBgYLxXmRSH1EVcIDp8Q==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-context': 1.0.1(react@18.2.0) - '@radix-ui/react-direction': 1.0.1(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-roving-focus': 1.0.4(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-separator': 1.0.3(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-toggle-group': 1.0.4(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /@radix-ui/react-use-callback-ref@1.0.1(react@18.2.0): - resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - react: 18.2.0 - dev: true - - /@radix-ui/react-use-controllable-state@1.0.1(react@18.2.0): - resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/react-use-callback-ref': 1.0.1(react@18.2.0) - react: 18.2.0 - dev: true - - /@radix-ui/react-use-escape-keydown@1.0.3(react@18.2.0): - resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/react-use-callback-ref': 1.0.1(react@18.2.0) - react: 18.2.0 - dev: true - - /@radix-ui/react-use-layout-effect@1.0.1(react@18.2.0): - resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - react: 18.2.0 - dev: true - - /@radix-ui/react-use-previous@1.0.1(react@18.2.0): - resolution: {integrity: sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.2 + '@babel/runtime': 7.23.4 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.28)(react@18.2.0) + '@types/react': 18.0.28 react: 18.2.0 dev: true - /@radix-ui/react-use-rect@1.0.1(react@18.2.0): - resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} + /@readme/better-ajv-errors@1.6.0(ajv@8.12.0): + resolution: {integrity: sha512-9gO9rld84Jgu13kcbKRU+WHseNhaVt76wYMeRDGsUGYxwJtI3RmEJ9LY9dZCYQGI8eUZLuxb5qDja0nqklpFjQ==} + engines: {node: '>=14'} peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true + ajv: 4.11.8 - 8 dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/rect': 1.0.1 - react: 18.2.0 - dev: true - - /@radix-ui/react-use-size@1.0.1(react@18.2.0): - resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/react-use-layout-effect': 1.0.1(react@18.2.0) - react: 18.2.0 - dev: true - - /@radix-ui/react-visually-hidden@1.0.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + '@babel/code-frame': 7.23.4 + '@babel/runtime': 7.23.4 + '@humanwhocodes/momoa': 2.0.4 + ajv: 8.12.0 + chalk: 4.1.2 + json-to-ast: 2.1.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + dev: true + + /@readme/json-schema-ref-parser@1.2.0: + resolution: {integrity: sha512-Bt3QVovFSua4QmHa65EHUmh2xS0XJ3rgTEUPH998f4OW4VVJke3BuS16f+kM0ZLOGdvIrzrPRqwihuv5BAjtrA==} dependencies: - '@babel/runtime': 7.23.2 - '@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@jsdevtools/ono': 7.1.3 + '@types/json-schema': 7.0.15 + call-me-maybe: 1.0.2 + js-yaml: 4.1.0 dev: true - /@radix-ui/rect@1.0.1: - resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} + /@readme/openapi-parser@2.5.0(openapi-types@12.1.3): + resolution: {integrity: sha512-IbymbOqRuUzoIgxfAAR7XJt2FWl6n2yqN09fF5adacGm7W03siA3bj1Emql0X9D2T+RpBYz3x9zDsMhuoMP62A==} + engines: {node: '>=14'} + peerDependencies: + openapi-types: '>=7' dependencies: - '@babel/runtime': 7.23.2 + '@apidevtools/openapi-schemas': 2.1.0 + '@apidevtools/swagger-methods': 3.0.2 + '@jsdevtools/ono': 7.1.3 + '@readme/better-ajv-errors': 1.6.0(ajv@8.12.0) + '@readme/json-schema-ref-parser': 1.2.0 + ajv: 8.12.0 + ajv-draft-04: 1.0.0(ajv@8.12.0) + call-me-maybe: 1.0.2 + openapi-types: 12.1.3 dev: true - /@rollup/plugin-json@6.1.0(rollup@4.9.1): + /@rollup/plugin-json@6.1.0(rollup@4.12.0): resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -5699,11 +5426,11 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.9.1) - rollup: 4.9.1 + '@rollup/pluginutils': 5.1.0(rollup@4.12.0) + rollup: 4.12.0 dev: false - /@rollup/plugin-replace@5.0.5(rollup@4.9.1): + /@rollup/plugin-replace@5.0.5(rollup@4.12.0): resolution: {integrity: sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==} engines: {node: '>=14.0.0'} peerDependencies: @@ -5712,12 +5439,12 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.9.1) - magic-string: 0.30.5 - rollup: 4.9.1 + '@rollup/pluginutils': 5.1.0(rollup@4.12.0) + magic-string: 0.30.7 + rollup: 4.12.0 dev: false - /@rollup/pluginutils@5.1.0(rollup@4.9.1): + /@rollup/pluginutils@5.1.0(rollup@4.12.0): resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} engines: {node: '>=14.0.0'} peerDependencies: @@ -5729,108 +5456,108 @@ packages: '@types/estree': 1.0.5 estree-walker: 2.0.2 picomatch: 2.3.1 - rollup: 4.9.1 + rollup: 4.12.0 - /@rollup/rollup-android-arm-eabi@4.9.1: - resolution: {integrity: sha512-6vMdBZqtq1dVQ4CWdhFwhKZL6E4L1dV6jUjuBvsavvNJSppzi6dLBbuV+3+IyUREaj9ZFvQefnQm28v4OCXlig==} + /@rollup/rollup-android-arm-eabi@4.12.0: + resolution: {integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==} cpu: [arm] os: [android] requiresBuild: true optional: true - /@rollup/rollup-android-arm64@4.9.1: - resolution: {integrity: sha512-Jto9Fl3YQ9OLsTDWtLFPtaIMSL2kwGyGoVCmPC8Gxvym9TCZm4Sie+cVeblPO66YZsYH8MhBKDMGZ2NDxuk/XQ==} + /@rollup/rollup-android-arm64@4.12.0: + resolution: {integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==} cpu: [arm64] os: [android] requiresBuild: true optional: true - /@rollup/rollup-darwin-arm64@4.9.1: - resolution: {integrity: sha512-LtYcLNM+bhsaKAIGwVkh5IOWhaZhjTfNOkGzGqdHvhiCUVuJDalvDxEdSnhFzAn+g23wgsycmZk1vbnaibZwwA==} + /@rollup/rollup-darwin-arm64@4.12.0: + resolution: {integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==} cpu: [arm64] os: [darwin] requiresBuild: true optional: true - /@rollup/rollup-darwin-x64@4.9.1: - resolution: {integrity: sha512-KyP/byeXu9V+etKO6Lw3E4tW4QdcnzDG/ake031mg42lob5tN+5qfr+lkcT/SGZaH2PdW4Z1NX9GHEkZ8xV7og==} + /@rollup/rollup-darwin-x64@4.12.0: + resolution: {integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==} cpu: [x64] os: [darwin] requiresBuild: true optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.9.1: - resolution: {integrity: sha512-Yqz/Doumf3QTKplwGNrCHe/B2p9xqDghBZSlAY0/hU6ikuDVQuOUIpDP/YcmoT+447tsZTmirmjgG3znvSCR0Q==} + /@rollup/rollup-linux-arm-gnueabihf@4.12.0: + resolution: {integrity: sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==} cpu: [arm] os: [linux] requiresBuild: true optional: true - /@rollup/rollup-linux-arm64-gnu@4.9.1: - resolution: {integrity: sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q==} + /@rollup/rollup-linux-arm64-gnu@4.12.0: + resolution: {integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@rollup/rollup-linux-arm64-musl@4.9.1: - resolution: {integrity: sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw==} + /@rollup/rollup-linux-arm64-musl@4.12.0: + resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@rollup/rollup-linux-riscv64-gnu@4.9.1: - resolution: {integrity: sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw==} + /@rollup/rollup-linux-riscv64-gnu@4.12.0: + resolution: {integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==} cpu: [riscv64] os: [linux] requiresBuild: true optional: true - /@rollup/rollup-linux-x64-gnu@4.9.1: - resolution: {integrity: sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg==} + /@rollup/rollup-linux-x64-gnu@4.12.0: + resolution: {integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@rollup/rollup-linux-x64-musl@4.9.1: - resolution: {integrity: sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw==} + /@rollup/rollup-linux-x64-musl@4.12.0: + resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@rollup/rollup-win32-arm64-msvc@4.9.1: - resolution: {integrity: sha512-7XI4ZCBN34cb+BH557FJPmh0kmNz2c25SCQeT9OiFWEgf8+dL6ZwJ8f9RnUIit+j01u07Yvrsuu1rZGxJCc51g==} + /@rollup/rollup-win32-arm64-msvc@4.12.0: + resolution: {integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==} cpu: [arm64] os: [win32] requiresBuild: true optional: true - /@rollup/rollup-win32-ia32-msvc@4.9.1: - resolution: {integrity: sha512-yE5c2j1lSWOH5jp+Q0qNL3Mdhr8WuqCNVjc6BxbVfS5cAS6zRmdiw7ktb8GNpDCEUJphILY6KACoFoRtKoqNQg==} + /@rollup/rollup-win32-ia32-msvc@4.12.0: + resolution: {integrity: sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==} cpu: [ia32] os: [win32] requiresBuild: true optional: true - /@rollup/rollup-win32-x64-msvc@4.9.1: - resolution: {integrity: sha512-PyJsSsafjmIhVgaI1Zdj7m8BB8mMckFah/xbpplObyHfiXzKcI5UOUXRyOdHW7nz4DpMCuzLnF7v5IWHenCwYA==} + /@rollup/rollup-win32-x64-msvc@4.12.0: + resolution: {integrity: sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==} cpu: [x64] os: [win32] requiresBuild: true optional: true - /@rushstack/node-core-library@3.62.0(@types/node@20.10.5): - resolution: {integrity: sha512-88aJn2h8UpSvdwuDXBv1/v1heM6GnBf3RjEy6ZPP7UnzHNCqOHA2Ut+ScYUbXcqIdfew9JlTAe3g+cnX9xQ/Aw==} + /@rushstack/node-core-library@3.63.0(@types/node@20.11.22): + resolution: {integrity: sha512-Q7B3dVpBQF1v+mUfxNcNZh5uHVR8ntcnkN5GYjbBLrxUYHBGKbnCM+OdcN+hzCpFlLBH6Ob0dEHhZ0spQwf24A==} peerDependencies: '@types/node': '*' peerDependenciesMeta: '@types/node': optional: true dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 colors: 1.2.5 fs-extra: 7.0.1 import-lazy: 4.0.0 @@ -5856,10 +5583,8 @@ packages: string-argv: 0.3.1 dev: true - /@sharkey/sfm-js@0.24.3: - resolution: {integrity: sha512-Fd2LWPYNVmnTg9AKdJm3MLMvYdxQafq/0eQlmJhUnQheRVm3f1xHrFFY12+yUWIq7rS0uxrKEmrVLnPzRqYG6Q==, tarball: https://git.joinsharkey.org/api/packages/Sharkey/npm/%40sharkey%2Fsfm-js/-/0.24.3/sfm-js-0.24.3.tgz} - dependencies: - '@twemoji/parser': 15.0.0 + /@shikijs/core@1.1.7: + resolution: {integrity: sha512-gTYLUIuD1UbZp/11qozD3fWpUTuMqPSf3svDMMrL0UmlGU7D9dPw/V1FonwAorCUJBltaaESxq90jrSjQyGixg==} dev: false /@sideway/address@4.1.4: @@ -5876,29 +5601,25 @@ packages: resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} dev: true - /@simplewebauthn/server@8.3.5: - resolution: {integrity: sha512-Y6FkggTkzUdPk3cG3LLCiv7rqPQ3QI7g//RU9937G1pxogChvx12Y7/AZdWeMoeP+LFl0fPpdc1bIE0etJOxGA==} + /@simplewebauthn/server@9.0.3: + resolution: {integrity: sha512-FMZieoBosrVLFxCnxPFD9Enhd1U7D8nidVDT4MsHc6l4fdVcjoeHjDueeXCloO1k5O/fZg1fsSXXPKbY2XTzDA==} engines: {node: '>=16.0.0'} dependencies: '@hexagon/base64': 1.1.27 - '@peculiar/asn1-android': 2.3.6 - '@peculiar/asn1-ecc': 2.3.6 - '@peculiar/asn1-rsa': 2.3.6 - '@peculiar/asn1-schema': 2.3.6 - '@peculiar/asn1-x509': 2.3.6 - '@simplewebauthn/typescript-types': 8.3.4 - cbor-x: 1.5.4 + '@levischuck/tiny-cbor': 0.2.2 + '@peculiar/asn1-android': 2.3.10 + '@peculiar/asn1-ecc': 2.3.8 + '@peculiar/asn1-rsa': 2.3.8 + '@peculiar/asn1-schema': 2.3.8 + '@peculiar/asn1-x509': 2.3.8 + '@simplewebauthn/types': 9.0.1 cross-fetch: 4.0.0 transitivePeerDependencies: - encoding dev: false - /@simplewebauthn/typescript-types@8.3.4: - resolution: {integrity: sha512-38xtca0OqfRVNloKBrFB5LEM6PN5vzFbJG6rAutPVrtGHFYxPdiV3btYWq0eAZAZmP+dqFPYJxJWeJrGfmYHng==} - - /@sinclair/typebox@0.24.51: - resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} - dev: true + /@simplewebauthn/types@9.0.1: + resolution: {integrity: sha512-tGSRP1QvsAvsJmnOlRQyw/mvK9gnPtjEc5fg2+m8n+QUa+D7rvrKkOYyfpy42GTs90X3RDOnqJgfHt+qO67/+w==} /@sinclair/typebox@0.27.8: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} @@ -5917,12 +5638,6 @@ packages: engines: {node: '>=16'} dev: false - /@sinonjs/commons@1.8.6: - resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} - dependencies: - type-detect: 4.0.8 - dev: true - /@sinonjs/commons@2.0.0: resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==} dependencies: @@ -5946,14 +5661,8 @@ packages: '@sinonjs/commons': 3.0.0 dev: false - /@sinonjs/fake-timers@9.1.2: - resolution: {integrity: sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==} - dependencies: - '@sinonjs/commons': 1.8.6 - dev: true - - /@sinonjs/samsam@7.0.1: - resolution: {integrity: sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==} + /@sinonjs/samsam@8.0.0: + resolution: {integrity: sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==} dependencies: '@sinonjs/commons': 2.0.0 lodash.get: 4.4.2 @@ -6427,121 +6136,110 @@ packages: resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} dev: false - /@storybook/addon-actions@7.6.5: - resolution: {integrity: sha512-lW/m9YcaNfBZk+TZLxyzHdd563mBWpsUIveOKYjcPdl/q0FblWWZrRsFHqwLK1ldZ4AZXs8J/47G8CBr6Ew2uQ==} + /@storybook/addon-actions@8.0.0-beta.6: + resolution: {integrity: sha512-g+X2M6Awg21vkXzRP7hWBYCdbXnxJ3BJWsP7BblYmPo2J7eJDzhQascNyTmSr0pb1/7nv+tworGviXThgvlUgw==} dependencies: - '@storybook/core-events': 7.6.5 + '@storybook/core-events': 8.0.0-beta.6 '@storybook/global': 5.0.0 - '@types/uuid': 9.0.7 + '@types/uuid': 9.0.8 dequal: 2.0.3 polished: 4.2.2 uuid: 9.0.1 dev: true - /@storybook/addon-backgrounds@7.6.5: - resolution: {integrity: sha512-wZZOL19vg4TTRtOTl71XKqPe5hQx3XUh9Fle0wOi91FiFrBdqusrppnyS89wPS8RQG5lXEOFEUvYcMmdCcdZfw==} + /@storybook/addon-backgrounds@8.0.0-beta.6: + resolution: {integrity: sha512-C8MS635knAOSat5JbkpZXOiAqkDm1bKWvuVqiQfbX2into45/aAuyN3mYxveGIRTRjPJCv/UpostkLSNvfH/NQ==} dependencies: '@storybook/global': 5.0.0 memoizerific: 1.11.3 ts-dedent: 2.2.0 dev: true - /@storybook/addon-controls@7.6.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-EdSZ2pYf74mOXZGGJ22lrDvdvL0YKc95iWv9FFEhUFOloMy/0OZPB2ybYmd2KVCy3SeIE4Zfeiw8pDXdCUniOQ==} + /@storybook/addon-controls@8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-G96MH7yU/KShq3lTrkgtU1IbNQXLVc3BG7miaLqzQgWFN8SSAivlu3vk1Vffui3+3Dv52WZhMKi3hueNfnM1Xw==} dependencies: - '@storybook/blocks': 7.6.5(react-dom@18.2.0)(react@18.2.0) + '@storybook/blocks': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) lodash: 4.17.21 ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - - '@types/react-dom' - encoding - react - react-dom - supports-color dev: true - /@storybook/addon-docs@7.6.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-D9tZyD41IujCHiPYdfS2bKtZRJPNwO4EydzyqODXppomluhFbY3uTEaf0H1UFnJLQxWNXZ7rr3aS0V3O6yu8pA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + /@storybook/addon-docs@8.0.0-beta.6: + resolution: {integrity: sha512-VLys4EuL8XVhmu1QxUiUG5keID8v/FsC5L71Y0Wcf5D+ll6ZD8vCqEtbMY3TiJJ9NqqNIcmcG3bG6JVXOYcD8g==} dependencies: - '@jest/transform': 29.7.0 - '@mdx-js/react': 2.3.0(react@18.2.0) - '@storybook/blocks': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/client-logger': 7.6.5 - '@storybook/components': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/csf-plugin': 7.6.5 - '@storybook/csf-tools': 7.6.5 + '@babel/core': 7.24.0 + '@mdx-js/react': 3.0.1(@types/react@18.0.28)(react@18.2.0) + '@storybook/blocks': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/components': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + '@storybook/csf-plugin': 8.0.0-beta.6 + '@storybook/csf-tools': 8.0.0-beta.6 '@storybook/global': 5.0.0 - '@storybook/mdx2-csf': 1.0.0 - '@storybook/node-logger': 7.6.5 - '@storybook/postinstall': 7.6.5 - '@storybook/preview-api': 7.6.5 - '@storybook/react-dom-shim': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/theming': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.6.5 + '@storybook/node-logger': 8.0.0-beta.6 + '@storybook/preview-api': 8.0.0-beta.6 + '@storybook/react-dom-shim': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 8.0.0-beta.6 + '@types/react': 18.0.28 fs-extra: 11.1.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - remark-external-links: 8.0.0 - remark-slug: 6.1.0 + rehype-external-links: 3.0.0 + rehype-slug: 6.0.0 ts-dedent: 2.2.0 transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - encoding - supports-color dev: true - /@storybook/addon-essentials@7.6.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-VCLj1JAEpGoqF5iFJOo1CZFFck/tg4m/98DLdQuNuXvxT6jqaF0NI9UUQuJLIGteDCR7NKRbTFc1hV3/Ev+Ziw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@storybook/addon-actions': 7.6.5 - '@storybook/addon-backgrounds': 7.6.5 - '@storybook/addon-controls': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/addon-docs': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/addon-highlight': 7.6.5 - '@storybook/addon-measure': 7.6.5 - '@storybook/addon-outline': 7.6.5 - '@storybook/addon-toolbars': 7.6.5 - '@storybook/addon-viewport': 7.6.5 - '@storybook/core-common': 7.6.5 - '@storybook/manager-api': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/node-logger': 7.6.5 - '@storybook/preview-api': 7.6.5 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + /@storybook/addon-essentials@8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-6Vjf03c0oIavXqOK9DIN0UeH0iJFmBoVrFt1mTwydMxchyJBSP785MSd9DuFhLdYZPQTMHaR4/JhOIjdDV8mbA==} + dependencies: + '@storybook/addon-actions': 8.0.0-beta.6 + '@storybook/addon-backgrounds': 8.0.0-beta.6 + '@storybook/addon-controls': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-docs': 8.0.0-beta.6 + '@storybook/addon-highlight': 8.0.0-beta.6 + '@storybook/addon-measure': 8.0.0-beta.6 + '@storybook/addon-outline': 8.0.0-beta.6 + '@storybook/addon-toolbars': 8.0.0-beta.6 + '@storybook/addon-viewport': 8.0.0-beta.6 + '@storybook/core-common': 8.0.0-beta.6 + '@storybook/manager-api': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/node-logger': 8.0.0-beta.6 + '@storybook/preview-api': 8.0.0-beta.6 ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - - '@types/react-dom' - encoding + - react + - react-dom - supports-color dev: true - /@storybook/addon-highlight@7.6.5: - resolution: {integrity: sha512-CxzmIb30F9nLPQwT0lCPYhOAwGlGF4IkgkO8hYA7VfGCGUkJZEyyN/YkP/ZCUSdCIRChDBouR3KiFFd4mDFKzg==} + /@storybook/addon-highlight@8.0.0-beta.6: + resolution: {integrity: sha512-U+qz4TNLrw24t1eZ2Zmhl2FZKZKiwHbibq4qR5ruAFe9W5/aMHqPuBB0POroaGu3P+tyDP2G46dckMNXVraiWA==} dependencies: '@storybook/global': 5.0.0 dev: true - /@storybook/addon-interactions@7.6.5: - resolution: {integrity: sha512-8Hzt9u1DQzFvtGER/hCGIvGpCoVwzVoqpM98f2KAIVx/NMFmRW7UyKihXzw1j2t4q2ZaF2jZDYWCBqlP+iwILA==} + /@storybook/addon-interactions@8.0.0-beta.6: + resolution: {integrity: sha512-KSigq+7vCA1tnj31MjhM7xaqickR1guZdjyXVRx7gi7qbdhSuCQv52gAkVpDapwlEuvGFCCYxzt7tmcn6dkLZQ==} dependencies: '@storybook/global': 5.0.0 - '@storybook/types': 7.6.5 + '@storybook/types': 8.0.0-beta.6 jest-mock: 27.5.1 polished: 4.2.2 ts-dedent: 2.2.0 dev: true - /@storybook/addon-links@7.6.5(react@18.2.0): - resolution: {integrity: sha512-Lx4Ng+iXt0YpIrKGr+nOZlpN9ypOoEDoP/7bZ6m7GXuVAkDm3JrRCBp7e2ZKSKcTxPdjPuO9HVKkIjtqjINlpw==} + /@storybook/addon-links@8.0.0-beta.6(react@18.2.0): + resolution: {integrity: sha512-+5knw5CHEb23n6Bm9Xp9nmoLRqWZ3QVGb1gNI3mGwmkpLwesohFR4fW7OrdRmzYHpS0PyYToZyfTCMYrmjBDvg==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 peerDependenciesMeta: @@ -6554,71 +6252,76 @@ packages: ts-dedent: 2.2.0 dev: true - /@storybook/addon-measure@7.6.5: - resolution: {integrity: sha512-tlUudVQSrA+bwI4dhO8J7nYHtYdylcBZ86ybnqMmdTthsnyc7jnaFVQwbb6bbQJpPxvEvoNds5bVGUFocuvymQ==} + /@storybook/addon-mdx-gfm@8.0.0-beta.6: + resolution: {integrity: sha512-b4pb59rrX+C/oYFeEiHb8jJn0h9WZSkHVkLIgaj0G64Nd9OpyKZXMbGpDxwMq4LTi1w65Wddi1UUQbUVVDNHRw==} + dependencies: + '@storybook/node-logger': 8.0.0-beta.6 + remark-gfm: 4.0.0 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@storybook/addon-measure@8.0.0-beta.6: + resolution: {integrity: sha512-D+KzWRULcbwR8/ysD7Qbw4uWBn9gwNm9s3IeVuhupawUb3u+H4XfVCOW2rA5qry/x8aroKOhAmyKd9v4i+l3pg==} dependencies: '@storybook/global': 5.0.0 tiny-invariant: 1.3.1 dev: true - /@storybook/addon-outline@7.6.5: - resolution: {integrity: sha512-P7X4+Z9L/l/RZW9UvvM+iuK2SUHD22KPc+dbYOifRXDovUqhfmcKVh1CUqTDMyZrg2ZAbropehMz1eI9BlQfxg==} + /@storybook/addon-outline@8.0.0-beta.6: + resolution: {integrity: sha512-U+5TFTj+gtkIiIJCk6h7zbrP588CUipzVVsiDTSLl4pc+H3ylGTGncq3ZGtOyl+DCoBsQCgKxy2YWQtKHrESOw==} dependencies: '@storybook/global': 5.0.0 ts-dedent: 2.2.0 dev: true - /@storybook/addon-storysource@7.6.5: - resolution: {integrity: sha512-mlGReftuGxfyfLXsnw4GF03G79w3rKKRclNasOVPuAR2vlSTRyltoglZ8TcXfxNQ+RzywtEZkjD7SeJZsuvBbQ==} + /@storybook/addon-storysource@8.0.0-beta.6: + resolution: {integrity: sha512-J9sCZ5/KQW2hbfKsom8LmgSWJxw+Kp/7LjIHGevFfov/i9DR8i9xbh5htUwC9fx+vWGR87tez03b+oUJbyHPog==} dependencies: - '@storybook/source-loader': 7.6.5 + '@storybook/source-loader': 8.0.0-beta.6 estraverse: 5.3.0 tiny-invariant: 1.3.1 dev: true - /@storybook/addon-toolbars@7.6.5: - resolution: {integrity: sha512-/zqWbVNE/SHc8I5Prnd2Q8U57RGEIYvHfeXjfkuLcE2Quc4Iss4x/9eU7SKu4jm+IOO2s0wlN6HcqI3XEf2XxA==} + /@storybook/addon-toolbars@8.0.0-beta.6: + resolution: {integrity: sha512-ClT5spwh6S1rUvyFEIFQndE3VK6tpwI2cyIW4E20LajtfUmj3dOfJQX/ZbnhEH3sDBsCm97ysZ/mNR0mbBHZrg==} dev: true - /@storybook/addon-viewport@7.6.5: - resolution: {integrity: sha512-9ghKTaduIUvQ6oShmWLuwMeTjtMR4RgKeKHrTJ7THMqvE/ydDPCYeL7ugF65ocXZSEz/QmxdK7uL686ZMKsqNA==} + /@storybook/addon-viewport@8.0.0-beta.6: + resolution: {integrity: sha512-KNYGM6nVrz/Ej25W3lcpaxxJDYVXBYeGl60FWN/WlqRnjo4c4Fyufl6Xev2plQ3eI8jIvWEdGNC/Z/NQnDx1+Q==} dependencies: memoizerific: 1.11.3 dev: true - /@storybook/addons@7.6.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-v+d8io1MsgTd7rruYInfKXY0c1uXn+ADLxAppUI0PUwPFYwg9tLn3cvwgt5SVum9E5IkVQwXoW6JNkDC5fC8XQ==} - dependencies: - '@storybook/manager-api': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.6.5 - '@storybook/types': 7.6.5 - transitivePeerDependencies: - - react - - react-dom - dev: true - - /@storybook/blocks@7.6.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-/NjuYkPks5w9lKn47KLgVC5cBkwfc+ERAp0CY0Xe//BQJkP+bcI8lE8d9Qc9IXFbOTvYEULeQrFgCkesk5BmLg==} + /@storybook/blocks@8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-QkrWT0BELNv3UGv/dtNuB/ROZn0f9VpERbadhXLE/oNXMJLalyjEbRGM635l0lDeoqjYnWHl+tuM6DTe1Xpk2w==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true dependencies: - '@storybook/channels': 7.6.5 - '@storybook/client-logger': 7.6.5 - '@storybook/components': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-events': 7.6.5 + '@storybook/channels': 8.0.0-beta.6 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/components': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 8.0.0-beta.6 '@storybook/csf': 0.1.2 - '@storybook/docs-tools': 7.6.5 + '@storybook/docs-tools': 8.0.0-beta.6 '@storybook/global': 5.0.0 - '@storybook/manager-api': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.6.5 - '@storybook/theming': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.6.5 + '@storybook/icons': 1.2.5(react-dom@18.2.0)(react@18.2.0) + '@storybook/manager-api': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 8.0.0-beta.6 + '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 8.0.0-beta.6 '@types/lodash': 4.14.191 color-convert: 2.0.1 dequal: 2.0.3 lodash: 4.17.21 - markdown-to-jsx: 7.2.0(react@18.2.0) + markdown-to-jsx: 7.3.2(react@18.2.0) memoizerific: 1.11.3 polished: 4.2.2 react: 18.2.0 @@ -6630,27 +6333,24 @@ packages: util-deprecate: 1.0.2 transitivePeerDependencies: - '@types/react' - - '@types/react-dom' - encoding - supports-color dev: true - /@storybook/builder-manager@7.6.5: - resolution: {integrity: sha512-FQyI+tfzMam2XKXq7k921YVafIJs9Vqvos5qx8vyRnRffo55UU8tgunwjGn0PswtbMm6sThVqE0C0ZzVr7RG8A==} + /@storybook/builder-manager@8.0.0-beta.6: + resolution: {integrity: sha512-bB/gSsPIpU22Tc6YTjPZdw1RM6nrsuJJ9aYXGqEJTqA4l4lBUN7fwIZQ1x/pS+5LbeUO0J9lAhGXurS+m8rI2A==} dependencies: '@fal-works/esbuild-plugin-global-externals': 2.1.2 - '@storybook/core-common': 7.6.5 - '@storybook/manager': 7.6.5 - '@storybook/node-logger': 7.6.5 + '@storybook/core-common': 8.0.0-beta.6 + '@storybook/manager': 8.0.0-beta.6 + '@storybook/node-logger': 8.0.0-beta.6 '@types/ejs': 3.1.2 - '@types/find-cache-dir': 3.2.1 - '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.18.17) + '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.18.20) browser-assert: 1.2.1 ejs: 3.1.9 - esbuild: 0.18.17 + esbuild: 0.18.20 esbuild-plugin-alias: 0.2.1 express: 4.18.2 - find-cache-dir: 3.3.2 fs-extra: 11.1.1 process: 0.11.10 util: 0.12.5 @@ -6659,12 +6359,12 @@ packages: - supports-color dev: true - /@storybook/builder-vite@7.6.5(typescript@5.3.3)(vite@5.0.10): - resolution: {integrity: sha512-VbAYTGr92lgCWTwO2Z7NgSW3f5/K4Vr0Qxa2IlTgMCymWdDbWdIQiREcmCP0vjAGM2ftq1+vxngohVgx/r7pUw==} + /@storybook/builder-vite@8.0.0-beta.6(typescript@5.3.3)(vite@5.1.4): + resolution: {integrity: sha512-3P5uTZqwwcUW64Hep/VtJXpQYi5vTkmqAjwZvr8gmzr37NYq3YT/PiSGn4CaZswSx5Z/lSYq3In8oIwmj/a1/g==} peerDependencies: '@preact/preset-vite': '*' typescript: '>= 4.3.x' - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 + vite: ^4.0.0 || ^5.0.0 vite-plugin-glimmerx: '*' peerDependenciesMeta: '@preact/preset-vite': @@ -6674,57 +6374,57 @@ packages: vite-plugin-glimmerx: optional: true dependencies: - '@storybook/channels': 7.6.5 - '@storybook/client-logger': 7.6.5 - '@storybook/core-common': 7.6.5 - '@storybook/csf-plugin': 7.6.5 - '@storybook/node-logger': 7.6.5 - '@storybook/preview': 7.6.5 - '@storybook/preview-api': 7.6.5 - '@storybook/types': 7.6.5 + '@storybook/channels': 8.0.0-beta.6 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/core-common': 8.0.0-beta.6 + '@storybook/core-events': 8.0.0-beta.6 + '@storybook/csf-plugin': 8.0.0-beta.6 + '@storybook/node-logger': 8.0.0-beta.6 + '@storybook/preview': 8.0.0-beta.6 + '@storybook/preview-api': 8.0.0-beta.6 + '@storybook/types': 8.0.0-beta.6 '@types/find-cache-dir': 3.2.1 browser-assert: 1.2.1 es-module-lexer: 0.9.3 express: 4.18.2 find-cache-dir: 3.3.2 fs-extra: 11.1.1 - magic-string: 0.30.5 - rollup: 3.29.4 + magic-string: 0.30.7 + ts-dedent: 2.2.0 typescript: 5.3.3 - vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0) + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) transitivePeerDependencies: - encoding - supports-color dev: true - /@storybook/channels@7.6.5: - resolution: {integrity: sha512-FIlNkyfQy9uHoJfAFL2/wO3ASGJELFvBzURBE2rcEF/TS7GcUiqWnBfiDxAbwSEjSOm2F0eEq3UXhaZEjpJHDw==} + /@storybook/channels@8.0.0-beta.6: + resolution: {integrity: sha512-DjwJhty45gQifo+TvGqddLX+NX1iGTmZyGLxlqPMpdp+x/yq8WwVZ316Q7tLt6z6fyAmsroc3ma5p1iLhqpV7g==} dependencies: - '@storybook/client-logger': 7.6.5 - '@storybook/core-events': 7.6.5 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/core-events': 8.0.0-beta.6 '@storybook/global': 5.0.0 qs: 6.11.1 telejson: 7.2.0 tiny-invariant: 1.3.1 dev: true - /@storybook/cli@7.6.5: - resolution: {integrity: sha512-w+Y8dx5oCLQVESOVmpsQuFksr/ewARKrnSKl9kwnVMN4sMgjOgoZ3zmV66J7SKexvwyuwlOjf840pmEglGdPPg==} + /@storybook/cli@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-sREQYnPds2bwQS7FLbRy7oaxGvOmYhPEYVf93pWKyo/qwSWyXEXbqGCGT6bNhSl/xzqXX7VryLDmuOoHmVTh1g==} hasBin: true dependencies: - '@babel/core': 7.23.3 - '@babel/preset-env': 7.23.6(@babel/core@7.23.3) - '@babel/types': 7.23.4 + '@babel/core': 7.24.0 + '@babel/types': 7.24.0 '@ndelangen/get-tarball': 3.0.7 - '@storybook/codemod': 7.6.5 - '@storybook/core-common': 7.6.5 - '@storybook/core-events': 7.6.5 - '@storybook/core-server': 7.6.5 - '@storybook/csf-tools': 7.6.5 - '@storybook/node-logger': 7.6.5 - '@storybook/telemetry': 7.6.5 - '@storybook/types': 7.6.5 - '@types/semver': 7.5.6 + '@storybook/codemod': 8.0.0-beta.6 + '@storybook/core-common': 8.0.0-beta.6 + '@storybook/core-events': 8.0.0-beta.6 + '@storybook/core-server': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/csf-tools': 8.0.0-beta.6 + '@storybook/node-logger': 8.0.0-beta.6 + '@storybook/telemetry': 8.0.0-beta.6 + '@storybook/types': 8.0.0-beta.6 + '@types/semver': 7.5.8 '@yarnpkg/fslib': 2.10.3 '@yarnpkg/libzip': 2.3.0 chalk: 4.1.2 @@ -6733,103 +6433,95 @@ packages: detect-indent: 6.1.0 envinfo: 7.8.1 execa: 5.1.1 - express: 4.18.2 find-up: 5.0.0 fs-extra: 11.1.1 get-npm-tarball-url: 2.0.3 - get-port: 5.1.1 giget: 1.1.2 globby: 11.1.0 jscodeshift: 0.15.1(@babel/preset-env@7.23.6) leven: 3.1.0 ora: 5.4.1 - prettier: 2.8.8 + prettier: 3.2.5 prompts: 2.4.2 - puppeteer-core: 2.1.1 read-pkg-up: 7.0.1 - semver: 7.5.4 - simple-update-notifier: 2.0.0 + semver: 7.6.0 strip-json-comments: 3.1.1 tempy: 1.0.1 + tiny-invariant: 1.3.1 ts-dedent: 2.2.0 - util-deprecate: 1.0.2 transitivePeerDependencies: + - '@babel/preset-env' - bufferutil - encoding + - react + - react-dom - supports-color - utf-8-validate dev: true - /@storybook/client-logger@7.6.5: - resolution: {integrity: sha512-S5aROWgssqg7tcs9lgW5wmCAz4SxMAtioiyVj5oFecmPCbQtFVIAREYzeoxE4GfJL+plrfRkum4BzziANn8EhQ==} + /@storybook/client-logger@8.0.0-beta.6: + resolution: {integrity: sha512-XX9CSWt9NDO/1K8tTYV+yuj0ur4HznM1Vc5mY5AwT5xh0RP5HtWZ+VoJfrWYXlBoRXaj0gf8si+FO+lSW82DcQ==} dependencies: '@storybook/global': 5.0.0 dev: true - /@storybook/codemod@7.6.5: - resolution: {integrity: sha512-K5C9ltBClZ0aSyujGt3RJFtRicrUZy8nzhHrcADUj27rrQD26jH/p+Y05jWKj9JcI8SyMg978GN5X/1aw2Y31A==} + /@storybook/codemod@8.0.0-beta.6: + resolution: {integrity: sha512-ttQYDkhKmtU6Qbg+Kgn4K2XXf8XMpa2euuC6PmYffBD7/qLiGfABfBc4FHKRv4yScnvKK7Ehy7K0lvipfg6tXw==} dependencies: - '@babel/core': 7.23.3 - '@babel/preset-env': 7.23.6(@babel/core@7.23.3) - '@babel/types': 7.23.4 + '@babel/core': 7.24.0 + '@babel/preset-env': 7.23.6(@babel/core@7.24.0) + '@babel/types': 7.24.0 '@storybook/csf': 0.1.2 - '@storybook/csf-tools': 7.6.5 - '@storybook/node-logger': 7.6.5 - '@storybook/types': 7.6.5 + '@storybook/csf-tools': 8.0.0-beta.6 + '@storybook/node-logger': 8.0.0-beta.6 + '@storybook/types': 8.0.0-beta.6 '@types/cross-spawn': 6.0.2 cross-spawn: 7.0.3 globby: 11.1.0 jscodeshift: 0.15.1(@babel/preset-env@7.23.6) lodash: 4.17.21 - prettier: 2.8.8 - recast: 0.23.1 + prettier: 3.2.5 + recast: 0.23.4 + tiny-invariant: 1.3.1 transitivePeerDependencies: - supports-color dev: true - /@storybook/components@7.6.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-w4ZucbBBZ+NKMWlJKVj2I/bMBBq7gzDp9lzc4+8QaQ3vUPXKqc1ilIPYo/7UR5oxwDVMZocmMSgl9L8lvf7+Mw==} + /@storybook/components@8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-J3aJtPgaSco0sefvRMBLFsWbslhKMhaS3U+5baRqlV5bjPLZN+d4P18gP1RMaw/coh6DiKEQJZuHRoPIOdt4CA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@radix-ui/react-select': 1.2.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-toolbar': 1.0.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/client-logger': 7.6.5 + '@radix-ui/react-slot': 1.0.2(@types/react@18.0.28)(react@18.2.0) + '@storybook/client-logger': 8.0.0-beta.6 '@storybook/csf': 0.1.2 '@storybook/global': 5.0.0 - '@storybook/theming': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.6.5 + '@storybook/icons': 1.2.5(react-dom@18.2.0)(react@18.2.0) + '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 8.0.0-beta.6 memoizerific: 1.11.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - use-resize-observer: 9.1.0(react-dom@18.2.0)(react@18.2.0) util-deprecate: 1.0.2 transitivePeerDependencies: - '@types/react' - - '@types/react-dom' - dev: true - - /@storybook/core-client@7.6.5: - resolution: {integrity: sha512-6FtyJcz8MSl+JYwNJZ53FM6rkT27pFHWcJPdtw/9229Ec8as9RpkNeZ/NBZjRTeDkn9Ki0VOiVAefNie9tZ/8Q==} - dependencies: - '@storybook/client-logger': 7.6.5 - '@storybook/preview-api': 7.6.5 dev: true - /@storybook/core-common@7.6.5: - resolution: {integrity: sha512-z4EgzZSIVbID6Ib0jhh3jimKeaDWU8OOhoZYfn3galFmgQWowWOv1oMgipWiXfRLWw9DaLFQiCHIdLANH+VO2g==} + /@storybook/core-common@8.0.0-beta.6: + resolution: {integrity: sha512-Mah4Kx/VBNhHaX6neYHTiVwfD93yf3LVVfLTS9WcJFOpek74EAAqbARV3vzOn/utOI75N7yu2PCVoKi5KkDoVw==} dependencies: - '@storybook/core-events': 7.6.5 - '@storybook/node-logger': 7.6.5 - '@storybook/types': 7.6.5 - '@types/find-cache-dir': 3.2.1 - '@types/node': 18.17.15 - '@types/node-fetch': 2.6.4 - '@types/pretty-hrtime': 1.0.1 + '@storybook/core-events': 8.0.0-beta.6 + '@storybook/csf-tools': 8.0.0-beta.6 + '@storybook/node-logger': 8.0.0-beta.6 + '@storybook/types': 8.0.0-beta.6 + '@yarnpkg/fslib': 2.10.3 + '@yarnpkg/libzip': 2.3.0 chalk: 4.1.2 - esbuild: 0.18.17 - esbuild-register: 3.5.0(esbuild@0.18.17) + cross-spawn: 7.0.3 + esbuild: 0.18.20 + esbuild-register: 3.5.0(esbuild@0.18.20) + execa: 5.1.1 file-system-cache: 2.3.0 find-cache-dir: 3.3.2 find-up: 5.0.0 @@ -6842,40 +6534,46 @@ packages: pkg-dir: 5.0.0 pretty-hrtime: 1.0.3 resolve-from: 5.0.0 + semver: 7.5.4 + tempy: 1.0.1 + tiny-invariant: 1.3.1 ts-dedent: 2.2.0 + util: 0.12.5 transitivePeerDependencies: - encoding - supports-color dev: true - /@storybook/core-events@7.6.5: - resolution: {integrity: sha512-zk2q/qicYXAzHA4oV3GDbIql+Kd4TOHUgDE8e4jPCOPp856z2ScqEKUAbiJizs6eEJOH4nW9Db1kuzgrBVEykQ==} + /@storybook/core-events@8.0.0-beta.6: + resolution: {integrity: sha512-ZyEVkOJ5gGGTfHjyasyeZgNGoeVJwVkLFRpV6cUl8hzOT29R5iDsf5PbJdrpF1x2pm1oLumeRckYQ7sYhr+R/w==} dependencies: ts-dedent: 2.2.0 dev: true - /@storybook/core-server@7.6.5: - resolution: {integrity: sha512-BfKzK/ObTjUcPvE5/r1pogCifM/4nLRhOUYJl7XekwHkOQwn19e6H3/ku1W3jDoYXBu642Dc9X7l/ERjKTqxFg==} + /@storybook/core-server@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-0ciJTWZs+mCnQOUzB3WuSfkhwXKpO033M5iYK92PKu9A6KSrwdc/WCwIJHeBNnIpmxC0GEh9j6/CgIsWehwJvg==} dependencies: '@aw-web-design/x-default-browser': 1.4.126 + '@babel/core': 7.24.0 '@discoveryjs/json-ext': 0.5.7 - '@storybook/builder-manager': 7.6.5 - '@storybook/channels': 7.6.5 - '@storybook/core-common': 7.6.5 - '@storybook/core-events': 7.6.5 + '@storybook/builder-manager': 8.0.0-beta.6 + '@storybook/channels': 8.0.0-beta.6 + '@storybook/core-common': 8.0.0-beta.6 + '@storybook/core-events': 8.0.0-beta.6 '@storybook/csf': 0.1.2 - '@storybook/csf-tools': 7.6.5 - '@storybook/docs-mdx': 0.1.0 + '@storybook/csf-tools': 8.0.0-beta.6 + '@storybook/docs-mdx': 3.0.0 '@storybook/global': 5.0.0 - '@storybook/manager': 7.6.5 - '@storybook/node-logger': 7.6.5 - '@storybook/preview-api': 7.6.5 - '@storybook/telemetry': 7.6.5 - '@storybook/types': 7.6.5 + '@storybook/manager': 8.0.0-beta.6 + '@storybook/manager-api': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/node-logger': 8.0.0-beta.6 + '@storybook/preview-api': 8.0.0-beta.6 + '@storybook/telemetry': 8.0.0-beta.6 + '@storybook/types': 8.0.0-beta.6 '@types/detect-port': 1.3.2 '@types/node': 18.17.15 '@types/pretty-hrtime': 1.0.1 - '@types/semver': 7.5.6 + '@types/semver': 7.5.8 better-opn: 3.0.2 chalk: 4.1.2 cli-table3: 0.6.3 @@ -6884,7 +6582,7 @@ packages: express: 4.18.2 fs-extra: 11.1.1 globby: 11.1.0 - ip: 2.0.0 + ip: 2.0.1 lodash: 4.17.21 open: 8.4.2 pretty-hrtime: 1.0.3 @@ -6897,34 +6595,36 @@ packages: util: 0.12.5 util-deprecate: 1.0.2 watchpack: 2.4.0 - ws: 8.15.1(bufferutil@4.0.7)(utf-8-validate@6.0.3) + ws: 8.16.0(bufferutil@4.0.7)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - encoding + - react + - react-dom - supports-color - utf-8-validate dev: true - /@storybook/csf-plugin@7.6.5: - resolution: {integrity: sha512-iQ8Y/Qq1IUhHRddjDVicWJA2sM7OZA1FR97OvWUT2240WjCuQSCfy32JD8TQlYjqXgEolJeLPv3zW4qH5om4LQ==} + /@storybook/csf-plugin@8.0.0-beta.6: + resolution: {integrity: sha512-cYI/4OndODf0utV0DxJs8AOKbmjCG+pEgxQGcmPtGnkSmEuieUwpQpN7v+fEIN7IPUQLYvs0wspR0njZQAIzyA==} dependencies: - '@storybook/csf-tools': 7.6.5 + '@storybook/csf-tools': 8.0.0-beta.6 unplugin: 1.5.1 transitivePeerDependencies: - supports-color dev: true - /@storybook/csf-tools@7.6.5: - resolution: {integrity: sha512-1iaCh7nt+WE7Q5UwRhLLc5flMNoAV/vBr0tvDSCKiHaO+D3dZzlZOe/U+S6wegdyN2QNcvT2xs179CcrX6Qp6w==} + /@storybook/csf-tools@8.0.0-beta.6: + resolution: {integrity: sha512-wwzbE6f8ykrvIeZlXYTba0IA8D5GPSyZ4L0+PqRAYHm3ozu0DXqtm4USDHKrjYAzuD+W+fG/6qIOQmsWYbNmpA==} dependencies: - '@babel/generator': 7.23.4 - '@babel/parser': 7.23.4 - '@babel/traverse': 7.23.4 - '@babel/types': 7.23.4 + '@babel/generator': 7.23.6 + '@babel/parser': 7.24.0 + '@babel/traverse': 7.24.0 + '@babel/types': 7.24.0 '@storybook/csf': 0.1.2 - '@storybook/types': 7.6.5 + '@storybook/types': 8.0.0-beta.6 fs-extra: 11.1.1 - recast: 0.23.1 + recast: 0.23.4 ts-dedent: 2.2.0 transitivePeerDependencies: - supports-color @@ -6936,16 +6636,16 @@ packages: type-fest: 2.19.0 dev: true - /@storybook/docs-mdx@0.1.0: - resolution: {integrity: sha512-JDaBR9lwVY4eSH5W8EGHrhODjygPd6QImRbwjAuJNEnY0Vw4ie3bPkeGfnacB3OBW6u/agqPv2aRlR46JcAQLg==} + /@storybook/docs-mdx@3.0.0: + resolution: {integrity: sha512-NmiGXl2HU33zpwTv1XORe9XG9H+dRUC1Jl11u92L4xr062pZtrShLmD4VKIsOQujxhhOrbxpwhNOt+6TdhyIdQ==} dev: true - /@storybook/docs-tools@7.6.5: - resolution: {integrity: sha512-UyHkHu5Af6jMpYsR4lZ69D32GQGeA0pLAn7jaBbQndgAjBdK1ykZcifiUC7Wz1hG7+YpuYspEGuDEddOh+X8FQ==} + /@storybook/docs-tools@8.0.0-beta.6: + resolution: {integrity: sha512-fSKXEu0vegzqC2HT1RaOKqi0+W/vIn+qa5D+dZHkj2BnceYxWAGYsX9ZZPHW6DUvvwp0WZp1vz57nPUhsLvcQg==} dependencies: - '@storybook/core-common': 7.6.5 - '@storybook/preview-api': 7.6.5 - '@storybook/types': 7.6.5 + '@storybook/core-common': 8.0.0-beta.6 + '@storybook/preview-api': 8.0.0-beta.6 + '@storybook/types': 8.0.0-beta.6 '@types/doctrine': 0.0.3 assert: 2.1.0 doctrine: 3.0.0 @@ -6955,44 +6655,47 @@ packages: - supports-color dev: true - /@storybook/expect@28.1.3-5: - resolution: {integrity: sha512-lS1oJnY1qTAxnH87C765NdfvGhksA6hBcbUVI5CHiSbNsEtr456wtg/z+dT9XlPriq1D5t2SgfNL9dBAoIGyIA==} - dependencies: - '@types/jest': 28.1.3 - dev: true - /@storybook/global@5.0.0: resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} dev: true - /@storybook/jest@0.2.3(vitest@0.34.6): - resolution: {integrity: sha512-ov5izrmbAFObzKeh9AOC5MlmFxAcf0o5i6YFGae9sDx6DGh6alXsRM+chIbucVkUwVHVlSzdfbLDEFGY/ShaYw==} + /@storybook/icons@1.2.5(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-m3jnuE+zmkZy6K+cdUDzAoUuCJyl0fWCAXPCji7VZCH1TzFohyvnPqhc9JMkQpanej2TOW3wWXaplPzHghcBSg==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@storybook/expect': 28.1.3-5 - '@testing-library/jest-dom': 6.1.2(@types/jest@28.1.3)(vitest@0.34.6) - '@types/jest': 28.1.3 - jest-mock: 27.5.1 - transitivePeerDependencies: - - '@jest/globals' - - jest - - vitest + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + + /@storybook/instrumenter@8.0.0-beta.6: + resolution: {integrity: sha512-xJ3qkvj8dce7nJEa6hmp4PDDZJMBuP5UlSKPidiMAfEsB0MeUbDulTFNDb0t1DwcH9ywinDl8TilSzG4+r1kDA==} + dependencies: + '@storybook/channels': 8.0.0-beta.6 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/core-events': 8.0.0-beta.6 + '@storybook/global': 5.0.0 + '@storybook/preview-api': 8.0.0-beta.6 + '@vitest/utils': 0.34.6 + util: 0.12.5 dev: true - /@storybook/manager-api@7.6.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-tE3OShOcs6A3XtI3NJd6hYQOZLaP++Fn0dCtowBwYh/vS1EN/AyroVmL97tsxn1DZTyoRt0GidwbB6dvLMBOwA==} + /@storybook/manager-api@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-kOGOT/yGFgKzld9IL1HREouFwZ0LpFuXZZOHBih5ydK8XT+bkWF6e3SiqthB3qtqpd0eVLAbNiPfY9R8t3qfWg==} dependencies: - '@storybook/channels': 7.6.5 - '@storybook/client-logger': 7.6.5 - '@storybook/core-events': 7.6.5 + '@storybook/channels': 8.0.0-beta.6 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/core-events': 8.0.0-beta.6 '@storybook/csf': 0.1.2 '@storybook/global': 5.0.0 - '@storybook/router': 7.6.5 - '@storybook/theming': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.6.5 + '@storybook/router': 8.0.0-beta.6 + '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 8.0.0-beta.6 dequal: 2.0.3 lodash: 4.17.21 memoizerific: 1.11.3 - semver: 7.5.4 store2: 2.14.2 telejson: 7.2.0 ts-dedent: 2.2.0 @@ -7001,47 +6704,39 @@ packages: - react-dom dev: true - /@storybook/manager@7.6.5: - resolution: {integrity: sha512-y1KLH0O1PGPyMxGMvOhppzFSO7r4ibjTve5iqsI0JZwxUjNuBKRLYbrhXdAyC2iacvxYNrHgevae1k9XdD+FQw==} - dev: true - - /@storybook/mdx2-csf@1.0.0: - resolution: {integrity: sha512-dBAnEL4HfxxJmv7LdEYUoZlQbWj9APZNIbOaq0tgF8XkxiIbzqvgB0jhL/9UOrysSDbQWBiCRTu2wOVxedGfmw==} + /@storybook/manager@8.0.0-beta.6: + resolution: {integrity: sha512-FeQ2/CIasSOgcTMEE3QYMFa92KeMnfEMyUVO4hHEmPh3SqPsz6OOv8p0bQvN0SWWBgZarbhFR0dKC3W10yYrXg==} dev: true - /@storybook/node-logger@7.6.5: - resolution: {integrity: sha512-xKw6IH1wLkIssekdBv3bd13xYKUF1t8EwqDR8BYcN8AVjZlqJMTifssqG4bYV+G/B7J3tz4ugJ5nmtWg6RQ0Qw==} + /@storybook/node-logger@8.0.0-beta.6: + resolution: {integrity: sha512-nmBlmZ8wzJiU1/ubhUmFeWQaJPBv6l6s0Cndk04omPSjROa+O1whoPhDTVGvWC28zm17tmAYVcQRujkdoi+YBA==} dev: true - /@storybook/postinstall@7.6.5: - resolution: {integrity: sha512-12WxfpqGKsk7GQ3KWiZSbamsYK8vtRmhOTkavZ9IQkcJ/zuVfmqK80/Mds+njJMudUPzuREuSFGWACczo17EDA==} - dev: true - - /@storybook/preview-api@7.6.5: - resolution: {integrity: sha512-9XzuDXXgNuA6dDZ3DXsUwEG6ElxeTbzLuYuzcjtS1FusSICZ2iYmxfS0GfSud9MjPPYOJYoSOvMdIHjorjgByA==} + /@storybook/preview-api@8.0.0-beta.6: + resolution: {integrity: sha512-V07MF1ArjBGi2EPSjrEW8pjCoW/TIwxNDilcO9cD12LHrDQGXuo/iKyR47TGUYmcJ/u1I2Eu9cjyVj9DVyppag==} dependencies: - '@storybook/channels': 7.6.5 - '@storybook/client-logger': 7.6.5 - '@storybook/core-events': 7.6.5 + '@storybook/channels': 8.0.0-beta.6 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/core-events': 8.0.0-beta.6 '@storybook/csf': 0.1.2 '@storybook/global': 5.0.0 - '@storybook/types': 7.6.5 + '@storybook/types': 8.0.0-beta.6 '@types/qs': 6.9.7 dequal: 2.0.3 lodash: 4.17.21 memoizerific: 1.11.3 qs: 6.11.1 - synchronous-promise: 2.0.17 + tiny-invariant: 1.3.1 ts-dedent: 2.2.0 util-deprecate: 1.0.2 dev: true - /@storybook/preview@7.6.5: - resolution: {integrity: sha512-zmLa7C7yFGTYhgGZXoecdww9rx0Z5HpNi/GDBRWoNSK+FEdE8Jj2jF5NJ2ncldtYIyegz9ku29JFMKbhMj9K5Q==} + /@storybook/preview@8.0.0-beta.6: + resolution: {integrity: sha512-tp3Wyvjsbf5r5RhbCQSafArQWJAir1bmIJWGG2S4o2E3YT6TlHFpR078tNJtgXqsPyG0yhF9vhRRkDczrPX/Gw==} dev: true - /@storybook/react-dom-shim@7.6.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Qp3N3zENdvx20ikHmz5yI03z+mAWF8bUAwUofqXarVtZUkBNtvfTfUwgAezOAF0eClClH+ktIziIKd976tLSPw==} + /@storybook/react-dom-shim@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-l14oDKAW2jyrXynHKP6SoNGal78gXcWCgj0zLwSDWpKgAFWC7SuIneuxLv6weU1D4+f9Y9FBrz+K3CCaMgMtOA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -7050,24 +6745,23 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/react-vite@7.6.5(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.1)(typescript@5.3.3)(vite@5.0.10): - resolution: {integrity: sha512-fIoSBbou3rQdOo6qX/nD5givb3qIOSwXeZWjAqRB6560cqmeSQFlRGtKUJ0nzQYADwJ0/iNHz3nOvJOOSnPepA==} - engines: {node: '>=16'} + /@storybook/react-vite@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(rollup@4.12.0)(typescript@5.3.3)(vite@5.1.4): + resolution: {integrity: sha512-Tvz25pTXmhncDxprjIYsnXc68Lfa9idDybpRTRRbtvjsJyVpZogUdgz2/kddGNTuX3mqz6vmTMWiLiIVh+ytQA==} + engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 + vite: ^4.0.0 || ^5.0.0 dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.3)(vite@5.0.10) - '@rollup/pluginutils': 5.1.0(rollup@4.9.1) - '@storybook/builder-vite': 7.6.5(typescript@5.3.3)(vite@5.0.10) - '@storybook/react': 7.6.5(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3) - '@vitejs/plugin-react': 3.1.0(vite@5.0.10) - magic-string: 0.30.5 + '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.3)(vite@5.1.4) + '@rollup/pluginutils': 5.1.0(rollup@4.12.0) + '@storybook/builder-vite': 8.0.0-beta.6(typescript@5.3.3)(vite@5.1.4) + '@storybook/react': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3) + magic-string: 0.30.7 react: 18.2.0 react-docgen: 7.0.1 react-dom: 18.2.0(react@18.2.0) - vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0) + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) transitivePeerDependencies: - '@preact/preset-vite' - encoding @@ -7077,24 +6771,23 @@ packages: - vite-plugin-glimmerx dev: true - /@storybook/react@7.6.5(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3): - resolution: {integrity: sha512-z0l5T+gL//VekMXnHi+lW5qr7OQ8X7WoeIRMk38e62ppSpGUZRfoxRmmhU/9YcIFAlCgMaoLSYmhOceKGRZuVw==} - engines: {node: '>=16.0.0'} + /@storybook/react@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3): + resolution: {integrity: sha512-69B0c08HDYHEgZRRnkB+3z4dY/HO/GMSiRzRCNpzI0SBQzk1YwDzG9MOtkNgGqzdLK3e3DveSXb5Uyy1cB0ZiQ==} + engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - typescript: '*' + typescript: '>= 4.2.x' peerDependenciesMeta: typescript: optional: true dependencies: - '@storybook/client-logger': 7.6.5 - '@storybook/core-client': 7.6.5 - '@storybook/docs-tools': 7.6.5 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/docs-tools': 8.0.0-beta.6 '@storybook/global': 5.0.0 - '@storybook/preview-api': 7.6.5 - '@storybook/react-dom-shim': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.6.5 + '@storybook/preview-api': 8.0.0-beta.6 + '@storybook/react-dom-shim': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 8.0.0-beta.6 '@types/escodegen': 0.0.6 '@types/estree': 0.0.51 '@types/node': 18.17.15 @@ -7108,6 +6801,7 @@ packages: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) react-element-to-jsx-string: 15.0.0(react-dom@18.2.0)(react@18.2.0) + semver: 7.5.4 ts-dedent: 2.2.0 type-fest: 2.19.0 typescript: 5.3.3 @@ -7117,30 +6811,30 @@ packages: - supports-color dev: true - /@storybook/router@7.6.5: - resolution: {integrity: sha512-QiTC86gRuoepzzmS6HNJZTwfz/n27NcqtaVEIxJi1Yvsx2/kLa9NkRhylNkfTuZ1gEry9stAlKWanMsB2aKyjQ==} + /@storybook/router@8.0.0-beta.6: + resolution: {integrity: sha512-JjLyDaVzCH3kmNsOkuJ8/U2bPIoReZZ/QsgHJdfvm22T2wKNjQ+lfNrQptBgNybfi1o/Tmn9VbCdRqurSlh9Dw==} dependencies: - '@storybook/client-logger': 7.6.5 + '@storybook/client-logger': 8.0.0-beta.6 memoizerific: 1.11.3 qs: 6.11.1 dev: true - /@storybook/source-loader@7.6.5: - resolution: {integrity: sha512-3GpXJY9GUOOl3Uq/xcsJ12XWLBNZJwUWzwkBm4Eev1xl5eg/ygeyJflwM5egsA1NfkV77hNxtjQcbfw4cBtqdg==} + /@storybook/source-loader@8.0.0-beta.6: + resolution: {integrity: sha512-cYtjnuJZgm8MS9SsNsbuhuFz2d7j6BKRLZByBUqELrK+ftup0qqOWM+78w26qn3nPgA8myZXWxGa+V/Pjxio5w==} dependencies: '@storybook/csf': 0.1.2 - '@storybook/types': 7.6.5 + '@storybook/types': 8.0.0-beta.6 estraverse: 5.3.0 lodash: 4.17.21 - prettier: 2.8.8 + prettier: 3.2.5 dev: true - /@storybook/telemetry@7.6.5: - resolution: {integrity: sha512-FiLRh9k9LoGphqgBqPYySWdGqplihiZyDwqdo+Qs19RcQ/eiKg0W7fdA09nStcdcsHmDl/1cMfRhz9KUiMtwOw==} + /@storybook/telemetry@8.0.0-beta.6: + resolution: {integrity: sha512-3CU5Sdj8eVm0tb35GriMkDrxJyTpdGcfU/hgUnsuw+I4eHYdZsc4Boh9uXWTVNsaBaoqbD/MP1aqbfxkElqPxQ==} dependencies: - '@storybook/client-logger': 7.6.5 - '@storybook/core-common': 7.6.5 - '@storybook/csf-tools': 7.6.5 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/core-common': 8.0.0-beta.6 + '@storybook/csf-tools': 8.0.0-beta.6 chalk: 4.1.2 detect-package-manager: 2.0.1 fetch-retry: 5.0.4 @@ -7151,86 +6845,104 @@ packages: - supports-color dev: true - /@storybook/testing-library@0.2.2: - resolution: {integrity: sha512-L8sXFJUHmrlyU2BsWWZGuAjv39Jl1uAqUHdxmN42JY15M4+XCMjGlArdCCjDe1wpTSW6USYISA9axjZojgtvnw==} + /@storybook/test@8.0.0-beta.6(vitest@0.34.6): + resolution: {integrity: sha512-GcV76EX3U77G+k8+0V+jAa/sJQZEuNb/4W+g/RaqGLRCEG73UADzkgRuFm60UQUBGtltvvRZU9sIPVbFTJFxuA==} dependencies: - '@testing-library/dom': 9.2.0 - '@testing-library/user-event': 14.4.3(@testing-library/dom@9.2.0) - ts-dedent: 2.2.0 + '@storybook/client-logger': 8.0.0-beta.6 + '@storybook/core-events': 8.0.0-beta.6 + '@storybook/instrumenter': 8.0.0-beta.6 + '@storybook/preview-api': 8.0.0-beta.6 + '@testing-library/dom': 9.3.3 + '@testing-library/jest-dom': 6.4.2(vitest@0.34.6) + '@testing-library/user-event': 14.5.2(@testing-library/dom@9.3.3) + '@vitest/expect': 1.1.3 + '@vitest/spy': 1.3.0 + chai: 4.3.10 + util: 0.12.5 + transitivePeerDependencies: + - '@jest/globals' + - '@types/bun' + - '@types/jest' + - jest + - vitest dev: true - /@storybook/theming@7.6.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-RpcWT0YEgiobO41McVPDfQQHHFnjyr1sJnNTPJIvOUgSfURdgSj17mQVxtD5xcXcPWUdle5UhIOrCixHbL/NNw==} + /@storybook/theming@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-WXvDbV257fKbHM5jHd7hOHefRSBnyZec08NGpcVOG6muJjLu8nPjazcYgISqFc97MkFmxvEDPFfX8CvBEeefzA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true dependencies: - '@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0) - '@storybook/client-logger': 7.6.5 + '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) + '@storybook/client-logger': 8.0.0-beta.6 '@storybook/global': 5.0.0 memoizerific: 1.11.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/types@7.6.5: - resolution: {integrity: sha512-Q757v+fYZZSaEpks/zDL5YgXRozxkgKakXFc+BoQHK5q5sVhJ+0jvpLJiAQAniIIaMIkqY/G24Kd6Uo6UdKBCg==} + /@storybook/types@8.0.0-beta.6: + resolution: {integrity: sha512-w3jq8mBcxir4P0RK3gQePeUJ0rXbnUbCKg91YBOKeitmU0+4jSr4e1EwTWOYgsyz7KtikzSNr8JXtMQn2TJD5A==} dependencies: - '@storybook/channels': 7.6.5 - '@types/babel__core': 7.20.5 + '@storybook/channels': 8.0.0-beta.6 '@types/express': 4.17.17 file-system-cache: 2.3.0 dev: true - /@storybook/vue3-vite@7.6.5(@vue/compiler-core@3.3.12)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.12): - resolution: {integrity: sha512-7wUCq2Lrjlekftd5ha3hG0GSGbbzuc370cKkBqSmwFuOfI38z5+VeYt7nDtAlncxcpVSH7DejTGRuKTlC7NyYg==} - engines: {node: ^14.18 || >=16} + /@storybook/vue3-vite@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(vite@5.1.4)(vue@3.4.21): + resolution: {integrity: sha512-Pf9W7hcHjx1FE3JmhY1iSxGq9k/Tp5n/obOCd4FJGUdIttPYFclG9km49DrCJtNfhK7M6+d2QTZ6Uds4ORWZPg==} + engines: {node: '>=18.0.0'} peerDependencies: - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 + vite: ^4.0.0 || ^5.0.0 dependencies: - '@storybook/builder-vite': 7.6.5(typescript@5.3.3)(vite@5.0.10) - '@storybook/core-server': 7.6.5 - '@storybook/vue3': 7.6.5(@vue/compiler-core@3.3.12)(vue@3.3.12) - '@vitejs/plugin-vue': 4.5.2(vite@5.0.10)(vue@3.3.12) - magic-string: 0.30.5 - vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0) - vue-docgen-api: 4.64.1(vue@3.3.12) + '@storybook/builder-vite': 8.0.0-beta.6(typescript@5.3.3)(vite@5.1.4) + '@storybook/core-server': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/vue3': 8.0.0-beta.6(vue@3.4.21) + find-package-json: 1.2.0 + magic-string: 0.30.7 + typescript: 5.3.3 + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) + vue-component-meta: 1.8.27(typescript@5.3.3) + vue-docgen-api: 4.75.1(vue@3.4.21) transitivePeerDependencies: - '@preact/preset-vite' - - '@vue/compiler-core' - bufferutil - encoding + - react + - react-dom - supports-color - - typescript - utf-8-validate - vite-plugin-glimmerx - vue dev: true - /@storybook/vue3@7.6.5(@vue/compiler-core@3.3.12)(vue@3.3.12): - resolution: {integrity: sha512-tv/9rVc3XXDOJu5hfZtKhrhM8x4GTLKon62Rmaxlq06weqkGlfBi/V/g1EZ7OE71Pi+woKS/TX7p9qbRrvgahg==} - engines: {node: '>=16.0.0'} + /@storybook/vue3@8.0.0-beta.6(vue@3.4.21): + resolution: {integrity: sha512-027KDM1f6y0XzMK1yE5W4JKY/VsbGpr1kj0mvEKxaPUYgBJV9wTHADWgmluiJS/e/MWrCCZql5mE+D9lVJUjoA==} + engines: {node: '>=18.0.0'} peerDependencies: - '@vue/compiler-core': ^3.0.0 vue: ^3.0.0 dependencies: - '@storybook/core-client': 7.6.5 - '@storybook/docs-tools': 7.6.5 + '@storybook/docs-tools': 8.0.0-beta.6 '@storybook/global': 5.0.0 - '@storybook/preview-api': 7.6.5 - '@storybook/types': 7.6.5 - '@vue/compiler-core': 3.3.12 + '@storybook/preview-api': 8.0.0-beta.6 + '@storybook/types': 8.0.0-beta.6 + '@vue/compiler-core': 3.4.18 lodash: 4.17.21 ts-dedent: 2.2.0 type-fest: 2.19.0 - vue: 3.3.12(typescript@5.3.3) + vue: 3.4.21(typescript@5.3.3) vue-component-type-helpers: 1.8.27 transitivePeerDependencies: - encoding - supports-color dev: true - /@swc/cli@0.1.63(@swc/core@1.3.100)(chokidar@3.5.3): + /@swc/cli@0.1.63(@swc/core@1.3.105): resolution: {integrity: sha512-EM9oxxHzmmsprYRbGqsS2M4M/Gr5Gkcl0ROYYIdlUyTkhOiX822EQiRCpPCwdutdnzH2GyaTN7wc6i0Y+CKd3A==} engines: {node: '>= 12.13'} hasBin: true @@ -7242,7 +6954,27 @@ packages: optional: true dependencies: '@mole-inc/bin-wrapper': 8.0.1 - '@swc/core': 1.3.100 + '@swc/core': 1.3.105 + commander: 7.2.0 + fast-glob: 3.3.2 + semver: 7.5.4 + slash: 3.0.0 + source-map: 0.7.4 + dev: false + + /@swc/cli@0.1.63(@swc/core@1.3.107)(chokidar@3.5.3): + resolution: {integrity: sha512-EM9oxxHzmmsprYRbGqsS2M4M/Gr5Gkcl0ROYYIdlUyTkhOiX822EQiRCpPCwdutdnzH2GyaTN7wc6i0Y+CKd3A==} + engines: {node: '>= 12.13'} + hasBin: true + peerDependencies: + '@swc/core': ^1.2.66 + chokidar: 3.5.3 + peerDependenciesMeta: + chokidar: + optional: true + dependencies: + '@mole-inc/bin-wrapper': 8.0.1 + '@swc/core': 1.3.107 chokidar: 3.5.3 commander: 7.2.0 fast-glob: 3.3.2 @@ -7262,8 +6994,16 @@ packages: dev: false optional: true - /@swc/core-darwin-arm64@1.3.100: - resolution: {integrity: sha512-XVWFsKe6ei+SsDbwmsuRkYck1SXRpO60Hioa4hoLwR8fxbA9eVp6enZtMxzVVMBi8ej5seZ4HZQeAWepbukiBw==} + /@swc/core-darwin-arm64@1.3.105: + resolution: {integrity: sha512-buWeweLVDXXmcnfIemH4PGnpjwsDTUGitnPchdftb0u1FU8zSSP/lw/pUCBDG/XvWAp7c/aFxgN4CyG0j7eayA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + optional: true + + /@swc/core-darwin-arm64@1.3.107: + resolution: {integrity: sha512-47tD/5vSXWxPd0j/ZllyQUg4bqalbQTsmqSw0J4dDdS82MWqCAwUErUrAZPRjBkjNQ6Kmrf5rpCWaGTtPw+ngw==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] @@ -7279,8 +7019,16 @@ packages: dev: false optional: true - /@swc/core-darwin-x64@1.3.100: - resolution: {integrity: sha512-KF/MXrnH1nakm1wbt4XV8FS7kvqD9TGmVxeJ0U4bbvxXMvzeYUurzg3AJUTXYmXDhH/VXOYJE5N5RkwZZPs5iA==} + /@swc/core-darwin-x64@1.3.105: + resolution: {integrity: sha512-hFmXPApqjA/8sy/9NpljHVaKi1OvL9QkJ2MbbTCCbJERuHMpMUeMBUWipHRfepGHFhU+9B9zkEup/qJaJR4XIg==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + optional: true + + /@swc/core-darwin-x64@1.3.107: + resolution: {integrity: sha512-hwiLJ2ulNkBGAh1m1eTfeY1417OAYbRGcb/iGsJ+LuVLvKAhU/itzsl535CvcwAlt2LayeCFfcI8gdeOLeZa9A==} engines: {node: '>=10'} cpu: [x64] os: [darwin] @@ -7307,6 +7055,22 @@ packages: dev: false optional: true + /@swc/core-linux-arm-gnueabihf@1.3.105: + resolution: {integrity: sha512-mwXyMC41oMKkKrPpL8uJpOxw7fyfQoVtIw3Y5p0Blabk+espNYqix0E8VymHdRKuLmM//z5wVmMsuHdGBHvZeg==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + requiresBuild: true + optional: true + + /@swc/core-linux-arm-gnueabihf@1.3.107: + resolution: {integrity: sha512-I2wzcC0KXqh0OwymCmYwNRgZ9nxX7DWnOOStJXV3pS0uB83TXAkmqd7wvMBuIl9qu4Hfomi9aDM7IlEEn9tumQ==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + requiresBuild: true + optional: true + /@swc/core-linux-arm-gnueabihf@1.3.56: resolution: {integrity: sha512-LWwPo6NnJkH01+ukqvkoNIOpMdw+Zundm4vBeicwyVrkP+mC3kwVfi03TUFpQUz3kRKdw/QEnxGTj+MouCPbtw==} engines: {node: '>=10'} @@ -7316,8 +7080,16 @@ packages: dev: false optional: true - /@swc/core-linux-arm64-gnu@1.3.100: - resolution: {integrity: sha512-p8hikNnAEJrw5vHCtKiFT4hdlQxk1V7vqPmvUDgL/qe2menQDK/i12tbz7/3BEQ4UqUPnvwpmVn2d19RdEMNxw==} + /@swc/core-linux-arm64-gnu@1.3.105: + resolution: {integrity: sha512-H7yEIVydnUtqBSUxwmO6vpIQn7j+Rr0DF6ZOORPyd/SFzQJK9cJRtmJQ3ZMzlJ1Bb+1gr3MvjgLEnmyCYEm2Hg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optional: true + + /@swc/core-linux-arm64-gnu@1.3.107: + resolution: {integrity: sha512-HWgnn7JORYlOYnGsdunpSF8A+BCZKPLzLtEUA27/M/ZuANcMZabKL9Zurt7XQXq888uJFAt98Gy+59PU90aHKg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] @@ -7333,8 +7105,16 @@ packages: dev: false optional: true - /@swc/core-linux-arm64-musl@1.3.100: - resolution: {integrity: sha512-BWx/0EeY89WC4q3AaIaBSGfQxkYxIlS3mX19dwy2FWJs/O+fMvF9oLk/CyJPOZzbp+1DjGeeoGFuDYpiNO91JA==} + /@swc/core-linux-arm64-musl@1.3.105: + resolution: {integrity: sha512-Jg7RTFT3pGFdGt5elPV6oDkinRy7q9cXpenjXnJnM2uvx3jOwnsAhexPyCDHom8SHL0j+9kaLLC66T3Gz1E4UA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optional: true + + /@swc/core-linux-arm64-musl@1.3.107: + resolution: {integrity: sha512-vfPF74cWfAm8hyhS8yvYI94ucMHIo8xIYU+oFOW9uvDlGQRgnUf/6DEVbLyt/3yfX5723Ln57U8uiMALbX5Pyw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] @@ -7350,8 +7130,16 @@ packages: dev: false optional: true - /@swc/core-linux-x64-gnu@1.3.100: - resolution: {integrity: sha512-XUdGu3dxAkjsahLYnm8WijPfKebo+jHgHphDxaW0ovI6sTdmEGFDew7QzKZRlbYL2jRkUuuKuDGvD6lO5frmhA==} + /@swc/core-linux-x64-gnu@1.3.105: + resolution: {integrity: sha512-DJghplpyusAmp1X5pW/y93MmS/u83Sx5GrpJxI6KLPa82+NItTgMcl8KBQmW5GYAJpVKZyaIvBanS5TdR8aN2w==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + optional: true + + /@swc/core-linux-x64-gnu@1.3.107: + resolution: {integrity: sha512-uBVNhIg0ip8rH9OnOsCARUFZ3Mq3tbPHxtmWk9uAa5u8jQwGWeBx5+nTHpDOVd3YxKb6+5xDEI/edeeLpha/9g==} engines: {node: '>=10'} cpu: [x64] os: [linux] @@ -7367,8 +7155,16 @@ packages: dev: false optional: true - /@swc/core-linux-x64-musl@1.3.100: - resolution: {integrity: sha512-PhoXKf+f0OaNW/GCuXjJ0/KfK9EJX7z2gko+7nVnEA0p3aaPtbP6cq1Ubbl6CMoPL+Ci3gZ7nYumDqXNc3CtLQ==} + /@swc/core-linux-x64-musl@1.3.105: + resolution: {integrity: sha512-wD5jL2dZH/5nPNssBo6jhOvkI0lmWnVR4vnOXWjuXgjq1S0AJpO5jdre/6pYLmf26hft3M42bteDnjR4AAZ38w==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + optional: true + + /@swc/core-linux-x64-musl@1.3.107: + resolution: {integrity: sha512-mvACkUvzSIB12q1H5JtabWATbk3AG+pQgXEN95AmEX2ZA5gbP9+B+mijsg7Sd/3tboHr7ZHLz/q3SHTvdFJrEw==} engines: {node: '>=10'} cpu: [x64] os: [linux] @@ -7384,8 +7180,16 @@ packages: dev: false optional: true - /@swc/core-win32-arm64-msvc@1.3.100: - resolution: {integrity: sha512-PwLADZN6F9cXn4Jw52FeP/MCLVHm8vwouZZSOoOScDtihjY495SSjdPnlosMaRSR4wJQssGwiD/4MbpgQPqbAw==} + /@swc/core-win32-arm64-msvc@1.3.105: + resolution: {integrity: sha512-UqJtwILUHRw2+3UTPnRkZrzM/bGdQtbR4UFdp79mZQYfryeOUVNg7aJj/bWUTkKtLiZ3o+FBNrM/x2X1mJX5bA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + optional: true + + /@swc/core-win32-arm64-msvc@1.3.107: + resolution: {integrity: sha512-J3P14Ngy/1qtapzbguEH41kY109t6DFxfbK4Ntz9dOWNuVY3o9/RTB841ctnJk0ZHEG+BjfCJjsD2n8H5HcaOA==} engines: {node: '>=10'} cpu: [arm64] os: [win32] @@ -7401,8 +7205,16 @@ packages: dev: false optional: true - /@swc/core-win32-ia32-msvc@1.3.100: - resolution: {integrity: sha512-0f6nicKSLlDKlyPRl2JEmkpBV4aeDfRQg6n8mPqgL7bliZIcDahG0ej+HxgNjZfS3e0yjDxsNRa6sAqWU2Z60A==} + /@swc/core-win32-ia32-msvc@1.3.105: + resolution: {integrity: sha512-Z95C6vZgBEJ1snidYyjVKnVWiy/ZpPiIFIXGWkDr4ZyBgL3eZX12M6LzZ+NApHKffrbO4enbFyFomueBQgS2oA==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + optional: true + + /@swc/core-win32-ia32-msvc@1.3.107: + resolution: {integrity: sha512-ZBUtgyjTHlz8TPJh7kfwwwFma+ktr6OccB1oXC8fMSopD0AxVnQasgun3l3099wIsAB9eEsJDQ/3lDkOLs1gBA==} engines: {node: '>=10'} cpu: [ia32] os: [win32] @@ -7418,8 +7230,16 @@ packages: dev: false optional: true - /@swc/core-win32-x64-msvc@1.3.100: - resolution: {integrity: sha512-b7J0rPoMkRTa3XyUGt8PwCaIBuYWsL2DqbirrQKRESzgCvif5iNpqaM6kjIjI/5y5q1Ycv564CB51YDpiS8EtQ==} + /@swc/core-win32-x64-msvc@1.3.105: + resolution: {integrity: sha512-3J8fkyDPFsS3mszuYUY4Wfk7/B2oio9qXUwF3DzOs2MK+XgdyMLIptIxL7gdfitXJBH8k39uVjrIw1JGJDjyFA==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + requiresBuild: true + optional: true + + /@swc/core-win32-x64-msvc@1.3.107: + resolution: {integrity: sha512-Eyzo2XRqWOxqhE1gk9h7LWmUf4Bp4Xn2Ttb0ayAXFp6YSTxQIThXcT9kipXZqcpxcmDwoq8iWbbf2P8XL743EA==} engines: {node: '>=10'} cpu: [x64] os: [win32] @@ -7435,8 +7255,32 @@ packages: dev: false optional: true - /@swc/core@1.3.100: - resolution: {integrity: sha512-7dKgTyxJjlrMwFZYb1auj3Xq0D8ZBe+5oeIgfMlRU05doXZypYJe0LAk0yjj3WdbwYzpF+T1PLxwTWizI0pckw==} + /@swc/core@1.3.105: + resolution: {integrity: sha512-me2VZyr3OjqRpFrYQJJYy7x/zbFSl9nt+MAGnIcBtjDsN00iTVqEaKxBjPBFQV9BDAgPz2SRWes/DhhVm5SmMw==} + engines: {node: '>=10'} + requiresBuild: true + peerDependencies: + '@swc/helpers': ^0.5.0 + peerDependenciesMeta: + '@swc/helpers': + optional: true + dependencies: + '@swc/counter': 0.1.2 + '@swc/types': 0.1.5 + optionalDependencies: + '@swc/core-darwin-arm64': 1.3.105 + '@swc/core-darwin-x64': 1.3.105 + '@swc/core-linux-arm-gnueabihf': 1.3.105 + '@swc/core-linux-arm64-gnu': 1.3.105 + '@swc/core-linux-arm64-musl': 1.3.105 + '@swc/core-linux-x64-gnu': 1.3.105 + '@swc/core-linux-x64-musl': 1.3.105 + '@swc/core-win32-arm64-msvc': 1.3.105 + '@swc/core-win32-ia32-msvc': 1.3.105 + '@swc/core-win32-x64-msvc': 1.3.105 + + /@swc/core@1.3.107: + resolution: {integrity: sha512-zKhqDyFcTsyLIYK1iEmavljZnf4CCor5pF52UzLAz4B6Nu/4GLU+2LQVAf+oRHjusG39PTPjd2AlRT3f3QWfsQ==} engines: {node: '>=10'} requiresBuild: true peerDependencies: @@ -7448,27 +7292,39 @@ packages: '@swc/counter': 0.1.2 '@swc/types': 0.1.5 optionalDependencies: - '@swc/core-darwin-arm64': 1.3.100 - '@swc/core-darwin-x64': 1.3.100 - '@swc/core-linux-arm64-gnu': 1.3.100 - '@swc/core-linux-arm64-musl': 1.3.100 - '@swc/core-linux-x64-gnu': 1.3.100 - '@swc/core-linux-x64-musl': 1.3.100 - '@swc/core-win32-arm64-msvc': 1.3.100 - '@swc/core-win32-ia32-msvc': 1.3.100 - '@swc/core-win32-x64-msvc': 1.3.100 + '@swc/core-darwin-arm64': 1.3.107 + '@swc/core-darwin-x64': 1.3.107 + '@swc/core-linux-arm-gnueabihf': 1.3.107 + '@swc/core-linux-arm64-gnu': 1.3.107 + '@swc/core-linux-arm64-musl': 1.3.107 + '@swc/core-linux-x64-gnu': 1.3.107 + '@swc/core-linux-x64-musl': 1.3.107 + '@swc/core-win32-arm64-msvc': 1.3.107 + '@swc/core-win32-ia32-msvc': 1.3.107 + '@swc/core-win32-x64-msvc': 1.3.107 /@swc/counter@0.1.2: resolution: {integrity: sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==} - /@swc/jest@0.2.29(@swc/core@1.3.100): - resolution: {integrity: sha512-8reh5RvHBsSikDC3WGCd5ZTd2BXKkyOdK7QwynrCH58jk2cQFhhHhFBg/jvnWZehUQe/EoOImLENc9/DwbBFow==} + /@swc/jest@0.2.31(@swc/core@1.3.105): + resolution: {integrity: sha512-Gh0Ste380O8KUY1IqsKr+aOvqqs2Loa+WcWWVNwl+lhXqOWK1iTFAP1K0IDfLqAuFP68+D/PxcpBJn21e6Quvw==} + engines: {npm: '>= 7.0.0'} + peerDependencies: + '@swc/core': '*' + dependencies: + '@jest/create-cache-key-function': 29.7.0 + '@swc/core': 1.3.105 + jsonc-parser: 3.2.0 + dev: true + + /@swc/jest@0.2.31(@swc/core@1.3.107): + resolution: {integrity: sha512-Gh0Ste380O8KUY1IqsKr+aOvqqs2Loa+WcWWVNwl+lhXqOWK1iTFAP1K0IDfLqAuFP68+D/PxcpBJn21e6Quvw==} engines: {npm: '>= 7.0.0'} peerDependencies: '@swc/core': '*' dependencies: - '@jest/create-cache-key-function': 27.5.1 - '@swc/core': 1.3.100 + '@jest/create-cache-key-function': 29.7.0 + '@swc/core': 1.3.107 jsonc-parser: 3.2.0 dev: true @@ -7481,8 +7337,8 @@ packages: dev: false optional: true - /@syuilo/aiscript@0.16.0: - resolution: {integrity: sha512-CXvoWOq6kmOSUQtKv0IEf7Ebfkk5PO1LxAgLqgRRPgssPvDvINCXu/gFNXKdapkFMkmX+Gj8qjemKR1vnUS4ZA==} + /@syuilo/aiscript@0.17.0: + resolution: {integrity: sha512-3JtQ1rWJHMxQ3153zLCXMUOwrOgjPPYGBl0dPHhR0ohm4tn7okMQRugxMCT0t3YxByemb9FfiM6TUjd0tEGxdA==} dependencies: seedrandom: 3.0.5 stringz: 2.1.0 @@ -7502,26 +7358,12 @@ packages: dependencies: defer-to-connect: 2.0.1 - /@testing-library/dom@9.2.0: - resolution: {integrity: sha512-xTEnpUKiV/bMyEsE5bT4oYA0x0Z/colMtxzUY8bKyPXBNLn/e0V4ZjBZkEhms0xE4pv9QsPfSRu9AWS4y5wGvA==} - engines: {node: '>=14'} - dependencies: - '@babel/code-frame': 7.22.13 - '@babel/runtime': 7.21.0 - '@types/aria-query': 5.0.1 - aria-query: 5.1.3 - chalk: 4.1.2 - dom-accessibility-api: 0.5.16 - lz-string: 1.5.0 - pretty-format: 27.5.1 - dev: true - /@testing-library/dom@9.3.3: resolution: {integrity: sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw==} engines: {node: '>=14'} dependencies: '@babel/code-frame': 7.23.4 - '@babel/runtime': 7.23.2 + '@babel/runtime': 7.23.4 '@types/aria-query': 5.0.1 aria-query: 5.1.3 chalk: 4.1.2 @@ -7530,17 +7372,20 @@ packages: pretty-format: 27.5.1 dev: true - /@testing-library/jest-dom@6.1.2(@types/jest@28.1.3)(vitest@0.34.6): - resolution: {integrity: sha512-NP9jl1Q2qDDtx+cqogowtQtmgD2OVs37iMSIsTv5eN5ETRkf26Kj6ugVwA93/gZzzFWQAsgkKkcftDe91BJCkQ==} + /@testing-library/jest-dom@6.4.2(vitest@0.34.6): + resolution: {integrity: sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} peerDependencies: '@jest/globals': '>= 28' + '@types/bun': latest '@types/jest': '>= 28' jest: '>= 28' vitest: '>= 0.32' peerDependenciesMeta: '@jest/globals': optional: true + '@types/bun': + optional: true '@types/jest': optional: true jest: @@ -7548,39 +7393,41 @@ packages: vitest: optional: true dependencies: - '@adobe/css-tools': 4.3.1 - '@babel/runtime': 7.23.2 - '@types/jest': 28.1.3 + '@adobe/css-tools': 4.3.3 + '@babel/runtime': 7.23.4 aria-query: 5.1.3 chalk: 3.0.0 css.escape: 1.5.1 - dom-accessibility-api: 0.5.16 + dom-accessibility-api: 0.6.3 lodash: 4.17.21 redent: 3.0.0 - vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.5)(terser@5.26.0) + vitest: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1) dev: true - /@testing-library/user-event@14.4.3(@testing-library/dom@9.2.0): - resolution: {integrity: sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==} + /@testing-library/user-event@14.5.2(@testing-library/dom@9.3.3): + resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==} engines: {node: '>=12', npm: '>=6'} peerDependencies: '@testing-library/dom': '>=7.21.4' dependencies: - '@testing-library/dom': 9.2.0 + '@testing-library/dom': 9.3.3 dev: true - /@testing-library/vue@8.0.1(@vue/compiler-sfc@3.3.12)(vue@3.3.12): - resolution: {integrity: sha512-l51ZEpjTQ6glq3wM+asQ1GbKJMGcxwgHEygETx0aCRN4TjFEGvMZy4YdWKs/y7bu4bmLrxcxhbEPP7iPSW/2OQ==} + /@testing-library/vue@8.0.2(@vue/compiler-sfc@3.4.21)(vue@3.4.21): + resolution: {integrity: sha512-A8wWX+qQn0o0izpQWnGCpwQt8wAdpsVP8vPP2h5Q/jcGhZ5yKXz9PPUqhQv+45LTFaWlyRf8bArTVaB/KFFd5A==} engines: {node: '>=14'} peerDependencies: '@vue/compiler-sfc': '>= 3' vue: '>= 3' + peerDependenciesMeta: + '@vue/compiler-sfc': + optional: true dependencies: - '@babel/runtime': 7.23.2 + '@babel/runtime': 7.23.4 '@testing-library/dom': 9.3.3 - '@vue/compiler-sfc': 3.3.12 - '@vue/test-utils': 2.4.1(vue@3.3.12) - vue: 3.3.12(typescript@5.3.3) + '@vue/compiler-sfc': 3.4.21 + '@vue/test-utils': 2.4.1(vue@3.4.21) + vue: 3.4.21(typescript@5.3.3) transitivePeerDependencies: - '@vue/server-renderer' dev: true @@ -7589,6 +7436,12 @@ packages: resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} dev: false + /@transfem-org/sfm-js@0.24.4: + resolution: {integrity: sha1-0wEXqL5UJseGFO4GGFRrES6NCDk=, tarball: https://activitypub.software/api/v4/projects/2/packages/npm/@transfem-org/sfm-js/-/@transfem-org/sfm-js-0.24.4.tgz} + dependencies: + '@twemoji/parser': 15.0.0 + dev: false + /@trysound/sax@0.2.0: resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} @@ -7606,7 +7459,7 @@ packages: /@types/accepts@1.3.7: resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: true /@types/archiver@6.0.2: @@ -7626,8 +7479,8 @@ packages: /@types/babel__core@7.20.5: resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} dependencies: - '@babel/parser': 7.23.4 - '@babel/types': 7.23.4 + '@babel/parser': 7.23.6 + '@babel/types': 7.24.0 '@types/babel__generator': 7.6.7 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.4 @@ -7636,20 +7489,20 @@ packages: /@types/babel__generator@7.6.7: resolution: {integrity: sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==} dependencies: - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 dev: true /@types/babel__template@7.4.4: resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} dependencies: - '@babel/parser': 7.23.4 - '@babel/types': 7.23.4 + '@babel/parser': 7.23.6 + '@babel/types': 7.24.0 dev: true /@types/babel__traverse@7.20.4: resolution: {integrity: sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==} dependencies: - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 dev: true /@types/bcryptjs@2.4.6: @@ -7660,7 +7513,7 @@ packages: resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: '@types/connect': 3.4.35 - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: true /@types/braces@3.0.1: @@ -7672,24 +7525,18 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.4 '@types/keyv': 3.1.4 - '@types/node': 20.10.5 + '@types/node': 20.11.22 '@types/responselike': 1.0.0 dev: false - /@types/cbor@6.0.0: - resolution: {integrity: sha512-mGQ1lbYOwVti5Xlarn1bTeBZqgY0kstsdjnkoEovgohYKdBjGejHyNGXHdMBeqyQazIv32Jjp33+5pBEaSRy2w==} - dependencies: - cbor: 9.0.1 - dev: true - - /@types/chai-subset@1.3.3: - resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} + /@types/chai-subset@1.3.5: + resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} dependencies: - '@types/chai': 4.3.5 + '@types/chai': 4.3.11 dev: true - /@types/chai@4.3.5: - resolution: {integrity: sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==} + /@types/chai@4.3.11: + resolution: {integrity: sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==} dev: true /@types/color-convert@2.0.3: @@ -7705,15 +7552,15 @@ packages: /@types/connect@3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: true /@types/content-disposition@0.5.8: resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==} dev: true - /@types/cookie@0.4.1: - resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} + /@types/cookie@0.6.0: + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} dev: true /@types/core-js@2.5.8: @@ -7723,11 +7570,11 @@ packages: /@types/cross-spawn@6.0.2: resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: true - /@types/debug@4.1.7: - resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} + /@types/debug@4.1.12: + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} dependencies: '@types/ms': 0.7.34 dev: true @@ -7781,7 +7628,7 @@ packages: /@types/express-serve-static-core@4.17.33: resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 dev: true @@ -7802,7 +7649,7 @@ packages: /@types/fluent-ffmpeg@2.1.24: resolution: {integrity: sha512-g5oQO8Jgi2kFS3tTub7wLvfLztr1s8tdXmRd8PiL/hLMLzTIAyMR2sANkTggM/rdEDAg3d63nYRRVepwBiCw5A==} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: true /@types/form-data@2.5.0: @@ -7816,13 +7663,23 @@ packages: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: true /@types/graceful-fs@4.1.9: resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 + dev: true + + /@types/hast@3.0.4: + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + dependencies: + '@types/unist': 2.0.6 + dev: true + + /@types/htmlescape@1.1.3: + resolution: {integrity: sha512-tuC81YJXGUe0q8WRtBNW+uyx79rkkzWK651ALIXXYq5/u/IxjX4iHneGF2uUqzsNp+F+9J2mFZOv9jiLTtIq0w==} dev: true /@types/http-cache-semantics@4.0.4: @@ -7831,45 +7688,22 @@ packages: /@types/http-link-header@1.0.5: resolution: {integrity: sha512-AxhIKR8UbyoqCTNp9rRepkktHuUOw3DjfOfDCaO9kwI8AYzjhxyrvZq4+mRw/2daD3hYDknrtSeV6SsPwmc71w==} dependencies: - '@types/node': 20.10.5 - dev: true - - /@types/istanbul-lib-coverage@2.0.4: - resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + '@types/node': 20.11.22 dev: true /@types/istanbul-lib-coverage@2.0.6: resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - /@types/istanbul-lib-report@3.0.0: - resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} - dependencies: - '@types/istanbul-lib-coverage': 2.0.4 - dev: true - /@types/istanbul-lib-report@3.0.3: resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} dependencies: '@types/istanbul-lib-coverage': 2.0.6 - /@types/istanbul-reports@3.0.1: - resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} - dependencies: - '@types/istanbul-lib-report': 3.0.0 - dev: true - /@types/istanbul-reports@3.0.4: resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} dependencies: '@types/istanbul-lib-report': 3.0.3 - /@types/jest@28.1.3: - resolution: {integrity: sha512-Tsbjk8Y2hkBaY/gJsataeb4q9Mubw9EOz7+4RjPkzD5KjTvHHs7cpws22InaoXxAVAhF5HfFbzJjo6oKWqSZLw==} - dependencies: - jest-matcher-utils: 28.1.3 - pretty-format: 28.1.3 - dev: true - /@types/jest@29.5.10: resolution: {integrity: sha512-tE4yxKEphEyxj9s4inideLHktW/x6DwesIwWZ9NN1FKf9zbJYsnhBoA9vrHA/IuIOKwPa5PcFBNV4lpMIOEzyQ==} dependencies: @@ -7884,8 +7718,11 @@ packages: pretty-format: 29.7.0 dev: true - /@types/js-levenshtein@1.1.1: - resolution: {integrity: sha512-qC4bCqYGy1y/NP7dDVr7KJarn+PbX1nSpwA7JXdu0HxT3QYjO8MJ+cntENtHFVy2dRAyBV23OZ6MxsW1AM1L8g==} + /@types/jest@29.5.12: + resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==} + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 dev: true /@types/js-yaml@4.0.9: @@ -7895,7 +7732,7 @@ packages: /@types/jsdom@21.1.6: resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 '@types/tough-cookie': 4.0.2 parse5: 7.1.2 dev: true @@ -7919,15 +7756,21 @@ packages: /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: false /@types/lodash@4.14.191: resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==} dev: true - /@types/matter-js@0.19.5: - resolution: {integrity: sha512-pTVB5krRGb01hr8L6BJqWGoSriqUbbvJ9Fd0Qp0eAOE//w/lFvkaVHkVB8J3wXr9U3lZDzmAjJPPQn7x4wzbWg==} + /@types/matter-js@0.19.6: + resolution: {integrity: sha512-ffk6tqJM5scla+ThXmnox+mdfCo3qYk6yMjQsNcrbo6eQ5DqorVdtnaL+1agCoYzxUjmHeiNB7poBMAmhuLY7w==} + dev: true + + /@types/mdast@4.0.3: + resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==} + dependencies: + '@types/unist': 2.0.6 dev: true /@types/mdx@2.0.3: @@ -7960,13 +7803,6 @@ packages: resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} dev: true - /@types/node-fetch@2.6.4: - resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==} - dependencies: - '@types/node': 20.10.5 - form-data: 3.0.1 - dev: true - /@types/node-fetch@3.0.3: resolution: {integrity: sha512-HhggYPH5N+AQe/OmN6fmhKmRRt2XuNJow+R3pQwJxOOF9GuwM7O2mheyGeIrs5MOIeNjDEdgdoyHBOrFeJBR3g==} dependencies: @@ -7977,11 +7813,16 @@ packages: resolution: {integrity: sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==} dev: true - /@types/node@20.10.5: - resolution: {integrity: sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==} - requiresBuild: true + /@types/node@20.11.22: + resolution: {integrity: sha512-/G+IxWxma6V3E+pqK1tSl2Fo1kl41pK1yeCyDsgkF9WlVAme4j5ISYM2zR11bgLFJGLN5sVK40T4RJNuiZbEjA==} + dependencies: + undici-types: 5.26.5 + + /@types/node@20.11.5: + resolution: {integrity: sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==} dependencies: undici-types: 5.26.5 + dev: true /@types/node@20.9.1: resolution: {integrity: sha512-HhmzZh5LSJNS5O8jQKpJ/3ZcrrlG6L70hpGqMIAoM9YVD0YBRNWYsfwcXq8VnSjlNpCpgLzMXdiPo+dxcvSmiA==} @@ -7992,7 +7833,7 @@ packages: /@types/nodemailer@6.4.14: resolution: {integrity: sha512-fUWthHO9k9DSdPCSPRqcu6TWhYyxTBg382vlNIttSe9M7XfsT06y0f24KHXtbnijPGGRIcVvdKHTNikOI6qiHA==} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: true /@types/normalize-package-data@2.4.1: @@ -8009,13 +7850,13 @@ packages: resolution: {integrity: sha512-Ali0fUUn+zgr4Yy/pCTFbuiaiJpq7l7OQwFnxYVchNbNGIx0c4Wkcdje6WO89I91RAaYF+gVc1pOaizA4YKZmA==} dependencies: '@types/express': 4.17.17 - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: true /@types/oauth@0.9.4: resolution: {integrity: sha512-qk9orhti499fq5XxKCCEbd0OzdPZuancneyse3KtR+vgMiHRbh+mn8M4G6t64ob/Fg+GZGpa565MF/2dKWY32A==} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 /@types/object-assign-deep@0.4.3: resolution: {integrity: sha512-d9Gxaj5j1hzrxJ61EFEg13B4g4FgrT/DYtcDWFXPehR8DF2SUZbVMFtZIs8exkVRiqrqBpdTc/lUUZjncsPpMw==} @@ -8025,10 +7866,10 @@ packages: resolution: {integrity: sha512-ffLAxD6Xqcf2gSbtEJehj8yJ5R/2OZqD4liodQvQQ+hhO4kg1mk9ToEZQPMtNTm/zIQj2GNleQbsjPp9+UQm4Q==} dev: false - /@types/pg@8.10.9: - resolution: {integrity: sha512-UksbANNE/f8w0wOMxVKKIrLCbEMV+oM1uKejmwXr39olg4xqcfBDbXxObJAt6XxHbDa4XTKOlUEcEltXDX+XLQ==} + /@types/pg@8.11.2: + resolution: {integrity: sha512-G2Mjygf2jFMU/9hCaTYxJrwdObdcnuQde1gndooZSOHsNSaCehAuwc7EIuSA34Do8Jx2yZ19KtvW8P0j4EuUXw==} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 pg-protocol: 1.6.0 pg-types: 4.0.1 dev: true @@ -8045,14 +7886,14 @@ packages: resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==} dev: true - /@types/punycode@2.1.3: - resolution: {integrity: sha512-dFkH9Mz0yY5UfQVSrpj1grQyqRwe4TohTLlHFx4Gli8/fsaNyoOVUAsiEBZk5JBwbEJVZ49W6st8D5g6dRJb/w==} + /@types/punycode@2.1.4: + resolution: {integrity: sha512-trzh6NzBnq8yw5e35f8xe8VTYjqM3NE7bohBtvDVf/dtUer3zYTLK1Ka3DG3p7bdtoaOHZucma6FfVKlQ134pQ==} dev: true /@types/qrcode@1.5.5: resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: true /@types/qs@6.9.7: @@ -8076,13 +7917,13 @@ packages: dependencies: '@types/prop-types': 15.7.5 '@types/scheduler': 0.16.2 - csstype: 3.1.2 + csstype: 3.1.3 dev: true /@types/readdir-glob@1.1.1: resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: true /@types/rename@1.0.7: @@ -8096,11 +7937,11 @@ packages: /@types/responselike@1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: false - /@types/sanitize-html@2.9.5: - resolution: {integrity: sha512-2Sr1vd8Dw+ypsg/oDDfZ57OMSG2Befs+l2CMyCC5bVSK3CpE7lTB2aNlbbWzazgVA+Qqfuholwom6x/mWd1qmw==} + /@types/sanitize-html@2.11.0: + resolution: {integrity: sha512-7oxPGNQHXLHE48r/r/qjn7q0hlrs3kL7oZnGj0Wf/h9tj/6ibFyRkNbsDxaBBZ4XUZ0Dx5LGCyDJ04ytSofacQ==} dependencies: htmlparser2: 8.0.2 dev: true @@ -8109,34 +7950,25 @@ packages: resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} dev: true - /@types/semver@7.5.6: - resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} + /@types/seedrandom@3.0.8: + resolution: {integrity: sha512-TY1eezMU2zH2ozQoAFAQFOPpvP15g+ZgSfTZt31AUUH/Rxtnz3H+A/Sv1Snw2/amp//omibc+AEkTaA8KUeOLQ==} + dev: true + + /@types/semver@7.5.8: + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} dev: true /@types/serve-static@1.15.1: resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==} dependencies: '@types/mime': 3.0.1 - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: true /@types/serviceworker@0.0.67: resolution: {integrity: sha512-7TCH7iNsCSNb+aUD9M/36TekrWFSLCjNK8zw/3n5kOtRjbLtDfGYMXTrDnGhSfqXNwpqmt9Vd90w5C/ad1tX6Q==} dev: true - /@types/set-cookie-parser@2.4.3: - resolution: {integrity: sha512-7QhnH7bi+6KAhBB+Auejz1uV9DHiopZqu7LfR/5gZZTkejJV5nYeZZpgfFoE0N8aDsXuiYpfKyfyMatCwQhyTQ==} - dependencies: - '@types/node': 20.10.5 - dev: true - - /@types/sharp@0.32.0: - resolution: {integrity: sha512-OOi3kL+FZDnPhVzsfD37J88FNeZh6gQsGcLc95NbeURRGvmSjeXiDcyWzF2o3yh/gQAUn2uhh/e+CPCa5nwAxw==} - deprecated: This is a stub types definition. sharp provides its own type definitions, so you do not need this installed. - dependencies: - sharp: 0.32.6 - dev: true - /@types/simple-oauth2@5.0.7: resolution: {integrity: sha512-8JbWVJbiTSBQP/7eiyGKyXWAqp3dKQZpaA+pdW16FCi32ujkzRMG8JfjoAzdWt6W8U591ZNdHcPtP2D7ILTKuA==} dev: true @@ -8162,6 +7994,10 @@ packages: /@types/stack-utils@2.0.3: resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + /@types/statuses@2.0.4: + resolution: {integrity: sha512-eqNDvZsCNY49OAXB0Firg/Sc2BgoWsntsLUdybGFOhAfCD6QJ2n9HXUIHGqt5qjrxmMv4wS8WLAw43ZkKcJ8Pw==} + dev: true + /@types/throttle-debounce@5.0.2: resolution: {integrity: sha512-pDzSNulqooSKvSNcksnV72nk8p7gRqN8As71Sp28nov1IgmPKWbOEIwAWvBME5pPTtaXJAvG3O4oc76HlQ4kqQ==} dev: true @@ -8182,33 +8018,38 @@ packages: resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} dev: true + /@types/unist@3.0.2: + resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} + dev: true + /@types/uuid@9.0.4: resolution: {integrity: sha512-zAuJWQflfx6dYJM62vna+Sn5aeSWhh3OB+wfUEACNcqUSc0AGc5JKl+ycL1vrH7frGTXhJchYjE1Hak8L819dA==} dev: true /@types/uuid@9.0.7: resolution: {integrity: sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==} + dev: false + + /@types/uuid@9.0.8: + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + dev: true /@types/vary@1.1.3: resolution: {integrity: sha512-XJT8/ZQCL7NUut9QDLf6l24JfAEl7bnNdgxfj50cHIpEPRJLHHDDFOAq6i+GsEmeFfH7NamhBE4c4Thtb2egWg==} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: true /@types/web-push@3.6.3: resolution: {integrity: sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: true /@types/ws@8.5.10: resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} dependencies: - '@types/node': 20.10.5 - - /@types/yargs-parser@21.0.0: - resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} - dev: true + '@types/node': 20.11.22 /@types/yargs-parser@21.0.3: resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -8216,7 +8057,7 @@ packages: /@types/yargs@16.0.5: resolution: {integrity: sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==} dependencies: - '@types/yargs-parser': 21.0.0 + '@types/yargs-parser': 21.0.3 dev: true /@types/yargs@17.0.32: @@ -8228,7 +8069,7 @@ packages: resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} requiresBuild: true dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: true optional: true @@ -8261,8 +8102,8 @@ packages: - supports-color dev: true - /@typescript-eslint/eslint-plugin@6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.1.6): - resolution: {integrity: sha512-XOpZ3IyJUIV1b15M7HVOpgQxPPF7lGXgsfcEIu3yDxFPaf/xZKt7s9QO/pbk7vpWQyVulpJbu4E5LwpZiQo4kA==} + /@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.54.0)(typescript@5.1.6): + resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -8273,25 +8114,25 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.12.0(eslint@8.54.0)(typescript@5.1.6) - '@typescript-eslint/scope-manager': 6.12.0 - '@typescript-eslint/type-utils': 6.12.0(eslint@8.54.0)(typescript@5.1.6) - '@typescript-eslint/utils': 6.12.0(eslint@8.54.0)(typescript@5.1.6) - '@typescript-eslint/visitor-keys': 6.12.0 + '@typescript-eslint/parser': 6.21.0(eslint@8.54.0)(typescript@5.1.6) + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/type-utils': 6.21.0(eslint@8.54.0)(typescript@5.1.6) + '@typescript-eslint/utils': 6.21.0(eslint@8.54.0)(typescript@5.1.6) + '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.3.4(supports-color@8.1.1) eslint: 8.54.0 graphemer: 1.4.0 ignore: 5.3.0 natural-compare: 1.4.0 - semver: 7.5.4 + semver: 7.6.0 ts-api-utils: 1.0.3(typescript@5.1.6) typescript: 5.1.6 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/eslint-plugin@6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-1ZJBykBCXaSHG94vMMKmiHoL0MhNHKSVlcHVYZNw+BKxufhqQVTOawNpwwI1P5nIFZ/4jLVop0mcY6mJJDFNaw==} + /@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -8302,17 +8143,46 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.14.0(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/scope-manager': 6.14.0 - '@typescript-eslint/type-utils': 6.14.0(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/utils': 6.14.0(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.14.0 + '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.3.4(supports-color@8.1.1) - eslint: 8.56.0 + eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.0 natural-compare: 1.4.0 - semver: 7.5.4 + semver: 7.6.0 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-j6vT/kCulhG5wBmGtstKeiVr1rdXE4nk+DT1k6trYkwlrvW9eOF5ZbgKnd/YR6PcM4uTEXa0h6Fcvf6X7Dxl0w==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 7.1.0 + '@typescript-eslint/type-utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 7.1.0 + debug: 4.3.4(supports-color@8.1.1) + eslint: 8.57.0 + graphemer: 1.4.0 + ignore: 5.3.0 + natural-compare: 1.4.0 + semver: 7.6.0 ts-api-utils: 1.0.3(typescript@5.3.3) typescript: 5.3.3 transitivePeerDependencies: @@ -8340,8 +8210,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@6.12.0(eslint@8.54.0)(typescript@5.1.6): - resolution: {integrity: sha512-s8/jNFPKPNRmXEnNXfuo1gemBdVmpQsK1pcu+QIvuNJuhFzGrpD7WjOcvDc/+uEdfzSYpNu7U/+MmbScjoQ6vg==} + /@typescript-eslint/parser@6.21.0(eslint@8.54.0)(typescript@5.1.6): + resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -8350,10 +8220,10 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.12.0 - '@typescript-eslint/types': 6.12.0 - '@typescript-eslint/typescript-estree': 6.12.0(typescript@5.1.6) - '@typescript-eslint/visitor-keys': 6.12.0 + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.1.6) + '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.3.4(supports-color@8.1.1) eslint: 8.54.0 typescript: 5.1.6 @@ -8361,22 +8231,22 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@6.14.0(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-QjToC14CKacd4Pa7JK4GeB/vHmWFJckec49FR4hmIRf97+KXole0T97xxu9IFiPxVQ1DBWrQ5wreLwAGwWAVQA==} + /@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-V1EknKUubZ1gWFjiOZhDSNToOjs63/9O0puCgGS8aDOgpZY326fzFu15QAUjwaXzRZjf/qdsdBrckYdv9YxB8w==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.14.0 - '@typescript-eslint/types': 6.14.0 - '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.14.0 + '@typescript-eslint/scope-manager': 7.1.0 + '@typescript-eslint/types': 7.1.0 + '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 7.1.0 debug: 4.3.4(supports-color@8.1.1) - eslint: 8.56.0 + eslint: 8.57.0 typescript: 5.3.3 transitivePeerDependencies: - supports-color @@ -8390,20 +8260,20 @@ packages: '@typescript-eslint/visitor-keys': 6.11.0 dev: true - /@typescript-eslint/scope-manager@6.12.0: - resolution: {integrity: sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==} + /@typescript-eslint/scope-manager@6.21.0: + resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.12.0 - '@typescript-eslint/visitor-keys': 6.12.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 dev: true - /@typescript-eslint/scope-manager@6.14.0: - resolution: {integrity: sha512-VT7CFWHbZipPncAZtuALr9y3EuzY1b1t1AEkIq2bTXUPKw+pHoXflGNG5L+Gv6nKul1cz1VH8fz16IThIU0tdg==} + /@typescript-eslint/scope-manager@7.1.0: + resolution: {integrity: sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.14.0 - '@typescript-eslint/visitor-keys': 6.14.0 + '@typescript-eslint/types': 7.1.0 + '@typescript-eslint/visitor-keys': 7.1.0 dev: true /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.3.3): @@ -8426,8 +8296,8 @@ packages: - supports-color dev: true - /@typescript-eslint/type-utils@6.12.0(eslint@8.54.0)(typescript@5.1.6): - resolution: {integrity: sha512-WWmRXxhm1X8Wlquj+MhsAG4dU/Blvf1xDgGaYCzfvStP2NwPQh6KBvCDbiOEvaE0filhranjIlK/2fSTVwtBng==} + /@typescript-eslint/type-utils@6.21.0(eslint@8.54.0)(typescript@5.1.6): + resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -8436,8 +8306,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.12.0(typescript@5.1.6) - '@typescript-eslint/utils': 6.12.0(eslint@8.54.0)(typescript@5.1.6) + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.1.6) + '@typescript-eslint/utils': 6.21.0(eslint@8.54.0)(typescript@5.1.6) debug: 4.3.4(supports-color@8.1.1) eslint: 8.54.0 ts-api-utils: 1.0.3(typescript@5.1.6) @@ -8446,8 +8316,8 @@ packages: - supports-color dev: true - /@typescript-eslint/type-utils@6.14.0(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-x6OC9Q7HfYKqjnuNu5a7kffIYs3No30isapRBJl1iCHLitD8O0lFbRcVGiOcuyN837fqXzPZ1NS10maQzZMKqw==} + /@typescript-eslint/type-utils@6.21.0(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -8456,10 +8326,30 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3) - '@typescript-eslint/utils': 6.14.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.3.3) + debug: 4.3.4(supports-color@8.1.1) + eslint: 8.57.0 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/type-utils@7.1.0(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-UZIhv8G+5b5skkcuhgvxYWHjk7FW7/JP5lPASMEUoliAPwIH/rxoUSQPia2cuOj9AmDZmwUl1usKm85t5VUMew==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3) + '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3) debug: 4.3.4(supports-color@8.1.1) - eslint: 8.56.0 + eslint: 8.57.0 ts-api-utils: 1.0.3(typescript@5.3.3) typescript: 5.3.3 transitivePeerDependencies: @@ -8471,13 +8361,13 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/types@6.12.0: - resolution: {integrity: sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==} + /@typescript-eslint/types@6.21.0: + resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/types@6.14.0: - resolution: {integrity: sha512-uty9H2K4Xs8E47z3SnXEPRNDfsis8JO27amp2GNCnzGETEW3yTqEIVg5+AI7U276oGF/tw6ZA+UesxeQ104ceA==} + /@typescript-eslint/types@7.1.0: + resolution: {integrity: sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==} engines: {node: ^16.0.0 || >=18.0.0} dev: true @@ -8502,8 +8392,8 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree@6.12.0(typescript@5.1.6): - resolution: {integrity: sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==} + /@typescript-eslint/typescript-estree@6.21.0(typescript@5.1.6): + resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -8511,20 +8401,21 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.12.0 - '@typescript-eslint/visitor-keys': 6.12.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 - semver: 7.5.4 + minimatch: 9.0.3 + semver: 7.6.0 ts-api-utils: 1.0.3(typescript@5.1.6) typescript: 5.1.6 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/typescript-estree@6.14.0(typescript@5.3.3): - resolution: {integrity: sha512-yPkaLwK0yH2mZKFE/bXkPAkkFgOv15GJAUzgUVonAbv0Hr4PK/N2yaA/4XQbTZQdygiDkpt5DkxPELqHguNvyw==} + /@typescript-eslint/typescript-estree@6.21.0(typescript@5.3.3): + resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -8532,12 +8423,35 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.14.0 - '@typescript-eslint/visitor-keys': 6.14.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 - semver: 7.5.4 + minimatch: 9.0.3 + semver: 7.6.0 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/typescript-estree@7.1.0(typescript@5.3.3): + resolution: {integrity: sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 7.1.0 + '@typescript-eslint/visitor-keys': 7.1.0 + debug: 4.3.4(supports-color@8.1.1) + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.6.0 ts-api-utils: 1.0.3(typescript@5.3.3) typescript: 5.3.3 transitivePeerDependencies: @@ -8552,7 +8466,7 @@ packages: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) '@types/json-schema': 7.0.15 - '@types/semver': 7.5.6 + '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 6.11.0 '@typescript-eslint/types': 6.11.0 '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3) @@ -8563,39 +8477,58 @@ packages: - typescript dev: true - /@typescript-eslint/utils@6.12.0(eslint@8.54.0)(typescript@5.1.6): - resolution: {integrity: sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ==} + /@typescript-eslint/utils@6.21.0(eslint@8.54.0)(typescript@5.1.6): + resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.54.0) '@types/json-schema': 7.0.15 - '@types/semver': 7.5.6 - '@typescript-eslint/scope-manager': 6.12.0 - '@typescript-eslint/types': 6.12.0 - '@typescript-eslint/typescript-estree': 6.12.0(typescript@5.1.6) + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.1.6) eslint: 8.54.0 - semver: 7.5.4 + semver: 7.6.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/utils@6.14.0(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-XwRTnbvRr7Ey9a1NT6jqdKX8y/atWG+8fAIu3z73HSP8h06i3r/ClMhmaF/RGWGW1tHJEwij1uEg2GbEmPYvYg==} + /@typescript-eslint/utils@6.21.0(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.15 - '@types/semver': 7.5.6 - '@typescript-eslint/scope-manager': 6.14.0 - '@typescript-eslint/types': 6.14.0 - '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3) - eslint: 8.56.0 - semver: 7.5.4 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) + eslint: 8.57.0 + semver: 7.6.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/utils@7.1.0(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-WUFba6PZC5OCGEmbweGpnNJytJiLG7ZvDBJJoUcX4qZYf1mGZ97mO2Mps6O2efxJcJdRNpqweCistDbZMwIVHw==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^8.56.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 7.1.0 + '@typescript-eslint/types': 7.1.0 + '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3) + eslint: 8.57.0 + semver: 7.6.0 transitivePeerDependencies: - supports-color - typescript @@ -8609,19 +8542,19 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@typescript-eslint/visitor-keys@6.12.0: - resolution: {integrity: sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==} + /@typescript-eslint/visitor-keys@6.21.0: + resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.12.0 + '@typescript-eslint/types': 6.21.0 eslint-visitor-keys: 3.4.3 dev: true - /@typescript-eslint/visitor-keys@6.14.0: - resolution: {integrity: sha512-fB5cw6GRhJUz03MrROVuj5Zm/Q+XWlVdIsFj+Zb1Hvqouc8t+XP2H5y53QYU/MGtd2dPg6/vJJlhoX3xc2ehfw==} + /@typescript-eslint/visitor-keys@7.1.0: + resolution: {integrity: sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.14.0 + '@typescript-eslint/types': 7.1.0 eslint-visitor-keys: 3.4.3 dev: true @@ -8629,31 +8562,16 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true - /@vitejs/plugin-react@3.1.0(vite@5.0.10): - resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - vite: ^4.1.0-beta.0 - dependencies: - '@babel/core': 7.23.3 - '@babel/plugin-transform-react-jsx-self': 7.21.0(@babel/core@7.23.3) - '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.23.3) - magic-string: 0.27.0 - react-refresh: 0.14.0 - vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0) - transitivePeerDependencies: - - supports-color - dev: true - - /@vitejs/plugin-vue@4.5.2(vite@5.0.10)(vue@3.3.12): - resolution: {integrity: sha512-UGR3DlzLi/SaVBPX0cnSyE37vqxU3O6chn8l0HJNzQzDia6/Au2A4xKv+iIJW8w2daf80G7TYHhi1pAUjdZ0bQ==} - engines: {node: ^14.18.0 || >=16.0.0} + /@vitejs/plugin-vue@5.0.4(vite@5.1.4)(vue@3.4.21): + resolution: {integrity: sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==} + engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: - vite: ^4.0.0 || ^5.0.0 + vite: ^5.0.0 vue: ^3.2.25 dependencies: - vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0) - vue: 3.3.12(typescript@5.3.3) + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) + vue: 3.4.21(typescript@5.3.3) + dev: false /@vitest/coverage-v8@0.34.6(vitest@0.34.6): resolution: {integrity: sha512-fivy/OK2d/EsJFoEoxHFEnNGTg+MmdZBAVK9Ka4qhXR2K3J0DS08vcGVwzDtXSuUMabLv4KtPcpSKkcMXFDViw==} @@ -8662,16 +8580,16 @@ packages: dependencies: '@ampproject/remapping': 2.2.1 '@bcoe/v8-coverage': 0.2.3 - istanbul-lib-coverage: 3.2.0 + istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.5 - magic-string: 0.30.3 + istanbul-reports: 3.1.6 + magic-string: 0.30.5 picocolors: 1.0.0 - std-env: 3.3.3 + std-env: 3.7.0 test-exclude: 6.0.0 - v8-to-istanbul: 9.1.0 - vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.5)(terser@5.26.0) + v8-to-istanbul: 9.2.0 + vitest: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1) transitivePeerDependencies: - supports-color dev: true @@ -8684,33 +8602,62 @@ packages: chai: 4.3.10 dev: true + /@vitest/expect@1.1.3: + resolution: {integrity: sha512-MnJqsKc1Ko04lksF9XoRJza0bGGwTtqfbyrsYv5on4rcEkdo+QgUdITenBQBUltKzdxW7K3rWh+nXRULwsdaVg==} + dependencies: + '@vitest/spy': 1.1.3 + '@vitest/utils': 1.1.3 + chai: 4.3.10 + dev: true + /@vitest/runner@0.34.6: resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} dependencies: '@vitest/utils': 0.34.6 p-limit: 4.0.0 - pathe: 1.1.1 + pathe: 1.1.2 dev: true /@vitest/snapshot@0.34.6: resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} dependencies: magic-string: 0.30.5 - pathe: 1.1.1 + pathe: 1.1.2 pretty-format: 29.7.0 dev: true /@vitest/spy@0.34.6: resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} dependencies: - tinyspy: 2.1.1 + tinyspy: 2.2.0 + dev: true + + /@vitest/spy@1.1.3: + resolution: {integrity: sha512-Ec0qWyGS5LhATFQtldvChPTAHv08yHIOZfiNcjwRQbFPHpkih0md9KAbs7TfeIfL7OFKoe7B/6ukBTqByubXkQ==} + dependencies: + tinyspy: 2.2.0 + dev: true + + /@vitest/spy@1.3.0: + resolution: {integrity: sha512-AkCU0ThZunMvblDpPKgjIi025UxR8V7MZ/g/EwmAGpjIujLVV2X6rGYGmxE2D4FJbAy0/ijdROHMWa2M/6JVMw==} + dependencies: + tinyspy: 2.2.0 dev: true /@vitest/utils@0.34.6: resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} dependencies: diff-sequences: 29.6.3 - loupe: 2.3.6 + loupe: 2.3.7 + pretty-format: 29.7.0 + dev: true + + /@vitest/utils@1.1.3: + resolution: {integrity: sha512-Dyt3UMcdElTll2H75vhxfpZu03uFpXRCHxWnzcrFjZxT1kTbq8ALUYIeBgGolo1gldVdI0YSlQRacsqxTwNqwg==} + dependencies: + diff-sequences: 29.6.3 + estree-walker: 3.0.3 + loupe: 2.3.7 pretty-format: 29.7.0 dev: true @@ -8733,58 +8680,59 @@ packages: path-browserify: 1.0.1 dev: true - /@vue/compiler-core@3.3.12: - resolution: {integrity: sha512-qAtjyG3GBLG0chzp5xGCyRLLe6wFCHmjI82aGzwuGKyznNP+GJJMxjc0wOYWDB2YKfho7niJFdoFpo0CZZQg9w==} + /@vue/compiler-core@3.4.18: + resolution: {integrity: sha512-F7YK8lMK0iv6b9/Gdk15A67wM0KKZvxDxed0RR60C1z9tIJTKta+urs4j0RTN5XqHISzI3etN3mX0uHhjmoqjQ==} dependencies: - '@babel/parser': 7.23.6 - '@vue/shared': 3.3.12 + '@babel/parser': 7.23.9 + '@vue/shared': 3.4.18 + entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.0.2 + dev: true - /@vue/compiler-core@3.3.8: - resolution: {integrity: sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==} + /@vue/compiler-core@3.4.21: + resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==} dependencies: - '@babel/parser': 7.23.4 - '@vue/shared': 3.3.8 + '@babel/parser': 7.23.9 + '@vue/shared': 3.4.21 + entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.0.2 - dev: true - /@vue/compiler-dom@3.3.12: - resolution: {integrity: sha512-RdJU9oEYaoPKUdGXCy0l+i4clesdDeLmbvRlszoc9iagsnBnMmQtYfCPVQ5BHB6o7K4SCucDdJM2Dh3oXB0D6g==} + /@vue/compiler-dom@3.4.18: + resolution: {integrity: sha512-24Eb8lcMfInefvQ6YlEVS18w5Q66f4+uXWVA+yb7praKbyjHRNuKVWGuinfSSjM0ZIiPi++QWukhkgznBaqpEA==} dependencies: - '@vue/compiler-core': 3.3.12 - '@vue/shared': 3.3.12 + '@vue/compiler-core': 3.4.18 + '@vue/shared': 3.4.18 + dev: true - /@vue/compiler-dom@3.3.8: - resolution: {integrity: sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==} + /@vue/compiler-dom@3.4.21: + resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==} dependencies: - '@vue/compiler-core': 3.3.8 - '@vue/shared': 3.3.8 - dev: true + '@vue/compiler-core': 3.4.21 + '@vue/shared': 3.4.21 - /@vue/compiler-sfc@3.3.12: - resolution: {integrity: sha512-yy5b9e7b79dsGbMmglCe/YnhCQgBkHO7Uf6JfjWPSf2/5XH+MKn18LhzhHyxbHdJgnA4lZCqtXzLaJz8Pd8lMw==} + /@vue/compiler-sfc@3.4.21: + resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==} dependencies: - '@babel/parser': 7.23.6 - '@vue/compiler-core': 3.3.12 - '@vue/compiler-dom': 3.3.12 - '@vue/compiler-ssr': 3.3.12 - '@vue/reactivity-transform': 3.3.12 - '@vue/shared': 3.3.12 + '@babel/parser': 7.23.9 + '@vue/compiler-core': 3.4.21 + '@vue/compiler-dom': 3.4.21 + '@vue/compiler-ssr': 3.4.21 + '@vue/shared': 3.4.21 estree-walker: 2.0.2 - magic-string: 0.30.5 - postcss: 8.4.32 + magic-string: 0.30.7 + postcss: 8.4.35 source-map-js: 1.0.2 - /@vue/compiler-ssr@3.3.12: - resolution: {integrity: sha512-adCiMJPznfWcQyk/9HSuXGja859IaMV+b8UNSVzDatqv7h0PvT9BEeS22+gjkWofDiSg5d78/ZLls3sLA+cn3A==} + /@vue/compiler-ssr@3.4.21: + resolution: {integrity: sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==} dependencies: - '@vue/compiler-dom': 3.3.12 - '@vue/shared': 3.3.12 + '@vue/compiler-dom': 3.4.21 + '@vue/shared': 3.4.21 - /@vue/language-core@1.8.25(typescript@5.3.3): - resolution: {integrity: sha512-NJk/5DnAZlpvXX8BdWmHI45bWGLViUaS3R/RMrmFSvFMSbJKuEODpM4kR0F0Ofv5SFzCWuNiMhxameWpVdQsnA==} + /@vue/language-core@1.8.27(typescript@5.3.3): + resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -8793,8 +8741,8 @@ packages: dependencies: '@volar/language-core': 1.11.1 '@volar/source-map': 1.11.1 - '@vue/compiler-dom': 3.3.8 - '@vue/shared': 3.3.8 + '@vue/compiler-dom': 3.4.18 + '@vue/shared': 3.4.18 computeds: 0.0.1 minimatch: 9.0.3 muggle-string: 0.3.1 @@ -8803,50 +8751,41 @@ packages: vue-template-compiler: 2.7.14 dev: true - /@vue/reactivity-transform@3.3.12: - resolution: {integrity: sha512-g5TijmML7FyKkLt6QnpqNmA4KD7K/T5SbXa88Bhq+hydNQEkzA8veVXWAQuNqg9rjaFYD0rPf0a9NofKA0ENgg==} - dependencies: - '@babel/parser': 7.23.6 - '@vue/compiler-core': 3.3.12 - '@vue/shared': 3.3.12 - estree-walker: 2.0.2 - magic-string: 0.30.5 - - /@vue/reactivity@3.3.12: - resolution: {integrity: sha512-vOJORzO8DlIx88cgTnMLIf2GlLYpoXAKsuoQsK6SGdaqODjxO129pVPTd2s/N/Mb6KKZEFIHIEwWGmtN4YPs+g==} + /@vue/reactivity@3.4.21: + resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==} dependencies: - '@vue/shared': 3.3.12 + '@vue/shared': 3.4.21 - /@vue/runtime-core@3.3.12: - resolution: {integrity: sha512-5iL4w7MZrSGKEZU2wFAYhDZdZmgn+s//73EfgDXW1M+ZUOl36md7tlWp1QFK/ladiq4FvQ82shVjo0KiPDPr0A==} + /@vue/runtime-core@3.4.21: + resolution: {integrity: sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==} dependencies: - '@vue/reactivity': 3.3.12 - '@vue/shared': 3.3.12 + '@vue/reactivity': 3.4.21 + '@vue/shared': 3.4.21 - /@vue/runtime-dom@3.3.12: - resolution: {integrity: sha512-8mMzqiIdl+IYa/OXwKwk6/4ebLq7cYV1pUcwCSwBK2KerUa6cwGosen5xrCL9f8o2DJ9TfPFwbPEvH7OXzUpoA==} + /@vue/runtime-dom@3.4.21: + resolution: {integrity: sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==} dependencies: - '@vue/runtime-core': 3.3.12 - '@vue/shared': 3.3.12 + '@vue/runtime-core': 3.4.21 + '@vue/shared': 3.4.21 csstype: 3.1.3 - /@vue/server-renderer@3.3.12(vue@3.3.12): - resolution: {integrity: sha512-OZ0IEK5TU5GXb5J8/wSplyxvGGdIcwEmS8EIO302Vz8K6fGSgSJTU54X0Sb6PaefzZdiN3vHsLXO8XIeF8crQQ==} + /@vue/server-renderer@3.4.21(vue@3.4.21): + resolution: {integrity: sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==} peerDependencies: - vue: 3.3.12 + vue: 3.4.21 dependencies: - '@vue/compiler-ssr': 3.3.12 - '@vue/shared': 3.3.12 - vue: 3.3.12(typescript@5.3.3) + '@vue/compiler-ssr': 3.4.21 + '@vue/shared': 3.4.21 + vue: 3.4.21(typescript@5.3.3) - /@vue/shared@3.3.12: - resolution: {integrity: sha512-6p0Yin0pclvnER7BLNOQuod9Z+cxSYh8pSh7CzHnWNjAIP6zrTlCdHRvSCb1aYEx6i3Q3kvfuWU7nG16CgG1ag==} - - /@vue/shared@3.3.8: - resolution: {integrity: sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==} + /@vue/shared@3.4.18: + resolution: {integrity: sha512-CxouGFxxaW5r1WbrSmWwck3No58rApXgRSBxrqgnY1K+jk20F6DrXJkHdH9n4HVT+/B6G2CAn213Uq3npWiy8Q==} dev: true - /@vue/test-utils@2.4.1(vue@3.3.12): + /@vue/shared@3.4.21: + resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==} + + /@vue/test-utils@2.4.1(vue@3.4.21): resolution: {integrity: sha512-VO8nragneNzUZUah6kOjiFmD/gwRjUauG9DROh6oaOeFwX1cZRUNHhdeogE8635cISigXFTtGLUQWx5KCb0xeg==} peerDependencies: '@vue/server-renderer': ^3.0.1 @@ -8856,22 +8795,17 @@ packages: optional: true dependencies: js-beautify: 1.14.9 - vue: 3.3.12(typescript@5.3.3) + vue: 3.4.21(typescript@5.3.3) vue-component-type-helpers: 1.8.4 dev: true - /@xmldom/xmldom@0.8.6: - resolution: {integrity: sha512-uRjjusqpoqfmRkTaNuLJ2VohVr67Q5YwDATW3VU7PfzTj6IRaihGrYI7zckGZjxQPBIp63nfvJbM+Yu5ICh0Bg==} - engines: {node: '>=10.0.0'} - dev: true - - /@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.18.17): + /@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.18.20): resolution: {integrity: sha512-kYzDJO5CA9sy+on/s2aIW0411AklfCi8Ck/4QDivOqsMKpStZA2SsR+X27VTggGwpStWaLrjJcDcdDMowtG8MA==} engines: {node: '>=14.15.0'} peerDependencies: esbuild: '>=0.10.0' dependencies: - esbuild: 0.18.17 + esbuild: 0.18.20 tslib: 2.6.2 dev: true @@ -8891,14 +8825,9 @@ packages: tslib: 1.14.1 dev: true - /@zxing/text-encoding@0.9.0: - resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} - requiresBuild: true - dev: true - optional: true - /abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + dev: true /abbrev@2.0.0: resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} @@ -8931,12 +8860,12 @@ packages: acorn: 7.4.1 dev: true - /acorn-jsx@5.3.2(acorn@8.11.2): + /acorn-jsx@5.3.2(acorn@8.11.3): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - acorn: 8.11.2 + acorn: 8.11.3 dev: true /acorn-walk@7.2.0: @@ -8944,8 +8873,8 @@ packages: engines: {node: '>=0.4.0'} dev: true - /acorn-walk@8.2.0: - resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + /acorn-walk@8.3.2: + resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} dev: true @@ -8954,8 +8883,8 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - /acorn@8.11.2: - resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} + /acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} engines: {node: '>=0.4.0'} hasBin: true @@ -8964,11 +8893,6 @@ packages: engines: {node: '>= 10.0.0'} dev: true - /agent-base@5.1.1: - resolution: {integrity: sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==} - engines: {node: '>= 6.0.0'} - dev: true - /agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} @@ -8976,6 +8900,7 @@ packages: debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color + dev: true /agent-base@7.1.0: resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==} @@ -8993,6 +8918,14 @@ packages: clean-stack: 2.2.0 indent-string: 4.0.0 + /aggregate-error@5.0.0: + resolution: {integrity: sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==} + engines: {node: '>=18'} + dependencies: + clean-stack: 5.2.0 + indent-string: 5.0.0 + dev: true + /ajv-draft-04@1.0.0(ajv@8.12.0): resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} peerDependencies: @@ -9054,6 +8987,7 @@ packages: /ansi-sequence-parser@1.1.1: resolution: {integrity: sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==} + dev: true /ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} @@ -9097,11 +9031,6 @@ packages: /append-field@1.0.0: resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} - dev: false - - /aproba@2.0.0: - resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} - dev: false /arch@2.2.0: resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} @@ -9135,30 +9064,18 @@ packages: resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} dev: false - /are-we-there-yet@2.0.0: - resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} - engines: {node: '>=10'} - requiresBuild: true - dependencies: - delegates: 1.0.0 - readable-stream: 3.6.2 - dev: false - /arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} dev: true - /argon2@0.31.1: - resolution: {integrity: sha512-ik2xnJrLXazya7m4Nz1XfBSRjXj8Koq8qF9PsQC8059p20ifWc9zx/hgU3ItZh/3TnwXkv0RbhvjodPkmFf0bg==} - engines: {node: '>=14.0.0'} + /argon2@0.40.1: + resolution: {integrity: sha512-DjtHDwd7pm12qeWyfihHoM8Bn5vGcgH6sKwgPqwNYroRmxlrzadHEvMyuvQxN/V8YSyRRKD5x6ito09q1e9OyA==} + engines: {node: '>=16.17.0'} requiresBuild: true dependencies: - '@mapbox/node-pre-gyp': 1.0.11 '@phc/format': 1.0.0 - node-addon-api: 7.0.0 - transitivePeerDependencies: - - encoding - - supports-color + node-addon-api: 7.1.0 + node-gyp-build: 4.8.0 dev: false /argparse@1.0.10: @@ -9170,13 +9087,6 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - /aria-hidden@1.2.3: - resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==} - engines: {node: '>=10'} - dependencies: - tslib: 2.6.2 - dev: true - /aria-query@5.1.3: resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} dependencies: @@ -9289,15 +9199,6 @@ packages: resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} engines: {node: '>=0.8'} - /assert@2.0.0: - resolution: {integrity: sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==} - dependencies: - es6-object-assign: 1.1.0 - is-nan: 1.3.2 - object-is: 1.1.5 - util: 0.12.5 - dev: true - /assert@2.1.0: resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} dependencies: @@ -9309,21 +9210,7 @@ packages: dev: true /assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - dev: true - - /ast-types@0.14.2: - resolution: {integrity: sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==} - engines: {node: '>=4'} - dependencies: - tslib: 2.6.2 - dev: true - - /ast-types@0.15.2: - resolution: {integrity: sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==} - engines: {node: '>=4'} - dependencies: - tslib: 2.6.2 + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} dev: true /ast-types@0.16.1: @@ -9343,14 +9230,10 @@ packages: hasBin: true dev: false - /async-limiter@1.0.1: - resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} - dev: true - - /async-mutex@0.4.0: - resolution: {integrity: sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==} + /async-mutex@0.4.1: + resolution: {integrity: sha512-WfoBo4E/TbCX1G95XTjbWTE3X2XLG0m1Xbv2cwOtuPdyH9CZvnaA5nCt1ucjaKEgW2A5IF71hxrRhr83Je5xjA==} dependencies: - tslib: 2.6.1 + tslib: 2.6.2 dev: false /async@3.2.4: @@ -9384,12 +9267,12 @@ packages: - supports-color dev: false - /aws-sdk-client-mock@3.0.0: - resolution: {integrity: sha512-4mBiWhuLYLZe1+K/iB8eYy5SAZyW2se+Keyh5u9QouMt6/qJ5SRZhss68xvUX5g3ApzROJ06QPRziYHP6buuvQ==} + /aws-sdk-client-mock@3.0.1: + resolution: {integrity: sha512-9VAzJLl8mz99KP9HjOm/93d8vznRRUTpJooPBOunRdUAnVYopCe9xmMuu7eVemu8fQ+w6rP7o5bBK1kAFkB2KQ==} dependencies: '@types/sinon': 10.0.13 - sinon: 14.0.2 - tslib: 2.5.3 + sinon: 16.1.3 + tslib: 2.6.2 dev: true /aws-sign2@0.7.0: @@ -9430,13 +9313,14 @@ packages: /b4a@1.6.4: resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} + dev: false - /babel-core@7.0.0-bridge.0(@babel/core@7.23.3): + /babel-core@7.0.0-bridge.0(@babel/core@7.24.0): resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 dev: true /babel-jest@29.7.0(@babel/core@7.23.3): @@ -9474,44 +9358,44 @@ packages: resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/template': 7.22.15 - '@babel/types': 7.23.4 + '@babel/template': 7.24.0 + '@babel/types': 7.24.0 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.4 dev: true - /babel-plugin-polyfill-corejs2@0.4.7(@babel/core@7.23.3): + /babel-plugin-polyfill-corejs2@0.4.7(@babel/core@7.24.0): resolution: {integrity: sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: '@babel/compat-data': 7.23.5 - '@babel/core': 7.23.3 - '@babel/helper-define-polyfill-provider': 0.4.4(@babel/core@7.23.3) + '@babel/core': 7.24.0 + '@babel/helper-define-polyfill-provider': 0.4.4(@babel/core@7.24.0) semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-corejs3@0.8.7(@babel/core@7.23.3): + /babel-plugin-polyfill-corejs3@0.8.7(@babel/core@7.24.0): resolution: {integrity: sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-define-polyfill-provider': 0.4.4(@babel/core@7.23.3) + '@babel/core': 7.24.0 + '@babel/helper-define-polyfill-provider': 0.4.4(@babel/core@7.24.0) core-js-compat: 3.34.0 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-regenerator@0.5.4(@babel/core@7.23.3): + /babel-plugin-polyfill-regenerator@0.5.4(@babel/core@7.24.0): resolution: {integrity: sha512-S/x2iOCvDaCASLYsOOgWOq4bCfKYVqvO/uxjkaYyZ3rVsVE3CeAI/c84NpyuBBymEgNvHgjEot3a9/Z/kXvqsg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.23.3 - '@babel/helper-define-polyfill-provider': 0.4.4(@babel/core@7.23.3) + '@babel/core': 7.24.0 + '@babel/helper-define-polyfill-provider': 0.4.4(@babel/core@7.24.0) transitivePeerDependencies: - supports-color dev: true @@ -9551,7 +9435,11 @@ packages: resolution: {integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==} engines: {node: '>= 10.0.0'} dependencies: - '@babel/types': 7.22.17 + '@babel/types': 7.24.0 + + /bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + dev: true /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -9575,6 +9463,12 @@ packages: open: 8.4.2 dev: true + /bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + dependencies: + require-from-string: 2.0.2 + dev: false + /big-integer@1.6.51: resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} engines: {node: '>=0.6'} @@ -9615,6 +9509,7 @@ packages: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 + dev: true /blob-util@2.0.2: resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==} @@ -9669,7 +9564,6 @@ packages: unpipe: 1.0.0 transitivePeerDependencies: - supports-color - dev: false /boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -9727,21 +9621,32 @@ packages: hasBin: true dependencies: caniuse-lite: 1.0.30001564 - electron-to-chromium: 1.4.591 + electron-to-chromium: 1.4.686 node-releases: 2.0.13 update-browserslist-db: 1.0.13(browserslist@4.22.1) + dev: true /browserslist@4.22.2: resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001571 - electron-to-chromium: 1.4.616 + caniuse-lite: 1.0.30001591 + electron-to-chromium: 1.4.686 node-releases: 2.0.14 update-browserslist-db: 1.0.13(browserslist@4.22.2) dev: true + /browserslist@4.23.0: + resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001591 + electron-to-chromium: 1.4.686 + node-releases: 2.0.14 + update-browserslist-db: 1.0.13(browserslist@4.23.0) + /bs-logger@0.2.6: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} engines: {node: '>= 6'} @@ -9782,6 +9687,7 @@ packages: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 + dev: true /buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -9797,14 +9703,15 @@ packages: dependencies: node-gyp-build: 4.6.0 - /bullmq@4.15.4: - resolution: {integrity: sha512-lrEtiERjYPPfb0OEXva5G8cF6kbtJFy/BmIL0yjmO+kj2eSVjCpAVVeYikxoqKtaYyEnNFal7ERVLanEvp+1Lw==} + /bullmq@5.4.0: + resolution: {integrity: sha512-QNOT+Vp/OFctwKa1/LYvrfIMXqb6Hu8a1VwXxQpa/JXoFAQ9E4ZcqW4fyEjx9iYrXakpV6cAGPbmdgWaKTGXOQ==} dependencies: cron-parser: 4.8.1 - glob: 8.1.0 + fast-glob: 3.3.2 ioredis: 5.3.2 lodash: 4.17.21 - msgpackr: 1.9.2 + minimatch: 9.0.3 + msgpackr: 1.10.1 node-abort-controller: 3.1.1 semver: 7.5.4 tslib: 2.6.2 @@ -9822,7 +9729,6 @@ packages: engines: {node: '>=10.16.0'} dependencies: streamsearch: 1.1.0 - dev: false /bytes@3.0.0: resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} @@ -9852,7 +9758,7 @@ packages: minipass-pipeline: 1.2.4 p-map: 4.0.0 ssri: 10.0.4 - tar: 6.1.13 + tar: 6.2.0 unique-filename: 3.0.0 dev: false @@ -9931,58 +9837,41 @@ packages: /caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: - browserslist: 4.22.1 - caniuse-lite: 1.0.30001564 + browserslist: 4.23.0 + caniuse-lite: 1.0.30001591 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: false /caniuse-lite@1.0.30001564: resolution: {integrity: sha512-DqAOf+rhof+6GVx1y+xzbFPeOumfQnhYzVnZD6LAXijR77yPtm9mfOcqOnT3mpnJiZVT+kwLAFnRlZcIz+c6bg==} - - /caniuse-lite@1.0.30001571: - resolution: {integrity: sha512-tYq/6MoXhdezDLFZuCO/TKboTzuQ/xR5cFdgXPfDtM7/kchBO3b4VWghE/OAi/DV7tTdhmLjZiZBZi1fA/GheQ==} dev: true + /caniuse-lite@1.0.30001591: + resolution: {integrity: sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==} + /canonicalize@1.0.8: resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==} dev: false - /canvas-confetti@1.6.1: - resolution: {integrity: sha512-CgGR5DL9+dkne4AwcpvWQc0LIQq43yDIxlwdZcyrq3yklricNfuPHoOSoM6Ya7yCQ+sXmZ2iNV2feiKjVG8C1g==} + /canvas-confetti@1.9.2: + resolution: {integrity: sha512-6Xi7aHHzKwxZsem4mCKoqP6YwUG3HamaHHAlz1hTNQPCqXhARFpSXnkC9TWlahHY5CG6hSL5XexNjxK8irVErg==} dev: false /caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} dev: true - /cbor-extract@2.1.1: - resolution: {integrity: sha512-1UX977+L+zOJHsp0mWFG13GLwO6ucKgSmSW6JTl8B9GUvACvHeIVpFqhU92299Z6PfD09aTXDell5p+lp1rUFA==} - hasBin: true - requiresBuild: true - dependencies: - node-gyp-build-optional-packages: 5.0.3 - optionalDependencies: - '@cbor-extract/cbor-extract-darwin-arm64': 2.1.1 - '@cbor-extract/cbor-extract-darwin-x64': 2.1.1 - '@cbor-extract/cbor-extract-linux-arm': 2.1.1 - '@cbor-extract/cbor-extract-linux-arm64': 2.1.1 - '@cbor-extract/cbor-extract-linux-x64': 2.1.1 - '@cbor-extract/cbor-extract-win32-x64': 2.1.1 - dev: false - optional: true - - /cbor-x@1.5.4: - resolution: {integrity: sha512-PVKILDn+Rf6MRhhcyzGXi5eizn1i0i3F8Fe6UMMxXBnWkalq9+C5+VTmlIjAYM4iF2IYF2N+zToqAfYOp+3rfw==} - optionalDependencies: - cbor-extract: 2.1.1 - dev: false - - /cbor@9.0.1: - resolution: {integrity: sha512-/TQOWyamDxvVIv+DY9cOLNuABkoyz8K/F3QE56539pGVYohx0+MEA1f4lChFTX79dBTBS7R1PF6ovH7G+VtBfQ==} + /cbor@9.0.2: + resolution: {integrity: sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==} engines: {node: '>=16'} dependencies: nofilter: 3.1.0 + dev: false + + /ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + dev: true /chai@4.3.10: resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} @@ -9992,7 +9881,7 @@ packages: check-error: 1.0.3 deep-eql: 4.1.3 get-func-name: 2.0.2 - loupe: 2.3.6 + loupe: 2.3.7 pathval: 1.1.1 type-detect: 4.0.8 dev: true @@ -10036,6 +9925,10 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + /character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + dev: true + /character-parser@2.2.0: resolution: {integrity: sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==} dependencies: @@ -10045,45 +9938,45 @@ packages: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: true - /chart.js@4.4.1: - resolution: {integrity: sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==} - engines: {pnpm: '>=7'} + /chart.js@4.4.2: + resolution: {integrity: sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==} + engines: {pnpm: '>=8'} dependencies: '@kurkle/color': 0.3.2 dev: false - /chartjs-adapter-date-fns@3.0.0(chart.js@4.4.1)(date-fns@2.30.0): + /chartjs-adapter-date-fns@3.0.0(chart.js@4.4.2)(date-fns@2.30.0): resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==} peerDependencies: chart.js: '>=2.8.0' date-fns: '>=2.0.0' dependencies: - chart.js: 4.4.1 + chart.js: 4.4.2 date-fns: 2.30.0 dev: false - /chartjs-chart-matrix@2.0.1(chart.js@4.4.1): + /chartjs-chart-matrix@2.0.1(chart.js@4.4.2): resolution: {integrity: sha512-BGfeY+/PHnITyDlc7WfnKJ1RyOfgOzIqWp/gxzzl7pUjyoGzHDcw51qd2xJF9gdT9Def7ZwOnOMm8GJUXDxI0w==} peerDependencies: chart.js: '>=3.0.0' dependencies: - chart.js: 4.4.1 + chart.js: 4.4.2 dev: false - /chartjs-plugin-gradient@0.6.1(chart.js@4.4.1): + /chartjs-plugin-gradient@0.6.1(chart.js@4.4.2): resolution: {integrity: sha512-TGHNIh8KqQMLdb+UfY80cBHYRyOC47eeokmgkeajRdKGbFt462lJiyiq4ZJ25fiM7BGsmzoBLhmVyEw4B3gQxw==} peerDependencies: chart.js: '>=2.6.0' dependencies: - chart.js: 4.4.1 + chart.js: 4.4.2 dev: false - /chartjs-plugin-zoom@2.0.1(chart.js@4.4.1): + /chartjs-plugin-zoom@2.0.1(chart.js@4.4.2): resolution: {integrity: sha512-ogOmLu6e+Q7E1XWOCOz9YwybMslz9qNfGV2a+qjfmqJYpsw5ZMoRHZBUyW+NGhkpQ5PwwPA/+rikHpBZb7PZuA==} peerDependencies: chart.js: '>=3.2.0' dependencies: - chart.js: 4.4.1 + chart.js: 4.4.2 hammerjs: 2.0.8 dev: false @@ -10136,15 +10029,24 @@ packages: /chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + dev: true /chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} requiresBuild: true - /chromatic@10.1.0: - resolution: {integrity: sha512-S+ztO8f1k/LckuzJKCqaTs6AfUQ0eLNT9kEoyCUwX7gkJnveQo5JStCfY55v30zogjRkHJpwqzEfSXl6AwO2tQ==} + /chromatic@11.0.0: + resolution: {integrity: sha512-utzRVqdMrpzYwZNf7dHWU0z0/rx6SH/FUVNozQxYHDtQfYUdEj+Ro4OSch5+Wsk2FoUmznJyLkaC2J839z1N7A==} hasBin: true + peerDependencies: + '@chromatic-com/cypress': ^0.5.2 || ^1.0.0 + '@chromatic-com/playwright': ^0.5.2 || ^1.0.0 + peerDependenciesMeta: + '@chromatic-com/cypress': + optional: true + '@chromatic-com/playwright': + optional: true dev: false /ci-info@3.9.0: @@ -10159,6 +10061,13 @@ packages: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} + /clean-stack@5.2.0: + resolution: {integrity: sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==} + engines: {node: '>=14.16'} + dependencies: + escape-string-regexp: 5.0.0 + dev: true + /cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -10259,6 +10168,11 @@ packages: engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} dev: true + /code-error-fragment@0.0.230: + resolution: {integrity: sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==} + engines: {node: '>= 4'} + dev: true + /collect-v8-coverage@1.0.1: resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==} dev: true @@ -10289,10 +10203,6 @@ packages: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 - - /color-support@1.1.3: - resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} - hasBin: true dev: false /color@4.2.3: @@ -10301,6 +10211,7 @@ packages: dependencies: color-convert: 2.0.1 color-string: 1.9.1 + dev: false /colord@2.9.3: resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} @@ -10408,7 +10319,6 @@ packages: inherits: 2.0.4 readable-stream: 2.3.8 typedarray: 0.0.6 - dev: true /concat-stream@2.0.0: resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} @@ -10429,17 +10339,12 @@ packages: /consola@2.15.3: resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} - dev: false - - /console-control-strings@1.1.0: - resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - dev: false /constantinople@4.0.1: resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==} dependencies: '@babel/parser': 7.22.16 - '@babel/types': 7.22.17 + '@babel/types': 7.24.0 /content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} @@ -10451,10 +10356,6 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} - /convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - dev: true - /convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} dev: true @@ -10467,21 +10368,10 @@ packages: engines: {node: '>=6.6.0'} dev: false - /cookie@0.4.2: - resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} - engines: {node: '>= 0.6'} - dev: true - /cookie@0.5.0: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} - /core-js-compat@3.31.1: - resolution: {integrity: sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA==} - dependencies: - browserslist: 4.22.1 - dev: true - /core-js-compat@3.34.0: resolution: {integrity: sha512-4ZIyeNbW/Cn1wkMMDy+mvrRUxrwFNjKwbhCfQpDd+eLgYipDqp8oGFGtLmhh18EDPKA0g3VUBYOxQGGwvWLVpA==} dependencies: @@ -10494,6 +10384,13 @@ packages: /core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + /cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + /crc-32@1.2.2: resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} engines: {node: '>=0.8'} @@ -10508,7 +10405,7 @@ packages: readable-stream: 3.6.2 dev: false - /create-jest@29.7.0(@types/node@20.10.5): + /create-jest@29.7.0(@types/node@20.11.22): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -10517,7 +10414,7 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.10.5) + jest-config: 29.7.0(@types/node@20.11.22) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -10562,7 +10459,6 @@ packages: node-fetch: 2.7.0 transitivePeerDependencies: - encoding - dev: false /cross-fetch@4.0.0: resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} @@ -10593,13 +10489,13 @@ packages: engines: {node: '>=8'} dev: true - /css-declaration-sorter@7.1.1(postcss@8.4.32): + /css-declaration-sorter@7.1.1(postcss@8.4.35): resolution: {integrity: sha512-dZ3bVTEEc1vxr3Bek9vGwfB5Z6ESPULhcRvO472mfjVnj8jRcTnKO8/JTczlvxM10Myb+wBM++1MtdO76eWcaQ==} engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.0.9 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 dev: false /css-select@5.1.0: @@ -10639,62 +10535,62 @@ packages: engines: {node: '>=4'} hasBin: true - /cssnano-preset-default@6.0.2(postcss@8.4.32): - resolution: {integrity: sha512-VnZybFeZ63AiVqIUNlxqMxpj9VU8B5j0oKgP7WyVt/7mkyf97KsYkNzsPTV/RVmy54Pg7cBhOK4WATbdCB44gw==} + /cssnano-preset-default@6.0.5(postcss@8.4.35): + resolution: {integrity: sha512-M+qRDEr5QZrfNl0B2ySdbTLGyNb8kBcSjuwR7WBamYBOEREH9t2efnB/nblekqhdGLZdkf4oZNetykG2JWRdZQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - css-declaration-sorter: 7.1.1(postcss@8.4.32) - cssnano-utils: 4.0.1(postcss@8.4.32) - postcss: 8.4.32 - postcss-calc: 9.0.1(postcss@8.4.32) - postcss-colormin: 6.0.1(postcss@8.4.32) - postcss-convert-values: 6.0.1(postcss@8.4.32) - postcss-discard-comments: 6.0.1(postcss@8.4.32) - postcss-discard-duplicates: 6.0.1(postcss@8.4.32) - postcss-discard-empty: 6.0.1(postcss@8.4.32) - postcss-discard-overridden: 6.0.1(postcss@8.4.32) - postcss-merge-longhand: 6.0.1(postcss@8.4.32) - postcss-merge-rules: 6.0.2(postcss@8.4.32) - postcss-minify-font-values: 6.0.1(postcss@8.4.32) - postcss-minify-gradients: 6.0.1(postcss@8.4.32) - postcss-minify-params: 6.0.1(postcss@8.4.32) - postcss-minify-selectors: 6.0.1(postcss@8.4.32) - postcss-normalize-charset: 6.0.1(postcss@8.4.32) - postcss-normalize-display-values: 6.0.1(postcss@8.4.32) - postcss-normalize-positions: 6.0.1(postcss@8.4.32) - postcss-normalize-repeat-style: 6.0.1(postcss@8.4.32) - postcss-normalize-string: 6.0.1(postcss@8.4.32) - postcss-normalize-timing-functions: 6.0.1(postcss@8.4.32) - postcss-normalize-unicode: 6.0.1(postcss@8.4.32) - postcss-normalize-url: 6.0.1(postcss@8.4.32) - postcss-normalize-whitespace: 6.0.1(postcss@8.4.32) - postcss-ordered-values: 6.0.1(postcss@8.4.32) - postcss-reduce-initial: 6.0.1(postcss@8.4.32) - postcss-reduce-transforms: 6.0.1(postcss@8.4.32) - postcss-svgo: 6.0.1(postcss@8.4.32) - postcss-unique-selectors: 6.0.1(postcss@8.4.32) - dev: false - - /cssnano-utils@4.0.1(postcss@8.4.32): + css-declaration-sorter: 7.1.1(postcss@8.4.35) + cssnano-utils: 4.0.1(postcss@8.4.35) + postcss: 8.4.35 + postcss-calc: 9.0.1(postcss@8.4.35) + postcss-colormin: 6.0.3(postcss@8.4.35) + postcss-convert-values: 6.0.4(postcss@8.4.35) + postcss-discard-comments: 6.0.1(postcss@8.4.35) + postcss-discard-duplicates: 6.0.2(postcss@8.4.35) + postcss-discard-empty: 6.0.2(postcss@8.4.35) + postcss-discard-overridden: 6.0.1(postcss@8.4.35) + postcss-merge-longhand: 6.0.3(postcss@8.4.35) + postcss-merge-rules: 6.0.4(postcss@8.4.35) + postcss-minify-font-values: 6.0.2(postcss@8.4.35) + postcss-minify-gradients: 6.0.2(postcss@8.4.35) + postcss-minify-params: 6.0.3(postcss@8.4.35) + postcss-minify-selectors: 6.0.2(postcss@8.4.35) + postcss-normalize-charset: 6.0.1(postcss@8.4.35) + postcss-normalize-display-values: 6.0.1(postcss@8.4.35) + postcss-normalize-positions: 6.0.1(postcss@8.4.35) + postcss-normalize-repeat-style: 6.0.1(postcss@8.4.35) + postcss-normalize-string: 6.0.1(postcss@8.4.35) + postcss-normalize-timing-functions: 6.0.1(postcss@8.4.35) + postcss-normalize-unicode: 6.0.3(postcss@8.4.35) + postcss-normalize-url: 6.0.1(postcss@8.4.35) + postcss-normalize-whitespace: 6.0.1(postcss@8.4.35) + postcss-ordered-values: 6.0.1(postcss@8.4.35) + postcss-reduce-initial: 6.0.3(postcss@8.4.35) + postcss-reduce-transforms: 6.0.1(postcss@8.4.35) + postcss-svgo: 6.0.2(postcss@8.4.35) + postcss-unique-selectors: 6.0.2(postcss@8.4.35) + dev: false + + /cssnano-utils@4.0.1(postcss@8.4.35): resolution: {integrity: sha512-6qQuYDqsGoiXssZ3zct6dcMxiqfT6epy7x4R0TQJadd4LWO3sPR6JH6ZByOvVLoZ6EdwPGgd7+DR1EmX3tiXQQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 dev: false - /cssnano@6.0.2(postcss@8.4.32): - resolution: {integrity: sha512-Tu9wv8UdN6CoiQnIVkCNvi+0rw/BwFWOJBlg2bVfEyKaadSuE3Gq/DD8tniVvggTJGwK88UjqZp7zL5sv6t1aA==} + /cssnano@6.0.5(postcss@8.4.35): + resolution: {integrity: sha512-tpTp/ukgrElwu3ESFY4IvWnGn8eTt8cJhC2aAbtA3lvUlxp6t6UPv8YCLjNnEGiFreT1O0LiOM1U3QyTBVFl2A==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - cssnano-preset-default: 6.0.2(postcss@8.4.32) - lilconfig: 3.0.0 - postcss: 8.4.32 + cssnano-preset-default: 6.0.5(postcss@8.4.35) + lilconfig: 3.1.1 + postcss: 8.4.35 dev: false /csso@5.0.5: @@ -10704,29 +10600,24 @@ packages: css-tree: 2.2.1 dev: false - /cssstyle@3.0.0: - resolution: {integrity: sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==} - engines: {node: '>=14'} + /cssstyle@4.0.1: + resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==} + engines: {node: '>=18'} dependencies: rrweb-cssom: 0.6.0 dev: false - /csstype@3.1.2: - resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} - dev: true - /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - /cypress@13.6.1: - resolution: {integrity: sha512-k1Wl5PQcA/4UoTffYKKaxA0FJKwg8yenYNYRzLt11CUR0Kln+h7Udne6mdU1cUIdXBDTVZWtmiUjzqGs7/pEpw==} + /cypress@13.6.6: + resolution: {integrity: sha512-S+2S9S94611hXimH9a3EAYt81QM913ZVA03pUmGDfLTFa5gyp85NJ8dJGSlEAEmyRsYkioS1TtnWtbv/Fzt11A==} engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} hasBin: true requiresBuild: true dependencies: '@cypress/request': 3.0.0 '@cypress/xvfb': 1.2.4(supports-color@8.1.1) - '@types/node': 18.17.15 '@types/sinonjs__fake-timers': 8.1.1 '@types/sizzle': 2.3.3 arch: 2.2.0 @@ -10764,7 +10655,7 @@ packages: request-progress: 3.0.0 semver: 7.5.4 supports-color: 8.1.1 - tmp: 0.2.1 + tmp: 0.2.2 untildify: 4.0.0 yauzl: 2.10.0 dev: true @@ -10880,6 +10771,12 @@ packages: to-data-view: 1.1.0 dev: false + /decode-named-character-reference@1.0.2: + resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + dependencies: + character-entities: 2.0.2 + dev: true + /decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -10918,7 +10815,7 @@ packages: dependencies: call-bind: 1.0.2 es-get-iterator: 1.1.3 - get-intrinsic: 1.2.0 + get-intrinsic: 1.2.1 is-arguments: 1.1.1 is-array-buffer: 3.0.2 is-date-object: 1.0.5 @@ -10928,17 +10825,13 @@ packages: object-is: 1.1.5 object-keys: 1.1.1 object.assign: 4.1.4 - regexp.prototype.flags: 1.4.3 + regexp.prototype.flags: 1.5.0 side-channel: 1.0.4 which-boxed-primitive: 1.0.2 which-collection: 1.0.1 - which-typed-array: 1.1.9 + which-typed-array: 1.1.11 dev: true - /deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true @@ -10984,8 +10877,8 @@ packages: object-keys: 1.1.1 dev: true - /defu@6.1.2: - resolution: {integrity: sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ==} + /defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} dev: true /del@6.1.1: @@ -11006,10 +10899,6 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - /delegates@1.0.0: - resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - dev: false - /denque@2.1.0: resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} engines: {node: '>=0.10'} @@ -11036,16 +10925,13 @@ packages: /detect-libc@2.0.2: resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} engines: {node: '>=8'} + dev: false /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} dev: true - /detect-node-es@1.1.0: - resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} - dev: true - /detect-package-manager@2.0.1: resolution: {integrity: sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A==} engines: {node: '>=12'} @@ -11063,15 +10949,16 @@ packages: - supports-color dev: true + /devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dependencies: + dequal: 2.0.3 + dev: true + /diff-match-patch@1.0.5: resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} dev: false - /diff-sequences@28.1.1: - resolution: {integrity: sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dev: true - /diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -11115,6 +11002,10 @@ packages: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} dev: true + /dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dev: true + /dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} dependencies: @@ -11204,12 +11095,8 @@ packages: dependencies: jake: 10.8.5 - /electron-to-chromium@1.4.591: - resolution: {integrity: sha512-vLv/P7wwAPKQoY+CVMyyI6rsTp+A14KGtPXx92oz1FY41AAqa9l6Wkizcixg0LDuJgyeo8xgNN9+9hsnGp66UA==} - - /electron-to-chromium@1.4.616: - resolution: {integrity: sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==} - dev: true + /electron-to-chromium@1.4.686: + resolution: {integrity: sha512-3avY1B+vUzNxEgkBDpKOP8WarvUAEwpRaiCL0He5OKWEFxzaOFiq4WoZEZe7qh0ReS7DiWoHMnYoQCKxNZNzSg==} /emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -11366,55 +11253,21 @@ packages: is-symbol: 1.0.4 dev: true - /es6-object-assign@1.1.0: - resolution: {integrity: sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==} - dev: true - /esbuild-plugin-alias@0.2.1: resolution: {integrity: sha512-jyfL/pwPqaFXyKnj8lP8iLk6Z0m099uXR45aSN8Av1XD4vhvQutxxPzgA2bTcAwQpa1zCXDcWOlhFgyP3GKqhQ==} dev: true - /esbuild-register@3.5.0(esbuild@0.18.17): + /esbuild-register@3.5.0(esbuild@0.18.20): resolution: {integrity: sha512-+4G/XmakeBAsvJuDugJvtyF1x+XJT4FMocynNpxrvEBViirpfUn2PgNpCHedfWhF4WokNsO/OvMKrmJOIJsI5A==} peerDependencies: esbuild: '>=0.12 <1' dependencies: debug: 4.3.4(supports-color@8.1.1) - esbuild: 0.18.17 + esbuild: 0.18.20 transitivePeerDependencies: - supports-color dev: true - /esbuild@0.18.17: - resolution: {integrity: sha512-1GJtYnUxsJreHYA0Y+iQz2UEykonY66HNWOb0yXYZi9/kNrORUEHVg87eQsCtqh59PEJ5YVZJO98JHznMJSWjg==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true - optionalDependencies: - '@esbuild/android-arm': 0.18.17 - '@esbuild/android-arm64': 0.18.17 - '@esbuild/android-x64': 0.18.17 - '@esbuild/darwin-arm64': 0.18.17 - '@esbuild/darwin-x64': 0.18.17 - '@esbuild/freebsd-arm64': 0.18.17 - '@esbuild/freebsd-x64': 0.18.17 - '@esbuild/linux-arm': 0.18.17 - '@esbuild/linux-arm64': 0.18.17 - '@esbuild/linux-ia32': 0.18.17 - '@esbuild/linux-loong64': 0.18.17 - '@esbuild/linux-mips64el': 0.18.17 - '@esbuild/linux-ppc64': 0.18.17 - '@esbuild/linux-riscv64': 0.18.17 - '@esbuild/linux-s390x': 0.18.17 - '@esbuild/linux-x64': 0.18.17 - '@esbuild/netbsd-x64': 0.18.17 - '@esbuild/openbsd-x64': 0.18.17 - '@esbuild/sunos-x64': 0.18.17 - '@esbuild/win32-arm64': 0.18.17 - '@esbuild/win32-ia32': 0.18.17 - '@esbuild/win32-x64': 0.18.17 - dev: true - /esbuild@0.18.20: resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} engines: {node: '>=12'} @@ -11445,34 +11298,35 @@ packages: '@esbuild/win32-x64': 0.18.20 dev: true - /esbuild@0.19.9: - resolution: {integrity: sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg==} + /esbuild@0.19.11: + resolution: {integrity: sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==} engines: {node: '>=12'} hasBin: true requiresBuild: true optionalDependencies: - '@esbuild/android-arm': 0.19.9 - '@esbuild/android-arm64': 0.19.9 - '@esbuild/android-x64': 0.19.9 - '@esbuild/darwin-arm64': 0.19.9 - '@esbuild/darwin-x64': 0.19.9 - '@esbuild/freebsd-arm64': 0.19.9 - '@esbuild/freebsd-x64': 0.19.9 - '@esbuild/linux-arm': 0.19.9 - '@esbuild/linux-arm64': 0.19.9 - '@esbuild/linux-ia32': 0.19.9 - '@esbuild/linux-loong64': 0.19.9 - '@esbuild/linux-mips64el': 0.19.9 - '@esbuild/linux-ppc64': 0.19.9 - '@esbuild/linux-riscv64': 0.19.9 - '@esbuild/linux-s390x': 0.19.9 - '@esbuild/linux-x64': 0.19.9 - '@esbuild/netbsd-x64': 0.19.9 - '@esbuild/openbsd-x64': 0.19.9 - '@esbuild/sunos-x64': 0.19.9 - '@esbuild/win32-arm64': 0.19.9 - '@esbuild/win32-ia32': 0.19.9 - '@esbuild/win32-x64': 0.19.9 + '@esbuild/aix-ppc64': 0.19.11 + '@esbuild/android-arm': 0.19.11 + '@esbuild/android-arm64': 0.19.11 + '@esbuild/android-x64': 0.19.11 + '@esbuild/darwin-arm64': 0.19.11 + '@esbuild/darwin-x64': 0.19.11 + '@esbuild/freebsd-arm64': 0.19.11 + '@esbuild/freebsd-x64': 0.19.11 + '@esbuild/linux-arm': 0.19.11 + '@esbuild/linux-arm64': 0.19.11 + '@esbuild/linux-ia32': 0.19.11 + '@esbuild/linux-loong64': 0.19.11 + '@esbuild/linux-mips64el': 0.19.11 + '@esbuild/linux-ppc64': 0.19.11 + '@esbuild/linux-riscv64': 0.19.11 + '@esbuild/linux-s390x': 0.19.11 + '@esbuild/linux-x64': 0.19.11 + '@esbuild/netbsd-x64': 0.19.11 + '@esbuild/openbsd-x64': 0.19.11 + '@esbuild/sunos-x64': 0.19.11 + '@esbuild/win32-arm64': 0.19.11 + '@esbuild/win32-ia32': 0.19.11 + '@esbuild/win32-x64': 0.19.11 /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} @@ -11499,7 +11353,6 @@ packages: /escape-string-regexp@5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} - dev: false /escodegen@2.1.0: resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} @@ -11546,7 +11399,36 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.14.0)(eslint-import-resolver-node@0.3.9)(eslint@8.56.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.11.0)(eslint-import-resolver-node@0.3.9)(eslint@8.53.0): + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.3) + debug: 3.2.7(supports-color@8.1.1) + eslint: 8.53.0 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-module-utils@2.8.0(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -11567,15 +11449,50 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.14.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3) + debug: 3.2.7(supports-color@8.1.1) + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.11.0)(eslint@8.53.0): + resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.3) + array-includes: 3.1.7 + array.prototype.findlastindex: 1.2.3 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 debug: 3.2.7(supports-color@8.1.1) - eslint: 8.56.0 + doctrine: 2.1.0 + eslint: 8.53.0 eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.11.0)(eslint-import-resolver-node@0.3.9)(eslint@8.53.0) + hasown: 2.0.0 + is-core-module: 2.13.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.7 + object.groupby: 1.0.1 + object.values: 1.1.7 + semver: 6.3.1 + tsconfig-paths: 3.15.0 transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack - supports-color dev: true - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.14.0)(eslint@8.56.0): + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0): resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} engines: {node: '>=4'} peerDependencies: @@ -11585,16 +11502,16 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.14.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3) array-includes: 3.1.7 array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 debug: 3.2.7(supports-color@8.1.1) doctrine: 2.1.0 - eslint: 8.56.0 + eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.14.0)(eslint-import-resolver-node@0.3.9)(eslint@8.56.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) hasown: 2.0.0 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -11610,19 +11527,19 @@ packages: - supports-color dev: true - /eslint-plugin-vue@9.19.2(eslint@8.56.0): - resolution: {integrity: sha512-CPDqTOG2K4Ni2o4J5wixkLVNwgctKXFu6oBpVJlpNq7f38lh9I80pRTouZSJ2MAebPJlINU/KTFSXyQfBUlymA==} + /eslint-plugin-vue@9.22.0(eslint@8.57.0): + resolution: {integrity: sha512-7wCXv5zuVnBtZE/74z4yZ0CM8AjH6bk4MQGm7hZjUC2DBppKU5ioeOk5LGSg/s9a1ZJnIsdPLJpXnu1Rc+cVHg==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) - eslint: 8.56.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + eslint: 8.57.0 natural-compare: 1.4.0 nth-check: 2.1.1 - postcss-selector-parser: 6.0.13 - semver: 7.5.4 - vue-eslint-parser: 9.3.2(eslint@8.56.0) + postcss-selector-parser: 6.0.15 + semver: 7.6.0 + vue-eslint-parser: 9.4.2(eslint@8.57.0) xml-name-validator: 4.0.0 transitivePeerDependencies: - supports-color @@ -11739,16 +11656,16 @@ packages: - supports-color dev: true - /eslint@8.56.0: - resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==} + /eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@eslint-community/regexpp': 4.10.0 '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.56.0 - '@humanwhocodes/config-array': 0.11.13 + '@eslint/js': 8.57.0 + '@humanwhocodes/config-array': 0.11.14 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 '@ungap/structured-clone': 1.2.0 @@ -11790,8 +11707,8 @@ packages: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - acorn: 8.11.2 - acorn-jsx: 5.3.2(acorn@8.11.2) + acorn: 8.11.3 + acorn-jsx: 5.3.2(acorn@8.11.3) eslint-visitor-keys: 3.4.3 dev: true @@ -11801,13 +11718,6 @@ packages: hasBin: true dev: true - /esquery@1.4.2: - resolution: {integrity: sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==} - engines: {node: '>=0.10'} - dependencies: - estraverse: 5.3.0 - dev: true - /esquery@1.5.0: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} @@ -11834,7 +11744,6 @@ packages: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} dependencies: '@types/estree': 1.0.5 - dev: false /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} @@ -11877,6 +11786,7 @@ packages: /events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + dev: false /execa@0.7.0: resolution: {integrity: sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==} @@ -11920,6 +11830,21 @@ packages: signal-exit: 3.0.7 strip-final-newline: 2.0.0 + /execa@6.1.0: + resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 3.0.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: true + /execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} @@ -11945,10 +11870,6 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /expand-template@2.0.3: - resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} - engines: {node: '>=6'} - /expect@29.7.0: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -12029,18 +11950,6 @@ packages: tmp: 0.0.33 dev: true - /extract-zip@1.7.0: - resolution: {integrity: sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==} - hasBin: true - dependencies: - concat-stream: 1.6.2 - debug: 2.6.9 - mkdirp: 0.5.6 - yauzl: 2.10.0 - transitivePeerDependencies: - - supports-color - dev: true - /extract-zip@2.0.1(supports-color@8.1.1): resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} @@ -12072,6 +11981,7 @@ packages: /fast-fifo@1.3.0: resolution: {integrity: sha512-IgfweLvEpwyA4WgiQe9Nx6VV2QkML2NkvZnk1oKnIzXgXdWxuhF7zw4DvLTPZJn6PIUneiAXPF24QmoEqHTjyw==} + dev: false /fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} @@ -12102,12 +12012,6 @@ packages: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true - /fast-querystring@1.1.0: - resolution: {integrity: sha512-LWkjBCZlxjnSanuPpZ6mHswjy8hQv3VcPJsQB3ltUF2zjvrycr0leP3TSTEEfvQ1WEMSRl5YNsGqaft9bjLqEw==} - dependencies: - fast-decode-uri-component: 1.0.1 - dev: false - /fast-querystring@1.1.2: resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} dependencies: @@ -12121,7 +12025,6 @@ packages: /fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - dev: false /fast-uri@2.2.0: resolution: {integrity: sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg==} @@ -12167,8 +12070,8 @@ packages: secure-json-parse: 2.7.0 dev: false - /fastify@4.24.3: - resolution: {integrity: sha512-6HHJ+R2x2LS3y1PqxnwEIjOTZxFl+8h4kSC/TuDPXtA+v2JnV9yEtOsNSKK1RMD7sIR2y1ZsA4BEFaid/cK5pg==} + /fastify@4.25.2: + resolution: {integrity: sha512-SywRouGleDHvRh054onj+lEZnbC1sBCLkR0UY3oyJwjD4BdZJUrxBqfkfCaqn74pVCwBaRHGuL3nEWeHbHzAfw==} dependencies: '@fastify/ajv-compiler': 3.5.0 '@fastify/error': 3.4.0 @@ -12179,8 +12082,8 @@ packages: fast-json-stringify: 5.8.0 find-my-way: 7.7.0 light-my-request: 5.11.0 - pino: 8.16.0 - process-warning: 2.2.0 + pino: 8.17.2 + process-warning: 3.0.0 proxy-addr: 2.0.7 rfdc: 1.3.0 secure-json-parse: 2.7.0 @@ -12255,9 +12158,9 @@ packages: token-types: 5.0.1 dev: false - /file-type@18.7.0: - resolution: {integrity: sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw==} - engines: {node: '>=14.16'} + /file-type@19.0.0: + resolution: {integrity: sha512-s7cxa7/leUWLiXO78DVVfBVse+milos9FitauDLG1pI7lNaJ2+5lzPnr2N24ym+84HVwJL6hVuGfgVE+ALvU8Q==} + engines: {node: '>=18'} dependencies: readable-web-to-node-stream: 3.0.2 strtok3: 7.0.0 @@ -12326,10 +12229,14 @@ packages: engines: {node: '>=14'} dependencies: fast-deep-equal: 3.1.3 - fast-querystring: 1.1.0 + fast-querystring: 1.1.2 safe-regex2: 2.0.0 dev: false + /find-package-json@1.2.0: + resolution: {integrity: sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw==} + dev: true + /find-up@3.0.0: resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} engines: {node: '>=6'} @@ -12359,6 +12266,18 @@ packages: semver-regex: 4.0.5 dev: false + /fkill@9.0.0: + resolution: {integrity: sha512-MdYSsbdCaIRjzo5edthZtWmEZVMfr1qrtYZUHIdO3swCE+CoZA8S5l0s4jDsYlTa9ZiXv0pTgpzE7s4N8NeUOA==} + engines: {node: '>=18'} + dependencies: + aggregate-error: 5.0.0 + execa: 8.0.1 + pid-port: 1.0.0 + process-exists: 5.0.0 + ps-list: 8.1.1 + taskkill: 5.0.0 + dev: true + /flat-cache@3.0.4: resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -12430,16 +12349,6 @@ packages: mime-types: 2.1.35 dev: true - /form-data@3.0.1: - resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} - engines: {node: '>= 6'} - requiresBuild: true - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - dev: true - /form-data@4.0.0: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} @@ -12468,6 +12377,7 @@ packages: /fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + dev: true /fs-extra@11.1.1: resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} @@ -12550,22 +12460,6 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true - /gauge@3.0.2: - resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} - engines: {node: '>=10'} - requiresBuild: true - dependencies: - aproba: 2.0.0 - color-support: 1.1.3 - console-control-strings: 1.1.0 - has-unicode: 2.0.1 - object-assign: 4.1.1 - signal-exit: 3.0.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wide-align: 1.1.5 - dev: false - /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -12579,14 +12473,6 @@ packages: resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} dev: true - /get-intrinsic@1.2.0: - resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} - dependencies: - function-bind: 1.1.1 - has: 1.0.3 - has-symbols: 1.0.3 - dev: true - /get-intrinsic@1.2.1: resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} dependencies: @@ -12595,11 +12481,6 @@ packages: has-proto: 1.0.1 has-symbols: 1.0.3 - /get-nonce@1.0.1: - resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} - engines: {node: '>=6'} - dev: true - /get-npm-tarball-url@2.0.3: resolution: {integrity: sha512-R/PW6RqyaBQNWYaSyfrh54/qtcnOp22FHCCiRhSSZj0FP3KQWCsxxt0DzIdVTbwTqe9CtQfvl/FPD4UIPt4pqw==} engines: {node: '>=12.17'} @@ -12610,11 +12491,6 @@ packages: engines: {node: '>=8.0.0'} dev: true - /get-port@5.1.1: - resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} - engines: {node: '>=8'} - dev: true - /get-stream@3.0.0: resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==} engines: {node: '>=4'} @@ -12664,21 +12540,18 @@ packages: hasBin: true dependencies: colorette: 2.0.19 - defu: 6.1.2 + defu: 6.1.4 https-proxy-agent: 5.0.1 mri: 1.2.0 node-fetch-native: 1.0.2 - pathe: 1.1.1 - tar: 6.1.13 + pathe: 1.1.2 + tar: 6.2.0 transitivePeerDependencies: - supports-color dev: true - /github-from-package@0.0.0: - resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} - - /github-slugger@1.5.0: - resolution: {integrity: sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==} + /github-slugger@2.0.0: + resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} dev: true /glob-parent@5.1.2: @@ -12728,6 +12601,7 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 + dev: true /glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} @@ -12815,8 +12689,8 @@ packages: p-cancelable: 3.0.0 responselike: 3.0.0 - /got@14.0.0: - resolution: {integrity: sha512-X01vTgaX9SwaMq5DfImvS+3GMQFFs5HtrrlS9CuzUSzkxAf/tWGEyynuI+Qy7BjciMczZGjyVSmawYbP4eYhYA==} + /got@14.2.0: + resolution: {integrity: sha512-dBq2KkHcQl3AwPoIWsLsQScCPpUgRulz1qZVthjPYKYOPmYfBnekR3vxecjZbm91Vc3JUGnV9mqFX7B+Fe2quw==} engines: {node: '>=20'} dependencies: '@sindresorhus/is': 6.1.0 @@ -12835,6 +12709,10 @@ packages: /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + /grapheme-splitter@1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + dev: true + /graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true @@ -12844,10 +12722,6 @@ packages: engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} dev: true - /gsap@3.12.4: - resolution: {integrity: sha512-1ByAq8dD0W4aBZ/JArgaQvc0gyUfkGkP8mgAQa0qZGdpOKlSOhOf+WNXjoLimKaKG3Z4Iu6DKZtnyszqQeyqWQ==} - dev: false - /gunzip-maybe@1.4.2: resolution: {integrity: sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw==} hasBin: true @@ -12887,6 +12761,16 @@ packages: webidl-conversions: 7.0.0 whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 + dev: false + + /happy-dom@13.6.2: + resolution: {integrity: sha512-Ku+wDqcF/KwFA0dI+xIMZd9Jn020RXjuSil/Vz7gu2yhDC3FsDYZ55qqV9k+SGC4opwb4acisXqVSRxUJMlPbQ==} + engines: {node: '>=16.0.0'} + dependencies: + entities: 4.5.0 + webidl-conversions: 7.0.0 + whatwg-mimetype: 3.0.0 + dev: true /hard-rejection@2.1.0: resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} @@ -12925,10 +12809,6 @@ packages: dependencies: has-symbols: 1.0.3 - /has-unicode@2.0.1: - resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - dev: false - /has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} @@ -12949,21 +12829,39 @@ packages: dependencies: function-bind: 1.1.2 + /hast-util-heading-rank@3.0.0: + resolution: {integrity: sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==} + dependencies: + '@types/hast': 3.0.4 + dev: true + + /hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + dependencies: + '@types/hast': 3.0.4 + dev: true + + /hast-util-to-string@3.0.0: + resolution: {integrity: sha512-OGkAxX1Ua3cbcW6EJ5pT/tslVb90uViVkcJ4ZZIMW/R33DX/AkcJcRrPebPwJkHYwlDHXz4aIwvAAaAdtrACFA==} + dependencies: + '@types/hast': 3.0.4 + dev: true + /he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true dev: true - /headers-polyfill@3.2.5: - resolution: {integrity: sha512-tUCGvt191vNSQgttSyJoibR+VO+I6+iCHIUdhzEMJKE+EAL8BwCN7fUOZlY4ofOelNHsK+gEjxB/B+9N3EWtdA==} + /headers-polyfill@4.0.2: + resolution: {integrity: sha512-EWGTfnTqAO2L/j5HZgoM/3z82L7necsJ0pO9Tp0X1wil3PDLrkypTBRgVO2ExehEEvUycejZD3FuRaXpZZc3kw==} dev: true /highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} dev: false - /highlight.js@11.8.0: - resolution: {integrity: sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==} + /highlight.js@11.9.0: + resolution: {integrity: sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==} engines: {node: '>=12.0.0'} dev: false @@ -13002,6 +12900,11 @@ packages: engines: {node: '>=8'} dev: true + /htmlescape@1.1.1: + resolution: {integrity: sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg==} + engines: {node: '>=0.10'} + dev: false + /htmlparser2@8.0.1: resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==} dependencies: @@ -13032,8 +12935,8 @@ packages: statuses: 2.0.1 toidentifier: 1.0.1 - /http-link-header@1.1.1: - resolution: {integrity: sha512-mW3N/rTYpCn99s1do0zx6nzFZSwLH9HGfUM4ZqLWJ16ylmYaC2v5eYGqrNTQlByx8AzUgGI+V/32gXPugs1+Sw==} + /http-link-header@1.1.2: + resolution: {integrity: sha512-6qz1XhMq/ryde52SZGzVhzi3jcG2KqO16KITkupyQxvW6u7iylm0Fq7r3OpCYsc0S0ELlCiFpuxDcccUwjbEqA==} engines: {node: '>=6.0.0'} dev: false @@ -13071,23 +12974,11 @@ packages: quick-lru: 5.1.1 resolve-alpn: 1.2.1 - /http_ece@1.1.0: - resolution: {integrity: sha512-bptAfCDdPJxOs5zYSe7Y3lpr772s1G346R4Td5LgRUeCwIGpCGDUTJxRrhTNcAXbx37spge0kWEIH7QAYWNTlA==} - engines: {node: '>=4'} - dependencies: - urlsafe-base64: 1.0.0 + /http_ece@1.2.0: + resolution: {integrity: sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==} + engines: {node: '>=16'} dev: false - /https-proxy-agent@4.0.0: - resolution: {integrity: sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==} - engines: {node: '>= 6.0.0'} - dependencies: - agent-base: 5.1.1 - debug: 4.3.4(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - dev: true - /https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} @@ -13096,16 +12987,7 @@ packages: debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color - - /https-proxy-agent@7.0.0: - resolution: {integrity: sha512-0euwPCRyAPSgGdzD1IVN9nJYHtBhJwb6XPfbpQcYbPCwrBidX6GzxmchnaF4sfF/jPb74Ojx5g4yTg3sixlyPw==} - engines: {node: '>= 14'} - dependencies: - agent-base: 7.1.0 - debug: 4.3.4(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - dev: false + dev: true /https-proxy-agent@7.0.2: resolution: {integrity: sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==} @@ -13126,6 +13008,11 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} + /human-signals@3.0.1: + resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==} + engines: {node: '>=12.20.0'} + dev: true + /human-signals@5.0.0: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} @@ -13153,6 +13040,13 @@ packages: resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} dev: true + /ignore-walk@6.0.4: + resolution: {integrity: sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dependencies: + minimatch: 9.0.3 + dev: false + /ignore@5.3.0: resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==} engines: {node: '>= 4'} @@ -13190,6 +13084,11 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} + /indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + dev: true + /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: @@ -13201,6 +13100,7 @@ packages: /ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: true /ini@2.0.0: resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} @@ -13250,12 +13150,6 @@ packages: resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} dev: true - /invariant@2.2.4: - resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} - dependencies: - loose-envify: 1.4.0 - dev: true - /ioredis@5.3.2: resolution: {integrity: sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==} engines: {node: '>=12.22.0'} @@ -13295,6 +13189,11 @@ packages: /ip@2.0.0: resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} + dev: false + + /ip@2.0.1: + resolution: {integrity: sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==} + dev: true /ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} @@ -13309,9 +13208,9 @@ packages: engines: {node: '>=8'} dev: true - /is-absolute-url@3.0.3: - resolution: {integrity: sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==} - engines: {node: '>=8'} + /is-absolute-url@4.0.1: + resolution: {integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true /is-arguments@1.1.1: @@ -13336,6 +13235,7 @@ packages: /is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: false /is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} @@ -13501,6 +13401,11 @@ packages: resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} engines: {node: '>=0.10.0'} + /is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + dev: true + /is-plain-object@2.0.4: resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} engines: {node: '>=0.10.0'} @@ -13642,11 +13547,6 @@ packages: resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} dev: true - /istanbul-lib-coverage@3.2.0: - resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} - engines: {node: '>=8'} - dev: true - /istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -13657,7 +13557,7 @@ packages: engines: {node: '>=8'} dependencies: '@babel/core': 7.23.3 - '@babel/parser': 7.23.4 + '@babel/parser': 7.23.6 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -13670,7 +13570,7 @@ packages: engines: {node: '>=10'} dependencies: '@babel/core': 7.23.3 - '@babel/parser': 7.23.4 + '@babel/parser': 7.23.6 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.5.4 @@ -13682,7 +13582,7 @@ packages: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} dependencies: - istanbul-lib-coverage: 3.2.0 + istanbul-lib-coverage: 3.2.2 make-dir: 4.0.0 supports-color: 7.2.0 dev: true @@ -13692,20 +13592,12 @@ packages: engines: {node: '>=10'} dependencies: debug: 4.3.4(supports-color@8.1.1) - istanbul-lib-coverage: 3.2.0 + istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: - supports-color dev: true - /istanbul-reports@3.1.5: - resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} - engines: {node: '>=8'} - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - dev: true - /istanbul-reports@3.1.6: resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==} engines: {node: '>=8'} @@ -13717,7 +13609,6 @@ packages: /iterare@1.2.1: resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} engines: {node: '>=6'} - dev: false /jackspeak@2.3.6: resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} @@ -13754,7 +13645,7 @@ packages: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.10.5 + '@types/node': 20.11.22 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.1 @@ -13775,7 +13666,7 @@ packages: - supports-color dev: true - /jest-cli@29.7.0(@types/node@20.10.5): + /jest-cli@29.7.0(@types/node@20.11.22): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -13789,10 +13680,10 @@ packages: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.10.5) + create-jest: 29.7.0(@types/node@20.11.22) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.10.5) + jest-config: 29.7.0(@types/node@20.11.22) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.6.2 @@ -13803,7 +13694,7 @@ packages: - ts-node dev: true - /jest-config@29.7.0(@types/node@20.10.5): + /jest-config@29.7.0(@types/node@20.11.22): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -13818,7 +13709,7 @@ packages: '@babel/core': 7.23.3 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.10.5 + '@types/node': 20.11.22 babel-jest: 29.7.0(@babel/core@7.23.3) chalk: 4.1.2 ci-info: 3.9.0 @@ -13843,16 +13734,6 @@ packages: - supports-color dev: true - /jest-diff@28.1.3: - resolution: {integrity: sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - chalk: 4.1.2 - diff-sequences: 28.1.1 - jest-get-type: 28.0.2 - pretty-format: 28.1.3 - dev: true - /jest-diff@29.6.4: resolution: {integrity: sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -13897,7 +13778,7 @@ packages: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.10.5 + '@types/node': 20.11.22 jest-mock: 29.7.0 jest-util: 29.7.0 dev: true @@ -13911,11 +13792,6 @@ packages: - encoding dev: true - /jest-get-type@28.0.2: - resolution: {integrity: sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dev: true - /jest-get-type@29.6.3: resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -13926,7 +13802,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 20.10.5 + '@types/node': 20.11.22 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -13947,16 +13823,6 @@ packages: pretty-format: 29.7.0 dev: true - /jest-matcher-utils@28.1.3: - resolution: {integrity: sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - chalk: 4.1.2 - jest-diff: 28.1.3 - jest-get-type: 28.0.2 - pretty-format: 28.1.3 - dev: true - /jest-matcher-utils@29.7.0: resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -13985,7 +13851,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 20.10.5 + '@types/node': 20.11.22 dev: true /jest-mock@29.7.0: @@ -13993,7 +13859,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.10.5 + '@types/node': 20.11.22 jest-util: 29.7.0 dev: true @@ -14048,7 +13914,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.10.5 + '@types/node': 20.11.22 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -14079,7 +13945,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.10.5 + '@types/node': 20.11.22 chalk: 4.1.2 cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 @@ -14103,10 +13969,10 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/core': 7.23.3 - '@babel/generator': 7.23.4 + '@babel/generator': 7.23.6 '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.3) '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.3) - '@babel/types': 7.23.4 + '@babel/types': 7.24.0 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 @@ -14131,7 +13997,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.10.5 + '@types/node': 20.11.22 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -14155,7 +14021,7 @@ packages: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.10.5 + '@types/node': 20.11.22 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -14174,13 +14040,13 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 20.10.5 + '@types/node': 20.11.22 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest@29.7.0(@types/node@20.10.5): + /jest@29.7.0(@types/node@20.11.22): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -14193,7 +14059,7 @@ packages: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.10.5) + jest-cli: 29.7.0(@types/node@20.11.22) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -14240,11 +14106,6 @@ packages: nopt: 6.0.0 dev: true - /js-levenshtein@1.1.6: - resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} - engines: {node: '>=0.10.0'} - dev: true - /js-stringify@1.0.2: resolution: {integrity: sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==} @@ -14284,18 +14145,18 @@ packages: '@babel/preset-env': optional: true dependencies: - '@babel/core': 7.23.3 - '@babel/parser': 7.23.4 - '@babel/plugin-transform-class-properties': 7.22.5(@babel/core@7.23.3) - '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.3) - '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.3) - '@babel/plugin-transform-private-methods': 7.22.5(@babel/core@7.23.3) - '@babel/preset-env': 7.23.6(@babel/core@7.23.3) - '@babel/preset-flow': 7.23.3(@babel/core@7.23.3) - '@babel/preset-typescript': 7.23.3(@babel/core@7.23.3) - '@babel/register': 7.22.15(@babel/core@7.23.3) - babel-core: 7.0.0-bridge.0(@babel/core@7.23.3) + '@babel/core': 7.24.0 + '@babel/parser': 7.24.0 + '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.24.0) + '@babel/preset-env': 7.23.6(@babel/core@7.24.0) + '@babel/preset-flow': 7.23.3(@babel/core@7.24.0) + '@babel/preset-typescript': 7.23.3(@babel/core@7.24.0) + '@babel/register': 7.22.15(@babel/core@7.24.0) + babel-core: 7.0.0-bridge.0(@babel/core@7.24.0) chalk: 4.1.2 flow-parser: 0.202.0 graceful-fs: 4.2.11 @@ -14309,8 +14170,8 @@ packages: - supports-color dev: true - /jsdom@23.0.1(bufferutil@4.0.7)(utf-8-validate@6.0.3): - resolution: {integrity: sha512-2i27vgvlUsGEBO9+/kJQRbtqtm+191b5zAZrU/UezVmnC2dlDAFLgDYJvAEi94T4kjsRKkezEtLQTgsNEsW2lQ==} + /jsdom@23.2.0(bufferutil@4.0.7)(utf-8-validate@6.0.3): + resolution: {integrity: sha512-L88oL7D/8ufIES+Zjz7v0aes+oBMh2Xnh3ygWvL0OaICOomKEPKuPnIfBJekiXr+BHbbMjrWn/xqrDQuxFTeyA==} engines: {node: '>=18'} peerDependencies: canvas: ^2.11.2 @@ -14318,7 +14179,8 @@ packages: canvas: optional: true dependencies: - cssstyle: 3.0.0 + '@asamuzakjp/dom-selector': 2.0.2 + cssstyle: 4.0.1 data-urls: 5.0.0 decimal.js: 10.4.3 form-data: 4.0.0 @@ -14326,7 +14188,6 @@ packages: http-proxy-agent: 7.0.0 https-proxy-agent: 7.0.2 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.7 parse5: 7.1.2 rrweb-cssom: 0.6.0 saxes: 6.0.0 @@ -14337,7 +14198,7 @@ packages: whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 whatwg-url: 14.0.0 - ws: 8.15.1(bufferutil@4.0.7)(utf-8-validate@6.0.3) + ws: 8.16.0(bufferutil@4.0.7)(utf-8-validate@6.0.3) xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil @@ -14363,17 +14224,6 @@ packages: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: true - /json-schema-resolver@2.0.0: - resolution: {integrity: sha512-pJ4XLQP4Q9HTxl6RVDLJ8Cyh1uitSs0CzDBAz1uoJ4sRD/Bk7cFSXL1FUXDW3zJ7YnfliJx6eu8Jn283bpZ4Yg==} - engines: {node: '>=10'} - dependencies: - debug: 4.3.4(supports-color@8.1.1) - rfdc: 1.3.0 - uri-js: 4.4.1 - transitivePeerDependencies: - - supports-color - dev: false - /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: true @@ -14391,6 +14241,14 @@ packages: /json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + /json-to-ast@2.1.0: + resolution: {integrity: sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==} + engines: {node: '>= 4'} + dependencies: + code-error-fragment: 0.0.230 + grapheme-splitter: 1.0.4 + dev: true + /json5@1.0.2: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true @@ -14404,6 +14262,7 @@ packages: /jsonc-parser@3.2.0: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true /jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} @@ -14438,6 +14297,11 @@ packages: - web-streams-polyfill dev: false + /jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + dev: true + /jsprim@1.4.2: resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} engines: {node: '>=0.6.0'} @@ -14458,8 +14322,8 @@ packages: verror: 1.10.0 dev: true - /jsrsasign@10.9.0: - resolution: {integrity: sha512-QWLUikj1SBJGuyGK8tjKSx3K7Y69KYJnrs/pQ1KZ6wvZIkHkWjZ1PJDpuvc1/28c1uP0KW9qn1eI1LzHQqDOwQ==} + /jsrsasign@11.1.0: + resolution: {integrity: sha512-Ov74K9GihaK9/9WncTe1mPmvrO7Py665TUfUKvraXBpu+xcTWitrtuOwcjf4KMU9maPaYn0OuaWy0HOzy/GBXg==} dev: false /jssha@3.3.1: @@ -14575,8 +14439,8 @@ packages: set-cookie-parser: 2.6.0 dev: false - /lilconfig@3.0.0: - resolution: {integrity: sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==} + /lilconfig@3.1.1: + resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==} engines: {node: '>=14'} dev: false @@ -14685,6 +14549,10 @@ packages: wrap-ansi: 6.2.0 dev: true + /longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + dev: true + /loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -14692,8 +14560,8 @@ packages: js-tokens: 4.0.0 dev: true - /loupe@2.3.6: - resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} dependencies: get-func-name: 2.0.2 dev: true @@ -14756,15 +14624,15 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /magic-string@0.30.3: - resolution: {integrity: sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==} + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} engines: {node: '>=12'} dependencies: '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /magic-string@0.30.5: - resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} + /magic-string@0.30.7: + resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==} engines: {node: '>=12'} dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -14786,6 +14654,7 @@ packages: engines: {node: '>=8'} dependencies: semver: 6.3.1 + dev: true /make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} @@ -14841,8 +14710,12 @@ packages: resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} dev: true - /markdown-to-jsx@7.2.0(react@18.2.0): - resolution: {integrity: sha512-3l4/Bigjm4bEqjCR6Xr+d4DtM1X6vvtGsMGSjJYyep8RjjIvcWtrXBS8Wbfe1/P+atKNMccpsraESIaWVplzVg==} + /markdown-table@3.0.3: + resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} + dev: true + + /markdown-to-jsx@7.3.2(react@18.2.0): + resolution: {integrity: sha512-B+28F5ucp83aQm+OxNrPkS8z0tMKaeHiy0lHJs3LqCyDQFtWuenaIrkaVTgAm1pf1AU85LXltva86hlaT17i8Q==} engines: {node: '>= 10'} peerDependencies: react: '>= 0.14.0' @@ -14860,14 +14733,127 @@ packages: resolution: {integrity: sha512-v2huwvQGOHTGOkMqtHd2hercCG3f6QAObTisPPHg8TZqq2lz7eIY/5i/5YUV8Ibf3mEioFEmwibcPUF2/fnKKQ==} dev: false - /mdast-util-definitions@4.0.0: - resolution: {integrity: sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==} + /mdast-util-find-and-replace@3.0.1: + resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==} + dependencies: + '@types/mdast': 4.0.3 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + dev: true + + /mdast-util-from-markdown@2.0.0: + resolution: {integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==} + dependencies: + '@types/mdast': 4.0.3 + '@types/unist': 3.0.2 + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-decode-string: 2.0.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-gfm-autolink-literal@2.0.0: + resolution: {integrity: sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==} + dependencies: + '@types/mdast': 4.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.1 + micromark-util-character: 2.1.0 + dev: true + + /mdast-util-gfm-footnote@2.0.0: + resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==} + dependencies: + '@types/mdast': 4.0.3 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + micromark-util-normalize-identifier: 2.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + dependencies: + '@types/mdast': 4.0.3 + devlop: 1.1.0 + markdown-table: 3.0.3 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + dependencies: + '@types/mdast': 4.0.3 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-gfm@3.0.0: + resolution: {integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==} + dependencies: + mdast-util-from-markdown: 2.0.0 + mdast-util-gfm-autolink-literal: 2.0.0 + mdast-util-gfm-footnote: 2.0.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + dependencies: + '@types/mdast': 4.0.3 + unist-util-is: 6.0.0 + dev: true + + /mdast-util-to-markdown@2.1.0: + resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==} dependencies: - unist-util-visit: 2.0.3 + '@types/mdast': 4.0.3 + '@types/unist': 3.0.2 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-decode-string: 2.0.0 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 dev: true - /mdast-util-to-string@1.1.0: - resolution: {integrity: sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==} + /mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + dependencies: + '@types/mdast': 4.0.3 dev: true /mdn-data@2.0.28: @@ -14882,8 +14868,8 @@ packages: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} - /meilisearch@0.36.0: - resolution: {integrity: sha512-swcvEYrct0/zsGj3jlbPm1OYxbH14IURnlysKlXywNicIQ5EMkSYLYCLCwOuBKAaGcdISWdgdylH9TXVLegmOQ==} + /meilisearch@0.37.0: + resolution: {integrity: sha512-LdbK6JmRghCawrmWKJSEQF0OiE82md+YqJGE/U2JcCD8ROwlhTx0KM6NX4rQt0u0VpV0QZVG9umYiu3CSSIJAQ==} dependencies: cross-fetch: 3.1.6 transitivePeerDependencies: @@ -14924,16 +14910,263 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - /methods@1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} - engines: {node: '>= 0.6'} + /methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + /microformats-parser@2.0.2: + resolution: {integrity: sha512-tUf9DmN4Jq/tGyp1YH2V6D/Cud+9Uc0WhjjUFirqVeHTRkkfLDacv6BQFT7h7HFsD0Z8wja5eKkRgzZU8bv0Fw==} + engines: {node: '>=18'} + dependencies: + parse5: 7.1.2 + dev: false + + /micromark-core-commonmark@2.0.0: + resolution: {integrity: sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==} + dependencies: + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-factory-destination: 2.0.0 + micromark-factory-label: 2.0.0 + micromark-factory-space: 2.0.0 + micromark-factory-title: 2.0.0 + micromark-factory-whitespace: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-classify-character: 2.0.0 + micromark-util-html-tag-name: 2.0.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-resolve-all: 2.0.0 + micromark-util-subtokenize: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-extension-gfm-autolink-literal@2.0.0: + resolution: {integrity: sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==} + dependencies: + micromark-util-character: 2.1.0 + micromark-util-sanitize-uri: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-extension-gfm-footnote@2.0.0: + resolution: {integrity: sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==} + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-sanitize-uri: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-extension-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==} + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-classify-character: 2.0.0 + micromark-util-resolve-all: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-extension-gfm-table@2.0.0: + resolution: {integrity: sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==} + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + dependencies: + micromark-util-types: 2.0.0 + dev: true + + /micromark-extension-gfm-task-list-item@2.0.1: + resolution: {integrity: sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==} + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + dependencies: + micromark-extension-gfm-autolink-literal: 2.0.0 + micromark-extension-gfm-footnote: 2.0.0 + micromark-extension-gfm-strikethrough: 2.0.0 + micromark-extension-gfm-table: 2.0.0 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.0.1 + micromark-util-combine-extensions: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-factory-destination@2.0.0: + resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==} + dependencies: + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-factory-label@2.0.0: + resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==} + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-factory-space@2.0.0: + resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==} + dependencies: + micromark-util-character: 2.1.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-factory-title@2.0.0: + resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==} + dependencies: + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-factory-whitespace@2.0.0: + resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==} + dependencies: + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-util-character@2.1.0: + resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==} + dependencies: + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-util-chunked@2.0.0: + resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==} + dependencies: + micromark-util-symbol: 2.0.0 + dev: true + + /micromark-util-classify-character@2.0.0: + resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==} + dependencies: + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-util-combine-extensions@2.0.0: + resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==} + dependencies: + micromark-util-chunked: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-util-decode-numeric-character-reference@2.0.1: + resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==} + dependencies: + micromark-util-symbol: 2.0.0 + dev: true + + /micromark-util-decode-string@2.0.0: + resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==} + dependencies: + decode-named-character-reference: 1.0.2 + micromark-util-character: 2.1.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-symbol: 2.0.0 + dev: true + + /micromark-util-encode@2.0.0: + resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} + dev: true + + /micromark-util-html-tag-name@2.0.0: + resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==} + dev: true + + /micromark-util-normalize-identifier@2.0.0: + resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==} + dependencies: + micromark-util-symbol: 2.0.0 + dev: true + + /micromark-util-resolve-all@2.0.0: + resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==} + dependencies: + micromark-util-types: 2.0.0 + dev: true + + /micromark-util-sanitize-uri@2.0.0: + resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} + dependencies: + micromark-util-character: 2.1.0 + micromark-util-encode: 2.0.0 + micromark-util-symbol: 2.0.0 + dev: true + + /micromark-util-subtokenize@2.0.0: + resolution: {integrity: sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==} + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-util-symbol@2.0.0: + resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} + dev: true - /microformats-parser@2.0.2: - resolution: {integrity: sha512-tUf9DmN4Jq/tGyp1YH2V6D/Cud+9Uc0WhjjUFirqVeHTRkkfLDacv6BQFT7h7HFsD0Z8wja5eKkRgzZU8bv0Fw==} - engines: {node: '>=18'} + /micromark-util-types@2.0.0: + resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} + dev: true + + /micromark@4.0.0: + resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} dependencies: - parse5: 7.1.2 - dev: false + '@types/debug': 4.1.12 + debug: 4.3.4(supports-color@8.1.1) + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-combine-extensions: 2.0.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-encode: 2.0.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-resolve-all: 2.0.0 + micromark-util-sanitize-uri: 2.0.0 + micromark-util-subtokenize: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + transitivePeerDependencies: + - supports-color + dev: true /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} @@ -14957,12 +15190,6 @@ packages: engines: {node: '>=4'} hasBin: true - /mime@2.6.0: - resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} - engines: {node: '>=4.0.0'} - hasBin: true - dev: true - /mime@3.0.0: resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} engines: {node: '>=10.0.0'} @@ -15084,15 +15311,9 @@ packages: dependencies: yallist: 4.0.0 - /minipass@4.2.5: - resolution: {integrity: sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==} - engines: {node: '>=8'} - requiresBuild: true - /minipass@5.0.0: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} - dev: false /minipass@7.0.4: resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} @@ -15107,13 +15328,13 @@ packages: /mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + dev: true /mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true dependencies: minimist: 1.2.8 - dev: true /mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} @@ -15127,13 +15348,13 @@ packages: hasBin: true dev: false - /mlly@1.4.0: - resolution: {integrity: sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg==} + /mlly@1.5.0: + resolution: {integrity: sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==} dependencies: - acorn: 8.11.2 - pathe: 1.1.1 + acorn: 8.11.3 + pathe: 1.1.2 pkg-types: 1.0.3 - ufo: 1.1.2 + ufo: 1.3.2 dev: true /mnemonist@0.39.6: @@ -15182,61 +15403,69 @@ packages: dev: false optional: true - /msgpackr@1.9.2: - resolution: {integrity: sha512-xtDgI3Xv0AAiZWLRGDchyzBwU6aq0rwJ+W+5Y4CZhEWtkl/hJtFFLc+3JtGTw7nz1yquxs7nL8q/yA2aqpflIQ==} + /msgpackr@1.10.1: + resolution: {integrity: sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ==} optionalDependencies: msgpackr-extract: 3.0.2 dev: false - /msw-storybook-addon@1.10.0(msw@1.3.2): - resolution: {integrity: sha512-soCTMTf7DnLeaMnFHPrtVgbyeFTJALVvnDHpzzXpJad+HOzJgQdwU4EAzVfDs1q+X5cVEgxOdAhSMC7ljvnSXg==} + /msw-storybook-addon@2.0.0-beta.1(msw@2.1.7): + resolution: {integrity: sha512-DRyIAMK3waEfC+pKTyiIq68OZfiZ4WZGUVAn6J4YwCRpDdoCvLzzoC2spN0Jgegx4dEmJ7589ATnS14NxqeBig==} peerDependencies: - msw: '>=0.35.0 <2.0.0' + msw: ^2.0.0 dependencies: is-node-process: 1.2.0 - msw: 1.3.2(typescript@5.3.3) + msw: 2.1.7(typescript@5.3.3) dev: true - /msw@1.3.2(typescript@5.3.3): - resolution: {integrity: sha512-wKLhFPR+NitYTkQl5047pia0reNGgf0P6a1eTnA5aNlripmiz0sabMvvHcicE8kQ3/gZcI0YiPFWmYfowfm3lA==} - engines: {node: '>=14'} + /msw@2.1.7(typescript@5.3.3): + resolution: {integrity: sha512-yTIYqEMqDSrdbVMrfmqP6rTKQsnIbglTvVmAHDWwNegyXPXRcV+RjsaFEqubRS266gwWCDLm9YdOkWSKLdDvJQ==} + engines: {node: '>=18'} hasBin: true requiresBuild: true peerDependencies: - typescript: '>= 4.4.x <= 5.2.x' + typescript: '>= 4.7.x <= 5.3.x' peerDependenciesMeta: typescript: optional: true dependencies: - '@mswjs/cookies': 0.2.2 - '@mswjs/interceptors': 0.17.10 - '@open-draft/until': 1.0.3 - '@types/cookie': 0.4.1 - '@types/js-levenshtein': 1.1.1 + '@bundled-es-modules/cookie': 2.0.0 + '@bundled-es-modules/statuses': 1.0.1 + '@mswjs/cookies': 1.1.0 + '@mswjs/interceptors': 0.25.16 + '@open-draft/until': 2.1.0 + '@types/cookie': 0.6.0 + '@types/statuses': 2.0.4 chalk: 4.1.2 chokidar: 3.5.3 - cookie: 0.4.2 graphql: 16.8.1 - headers-polyfill: 3.2.5 + headers-polyfill: 4.0.2 inquirer: 8.2.5 is-node-process: 1.2.0 - js-levenshtein: 1.1.6 - node-fetch: 2.7.0 - outvariant: 1.4.0 + outvariant: 1.4.2 path-to-regexp: 6.2.1 - strict-event-emitter: 0.4.6 - type-fest: 2.19.0 + strict-event-emitter: 0.5.1 + type-fest: 4.9.0 typescript: 5.3.3 - yargs: 17.6.2 - transitivePeerDependencies: - - encoding - - supports-color + yargs: 17.7.2 dev: true /muggle-string@0.3.1: resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==} dev: true + /multer@1.4.4-lts.1: + resolution: {integrity: sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==} + engines: {node: '>= 6.0.0'} + dependencies: + append-field: 1.0.0 + busboy: 1.6.0 + concat-stream: 1.6.2 + mkdirp: 0.5.6 + object-assign: 4.1.1 + type-is: 1.6.18 + xtend: 4.0.2 + /mute-stream@0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} dev: true @@ -15258,26 +15487,17 @@ packages: resolution: {integrity: sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==} dev: false - /nanoid@3.3.6: - resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: false - /nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - /nanoid@5.0.4: - resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==} + /nanoid@5.0.6: + resolution: {integrity: sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==} engines: {node: ^18 || >=20} hasBin: true dev: false - /napi-build-utils@1.0.2: - resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} - /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true @@ -15324,25 +15544,13 @@ packages: path-to-regexp: 1.8.0 dev: true - /node-abi@3.31.0: - resolution: {integrity: sha512-eSKV6s+APenqVh8ubJyiu/YhZgxQpGP66ntzUb3lY1xB9ukSRaGnx0AIxI+IM+1+IVYC1oWobgG5L3Lt9ARykQ==} - engines: {node: '>=10'} - dependencies: - semver: 7.5.4 - /node-abort-controller@3.1.1: resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} dev: false - /node-addon-api@5.1.0: - resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} - dev: false - - /node-addon-api@6.1.0: - resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} - - /node-addon-api@7.0.0: - resolution: {integrity: sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==} + /node-addon-api@7.1.0: + resolution: {integrity: sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==} + engines: {node: ^16 || ^18 || >= 20} dev: false /node-dir@0.1.17: @@ -15391,13 +15599,6 @@ packages: fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 - /node-gyp-build-optional-packages@5.0.3: - resolution: {integrity: sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==} - hasBin: true - requiresBuild: true - dev: false - optional: true - /node-gyp-build-optional-packages@5.0.7: resolution: {integrity: sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==} hasBin: true @@ -15410,6 +15611,11 @@ packages: hasBin: true requiresBuild: true + /node-gyp-build@4.8.0: + resolution: {integrity: sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==} + hasBin: true + dev: false + /node-gyp@10.0.1: resolution: {integrity: sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg==} engines: {node: ^16.14.0 || >=18.0.0} @@ -15423,7 +15629,7 @@ packages: nopt: 7.2.0 proc-log: 3.0.0 semver: 7.5.4 - tar: 6.1.13 + tar: 6.2.0 which: 4.0.0 transitivePeerDependencies: - supports-color @@ -15435,13 +15641,13 @@ packages: /node-releases@2.0.13: resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + dev: true /node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} - dev: true - /nodemailer@6.9.7: - resolution: {integrity: sha512-rUtR77ksqex/eZRLmQ21LKVH5nAAsVicAtAYudK7JgwenEDZ0UIQ1adUGqErz7sMkWYxWTTU1aeP2Jga6WQyJw==} + /nodemailer@6.9.10: + resolution: {integrity: sha512-qtoKfGFhvIFW5kLfrkw2R6Nm6Ur4LNUMykyqu6n9BRKJuyQrqEGwdXXUAbwWEKt33dlWUGXb7rzmJP/p4+O+CA==} engines: {node: '>=6.0.0'} dev: false @@ -15462,9 +15668,27 @@ packages: undefsafe: 2.0.5 dev: true + /nodemon@3.1.0: + resolution: {integrity: sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + chokidar: 3.5.3 + debug: 4.3.4(supports-color@5.5.0) + ignore-by-default: 1.0.1 + minimatch: 3.1.2 + pstree.remy: 1.1.8 + semver: 7.5.4 + simple-update-notifier: 2.0.0 + supports-color: 5.5.0 + touch: 3.1.0 + undefsafe: 2.0.5 + dev: true + /nofilter@3.1.0: resolution: {integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==} engines: {node: '>=12.19'} + dev: false /nopt@1.0.10: resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==} @@ -15473,15 +15697,6 @@ packages: abbrev: 1.1.1 dev: true - /nopt@5.0.0: - resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} - engines: {node: '>=6'} - hasBin: true - requiresBuild: true - dependencies: - abbrev: 1.1.1 - dev: false - /nopt@6.0.0: resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -15549,25 +15764,11 @@ packages: dependencies: path-key: 4.0.0 - /npmlog@5.0.1: - resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} - requiresBuild: true - dependencies: - are-we-there-yet: 2.0.0 - console-control-strings: 1.1.0 - gauge: 3.0.2 - set-blocking: 2.0.0 - dev: false - /nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} dependencies: boolbase: 1.0.0 - /nwsapi@2.2.7: - resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==} - dev: false - /oauth2orize-pkce@0.1.2: resolution: {integrity: sha512-grto2UYhXHi9GLE3IBgBBbV87xci55+bCyjpVuxKyzol6I5Rg0K1MiTuXE+JZk54R86SG2wqXODMiZYHraPpxw==} dev: false @@ -15705,9 +15906,10 @@ packages: /openapi-types@12.1.3: resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + dev: true - /openapi-typescript@6.7.1: - resolution: {integrity: sha512-Q3Ltt0KUm2smcPrsaR8qKmSwQ1KM4yGDJVoQdpYa0yvKPeN8huDx5utMT7DvwvJastHHzUxajjivK3WN2+fobg==} + /openapi-typescript@6.7.3: + resolution: {integrity: sha512-es3mGcDXV6TKPo6n3aohzHm0qxhLyR39MhF6mkD1FwFGjhxnqMqfSIgM0eCpInZvqatve4CxmXcMZw3jnnsaXw==} hasBin: true dependencies: ansi-colors: 4.1.3 @@ -15770,14 +15972,14 @@ packages: resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==} dev: true - /otpauth@9.2.1: - resolution: {integrity: sha512-/MRvcm63pzK20NCsIOe8Btun42/yWNylPbUo/h5dMpSRJpoAJstWodEUjm4zUDeT1+Vbqif2E8IcP4trl1U4gQ==} + /otpauth@9.2.2: + resolution: {integrity: sha512-2VcnYRUmq1dNckIfySNYP32ITWp1bvTeAEW0BSCR6G3GBf3a5zb9E+ubY62t3Dma9RjoHlvd7QpmzHfJZRkiNg==} dependencies: jssha: 3.3.1 dev: false - /outvariant@1.4.0: - resolution: {integrity: sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==} + /outvariant@1.4.2: + resolution: {integrity: sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==} dev: true /p-cancelable@2.1.1: @@ -15943,6 +16145,7 @@ packages: /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} + dev: true /path-key@2.0.1: resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} @@ -15978,7 +16181,6 @@ packages: /path-to-regexp@3.2.0: resolution: {integrity: sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==} - dev: false /path-to-regexp@6.2.1: resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} @@ -15988,8 +16190,8 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - /pathe@1.1.1: - resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} + /pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} dev: true /pathval@1.1.1: @@ -16115,6 +16317,13 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + /pid-port@1.0.0: + resolution: {integrity: sha512-LSNBeKChRPA4Xlrs6+zV588G1hSrFvANtPV5rt/5MPfSPK3V9XPWxx1d29svsrOjngT9ifLisXWCLS7DvO9ZhQ==} + engines: {node: '>=18'} + dependencies: + execa: 8.0.1 + dev: true + /pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -16135,8 +16344,8 @@ packages: resolution: {integrity: sha512-KO0m2f1HkrPe9S0ldjx7za9BJjeHqBku5Ch8JyxETxT8dEFGz1PwgrHaOQupVYitpzbFSYm7nnljxD8dik2c+g==} dev: false - /pino@8.16.0: - resolution: {integrity: sha512-UUmvQ/7KTZt/vHjhRrnyS7h+J7qPBQnpG80V56xmIC+o9IqYmQOw/UIny9S9zYDfRBR0ClouCr464EkBMIT7Fw==} + /pino@8.17.2: + resolution: {integrity: sha512-LA6qKgeDMLr2ux2y/YiUt47EfgQ+S9LznBWOJdN3q1dx2sv0ziDLUBeVpyVv17TEcGCBuWf0zNtg3M5m1NhhWQ==} hasBin: true dependencies: atomic-sleep: 1.0.0 @@ -16144,7 +16353,7 @@ packages: on-exit-leak-free: 2.1.0 pino-abstract-transport: 1.1.0 pino-std-serializers: 6.1.0 - process-warning: 2.2.0 + process-warning: 3.0.0 quick-format-unescaped: 4.0.4 real-require: 0.2.0 safe-stable-stringify: 2.4.2 @@ -16157,8 +16366,8 @@ packages: engines: {node: '>= 6'} dev: true - /pkce-challenge@4.0.1: - resolution: {integrity: sha512-WGmtS1stcStsvRwNXix3iR1ujFcDaJR+sEODRa2ZFruT0lM4lhPAFTL5SUpqD5vTJdRlgtuMQhcp1kIEJx4LUw==} + /pkce-challenge@4.1.0: + resolution: {integrity: sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ==} engines: {node: '>=16.20.0'} dev: false @@ -16187,8 +16396,8 @@ packages: resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} dependencies: jsonc-parser: 3.2.0 - mlly: 1.4.0 - pathe: 1.1.1 + mlly: 1.5.0 + pathe: 1.1.2 dev: true /plimit-lit@1.5.0: @@ -16218,313 +16427,304 @@ packages: resolution: {integrity: sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==} engines: {node: '>=10'} dependencies: - '@babel/runtime': 7.23.2 + '@babel/runtime': 7.23.4 dev: true - /postcss-calc@9.0.1(postcss@8.4.32): + /postcss-calc@9.0.1(postcss@8.4.35): resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.2.2 dependencies: - postcss: 8.4.32 - postcss-selector-parser: 6.0.13 + postcss: 8.4.35 + postcss-selector-parser: 6.0.15 postcss-value-parser: 4.2.0 dev: false - /postcss-colormin@6.0.1(postcss@8.4.32): - resolution: {integrity: sha512-Tb9aR2wCJCzKuNjIeMzVNd0nXjQy25HDgFmmaRsHnP0eP/k8uQWE4S8voX5S2coO5CeKrp+USFs1Ayv9Tpxx6w==} + /postcss-colormin@6.0.3(postcss@8.4.35): + resolution: {integrity: sha512-ECpkS+UZRyAtu/kjive2/1mihP+GNtgC8kcdU8ueWZi1ZVxMNnRziCLdhrWECJhEtSWijfX2Cl9XTTCK/hjGaA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - browserslist: 4.22.1 + browserslist: 4.23.0 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.4.32 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-convert-values@6.0.1(postcss@8.4.32): - resolution: {integrity: sha512-zTd4Vh0HxGkhg5aHtfCogcRHzGkvblfdWlQ53lIh1cJhYcGyIxh2hgtKoVh40AMktRERet+JKdB04nNG19kjmA==} + /postcss-convert-values@6.0.4(postcss@8.4.35): + resolution: {integrity: sha512-YT2yrGzPXoQD3YeA2kBo/696qNwn7vI+15AOS2puXWEvSWqdCqlOyDWRy5GNnOc9ACRGOkuQ4ESQEqPJBWt/GA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - browserslist: 4.22.1 - postcss: 8.4.32 + browserslist: 4.23.0 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-discard-comments@6.0.1(postcss@8.4.32): + /postcss-discard-comments@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-f1KYNPtqYLUeZGCHQPKzzFtsHaRuECe6jLakf/RjSRqvF5XHLZnM2+fXLhb8Qh/HBFHs3M4cSLb1k3B899RYIg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 dev: false - /postcss-discard-duplicates@6.0.1(postcss@8.4.32): - resolution: {integrity: sha512-1hvUs76HLYR8zkScbwyJ8oJEugfPV+WchpnA+26fpJ7Smzs51CzGBHC32RS03psuX/2l0l0UKh2StzNxOrKCYg==} + /postcss-discard-duplicates@6.0.2(postcss@8.4.35): + resolution: {integrity: sha512-U2rsj4w6pAGROCCcD13LP2eBIi1whUsXs4kgE6xkIuGfkbxCBSKhkCTWyowFd66WdVlLv0uM1euJKIgmdmZObg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 dev: false - /postcss-discard-empty@6.0.1(postcss@8.4.32): - resolution: {integrity: sha512-yitcmKwmVWtNsrrRqGJ7/C0YRy53i0mjexBDQ9zYxDwTWVBgbU4+C9jIZLmQlTDT9zhml+u0OMFJh8+31krmOg==} + /postcss-discard-empty@6.0.2(postcss@8.4.35): + resolution: {integrity: sha512-rj6pVC2dVCJrP0Y2RkYTQEbYaCf4HEm+R/2StQgJqGHxAa3+KcYslNQhcRqjLHtl/4wpzipJluaJLqBj6d5eDQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 dev: false - /postcss-discard-overridden@6.0.1(postcss@8.4.32): + /postcss-discard-overridden@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-qs0ehZMMZpSESbRkw1+inkf51kak6OOzNRaoLd/U7Fatp0aN2HQ1rxGOrJvYcRAN9VpX8kUF13R2ofn8OlvFVA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 dev: false - /postcss-merge-longhand@6.0.1(postcss@8.4.32): - resolution: {integrity: sha512-vmr/HZQzaPXc45FRvSctqFTF05UaDnTn5ABX+UtQPJznDWT/QaFbVc/pJ5C2YPxx2J2XcfmWowlKwtCDwiQ5hA==} + /postcss-merge-longhand@6.0.3(postcss@8.4.35): + resolution: {integrity: sha512-kF/y3DU8CRt+SX3tP/aG+2gkZI2Z7OXDsPU7FgxIJmuyhQQ1EHceIYcsp/alvzCm2P4c37Sfdu8nNrHc+YeyLg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 postcss-value-parser: 4.2.0 - stylehacks: 6.0.1(postcss@8.4.32) + stylehacks: 6.0.3(postcss@8.4.35) dev: false - /postcss-merge-rules@6.0.2(postcss@8.4.32): - resolution: {integrity: sha512-6lm8bl0UfriSfxI+F/cezrebqqP8w702UC6SjZlUlBYwuRVNbmgcJuQU7yePIvD4MNT53r/acQCUAyulrpgmeQ==} + /postcss-merge-rules@6.0.4(postcss@8.4.35): + resolution: {integrity: sha512-97iF3UJ5v8N1BWy38y+0l+Z8o5/9uGlEgtWic2PJPzoRrLB6Gxg8TVG93O0EK52jcLeMsywre26AUlX1YAYeHA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - browserslist: 4.22.1 + browserslist: 4.23.0 caniuse-api: 3.0.0 - cssnano-utils: 4.0.1(postcss@8.4.32) - postcss: 8.4.32 - postcss-selector-parser: 6.0.13 + cssnano-utils: 4.0.1(postcss@8.4.35) + postcss: 8.4.35 + postcss-selector-parser: 6.0.15 dev: false - /postcss-minify-font-values@6.0.1(postcss@8.4.32): - resolution: {integrity: sha512-tIwmF1zUPoN6xOtA/2FgVk1ZKrLcCvE0dpZLtzyyte0j9zUeB8RTbCqrHZGjJlxOvNWKMYtunLrrl7HPOiR46w==} + /postcss-minify-font-values@6.0.2(postcss@8.4.35): + resolution: {integrity: sha512-IedzbVMoX0a7VZWjSYr5qJ6C37rws8kl8diPBeMZLJfWKkgXuMFY5R/OxPegn/q9tK9ztd0XRH3aR0u2t+A7uQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-minify-gradients@6.0.1(postcss@8.4.32): - resolution: {integrity: sha512-M1RJWVjd6IOLPl1hYiOd5HQHgpp6cvJVLrieQYS9y07Yo8itAr6jaekzJphaJFR0tcg4kRewCk3kna9uHBxn/w==} + /postcss-minify-gradients@6.0.2(postcss@8.4.35): + resolution: {integrity: sha512-vP5mF7iI6/5fcpv+rSfwWQekOE+8I1i7/7RjZPGuIjj6eUaZVeG4XZYZrroFuw1WQd51u2V32wyQFZ+oYdE7CA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: colord: 2.9.3 - cssnano-utils: 4.0.1(postcss@8.4.32) - postcss: 8.4.32 + cssnano-utils: 4.0.1(postcss@8.4.35) + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-minify-params@6.0.1(postcss@8.4.32): - resolution: {integrity: sha512-eFvGWArqh4khPIgPDu6SZNcaLctx97nO7c59OXnRtGntAp5/VS4gjMhhW9qUFsK6mQ27pEZGt2kR+mPizI+Z9g==} + /postcss-minify-params@6.0.3(postcss@8.4.35): + resolution: {integrity: sha512-j4S74d3AAeCK5eGdQndXSrkxusV2ekOxbXGnlnZthMyZBBvSDiU34CihTASbJxuVB3bugudmwolS7+Dgs5OyOQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - browserslist: 4.22.1 - cssnano-utils: 4.0.1(postcss@8.4.32) - postcss: 8.4.32 + browserslist: 4.23.0 + cssnano-utils: 4.0.1(postcss@8.4.35) + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-minify-selectors@6.0.1(postcss@8.4.32): - resolution: {integrity: sha512-mfReq5wrS6vkunxvJp6GDuOk+Ak6JV7134gp8L+ANRnV9VwqzTvBtX6lpohooVU750AR0D3pVx2Zn6uCCwOAfQ==} + /postcss-minify-selectors@6.0.2(postcss@8.4.35): + resolution: {integrity: sha512-0b+m+w7OAvZejPQdN2GjsXLv5o0jqYHX3aoV0e7RBKPCsB7TYG5KKWBFhGnB/iP3213Ts8c5H4wLPLMm7z28Sg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 - postcss-selector-parser: 6.0.13 + postcss: 8.4.35 + postcss-selector-parser: 6.0.15 dev: false - /postcss-normalize-charset@6.0.1(postcss@8.4.32): + /postcss-normalize-charset@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-aW5LbMNRZ+oDV57PF9K+WI1Z8MPnF+A8qbajg/T8PP126YrGX1f9IQx21GI2OlGz7XFJi/fNi0GTbY948XJtXg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 dev: false - /postcss-normalize-display-values@6.0.1(postcss@8.4.32): + /postcss-normalize-display-values@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-mc3vxp2bEuCb4LgCcmG1y6lKJu1Co8T+rKHrcbShJwUmKJiEl761qb/QQCfFwlrvSeET3jksolCR/RZuMURudw==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-positions@6.0.1(postcss@8.4.32): + /postcss-normalize-positions@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-HRsq8u/0unKNvm0cvwxcOUEcakFXqZ41fv3FOdPn916XFUrympjr+03oaLkuZENz3HE9RrQE9yU0Xv43ThWjQg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-repeat-style@6.0.1(postcss@8.4.32): + /postcss-normalize-repeat-style@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-Gbb2nmCy6tTiA7Sh2MBs3fj9W8swonk6lw+dFFeQT68B0Pzwp1kvisJQkdV6rbbMSd9brMlS8I8ts52tAGWmGQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-string@6.0.1(postcss@8.4.32): + /postcss-normalize-string@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-5Fhx/+xzALJD9EI26Aq23hXwmv97Zfy2VFrt5PLT8lAhnBIZvmaT5pQk+NuJ/GWj/QWaKSKbnoKDGLbV6qnhXg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-timing-functions@6.0.1(postcss@8.4.32): + /postcss-normalize-timing-functions@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-4zcczzHqmCU7L5dqTB9rzeqPWRMc0K2HoR+Bfl+FSMbqGBUcP5LRfgcH4BdRtLuzVQK1/FHdFoGT3F7rkEnY+g==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-unicode@6.0.1(postcss@8.4.32): - resolution: {integrity: sha512-ok9DsI94nEF79MkvmLfHfn8ddnKXA7w+8YuUoz5m7b6TOdoaRCpvu/QMHXQs9+DwUbvp+ytzz04J55CPy77PuQ==} + /postcss-normalize-unicode@6.0.3(postcss@8.4.35): + resolution: {integrity: sha512-T2Bb3gXz0ASgc3ori2dzjv6j/P2IantreaC6fT8tWjqYUiqMAh5jGIkdPwEV2FaucjQlCLeFJDJh2BeSugE1ig==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - browserslist: 4.22.1 - postcss: 8.4.32 + browserslist: 4.23.0 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-url@6.0.1(postcss@8.4.32): + /postcss-normalize-url@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-jEXL15tXSvbjm0yzUV7FBiEXwhIa9H88JOXDGQzmcWoB4mSjZIsmtto066s2iW9FYuIrIF4k04HA2BKAOpbsaQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-whitespace@6.0.1(postcss@8.4.32): + /postcss-normalize-whitespace@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-76i3NpWf6bB8UHlVuLRxG4zW2YykF9CTEcq/9LGAiz2qBuX5cBStadkk0jSkg9a9TCIXbMQz7yzrygKoCW9JuA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-ordered-values@6.0.1(postcss@8.4.32): + /postcss-ordered-values@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-XXbb1O/MW9HdEhnBxitZpPFbIvDgbo9NK4c/5bOfiKpnIGZDoL2xd7/e6jW5DYLsWxBbs+1nZEnVgnjnlFViaA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - cssnano-utils: 4.0.1(postcss@8.4.32) - postcss: 8.4.32 + cssnano-utils: 4.0.1(postcss@8.4.35) + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-reduce-initial@6.0.1(postcss@8.4.32): - resolution: {integrity: sha512-cgzsI2ThG1PMSdSyM9A+bVxiiVgPIVz9f5c6H+TqEv0CA89iCOO81mwLWRWLgOKFtQkKob9nNpnkxG/1RlgFcA==} + /postcss-reduce-initial@6.0.3(postcss@8.4.35): + resolution: {integrity: sha512-w4QIR9pEa1N4xMx3k30T1vLZl6udVK2RmNqrDXhBXX9L0mBj2a8ADs8zkbaEH7eUy1m30Wyr5EBgHN31Yq1JvA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - browserslist: 4.22.1 + browserslist: 4.23.0 caniuse-api: 3.0.0 - postcss: 8.4.32 + postcss: 8.4.35 dev: false - /postcss-reduce-transforms@6.0.1(postcss@8.4.32): + /postcss-reduce-transforms@6.0.1(postcss@8.4.35): resolution: {integrity: sha512-fUbV81OkUe75JM+VYO1gr/IoA2b/dRiH6HvMwhrIBSUrxq3jNZQZitSnugcTLDi1KkQh1eR/zi+iyxviUNBkcQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 postcss-value-parser: 4.2.0 dev: false - /postcss-selector-parser@6.0.13: - resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} + /postcss-selector-parser@6.0.15: + resolution: {integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==} engines: {node: '>=4'} dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - /postcss-svgo@6.0.1(postcss@8.4.32): - resolution: {integrity: sha512-eWV4Rrqa06LzTgqirOv5Ln6WTGyU7Pbeqj9WEyKo9tpnWixNATVJMeaEcOHOW1ZYyjcG8wSJwX/28DvU3oy3HA==} + /postcss-svgo@6.0.2(postcss@8.4.35): + resolution: {integrity: sha512-IH5R9SjkTkh0kfFOQDImyy1+mTCb+E830+9SV1O+AaDcoHTvfsvt6WwJeo7KwcHbFnevZVCsXhDmjFiGVuwqFQ==} engines: {node: ^14 || ^16 || >= 18} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 + postcss: 8.4.35 postcss-value-parser: 4.2.0 - svgo: 3.1.0 + svgo: 3.2.0 dev: false - /postcss-unique-selectors@6.0.1(postcss@8.4.32): - resolution: {integrity: sha512-/KCCEpNNR7oXVJ38/Id7GC9Nt0zxO1T3zVbhVaq6F6LSG+3gU3B7+QuTHfD0v8NPEHlzewAout29S0InmB78EQ==} + /postcss-unique-selectors@6.0.2(postcss@8.4.35): + resolution: {integrity: sha512-8IZGQ94nechdG7Y9Sh9FlIY2b4uS8/k8kdKRX040XHsS3B6d1HrJAkXrBSsSu4SuARruSsUjW3nlSw8BHkaAYQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - postcss: 8.4.32 - postcss-selector-parser: 6.0.13 + postcss: 8.4.35 + postcss-selector-parser: 6.0.15 dev: false /postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} dev: false - /postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} - engines: {node: ^10 || ^12 || >=14} - dependencies: - nanoid: 3.3.6 - picocolors: 1.0.0 - source-map-js: 1.0.2 - dev: false - - /postcss@8.4.32: - resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==} + /postcss@8.4.35: + resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.7 @@ -16579,43 +16779,19 @@ packages: resolution: {integrity: sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==} dev: true - /prebuild-install@7.1.1: - resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} - engines: {node: '>=10'} - hasBin: true - dependencies: - detect-libc: 2.0.2 - expand-template: 2.0.3 - github-from-package: 0.0.0 - minimist: 1.2.8 - mkdirp-classic: 0.5.3 - napi-build-utils: 1.0.2 - node-abi: 3.31.0 - pump: 3.0.0 - rc: 1.2.8 - simple-get: 4.0.1 - tar-fs: 2.1.1 - tunnel-agent: 0.6.0 - /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} dev: true - /prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - dev: true - /prettier@3.1.0: resolution: {integrity: sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==} engines: {node: '>=14'} hasBin: true dev: true - /prettier@3.1.1: - resolution: {integrity: sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==} + /prettier@3.2.5: + resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} engines: {node: '>=14'} hasBin: true dev: true @@ -16634,16 +16810,6 @@ packages: react-is: 17.0.2 dev: true - /pretty-format@28.1.3: - resolution: {integrity: sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/schemas': 28.1.3 - ansi-regex: 5.0.1 - ansi-styles: 5.2.0 - react-is: 18.2.0 - dev: true - /pretty-format@29.7.0: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -16680,6 +16846,13 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dev: false + /process-exists@5.0.0: + resolution: {integrity: sha512-6QPRh5fyHD8MaXr4GYML8K/YY0Sq5dKHGIOrAKS3cYpHQdmygFCcijIu1dVoNKAZ0TWAMoeh8KDK9dF8auBkJA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + ps-list: 8.1.1 + dev: true + /process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -16687,15 +16860,14 @@ packages: resolution: {integrity: sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg==} dev: false + /process-warning@3.0.0: + resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} + dev: false + /process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} - /progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - dev: true - /promise-limit@2.7.0: resolution: {integrity: sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==} dev: false @@ -16751,6 +16923,11 @@ packages: /proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + /ps-list@8.1.1: + resolution: {integrity: sha512-OPS9kEJYVmiO48u/B9qneqhkMvgCxT+Tm28VCEJpheTpl8cJ0ffZRRNgS5mrQRTrX5yRTpaJ+hRDeefXYmmorQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /ps-tree@1.2.0: resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} engines: {node: '>= 0.10'} @@ -16874,26 +17051,6 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - /puppeteer-core@2.1.1: - resolution: {integrity: sha512-n13AWriBMPYxnpbb6bnaY5YoY6rGj8vPLrz6CZF3o0qJNEwlcfJVxBzYZ0NJsQ21UbdJoijPCDrM++SUVEz7+w==} - engines: {node: '>=8.16.0'} - dependencies: - '@types/mime-types': 2.1.4 - debug: 4.3.4(supports-color@8.1.1) - extract-zip: 1.7.0 - https-proxy-agent: 4.0.0 - mime: 2.6.0 - mime-types: 2.1.35 - progress: 2.0.3 - proxy-from-env: 1.1.0 - rimraf: 2.7.1 - ws: 6.2.2 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: true - /pure-rand@6.0.4: resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==} dev: true @@ -16961,6 +17118,7 @@ packages: /queue-tick@1.0.1: resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + dev: false /quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} @@ -17011,16 +17169,6 @@ packages: http-errors: 2.0.0 iconv-lite: 0.4.24 unpipe: 1.0.0 - dev: false - - /rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true - dependencies: - deep-extend: 0.6.0 - ini: 1.3.8 - minimist: 1.2.8 - strip-json-comments: 2.0.1 /rdf-canonize@3.4.0: resolution: {integrity: sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA==} @@ -17063,8 +17211,8 @@ packages: engines: {node: '>=16.14.0'} dependencies: '@babel/core': 7.23.3 - '@babel/traverse': 7.23.4 - '@babel/types': 7.23.4 + '@babel/traverse': 7.24.0 + '@babel/types': 7.24.0 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.4 '@types/doctrine': 0.0.9 @@ -17114,60 +17262,6 @@ packages: /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} - /react-refresh@0.14.0: - resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} - engines: {node: '>=0.10.0'} - dev: true - - /react-remove-scroll-bar@2.3.4(react@18.2.0): - resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - react: 18.2.0 - react-style-singleton: 2.2.1(react@18.2.0) - tslib: 2.6.2 - dev: true - - /react-remove-scroll@2.5.5(react@18.2.0): - resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - react: 18.2.0 - react-remove-scroll-bar: 2.3.4(react@18.2.0) - react-style-singleton: 2.2.1(react@18.2.0) - tslib: 2.6.2 - use-callback-ref: 1.3.0(react@18.2.0) - use-sidecar: 1.1.2(react@18.2.0) - dev: true - - /react-style-singleton@2.2.1(react@18.2.0): - resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - get-nonce: 1.0.1 - invariant: 2.2.4 - react: 18.2.0 - tslib: 2.6.2 - dev: true - /react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} @@ -17236,7 +17330,7 @@ packages: resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==} engines: {node: '>=8'} dependencies: - readable-stream: 3.6.0 + readable-stream: 3.6.2 dev: false /readdir-glob@1.1.2: @@ -17256,33 +17350,11 @@ packages: engines: {node: '>= 12.13.0'} dev: false - /recast@0.22.0: - resolution: {integrity: sha512-5AAx+mujtXijsEavc5lWXBPQqrM4+Dl5qNH96N2aNeuJFUzpiiToKPsxQD/zAIJHspz7zz0maX0PCtCTFVlixQ==} - engines: {node: '>= 4'} - dependencies: - assert: 2.0.0 - ast-types: 0.15.2 - esprima: 4.0.1 - source-map: 0.6.1 - tslib: 2.6.2 - dev: true - - /recast@0.23.1: - resolution: {integrity: sha512-RokaBcoxSjXUDzz1TXSZmZsSW6ZpLmlA3GGqJ8uuTrQ9hZhEz+4Tpsc+gRvYRJ2BU4H+ZyUlg91eSGDw7bwy7g==} - engines: {node: '>= 4'} - dependencies: - assert: 2.0.0 - ast-types: 0.16.1 - esprima: 4.0.1 - source-map: 0.6.1 - tslib: 2.6.2 - dev: true - /recast@0.23.4: resolution: {integrity: sha512-qtEDqIZGVcSZCHniWwZWbRy79Dc6Wp3kT/UmDA2RJKBPg7+7k51aQBZirHmUGn5uvHf2rg8DkjizrN26k61ATw==} engines: {node: '>= 4'} dependencies: - assert: 2.0.0 + assert: 2.1.0 ast-types: 0.16.1 esprima: 4.0.1 source-map: 0.6.1 @@ -17324,9 +17396,8 @@ packages: redis-errors: 1.2.0 dev: false - /reflect-metadata@0.1.14: - resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==} - dev: false + /reflect-metadata@0.2.1: + resolution: {integrity: sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==} /regenerate-unicode-properties@10.1.0: resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==} @@ -17341,6 +17412,7 @@ packages: /regenerator-runtime@0.13.11: resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + dev: false /regenerator-runtime@0.14.0: resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} @@ -17348,16 +17420,7 @@ packages: /regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} dependencies: - '@babel/runtime': 7.23.2 - dev: true - - /regexp.prototype.flags@1.4.3: - resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - functions-have-names: 1.2.3 + '@babel/runtime': 7.23.4 dev: true /regexp.prototype.flags@1.5.0: @@ -17388,22 +17451,57 @@ packages: jsesc: 0.5.0 dev: true - /remark-external-links@8.0.0: - resolution: {integrity: sha512-5vPSX0kHoSsqtdftSHhIYofVINC8qmp0nctkeU9YoJwV3YfiBRiI6cbFRJ0oI/1F9xS+bopXG0m2KS8VFscuKA==} + /rehype-external-links@3.0.0: + resolution: {integrity: sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw==} dependencies: - extend: 3.0.2 - is-absolute-url: 3.0.3 - mdast-util-definitions: 4.0.0 - space-separated-tokens: 1.1.5 - unist-util-visit: 2.0.3 + '@types/hast': 3.0.4 + '@ungap/structured-clone': 1.2.0 + hast-util-is-element: 3.0.0 + is-absolute-url: 4.0.1 + space-separated-tokens: 2.0.2 + unist-util-visit: 5.0.0 + dev: true + + /rehype-slug@6.0.0: + resolution: {integrity: sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==} + dependencies: + '@types/hast': 3.0.4 + github-slugger: 2.0.0 + hast-util-heading-rank: 3.0.0 + hast-util-to-string: 3.0.0 + unist-util-visit: 5.0.0 + dev: true + + /remark-gfm@4.0.0: + resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-gfm: 3.0.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.4 + transitivePeerDependencies: + - supports-color dev: true - /remark-slug@6.1.0: - resolution: {integrity: sha512-oGCxDF9deA8phWvxFuyr3oSJsdyUAxMFbA0mZ7Y1Sas+emILtO+e5WutF9564gDsEN4IXaQXm5pFo6MLH+YmwQ==} + /remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} dependencies: - github-slugger: 1.5.0 - mdast-util-to-string: 1.1.0 - unist-util-visit: 2.0.3 + '@types/mdast': 4.0.3 + mdast-util-from-markdown: 2.0.0 + micromark-util-types: 2.0.0 + unified: 11.0.4 + transitivePeerDependencies: + - supports-color + dev: true + + /remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-to-markdown: 2.1.0 + unified: 11.0.4 dev: true /rename@1.0.4: @@ -17532,45 +17630,40 @@ packages: glob: 7.2.3 dev: true - /rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - hasBin: true - dependencies: - glob: 7.2.3 - dev: true - /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true dependencies: glob: 7.2.3 + dev: true - /rollup@3.29.4: - resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} - engines: {node: '>=14.18.0', npm: '>=8.0.0'} + /rimraf@5.0.5: + resolution: {integrity: sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==} + engines: {node: '>=14'} hasBin: true - optionalDependencies: - fsevents: 2.3.3 - dev: true + dependencies: + glob: 10.3.10 - /rollup@4.9.1: - resolution: {integrity: sha512-pgPO9DWzLoW/vIhlSoDByCzcpX92bKEorbgXuZrqxByte3JFk2xSW2JEeAcyLc9Ru9pqcNNW+Ob7ntsk2oT/Xw==} + /rollup@4.12.0: + resolution: {integrity: sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + dependencies: + '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.9.1 - '@rollup/rollup-android-arm64': 4.9.1 - '@rollup/rollup-darwin-arm64': 4.9.1 - '@rollup/rollup-darwin-x64': 4.9.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.9.1 - '@rollup/rollup-linux-arm64-gnu': 4.9.1 - '@rollup/rollup-linux-arm64-musl': 4.9.1 - '@rollup/rollup-linux-riscv64-gnu': 4.9.1 - '@rollup/rollup-linux-x64-gnu': 4.9.1 - '@rollup/rollup-linux-x64-musl': 4.9.1 - '@rollup/rollup-win32-arm64-msvc': 4.9.1 - '@rollup/rollup-win32-ia32-msvc': 4.9.1 - '@rollup/rollup-win32-x64-msvc': 4.9.1 + '@rollup/rollup-android-arm-eabi': 4.12.0 + '@rollup/rollup-android-arm64': 4.12.0 + '@rollup/rollup-darwin-arm64': 4.12.0 + '@rollup/rollup-darwin-x64': 4.12.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.12.0 + '@rollup/rollup-linux-arm64-gnu': 4.12.0 + '@rollup/rollup-linux-arm64-musl': 4.12.0 + '@rollup/rollup-linux-riscv64-gnu': 4.12.0 + '@rollup/rollup-linux-x64-gnu': 4.12.0 + '@rollup/rollup-linux-x64-musl': 4.12.0 + '@rollup/rollup-win32-arm64-msvc': 4.12.0 + '@rollup/rollup-win32-ia32-msvc': 4.12.0 + '@rollup/rollup-win32-x64-msvc': 4.12.0 fsevents: 2.3.3 /rrweb-cssom@0.6.0: @@ -17637,19 +17730,19 @@ packages: /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - /sanitize-html@2.11.0: - resolution: {integrity: sha512-BG68EDHRaGKqlsNjJ2xUB7gpInPA8gVx/mvjO743hZaeMCZ2DwzW7xvsqZ+KNU4QKwj86HJ3uu2liISf2qBBUA==} + /sanitize-html@2.12.1: + resolution: {integrity: sha512-Plh+JAn0UVDpBRP/xEjsk+xDCoOvMBwQUf/K+/cBAVuTbtX8bj2VB7S1sL1dssVpykqp0/KPSesHrqXtokVBpA==} dependencies: deepmerge: 4.2.2 escape-string-regexp: 4.0.0 htmlparser2: 8.0.1 is-plain-object: 5.0.0 parse-srcset: 1.0.2 - postcss: 8.4.31 + postcss: 8.4.35 dev: false - /sass@1.69.5: - resolution: {integrity: sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==} + /sass@1.71.1: + resolution: {integrity: sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==} engines: {node: '>=14.0.0'} hasBin: true dependencies: @@ -17710,6 +17803,14 @@ packages: dependencies: lru-cache: 6.0.0 + /semver@7.6.0: + resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + /send@0.18.0: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} engines: {node: '>= 0.8.0'} @@ -17747,6 +17848,7 @@ packages: /set-cookie-parser@2.6.0: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + dev: false /setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} @@ -17770,35 +17872,36 @@ packages: kind-of: 6.0.3 dev: true - /sharp@0.31.3: - resolution: {integrity: sha512-XcR4+FCLBFKw1bdB+GEhnUNXNXvnt0tDo4WsBsraKymuo/IAuPuCBVAL2wIkUw2r/dwFW5Q5+g66Kwl2dgDFVg==} - engines: {node: '>=14.15.0'} + /sharp@0.33.2: + resolution: {integrity: sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==} + engines: {libvips: '>=8.15.1', node: ^18.17.0 || ^20.3.0 || >=21.0.0} requiresBuild: true dependencies: color: 4.2.3 detect-libc: 2.0.2 - node-addon-api: 5.1.0 - prebuild-install: 7.1.1 semver: 7.5.4 - simple-get: 4.0.1 - tar-fs: 2.1.1 - tunnel-agent: 0.6.0 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.2 + '@img/sharp-darwin-x64': 0.33.2 + '@img/sharp-libvips-darwin-arm64': 1.0.1 + '@img/sharp-libvips-darwin-x64': 1.0.1 + '@img/sharp-libvips-linux-arm': 1.0.1 + '@img/sharp-libvips-linux-arm64': 1.0.1 + '@img/sharp-libvips-linux-s390x': 1.0.1 + '@img/sharp-libvips-linux-x64': 1.0.1 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.1 + '@img/sharp-libvips-linuxmusl-x64': 1.0.1 + '@img/sharp-linux-arm': 0.33.2 + '@img/sharp-linux-arm64': 0.33.2 + '@img/sharp-linux-s390x': 0.33.2 + '@img/sharp-linux-x64': 0.33.2 + '@img/sharp-linuxmusl-arm64': 0.33.2 + '@img/sharp-linuxmusl-x64': 0.33.2 + '@img/sharp-wasm32': 0.33.2 + '@img/sharp-win32-ia32': 0.33.2 + '@img/sharp-win32-x64': 0.33.2 dev: false - /sharp@0.32.6: - resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==} - engines: {node: '>=14.15.0'} - requiresBuild: true - dependencies: - color: 4.2.3 - detect-libc: 2.0.2 - node-addon-api: 6.1.0 - prebuild-install: 7.1.1 - semver: 7.5.4 - simple-get: 4.0.1 - tar-fs: 3.0.4 - tunnel-agent: 0.6.0 - /shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -17828,6 +17931,13 @@ packages: jsonc-parser: 3.2.0 vscode-oniguruma: 1.7.0 vscode-textmate: 8.0.0 + dev: true + + /shiki@1.1.7: + resolution: {integrity: sha512-9kUTMjZtcPH3i7vHunA6EraTPpPOITYTdA5uMrvsJRexktqP0s7P3s9HVK80b4pP42FRVe03D7fT3NmJv2yYhw==} + dependencies: + '@shikijs/core': 1.1.7 + dev: false /side-channel@1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} @@ -17847,16 +17957,6 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - /simple-concat@1.0.1: - resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} - - /simple-get@4.0.1: - resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} - dependencies: - decompress-response: 6.0.0 - once: 1.4.0 - simple-concat: 1.0.1 - /simple-oauth2@5.0.0: resolution: {integrity: sha512-8291lo/z5ZdpmiOFzOs1kF3cxn22bMj5FFH+DNUppLJrpoIlM1QnFiE7KpshHu3J3i21TVcx4yW+gXYjdCKDLQ==} dependencies: @@ -17872,6 +17972,7 @@ packages: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} dependencies: is-arrayish: 0.3.2 + dev: false /simple-update-notifier@2.0.0: resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} @@ -17880,12 +17981,12 @@ packages: semver: 7.5.4 dev: true - /sinon@14.0.2: - resolution: {integrity: sha512-PDpV0ZI3ZCS3pEqx0vpNp6kzPhHrLx72wA0G+ZLaaJjLIYeE0n8INlgaohKuGy7hP0as5tbUd23QWu5U233t+w==} + /sinon@16.1.3: + resolution: {integrity: sha512-mjnWWeyxcAf9nC0bXcPmiDut+oE8HYridTNzBbF98AYVLmWwGRp2ISEpyhYflG1ifILT+eNn3BmKUJPxjXUPlA==} dependencies: - '@sinonjs/commons': 2.0.0 - '@sinonjs/fake-timers': 9.1.2 - '@sinonjs/samsam': 7.0.1 + '@sinonjs/commons': 3.0.0 + '@sinonjs/fake-timers': 10.3.0 + '@sinonjs/samsam': 8.0.0 diff: 5.1.0 nise: 5.1.4 supports-color: 7.2.0 @@ -18126,8 +18227,8 @@ packages: engines: {node: '>= 8'} dev: false - /space-separated-tokens@1.1.5: - resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} + /space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} dev: true /spdx-correct@3.1.1: @@ -18228,8 +18329,8 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} - /std-env@3.3.3: - resolution: {integrity: sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==} + /std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} dev: true /stop-iteration-iterator@1.0.0: @@ -18243,14 +18344,17 @@ packages: resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==} dev: true - /storybook@7.6.5: - resolution: {integrity: sha512-uHPrL+g/0v6iIVtDA8J0uWd3jDZcdr51lCR/vPXTkrCY1uVaFjswzl8EMy5PR05I7jMpKUzkJWZtFbgbh9e1Bw==} + /storybook@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-8d9gpKPDY9Ix64f0560rXIifmnuoswDdvSdTz4NXHGvPt7WrKNmaDTvWGyt1/fbTbv2dvvVp7bsWPgq1KGbrcg==} hasBin: true dependencies: - '@storybook/cli': 7.6.5 + '@storybook/cli': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) transitivePeerDependencies: + - '@babel/preset-env' - bufferutil - encoding + - react + - react-dom - supports-color - utf-8-validate dev: true @@ -18288,26 +18392,20 @@ packages: /streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} - dev: false /streamx@2.15.0: resolution: {integrity: sha512-HcxY6ncGjjklGs1xsP1aR71INYcsXFJet5CU1CHqihQ2J5nOsbd4OjgjHO42w/4QNv9gZb3BueV+Vxok5pLEXg==} dependencies: fast-fifo: 1.3.0 queue-tick: 1.0.1 + dev: false /strict-event-emitter-types@2.0.0: resolution: {integrity: sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==} dev: false - /strict-event-emitter@0.2.8: - resolution: {integrity: sha512-KDf/ujU8Zud3YaLtMCcTI4xkZlZVIYxTLr+XIULexP+77EEVWixeXroLUXQXiVtH4XH2W7jr/3PT1v3zBuvc3A==} - dependencies: - events: 3.3.0 - dev: true - - /strict-event-emitter@0.4.6: - resolution: {integrity: sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==} + /strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} dev: true /string-argv@0.3.1: @@ -18428,19 +18526,15 @@ packages: min-indent: 1.0.1 dev: true - /strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} dev: true - /strip-literal@1.0.1: - resolution: {integrity: sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==} + /strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} dependencies: - acorn: 8.11.2 + acorn: 8.11.3 dev: true /strip-outer@2.0.0: @@ -18460,15 +18554,15 @@ packages: peek-readable: 5.0.0 dev: false - /stylehacks@6.0.1(postcss@8.4.32): - resolution: {integrity: sha512-jTqG2aIoX2fYg0YsGvqE4ooE/e75WmaEjnNiP6Ag7irLtHxML8NJRxRxS0HyDpde8DRGuEXTFVHVfR5Tmbxqzg==} + /stylehacks@6.0.3(postcss@8.4.35): + resolution: {integrity: sha512-KzBqjnqktc8/I0ERCb+lGq06giF/JxDbw2r9kEVhen9noHeIDRtMWUp9r62sOk+/2bbX6sFG1GhsS7ToXG0PEg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 dependencies: - browserslist: 4.22.1 - postcss: 8.4.32 - postcss-selector-parser: 6.0.13 + browserslist: 4.23.0 + postcss: 8.4.35 + postcss-selector-parser: 6.0.15 dev: false /supports-color@5.5.0: @@ -18506,8 +18600,8 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - /svgo@3.1.0: - resolution: {integrity: sha512-R5SnNA89w1dYgNv570591F66v34b3eQShpIBcQtZtM5trJwm1VvxbIoMpRYY3ybTAutcKTLEmTsdnaknOHbiQA==} + /svgo@3.2.0: + resolution: {integrity: sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==} engines: {node: '>=14.0.0'} hasBin: true dependencies: @@ -18524,12 +18618,8 @@ packages: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} dev: false - /synchronous-promise@2.0.17: - resolution: {integrity: sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g==} - dev: true - - /systeminformation@5.21.20: - resolution: {integrity: sha512-AyS1fNc+MDoAJtFknFbbo587H8h6yejJwM+H9rVusnOToIEkiMehMyD5JM7o3j55Cto20MawIZrcgNMgd4BfOQ==} + /systeminformation@5.22.0: + resolution: {integrity: sha512-oAP80ymt8ssrAzjX8k3frbL7ys6AotqC35oikG6/SG15wBw+tG9nCk4oPaXIhEaAOAZ8XngxUv3ORq2IuR3r4Q==} engines: {node: '>=8.0.0'} os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] hasBin: true @@ -18542,13 +18632,7 @@ packages: mkdirp-classic: 0.5.3 pump: 3.0.0 tar-stream: 2.2.0 - - /tar-fs@3.0.4: - resolution: {integrity: sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==} - dependencies: - mkdirp-classic: 0.5.3 - pump: 3.0.0 - tar-stream: 3.1.6 + dev: true /tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} @@ -18559,6 +18643,7 @@ packages: fs-constants: 1.0.0 inherits: 2.0.4 readable-stream: 3.6.2 + dev: true /tar-stream@3.1.6: resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==} @@ -18566,18 +18651,26 @@ packages: b4a: 1.6.4 fast-fifo: 1.3.0 streamx: 2.15.0 + dev: false - /tar@6.1.13: - resolution: {integrity: sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==} + /tar@6.2.0: + resolution: {integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==} engines: {node: '>=10'} dependencies: chownr: 2.0.0 fs-minipass: 2.1.0 - minipass: 4.2.5 + minipass: 5.0.0 minizlib: 2.1.2 mkdirp: 1.0.4 yallist: 4.0.0 + /taskkill@5.0.0: + resolution: {integrity: sha512-+HRtZ40Vc+6YfCDWCeAsixwxJgMbPY4HHuTgzPYH3JXvqHWUlsCfy+ylXlAKhFNcuLp4xVeWeFBUhDk+7KYUvQ==} + engines: {node: '>=14.16'} + dependencies: + execa: 6.1.0 + dev: true + /telejson@7.2.0: resolution: {integrity: sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==} dependencies: @@ -18607,13 +18700,13 @@ packages: unique-string: 2.0.0 dev: true - /terser@5.26.0: - resolution: {integrity: sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==} + /terser@5.28.1: + resolution: {integrity: sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==} engines: {node: '>=10'} hasBin: true dependencies: '@jridgewell/source-map': 0.3.5 - acorn: 8.11.2 + acorn: 8.11.3 commander: 2.20.3 source-map-support: 0.5.21 @@ -18657,8 +18750,8 @@ packages: real-require: 0.2.0 dev: false - /three@0.159.0: - resolution: {integrity: sha512-eCmhlLGbBgucuo4VEA9IO3Qpc7dh8Bd4VKzr7WfW4+8hMcIfoAVi1ev0pJYN9PTTsCslbcKgBwr2wNZ1EvLInA==} + /three@0.162.0: + resolution: {integrity: sha512-xfCYj4RnlozReCmUd+XQzj6/5OjDNHBy5nT6rVwrOKGENAvpXe2z1jL+DZYaMu4/9pNsjH/4Os/VvS9IrH7IOQ==} dev: false /throttle-debounce@5.0.0: @@ -18690,8 +18783,8 @@ packages: engines: {node: '>=6'} dev: false - /tinybench@2.5.0: - resolution: {integrity: sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==} + /tinybench@2.6.0: + resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==} dev: true /tinycolor2@1.6.0: @@ -18703,8 +18796,8 @@ packages: engines: {node: '>=14.0.0'} dev: true - /tinyspy@2.1.1: - resolution: {integrity: sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==} + /tinyspy@2.2.0: + resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==} engines: {node: '>=14.0.0'} dev: true @@ -18715,11 +18808,11 @@ packages: os-tmpdir: 1.0.2 dev: true - /tmp@0.2.1: - resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==} - engines: {node: '>=8.17.0'} + /tmp@0.2.2: + resolution: {integrity: sha512-ETcvHhaIc9J2MDEAH6N67j9bvBvu/3Gb764qaGhwtFvjtvhegqoqSpofgeyq1Sc24mW5pdyUDs9HP5j3ehkxRw==} + engines: {node: '>=14'} dependencies: - rimraf: 3.0.2 + rimraf: 5.0.5 /tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} @@ -18805,6 +18898,10 @@ packages: escape-string-regexp: 5.0.0 dev: false + /trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + dev: true + /ts-api-utils@1.0.3(typescript@5.1.6): resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} engines: {node: '>=16.13.0'} @@ -18833,7 +18930,7 @@ packages: engines: {node: '>=6.10'} dev: true - /ts-jest@29.1.1(@babel/core@7.23.3)(jest@29.7.0)(typescript@5.1.6): + /ts-jest@29.1.1(@babel/core@7.24.0)(jest@29.7.0)(typescript@5.1.6): resolution: {integrity: sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -18854,10 +18951,10 @@ packages: esbuild: optional: true dependencies: - '@babel/core': 7.23.3 + '@babel/core': 7.24.0 bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.10.5) + jest: 29.7.0(@types/node@20.11.22) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -18901,8 +18998,8 @@ packages: strip-bom: 3.0.0 dev: false - /tsd@0.30.0: - resolution: {integrity: sha512-aHL4rEuf3wwRzKCH8yqsE1oMAJYn7SAQ2JfWSgjr1e5/fqr+ggohQazECMpSoRAqSQeM/iIFugoyL/0eFwdTcA==} + /tsd@0.30.7: + resolution: {integrity: sha512-oTiJ28D6B/KXoU3ww/Eji+xqHJojiuPVMwA12g4KYX1O72N93Nb6P3P3h2OAhhf92Xl8NIhb/xFmBZd5zw/xUw==} engines: {node: '>=14.16'} hasBin: true dependencies: @@ -18921,13 +19018,6 @@ packages: /tslib@2.5.0: resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} - /tslib@2.5.3: - resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==} - - /tslib@2.6.1: - resolution: {integrity: sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==} - dev: false - /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} @@ -18946,6 +19036,7 @@ packages: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} dependencies: safe-buffer: 5.2.1 + dev: true /tweetnacl@0.14.5: resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} @@ -18996,6 +19087,11 @@ packages: engines: {node: '>=12.20'} dev: true + /type-fest@4.9.0: + resolution: {integrity: sha512-KS/6lh/ynPGiHD/LnAobrEFq3Ad4pBzOlJ1wAnJx9N4EYoqFhMfLIBjUT2UEx4wg5ZE+cC1ob6DCSpppVo+rtg==} + engines: {node: '>=16'} + dev: true + /type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} @@ -19058,20 +19154,20 @@ packages: typescript: 5.1.6 dev: true - /typeorm@0.3.17(ioredis@5.3.2)(pg@8.11.3): - resolution: {integrity: sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig==} - engines: {node: '>= 12.9.0'} + /typeorm@0.3.20(ioredis@5.3.2)(pg@8.11.3): + resolution: {integrity: sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==} + engines: {node: '>=16.13.0'} hasBin: true peerDependencies: '@google-cloud/spanner': ^5.18.0 '@sap/hana-client': ^2.12.25 - better-sqlite3: ^7.1.2 || ^8.0.0 + better-sqlite3: ^7.1.2 || ^8.0.0 || ^9.0.0 hdb-pool: ^0.1.6 ioredis: ^5.0.4 - mongodb: ^5.2.0 - mssql: ^9.1.1 + mongodb: ^5.8.0 + mssql: ^9.1.1 || ^10.0.1 mysql2: ^2.2.5 || ^3.0.1 - oracledb: ^5.1.0 + oracledb: ^6.3.0 pg: ^8.5.1 pg-native: ^3.0.0 pg-query-stream: ^4.0.0 @@ -19121,28 +19217,22 @@ packages: buffer: 6.0.3 chalk: 4.1.2 cli-highlight: 2.1.11 - date-fns: 2.30.0 + dayjs: 1.11.10 debug: 4.3.4(supports-color@8.1.1) dotenv: 16.0.3 - glob: 8.1.0 + glob: 10.3.10 ioredis: 5.3.2 mkdirp: 2.1.6 pg: 8.11.3 - reflect-metadata: 0.1.14 + reflect-metadata: 0.2.1 sha.js: 2.4.11 - tslib: 2.5.3 + tslib: 2.6.2 uuid: 9.0.1 - yargs: 17.6.2 + yargs: 17.7.2 transitivePeerDependencies: - supports-color dev: false - /typescript@5.0.4: - resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} - engines: {node: '>=12.20'} - hasBin: true - dev: true - /typescript@5.1.6: resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} engines: {node: '>=14.17'} @@ -19153,8 +19243,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - /ufo@1.1.2: - resolution: {integrity: sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==} + /ufo@1.3.2: + resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==} dev: true /uglify-js@3.17.4: @@ -19174,7 +19264,6 @@ packages: engines: {node: '>=8'} dependencies: '@lukeed/csprng': 1.0.1 - dev: false /ulid@2.3.0: resolution: {integrity: sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==} @@ -19234,6 +19323,18 @@ packages: engines: {node: '>=4'} dev: true + /unified@11.0.4: + resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==} + dependencies: + '@types/unist': 3.0.2 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.1 + dev: true + /unique-filename@3.0.0: resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -19255,23 +19356,31 @@ packages: crypto-random-string: 2.0.0 dev: true - /unist-util-is@4.1.0: - resolution: {integrity: sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==} + /unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + dependencies: + '@types/unist': 3.0.2 dev: true - /unist-util-visit-parents@3.1.1: - resolution: {integrity: sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==} + /unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} dependencies: - '@types/unist': 2.0.6 - unist-util-is: 4.1.0 + '@types/unist': 3.0.2 dev: true - /unist-util-visit@2.0.3: - resolution: {integrity: sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==} + /unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} dependencies: - '@types/unist': 2.0.6 - unist-util-is: 4.1.0 - unist-util-visit-parents: 3.1.1 + '@types/unist': 3.0.2 + unist-util-is: 6.0.0 + dev: true + + /unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + dependencies: + '@types/unist': 3.0.2 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 dev: true /universalify@0.1.2: @@ -19298,7 +19407,7 @@ packages: /unplugin@1.5.1: resolution: {integrity: sha512-0QkvG13z6RD+1L1FoibQqnvTwVBXvS4XSPwAyinVgoOCl2jAgwzdUKmEj05o4Lt8xwQI85Hb6mSyYkcAGwZPew==} dependencies: - acorn: 8.11.2 + acorn: 8.11.3 chokidar: 3.5.3 webpack-sources: 3.2.3 webpack-virtual-modules: 0.6.1 @@ -19318,6 +19427,7 @@ packages: browserslist: 4.22.1 escalade: 3.1.1 picocolors: 1.0.0 + dev: true /update-browserslist-db@1.0.13(browserslist@4.22.2): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} @@ -19330,6 +19440,16 @@ packages: picocolors: 1.0.0 dev: true + /update-browserslist-db@1.0.13(browserslist@4.23.0): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.23.0 + escalade: 3.1.1 + picocolors: 1.0.0 + /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: @@ -19341,50 +19461,6 @@ packages: querystringify: 2.2.0 requires-port: 1.0.0 - /urlsafe-base64@1.0.0: - resolution: {integrity: sha512-RtuPeMy7c1UrHwproMZN9gN6kiZ0SvJwRaEzwZY0j9MypEkFqyBaKv176jvlPtg58Zh36bOkS0NFABXMHvvGCA==} - dev: false - - /use-callback-ref@1.3.0(react@18.2.0): - resolution: {integrity: sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - react: 18.2.0 - tslib: 2.6.2 - dev: true - - /use-resize-observer@9.1.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==} - peerDependencies: - react: 16.8.0 - 18 - react-dom: 16.8.0 - 18 - dependencies: - '@juggle/resize-observer': 3.4.0 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - - /use-sidecar@1.1.2(react@18.2.0): - resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - detect-node-es: 1.1.0 - react: 18.2.0 - tslib: 2.6.2 - dev: true - /utf-8-validate@6.0.3: resolution: {integrity: sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==} engines: {node: '>=6.14.2'} @@ -19417,8 +19493,8 @@ packages: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true - /v-code-diff@1.7.2(vue@3.3.12): - resolution: {integrity: sha512-y+q8ZHf8GfphYLhcZbjAKcId/h6vZujS71Ryq5u+dI6Jg4ZLTdLrBNVSzYpHywHSSFFfBMdilm6XvVryEaH4+A==} + /v-code-diff@1.9.0(vue@3.4.21): + resolution: {integrity: sha512-alg6krCxFvwTob/rJq+3LzjdIbLb/ni8tS8YmBbI0wckOkbJuN1cShFJ6XEkm82tMgpv5NYEeWLEWhggeV7BDg==} requiresBuild: true peerDependencies: '@vue/composition-api': ^1.4.9 @@ -19429,20 +19505,11 @@ packages: dependencies: diff: 5.1.0 diff-match-patch: 1.0.5 - highlight.js: 11.8.0 - vue: 3.3.12(typescript@5.3.3) - vue-demi: 0.13.11(vue@3.3.12) + highlight.js: 11.9.0 + vue: 3.4.21(typescript@5.3.3) + vue-demi: 0.14.7(vue@3.4.21) dev: false - /v8-to-istanbul@9.1.0: - resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} - engines: {node: '>=10.12.0'} - dependencies: - '@jridgewell/trace-mapping': 0.3.18 - '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 1.9.0 - dev: true - /v8-to-istanbul@9.2.0: resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==} engines: {node: '>=10.12.0'} @@ -19476,17 +19543,32 @@ packages: core-util-is: 1.0.2 extsprintf: 1.3.0 - /vite-node@0.34.6(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0): + /vfile-message@4.0.2: + resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + dependencies: + '@types/unist': 3.0.2 + unist-util-stringify-position: 4.0.0 + dev: true + + /vfile@6.0.1: + resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==} + dependencies: + '@types/unist': 3.0.2 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.2 + dev: true + + /vite-node@0.34.6(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1): resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} engines: {node: '>=v14.18.0'} hasBin: true dependencies: cac: 6.7.14 debug: 4.3.4(supports-color@8.1.1) - mlly: 1.4.0 - pathe: 1.1.1 + mlly: 1.5.0 + pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0) + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) transitivePeerDependencies: - '@types/node' - less @@ -19502,8 +19584,8 @@ packages: resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==} dev: true - /vite@5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0): - resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} + /vite@5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1): + resolution: {integrity: sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -19530,12 +19612,12 @@ packages: terser: optional: true dependencies: - '@types/node': 20.10.5 - esbuild: 0.19.9 - postcss: 8.4.32 - rollup: 4.9.1 - sass: 1.69.5 - terser: 5.26.0 + '@types/node': 20.11.22 + esbuild: 0.19.11 + postcss: 8.4.35 + rollup: 4.12.0 + sass: 1.71.1 + terser: 5.28.1 optionalDependencies: fsevents: 2.3.3 @@ -19545,13 +19627,13 @@ packages: peerDependencies: vitest: '>=0.16.0' dependencies: - cross-fetch: 3.1.5 - vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.5)(terser@5.26.0) + cross-fetch: 3.1.6 + vitest: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1) transitivePeerDependencies: - encoding dev: true - /vitest@0.34.6(happy-dom@10.0.3)(sass@1.69.5)(terser@5.26.0): + /vitest@0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1): resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} engines: {node: '>=v14.18.0'} hasBin: true @@ -19582,30 +19664,30 @@ packages: webdriverio: optional: true dependencies: - '@types/chai': 4.3.5 - '@types/chai-subset': 1.3.3 - '@types/node': 20.10.5 + '@types/chai': 4.3.11 + '@types/chai-subset': 1.3.5 + '@types/node': 20.11.22 '@vitest/expect': 0.34.6 '@vitest/runner': 0.34.6 '@vitest/snapshot': 0.34.6 '@vitest/spy': 0.34.6 '@vitest/utils': 0.34.6 - acorn: 8.11.2 - acorn-walk: 8.2.0 + acorn: 8.11.3 + acorn-walk: 8.3.2 cac: 6.7.14 chai: 4.3.10 debug: 4.3.4(supports-color@8.1.1) - happy-dom: 10.0.3 + happy-dom: 13.6.2 local-pkg: 0.4.3 - magic-string: 0.30.3 - pathe: 1.1.1 + magic-string: 0.30.5 + pathe: 1.1.2 picocolors: 1.0.0 - std-env: 3.3.3 - strip-literal: 1.0.1 - tinybench: 2.5.0 + std-env: 3.7.0 + strip-literal: 1.3.0 + tinybench: 2.6.0 tinypool: 0.7.0 - vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0) - vite-node: 0.34.6(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0) + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) + vite-node: 0.34.6(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1) why-is-node-running: 2.2.2 transitivePeerDependencies: - less @@ -19621,11 +19703,64 @@ packages: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} + /vscode-jsonrpc@8.2.0: + resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} + engines: {node: '>=14.0.0'} + dev: false + + /vscode-languageclient@9.0.1: + resolution: {integrity: sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==} + engines: {vscode: ^1.82.0} + dependencies: + minimatch: 5.1.6 + semver: 7.5.4 + vscode-languageserver-protocol: 3.17.5 + dev: false + + /vscode-languageserver-protocol@3.17.5: + resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} + dependencies: + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.5 + dev: false + + /vscode-languageserver-textdocument@1.0.11: + resolution: {integrity: sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==} + dev: false + + /vscode-languageserver-types@3.17.5: + resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} + dev: false + + /vscode-languageserver@9.0.1: + resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} + hasBin: true + dependencies: + vscode-languageserver-protocol: 3.17.5 + dev: false + /vscode-oniguruma@1.7.0: resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} + dev: true /vscode-textmate@8.0.0: resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==} + dev: true + + /vue-component-meta@1.8.27(typescript@5.3.3): + resolution: {integrity: sha512-j3WJsyQHP4TDlvnjHc/eseo0/eVkf0FaCpkqGwez5zD+Tj31onBzWZEXTnWKs8xRj0n3dMNYdy3SpiS6NubSvg==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@volar/typescript': 1.11.1 + '@vue/language-core': 1.8.27(typescript@5.3.3) + path-browserify: 1.0.1 + typescript: 5.3.3 + vue-component-type-helpers: 1.8.27 + dev: true /vue-component-type-helpers@1.8.27: resolution: {integrity: sha512-0vOfAtI67UjeO1G6UiX5Kd76CqaQ67wrRZiOe7UAb9Jm6GzlUr/fC7CV90XfwapJRjpCMaZFhv1V0ajWRmE9Dg==} @@ -19635,8 +19770,8 @@ packages: resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==} dev: true - /vue-demi@0.13.11(vue@3.3.12): - resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==} + /vue-demi@0.14.7(vue@3.4.21): + resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} engines: {node: '>=12'} hasBin: true requiresBuild: true @@ -19647,51 +19782,52 @@ packages: '@vue/composition-api': optional: true dependencies: - vue: 3.3.12(typescript@5.3.3) + vue: 3.4.21(typescript@5.3.3) dev: false - /vue-docgen-api@4.64.1(vue@3.3.12): - resolution: {integrity: sha512-jbOf7ByE3Zvtuk+429Jorl+eIeh2aB2Fx1GUo3xJd1aByJWE8KDlSEa6b11PB1ze8f0sRUBraRDinICCk0KY7g==} + /vue-docgen-api@4.75.1(vue@3.4.21): + resolution: {integrity: sha512-MECZ3uExz+ssmhD/2XrFoQQs93y17IVO1KDYTp8nr6i9GNrk67AAto6QAtilW1H/pTDPMkQxJ7w/25ZIqVtfAA==} + peerDependencies: + vue: '>=2' dependencies: - '@babel/parser': 7.23.4 - '@babel/types': 7.23.4 - '@vue/compiler-dom': 3.3.8 - '@vue/compiler-sfc': 3.3.12 - ast-types: 0.14.2 + '@babel/parser': 7.24.0 + '@babel/types': 7.24.0 + '@vue/compiler-dom': 3.4.18 + '@vue/compiler-sfc': 3.4.21 + ast-types: 0.16.1 hash-sum: 2.0.0 lru-cache: 8.0.4 pug: 3.0.2 - recast: 0.22.0 + recast: 0.23.4 ts-map: 1.0.3 - vue-inbrowser-compiler-independent-utils: 4.64.1(vue@3.3.12) - transitivePeerDependencies: - - vue + vue: 3.4.21(typescript@5.3.3) + vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.4.21) dev: true - /vue-eslint-parser@9.3.2(eslint@8.56.0): - resolution: {integrity: sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==} + /vue-eslint-parser@9.4.2(eslint@8.57.0): + resolution: {integrity: sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: '>=6.0.0' dependencies: debug: 4.3.4(supports-color@8.1.1) - eslint: 8.56.0 + eslint: 8.57.0 eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 - esquery: 1.4.2 + esquery: 1.5.0 lodash: 4.17.21 semver: 7.5.4 transitivePeerDependencies: - supports-color dev: true - /vue-inbrowser-compiler-independent-utils@4.64.1(vue@3.3.12): - resolution: {integrity: sha512-Hn32n07XZ8j9W8+fmOXPQL+i+W2e/8i6mkH4Ju3H6nR0+cfvmWM95GhczYi5B27+Y8JlCKgAo04IUiYce4mKAw==} + /vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.4.21): + resolution: {integrity: sha512-K3wt3iVmNGaFEOUR4JIThQRWfqokxLfnPslD41FDZB2ajXp789+wCqJyGYlIFsvEQ2P61PInw6/ph5iiqg51gg==} peerDependencies: vue: '>=2' dependencies: - vue: 3.3.12(typescript@5.3.3) + vue: 3.4.21(typescript@5.3.3) dev: true /vue-template-compiler@2.7.14: @@ -19701,40 +19837,40 @@ packages: he: 1.2.0 dev: true - /vue-tsc@1.8.25(typescript@5.3.3): - resolution: {integrity: sha512-lHsRhDc/Y7LINvYhZ3pv4elflFADoEOo67vfClAfF2heVHpHmVquLSjojgCSIwzA4F0Pc4vowT/psXCYcfk+iQ==} + /vue-tsc@1.8.27(typescript@5.3.3): + resolution: {integrity: sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==} hasBin: true peerDependencies: typescript: '*' dependencies: '@volar/typescript': 1.11.1 - '@vue/language-core': 1.8.25(typescript@5.3.3) + '@vue/language-core': 1.8.27(typescript@5.3.3) semver: 7.5.4 typescript: 5.3.3 dev: true - /vue@3.3.12(typescript@5.3.3): - resolution: {integrity: sha512-jYNv2QmET2OTHsFzfWHMnqgCfqL4zfo97QwofdET+GBRCHhSCHuMTTvNIgeSn0/xF3JRT5OGah6MDwUFN7MPlg==} + /vue@3.4.21(typescript@5.3.3): + resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@vue/compiler-dom': 3.3.12 - '@vue/compiler-sfc': 3.3.12 - '@vue/runtime-dom': 3.3.12 - '@vue/server-renderer': 3.3.12(vue@3.3.12) - '@vue/shared': 3.3.12 + '@vue/compiler-dom': 3.4.21 + '@vue/compiler-sfc': 3.4.21 + '@vue/runtime-dom': 3.4.21 + '@vue/server-renderer': 3.4.21(vue@3.4.21) + '@vue/shared': 3.4.21 typescript: 5.3.3 - /vuedraggable@4.1.0(vue@3.3.12): + /vuedraggable@4.1.0(vue@3.4.21): resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==} peerDependencies: vue: ^3.0.1 dependencies: sortablejs: 1.14.0 - vue: 3.3.12(typescript@5.3.3) + vue: 3.4.21(typescript@5.3.3) dev: false /w3c-xmlserializer@5.0.0: @@ -19778,22 +19914,14 @@ packages: defaults: 1.0.4 dev: true - /web-encoding@1.1.5: - resolution: {integrity: sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==} - dependencies: - util: 0.12.5 - optionalDependencies: - '@zxing/text-encoding': 0.9.0 - dev: true - - /web-push@3.6.6: - resolution: {integrity: sha512-SyteEck9fiCskNmPxs/GFhJsZrIyLfRvjWNmcUwULLJyCU0f1oxo2sWTokXA1mDAq9vxk4e4gVcb/8agq73NkQ==} + /web-push@3.6.7: + resolution: {integrity: sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==} engines: {node: '>= 16'} hasBin: true dependencies: asn1.js: 5.4.1 - http_ece: 1.1.0 - https-proxy-agent: 7.0.0 + http_ece: 1.2.0 + https-proxy-agent: 7.0.2 jws: 4.0.0 minimist: 1.2.8 transitivePeerDependencies: @@ -19826,6 +19954,7 @@ packages: engines: {node: '>=12'} dependencies: iconv-lite: 0.6.3 + dev: false /whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} @@ -19891,18 +20020,6 @@ packages: has-tostringtag: 1.0.0 dev: true - /which-typed-array@1.1.9: - resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} - engines: {node: '>= 0.4'} - dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.0 - is-typed-array: 1.1.10 - dev: true - /which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true @@ -19934,18 +20051,12 @@ packages: stackback: 0.0.2 dev: true - /wide-align@1.1.5: - resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} - dependencies: - string-width: 4.2.3 - dev: false - /with@7.0.2: resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==} engines: {node: '>= 10.0.0'} dependencies: '@babel/parser': 7.22.16 - '@babel/types': 7.22.17 + '@babel/types': 7.24.0 assert-never: 1.2.1 babel-walk: 3.0.0-canary-5 @@ -19996,20 +20107,6 @@ packages: signal-exit: 3.0.7 dev: true - /ws@6.2.2: - resolution: {integrity: sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dependencies: - async-limiter: 1.0.1 - dev: true - /ws@8.14.2: resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==} engines: {node: '>=10.0.0'} @@ -20023,8 +20120,8 @@ packages: optional: true dev: false - /ws@8.15.1(bufferutil@4.0.7)(utf-8-validate@6.0.3): - resolution: {integrity: sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ==} + /ws@8.16.0(bufferutil@4.0.7)(utf-8-validate@6.0.3): + resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -20098,11 +20195,6 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - /yaml@2.3.1: - resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==} - engines: {node: '>= 14'} - dev: false - /yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} @@ -20160,6 +20252,19 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 + dev: true + + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 /yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} @@ -20198,30 +20303,34 @@ packages: readable-stream: 3.6.0 dev: false - github.com/aiscript-dev/aiscript-vscode/b5a8aa0ad927831a0b867d1c183460a14e6c48cd: - resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/b5a8aa0ad927831a0b867d1c183460a14e6c48cd} - name: aiscript-vscode - version: 0.0.6 - engines: {vscode: ^1.83.0} - dev: false + /zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + dev: true - github.com/misskey-dev/browser-image-resizer/0227e860621e55cbed0aabe6dc601096a7748c4a: - resolution: {tarball: https://codeload.github.com/misskey-dev/browser-image-resizer/tar.gz/0227e860621e55cbed0aabe6dc601096a7748c4a} - name: browser-image-resizer - version: 2.2.1-misskey.3 + '@github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.5/aiscript-dev-aiscript-languageserver-0.1.5.tgz': + resolution: {tarball: https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.5/aiscript-dev-aiscript-languageserver-0.1.5.tgz} + name: '@aiscript-dev/aiscript-languageserver' + version: 0.1.5 + hasBin: true + dependencies: + seedrandom: 3.0.5 + stringz: 2.1.0 + uuid: 9.0.1 + vscode-languageserver: 9.0.1 + vscode-languageserver-textdocument: 1.0.11 dev: false - github.com/misskey-dev/sharp-read-bmp/02d9dc189fa7df0c4bea09330be26741772dac01: - resolution: {tarball: https://codeload.github.com/misskey-dev/sharp-read-bmp/tar.gz/02d9dc189fa7df0c4bea09330be26741772dac01} - name: sharp-read-bmp - version: 1.0.0 + github.com/aiscript-dev/aiscript-vscode/793211d40243c8775f6b85f015c221c82cbffb07: + resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/793211d40243c8775f6b85f015c221c82cbffb07} + name: aiscript-vscode + version: 0.1.2 + engines: {vscode: ^1.83.0} dependencies: - decode-bmp: 0.2.1 - decode-ico: 0.4.1 - sharp: 0.31.3 + '@aiscript-dev/aiscript-languageserver': '@github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.5/aiscript-dev-aiscript-languageserver-0.1.5.tgz' + vscode-languageclient: 9.0.1 dev: false - github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.5)(@storybook/components@7.6.5)(@storybook/core-events@7.6.5)(@storybook/manager-api@7.6.5)(@storybook/preview-api@7.6.5)(@storybook/theming@7.6.5)(@storybook/types@7.6.5)(react-dom@18.2.0)(react@18.2.0): + github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.0-beta.6)(@storybook/components@8.0.0-beta.6)(@storybook/core-events@8.0.0-beta.6)(@storybook/manager-api@8.0.0-beta.6)(@storybook/preview-api@8.0.0-beta.6)(@storybook/theming@8.0.0-beta.6)(@storybook/types@8.0.0-beta.6)(react-dom@18.2.0)(react@18.2.0): resolution: {tarball: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640} id: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640 name: storybook-addon-misskey-theme @@ -20242,27 +20351,13 @@ packages: react-dom: optional: true dependencies: - '@storybook/blocks': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/components': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-events': 7.6.5 - '@storybook/manager-api': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.6.5 - '@storybook/theming': 7.6.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.6.5 + '@storybook/blocks': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + '@storybook/components': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 8.0.0-beta.6 + '@storybook/manager-api': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 8.0.0-beta.6 + '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 8.0.0-beta.6 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - - github.com/misskey-dev/summaly/d2a3e07205c3c9769bc5a7b42031c8884b5a25c8: - resolution: {tarball: https://codeload.github.com/misskey-dev/summaly/tar.gz/d2a3e07205c3c9769bc5a7b42031c8884b5a25c8} - name: summaly - version: 4.0.2 - dependencies: - cheerio: 1.0.0-rc.12 - escape-regexp: 0.0.1 - got: 12.6.1 - html-entities: 2.3.2 - iconv-lite: 0.6.3 - jschardet: 3.0.0 - private-ip: 2.3.3 - trace-redirect: 1.0.6 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b69ec692c85da77cdf358eae38caa9762d9390f3..6b19c64e82b9177897e9b402a101b7e1f8dccdb1 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,4 +4,6 @@ packages: - 'packages/sw' - 'packages/misskey-js' - 'packages/misskey-js/generator' - - 'packages/megalodon' \ No newline at end of file + - 'packages/megalodon' + - 'packages/misskey-reversi' + - 'packages/misskey-bubble-game' diff --git a/scripts/build-assets.mjs b/scripts/build-assets.mjs index 22f0871caaaac3cfa3780a649b5269145f831b59..7d0ab2fc805aeb489bc9fb0ffca39e645c1817cd 100644 --- a/scripts/build-assets.mjs +++ b/scripts/build-assets.mjs @@ -1,20 +1,34 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import * as fs from 'node:fs/promises'; import * as path from 'node:path'; +import { fileURLToPath } from 'node:url'; import cssnano from 'cssnano'; +import * as yaml from 'js-yaml'; import postcss from 'postcss'; import * as terser from 'terser'; import { build as buildLocales } from '../locales/index.js'; import generateDTS from '../locales/generateDTS.js'; import meta from '../package.json' assert { type: "json" }; +import buildTarball from './tarball.mjs'; + +const configDir = fileURLToPath(new URL('../.config', import.meta.url)); +const configPath = process.env.MISSKEY_CONFIG_YML + ? path.resolve(configDir, process.env.MISSKEY_CONFIG_YML) + : process.env.NODE_ENV === 'test' + ? path.resolve(configDir, 'test.yml') + : path.resolve(configDir, 'default.yml'); let locales = buildLocales(); +async function loadConfig() { + return fs.readFile(configPath, 'utf-8').then(data => yaml.load(data)).catch(() => null); +} + async function copyFrontendFonts() { await fs.cp('./packages/frontend/node_modules/three/examples/fonts', './built/_frontend_dist_/fonts', { dereference: true, recursive: true }); } @@ -35,13 +49,6 @@ async function copyFrontendLocales() { } } -async function copyFrontendShikiAssets() { - await fs.cp('./packages/frontend/node_modules/shiki/dist', './built/_frontend_dist_/shiki/dist', { dereference: true, recursive: true }); - await fs.cp('./packages/frontend/node_modules/shiki/languages', './built/_frontend_dist_/shiki/languages', { dereference: true, recursive: true }); - await fs.cp('./packages/frontend/node_modules/aiscript-vscode/aiscript/syntaxes', './built/_frontend_dist_/shiki/languages', { dereference: true, recursive: true }); - await fs.cp('./packages/frontend/node_modules/shiki/themes', './built/_frontend_dist_/shiki/themes', { dereference: true, recursive: true }); -} - async function copyBackendViews() { await fs.cp('./packages/backend/src/server/web/views', './packages/backend/built/server/web/views', { recursive: true }); } @@ -81,16 +88,16 @@ async function build() { copyFrontendFonts(), copyFrontendTablerIcons(), copyFrontendLocales(), - copyFrontendShikiAssets(), copyBackendViews(), buildBackendScript(), buildBackendStyle(), + loadConfig().then(config => config?.publishTarballInsteadOfProvideRepositoryUrl && buildTarball()), ]); } await build(); -if (process.argv.includes("--watch")) { +if (process.argv.includes('--watch')) { const watcher = fs.watch('./locales'); for await (const event of watcher) { const filename = event.filename?.replaceAll('\\', '/'); diff --git a/scripts/build-pre.js b/scripts/build-pre.js index ed75aa6553ed6e1845ab4a215dc28cec1b20c5ee..a90d53c75d03e448cd8f1ca40a3c198ecbc1c3ca 100644 --- a/scripts/build-pre.js +++ b/scripts/build-pre.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ diff --git a/scripts/clean-all.js b/scripts/clean-all.js index e4f5acae0df041d83e19dca0dba65578e26eaa6d..3df2f2ceff694efdcac29bf764c3b98b4158d769 100644 --- a/scripts/clean-all.js +++ b/scripts/clean-all.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -17,6 +17,15 @@ const fs = require('fs'); fs.rmSync(__dirname + '/../packages/sw/node_modules', { recursive: true, force: true }); fs.rmSync(__dirname + '/../packages/megalodon/lib', { recursive: true, force: true }); + + fs.rmSync(__dirname + '/../packages/misskey-js/built', { recursive: true, force: true }); + fs.rmSync(__dirname + '/../packages/misskey-js/node_modules', { recursive: true, force: true }); + + fs.rmSync(__dirname + '/../packages/misskey-reversi/built', { recursive: true, force: true }); + fs.rmSync(__dirname + '/../packages/misskey-reversi/node_modules', { recursive: true, force: true }); + + fs.rmSync(__dirname + '/../packages/misskey-bubble-game/built', { recursive: true, force: true }); + fs.rmSync(__dirname + '/../packages/misskey-bubble-game/node_modules', { recursive: true, force: true }); fs.rmSync(__dirname + '/../built', { recursive: true, force: true }); fs.rmSync(__dirname + '/../node_modules', { recursive: true, force: true }); diff --git a/scripts/clean.js b/scripts/clean.js index df1d33888d821207917e1321800b5a9ad433ab2a..6d8182fec2aaf4ea26ad59de2999082303ce36ac 100644 --- a/scripts/clean.js +++ b/scripts/clean.js @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -9,6 +9,9 @@ const fs = require('fs'); fs.rmSync(__dirname + '/../packages/backend/built', { recursive: true, force: true }); fs.rmSync(__dirname + '/../packages/frontend/built', { recursive: true, force: true }); fs.rmSync(__dirname + '/../packages/sw/built', { recursive: true, force: true }); + fs.rmSync(__dirname + '/../packages/misskey-js/built', { recursive: true, force: true }); + fs.rmSync(__dirname + '/../packages/misskey-reversi/built', { recursive: true, force: true }); + fs.rmSync(__dirname + '/../packages/misskey-bubble-game/built', { recursive: true, force: true }); fs.rmSync(__dirname + '/../built', { recursive: true, force: true }); fs.rmSync(__dirname + '/../packages/megalodon/lib', { recursive: true, force: true }); })(); diff --git a/scripts/dev.mjs b/scripts/dev.mjs index 43d9496f3c0862ca96f568446a7009c747d7cc44..e00b2e254d6fb73dc29602ec9d839616636f54a3 100644 --- a/scripts/dev.mjs +++ b/scripts/dev.mjs @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ @@ -28,7 +28,7 @@ await execa('pnpm', ['build-assets'], { stderr: process.stderr, }); -await execa('pnpm', ['--filter', 'misskey-js', 'build'], { +await execa('pnpm', ['--filter', 'misskey-js', 'ts'], { cwd: _dirname + '/../', stdout: process.stdout, stderr: process.stderr, @@ -40,6 +40,18 @@ await execa("pnpm", ['--filter', 'megalodon', 'build'], { stderr: process.stderr, }); +await execa('pnpm', ['--filter', 'misskey-reversi', 'build:tsc'], { + cwd: _dirname + '/../', + stdout: process.stdout, + stderr: process.stderr, +}); + +await execa('pnpm', ['--filter', 'misskey-bubble-game', 'build:tsc'], { + cwd: _dirname + '/../', + stdout: process.stdout, + stderr: process.stderr, +}); + execa('pnpm', ['build-pre', '--watch'], { cwd: _dirname + '/../', stdout: process.stdout, @@ -58,7 +70,7 @@ execa('pnpm', ['--filter', 'backend', 'dev'], { stderr: process.stderr, }); -execa('pnpm', ['--filter', 'frontend', 'dev'], { +execa('pnpm', ['--filter', 'frontend', process.env.MK_DEV_PREFER === 'backend' ? 'watch' : 'dev'], { cwd: _dirname + '/../', stdout: process.stdout, stderr: process.stderr, @@ -75,3 +87,15 @@ execa('pnpm', ['--filter', 'misskey-js', 'watch'], { stdout: process.stdout, stderr: process.stderr, }); + +execa('pnpm', ['--filter', 'misskey-reversi', 'watch'], { + cwd: _dirname + '/../', + stdout: process.stdout, + stderr: process.stderr, +}); + +execa('pnpm', ['--filter', 'misskey-bubble-game', 'watch'], { + cwd: _dirname + '/../', + stdout: process.stdout, + stderr: process.stderr, +}); diff --git a/scripts/tarball.mjs b/scripts/tarball.mjs new file mode 100644 index 0000000000000000000000000000000000000000..fbb833d94e73ee9f1b005ebabef2905cb695aeb7 --- /dev/null +++ b/scripts/tarball.mjs @@ -0,0 +1,32 @@ +import { createWriteStream } from 'node:fs'; +import { mkdir } from 'node:fs/promises'; +import { resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import glob from 'fast-glob'; +import walk from 'ignore-walk'; +import Pack from 'tar/lib/pack.js'; +import meta from '../package.json' assert { type: "json" }; + +const cwd = fileURLToPath(new URL('..', import.meta.url)); +const ignore = [ + '**/.git/**/*', + '**/*ignore', + '**/.gitmodules', + // Exclude files you don't want to include in the tarball here +]; + +export default async function build() { + const mkdirPromise = mkdir(resolve(cwd, 'built', 'tarball'), { recursive: true }); + const pack = new Pack({ cwd, gzip: true }); + const patterns = await walk({ path: cwd, ignoreFiles: ['.gitignore'] }); + + for await (const entry of glob.stream(patterns, { cwd, ignore, dot: true })) { + pack.add(entry); + } + + pack.end(); + + await mkdirPromise; + + pack.pipe(createWriteStream(resolve(cwd, 'built', 'tarball', `sharkey-${meta.version}.tar.gz`))); +} diff --git a/tossface-emojis b/tossface-emojis new file mode 160000 index 0000000000000000000000000000000000000000..3c0ac3f7bdd794cc334363bf834e58079ca00dd2 --- /dev/null +++ b/tossface-emojis @@ -0,0 +1 @@ +Subproject commit 3c0ac3f7bdd794cc334363bf834e58079ca00dd2