diff options
198 files changed, 4061 insertions, 2745 deletions
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE/01_bug.md index 79ca97dfa0..79ca97dfa0 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE/01_bug.md diff --git a/.github/PULL_REQUEST_TEMPLATE/02_enhance.md b/.github/PULL_REQUEST_TEMPLATE/02_enhance.md new file mode 100644 index 0000000000..79ca97dfa0 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/02_enhance.md @@ -0,0 +1,17 @@ +<!-- ℹ ãŠèªã¿ãã ã•ã„ / 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 --> diff --git a/.github/PULL_REQUEST_TEMPLATE/03_release.md b/.github/PULL_REQUEST_TEMPLATE/03_release.md new file mode 100644 index 0000000000..0c71ea804d --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/03_release.md @@ -0,0 +1,10 @@ +# Summary +This is a release PR. + +For more information on the release instructions, please see: +https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md#release + +# Checklist +- [ ] package.jsonã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãŒæ£ã—ãæ›´æ–°ã•れã¦ã„ã‚‹ +- [ ] CHANGELOGãŒéŽä¸è¶³ç„¡ãæ›´æ–°ã•れã¦ã„ã‚‹ +- [ ] CIãŒå…¨ã¦é€šã£ã¦ã„ã‚‹ diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 94d7d1371c..d65076ebb2 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -36,9 +36,6 @@ jobs: - backend - frontend - sw - lint: - - typecheck - - eslint steps: - uses: actions/checkout@v3.3.0 with: @@ -54,4 +51,29 @@ jobs: cache: 'pnpm' - run: corepack enable - run: pnpm i --frozen-lockfile - - run: pnpm --filter ${{ matrix.workspace }} run ${{ matrix.lint }} + - run: pnpm --filter ${{ matrix.workspace }} run eslint + + typecheck: + needs: [pnpm_install] + runs-on: ubuntu-latest + continue-on-error: true + strategy: + matrix: + workspace: + - backend + steps: + - uses: actions/checkout@v3.3.0 + with: + fetch-depth: 0 + submodules: true + - uses: pnpm/action-setup@v2 + with: + version: 7 + run_install: false + - uses: actions/setup-node@v3.6.0 + with: + node-version: 18.x + cache: 'pnpm' + - run: corepack enable + - run: pnpm i --frozen-lockfile + - run: pnpm --filter ${{ matrix.workspace }} run typecheck diff --git a/.github/workflows/pr-preview-destroy.yml b/.github/workflows/pr-preview-destroy.yml index 49f1ba8a34..8adfad9dab 100644 --- a/.github/workflows/pr-preview-destroy.yml +++ b/.github/workflows/pr-preview-destroy.yml @@ -9,14 +9,46 @@ name: Destroy preview environment jobs: destroy-preview-environment: runs-on: ubuntu-latest - if: github.repository == github.event.pull_request.head.repo.full_name steps: + - uses: actions/github-script@v6.3.3 + id: check-conclusion + env: + number: ${{ github.event.number }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + result-encoding: string + script: | + const { data: pull } = await github.rest.pulls.get({ + ...context.repo, + pull_number: process.env.number + }); + const ref = pull.head.sha; + + const { data: checks } = await github.rest.checks.listForRef({ + ...context.repo, + ref + }); + + const check = checks.check_runs.filter(c => c.name === 'deploy-preview-environment'); + + if (check.length === 0) { + return; + } + + const { data: result } = await github.rest.checks.get({ + ...context.repo, + check_run_id: check[0].id, + }); + + return result.conclusion; - name: Context + if: steps.check-conclusion.outputs.result == 'success' uses: okteto/context@latest with: token: ${{ secrets.OKTETO_TOKEN }} - name: Destroy preview environment + if: steps.check-conclusion.outputs.result == 'success' uses: okteto/destroy-preview@latest with: name: pr-${{ github.event.number }}-syuilo diff --git a/.vscode/settings.json b/.vscode/settings.json index b7e7b20c17..c94a34194e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "search.exclude": { "**/node_modules": true - } + }, + "typescript.tsdk": "node_modules/typescript/lib" }
\ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 81d81f5de0..489fcff2cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 13.x.x (unreleased) ### Improvements +- feat: 検索画é¢ã®çµ±åˆ (Khsmty) ### Bugfixes - @@ -11,10 +12,86 @@ You should also include the user name that made the change. ## 13.x.x (unreleased) ### Improvements +- ãƒãƒ£ãƒ³ãƒãƒ«å†…ãƒã‚¤ãƒ©ã‚¤ãƒˆ +- renoteã—ãŸéš›ã®è¡¨ç¤ºã‚’改善 +- ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰ã§ä¸€å®šæ™‚間経éŽã—ãŸã‚‰ãƒšãƒ¼ã‚¸ãƒãƒ¼ã‚·ãƒ§ãƒ³ã®ã‚¢ã‚¤ãƒ†ãƒ æ›´æ–°ã‚’ã—ãªã„ +- enhance(client): MkUrlPreviewã®é–‰ã˜ã‚‹ãƒœã‚¿ãƒ³ã‚’見やã™ã +- Add dialog to remove follower +- enhance(client): improve clip menu ux +- 検索画é¢ã®çµ±åˆ +- enhance(client): ノートメニューã‹ã‚‰ãƒ¦ãƒ¼ã‚¶ãƒ¼ãƒ¡ãƒ‹ãƒ¥ãƒ¼ã‚’é–‹ã‘るよã†ã« + +### Bugfixes +- Windows環境ã§swcを使ã†ã¨æ£ã—ãビルドã§ããªã„å•題ã®ä¿®æ£ +- fix(client): Android Chromeã§PWAã¨ã—ã¦ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã§ããªã„å•é¡Œã‚’ä¿®æ£ +- 未知ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒ deleteActor ã•れãŸã‚‰å‡¦ç†ã‚’スã‚ップã™ã‚‹ +- fix(server): notes/createã§ã€fileIdsã¨è¦‹ã¤ã‹ã£ãŸãƒ•ã‚¡ã‚¤ãƒ«ã®æ•°ãŒç•°ãªã‚‹å ´åˆã¯ã‚¨ãƒ©ãƒ¼ã«ã™ã‚‹ + +## 13.7.5 (2023/02/24) + +### Note +13.7.0以å‰ã‹ã‚‰ç›´æŽ¥ã“ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã«ã‚¢ãƒƒãƒ—デートã™ã‚‹å ´åˆã¯å…¨ã¦ã®é€šçŸ¥ãŒå‰Šé™¤**ã•れã¾ã›ã‚“。** + +### Improvements +- 紛らã‚ã—ã„ãŸã‚公開範囲ã®ã€Œãƒãƒ¼ã‚«ãƒ«ã®ã¿ã€ã‚ªãƒ—ションã®å称を「連åˆãªã—ã€ã«å¤‰æ›´ +- Frontend: スマホ・タブレットã®å ´åˆã€ãƒãƒ£ãƒ³ãƒãƒ«ã®æŠ•稿フォームã«è‡ªå‹•ã§ãƒ•ォーカスã—ãªã„よã†ã« + +### Bugfixes +- å…¨ã¦ã®é€šçŸ¥ãŒå‰Šé™¤ã•れã¦ã—ã¾ã†ã®ã‚’ä¿®æ£ + +## 13.7.3 (2023/02/23) + +### Note +~~13.7.0以å‰ã‹ã‚‰ç›´æŽ¥ã“ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã«ã‚¢ãƒƒãƒ—デートã™ã‚‹å ´åˆã¯å…¨ã¦ã®é€šçŸ¥ãŒå‰Šé™¤**ã•れã¾ã›ã‚“。**~~ + +### Improvements + +### Bugfixes +- Client: 「ã‚ャッシュを削除ã€ã—ãŸå¾Œã€ãƒãƒ¼ã‚«ãƒ«ã®ã‚«ã‚¹ã‚¿ãƒ 絵文å—ãŒè¡¨ç¤ºã•れãªããªã‚‹ã•れãªããªã‚‹å•é¡Œã‚’ä¿®æ£ +- Client: 通知è¨å®šç”»é¢ã§ä»¥å‰ã‹ã‚‰ã‚°ãƒ«ãƒ¼ãƒ—ã®æ‹›å¾…を有効化ã—ã¦ã„ãŸå ´åˆã€é€šçŸ¥ã®è¡¨ç¤ºã«å¤±æ•—ã™ã‚‹å•題ã®ä¿®æ£ +- Client: 通知è¨å®šç”»é¢ã«å¤ã„ãƒˆã‚°ãƒ«ãŒæ®‹ã£ã¦ã„ãŸå•é¡Œã‚’ä¿®æ£ + +## 13.7.2 (2023/02/23) + +### Note +13.7.0以å‰ã‹ã‚‰ã‚¢ãƒƒãƒ—デートã™ã‚‹å ´åˆã¯å…¨ã¦ã®é€šçŸ¥ãŒå‰Šé™¤ã•れã¾ã™ã€‚ + +### Improvements +- enhance: make pwa icon maskable +- chore(client): tweak custom emoji size + +### Bugfixes +- マイグレーションãŒå¤±æ•—ã™ã‚‹ã“ã¨ãŒã‚ã‚‹ã®ã‚’ä¿®æ£ + +## 13.7.1 (2023/02/23) + +### Improvements +- pnpm buildã§ã¯swcを使ã†ã‚ˆã†ã« + +### Bugfixes +- NODE_ENV=productionã§ãƒ“ルドã§ããªã„ã®ã‚’ä¿®æ£ + +## 13.7.0 (2023/02/22) + +### Changes +- ãƒãƒ£ãƒƒãƒˆæ©Ÿèƒ½ãŒå‰Šé™¤ã•れã¾ã—㟠+ +### Improvements - Server: URLプレビュー(summaly)ã¯ãƒ—ãƒã‚シを通ã™ã‚ˆã†ã« +- Client: 2FAè¨å®šã®UIã‚’ã¾ã¨ã‚‚ã«ã—㟠+- ã‚»ã‚ュリティã‚ーã®åå‰ã‚’変更ã§ãるよã†ã« +- enhance(client): add quiz preset for play +- 広告開始時期をè¨å®šã§ãるよã†ã« +- ã¿ã¤ã‘ã‚‹ã§å…¬é–‹ãƒãƒ¼ãƒ«ä¸€è¦§ã¨ãã®ãƒ¡ãƒ³ãƒãƒ¼ã‚’閲覧ã§ãるよã†ã« +- enhance(client): MFMã®x3, x4ãŒå«ã¾ã‚Œã¦ã„ãŸã‚‰ãƒŽãƒ¼ãƒˆã‚’ãŸãŸã‚€ã‚ˆã†ã« +- enhance(client): make possible to reload page of window ### Bugfixes -- +- ユーザー検索ダイアãƒã‚°ã§ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’絞ã£ã¦æ¤œç´¢ã§ããªã„å•é¡Œã‚’ä¿®æ£ +- fix(client): MkHeaderåŠã³ãƒ‡ãƒƒã‚ã®ã‚«ãƒ©ãƒ ã§ãƒãƒ£ãƒ³ãƒãƒ«ä¸€è¦§ã‚’é¸æŠžã—ãŸã¨ãã€æœ€å¤§5個ã¾ã§ã—ã‹è¡¨ç¤ºã•れãªã„ +- 管ç†ç”»é¢ã®åºƒå‘Šã‚’10個以上見ãˆã‚‹ã‚ˆã†ã« +- Moderation note ãŒä¿å˜ã§ããªã„ +- ユーザーã®ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°æ¤œç´¢ãŒæ©Ÿèƒ½ã—ã¦ã„ãªã„ã®ã‚’ä¿®æ£ ## 13.6.1 (2023/02/12) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 48d8a40dea..668989f122 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -83,11 +83,18 @@ An actual domain will be assigned so you can test the federation. - The title must be in the format `Release: x.y.z`. - `x.y.z` is the new version you are trying to release. 3. Deploy and perform a simple QA check. Also verify that the tests passed. -4. Merge it. +4. Merge it. (Do not squash commit) 5. Create a [release of GitHub](https://github.com/misskey-dev/misskey/releases) - The target branch must be `master` - The tag name must be the version +> **Note** +> Why this instruction is necessary: +> - To perform final QA checks +> - To distribute responsibility +> - To check direct commits to develop +> - To celebrate the release together 🎉 + ## Localization (l10n) Misskey uses [Crowdin](https://crowdin.com/project/misskey) for localization management. You can improve our translations with your Crowdin account. @@ -278,9 +285,10 @@ SQLã§ã¯é…列ã®ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ã¯**1å§‹ã¾ã‚Š**。 ### null IN nullãŒå«ã¾ã‚Œã‚‹å¯èƒ½æ€§ã®ã‚るカラムã«INã™ã‚‹ã¨ãã¯ã€ãã®ã¾ã¾ã ã¨ãŠã‹ã—ããªã‚‹ã®ã§ORãªã©ã§nullã®ãƒãƒ³ãƒ‰ãƒªãƒ³ã‚°ã‚’ã—よã†ã€‚ -### `undefined`ã«ã”用心 -MongoDBã®æ™‚ã¨ã¯é•ã„ã€findOneã§ãƒ¬ã‚³ãƒ¼ãƒ‰ã‚’å–å¾—ã™ã‚‹æ™‚ã«å¯¾è±¡ãƒ¬ã‚³ãƒ¼ãƒ‰ãŒå˜åœ¨ã—ãªã„å ´åˆ **`undefined`** ãŒè¿”ã£ã¦ãã‚‹ã®ã§æ³¨æ„。 -MongoDBã¯`null`ã§è¿”ã—ã¦ãã¦ãŸã®ã§ã€ãã®æ„Ÿè¦šã§`if (x === null)`ã¨ã‹æ›¸ãã¨ãƒã‚°ã‚‹ã€‚代ã‚りã«`if (x == null)`ã¨æ›¸ã„ã¦ãã ã•ã„ +### enumã®å‰Šé™¤ã¯æ°—ã‚’ã¤ã‘ã‚‹ +enumã®åˆ—挙ã®å†…容ã®å‰Šé™¤ã¯ã€ãã®å€¤ã‚’ã‚‚ã¤ãƒ¬ã‚³ãƒ¼ãƒ‰ã‚’å…¨ã¦å‰Šé™¤ã—ãªã„ã¨ã„ã‘ãªã„ + +削除ãŒé‡ãŸã‹ã£ãŸã‚Šä¸å¯èƒ½ã ã£ãŸã‚Šã™ã‚‹å ´åˆã¯ã€å‰Šé™¤ã—ãªã„ã§ãŠã ### Migrationä½œæˆæ–¹æ³• packages/backendã§: diff --git a/ROADMAP.md b/ROADMAP.md index c95bb8d92b..420f728758 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -5,9 +5,9 @@ Also, the later tasks are more indefinite and are subject to change as developme ## (1) Improve maintainability \<current phase\> 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) +- ~~Make the number of type errors zero (backend)~~ → Done âœ”ï¸ - Improve CI - - Fix tests + - ~~Fix tests~~ → Done âœ”ï¸ - Fix random test failures - https://github.com/misskey-dev/misskey/issues/7985 and https://github.com/misskey-dev/misskey/issues/7986 - Add more tests - ~~May need to implement a mechanism that allows for DI~~ → Done âœ”ï¸ diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000000..5d8d232b5e --- /dev/null +++ b/codecov.yml @@ -0,0 +1,6 @@ +coverage: + status: + project: + default: + only_pulls: true + if_ci_failed: success diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index 467aed1294..fb06f77daf 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -379,13 +379,10 @@ about: "عن" aboutMisskey: "عن Misskey" administrator: "المدير" token: "الرمز المميز" -twoStepAuthentication: "الإستيثاق بعاملَيْن" moderator: "مشرÙÙ" moderation: "الإشراÙ" nUsersMentioned: "{n} مستخدمين Ø£ÙØ´ÙŠØ± إليهم" securityKey: "Ù…ÙØªØ§Ø الأمان" -securityKeyName: "اسم Ø§Ù„Ù…ÙØªØ§Ø" -registerSecurityKey: "سجل Ù…ÙØªØ§Ø أمان" lastUsed: "آخر استخدام" unregister: "إلغاء التسجيل" passwordLessLogin: "Ù„ÙØ¬ Ù…ÙÙ† دون كلمة سرية" @@ -403,24 +400,15 @@ markAsReadAllTalkMessages: "علّم جميع الرسائل كمقروءة" help: "المساعدة" inputMessageHere: "اكتب رسالتك هنا" close: "اغلق" -group: "Ø§Ù„ÙØ±ÙŠÙ‚" -groups: "الÙÙØ±ÙŽÙ‚" -createGroup: "انشئ ÙØ±ÙŠÙ‚ًا" -ownedGroups: "ÙÙØ±Ù‚ÙŠ" -joinedGroups: "الÙÙØ±Ù‚ المÙنضم إليها" invites: "دعوة" -groupName: "اسم Ø§Ù„ÙØ±ÙŠÙ‚" members: "الأعضاء" transfer: "نقل" -messagingWithUser: "ØªØØ¯Ø« مع مستخدم" -messagingWithGroup: "Ù…ØØ§Ø¯Ø«Ø© جماعية" title: "العنوان" text: "النص" enable: "تشغيل" next: "التالية" retype: "أعد الكتابة" noteOf: "Ù…Ù„Ø§ØØ¸Ø§Øª {user}" -inviteToGroup: "دعوة إلى ÙØ±ÙŠÙ‚" quoteAttached: "اÙÙ‚ØªÙØ¨Ø³ÙŽ" quoteQuestion: "أتريد تضمينها كاقتباس" noMessagesYet: "ليس هناك رسائل بعد" @@ -442,14 +430,10 @@ passwordMatched: "التطابق صØÙŠØ!" passwordNotMatched: "غير متطابقتان" signinWith: "الولوج عبر {x}" signinFailed: "ÙØ´Ù„ الولوج، خطأ ÙÙŠ اسم المستخدم أو كلمة المرور." -tapSecurityKey: "أنقر Ù…ÙØªØ§Ø الأمان" or: "أو" language: "اللغة" uiLanguage: "لغة واجهة المستخدم" -groupInvited: "Ø¯ÙØ¹ÙŠØª إلى ÙØ±ÙŠÙ‚Ù" aboutX: "عن {x}" -youHaveNoGroups: "لا تمتلك أية ÙÙØ±ÙŽÙ‚" -joinOrCreateGroup: "Ø§ØØµÙ„ على دعوة Ù„ÙØ±ÙŠÙ‚ أو أنشئ ÙˆØ§ØØ¯Ù‹Ø§." noHistory: "السجل ÙØ§Ø±Øº" signinHistory: "تاريخ تسجيل الدخول" doing: "انتظر Ù„ØØ¸Ø©" @@ -790,8 +774,6 @@ deleteAccountConfirm: "Ø³ÙŠØØ°Ù ØØ³Ø§Ø¨Ùƒ نهائيًا، أتريد Ø§Ù„Ù…Ø incorrectPassword: "كلمة السر خاطئة." voteConfirm: "متيقّÙÙ† من تصويتك لـ {choice}ØŸ" hide: "Ø¥Ø®ÙØ§Ø¡" -leaveGroup: "مغادرة Ø§Ù„ÙØ±ÙŠÙ‚" -leaveGroupConfirm: "متيقن من مغادرة \"{name}\"ØŸ" welcomeBackWithName: "Ù…Ø±ØØ¨Ù‹Ø§ بك مجددًا {name}" clickToFinishEmailVerification: "انقر [{ok}] لاستيثاق بريدك الإلكتروني." overridedDeviceKind: "نوع الجهاز" @@ -1019,12 +1001,11 @@ _tutorial: step7_3: "ØØ¸Ù‹Ø§ سعيدًا واستمتع بوقتك مع ميسكي! 🚀" _2fa: alreadyRegistered: "سجلت سلÙًا جهازًا للاستيثاق بعاملين." - registerDevice: "سجّل جهازًا جديدًا" - registerKey: "تسجيل Ù…ÙØªØ§Ø أمان جديد" step1: "أولًا ثبّت تطبيق استيثاق على جهازك (مثل {a} Ùˆ{b})." step2: "Ø§Ù…Ø³Ø Ø±Ù…Ø² الاستجابة السريعة الموجد على الشاشة." step3: "أدخل الرمز الموجود ÙÙŠ تطبيقك لإكمال التثبيت." step4: "من هذه Ø§Ù„Ù„ØØ¸Ø© أثناء ولوجك Ø³ÙŠÙØ·Ù„ب منك الرمز." + renewTOTPCancel: "ليس اﻵن" _permissions: "read:account": "اعرض معلومات ØØ³Ø§Ø¨Ùƒ" "write:account": "تعديل معلومات ØØ³Ø§Ø¨Ùƒ" @@ -1133,8 +1114,6 @@ _visibility: followersDescription: "اجعلها مرئية Ù„Ù…ØªØ§Ø¨ÙØ¹ÙŠÙƒ Ùقط" specified: "مباشرة" specifiedDescription: "اجعلها مرئية لمستخدمين Ù…ØØ¯Ø¯ÙŠÙ†" - localOnly: "المØÙ„ÙŠ Ùقط" - localOnlyDescription: "ليس مرئيًا للمستخدمين Ø§Ù„Ø¨ÙØ¹Ø§Ø¯" _postForm: replyPlaceholder: "رد على هذه Ø§Ù„Ù…Ù„Ø§ØØ¸Ø©â€¦" quotePlaceholder: "اقتبس هذه Ø§Ù„Ù…Ù„Ø§ØØ¸Ø©â€¦" @@ -1255,12 +1234,9 @@ _notification: youGotReply: "ردّ عليك {name}" youGotQuote: "اقتبس منك {name}" youRenoted: "إعادت نشر من {name}" - youGotMessagingMessageFromUser: "لقد تلقيت رسالة Ù…ÙÙ† {name}" - youGotMessagingMessageFromGroup: "لقد أرسÙلَت رسالة إلى Ø§Ù„ÙØ±ÙŠÙ‚ {name}" youWereFollowed: "يتابعك" youReceivedFollowRequest: "تلقيتَ طلب متابعة" yourFollowRequestAccepted: "Ù‚ÙØ¨Ù„ طلب المتابعة" - youWereInvitedToGroup: "Ø¯ÙØ¹ÙŠØª إلى ÙØ±ÙŠÙ‚Ù" pollEnded: "ظهرت نتائج الاستطلاع" unreadAntennaNote: "هوائي {name}" _types: @@ -1273,7 +1249,6 @@ _notification: reaction: "Ø§Ù„ØªÙØ§Ø¹Ù„ات" receiveFollowRequest: "طلبات المتابعة المتلقاة" followRequestAccepted: "طلبات المتابعة المقبولة" - groupInvited: "دعوات Ø§Ù„ÙØ±ÙŠÙ‚" app: "إشعارات التطبيقات المرتبطة" _actions: followBack: "تابعك بالمثل" diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index 92e3785936..18898bf4d6 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -382,12 +382,9 @@ about: "আপনার সমà§à¦ªà¦°à§à¦•ে" aboutMisskey: "Misskey সমà§à¦ªà¦°à§à¦•ে" administrator: "পà§à¦°à¦¶à¦¾à¦¸à¦•" token: "টোকেন" -twoStepAuthentication: "২-ধাপ পà§à¦°à¦®à¦¾à¦£à§€à¦•রণ" moderator: "মডারেটর" nUsersMentioned: "{n} জনকে উলà§à¦²à§‡à¦– করা হয়েছে" securityKey: "সিকিউরিটি কী" -securityKeyName: "কী'র নাম" -registerSecurityKey: "সিকিউরিটি কী নিবনà§à¦§à¦¨ করà§à¦¨" lastUsed: "শেষ বà§à¦¯à¦¾à¦¬à¦¹à¦¾à¦° করা হয়েছে" unregister: "নিবনà§à¦§à¦¨à¦®à§à¦•à§à¦¤ হন" passwordLessLogin: "পাসওয়ারà§à¦¡-বিহীন লগইন সেট আপ করà§à¦¨" @@ -405,24 +402,15 @@ markAsReadAllTalkMessages: "সমসà§à¦¤ মেসেজ পঠিত হিà help: "সহায়তা" inputMessageHere: "à¦à¦–ানে মেসেজ লিখà§à¦¨" close: "বনà§à¦§" -group: "গà§à¦°à§à¦ª" -groups: "গà§à¦°à§à¦ªà¦¸à¦®à§‚হ" -createGroup: "গà§à¦°à§à¦ª তৈরী করà§à¦¨" -ownedGroups: "আপনার গà§à¦°à§à¦ªà¦—à§à¦²à¦¿" -joinedGroups: "যেসব গà§à¦°à§à¦ªà§‡ আপনি আছেন" invites: "আমনà§à¦¤à§à¦°à¦£" -groupName: "গà§à¦°à§à¦ªà§‡à¦° নাম" members: "সদসà§à¦¯à¦¬à§ƒà¦¨à§à¦¦" transfer: "হসà§à¦¤à¦¾à¦¨à§à¦¤à¦°" -messagingWithUser: "পà§à¦°à¦¾à¦‡à¦à§‡à¦Ÿ চà§à¦¯à¦¾à¦Ÿ" -messagingWithGroup: "গà§à¦°à§à¦ª চà§à¦¯à¦¾à¦Ÿ" title: "শিরোনাম" text: "পাঠà§à¦¯" enable: "সকà§à¦°à¦¿à§Ÿ" next: "পরবরà§à¦¤à§€" retype: "পà§à¦¨à¦ƒ পà§à¦°à¦¬à§‡à¦¶" noteOf: "{user} à¦à¦° নোট" -inviteToGroup: "গà§à¦°à§à¦ªà§‡ আমনà§à¦¤à§à¦°à¦£ জানান" quoteAttached: "উদà§à¦§à§ƒà¦¤" quoteQuestion: "উদà§à¦§à§ƒà¦¤à¦¿ হিসাবে সংযà§à¦•à§à¦¤ করবেন?" noMessagesYet: "কোন মেসেজ নেই" @@ -444,15 +432,11 @@ passwordMatched: "মিলেছে" passwordNotMatched: "মিলেনি" signinWith: "{x} à¦à¦° সাহাযà§à¦¯à§‡ সাইন ইন করà§à¦¨" signinFailed: "লগ ইন করা যায়নি। আপনার বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারীর নাম à¦à¦¬à¦‚ পাসওয়ারà§à¦¡ চেক করà§à¦¨." -tapSecurityKey: "সিকিউরিটি কী সà§à¦ªà¦°à§à¦¶ করà§à¦¨" or: "অথবা" language: "à¦à¦¾à¦·à¦¾" uiLanguage: "UI à¦à¦° à¦à¦¾à¦·à¦¾" -groupInvited: "আপনি à¦à¦•টি গà§à¦°à§à¦ªà§‡ আমনà§à¦¤à§à¦°à¦¿à¦¤ হয়েছেন" aboutX: "{x} সমà§à¦ªà¦°à§à¦•ে" disableDrawer: "ডà§à¦°à¦¯à¦¼à¦¾à¦° মেনৠপà§à¦°à¦¦à¦°à§à¦¶à¦¨ করবেন না" -youHaveNoGroups: "আপনার কোন গà§à¦°à§à¦ª নেই " -joinOrCreateGroup: "à¦à¦•টি বিদà§à¦¯à¦®à¦¾à¦¨ গà§à¦°à§à¦ªà§‡à¦° আমনà§à¦¤à§à¦°à¦£ পান বা à¦à¦•টি নতà§à¦¨ গà§à¦°à§à¦ª তৈরি করà§à¦¨à§·" noHistory: "কোনো ইতিহাস নেই" signinHistory: "পà§à¦°à¦¬à§‡à¦¶ করার ইতিহাস" doing: "পà§à¦°à¦•à§à¦°à¦¿à§Ÿà¦¾ করছে..." @@ -820,8 +804,6 @@ deleteAccountConfirm: "আপনার অà§à¦¯à¦¾à¦•াউনà§à¦Ÿ মà§à¦›à incorrectPassword: "আপনার দেওয়া পাসওয়ারà§à¦¡à¦Ÿà¦¿ à¦à§à¦²à¥¤" voteConfirm: "\"{choice}\" ঠà¦à§‹à¦Ÿ দিতে চান?" hide: "লà§à¦•ান" -leaveGroup: "গà§à¦°à§à¦ª ছেড়ে চলে যান" -leaveGroupConfirm: "\"{name}\" গà§à¦°à§à¦ª ছেড়ে চলে যেতে চান?" useDrawerReactionPickerForMobile: "মোবাইলে রিঅà§à¦¯à¦¾à¦•শন পিকারকে ডà§à¦°à¦¯à¦¼à¦¾à¦°à§‡ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করà§à¦¨" welcomeBackWithName: "আবার সà§à¦¬à¦¾à¦—তম, {name}" clickToFinishEmailVerification: " [{ok}] কà§à¦²à¦¿à¦• করার মাধà§à¦¯à¦®à§‡ আপনার ইমেল ঠিকানা নিশà§à¦šà¦¿à¦¤ করà§à¦¨à¥¤" @@ -1081,8 +1063,6 @@ _tutorial: step7_3: "à¦à¦–ন Misskey উপà¦à§‹à¦— করà§à¦¨ 🚀" _2fa: alreadyRegistered: "আপনি ইতিমধà§à¦¯à§‡ à¦à¦•টি 2-ফà§à¦¯à¦¾à¦•à§à¦Ÿà¦° অথেনটিকেশন ডিà¦à¦¾à¦‡à¦¸ নিবনà§à¦§à¦¨ করেছেন৷" - registerDevice: "নতà§à¦¨ ডিà¦à¦¾à¦‡à¦¸ নিবনà§à¦§à¦¨ করà§à¦¨" - registerKey: "সিকিউরিটি কী নিবনà§à¦§à¦¨ করà§à¦¨" step1: "পà§à¦°à¦¥à¦®à§‡, আপনার ডিà¦à¦¾à¦‡à¦¸à§‡ {a} বা {b} à¦à¦° মতো à¦à¦•টি অথেনটিকেশন অà§à¦¯à¦¾à¦ª ইনসà§à¦Ÿà¦² করà§à¦¨à§·" step2: "à¦à¦°à¦ªà¦°à§‡, অà§à¦¯à¦¾à¦ªà§‡à¦° সাহাযà§à¦¯à§‡ পà§à¦°à¦¦à¦°à§à¦¶à¦¿à¦¤ QR কোডটি সà§à¦•à§à¦¯à¦¾à¦¨ করà§à¦¨à¥¤" step2Url: "ডেসà§à¦•টপ অà§à¦¯à¦¾à¦ªà§‡, নিমà§à¦¨à¦²à¦¿à¦–িত URL লিখà§à¦¨:" @@ -1134,7 +1114,6 @@ _antennaSources: homeTimeline: "আপনি অনà§à¦¸à¦°à¦£ করছেন, à¦à¦®à¦¨ বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারীদের নোট" users: "à¦à¦• বা à¦à¦•াধিক নিরà§à¦¦à¦¿à¦·à§à¦Ÿ বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারীর নোট" userList: "নিরà§à¦¦à¦¿à¦·à§à¦Ÿ তালিকায় নাম থাকা বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারীদের নোট" - userGroup: "নিরà§à¦¦à¦¿à¦·à§à¦Ÿ গà§à¦°à§à¦ªà§‡ থাকা বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারীদের নোট" _weekday: sunday: "রবিবার" monday: "সোমবার" @@ -1203,8 +1182,6 @@ _visibility: followersDescription: "শà§à¦§à§à¦®à¦¾à¦¤à§à¦° আপনার অনà§à¦¸à¦°à¦£à¦•ারীদের নিকট পোসà§à¦Ÿ করà§à¦¨" specified: "ডাইরেকà§à¦Ÿ নোট" specifiedDescription: "শà§à¦§à§à¦®à¦¾à¦¤à§à¦° নিরà§à¦¦à¦¿à¦·à§à¦Ÿ বà§à¦¯à¦¾à¦¬à¦¹à¦¾à¦°à¦•ারীর নিকট পাঠান" - localOnly: "শà§à¦§à§à¦®à¦¾à¦¤à§à¦° লোকাল" - localOnlyDescription: "রিমোট বà§à¦¯à¦¾à¦¬à¦¹à¦¾à¦°à¦•ারীদের নিকট দৃশà§à¦¯à¦®à¦¾à¦¨ নয়" _postForm: replyPlaceholder: "নোটটির জবাব দিন..." quotePlaceholder: "নোটটিকে উদà§à¦§à§ƒà¦¤ করà§à¦¨..." @@ -1332,12 +1309,9 @@ _notification: youGotReply: "{name} আপনাকে জবাব দিয়েছে" youGotQuote: "{name} আপনাকে উদà§à¦§à§ƒà¦¤ করেছে" youRenoted: "{name} à¦à¦° Renote" - youGotMessagingMessageFromUser: "{name} আপনাকে মেসেজ করেছে" - youGotMessagingMessageFromGroup: "{name} গà§à¦°à§à¦ªà§‡ à¦à¦•টি নতà§à¦¨ মেসেজ আছে" youWereFollowed: "আপনাকে অনà§à¦¸à¦°à¦£ করছে" youReceivedFollowRequest: "অনà§à¦¸à¦°à¦£ করার জনà§à¦¯ অনà§à¦°à§‹à¦§ পাওয়া গেছে" yourFollowRequestAccepted: "আপনার অনà§à¦¸à¦°à¦£ করার অনà§à¦°à§‹à¦§ গৃহীত হয়েছে" - youWereInvitedToGroup: "আপনি à¦à¦•টি গà§à¦°à§à¦ªà§‡ আমনà§à¦¤à§à¦°à¦¿à¦¤ হয়েছেন" pollEnded: "পোলের ফলাফল দেখা যাবে" emptyPushNotificationMessage: "আপডেট করা পà§à¦¶ বিজà§à¦žà¦ªà§à¦¤à¦¿" _types: @@ -1351,7 +1325,6 @@ _notification: pollEnded: "পোল শেষ" receiveFollowRequest: "পà§à¦°à¦¾à¦ªà§à¦¤ অনà§à¦¸à¦°à¦£à§‡à¦° অনà§à¦°à§‹à¦§à¦¸à¦®à§‚হ" followRequestAccepted: "গৃহীত অনà§à¦¸à¦°à¦£à§‡à¦° অনà§à¦°à§‹à¦§à¦¸à¦®à§‚হ" - groupInvited: "গà§à¦°à§à¦ªà§‡à¦° আমনà§à¦¤à§à¦°à¦¨à¦¸à¦®à§‚হ" app: "লিঙà§à¦• করা অà§à¦¯à¦¾à¦ª থেকে বিজà§à¦žà¦ªà§à¦¤à¦¿" _actions: followBack: "ফলো বà§à¦¯à¦¾à¦• করেছে" diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index d89e22631c..2b1168f780 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -315,13 +315,10 @@ userList: "Llistes" about: "Informació" aboutMisskey: "Quant a Misskey" administrator: "Administrador/a" -twoStepAuthentication: "Verificació en dos passos" moderator: "Moderador/a" moderation: "Moderació" nUsersMentioned: "{n} usuaris mencionats" securityKey: "Clau de seguretat" -securityKeyName: "Nom de la clau" -registerSecurityKey: "Registra la clau de seguretat" unregister: "Cancel·la el registre" passwordLessLogin: "Inici de sessió sense contrasenya" resetPassword: "Restableix la contrasenya" @@ -334,7 +331,6 @@ help: "Ajuda" invites: "Convida" next: "Següent" noteOf: "Publicació de: {user}" -inviteToGroup: "Convida'l al grup" invitations: "Convida" tags: "Etiquetes" docSource: "Font del document" @@ -393,7 +389,6 @@ _antennaSources: homeTimeline: "Publicacions dels usuaris seguits" users: "Publicacions d'usuaris especÃfics" userList: "Publicacions d'una llista d'usuaris" - userGroup: "Publicacions d'usuaris d'un grup" _widgets: profile: "Perfil" instanceInfo: "Informació del fitxer d'instal·lació" diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index eaafd5ee10..32891ff9fd 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -337,12 +337,9 @@ about: "Informace" aboutMisskey: "O Misskey" administrator: "Administrátor" token: "Token" -twoStepAuthentication: "Dvoufaktorová autentikace" moderator: "Moderátor" nUsersMentioned: "{n} uživatelů zmÃnilo" securityKey: "BezpeÄnostnà klÃÄ" -securityKeyName: "Název klÃÄe" -registerSecurityKey: "Registrovat bezpeÄnostnà klÃÄ" lastUsed: "Naposledy použito" unregister: "Odstranit" resetPassword: "Resetovat heslo" @@ -359,13 +356,7 @@ markAsReadAllTalkMessages: "OznaÄit vÅ¡echny zprávy za pÅ™eÄtené" help: "NápovÄ›da" inputMessageHere: "Sem zadejte zprávu" close: "ZavÅ™Ãt" -group: "Skupina" -groups: "Skupiny" -createGroup: "VytvoÅ™it skupinu" -ownedGroups: "VlastnÄ›né skupiny" -joinedGroups: "ÄŒlenstvà ve skupinách" invites: "Pozvat" -groupName: "Název skupiny" members: "ÄŒlenové" transfer: "PÅ™evod" title: "Titulek" @@ -374,7 +365,6 @@ enable: "Povolit" next: "DalÅ¡Ã" retype: "Zadejte znovu" noteOf: "{user} poznámky" -inviteToGroup: "Pozvat do skupiny" quoteAttached: "Citace" quoteQuestion: "PÅ™iložit jako citaci?" noMessagesYet: "ZatÃm tu nejsou žádné zprávy" @@ -396,14 +386,10 @@ passwordMatched: "Hesla se schodujÃ" passwordNotMatched: "Hesla se neschodujÃ" signinWith: "PÅ™ihlásit se s {x}" signinFailed: "Nelze se pÅ™ihlásit. Zkontrolujte prosÃm své uživatelské jméno a heslo." -tapSecurityKey: "ŤuknÄ›te na bezpeÄnostnà klÃÄ" or: "Nebo" language: "Jazyk" uiLanguage: "Jazyk uživatelského rozhranÃ" -groupInvited: "Pozvat do skupiny" aboutX: "O {x}" -youHaveNoGroups: "Nemáte žádné skupiny" -joinOrCreateGroup: "Můžete požádat o pozvánà do stávajÃcà skupiny nebo vytvoÅ™it novou." noHistory: "Žádná historie" signinHistory: "Historie pÅ™ihlášenÃ" category: "Kategorie" @@ -677,9 +663,6 @@ _time: second: "Sekund" minute: "Minut" hour: "Hodin" -_2fa: - registerDevice: "PÅ™idat zaÅ™ÃzenÃ" - registerKey: "PÅ™idat bezpeÄnostnà klÃÄ" _weekday: sunday: "NedÄ›le" monday: "PondÄ›lÃ" @@ -773,7 +756,6 @@ _pages: button: "TlaÄÃtko" _notification: youWereFollowed: "Máte nového následovnÃka" - youWereInvitedToGroup: "Pozvat do skupiny" _types: all: "VÅ¡e" follow: "SledovanÃ" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index fb80b92850..e68207f902 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -103,6 +103,8 @@ renoted: "Renote getätigt." cantRenote: "Renote dieses Beitrags nicht möglich." cantReRenote: "Renote einer Renote nicht möglich." quote: "Zitieren" +inChannelRenote: "Kanal-interner Renote" +inChannelQuote: "Kanal-internes Zitat" pinnedNote: "Angeheftete Notiz" pinned: "Angeheftet" you: "Du" @@ -391,13 +393,10 @@ about: "Über" aboutMisskey: "Über Misskey" administrator: "Administrator" token: "Token" -twoStepAuthentication: "Zwei-Faktor-Authentifizierung" moderator: "Moderator" moderation: "Moderation" nUsersMentioned: "Von {n} Benutzern erwähnt" securityKey: "Sicherheitsschlüssel" -securityKeyName: "Schlüsselname" -registerSecurityKey: "Sicherheitsschlüssel registrieren" lastUsed: "Zuletzt benutzt" unregister: "Deaktivieren" passwordLessLogin: "Passwortloses Anmelden einrichten" @@ -415,24 +414,15 @@ markAsReadAllTalkMessages: "Alle Chats als gelesen markieren" help: "Hilfe" inputMessageHere: "Hier Nachricht eingeben" close: "Schließen" -group: "Gruppe" -groups: "Gruppen" -createGroup: "Gruppe erstellen" -ownedGroups: "Meine Gruppen" -joinedGroups: "Beigetretene Gruppen" invites: "Einladungen" -groupName: "Gruppenname" members: "Mitglieder" transfer: "Übertragen" -messagingWithUser: "Privatchat" -messagingWithGroup: "Gruppenchat" title: "Titel" text: "Text" enable: "Aktivieren" next: "Weiter" retype: "Erneut eingeben" noteOf: "Notiz von {user}" -inviteToGroup: "Zu Gruppe einladen" quoteAttached: "Zitat" quoteQuestion: "Als Zitat anhängen?" noMessagesYet: "Noch keine Nachrichten vorhanden" @@ -454,19 +444,17 @@ passwordMatched: "Stimmt überein" passwordNotMatched: "Stimmt nicht überein" signinWith: "Mit {x} anmelden" signinFailed: "Anmeldung fehlgeschlagen. Überprüfe Benutzername und Passswort." -tapSecurityKey: "Tippe deinen Sicherheitsschlüssel an" or: "Oder" language: "Sprache" uiLanguage: "Sprache der Benutzeroberfläche" -groupInvited: "Du wurdest in eine Gruppe eingeladen" aboutX: "Über {x}" emojiStyle: "Emoji-Stil" native: "Nativ" disableDrawer: "Keine ausfahrbaren Menüs verwenden" -youHaveNoGroups: "Keine Gruppen vorhanden" -joinOrCreateGroup: "Lass dich zu einer Gruppe einladen oder erstelle deine eigene." noHistory: "Kein Verlauf gefunden" signinHistory: "Anmeldungsverlauf" +enableAdvancedMfm: "Erweitertes MFM aktivieren" +enableAnimatedMfm: "Animiertes MFM aktivieren" doing: "In Bearbeitung …" category: "Kategorie" tags: "Schlagwörter" @@ -836,8 +824,6 @@ deleteAccountConfirm: "Dein Benutzerkonto wird unwiderruflich gelöscht. Trotzde incorrectPassword: "Falsches Passwort." voteConfirm: "Wirklich für „{choice}“ abstimmen?" hide: "Inhalt verbergen" -leaveGroup: "Gruppe verlassen" -leaveGroupConfirm: "Möchtest du „{name}“ wirklich verlassen?" useDrawerReactionPickerForMobile: "Auf mobilen Geräten ausfahrbare Reaktionsauswahl anzeigen" welcomeBackWithName: "Willkommen zurück, {name}" clickToFinishEmailVerification: "Drücke bitte auf [{ok}], um die Email-Bestätigung abzuschließen." @@ -945,6 +931,14 @@ selectFromPresets: "Aus Vorlagen wählen" achievements: "Errungenschaften" gotInvalidResponseError: "Ungültige Antwort des Servers" gotInvalidResponseErrorDescription: "Eventuell ist der Server momentan nicht erreichbar oder untergeht Wartungsarbeiten. Bitte versuche es später noch einmal." +thisPostMayBeAnnoying: "Dieser Beitrag stört eventuell andere Benutzer." +thisPostMayBeAnnoyingHome: "Zur Startseite schicken" +thisPostMayBeAnnoyingCancel: "Abbrechen" +thisPostMayBeAnnoyingIgnore: "Trotzdem schicken" +collapseRenotes: "Bereits gesehene Renotes verkürzt anzeigen" +internalServerError: "Serverinterner Fehler" +internalServerErrorDescription: "Im Server ist ein unerwarteter Fehler aufgetreten." +copyErrorInfo: "Fehlerdetails kopieren" _achievements: earnedAt: "Freigeschaltet am" _types: @@ -1491,14 +1485,14 @@ _tutorial: step8_3: "Diese Einstellung kannst du jederzeit ändern." _2fa: alreadyRegistered: "Du hast bereits ein Gerät für Zwei-Faktor-Authentifizierung registriert." - registerDevice: "Neues Gerät registrieren" - registerKey: "Neuen Sicherheitsschlüssel registrieren" step1: "Installiere zuerst eine Authentifizierungsapp (z.B. {a} oder {b}) auf deinem Gerät." step2: "Dann, scanne den angezeigten QR-Code mit deinem Gerät." step2Url: "Nutzt du ein Desktopprogramm kannst du alternativ diese URL eingeben:" step3: "Gib zum Abschluss den Token ein, der von deiner App angezeigt wird." step4: "Alle folgenden Anmeldungsversuche werden ab sofort die Eingabe eines solchen Tokens benötigen." securityKeyInfo: "Du kannst neben Fingerabdruck- oder PIN-Authentifizierung auf deinem Gerät auch Anmeldung mit Hilfe eines FIDO2-kompatiblen Hardware-Sicherheitsschlüssels einrichten." + removeKeyConfirm: "Das Backup {name} löschen?" + renewTOTPCancel: "Nein, danke" _permissions: "read:account": "Deine Benutzerkontoinformationen lesen" "write:account": "Deine Benutzerkontoinformationen bearbeiten" @@ -1547,7 +1541,6 @@ _antennaSources: homeTimeline: "Notizen von Benutzern, denen gefolgt wird" users: "Notizen von einem oder mehreren angegebenen Benutzern" userList: "Notizen von allen Benutzern einer Liste" - userGroup: "Notizen von allen Benutzern einer Gruppe" _weekday: sunday: "Sonntag" monday: "Montag" @@ -1622,8 +1615,6 @@ _visibility: followersDescription: "Nur für Follower sichtbar" specified: "Direkt" specifiedDescription: "Nur für bestimmte Benutzer sichtbar" - localOnly: "Nur Lokal" - localOnlyDescription: "Unsichtbar für Benutzer anderer Instanzen" _postForm: replyPlaceholder: "Dieser Notiz antworten …" quotePlaceholder: "Diese Notiz zitieren …" @@ -1761,12 +1752,9 @@ _notification: youGotReply: "{name} hat dir geantwortet" youGotQuote: "{name} hat dich zitiert" youRenoted: "Renote deiner Notiz von {name}" - youGotMessagingMessageFromUser: "{name} hat dir eine Chatnachricht gesendet" - youGotMessagingMessageFromGroup: "In die Gruppe {name} wurde eine Chatnachricht gesendet" youWereFollowed: "ist dir gefolgt" youReceivedFollowRequest: "Du hast eine Follow-Anfrage erhalten" yourFollowRequestAccepted: "Deine Follow-Anfrage wurde akzeptiert" - youWereInvitedToGroup: "{userName} hat dich in eine Gruppe eingeladen" pollEnded: "Umfrageergebnisse sind verfügbar" unreadAntennaNote: "Antenne {name}" emptyPushNotificationMessage: "Push-Benachrichtigungen wurden aktualisiert" @@ -1782,7 +1770,6 @@ _notification: pollEnded: "Ende von Umfragen" receiveFollowRequest: "Erhaltene Follow-Anfragen" followRequestAccepted: "Akzeptierte Follow-Anfragen" - groupInvited: "Erhaltene Gruppeneinladungen" app: "Benachrichtigungen von Apps" _actions: followBack: "folgt dir nun auch" diff --git a/locales/el-GR.yml b/locales/el-GR.yml index 889be05578..0721ba6e99 100644 --- a/locales/el-GR.yml +++ b/locales/el-GR.yml @@ -230,21 +230,13 @@ moderator: "Συντονιστής" moderation: "Συντονισμός" cacheClear: "ΕκκαθάÏιση Ï€ÏοσωÏινής μνήμης" markAsReadAllNotifications: "Όλες οι ειδοποιήσεις διαβάστηκαν" -group: "Ομάδα" -groups: "Ομάδες" -createGroup: "ΔημιουÏγία ομάδας" -ownedGroups: "Οι ομάδες σας" -groupName: "Όνομα ομάδας" members: "ΜÎλη" transfer: "ΜεταφοÏά" -messagingWithUser: "Ιδιωτική συνομιλία" -messagingWithGroup: "Ομαδική συνομιλία" title: "Τίτλος" text: "Κείμενο" enable: "ΕνεÏγοποίηση" next: "Επόμενο" noteOf: "Σημείωμα από {user}" -inviteToGroup: "Î Ïόσκληση στην ομάδα" quoteAttached: "ΠαÏάθεση" signinRequired: "ΠαÏακαλοÏμε δημιουÏγήστε λογαÏιασμό ή συνδεθείτε Ï€Ïιν συνεχίσετε" category: "ΚατηγοÏία" @@ -337,7 +329,6 @@ _antennaSources: homeTimeline: "Σημειώματα από μÎλη που ακολουθείτε" users: "Σημειώματα από συγκεκÏιμÎνα μÎλη" userList: "Σημειώματα από καθοÏισμÎνη λίστα μελών" - userGroup: "Σημειώματα από μÎλη καθοÏισμÎνης ομάδας" _widgets: profile: "Î Ïοφίλ" instanceInfo: "ΠληÏοφοÏίες του instance" @@ -382,7 +373,6 @@ _pages: blocks: image: "Εικόνες" _notification: - youGotMessagingMessageFromUser: "{name} σάς Îστειλε Îνα μήνυμα συνομιλίας" youWereFollowed: "σε ακολοÏθησε" _types: follow: "ÎÎοι ακόλουθοι" diff --git a/locales/en-US.yml b/locales/en-US.yml index beb5242b1c..1ee5620916 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -103,6 +103,8 @@ renoted: "Renoted." cantRenote: "This post can't be renoted." cantReRenote: "A renote can't be renoted." quote: "Quote" +inChannelRenote: "Channel-only Renote" +inChannelQuote: "Channel-only Quote" pinnedNote: "Pinned note" pinned: "Pin to profile" you: "You" @@ -391,13 +393,10 @@ about: "About" aboutMisskey: "About Misskey" administrator: "Administrator" token: "Token" -twoStepAuthentication: "Two-factor authentication" moderator: "Moderator" moderation: "Moderation" nUsersMentioned: "Mentioned by {n} users" securityKey: "Security key" -securityKeyName: "Key name" -registerSecurityKey: "Register a security key" lastUsed: "Last used" unregister: "Unregister" passwordLessLogin: "Password-less login" @@ -415,24 +414,15 @@ markAsReadAllTalkMessages: "Mark all messages as read" help: "Help" inputMessageHere: "Enter message here" close: "Close" -group: "Group" -groups: "Groups" -createGroup: "Create a group" -ownedGroups: "Owned Groups" -joinedGroups: "Joined groups" invites: "Invites" -groupName: "Group name" members: "Members" transfer: "Transfer" -messagingWithUser: "Private chat" -messagingWithGroup: "Group chat" title: "Title" text: "Text" enable: "Enable" next: "Next" retype: "Enter again" noteOf: "Note by {user}" -inviteToGroup: "Invite to group" quoteAttached: "Quote" quoteQuestion: "Append as quote?" noMessagesYet: "No messages yet" @@ -454,21 +444,17 @@ passwordMatched: "Matches" passwordNotMatched: "Does not match" signinWith: "Sign in with {x}" signinFailed: "Unable to sign in. The entered username or password is incorrect." -tapSecurityKey: "Tap your security key" or: "Or" language: "Language" uiLanguage: "User interface language" -groupInvited: "You've been invited to a group" aboutX: "About {x}" emojiStyle: "Emoji style" native: "Native" disableDrawer: "Don't use drawer-style menus" -youHaveNoGroups: "You have no groups" -joinOrCreateGroup: "Get invited to a group or create your own." noHistory: "No history available" signinHistory: "Login history" enableAdvancedMfm: "Enable advanced MFM" -enableAnimatedMfm: "Enable MFM with animation" +enableAnimatedMfm: "Enable animated MFM" doing: "Processing..." category: "Category" tags: "Tags" @@ -819,6 +805,7 @@ lastCommunication: "Last communication" resolved: "Resolved" unresolved: "Unresolved" breakFollow: "Remove follower" +breakFollowConfirm: "Are you sure want to remove follower?" itsOn: "Enabled" itsOff: "Disabled" emailRequiredForSignup: "Require email address for sign-up" @@ -838,8 +825,6 @@ deleteAccountConfirm: "This will irreversibly delete your account. Proceed?" incorrectPassword: "Incorrect password." voteConfirm: "Confirm your vote for \"{choice}\"?" hide: "Hide" -leaveGroup: "Leave group" -leaveGroupConfirm: "Are you sure you want to leave \"{name}\"?" useDrawerReactionPickerForMobile: "Display reaction picker as drawer on mobile" welcomeBackWithName: "Welcome back, {name}" clickToFinishEmailVerification: "Please click [{ok}] to complete email verification." @@ -951,6 +936,10 @@ thisPostMayBeAnnoying: "This note may annoy others." thisPostMayBeAnnoyingHome: "Post to home timeline" thisPostMayBeAnnoyingCancel: "Cancel" thisPostMayBeAnnoyingIgnore: "Post anyway" +collapseRenotes: "Collapse renotes you've already seen" +internalServerError: "Internal Server Error" +internalServerErrorDescription: "The server has run into an unexpected error." +copyErrorInfo: "Copy error details" _achievements: earnedAt: "Unlocked at" _types: @@ -1159,7 +1148,7 @@ _achievements: description: "You've clicked here" _justPlainLucky: title: "Just Plain Lucky" - description: "Has a chance to be obtained with a probability of 0.01% every 10 seconds" + description: "Has a chance to be obtained with a probability of 0.005% every 10 seconds" _setNameToSyuilo: title: "God Complex" description: "Set your name to \"syuilo\"" @@ -1497,14 +1486,14 @@ _tutorial: step8_3: "You can always change this setting later." _2fa: alreadyRegistered: "You have already registered a 2-factor authentication device." - registerDevice: "Register a new device" - registerKey: "Register a security key" step1: "First, install an authentication app (such as {a} or {b}) on your device." step2: "Then, scan the QR code displayed on this screen." step2Url: "You can also enter this URL if you're using a desktop program:" step3: "Enter the token provided by your app to finish setup." step4: "From now on, any future login attempts will ask for such a login token." securityKeyInfo: "Besides fingerprint or PIN authentication, you can also setup authentication via hardware security keys that support FIDO2 to further secure your account." + removeKeyConfirm: "Delete the {name} backup?" + renewTOTPCancel: "Not now" _permissions: "read:account": "View your account information" "write:account": "Edit your account information" @@ -1553,7 +1542,6 @@ _antennaSources: homeTimeline: "Notes from followed users" users: "Notes from specific users" userList: "Notes from a specified list of users" - userGroup: "Notes from users in a specified group" _weekday: sunday: "Sunday" monday: "Monday" @@ -1628,8 +1616,6 @@ _visibility: followersDescription: "Make visible to your followers only" specified: "Direct" specifiedDescription: "Make visible for specified users only" - localOnly: "Local only" - localOnlyDescription: "Not visible to remote users" _postForm: replyPlaceholder: "Reply to this note..." quotePlaceholder: "Quote this note..." @@ -1767,12 +1753,9 @@ _notification: youGotReply: "{name} replied to you" youGotQuote: "{name} quoted you" youRenoted: "Renote from {name}" - youGotMessagingMessageFromUser: "{name} sent you a chat message" - youGotMessagingMessageFromGroup: "A chat message was sent to the {name} group" youWereFollowed: "followed you" youReceivedFollowRequest: "You've received a follow request" yourFollowRequestAccepted: "Your follow request was accepted" - youWereInvitedToGroup: "{userName} invited you to a group" pollEnded: "Poll results have become available" unreadAntennaNote: "Antenna {name}" emptyPushNotificationMessage: "Push notifications have been updated" @@ -1788,7 +1771,6 @@ _notification: pollEnded: "Polls ending" receiveFollowRequest: "Received follow requests" followRequestAccepted: "Accepted follow requests" - groupInvited: "Group invitations" app: "Notifications from linked apps" _actions: followBack: "followed you back" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 093818f5a2..6f638b3369 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -391,13 +391,10 @@ about: "Información" aboutMisskey: "Sobre Misskey" administrator: "Administrador" token: "Token" -twoStepAuthentication: "Autenticación de dos factores" moderator: "Moderador" moderation: "Moderación" nUsersMentioned: "{n} usuarios mencionados" securityKey: "Clave de seguridad" -securityKeyName: "Nombre de la Clave" -registerSecurityKey: "Registrar clave de seguridad" lastUsed: "Última vez usado" unregister: "Cancelar registro" passwordLessLogin: "Iniciar sesión sin contraseña" @@ -415,24 +412,15 @@ markAsReadAllTalkMessages: "Marcar todos los chats como leÃdos" help: "Ayuda" inputMessageHere: "Escribe el mensaje aquÃ" close: "Cerrar" -group: "Grupo" -groups: "Grupos" -createGroup: "Crear grupo" -ownedGroups: "Tus" -joinedGroups: "Grupos a los que me unÃ" invites: "Invitar" -groupName: "Nombre del grupo" members: "Miembros" transfer: "Transferir" -messagingWithUser: "Chatear con usuario" -messagingWithGroup: "Chatear en grupo" title: "TÃtulo" text: "Texto" enable: "Activar" next: "Siguiente" retype: "Intentar de nuevo" noteOf: "Notas de {user}" -inviteToGroup: "Invitar al grupo" quoteAttached: "Cita añadida" quoteQuestion: "¿Quiere añadir una cita?" noMessagesYet: "Aún no hay chat" @@ -454,17 +442,13 @@ passwordMatched: "Correcto" passwordNotMatched: "Las contraseñas no son las mismas" signinWith: "Inicie sesión con {x}" signinFailed: "Autenticación fallida. Asegúrate de haber usado el nombre de usuario y contraseña correctos." -tapSecurityKey: "Toque la clave de seguridad" or: "O" language: "Idioma" uiLanguage: "Idioma de visualización de la interfaz" -groupInvited: "Invitado al grupo" aboutX: "Acerca de {x}" emojiStyle: "Estilo de emoji" native: "Nativo" disableDrawer: "No mostrar los menús en cajones" -youHaveNoGroups: "Sin grupos" -joinOrCreateGroup: "Obtenga una invitación para unirse al grupos o puede crear su propio grupo." noHistory: "No hay datos en el historial" signinHistory: "Historial de ingresos" doing: "Voy en camino" @@ -836,8 +820,6 @@ deleteAccountConfirm: "La cuenta será borrada. ¿Está seguro?" incorrectPassword: "La contraseña es incorrecta" voteConfirm: "¿Confirma su voto a {choice}?" hide: "Ocultar" -leaveGroup: "Dejar el grupo" -leaveGroupConfirm: "¿Desea salir de {name}?" useDrawerReactionPickerForMobile: "Mostrar panel de reacciones en móviles" welcomeBackWithName: "Bienvenido otra vez, {name}" clickToFinishEmailVerification: "Cliquée {ok} y verifique su correo" @@ -1489,14 +1471,14 @@ _tutorial: step8_3: "La configuración de las notificaciones puede modificarse posteriormente." _2fa: alreadyRegistered: "Ya has completado la configuración." - registerDevice: "Registrar dispositivo" - registerKey: "Registrar clave" step1: "Primero, instale en su dispositivo la aplicación de autenticación {a} o {b} u otra." step2: "Luego, escanee con la aplicación el código QR mostrado en pantalla." step2Url: "En una aplicación de escritorio se puede ingresar la siguiente URL:" step3: "Para terminar, ingrese el token mostrado en la aplicación." step4: "Ahora cuando inicie sesión, ingrese el mismo token" securityKeyInfo: "Se puede configurar el inicio de sesión usando una clave de seguridad de hardware que soporte FIDO2 o con un certificado de huella digital o con un PIN" + removeKeyConfirm: "¿Borrar el respaldo \"{name}\"?" + renewTOTPCancel: "No gracias" _permissions: "read:account": "Ver información de la cuenta" "write:account": "Editar información de la cuenta" @@ -1545,7 +1527,6 @@ _antennaSources: homeTimeline: "Notas de los usuarios que sigues" users: "Notas de un usuario o varios" userList: "Notas de los usuarios de una lista" - userGroup: "Notas de los usuarios de una grupo" _weekday: sunday: "Domingo" monday: "Lunes" @@ -1620,8 +1601,6 @@ _visibility: followersDescription: "Visible sólo para tus seguidores" specified: "Mensaje directo" specifiedDescription: "Visible sólo para los usuarios elegidos" - localOnly: "Solo local" - localOnlyDescription: "Oculto para usuarios remotos" _postForm: replyPlaceholder: "Responder a esta nota" quotePlaceholder: "Citar esta nota" @@ -1759,12 +1738,9 @@ _notification: youGotReply: "Respuesta de {name}" youGotQuote: "Citado por {name}" youRenoted: "Renotado por {name}" - youGotMessagingMessageFromUser: "{name} comenzó un chat contigo" - youGotMessagingMessageFromGroup: "Tienes un chat de {name}" youWereFollowed: "te ha seguido" youReceivedFollowRequest: "Has mandado una solicitud de seguimiento" yourFollowRequestAccepted: "Tu solicitud de seguimiento fue aceptada" - youWereInvitedToGroup: "Invitado al grupo" pollEnded: "Estan disponibles los resultados de la encuesta" unreadAntennaNote: "Antena {name}" emptyPushNotificationMessage: "Se han actualizado las notificaciones push" @@ -1780,7 +1756,6 @@ _notification: pollEnded: "La encuesta terminó" receiveFollowRequest: "Recibió una solicitud de seguimiento" followRequestAccepted: "El seguimiento fue aceptado" - groupInvited: "Invitado al grupo" app: "Notificaciones desde aplicaciones" _actions: followBack: "Te sigue de vuelta" diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index bb976334e9..de6156e39d 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -388,13 +388,10 @@ about: "Informations" aboutMisskey: "À propos de Misskey" administrator: "Administrateur" token: "Jeton" -twoStepAuthentication: "Authentification à deux facteurs" moderator: "Modérateur·rice·s" moderation: "Modérations" nUsersMentioned: "{n} utilisateur·rice·s mentionné·e·s" securityKey: "Clé de sécurité" -securityKeyName: "Nom de la clé" -registerSecurityKey: "Enregistrer une clé de sécurité" lastUsed: "Dernier utilisé" unregister: "Se désinscrire" passwordLessLogin: "Se connecter sans mot de passe" @@ -412,24 +409,15 @@ markAsReadAllTalkMessages: "Marquer toutes les discussions comme lues" help: "Aide" inputMessageHere: "Écrivez votre message ici" close: "Fermer" -group: "Groupe" -groups: "Groupes" -createGroup: "Créer un groupe" -ownedGroups: "Mes groupes" -joinedGroups: "Groupes rejoints" invites: "Invitations" -groupName: "Nom du groupe" members: "Membres" transfer: "Transférer" -messagingWithUser: "Discuter avec un·e autre utilisateur·rice" -messagingWithGroup: "Discuter avec un groupe" title: "Titre" text: "Texte" enable: "Activer" next: "Suivant" retype: "Confirmation" noteOf: "Notes de {user}" -inviteToGroup: "Inviter dans un groupe" quoteAttached: "Avec citation" quoteQuestion: "Souhaitez-vous ajouter une citation ?" noMessagesYet: "Pas encore de discussion" @@ -451,17 +439,13 @@ passwordMatched: "Les mots de passe correspondent" passwordNotMatched: "Les mots de passe ne correspondent pas" signinWith: "Se connecter avec {x}" signinFailed: "Échec d’authentification. Veuillez vérifier que votre nom d’utilisateur et mot de passe sont corrects." -tapSecurityKey: "Appuyez sur votre clé de sécurité" or: "OU" language: "Langue" uiLanguage: "Langue d’affichage de l’interface" -groupInvited: "Invité au groupe" aboutX: "À propos de {x}" emojiStyle: "Style des émojis" native: "Natif" disableDrawer: "Les menus ne s'affichent pas dans le tiroir" -youHaveNoGroups: "Vous n’avez aucun groupe" -joinOrCreateGroup: "Vous pouvez être invité·e à rejoindre des groupes existants ou créer votre propre nouveau groupe." noHistory: "Pas d'historique" signinHistory: "Historique de connexion" doing: "En cours..." @@ -831,8 +815,6 @@ deleteAccountConfirm: "Votre compte sera supprimé. Êtes vous certain ?" incorrectPassword: "Le mot de passe est incorrect." voteConfirm: "Confirmez-vous votre vote pour « {choice} » ?" hide: "Masquer" -leaveGroup: "Quitter le groupe" -leaveGroupConfirm: "Êtes vous sûr de vouloir quitter \"{name}\" ?" useDrawerReactionPickerForMobile: "Afficher le sélecteur de réactions en tant que panneau sur mobile" welcomeBackWithName: "Heureux de vous revoir, {name}" clickToFinishEmailVerification: "Veuillez cliquer sur [{ok}] afin de compléter la vérification par courriel." @@ -1171,14 +1153,14 @@ _tutorial: step8_2: "En les activant, vous recevrez des notifications pour les mentions, les réactions, les suivis, etc., même lorsque Misskey n'est pas ouvert." _2fa: alreadyRegistered: "Configuration déjà achevée." - registerDevice: "Ajouter un nouvel appareil" - registerKey: "Enregistrer une clef" step1: "Tout d'abord, installez une application d'authentification, telle que {a} ou {b}, sur votre appareil." step2: "Ensuite, scannez le code QR affiché sur l’écran." step2Url: "Vous pouvez également saisir cette URL si vous utilisez un programme de bureau :" step3: "Entrez le jeton affiché sur votre application pour compléter la configuration." step4: "À partir de maintenant, ce même jeton vous sera demandé à chacune de vos connexions." securityKeyInfo: "Vous pouvez configurer l'authentification WebAuthN pour sécuriser davantage le processus de connexion grâce à une clé de sécurité matérielle qui prend en charge FIDO2, ou bien en configurant l'authentification par empreinte digitale ou par code PIN sur votre appareil." + removeKeyConfirm: "Voulez-vous supprimer {name} ?" + renewTOTPCancel: "Pas maintenant" _permissions: "read:account": "Afficher les informations du compte" "write:account": "Mettre à jour les informations de votre compte" @@ -1224,7 +1206,6 @@ _antennaSources: homeTimeline: "Notes venant des utilisateur·rice·s auxquel·les je suis abonné" users: "Notes venant de la part d’utilisateur·rice·s précis" userList: "Notes venant d’une liste spécifique" - userGroup: "Notes venant d’utilisateur·rice·s du groupe spécifié" _weekday: sunday: "Dimanche" monday: "Lundi" @@ -1296,8 +1277,6 @@ _visibility: followersDescription: "Publier à vos abonné·e·s uniquement" specified: "Direct" specifiedDescription: "Publier uniquement aux utilisateur·rice·s mentionné·e·s" - localOnly: "Local seulement" - localOnlyDescription: "Caché pour les utilisateurs distant" _postForm: replyPlaceholder: "Répondre à cette note ..." quotePlaceholder: "Citez cette note ..." @@ -1425,12 +1404,9 @@ _notification: youGotReply: "Réponse de {name}" youGotQuote: "Cité·e par {name}" youRenoted: "{name} vous a Renoté" - youGotMessagingMessageFromUser: "{name} vous envoyé un message" - youGotMessagingMessageFromGroup: "Un message a été envoyé au groupe {name}" youWereFollowed: "Vous suit" youReceivedFollowRequest: "Vous avez reçu une demande d’abonnement" yourFollowRequestAccepted: "Votre demande d’abonnement a été accepté" - youWereInvitedToGroup: "Invité·e au groupe" pollEnded: "Les résultats du sondage sont disponibles" unreadAntennaNote: "Antenne {name}" emptyPushNotificationMessage: "Les notifications push ont été mises à jour" @@ -1445,7 +1421,6 @@ _notification: pollEnded: "Sondages se cloturant" receiveFollowRequest: "Demande d'abonnement reçue" followRequestAccepted: "Demande d'abonnement acceptée" - groupInvited: "Invitation à un groupe" app: "Notifications provenant des apps" _actions: followBack: "Suivre" diff --git a/locales/id-ID.yml b/locales/id-ID.yml index 002eead689..d3d1a16219 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -84,7 +84,7 @@ error: "Galat" somethingHappened: "Terjadi kesalahan" retry: "Coba lagi" pageLoadError: "Gagal memuat halaman." -pageLoadErrorDescription: "Umumnya disebabkan jaringan atau tembolok perambah. Cobalah bersihkan tembolok peramban lalu tunggu sesaat sebelum mencoba kembali." +pageLoadErrorDescription: "Umumnya disebabkan jaringan atau tembolok peramban. Cobalah bersihkan tembolok peramban lalu tunggu sesaat sebelum mencoba kembali." serverIsDead: "Tidak ada respon dari peladen. Mohon tunggu dan coba beberapa saat lagi." youShouldUpgradeClient: "Untuk melihat halaman ini, mohon muat ulang untuk memutakhirkan klienmu." enterListName: "Masukkan nama daftar" @@ -103,6 +103,8 @@ renoted: "Telah direnote" cantRenote: "Postingan ini tidak dapat direnote" cantReRenote: "Renote tidak dapat direnote" quote: "Kutip" +inChannelRenote: "Hanya renote dalam kanal" +inChannelQuote: "Hanya kutip dalam kanal" pinnedNote: "Catatan yang disematkan" pinned: "Sematkan ke profil" you: "Kamu" @@ -129,6 +131,7 @@ unblockConfirm: "Apakah kamu yakin ingin membuka blokir akun ini?" suspendConfirm: "Apakah kamu yakin ingin membekukan akun ini?" unsuspendConfirm: "Apakah kamu yakin ingin membuka pembekuan akun ini?" selectList: "Pilih daftar" +selectChannel: "Pilih kanal" selectAntenna: "Pilih Antena" selectWidget: "Pilih gawit" editWidgets: "Sunting gawit" @@ -256,6 +259,8 @@ noMoreHistory: "Tidak ada sejarah lagi" startMessaging: "Mulai mengirim pesan" nUsersRead: "Dibaca oleh {n}" agreeTo: "Saya setuju kepada {0}" +agreeBelow: "Saya setuju dengan di bawah ini" +basicNotesBeforeCreateAccount: "Catatan penting" tos: "Syarat dan ketentuan" start: "Mulai" home: "Beranda" @@ -388,13 +393,10 @@ about: "Informasi" aboutMisskey: "Tentang Misskey" administrator: "Admin" token: "Token" -twoStepAuthentication: "Otentikasi dua faktor" moderator: "Moderator" moderation: "Moderasi" nUsersMentioned: "{n} pengguna disebut" securityKey: "Kunci keamanan" -securityKeyName: "Nama kunci" -registerSecurityKey: "Daftarkan kunci keamanan" lastUsed: "Terakhir digunakan" unregister: "Batalkan pendaftaran" passwordLessLogin: "Setel login tanpa kata sandi" @@ -412,24 +414,15 @@ markAsReadAllTalkMessages: "Tandai semua pesan telah dibaca" help: "Bantuan" inputMessageHere: "Ketik pesan disini" close: "Tutup" -group: "Grup" -groups: "Grup" -createGroup: "Buat grup" -ownedGroups: "Grup yang dimiliki" -joinedGroups: "Grup yang diikuti" invites: "Undang" -groupName: "Nama grup" members: "Anggota" transfer: "Transfer" -messagingWithUser: "Obrolan dengan pengguna lain" -messagingWithGroup: "Obrolan di dalam grup" title: "Judul" text: "Teks" enable: "Aktifkan" next: "Selanjutnya" retype: "Masukkan ulang" noteOf: "Catatan milik {user}" -inviteToGroup: "Undang ke grup" quoteAttached: "Dikutip" quoteQuestion: "Apakah kamu ingin menambahkan kutipan?" noMessagesYet: "Tidak ada pesan" @@ -451,19 +444,17 @@ passwordMatched: "Kata sandi sama" passwordNotMatched: "Kata sandi tidak sama" signinWith: "Masuk dengan {x}" signinFailed: "Tidak dapat masuk. Nama pengguna atau kata sandi yang kamu masukkan salah." -tapSecurityKey: "Ketuk kunci keamanan kamu" or: "atau" language: "Bahasa" uiLanguage: "Bahasa antarmuka pengguna" -groupInvited: "Telah diundang ke grup" aboutX: "Tentang {x}" emojiStyle: "Gaya emoji" native: "Native" disableDrawer: "Jangan gunakan menu bergaya laci" -youHaveNoGroups: "Kamu tidak memiliki grup" -joinOrCreateGroup: "Bergabunglah dengan grup atau kamu dapat membuat grupmu sendiri." noHistory: "Tidak ada riwayat" signinHistory: "Riwayat masuk" +enableAdvancedMfm: "Nyalakan MFM tingkat lanjut" +enableAnimatedMfm: "Nyalakan animasi MFM" doing: "Sedang berkerja..." category: "Kategori" tags: "Tandai" @@ -833,8 +824,6 @@ deleteAccountConfirm: "Akun akan dihapus. Apakah kamu yakin?" incorrectPassword: "Kata sandi salah." voteConfirm: "Konfirmasi suara kamu untuk ({choice})?" hide: "Sembunyikan" -leaveGroup: "Keluar grup" -leaveGroupConfirm: "Apakah kamu yakin untuk keluar dari \"{name}\"?" useDrawerReactionPickerForMobile: "Tampilkan bilah reaksi sebagai laci di ponsel" welcomeBackWithName: "Selamat datang kembali, {name}." clickToFinishEmailVerification: "Mohon klik [{ok}] untuk menyelesaikan verifikasi email." @@ -860,6 +849,8 @@ failedToFetchAccountInformation: "Gagal untuk mendapatkan informasi akun" rateLimitExceeded: "Batas sudah terlampaui" cropImage: "potong gambar" cropImageAsk: "Ingin memotong gambar?" +cropYes: "Potong" +cropNo: "Gunakan apa adanya" file: "Berkas" recentNHours: "{n} jam terakhir" recentNDays: "{n} hari terakhir" @@ -926,10 +917,87 @@ didYouLikeMisskey: "Apakah kamu mulai menyukai Misskey?" pleaseDonate: "{host} menggunakan perangkat lunak bebas yaitu Misskey. Kami sangat mengapresiasi sekali donasi dari kamu agar pengembangan Misskey tetap dapat berlanjut!" roles: "Peran" role: "Peran" +normalUser: "Pengguna umum" +undefined: "Tak terdefinisi" +assign: "Tetapkan\n" +unassign: "Batalkan penetapan" color: "Warna" +manageCustomEmojis: "Kelola Emoji Kustom" +youCannotCreateAnymore: "Kamu melewati batas pembuatan." +cannotPerformTemporary: "Sementara Tidak Tersedia" +cannotPerformTemporaryDescription: "Aksi ini tidak dapat dilakukan sementara karena melewati batas eksekusi. Mohon tunggu sejenak dan coba lagi." +preset: "Prasetel" +selectFromPresets: "Pilih dari prasetel" +achievements: "Pencapaian" +gotInvalidResponseError: "Respon peladen tidak valid" +gotInvalidResponseErrorDescription: "Peladen tidak dapat dijangkau atau sedang dalam perawatan. Mohon coba lagi nanti." +thisPostMayBeAnnoying: "Catatan ini mungkin dapat mengganggu orang lain." +thisPostMayBeAnnoyingHome: "Catat ke linimasa beranda" +thisPostMayBeAnnoyingCancel: "Batalkan" +thisPostMayBeAnnoyingIgnore: "Tetap catat" +collapseRenotes: "Tutup renote yang sudah kamu lihat" +internalServerError: "Kesalahan internal peladen" +internalServerErrorDescription: "Peladen sedang mengalami galat tak terduga" +copyErrorInfo: "Salin detil galat" _achievements: + earnedAt: "Terbuka pada" _types: + _notes1: + title: "Cus, baru gabung Misskey nih!" + description: "Catat catatan pertama kamu" + flavor: "Selamat bersenang-senang dengan Misskey!" + _notes10: + title: "Beberapa catatan" + description: "Catat 10 catatan" + _notes100: + title: "Banyak catatan" + description: "Catat 100 catatan" + _notes500: + title: "Tertumpuk catatan" + description: "Catat 500 catatan" + _notes1000: + title: "Gunung catatan" + description: "Catat 1000 catatan" + _notes5000: + title: "Luapan catatan" + description: "Catat 5000 catatan" + _notes10000: + title: "Catatan super" + description: "Catat 10 ribu catatan" + _notes20000: + title: "Butuh... banyak... catatan..." + description: "Catat 20 ribu catatan" + _notes30000: + title: "Catat, catat, catat !" + description: "Catat 30 ribu catatan" + _notes40000: + title: "Pabrik catatan" + description: "Catat 40 ribu catatan" + _notes50000: + title: "Planet catatan" + description: "Catat 50 ribu catatan" + _notes60000: + title: "Kuasar catatan" + description: "Catat 60 ribu catatan" + _notes70000: + title: "Lubang hitam catatan" + description: "Catat 70 ribu catatan" + _notes80000: + title: "Galaksi catatan" + description: "Catat 80 ribu catatan" + _notes90000: + title: "Semesta catatan" + description: "Catat 90 ribu catatan" + _notes100000: + title: "ALL YOUR NOTE ARE BELONG TO US" + description: "Catat 100 ribu catatan" + flavor: "Banyak bacot ya kamu." + _login3: + title: "Pemula I" + description: "Login selama 3 hari" + flavor: "Mulai hari ini, panggil gue Misskist" _login7: + title: "Pemula II" description: "Login selama 7 hari" flavor: "Sudah mulai terbiasa?" _login15: @@ -1002,7 +1070,80 @@ _achievements: _following100: title: "100 Teman" description: "Ikuti 100 pengguna lain" + _following300: + title: "Kelebihan teman" + description: "Mengikuti 300 pengguna lain" + _followers1: + title: "Pengikut pertama" + description: "Dapatkan 1 pengikut" + _followers10: + title: "Ikuti aku!" + description: "Dapatkan 10 pengikut" + _followers50: + title: "Rame-rame" + description: "Dapatkan 50 pengikut" + _followers100: + title: "Terkenal" + description: "Dapatkan 100 pengikut" + _followers300: + title: "Mohon antri satu baris" + description: "Dapatkan 300 pengikut" + _followers500: + title: "Stasiun Informasi" + description: "Dapatkan 500 pengikut" + _followers1000: + title: "Influencer" + description: "Dapatkan 1000 pengikut" + _collectAchievements30: + title: "Kolektor pencapaian" + description: "Dapatkan 30 pencapaian" + _viewAchievements3min: + title: "Suka Pencapaian" + description: "Lugat daftar pencapaianmu setidaknya 3 menit" + _iLoveMisskey: + title: "I Love Misskey" + description: "Catat \"I ⤠#Misskey\"" + flavor: "Tim pengembang misskey sangat mengapresiasi dukungan kamu!" + _foundTreasure: + title: "Berburu Harta Karun" + description: "Kamu telah menemukan harta karun tersembunyi" + _client30min: + title: "Istirahat pendek" + description: "Habiskan waktu 30 menit di Misskey" + _noteDeletedWithin1min: + title: "Eh, salah coy!" + description: "Hapus catatan kurang dari semenit kamu catat" + _postedAtLateNight: + title: "Nokturnal" + description: "Catat catatan di tengah malam hari" + flavor: "Udah waktunya boboq." + _postedAt0min0sec: + title: "Jam ngomong" + description: "Catat catatan di jam 00.00" + flavor: "Tik Tok Tik Toeeeng" + _selfQuote: + title: "Rujukan mandiri" + description: "Kutip catatanmu sendiri" + _htl20npm: + title: "Linimasa mengalir" + description: "Memiliki linimasa beranda dengan kecepatan melebihi 20 cpm (catatan per menit)" + _viewInstanceChart: + title: "Analis" + description: "Lihat bagan instansimu" + _outputHelloWorldOnScratchpad: + title: "Halo, dunia!" + description: "Munculkan \"hello world\" di Scratchpad" + _open3windows: + title: "Jendela ganda" + description: "Memiliki setidaknya 3 jendela yang terbuka secara bersamaan" + _driveFolderCircularReference: + title: "Referensi Siklus" + description: "Mencoba membuat folder bersarang rekursif di Drive" + _reactWithoutRead: + title: "Beneran udah dibaca?" + description: "Mereaksi catatan dengan 100 karakter panjangnya dalam 3 detik setelah dicatat" _clickedClickHere: + title: "Klik di sini" description: "Kamu telah mengeklik disini" _justPlainLucky: title: "Lagi Beruntung" @@ -1025,6 +1166,7 @@ _achievements: _loggedInOnNewYearsDay: title: "Selamat Tahun Baru!" description: "Login di hari pertama tahun baru" + flavor: "Untuk tahun baru yang berkah bagi instansi ini" _cookieClicked: title: "Permainan dimana kamu mengeklik kue" description: "Mengeklik kue" @@ -1053,6 +1195,9 @@ _role: baseRole: "Templat peran" useBaseValue: "Gunakan nilai templat peran" chooseRoleToAssign: "Pilih peran yang ditugaskan" + iconUrl: "URL ikon" + asBadge: "Tampilkan sebagai lencana" + descriptionOfAsBadge: "Ikon peran ini akan ditampilkan bersebelahan dengan username pengguna yang memiliki peran ini jika dinyalakan." canEditMembersByModerator: "Perbolehkan moderator untuk menyunting daftar anggota untuk peran ini" descriptionOfCanEditMembersByModerator: "Ketika dinyalakan, moderator beserta administrator dapat menugaskan ataupun mencabut pengguna ke peran ini. Ketika dimatikan, hanya administrator saja yang dapat menugaskan pengguna ke peran ini." priority: "Prioritas" @@ -1068,6 +1213,36 @@ _role: canManageCustomEmojis: "Dapat mengelola Emoji kustom" driveCapacity: "Kapasitas Drive" pinMax: "Jumlah maksimal catatan yang disematkan" + antennaMax: "Jumlah maksimum antena" + wordMuteMax: "Jumlah maksimum karakter yang diperbolehkan dalam membisukan kata" + webhookMax: "Jumlah maksimum Webhook" + clipMax: "Jumlah maksimum Klip" + noteEachClipsMax: "Jumlah maksimum catatan di dalam Klip" + userListMax: "Jumlah maksimum daftar pengguna" + userEachUserListsMax: "Jumlah maksimum pengguna dalam dsftar pengguna" + rateLimitFactor: "Batas kecepatan" + descriptionOfRateLimitFactor: "Batas kecepatan yang rendah tidak begitu membatasi, batas kecepatan tinggi lebih membatasi. " + canHideAds: "Dapat menyembunyikan iklan" + _condition: + isLocal: "Pengguna lokal" + isRemote: "Pengguna remote" + createdLessThan: "Telah berlalu kurang dari X sejak pembuatan akun" + createdMoreThan: "Telah berlalu lebih dari X sejak pembuatan akun" + followersLessThanOrEq: "Memiliki pengikut X atau kurang dari tersebut" + followersMoreThanOrEq: "Memiliki pengikut X atau lebih dari tersebut" + followingLessThanOrEq: "Mengikuti X pengguna atau kurang dari itu" + followingMoreThanOrEq: "Mengikuti X pengguna atau lebih dari itu" + and: "Kondisi-AND" + or: "Kondisi-OR" + not: "Kondisi-NOT" +_sensitiveMediaDetection: + description: "Mengurangi usaha moderasi server dengan mengenali media NSFW srcara otomatis menggunakan Machine Learning. Fungsi ini akan sedikit menaikkan beban peladen." + sensitivity: "Sensitivitas deteksi" + sensitivityDescription: "Mengurangi sensitivitas akan mengurangi misdeteksi (false positive) sedangkan meningkatkannya akan menambah misdeteksi (false positive)." + setSensitiveFlagAutomatically: "Tandai sebagai NSFW" + setSensitiveFlagAutomaticallyDescription: "Hasil dari deteksi internal akan dipertahankan meskipun fungsi ini dimatikan." + analyzeVideos: "Nyalakan analisis terhadap video" + analyzeVideosDescription: "Analisa video sebagai tambahan dari gambar. Ini akan sedikit meningkatkan beban ke peladen." _emailUnavailable: used: "Alamat surel ini telah digunakan" format: "Format tidak valid." @@ -1111,6 +1286,24 @@ _plugin: install: "Memasang plugin" installWarn: "Mohon jangan memasang plugin yang tidak dapat dipercayai." manage: "Manajemen plugin" +_preferencesBackups: + list: "Cadangan yang dibuat" + saveNew: "Simpan cadangan baru" + loadFile: "Muat dari berkas" + apply: "Terapkan pada perangkat ini" + save: "Simpan perubahan" + inputName: "Mohon masukkan nama untuk cadangan ini" + cannotSave: "Gagal menyimpan" + nameAlreadyExists: "Cadangan bernama \"{name}\" sudah ada. Mohon gunakan nama lain." + applyConfirm: "Apakah kamu yakin untuk menerapkan cadangan \"{name}\" ke perangkat ini? Pengaturan yang sudah ada di perangkat ini nantinya akan ditimpa." + saveConfirm: "Simpan cadangan sebagai {name}?" + deleteConfirm: "Hapus cadangan {name}?" + renameConfirm: "Ganti cadangan ini dari \"{old}\" ke \"{new}\"?" + noBackups: "Tidak ada cadangan. Kamu dapat mencadangkan pengaturanmu di peladen ini dengan menggunakan \"Buat cadangan baru\"." + createdAt: "Dibuat pada: {date} {time}" + updatedAt: "Diperbarui pada: {date} {time}" + cannotLoad: "Gagal memuat" + invalidFile: "Format berkas tidak valid" _registry: scope: "Lingkup" key: "Kunci" @@ -1287,17 +1480,19 @@ _tutorial: step7_1: "Yay, Selamat! Kamu sudah menyelesaikan tutorial dasar Misskey." step7_2: "Jika kamu ingin mempelajari lebih lanjut tentang Misskey, cobalah berkunjung ke bagian {help}." step7_3: "Semoga berhasil dan bersenang-senanglah! 🚀" + step8_1: "Yang terakhir, apakah kamu ingin menyalakam pemberitahuan push?" + step8_2: "Menyalakan ini akan memungkinkan kamu menerima pemberitahuan untuk sebutan, reaksi, ikuti, dll. Bahkan ketika Misskey sedang tidak dibuka." step8_3: "Kamu dapat mengganti pengaturan ini nanti." _2fa: alreadyRegistered: "Kamu telah mendaftarkan perangkat otentikasi dua faktor." - registerDevice: "Daftarkan perangkat baru" - registerKey: "Daftarkan kunci keamanan baru" step1: "Pertama, pasang aplikasi otentikasi (seperti {a} atau {b}) di perangkat kamu." step2: "Lalu, pindai kode QR yang ada di layar." step2Url: "Di aplikasi desktop, masukkan URL berikut:" step3: "Masukkan token yang telah disediakan oleh aplikasimu untuk menyelesaikan pemasangan." step4: "Mulai sekarang, upaya login apapun akan meminta token login dari aplikasi otentikasi kamu." securityKeyInfo: "Kamu dapat memasang otentikasi WebAuthN untuk mengamankan proses login lebih lanjut dengan tidak hanya perangkat keras kunci keamanan yang mendukung FIDO2, namun juga sidik jari atau otentikasi PIN pada perangkatmu." + removeKeyConfirm: "Hapus cadangan {name}?" + renewTOTPCancel: "Tidak sekarang." _permissions: "read:account": "Lihat informasi akun" "write:account": "Sunting informasi akun" @@ -1332,18 +1527,20 @@ _permissions: "read:gallery-likes": "Lihat daftar postingan galeri yang disukai" "write:gallery-likes": "Sunting daftar postingan galeri yang disukai" _auth: + shareAccessTitle: "Mendapatkan ijin akses aplikasi" shareAccess: "Apakah kamu ingin mengijinkan \"{name}\" untuk mengakses akun ini?" shareAccessAsk: "Apakah kamu ingin mengijinkan aplikasi ini untuk mengakses akun kamu?" + permission: "{name} meminta ijin berikut" permissionAsk: "Aplikasi ini membutuhkan beberapa ijin, yaitu:" pleaseGoBack: "Mohon kembali ke aplikasi kamu" callback: "Mengembalikan kamu ke aplikasi" denied: "Akses ditolak" + pleaseLogin: "Mohon masuk untuk otorisasi aplikasi." _antennaSources: all: "Semua catatan" homeTimeline: "Catatan dari pengguna yang diikuti" users: "Catatan dari pengguna tertentu" userList: "Catatan dari daftar tertentu" - userGroup: "Catatan dari pengguna dalam grup yang ditentukan" _weekday: sunday: "Minggu" monday: "Senin" @@ -1418,8 +1615,6 @@ _visibility: followersDescription: "Catat ke pengikut saja" specified: "Langsung" specifiedDescription: "Catat ke pengguna yang ditentukan saja" - localOnly: "Hanya lokal" - localOnlyDescription: "Hanya dapat dilihat di instansi lokal" _postForm: replyPlaceholder: "Balas ke catatan ini..." quotePlaceholder: "Kutip catatan ini..." @@ -1557,12 +1752,9 @@ _notification: youGotReply: "{name} membalas kamu" youGotQuote: "{name} mengutip kamu" youRenoted: "{name} me-renote kamu" - youGotMessagingMessageFromUser: "{name} mengirimi kamu pesan" - youGotMessagingMessageFromGroup: "Sebuah pesan telah dikirim ke grup {name}" youWereFollowed: "Mengikuti kamu" youReceivedFollowRequest: "Kamu menerima permintaan mengikuti" yourFollowRequestAccepted: "Permintaan mengikuti kamu telah diterima" - youWereInvitedToGroup: "Telah diundang ke grup" pollEnded: "Hasil Kuesioner telah keluar" unreadAntennaNote: "Antena {name}" emptyPushNotificationMessage: "Pembaruan notifikasi dorong" @@ -1578,7 +1770,6 @@ _notification: pollEnded: "Jajak pendapat berakhir" receiveFollowRequest: "Permintaan mengikuti diterima" followRequestAccepted: "Permintaan mengikuti disetujui" - groupInvited: "Diundang ke grup" app: "Pemberitahuan dari aplikasi" _actions: followBack: "Ikuti Kembali" diff --git a/locales/it-IT.yml b/locales/it-IT.yml index fbee5d9719..0ce65eea85 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -11,7 +11,7 @@ password: "Password" forgotPassword: "Hai dimenticato la password?" fetchingAsApObject: "Recuperando dal Fediverso..." ok: "OK" -gotIt: "Ho capito" +gotIt: "ok!" cancel: "Annulla" noThankYou: "No grazie" enterUsername: "Inserisci un nome utente" @@ -46,7 +46,7 @@ copyContent: "Copia il contenuto" copyLink: "Copia il link" delete: "Elimina" deleteAndEdit: "Elimina e modifica" -deleteAndEditConfirm: "Vuoi davvero cancellare questa nota e scriverla di nuovo? Verrano eliminate anche tutte le reazioni, Rinote e risposte collegate." +deleteAndEditConfirm: "Vuoi davvero cancellare questa nota e scriverla di nuovo? Verranno eliminate anche tutte le reazioni, rinote e risposte collegate." addToList: "Aggiungi alla lista" sendMessage: "Invia messaggio" copyRSS: "Copia RSS" @@ -75,7 +75,7 @@ lists: "Liste" noLists: "Nessuna lista" note: "Nota" notes: "Note" -following: "Follows" +following: "LÇ segui" followers: "Followers" followsYou: "Ti segue" createList: "Aggiungi una nuova lista" @@ -84,12 +84,12 @@ error: "Errore" somethingHappened: "Si è verificato un problema" retry: "Riprova" pageLoadError: "Caricamento pagina non riuscito. " -pageLoadErrorDescription: "Questo viene normalmente causato dalla rete o dalla cache del browser. Si prega di pulire la cache, o di attendere e riprovare più tardi." +pageLoadErrorDescription: "Questo problema viene normalmente causato da errori di rete o dalla cache del browser. Si prega di pulire la cache, o di attendere e riprovare più tardi." serverIsDead: "Il server non risponde. Si prega di attendere e riprovare più tardi." youShouldUpgradeClient: "Per visualizzare la pagina è necessario aggiornare il client alla nuova versione e ricaricare." enterListName: "Nome della lista" privacy: "Privacy" -makeFollowManuallyApprove: "Richiedi di approvare i follower manualmente" +makeFollowManuallyApprove: "Approva i follower manualmente" defaultNoteVisibility: "Privacy predefinita delle note" follow: "Segui" followRequest: "Richiesta di follow" @@ -103,6 +103,8 @@ renoted: "Rinotato!" cantRenote: "È impossibile rinotare questa nota." cantReRenote: "È impossibile rinotare una Rinota." quote: "Cita" +inChannelRenote: "Rinota nel canale" +inChannelQuote: "Cita nel canale" pinnedNote: "Nota fissata" pinned: "Fissa sul profilo" you: "Tu" @@ -119,7 +121,7 @@ markAsSensitive: "Segna come sensibile" unmarkAsSensitive: "Segna come non sensibile" enterFileName: "Nome del file" mute: "Silenzia" -unmute: "Riattiva" +unmute: "Riattiva l'audio" block: "Blocca" unblock: "Sblocca" suspend: "Sospendi" @@ -129,6 +131,7 @@ unblockConfirm: "Vuoi davvero sbloccare il profilo?" suspendConfirm: "Vuoi sospendere questo profilo?" unsuspendConfirm: "Vuoi revocare la sospensione si questo profilo?" selectList: "Seleziona una lista" +selectChannel: "Seleziona canale" selectAntenna: "Scegli un'antenna" selectWidget: "Seleziona il riquadro" editWidgets: "Modifica i riquadri" @@ -140,7 +143,7 @@ emojiName: "Nome dell'emoji" emojiUrl: "URL dell'emoji" addEmoji: "Aggiungi un emoji" settingGuide: "Configurazione suggerita" -cacheRemoteFiles: "Memorizzazione nella cache dei file remoti" +cacheRemoteFiles: "Memorizza i file remoti nella cache" cacheRemoteFilesDescription: "Disabilitando questa opzione, i file remoti verranno linkati direttamente senza essere memorizzati nella cache. Sarà possibile risparmiare spazio di archiviazione sul server, ma il traffico aumenterà in quanto non verranno generate anteprime." flagAsBot: "Io sono un robot" flagAsBotDescription: "Attiva questo campo se il profilo esegue principalmente operazioni automatiche. L'attivazione segnala agli altri sviluppatori come comportarsi per evitare catene d’interazione infinite con altri bot. I sistemi interni di Misskey si adegueranno al fine di trattare questo profilo come bot." @@ -151,7 +154,7 @@ flagShowTimelineRepliesDescription: "Se è attiva, la timeline mostra le rispost autoAcceptFollowed: "Accetta automaticamente le richieste di follow da utenti che già segui" addAccount: "Aggiungi profilo" loginFailed: "Accesso non riuscito" -showOnRemote: "Sfoglia sull'istanza remota" +showOnRemote: "Visualizza sull'istanza remota" general: "Generali" wallpaper: "Sfondo" setWallpaper: "Imposta sfondo" @@ -206,14 +209,14 @@ intro: "L'installazione di Misskey è terminata! Si prega di creare il profilo a done: "Fine" processing: "In elaborazione" preview: "Anteprima" -default: "Predefinito" +default: "Medio" defaultValueIs: "Predefinito: {value}" noCustomEmojis: "Nessun emoji" noJobs: "Nessun lavoro" federating: "Federazione" blocked: "Bloccato" suspended: "Sospensione" -all: "Tutti" +all: "Tutte" subscribing: "Iscrizione" publishing: "Pubblicazione" notResponding: "Nessuna risposta" @@ -231,7 +234,7 @@ more: "Di più!" featured: "Tendenze" usernameOrUserId: "Nome utente o ID utente" noSuchUser: "Nessun utente trovato" -lookup: "Cercare" +lookup: "Cerca" announcements: "Annunci" imageUrl: "URL dell'immagine" remove: "Elimina" @@ -256,6 +259,8 @@ noMoreHistory: "Non c'è più cronologia da visualizzare" startMessaging: "Nuovo messaggio" nUsersRead: "Letto da {n} persone" agreeTo: "Sono d'accordo con {0}" +agreeBelow: "Accetto quanto riportato sotto" +basicNotesBeforeCreateAccount: "Note importanti" tos: "Termini di servizio" start: "Inizia!" home: "Home" @@ -283,7 +288,7 @@ selectFolders: "Seleziona cartella" renameFile: "Rinomina file" folderName: "Nome della cartella" createFolder: "Nuova cartella" -renameFolder: "Rinominare cartella" +renameFolder: "Rinomina cartella" deleteFolder: "Elimina cartella" addFile: "Allega" emptyDrive: "Il Drive è vuoto" @@ -300,7 +305,7 @@ avatar: "Foto del profilo" banner: "Intestazione" nsfw: "Contenuti sensibili" whenServerDisconnected: "Quando la connessione col server è persa" -disconnectedFromServer: "Disconness@ dal server" +disconnectedFromServer: "Il server si è disconnesso" reload: "Ricarica" doNothing: "Nessun'azione" reloadConfirm: "Vuoi ricaricare?" @@ -311,9 +316,9 @@ reject: "Rifiuta" normal: "Normale" instanceName: "Nome dell'istanza" instanceDescription: "Descrizione dell'istanza" -maintainerName: "Nome dell'Amministratore" -maintainerEmail: "Indirizzo e-mail dell'Amministratore" -tosUrl: "Termini di servizio URL" +maintainerName: "Nome dell'amministratore" +maintainerEmail: "Indirizzo e-mail dell'amministratore" +tosUrl: "URL dei termini del servizio e della privacy" thisYear: "Anno" thisMonth: "Mese" today: "Oggi" @@ -322,13 +327,13 @@ monthX: "{month}" yearX: "{year}" pages: "Pagine" integration: "App collegate" -connectService: "Connessione" -disconnectService: "Disconnessione " -enableLocalTimeline: "Abilita Timeline locale" -enableGlobalTimeline: "Abilita Timeline federata" +connectService: "Connetti" +disconnectService: "Disconnetti" +enableLocalTimeline: "Abilita la timeline locale" +enableGlobalTimeline: "Abilita la timeline federata" disablingTimelinesInfo: "Anche disabilitandole, gli Amministratori e i Moderatori potranno comunque accedervi." registration: "Iscriviti" -enableRegistration: "Permettere nuove registrazioni" +enableRegistration: "Consenti a chiunque di registrarsi" invite: "Invita" driveCapacityPerLocalAccount: "Capienza del Drive per profilo locale" driveCapacityPerRemoteAccount: "Capienza del Drive per profilo remoto" @@ -388,13 +393,10 @@ about: "Informazioni" aboutMisskey: "Informazioni di Misskey" administrator: "Amministratore" token: "Token" -twoStepAuthentication: "Autenticazione a due fattori" moderator: "Moderatore" moderation: "moderazione" nUsersMentioned: "{n} profili menzionati" securityKey: "Chiave di sicurezza" -securityKeyName: "Nome della chiave" -registerSecurityKey: "Registra una chiave di sicurezza" lastUsed: "Ultima attività " unregister: "Annulla l'iscrizione" passwordLessLogin: "Accedi senza password" @@ -412,24 +414,15 @@ markAsReadAllTalkMessages: "Segna tutte le chat come lette" help: "Guida" inputMessageHere: "Scrivi messaggio qui" close: "Chiudi" -group: "Gruppo" -groups: "Gruppi" -createGroup: "Nuovo gruppo" -ownedGroups: "I miei gruppi" -joinedGroups: "Gruppi a cui mi sono unit@" invites: "Inviti" -groupName: "Nome del gruppo" members: "Membri" transfer: "Trasferisci" -messagingWithUser: "Iniziare una chat con un altr@ utente" -messagingWithGroup: "Chattare in gruppo" title: "Titolo" text: "Testo" enable: "Abilita" next: "Avanti" retype: "Conferma" noteOf: "Note di {user}" -inviteToGroup: "Invitare al gruppo" quoteAttached: "Citazione allegata" quoteQuestion: "Vuoi aggiungere una citazione?" noMessagesYet: "Ancora nessuna chat" @@ -451,19 +444,17 @@ passwordMatched: "Corretta" passwordNotMatched: "Le password non corrispondono." signinWith: "Accedi con {x}" signinFailed: "Autenticazione non riuscita. Controlla la tua password e nome utente." -tapSecurityKey: "Premi la chiave di sicurezza" or: "oppure" language: "Lingua" uiLanguage: "Lingua di visualizzazione dell'interfaccia" -groupInvited: "Invitat@ al gruppo" aboutX: "Informazioni su {x}" emojiStyle: "Stile emoji" native: "Nativo" disableDrawer: "Non mostrare il menù sul drawer" -youHaveNoGroups: "Nessun gruppo" -joinOrCreateGroup: "Puoi creare il tuo gruppo o essere invitat@ a gruppi che già esistono." noHistory: "Nessuna cronologia" signinHistory: "Storico degli accessi al profilo" +enableAdvancedMfm: "Attiva MFM avanzati" +enableAnimatedMfm: "Attiva MFM animati" doing: "In corso..." category: "Categoria" tags: "Tag" @@ -473,7 +464,7 @@ existingAccount: "Profilo esistente" regenerate: "Generare di nuovo" fontSize: "Dimensione carattere" noFollowRequests: "Non hai alcuna richiesta di follow" -openImageInNewTab: "Aprire immagini in una nuova scheda" +openImageInNewTab: "Apri le immagini in un nuovo tab" dashboard: "Pannello di controllo" local: "Locale" remote: "Remoto" @@ -602,7 +593,7 @@ smtpPass: "Password" emptyToDisableSmtpAuth: "Lasciare il nome utente e la password vuoti per disabilitare la verifica SMTP" smtpSecure: "Usare la porta SSL/TLS implicito per le connessioni SMTP" smtpSecureInfo: "Disabilitare quando è attivo STARTTLS." -testEmail: "Testare la consegna di posta elettronica" +testEmail: "Testa la consegna di posta elettronica" wordMute: "Filtri parole" regexpError: "errore regex" regexpErrorDescription: "Si è verificato un errore nell'espressione regolare alla riga {line} della parola muta {tab}:" @@ -615,7 +606,7 @@ metrics: "Statistiche" overview: "Anteprima" logs: "Log" delayed: "Ritardo" -database: "Base di dati" +database: "Base dati" channel: "Canale" create: "Crea" notificationSetting: "Impostazioni notifiche" @@ -704,7 +695,7 @@ narrow: "Stretto" reloadToApplySetting: "Le tue preferenze verranno impostate dopo il ricaricamento della pagina. Vuoi ricaricare adesso?" needReloadToApply: "È necessario riavviare per rendere effettive le modifiche." showTitlebar: "Visualizza la barra del titolo" -clearCache: "Svuota cache" +clearCache: "Svuota la cache" onlineUsersCount: "{n} utenti online" nUsers: "{n} utenti" nNotes: "{n}Note" @@ -729,9 +720,9 @@ currentVersion: "Versione attuale" latestVersion: "Ultima versione" youAreRunningUpToDateClient: "Stai usando la versione più recente del client." newVersionOfClientAvailable: "Una nuova versione del tuo client è disponibile." -usageAmount: "In utilizzo" +usageAmount: "In uso" capacity: "Capacità " -inUse: "In utilizzo" +inUse: "In uso" editCode: "Modifica codice" apply: "Applica" receiveAnnouncementFromInstance: "Ricevi i messaggi informativi dall'istanza" @@ -817,7 +808,7 @@ breakFollow: "Smetti di seguire" itsOn: "Abilitato" itsOff: "Disabilitato" emailRequiredForSignup: "L'ndirizzo e-mail è obbligatorio per registrarsi" -unread: "Non letto" +unread: "Non lette" filter: "Filtri" controlPanel: "Pannello di controllo" manageAccounts: "Gestisci i profili" @@ -833,8 +824,6 @@ deleteAccountConfirm: "Così verrà eliminato il profilo. Vuoi procedere?" incorrectPassword: "La password è errata." voteConfirm: "Votare per「{choice}ã€?" hide: "Nascondere" -leaveGroup: "Esci dal gruppo" -leaveGroupConfirm: "Uscire da「{name}ã€?" useDrawerReactionPickerForMobile: "Mostra sul drawer da dispositivo mobile" welcomeBackWithName: "Ciao, {name}! Eccoti di nuovo!" clickToFinishEmailVerification: "Fai click su [{ok}] per completare la verifica dell'indirizzo email." @@ -857,9 +846,11 @@ oneDay: "1 giorno" oneWeek: "1 settimana" reflectMayTakeTime: "Potrebbe essere necessario un po' di tempo perché ciò abbia effetto." failedToFetchAccountInformation: "Impossibile recuperare le informazioni sul profilo" -rateLimitExceeded: "Superato il limite di velocità ." -cropImage: "Ritaglio dell'immagine" -cropImageAsk: "Si desidera ritagliare l'immagine?" +rateLimitExceeded: "Superato il limite di richieste." +cropImage: "Ritaglia l'immagine" +cropImageAsk: "Vuoi ritagliare l'immagine?" +cropYes: "Ritaglia" +cropNo: "Non ritagliare" file: "Allegati" recentNHours: "Ultime {n} ore" recentNDays: "Ultimi {n} giorni" @@ -938,6 +929,16 @@ cannotPerformTemporaryDescription: "L'attività non può essere svolta, poiché preset: "Preimpostato" selectFromPresets: "Seleziona preimpostato" achievements: "Obiettivi raggiunti" +gotInvalidResponseError: "Risposta del server non valida" +gotInvalidResponseErrorDescription: "Il server potrebbe essere irraggiungibile o in manutenzione. Riprova più tardi." +thisPostMayBeAnnoying: "Questa nota potrebbe essere offensiva" +thisPostMayBeAnnoyingHome: "Pubblica sulla timeline principale" +thisPostMayBeAnnoyingCancel: "Annulla" +thisPostMayBeAnnoyingIgnore: "Pubblica lo stesso" +collapseRenotes: "Comprimi i Rinota già letti" +internalServerError: "Errore interno del server" +internalServerErrorDescription: "Si è verificato un errore imprevisto all'interno del server" +copyErrorInfo: "Copia le informazioni sull'errore" _achievements: earnedAt: "Data di conseguimento" _types: @@ -993,53 +994,53 @@ _achievements: flavor: "Hai molto da scrivere?" _login3: title: "Principiante I" - description: "Accedi per un totale di 3 giorni" + description: "Hai totalizzato 3 accessi!" flavor: "Da oggi, chiamatemi Misskist" _login7: title: "Principiante II" - description: "Accedi per un totale di 7 giorni" + description: "Hai totalizzato 7 accessi!" flavor: "Ti sembra di avere la situazione sotto controllo?" _login15: title: "Principiante III" - description: "Accedi per un totale di 15 giorni" + description: "Hai totalizzato 15 accessi!" _login30: title: "Misskist I" - description: "Accedi per un totale di 30 giorni" + description: "Hai totalizzato 30 accessi!" _login60: title: "Misskeist II" - description: "Accedi per un totale di 60 giorni" + description: "Hai totalizzato 60 accessi!" _login100: title: "Misskeist III" - description: "Accedi per un totale di 100 giorni" + description: "Hai totalizzato 100 accessi!" flavor: "Violent Misskeist" _login200: - title: "Regolare I" - description: "Accedi per un totale di 200 giorni" + title: "Regolare I livello" + description: "Hai totalizzato 200 accessi!" _login300: - title: "Regolare II" - description: "Accedi per un totale di 300 giorni" + title: "Regolare II livello" + description: "Hai totalizzato 300 accessi!" _login400: - title: "Regolare III" - description: "Accedi per un totale di 400 giorni" + title: "Regolare III livello" + description: "Hai totalizzato 400 accessi!" _login500: - title: "Professionista I" - description: "Accedi per un totale di 500 giorni" + title: "Professionista I livello" + description: "Hai totalizzato 500 accessi!" flavor: "Amici cari, mi piacciono le Note" _login600: - title: "Professionista II" - description: "Accedi per un totale di 600 giorni" + title: "Professionista II livello" + description: "Hai totalizzato 600 accessi!" _login700: - title: "Professionista III" - description: "Accedi per un totale di 700 giorni" + title: "Professionista III livello" + description: "Hai totalizzato 700 accessi!" _login800: - title: "Maestro di Note I" - description: "Accedi per un totale di 800 giorni" + title: "Maestro di Note I livello" + description: "Hai totalizzato 800 accessi!" _login900: - title: "Maestro di Note II" - description: "Accedi per un totale di 900 giorni" + title: "Maestro di Note II livello" + description: "Hai totalizzato 900 accessi!" _login1000: - title: "Maestro di Note III" - description: "Accedi per un totale di 1.000 giorni" + title: "Maestro di Note III livello" + description: "Hai totalizzato 1000 accessi!" flavor: "Grazie per aver usato Misskey!" _noteClipped1: title: "Devo clippare!" @@ -1051,7 +1052,7 @@ _achievements: title: "Fornitura stelline" description: "Qualcuno ha preferito una delle tue Note" _profileFilled: - title: "Perfettamente" + title: "Preparazione perfetta!" description: "Imposta il tuo profilo" _markedAsCat: title: "Io sono un gatto" @@ -1459,10 +1460,10 @@ _time: _tutorial: title: "Come usare Misskey" step1_1: "Eccoci!" - step1_2: "Questa pagina si chiama una \" Timeline \". Mostra in ordine cronologico le \" note \" delle persone che segui." - step1_3: "Attualmente la tua Timeline è vuota perché non segui alcun profilo e non hai pubblicato alcuna nota ancora." + step1_2: "Questa pagina si chiama \"Timeline \" e mostra in ordine cronologico le \"note\" delle persone che segui." + step1_3: "Attualmente la tua Timeline è vuota perché non segui alcun profilo e non hai ancora pubblicato alcuna nota." step2_1: "Prima di scrivere una «Nota» o di seguire altri profili, prepara il tuo profilo!" - step2_2: "Aggiungere qualche informazione su di te aumenterà le tue possibilità di essere seguit@ da altre persone. " + step2_2: "Se aggiungi informazioni personali aumenterai le tue possibilità di essere seguit@ da altre persone. " step3_1: "Hai finito di impostare il tuo profilo?" step3_2: "Ora puoi pubblicare una «Nota». Proviamo subito! Premi il bottone con l'icona «penna» per iniziare a scrivere in una finestra di dialogo. " step3_3: "Scritto il testo della nota, puoi pubblicarla premendo il pulsante nella parte superiore destra della finestra di dialogo." @@ -1481,24 +1482,24 @@ _tutorial: step7_3: "Da ultimo, buon divertimento su Misskey! 🚀" step8_1: "Per concludere, vuoi attivare le notifiche push?" step8_2: "Attivandole, otterrai notifiche di follow, reazioni e menzioni anche quando Misskey è chiuso." - step8_3: "Puoi anche modificare questa impostazione successivamente." + step8_3: "Potrai modificare questa impostazione." _2fa: alreadyRegistered: "La configurazione è stata già completata." - registerDevice: "Aggiungi dispositivo" - registerKey: "Chiave di registro." step1: "Innanzitutto, installare sul dispositivo un'applicazione di autenticazione come {a} o {b}." step2: "Quindi, scansionare il codice QR visualizzato con l'app." step2Url: "Nell'applicazione desktop inserire il seguente URL: " step3: "Inserite il token visualizzato nell'app e il gioco è fatto." step4: "D'ora in poi, quando si accede, si inserisce il token nello stesso modo." securityKeyInfo: "È possibile impostare il dispositivo per accedere utilizzando una chiave di sicurezza hardware che supporta FIDO2 o un'impronta digitale o un PIN sul dispositivo." + removeKeyConfirm: "Vuoi davvero eliminare \"{name}\"?" + renewTOTPCancel: "No grazie" _permissions: - "read:account": "Visualizzare le informazioni sul profilo" - "write:account": "Modificare le informazioni sul profilo" + "read:account": "Visualizza le informazioni sul profilo" + "write:account": "Modifica le informazioni sul profilo" "read:blocks": "Visualizza i profili bloccati" "write:blocks": "Gestisci i profili bloccati" - "read:drive": "Aprire il Drive" - "write:drive": "Gestire il Drive" + "read:drive": "Apri il Drive" + "write:drive": "Gestisci il Drive" "read:favorites": "Visualizza i tuoi preferiti" "write:favorites": "Gestisci i tuoi preferiti" "read:following": "Vedi le informazioni di follow" @@ -1526,18 +1527,20 @@ _permissions: "read:gallery-likes": "Visualizza i contenuti della galleria." "write:gallery-likes": "Manipolazione dei \"Mi piace\" della galleria." _auth: + shareAccessTitle: "Permessi dell'applicazione" shareAccess: "Vuoi autorizzare {name} ad accedere al tuo profilo?" shareAccessAsk: "Vuoi autorizzare questa App ad accedere al tuo profilo?" + permission: "{name} richiede i permessi seguenti" permissionAsk: "Questa app richiede le seguenti autorizzazioni:" pleaseGoBack: "Si prega di ritornare sulla app" callback: "Ritornando sulla app" denied: "Accesso negato" + pleaseLogin: "Per favore accedi al tuo account per cambiare i permessi dell'applicazione" _antennaSources: all: "Tutte le note" homeTimeline: "Note dagli utenti che segui" users: "Note dagli utenti selezionati" userList: "Note dagli utenti della lista selezionata" - userGroup: "Note dagli utenti del gruppo selezionato" _weekday: sunday: "Domenica" monday: "Lunedì" @@ -1610,21 +1613,19 @@ _visibility: homeDescription: "Visibile solo sulla timeline \"Home\"" followers: "Followers" followersDescription: "Visibile solo per i tuoi followers" - specified: "Diretta" + specified: "Nota diretta" specifiedDescription: "Visibile solo ai profili menzionati" - localOnly: "Soltanto locale" - localOnlyDescription: "Non visibile ai profili remoti" _postForm: - replyPlaceholder: "Nota la tua risposta.." - quotePlaceholder: "Cita Nota..." - channelPlaceholder: "Pubblica in canale" + replyPlaceholder: "Rispondi a questa nota..." + quotePlaceholder: "Cita questa nota..." + channelPlaceholder: "Pubblica sul canale..." _placeholders: - a: "Che succede?" - b: "È successo qualcosa?" - c: "Che cos'hai in mente?" + a: "Come va?" + b: "Hai qualcosa da raccontare? Inizia pure..." + c: "Stai pensando a qualcosa?" d: "Vuoi dire qualcosa?" - e: "Scrivi qualcosa qui" - f: "Aspettando che scriva..." + e: "Puoi scrivere qui..." + f: "Inizia pure a scrivere..." _profile: name: "Nome" username: "Nome utente" @@ -1655,9 +1656,9 @@ _charts: notesIncDec: "Variazione del numero di note" localNotesIncDec: "Variazione del numero di note locali" remoteNotesIncDec: "Variazione del numero di note distanti" - notesTotal: "Conteggio totale di note" + notesTotal: "Numero di note in totale" filesIncDec: "Variazione del numero dei file" - filesTotal: "Numero totale di file" + filesTotal: "Numero di file in totale" storageUsageIncDec: "Variazione dell'utilizzo dell'immagazzinamento" storageUsageTotal: "Utilizzo totale dell'immagazzinamento" _instanceCharts: @@ -1749,14 +1750,11 @@ _notification: fileUploaded: "File caricato correttamente" youGotMention: "{name} ti ha menzionato" youGotReply: "{name} ti ha risposto" - youGotQuote: "{name} ha citato il tuo Nota e ha detto" + youGotQuote: "{name} ha citato la tua Nota e ha detto" youRenoted: "{name} ha rinotato" - youGotMessagingMessageFromUser: "{name} ti ha mandato un messaggio" - youGotMessagingMessageFromGroup: "{name} ti ha mandato un messaggio nella chat" youWereFollowed: "Ha iniziato a seguirti" youReceivedFollowRequest: "Hai ricevuto una richiesta di follow" yourFollowRequestAccepted: "La tua richiesta di follow è stata accettata" - youWereInvitedToGroup: "Invitat@ al gruppo" pollEnded: "Risultati del sondaggio." unreadAntennaNote: "Antenna {name}" emptyPushNotificationMessage: "Le notifiche push sono state aggiornate." @@ -1772,7 +1770,6 @@ _notification: pollEnded: "Sondaggio chiuso." receiveFollowRequest: "Richiesta di follow ricevuta" followRequestAccepted: "Richiesta di follow accettata" - groupInvited: "Invito a un gruppo" app: "Notifiche da applicazioni" _actions: followBack: "Segui" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index b1f484f6b2..73f9718aac 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -392,17 +392,20 @@ userList: "リスト" about: "æƒ…å ±" aboutMisskey: "Misskeyã«ã¤ã„ã¦" administrator: "管ç†è€…" -token: "トークン" -twoStepAuthentication: "二段階èªè¨¼" +token: "確èªã‚³ãƒ¼ãƒ‰" +2fa: "二è¦ç´ èªè¨¼" +totp: "èªè¨¼ã‚¢ãƒ—リ" +totpDescription: "èªè¨¼ã‚¢ãƒ—リを使ã£ã¦ãƒ¯ãƒ³ã‚¿ã‚¤ãƒ パスワードを入力" moderator: "モデレーター" moderation: "モデレーション" nUsersMentioned: "{n}äººãŒæŠ•ç¨¿" +securityKeyAndPasskey: "ã‚»ã‚ュリティã‚ー・パスã‚ー" securityKey: "ã‚»ã‚ュリティã‚ー" -securityKeyName: "ã‚ーã®åå‰" -registerSecurityKey: "ã‚»ã‚ュリティã‚ーを登録ã™ã‚‹" lastUsed: "最後ã®ä½¿ç”¨" +lastUsedAt: "最後ã®ä½¿ç”¨: {t}" unregister: "登録を解除" -passwordLessLogin: "パスワード無ã—ãƒã‚°ã‚¤ãƒ³" +passwordLessLogin: "パスワードレスãƒã‚°ã‚¤ãƒ³" +passwordLessLoginDescription: "パスワードを使用ã›ãšã€ã‚»ã‚ュリティã‚ーやパスã‚ーãªã©ã®ã¿ã§ãƒã‚°ã‚¤ãƒ³ã—ã¾ã™" resetPassword: "パスワードをリセット" newPasswordIs: "æ–°ã—ã„パスワードã¯ã€Œ{password}ã€ã§ã™" reduceUiAnimation: "UIã®ã‚¢ãƒ‹ãƒ¡ãƒ¼ã‚·ãƒ§ãƒ³ã‚’減らã™" @@ -447,7 +450,6 @@ passwordMatched: "一致ã—ã¾ã—ãŸ" passwordNotMatched: "一致ã—ã¦ã„ã¾ã›ã‚“" signinWith: "{x}ã§ãƒã‚°ã‚¤ãƒ³" signinFailed: "ãƒã‚°ã‚¤ãƒ³ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ユーザーåã¨ãƒ‘スワードを確èªã—ã¦ãã ã•ã„。" -tapSecurityKey: "ã‚»ã‚ュリティã‚ーã«ã‚¿ãƒƒãƒ" or: "ã‚‚ã—ãã¯" language: "言語" uiLanguage: "UIã®è¡¨ç¤ºè¨€èªž" @@ -810,6 +812,7 @@ lastCommunication: "ç›´è¿‘ã®é€šä¿¡" resolved: "解決済ã¿" unresolved: "未解決" breakFollow: "フォãƒãƒ¯ãƒ¼ã‚’解除" +breakFollowConfirm: "フォãƒãƒ¯ãƒ¼è§£é™¤ã—ã¾ã™ã‹ï¼Ÿ" itsOn: "オンã«ãªã£ã¦ã„ã¾ã™" itsOff: "オフã«ãªã£ã¦ã„ã¾ã™" emailRequiredForSignup: "アカウント登録ã«ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’å¿…é ˆã«ã™ã‚‹" @@ -944,6 +947,10 @@ collapseRenotes: "見ãŸã“ã¨ã®ã‚ã‚‹Renoteã‚’çœç•¥ã—ã¦è¡¨ç¤º" internalServerError: "サーãƒãƒ¼å†…部エラー" internalServerErrorDescription: "サーãƒãƒ¼å†…部ã§äºˆæœŸã—ãªã„エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚" copyErrorInfo: "ã‚¨ãƒ©ãƒ¼æƒ…å ±ã‚’ã‚³ãƒ”ãƒ¼" +joinThisServer: "ã“ã®ã‚µãƒ¼ãƒãƒ¼ã«ç™»éŒ²ã™ã‚‹" +exploreOtherServers: "ä»–ã®ã‚µãƒ¼ãƒãƒ¼ã‚’探ã™" +letsLookAtTimeline: "タイムラインを見ã¦ã¿ã‚‹" +disableFederationWarn: "連åˆãŒç„¡åйã«ãªã£ã¦ã„ã¾ã™ã€‚無効ã«ã—ã¦ã‚‚投稿ãŒéžå…¬é–‹ã«ã¯ãªã‚Šã¾ã›ã‚“。ã»ã¨ã‚“ã©ã®å ´åˆã€ã“ã®ã‚ªãƒ—ションを有効ã«ã™ã‚‹å¿…è¦ã¯ã‚りã¾ã›ã‚“。" _achievements: earnedAt: "ç²å¾—日時" @@ -1483,6 +1490,7 @@ _ago: weeksAgo: "{n}週間å‰" monthsAgo: "{n}ヶ月å‰" yearsAgo: "{n}å¹´å‰" + invalid: "ã‚りã¾ã›ã‚“" _time: second: "ç§’" @@ -1519,14 +1527,29 @@ _tutorial: _2fa: alreadyRegistered: "æ—¢ã«è¨å®šã¯å®Œäº†ã—ã¦ã„ã¾ã™ã€‚" - registerDevice: "デãƒã‚¤ã‚¹ã‚’登録" - registerKey: "ã‚ーを登録" + registerTOTP: "èªè¨¼ã‚¢ãƒ—リã®è¨å®šã‚’é–‹å§‹" + passwordToTOTP: "パスワードを入力ã—ã¦ãã ã•ã„" step1: "ã¾ãšã€{a}ã‚„{b}ãªã©ã®èªè¨¼ã‚¢ãƒ—リをãŠä½¿ã„ã®ãƒ‡ãƒã‚¤ã‚¹ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ã¾ã™ã€‚" step2: "次ã«ã€è¡¨ç¤ºã•れã¦ã„ã‚‹QRコードをアプリã§ã‚¹ã‚ャンã—ã¾ã™ã€‚" - step2Url: "デスクトップアプリã§ã¯æ¬¡ã®URLを入力ã—ã¾ã™:" - step3: "アプリã«è¡¨ç¤ºã•れã¦ã„るトークンを入力ã—ã¦å®Œäº†ã§ã™ã€‚" - step4: "ã“れã‹ã‚‰ãƒã‚°ã‚¤ãƒ³ã™ã‚‹ã¨ãã‚‚ã€åŒã˜ã‚ˆã†ã«ãƒˆãƒ¼ã‚¯ãƒ³ã‚’入力ã—ã¾ã™ã€‚" - securityKeyInfo: "FIDO2をサãƒãƒ¼ãƒˆã™ã‚‹ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã‚»ã‚ュリティã‚ーもã—ãã¯ç«¯æœ«ã®æŒ‡ç´‹èªè¨¼ã‚„PINを使用ã—ã¦ãƒã‚°ã‚¤ãƒ³ã™ã‚‹ã‚ˆã†ã«è¨å®šã§ãã¾ã™ã€‚" + step2Click: "QRコードをクリックã™ã‚‹ã¨ã€ãŠä½¿ã„ã®ç«¯æœ«ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ã‚‹èªè¨¼ã‚¢ãƒ—リやã‚ーリングã«ç™»éŒ²ã§ãã¾ã™ã€‚" + step2Url: "デスクトップアプリã§ã¯æ¬¡ã®URIを入力ã—ã¾ã™:" + step3Title: "確èªã‚³ãƒ¼ãƒ‰ã‚’入力" + step3: "アプリã«è¡¨ç¤ºã•れã¦ã„る確èªã‚³ãƒ¼ãƒ‰ï¼ˆãƒˆãƒ¼ã‚¯ãƒ³ï¼‰ã‚’入力ã—ã¦å®Œäº†ã§ã™ã€‚" + step4: "ã“れã‹ã‚‰ãƒã‚°ã‚¤ãƒ³ã™ã‚‹ã¨ãã‚‚ã€åŒã˜ã‚ˆã†ã«ç¢ºèªã‚³ãƒ¼ãƒ‰ã‚’入力ã—ã¾ã™ã€‚" + securityKeyNotSupported: "ãŠä½¿ã„ã®ãƒ–ラウザã¯ã‚»ã‚ュリティã‚ーã«å¯¾å¿œã—ã¦ã„ã¾ã›ã‚“。" + registerTOTPBeforeKey: "ã‚»ã‚ュリティã‚ー・パスã‚ーを登録ã™ã‚‹ã«ã¯ã€ã¾ãšèªè¨¼ã‚¢ãƒ—リã®è¨å®šã‚’行ãªã£ã¦ãã ã•ã„。" + securityKeyInfo: "FIDO2をサãƒãƒ¼ãƒˆã™ã‚‹ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã‚»ã‚ュリティã‚ーã€ç«¯æœ«ã®ç”Ÿä½“èªè¨¼ã‚„PINãƒãƒƒã‚¯ã€ãƒ‘スã‚ーã¨ã„ã£ãŸã€WebAuthnç”±æ¥ã®éµã‚’登録ã—ã¾ã™ã€‚" + chromePasskeyNotSupported: "Chromeã®ãƒ‘スã‚ーã¯ç¾åœ¨ã‚µãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“。" + registerSecurityKey: "ã‚»ã‚ュリティã‚ー・パスã‚ーを登録ã™ã‚‹" + securityKeyName: "ã‚ーã®åå‰ã‚’入力" + tapSecurityKey: "ãƒ–ãƒ©ã‚¦ã‚¶ã®æŒ‡ç¤ºã«å¾“ã„ã€ã‚»ã‚ュリティã‚ーやパスã‚ーを登録ã—ã¦ãã ã•ã„" + removeKey: "ã‚»ã‚ュリティã‚ーを削除" + removeKeyConfirm: "{name}を削除ã—ã¾ã™ã‹ï¼Ÿ" + whyTOTPOnlyRenew: "ã‚»ã‚ュリティã‚ーãŒç™»éŒ²ã•れã¦ã„ã‚‹å ´åˆã€èªè¨¼ã‚¢ãƒ—リã®è¨å®šã¯è§£é™¤ã§ãã¾ã›ã‚“。" + renewTOTP: "èªè¨¼ã‚¢ãƒ—リをå†è¨å®š" + renewTOTPConfirm: "今ã¾ã§ã®èªè¨¼ã‚¢ãƒ—リã®ç¢ºèªã‚³ãƒ¼ãƒ‰ã¯ä½¿ç”¨ã§ããªããªã‚Šã¾ã™" + renewTOTPOk: "å†è¨å®šã™ã‚‹" + renewTOTPCancel: "ã‚„ã‚ã¦ãŠã" _permissions: "read:account": "ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®æƒ…å ±ã‚’è¦‹ã‚‹" @@ -1657,8 +1680,8 @@ _visibility: followersDescription: "自分ã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã®ã¿ã«å…¬é–‹" specified: "ダイレクト" specifiedDescription: "指定ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã¿ã«å…¬é–‹" - localOnly: "ãƒãƒ¼ã‚«ãƒ«ã®ã¿" - localOnlyDescription: "リモートユーザーã«ã¯éžå…¬é–‹" + disableFederation: "連åˆãªã—" + disableFederationDescription: "他インスタンスã¸ã®é…信を行ã„ã¾ã›ã‚“" _postForm: replyPlaceholder: "ã“ã®ãƒŽãƒ¼ãƒˆã«è¿”ä¿¡..." @@ -1826,6 +1849,7 @@ _notification: pollEnded: "アンケートãŒçµ‚了" receiveFollowRequest: "フォãƒãƒ¼ç”³è«‹ã‚’å—ã‘å–ã£ãŸ" followRequestAccepted: "フォãƒãƒ¼ãŒå—ç†ã•れãŸ" + achievementEarned: "実績ã®ç²å¾—" app: "連æºã‚¢ãƒ—リã‹ã‚‰ã®é€šçŸ¥" _actions: @@ -1861,3 +1885,7 @@ _deck: channel: "ãƒãƒ£ãƒ³ãƒãƒ«" mentions: "ã‚ãªãŸå®›ã¦" direct: "ダイレクト" + +_dialog: + charactersExceeded: "æœ€å¤§æ–‡å—æ•°ã‚’è¶…ãˆã¦ã„ã¾ã™ï¼ ç¾åœ¨ {current} / åˆ¶é™ {max}" + charactersBelow: "æœ€å°æ–‡å—数を下回ã£ã¦ã„ã¾ã™ï¼ ç¾åœ¨ {current} / åˆ¶é™ {min}" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index cbbd928b2c..68664067c2 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -67,7 +67,7 @@ import: "インãƒãƒ¼ãƒˆ" export: "エクスãƒãƒ¼ãƒˆ" files: "ファイル" download: "ダウンãƒãƒ¼ãƒ‰" -driveFileDeleteConfirm: "ファイル「{name}ã€ã‚’消ã—ã¦ã—ã‚‚ã†ã¦ãˆãˆã‹ï¼Ÿã“ã®ãƒ•ァイルを添付ã—ãŸãƒŽãƒ¼ãƒˆã‚‚消ãˆã¦ã¾ã†ã§ã€‚" +driveFileDeleteConfirm: "ファイル「{name}ã€ã‚’ã»ã‹ã—ã¦ãˆãˆã‹ï¼Ÿã“ã®ãƒ•ァイルを添付ã—ãŸãƒŽãƒ¼ãƒˆã‚‚消ãˆã¦ã¾ã†ã§ã€‚" unfollowConfirm: "{name}ã®ãƒ•ã‚©ãƒãƒ¼ã‚’解除ã—ã¦ã‚‚ãˆãˆã‚“ã‹ï¼Ÿ" exportRequested: "エクスãƒãƒ¼ãƒˆã—ã¦ãªã€ã£ã¦ãƒªã‚¯ã‚¨ã‚¹ãƒˆã—ãŸã‘ã©ã€ã“れ多分ã‚ã£ã¡ã‚ƒæ™‚é–“ã‹ã‹ã‚‹ã§ã€‚エクスãƒãƒ¼ãƒˆçµ‚ã‚ã£ãŸã‚‰ã€Œãƒ‰ãƒ©ã‚¤ãƒ–ã€ã«çªã£è¾¼ã‚“ã©ãã§ã€‚" importRequested: "インãƒãƒ¼ãƒˆã—ã¦ãªã€ã£ã¦ãƒªã‚¯ã‚¨ã‚¹ãƒˆã—ãŸã‘ã©ã€ã“れ多分ã‚ã£ã¡ã‚ƒæ™‚é–“ã‹ã‹ã‚‹ã§ã€‚" @@ -83,7 +83,7 @@ manageLists: "リストã®ç®¡ç†" error: "エラー" somethingHappened: "ãªã‚“ã‹ã‚¢ã‚«ãƒ³ã“ã¨ãŒèµ·ã“ã£ãŸã§" retry: "ã‚‚ã£ãºã‚“やる?" -pageLoadError: "ページã®èªã¿è¾¼ã¿ã«å¤±æ•—ã—ã¦ã—ã‚‚ã†ãŸã§â€¦" +pageLoadError: "ページã®èªã¿è¾¼ã¿ã«å¤±æ•—ã—ã¦ã‚‚ã†ãŸã‚…" pageLoadErrorDescription: "ã“ã‚Œã¯æ™®é€šã€ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‹ãƒ–ラウザã‚ャッシュãŒåŽŸå› ã‚„ã‹ã‚‰ã。ã‚ャッシュをクリアã™ã‚‹ã‹ã€ã‚‚ã†ã¡ã£ã¨ã ã‘å¾…ã£ã¦ãれã¸ã‚“ã‹ï¼Ÿ" serverIsDead: "サーãƒãƒ¼ã‹ã‚‰ã®å¿œç”ãŒãªã„ã§ã€‚ã‚‚ã†ã¡ã‚‡ã„å¾…ã£ã¦ã‹ã‚‰è©¦ã—ã¦ã¿ã¦ãªã€‚" youShouldUpgradeClient: "ã“ã®ãƒšãƒ¼ã‚¸ã‚’表示ã™ã‚‹ã«ã¯ã€ãƒªãƒãƒ¼ãƒ‰ã—ã¦æ–°ã—ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆã‚’使ã£ã¦ãªãƒ¼ã€‚" @@ -103,6 +103,8 @@ renoted: "Renoteã—ãŸã§ã€‚" cantRenote: "ã“ã®æŠ•ç¨¿ã¯Renoteã§ãã¸ã‚“らã—ã„。" cantReRenote: "Renote自体ã¯Renoteã§ãã¸ã‚“ã§ã€‚" quote: "引用" +inChannelRenote: "ãƒãƒ£ãƒ³ãƒãƒ«å†…Renote" +inChannelQuote: "ãƒãƒ£ãƒ³ãƒãƒ«å†…引用" pinnedNote: "ピン留ã‚ã•れã¨ã‚‹ãƒŽãƒ¼ãƒˆ" pinned: "ピン留ã‚ã—ã¨ã" you: "ã‚ã‚“ãŸ" @@ -151,7 +153,7 @@ flagShowTimelineReplies: "タイムラインã«ãƒŽãƒ¼ãƒˆã¸ã®è¿”信を表示㙠flagShowTimelineRepliesDescription: "オンã«ã—ãŸã‚‰ã€ã‚¿ã‚¤ãƒ ラインã«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆã®ä»–ã«ã‚‚ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ä»–ã®ãƒŽãƒ¼ãƒˆã¸ã®è¿”信を表示ã™ã‚‹ã§ã€‚" autoAcceptFollowed: "フォãƒãƒ¼ã—ã¨ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’勿‰‹ã«è¨±å¯ã—ã¨ã" addAccount: "ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’è¿½åŠ " -loginFailed: "ãƒã‚°ã‚¤ãƒ³ã«å¤±æ•—ã—ã¦ã—ã‚‚ã†ãŸâ€¦" +loginFailed: "ãƒã‚°ã‚¤ãƒ³ã«å¤±æ•—ã—ã¦ã‚‚ã†ãŸâ€¦" showOnRemote: "リモートã§è¦‹ã‚‹" general: "全般" wallpaper: "å£ç´™" @@ -243,7 +245,7 @@ resetAreYouSure: "リセットã—ã¦ãˆãˆã‚“?" saved: "ä¿å˜ã—ãŸã§ï¼" messaging: "ãƒãƒ£ãƒƒãƒˆ" upload: "アップãƒãƒ¼ãƒ‰" -keepOriginalUploading: "オリジナル画åƒã‚’ä¿æŒã™ã‚‹ã§" +keepOriginalUploading: "オリジナル画åƒã‚’ä¿æŒã™ã‚‹ã‚" keepOriginalUploadingDescription: "ç”»åƒã‚’上ã’ã‚‹ã¨ãã«ã‚ªãƒªã‚¸ãƒŠãƒ«ç‰ˆã‚’ä¿æŒã™ã‚‹ã§ã€‚オフã«ã—ãŸã‚‰ä¸Šã’ãŸã¨ãã«ãƒ–ラウザã§Web公開用ã®ç”»åƒã‚’生æˆã™ã‚‹ã§ã€‚ " fromDrive: "ドライブã‹ã‚‰" fromUrl: "URLã‹ã‚‰" @@ -391,13 +393,10 @@ about: "æƒ…å ±" aboutMisskey: "Misskeyã£ã¦ãªã‚“や?" administrator: "管ç†è€…" token: "トークン" -twoStepAuthentication: "二段階èªè¨¼" moderator: "モデレーター" moderation: "モデレーション" nUsersMentioned: "{n}äººãŒæŠ•ç¨¿" securityKey: "ã‚»ã‚ュリティã‚ー" -securityKeyName: "ã‚ーã®åå‰" -registerSecurityKey: "ã‚»ã‚ュリティã‚ーを登録ã™ã‚‹ã§" lastUsed: "最後ã«ã¤ã“ã†ãŸæ—¥" unregister: "登録やã‚ã‚‹" passwordLessLogin: "パスワード無ãã¦ã‚‚ãƒã‚°ã‚¤ãƒ³ã§ãるよã†ã«ã™ã‚‹" @@ -415,24 +414,15 @@ markAsReadAllTalkMessages: "ãƒãƒ£ãƒƒãƒˆã¯ã‚‚ã†ãœã‚“ã¶èªã‚“ã ã‚ã£" help: "ヘルプ" inputMessageHere: "ã“ã“ã«ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸æ›¸ã„ã¦ã‚„" close: "é–‰ã˜ã‚‹" -group: "グループ" -groups: "グループ" -createGroup: "グループを作るã§" -ownedGroups: "所有ã—ã¨ã‚‹ã‚°ãƒ«ãƒ¼ãƒ—" -joinedGroups: "å‚åŠ ã—ã¨ã‚‹ã‚°ãƒ«ãƒ¼ãƒ—" invites: "æ¥ã¦ã‚„" -groupName: "グループå" members: "メンãƒãƒ¼" transfer: "è²æ¸¡" -messagingWithUser: "ユーザーã¨ãƒãƒ£ãƒƒãƒˆ" -messagingWithGroup: "グループã§ãƒãƒ£ãƒƒãƒˆ" title: "タイトル" text: "テã‚スト" enable: "有効ã«ã™ã‚‹ã§" next: "次" retype: "ã‚‚ã£ã‹ã„入力" noteOf: "{user}ã®ãƒŽãƒ¼ãƒˆ" -inviteToGroup: "ã‚°ãƒ«ãƒ¼ãƒ—ã«æ‹›ã" quoteAttached: "引用付ã„ã¨ã‚‹ã§" quoteQuestion: "引用ã¨ã—ã¦æ·»ä»˜ã—ã¦ã‚‚ãˆãˆã‹ï¼Ÿ" noMessagesYet: "ã¾ã ãƒãƒ£ãƒƒãƒˆã¯ã‚らã¸ã‚“ã§" @@ -454,17 +444,13 @@ passwordMatched: "よã—ï¼ä¸€è‡´ã‚„ï¼" passwordNotMatched: "一致ã—ã¨ã‚‰ã‚“ã§ï¼Ÿ" signinWith: "{x}ã§ãƒã‚°ã‚¤ãƒ³" signinFailed: "ãƒã‚°ã‚¤ãƒ³ã§ãã‚“ã‹ã£ãŸã§ã€‚ã‚‚ã£ã‹ã„ユーザーåã¨ãƒ‘スワードを確èªã—ã¦ã¿ã¦ãªã€‚" -tapSecurityKey: "ã‚»ã‚ュリティã‚ーã«ã‚¿ãƒƒãƒã—ã¦ãª" or: "ãれã‹" language: "言語" uiLanguage: "UIã®è¡¨ç¤ºè¨€èªž" -groupInvited: "ã‚°ãƒ«ãƒ¼ãƒ—ã«æ‹›å¾…ã•れã¨ã‚‹ã§" aboutX: "{x}ã«ã¤ã„ã¦" emojiStyle: "絵文å—ã®ã‚¹ã‚¿ã‚¤ãƒ«" native: "ãƒã‚¤ãƒ†ã‚£ãƒ–" disableDrawer: "メニューをドãƒãƒ¯ãƒ¼ã§è¡¨ç¤ºã›ã‡ã¸ã‚“" -youHaveNoGroups: "グループãŒã‚らã¸ã‚“ãã‡ã€‚" -joinOrCreateGroup: "æ—¢å˜ã®ã‚°ãƒ«ãƒ¼ãƒ—ã«æ‹›å¾…ã—ã¦ã‚‚らã†ã‹ã€æ–°ã—ãグループ作ã£ã¦ã‹ã‚‰ã‚„ã£ã¦ãª" noHistory: "å±¥æ´ã¯ã‚らã¸ã‚“ãã‡ã€‚" signinHistory: "ãƒã‚°ã‚¤ãƒ³å±¥æ´" enableAdvancedMfm: "ã‚„ã‚„ã“ã—ã„MFMã‚‚ã‚りã«ã™ã‚‹" @@ -838,10 +824,8 @@ deleteAccountConfirm: "アカウントを消ã™ã§ï¼Ÿãˆãˆã‚“ã‹ï¼Ÿ" incorrectPassword: "パスワードãŒã¡ã‚ƒã†ã§ã€‚" voteConfirm: "「{choice}ã€ã«æŠ•票ã™ã‚‹ã‚“ã‹ï¼Ÿ" hide: "éš ã™" -leaveGroup: "グループã‹ã‚‰æŠœã‘ã‚‹ã§" -leaveGroupConfirm: "「{name}ã€ã‹ã‚‰æŠœã‘るん?" useDrawerReactionPickerForMobile: "ケータイã¨ã‹ã®ã¨ãドãƒãƒ¯ãƒ¼ã§è¡¨ç¤ºã™ã‚‹ã§" -welcomeBackWithName: "ã¾ã„ã©ã€{name}ã•ã‚“" +welcomeBackWithName: "ã¾ã„ã©ã€{name}ã¯ã‚“" clickToFinishEmailVerification: "[{ok}]を押ã—ã¦ãƒ¡ã‚¢ãƒ‰ã®ç¢ºèªã‚’終ã‚らã›ã¦ãªãƒ¼" overridedDeviceKind: "デãƒã‚¤ã‚¹ã‚¿ã‚¤ãƒ—" smartphone: "スマホ" @@ -898,7 +882,7 @@ fast: "速ã„" sensitiveMediaDetection: "センシティブãªãƒ¡ãƒ‡ã‚£ã‚¢ã®æ¤œå‡º" localOnly: "ãƒãƒ¼ã‚«ãƒ«ã®ã¿" remoteOnly: "リモートã®ã¿" -failedToUpload: "アップãƒãƒ¼ãƒ‰ã«å¤±æ•—ã—ãŸã§" +failedToUpload: "アップãƒãƒ¼ãƒ‰ã«å¤±æ•—ã—ã¦ã‚‚ã†ãŸã‚…" cannotUploadBecauseInappropriate: "ä¸é©åˆ‡ãªå†…容をå«ã‚€ã‹ã‚‚ã—れã¸ã‚“ã£ã¦åˆ¤å®šã•れãŸã§ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã§ãã¾ã¸ã‚“。" cannotUploadBecauseNoFreeSpace: "ドライブã®ç©ºã容é‡ãŒç„¡ã„ã§ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã§ãã¾ã¸ã‚“。" beta: "ベータ" @@ -948,35 +932,224 @@ achievements: "実績" gotInvalidResponseError: "サーãƒãƒ¼é»™ã£ã¨ã‚‹ã‚ã€çŸ¥ã‚‰ã‚“ã‘ã©" gotInvalidResponseErrorDescription: "サーãƒãƒ¼ã„ã¾æ—¥æ›œæ—¥ã€‚ã¾ãŸãã¦æœˆæ›œæ—¥ã€‚" thisPostMayBeAnnoying: "ã“ã®æŠ•ç¨¿ã¯è¿·æƒ‘ã‹ã‚‚ã—らんã§ã€‚" +thisPostMayBeAnnoyingHome: "ãƒ›ãƒ¼ãƒ ã«æŠ•ç¨¿" +thisPostMayBeAnnoyingCancel: "ã‚„ã‚ã¨ã" +thisPostMayBeAnnoyingIgnore: "ã“ã®ã¾ã¾æŠ•稿" collapseRenotes: "見ãŸã“ã¨ã‚ã‚‹Renoteã¯çœç•¥ã‚„ã§" +internalServerError: "サーãƒãƒ¼å†…部エラー" +internalServerErrorDescription: "サーãƒãƒ¼å†…部ã§ã‚ˆã†åˆ†ã‹ã‚‰ã‚“エラーやã‚" +copyErrorInfo: "ã‚¨ãƒ©ãƒ¼æƒ…å ±ã‚’ã‚³ãƒ”ãƒ¼" _achievements: earnedAt: "è²°ã£ãŸæ—¥ãƒ" _types: _notes1: title: "ã¾ã„ã©ï¼" description: "åˆã‚ã¦ãƒŽãƒ¼ãƒˆæŠ•稿ã—ãŸã£ãŸ" + flavor: "Misskeyを楽ã—ã‚“ã§ãªï½ž" _notes10: title: "ノートã®å¤©ä¿å±±" + description: "ノートを10回投稿ã—ãŸ" _notes100: title: "ノートã®çœŸç”°å±±" + description: "ノートを100回投稿ã—ãŸ" _notes500: title: "ノートã®ç”Ÿé§’å±±" + description: "ノートを500回投稿ã—ãŸ" + _notes1000: + title: "ノートã®å±±" + description: "ノートを1,000回投稿ã—ãŸ" _notes5000: title: "箕é¢ã®æ»ã‹ã‚‰ãƒŽãƒ¼ãƒˆ" + description: "ノートを5,000回投稿ã—ãŸ" + _notes10000: + title: "スーパーノート" + description: "ノートを10,000回投稿ã—ãŸ" + _notes20000: + title: "ニードモアノート" + description: "ノートを20,000回投稿ã—ãŸ" + _notes30000: + title: "ノートノートノート" + description: "ノートを30,000回投稿ã—ãŸ" + _notes40000: + title: "ãƒŽãƒ¼ãƒˆå·¥å ´" + description: "ノートを40,000回投稿ã—ãŸ" + _notes50000: + title: "ãƒŽãƒ¼ãƒˆã®æƒ‘星" + description: "ノートを50,000回投稿ã—ãŸ" + _notes60000: + title: "ノートクエーサー" + description: "ノートを60,000回投稿ã—ãŸ" + _notes70000: + title: "ブラックノートホール" + description: "ノートを70,000回投稿ã—ãŸ" + _notes80000: + title: "ノートギャラクシー" + description: "ノートを80,000回投稿ã—ãŸ" + _notes90000: + title: "ノートãƒãƒ¼ã‚¹" + description: "ノートを90,000回投稿ã—ãŸ" + _notes100000: + title: "ALL YOUR NOTE ARE BELONG TO US" + description: "ノートを100,000回投稿ã—ãŸ" + flavor: "ãã‚“ãªã«æ›¸ãã“ã¨ã‚ã‚‹ã‚“ã‹ï¼Ÿ" _login3: + title: "ãƒ“ã‚®ãƒŠãƒ¼â… " + description: "通算3æ—¥ãƒã‚°ã‚¤ãƒ³ã—ãŸ" flavor: "今日ã‹ã‚‰ãƒ¯ã‚·ã¯ãƒŸã‚¹ã‚ストやã§" + _login7: + title: "ビギナーⅡ" + description: "通算7æ—¥ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + flavor: "慣れã¦ããŸã‚“ã¡ã‚ƒã†ï¼Ÿ" + _login15: + title: "ビギナーⅢ" + description: "通算15æ—¥ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + _login30: + title: "ミスã‚ã‚¹ãƒˆâ… " + description: "通算30æ—¥ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + _login60: + title: "ミスã‚ストⅡ" + description: "通算60æ—¥ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + _login100: + title: "ミスã‚ストⅢ" + description: "通算100æ—¥ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + flavor: "ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã€ãƒŸã‚¹ã‚ストã«ã¤ã" + _login200: + title: "常連ã•ã‚“â… " + description: "通算200æ—¥ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + _login300: + title: "常連ã•ã‚“â…¡" + description: "通算300æ—¥ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + _login400: + title: "常連ã•ã‚“â…¢" + description: "通算400æ—¥ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + _login500: + title: "ベテランã•ã‚“â… " + description: "通算500æ—¥ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + flavor: "ã‚ã‚“ãŸã‚‰ã€ã†ã¡ã¯ãƒŽãƒ¼ãƒˆãŒå¥½ãã‚„" + _login600: + title: "ベテランã•ã‚“â…¡" + description: "通算600æ—¥ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + _login700: + title: "ベテランã•ã‚“â…¢" + description: "通算700æ—¥ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + _login800: + title: "ãƒŽãƒ¼ãƒˆãƒžã‚¤ã‚¹ã‚¿ãƒ¼â… " + description: "通算800æ—¥ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + _login900: + title: "ノートマイスターⅡ" + description: "通算900æ—¥ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + _login1000: + title: "ノートマイスターⅢ" + description: "通算1,000æ—¥ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + flavor: "Misskeyよã†ã•ん使ã¦ã‚‚ã‚ã¦ãŠãŠãã«ãªï¼" + _noteClipped1: + title: "アカンã©ã‚Œã‚‚クリップã—ãŸã„ã‚" + description: "åˆã‚ã¦ãƒŽãƒ¼ãƒˆã‚’クリップã—ãŸ" + _noteFavorited1: + title: "星ãƒã¿ã‚‹ã²ã¨" + description: "åˆã‚ã¦ãƒŽãƒ¼ãƒˆã‚’ãŠæ°—ã«å…¥ã‚Šã«ç™»éŒ²ã—ãŸ" + _myNoteFavorited1: + title: "æ˜Ÿãƒæ¬²ã—ã„" + description: "ワレã®ãƒŽãƒ¼ãƒˆãŒä»–ã®ã²ã¨ã«ãŠæ°—ã«å…¥ã‚Šç™»éŒ²ã•れãŸã§" + _following10: + description: "フォãƒãƒ¼ãŒ10人超ãˆãŸ" + _following50: + description: "フォãƒãƒ¼ãŒ50人超ãˆãŸ" + _following100: + description: "フォãƒãƒ¼ãŒ100人超ãˆãŸ" + _following300: + description: "フォãƒãƒ¼ãŒ300人超ãˆãŸ" + _followers10: + description: "フォãƒãƒ¯ãƒ¼ãŒ10人超ãˆãŸ" + _followers50: + description: "フォãƒãƒ¯ãƒ¼ãŒ50人超ãˆãŸ" + _followers100: + description: "フォãƒãƒ¯ãƒ¼ãŒ100人超ãˆãŸ" + _followers300: + description: "フォãƒãƒ¯ãƒ¼ãŒ300人超ãˆãŸ" + _followers500: + title: "基地局" + description: "フォãƒãƒ¯ãƒ¼ãŒ500人超ãˆãŸ" + _followers1000: + title: "インフルエンサー" + description: "フォãƒãƒ¯ãƒ¼ãŒ1,000人超ãˆãŸ" + _collectAchievements30: + title: "実績コレクター" + description: "実績を30個以上ç²å¾—ã—ãŸ" + _viewAchievements3min: + title: "実績好ã" + description: "実績一覧を3分以上眺ã‚ç¶šã‘ãŸ" _iLoveMisskey: title: "Misskey好ãã‚„ãã‚“" + description: "\"I ⤠#Misskey\"を投稿ã—ãŸ" + flavor: "Misskeyを使ã£ã¦ãれã¦ã‚りãŒã¨ã†ãªï½žã€€by 開発ãƒãƒ¼ãƒ " _foundTreasure: title: "ãªã‚“ã§ã‚‚鑑定団" + description: "éš ã•れãŸãŠå®ã‚’発見ã—ãŸ" _client30min: title: "ãã‚“ã" + description: "クライアントを起動ã—ã¦ã‹ã‚‰30分以上経éŽã—ãŸ" _noteDeletedWithin1min: title: "*ãŠãŠã£ã¨ï¼Š" + description: "投稿ã—ã¦ã‹ã‚‰1分以内ã«ãã®æŠ•ç¨¿ã‚’æ¶ˆã—ãŸ" + _postedAtLateNight: + title: "夜行性" + description: "深夜ã«ãƒŽãƒ¼ãƒˆã‚’投稿ã—ãŸ" + flavor: "ãã‚ãã‚å¯ã‚ˆã‹" + _postedAt0min0sec: + title: "æ™‚å ±" + description: "0分0ç§’ã«ãƒŽãƒ¼ãƒˆã‚’投稿ã—ãŸ" + flavor: "ãƒãƒƒ ãƒãƒƒ ãƒãƒƒ ピーン" + _selfQuote: + title: "自己言åŠ" + description: "自分ã®ãƒŽãƒ¼ãƒˆã‚’引用ã—ãŸ" + _htl20npm: + title: "æµã‚Œã‚‹TL" + description: "ãƒ›ãƒ¼ãƒ ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã®æµé€ŸãŒ20npmã‚’è¶…ã™" + _viewInstanceChart: + title: "アナリスト" + description: "インスタンスã®ãƒãƒ£ãƒ¼ãƒˆã‚’表示ã—ãŸ" + _outputHelloWorldOnScratchpad: + title: "Hello, world!" + description: "スクラッãƒãƒ‘ッド㧠hello worldを出力ã—ãŸ" _open3windows: title: "マド開ã‘ã™ãŽ" + description: "ウィンドウを3ã¤ä»¥ä¸Šé–‹ã„ãŸçŠ¶æ…‹ã«ã—ãŸ" _driveFolderCircularReference: title: "環状線" + description: "ドライブã®ãƒ•ォルダをå†å¸°çš„ãªå…¥ã‚Œåã«ã—よã†ã¨ã—ãŸ" + _reactWithoutRead: + title: "ã¡ã‚ƒã‚“ã¨èªã‚“ã ã‚“ã‹ï¼Ÿ" + description: "100æ–‡å—以上ã®ãƒ†ã‚ストをå«ã‚€ãƒŽãƒ¼ãƒˆã«æŠ•稿ã•れã¦ã‹ã‚‰3秒以内ã«ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã—ãŸ" + _clickedClickHere: + title: "ã“ã“をクリック" + description: "ã“ã“をクリックã—ãŸ" + _justPlainLucky: + title: "å˜ãªã‚‹ãƒ©ãƒƒã‚ー" + description: "10ç§’ã”ã¨ã«0.005ï¼…ã®ç¢ºçއã§ç²å¾—" + _setNameToSyuilo: + title: "神様コンプレックス" + description: "åå‰ã‚’ syuilo ã«è¨å®šã—ãŸ" + _passedSinceAccountCreated1: + title: "一周年" + description: "アカウント作æˆã‹ã‚‰1年経éŽã—ãŸ" + _passedSinceAccountCreated2: + title: "二周年" + description: "アカウント作æˆã‹ã‚‰2年経éŽã—ãŸ" + _passedSinceAccountCreated3: + title: "三周年" + description: "アカウント作æˆã‹ã‚‰3年経éŽã—ãŸ" + _loggedInOnBirthday: + title: "ãƒãƒƒãƒ”ーãƒãƒ¼ã‚¹ãƒ‡ãƒ¼ï¼" + description: "誕生日ã«ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + _loggedInOnNewYearsDay: + title: "ã‚ã‘ã¾ã—ã¦ãŠã‚ã§ã¨ã†ã”ã–ã„ã¾ã™ï¼" + description: "元旦ã«ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + flavor: "今年も弊インスタンスをよã‚ã—ããŠé¡˜ã„ã—ã¾ã™" + _cookieClicked: + title: "クッã‚ーå©ãã‚„ã¤" + description: "クッã‚ーå©ã„ã¦ã‚‚ã†ãŸ" + flavor: "å…„ã¡ã‚ƒã‚“ソフト間é•ã£ã¨ã‚“ã§" _role: new: "ãƒãƒ¼ãƒ«ã®ä½œæˆ" edit: "ãƒãƒ¼ãƒ«ã®ç·¨é›†" @@ -997,6 +1170,7 @@ _role: baseRole: "ベースãƒãƒ¼ãƒ«" useBaseValue: "ベースãƒãƒ¼ãƒ«ã®å€¤ã‚’使用" chooseRoleToAssign: "アサインã™ã‚‹ãƒãƒ¼ãƒ«ã‚’é¸æŠž" + descriptionOfAsBadge: "オンã«ã™ã‚‹ã¨ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼åã®æ¨ªã‚“ã¨ã“ã«ãƒãƒ¼ãƒ«ã®ã‚¢ã‚¤ã‚³ãƒ³ãŒè¡¨ç¤ºã•れるã§ã€‚" canEditMembersByModerator: "モデレーターã®ãƒ¡ãƒ³ãƒãƒ¼ç·¨é›†ã‚’許å¯" descriptionOfCanEditMembersByModerator: "オンã«ã™ã‚‹ã¨ã€ç®¡ç†è€…ã«åŠ ãˆã¦ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚¿ãƒ¼ã‚‚ã“ã®ãƒãƒ¼ãƒ«ã¸ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’アサイン/アサイン解除ã§ãるよã†ã«ãªã‚‹ã§ã€‚オフã«ã™ã‚‹ã¨ç®¡ç†è€…ã®ã¿ãŒè¡Œãˆã‚‹ã§ã€‚" priority: "優先度" @@ -1284,14 +1458,14 @@ _tutorial: step8_3: "通知ã®è¨å®šã¯ã‚ã¨ã‹ã‚‰å¤‰æ›´ã§ãã‚‹ã§" _2fa: alreadyRegistered: "ã‚‚ã†è¨å®šçµ‚ã‚ã£ã¨ã‚‹ã‚。" - registerDevice: "デãƒã‚¤ã‚¹ã‚’登録ã™ã‚‹ã§" - registerKey: "ã‚ーを登録ã™ã‚‹ã§" step1: "ã»ã‚“ãªã‚‰ã€{a}ã‚„{b}ã¨ã‹ã®èªè¨¼ã‚¢ãƒ—リを使ã£ã¨ã‚‹ãƒ‡ãƒã‚¤ã‚¹ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ã¦ãªã€‚" step2: "次ã«ã€ã“ã“ã«ã‚ã‚‹QRコードをアプリã§ã‚¹ã‚ャンã—ã¦ãªï½žã€‚" step2Url: "デスクトップアプリやã£ãŸã‚‰æ¬¡ã®URLを入力ã—ã¦ã‚„:" step3: "アプリã«è¡¨ç¤ºã•れã¦ã„るトークンを入力ã—ã¦çµ‚ã‚りや。" step4: "ã“れã‹ã‚‰ãƒã‚°ã‚¤ãƒ³ã™ã‚‹ã¨ãã‚‚ã€åŒã˜ã‚ˆã†ã«ãƒˆãƒ¼ã‚¯ãƒ³ã‚’入力ã™ã‚‹ã‚“ã‚„ã§" securityKeyInfo: "FIDO2をサãƒãƒ¼ãƒˆã™ã‚‹ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã‚»ã‚ュリティã‚ーã‹ç«¯æœ«ã®æŒ‡ç´‹èªè¨¼ã‚„PINを使ã£ã¦ãƒã‚°ã‚¤ãƒ³ã™ã‚‹ã‚ˆã†ã«è¨å®šã§ãã‚‹ã§ã€‚" + removeKeyConfirm: "{name}を消ã™ã‚“?" + renewTOTPCancel: "ã‚„ã‚ã¨ã" _permissions: "read:account": "ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®æƒ…å ±ã‚’è¦‹ã‚‹ã§" "write:account": "ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®æƒ…å ±ã‚’å¤‰æ›´ã™ã‚‹ã§" @@ -1339,7 +1513,6 @@ _antennaSources: homeTimeline: "フォãƒãƒ¼ã—ã¨ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆ" users: "é¸ã‚‰ã‚“ã 一人ã‹è¤‡æ•°ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆ" userList: "é¸ã‚“ã リストã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆ" - userGroup: "é¸ã‚“ã グループã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆ" _weekday: sunday: "日曜日" monday: "月曜日" @@ -1414,8 +1587,6 @@ _visibility: followersDescription: "自分ã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã®ã¿ã«å…¬é–‹ã™ã‚‹ã§" specified: "ダイレクト" specifiedDescription: "é¸ã‚“ã ユーザーã®ã¿ã«å…¬é–‹ã™ã‚‹ã§" - localOnly: "ãƒãƒ¼ã‚«ãƒ«ã®ã¿" - localOnlyDescription: "リモートユーザーã«ã¯éžå…¬é–‹ã«ã™ã‚‹ã§" _postForm: replyPlaceholder: "ã“ã®ãƒŽãƒ¼ãƒˆã«è¿”ä¿¡..." quotePlaceholder: "ã“ã®ãƒŽãƒ¼ãƒˆã‚’引用..." @@ -1510,7 +1681,7 @@ _pages: viewPage: "ページを見る" like: "ãˆãˆã‚„ã‚“" unlike: "良ããªã„ã‚" - my: "人気ã®ãƒšãƒ¼ã‚¸" + my: "自分ã®ãƒšãƒ¼ã‚¸" liked: "ãˆãˆã¨æ€ã£ãŸãƒšãƒ¼ã‚¸" featured: "人気" inspector: "インスペクター" @@ -1553,12 +1724,9 @@ _notification: youGotReply: "{name}ã‹ã‚‰ã®ãƒªãƒ—ライ" youGotQuote: "{name}ã«ã‚ˆã‚‹å¼•用" youRenoted: "{name}ãŒRenoteã—ãŸã¿ãŸã„ã‚„ã§" - youGotMessagingMessageFromUser: "{name}ã‹ã‚‰ã®ãƒãƒ£ãƒƒãƒˆãŒã‚ã‚‹ã§" - youGotMessagingMessageFromGroup: "{name}ã®ãƒãƒ£ãƒƒãƒˆãŒã‚ã‚‹ã§" youWereFollowed: "フォãƒãƒ¼ã•れãŸã§" youReceivedFollowRequest: "フォãƒãƒ¼è¨±å¯ã—ã¦ã»ã—ã„ã¿ãŸã„ã‚„ãª" yourFollowRequestAccepted: "フォãƒãƒ¼ã•ã›ã¦ã‚‚ã‚ãŸã§" - youWereInvitedToGroup: "ã‚°ãƒ«ãƒ¼ãƒ—ã«æ‹›å¾…ã•れã¨ã‚‹ã§" pollEnded: "アンケートã®çµæžœãŒå‡ºãŸã¿ãŸã„ã‚„" unreadAntennaNote: "アンテナ {name}" emptyPushNotificationMessage: "ãƒ—ãƒƒã‚·ãƒ¥é€šçŸ¥ã®æ›´æ–°ã‚’ã—ã¨ã„ãŸã§" @@ -1574,7 +1742,6 @@ _notification: pollEnded: "アンケートãŒçµ‚了ã—ãŸã§" receiveFollowRequest: "フォãƒãƒ¼è¨±å¯ã—ã¦ã»ã—ã„ã¿ãŸã„ã‚„ã§" followRequestAccepted: "フォãƒãƒ¼ãŒå—ç†ã•れãŸã§" - groupInvited: "ã‚°ãƒ«ãƒ¼ãƒ—ã«æ‹›å¾…ã•れãŸã§" app: "連æºã‚¢ãƒ—リã‹ã‚‰ã®é€šçŸ¥ã‚„" _actions: followBack: "フォãƒãƒ¼ãƒãƒƒã‚¯" diff --git a/locales/kab-KAB.yml b/locales/kab-KAB.yml index fcd490db12..18fd8f5a58 100644 --- a/locales/kab-KAB.yml +++ b/locales/kab-KAB.yml @@ -39,10 +39,8 @@ remove: "Kkes" connectService: "Qqen" userList: "Tibdarin" securityKey: "Tasarutt n tÉ£ellist" -securityKeyName: "Isem n tsarutt" signinRequired: "Ttxil jerred" signinWith: "Tuqqna s {x}" -tapSecurityKey: "Sekcem tasarutt-ik·im n tÉ£ellist" uiLanguage: "Tutlayt n wegrudem" accountSettings: "IÉ£ewwaá¹›en n umiá¸an" plugins: "Izegrar" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 5096a5d316..1c5cf8517c 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -15,7 +15,7 @@ gotIt: "ì•Œê² ì–´ìš”" cancel: "취소" noThankYou: "나중ì—" enterUsername: "ìœ ì €ëª… ìž…ë ¥" -renotedBy: "{user}ë‹˜ì´ ë¦¬ë…¸íŠ¸" +renotedBy: "{user}ë‹˜ì˜ ë¦¬ë…¸íŠ¸" noNotes: "노트가 없습니다" noNotifications: "í‘œì‹œí• ì•Œë¦¼ì´ ì—†ìŠµë‹ˆë‹¤" instance: "ì¸ìŠ¤í„´ìŠ¤" @@ -103,6 +103,8 @@ renoted: "리노트했습니다" cantRenote: "ì´ ê²Œì‹œë¬¼ì€ ë¦¬ë…¸íŠ¸ í• ìˆ˜ 없습니다." cantReRenote: "리노트를 리노트 í• ìˆ˜ 없습니다." quote: "ì¸ìš©" +inChannelRenote: "ì±„ë„ ë‚´ 리노트" +inChannelQuote: "ì±„ë„ ë‚´ ì¸ìš©" pinnedNote: "ê³ ì •í•´ë†“ì€ ë…¸íŠ¸" pinned: "í”„ë¡œí•„ì— ê³ ì •" you: "ë‹¹ì‹ " @@ -391,13 +393,10 @@ about: "ì •ë³´" aboutMisskey: "Misskeyì— ëŒ€í•˜ì—¬" administrator: "관리ìž" token: "í† í°" -twoStepAuthentication: "2단계 ì¸ì¦" moderator: "모ë”ë ˆì´í„°" moderation: "모ë”ë ˆì´ì…˜" nUsersMentioned: "{n}ëª…ì´ ì–¸ê¸‰í•¨" securityKey: "보안 키" -securityKeyName: "키 ì´ë¦„" -registerSecurityKey: "보안 키를 등ë¡" lastUsed: "마지막 사용" unregister: "ë“±ë¡ í•´ì œ" passwordLessLogin: "비밀번호 ì—†ì´ ë¡œê·¸ì¸" @@ -415,24 +414,15 @@ markAsReadAllTalkMessages: "ëª¨ë“ ëŒ€í™”ë¥¼ ì½ì€ ìƒíƒœë¡œ 표시" help: "ë„움ë§" inputMessageHere: "ì—¬ê¸°ì— ë©”ì‹œì§€ë¥¼ ìž…ë ¥í•˜ì„¸ìš”" close: "닫기" -group: "그룹" -groups: "그룹" -createGroup: "그룹 만들기" -ownedGroups: "ì†Œìœ ê·¸ë£¹" -joinedGroups: "ì°¸ì—¬ì¤‘ì¸ ê·¸ë£¹" invites: "초대" -groupName: "그룹명" members: "멤버" transfer: "ì–‘ë„" -messagingWithUser: "ìœ ì €ì™€ 대화하기" -messagingWithGroup: "그룹ë¼ë¦¬ 대화하기" title: "ì œëª©" text: "í…스트" enable: "사용" next: "다ìŒ" retype: "다시 ìž…ë ¥" noteOf: "{user}ì˜ ë…¸íŠ¸" -inviteToGroup: "ê·¸ë£¹ì— ì´ˆëŒ€í•˜ê¸°" quoteAttached: "ì¸ìš©í•¨" quoteQuestion: "ì¸ìš©í•´ì„œ ìž‘ì„±í•˜ì‹œê² ìŠµë‹ˆê¹Œ?" noMessagesYet: "ì•„ì§ ëŒ€í™”ê°€ 없습니다" @@ -454,17 +444,13 @@ passwordMatched: "ì¼ì¹˜í•©ë‹ˆë‹¤" passwordNotMatched: "ì¼ì¹˜í•˜ì§€ 않습니다" signinWith: "{x}로 로그ì¸" signinFailed: "로그ì¸í• 수 없습니다. 사용ìžëª…ê³¼ 비밀번호를 확ì¸í•˜ì—¬ 주ì‹ì‹œì˜¤." -tapSecurityKey: "보안 키를 터치" or: "혹ì€" language: "언어" uiLanguage: "UI 표시 언어" -groupInvited: "ê·¸ë£¹ì— ì´ˆëŒ€ë˜ì—ˆìŠµë‹ˆë‹¤" aboutX: "{x}ì— ëŒ€í•˜ì—¬" emojiStyle: "ì´ëª¨ì§€ 스타ì¼" native: "네ì´í‹°ë¸Œ" disableDrawer: "드로어 메뉴를 사용하지 않기" -youHaveNoGroups: "ê·¸ë£¹ì´ ì—†ìŠµë‹ˆë‹¤" -joinOrCreateGroup: "다른 ê·¸ë£¹ì˜ ì´ˆëŒ€ë¥¼ 받거나, ì§ì ‘ 새 ê·¸ë£¹ì„ ë§Œë“¤ì–´ 보세요." noHistory: "기ë¡ì´ 없습니다" signinHistory: "ë¡œê·¸ì¸ ê¸°ë¡" enableAdvancedMfm: "ê³ ê¸‰ MFMì„ í™œì„±í™”" @@ -838,8 +824,6 @@ deleteAccountConfirm: "ê³„ì •ì´ ì‚ì œë˜ê³ ë˜ëŒë¦´ 수 없게 ë©ë‹ˆë‹¤. ê³ incorrectPassword: "비밀번호가 올바르지 않습니다." voteConfirm: "\"{choice}\"ì— íˆ¬í‘œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?" hide: "숨기기" -leaveGroup: "그룹 나가기" -leaveGroupConfirm: "\"{name}\"ì—서 나갈까요?" useDrawerReactionPickerForMobile: "모바ì¼ì—서 드로어 메뉴로 표시" welcomeBackWithName: "환ì˜í•©ë‹ˆë‹¤, {name}님" clickToFinishEmailVerification: "[{ok}]를 눌러 ì´ë©”ì¼ ì¸ì¦ì„ 완료하세요." @@ -951,6 +935,10 @@ thisPostMayBeAnnoying: "ì´ ê²Œì‹œë¬¼ì€ ë‹¤ë¥¸ ìœ ì €ì—게 피해를 줄 ê°€ëŠ thisPostMayBeAnnoyingHome: "í™ˆì— ê²Œì‹œ" thisPostMayBeAnnoyingCancel: "그만ë‘기" thisPostMayBeAnnoyingIgnore: "ì´ëŒ€ë¡œ 게시" +collapseRenotes: "ì´ë¯¸ 본 리노트를 간략화하기" +internalServerError: "ë‚´ë¶€ 서버 오류" +internalServerErrorDescription: "ë‚´ë¶€ 서버ì—서 예기치 ì•Šì€ ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤." +copyErrorInfo: "오류 ì •ë³´ 복사" _achievements: earnedAt: "달성 ì¼ì‹œ" _types: @@ -1232,7 +1220,7 @@ _role: noteEachClipsMax: "ê° í´ë¦½ì— ì¶”ê°€í• ìˆ˜ 있는 노트 수" userListMax: "ìƒì„±í• 수 있는 ìœ ì € 리스트 수" userEachUserListsMax: "ìœ ì € 리스트당 최대 ì‚¬ìš©ìž ìˆ˜" - rateLimitFactor: "ì†ë„ ì œí•œ" + rateLimitFactor: "ìš”ì² ë¹ˆë„ ì œí•œ" descriptionOfRateLimitFactor: "ìž‘ì„ìˆ˜ë¡ ì œí•œì´ ì™„í™”ë˜ê³ , í´ìˆ˜ë¡ ì œí•œì´ ê°•í™”ë©ë‹ˆë‹¤." canHideAds: "ê´‘ê³ ìˆ¨ê¸°ê¸°" _condition: @@ -1497,14 +1485,14 @@ _tutorial: step8_3: "알림 ì„¤ì •ì€ ë‚˜ì¤‘ì—ë„ ë³€ê²½í• ìˆ˜ 있습니다." _2fa: alreadyRegistered: "ì´ë¯¸ ì„¤ì •ì´ ì™„ë£Œë˜ì—ˆìŠµë‹ˆë‹¤." - registerDevice: "디바ì´ìФ 등ë¡" - registerKey: "키를 등ë¡" step1: "ë¨¼ì €, {a}나 {b}ë“±ì˜ ì¸ì¦ ì•±ì„ ì‚¬ìš© ì¤‘ì¸ ë””ë°”ì´ìŠ¤ì— ì„¤ì¹˜í•©ë‹ˆë‹¤." step2: "ê·¸ 후, 표시ë˜ì–´ 있는 QR코드를 앱으로 스캔합니다." step2Url: "ë°ìФí¬í†± 앱ì—서는 ë‹¤ìŒ URLì„ ìž…ë ¥í•˜ì„¸ìš”:" step3: "ì•±ì— í‘œì‹œëœ í† í°ì„ ìž…ë ¥í•˜ì‹œë©´ 완료ë©ë‹ˆë‹¤." step4: "ë‹¤ìŒ ë¡œê·¸ì¸ë¶€í„°ëŠ” í† í°ì„ ìž…ë ¥í•´ì•¼ 합니다." securityKeyInfo: "FIDO2를 ì§€ì›í•˜ëŠ” 하드웨어 보안 키 í˜¹ì€ ë””ë°”ì´ìŠ¤ì˜ ì§€ë¬¸ì¸ì‹ì´ë‚˜ í™”ë©´ìž ê¸ˆ PINì„ ì´ìš©í•´ì„œ 로그ì¸í•˜ë„ë¡ ì„¤ì •í• ìˆ˜ 있습니다." + removeKeyConfirm: "{name} ì„(를) ì‚ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?" + renewTOTPCancel: "나중ì—" _permissions: "read:account": "ê³„ì •ì˜ ì •ë³´ë¥¼ 봅니다" "write:account": "ê³„ì •ì˜ ì •ë³´ë¥¼ 변경합니다" @@ -1553,7 +1541,6 @@ _antennaSources: homeTimeline: "íŒ”ë¡œìš°ì¤‘ì¸ ìœ ì €ì˜ ë…¸íŠ¸" users: "ì§€ì •í•œ 한 명 í˜¹ì€ ì—¬ëŸ¬ ëª…ì˜ ìœ ì €ì˜ ë…¸íŠ¸" userList: "ì§€ì •í•œ ë¦¬ìŠ¤íŠ¸ì— ì†í•œ ìœ ì €ì˜ ë…¸íŠ¸" - userGroup: "ì§€ì •í•œ ê·¸ë£¹ì— ì†í•œ ìœ ì €ì˜ ë…¸íŠ¸" _weekday: sunday: "ì¼ìš”ì¼" monday: "월요ì¼" @@ -1628,8 +1615,6 @@ _visibility: followersDescription: "팔로워ì—게만 공개" specified: "다ì´ë ‰íЏ" specifiedDescription: "ì§€ì •í•œ ìœ ì €ì—게만 공개" - localOnly: "로컬ì—ë§Œ" - localOnlyDescription: "리모트 ìœ ì €ì—게 ë³´ì´ì§€ 않기" _postForm: replyPlaceholder: "ì´ ë…¸íŠ¸ì— ë‹µê¸€..." quotePlaceholder: "ì´ ë…¸íŠ¸ë¥¼ ì¸ìš©..." @@ -1767,12 +1752,9 @@ _notification: youGotReply: "{name}ë‹˜ì´ ë‹µê¸€í•¨" youGotQuote: "{name}ë‹˜ì´ ì¸ìš©í•¨" youRenoted: "{name}ë‹˜ì´ Renote" - youGotMessagingMessageFromUser: "{name} ë‹˜ì´ ë³´ë‚¸ ì±„íŒ…ì´ ìžˆì–´ìš”" - youGotMessagingMessageFromGroup: "{name}ì—서 보낸 ì±„íŒ…ì´ ìžˆì–´ìš”" youWereFollowed: "새로운 팔로워가 있습니다" youReceivedFollowRequest: "새로운 팔로우 ìš”ì²ì´ 있습니다" yourFollowRequestAccepted: "팔로우 ìš”ì²ì´ 수ë½ë˜ì—ˆìŠµë‹ˆë‹¤" - youWereInvitedToGroup: "ê·¸ë£¹ì— ì´ˆëŒ€ë˜ì—ˆìŠµë‹ˆë‹¤" pollEnded: "투표 결과가 발표ë˜ì—ˆìŠµë‹ˆë‹¤" unreadAntennaNote: "안테나 {name}" emptyPushNotificationMessage: "푸시 ì•Œë¦¼ì´ ê°±ì‹ ë˜ì—ˆìŠµë‹ˆë‹¤" @@ -1788,7 +1770,6 @@ _notification: pollEnded: "투표가 종료ë¨" receiveFollowRequest: "팔로우 ìš”ì²ì„ ë°›ì•˜ì„ ë•Œ" followRequestAccepted: "팔로우 ìš”ì²ì´ 승ì¸ë˜ì—ˆì„ 때" - groupInvited: "ê·¸ë£¹ì— ì´ˆëŒ€ë˜ì—ˆì„ 때" app: "ì—°ë™ëœ ì•±ì„ í†µí•œ 알림" _actions: followBack: "팔로우" diff --git a/locales/lo-LA.yml b/locales/lo-LA.yml index 263a7eb18c..ce596d038f 100644 --- a/locales/lo-LA.yml +++ b/locales/lo-LA.yml @@ -136,7 +136,29 @@ loginFailed: "àºàº²àº™à»€àº‚ົ້າສູ່ລະບົບບà»à»ˆàºªàº³à»€ general: "ທົ່ວໄປ" wallpaper: "ພາບພື້ນຫລັງ" setWallpaper: "ຕັ້ງເປັນພາບພື້ນຫຼັງ" +searchWith: "ຊàºàºàº«àº²: {q}" +proxyAccount: "ບັນຊີພຣັàºàºàºŠàºµ" +host: "ໂຮດສ" +selectUser: "ເລືàºàºàºœàº¹à»‰à»ƒàºŠà»‰" +recipient: "ເຖິງ" +annotation: "ຄຳເຫັນ" +federation: "ສະຫະພັນ" instances: "àºàºµàº™àºªàº°à»àº•ນ" +registeredAt: "ລົງທະບຽນຢູ່" +storageUsage: "ບ່àºàº™â€‹àºˆàº±àº”​ເàºàº±àºšâ€‹àº‚à»à»‰â€‹àº¡àº¹àº™àº—ີ່ໃຊ້" +charts: "àºàº±àº™àº”ັບເພງ" +perHour: "ຕà»à»ˆàºŠàº»à»ˆàº§à»‚ມງ" +perDay: "ຕà»à»ˆâ€‹àº¡àº·à»‰" +stopActivityDelivery: "ຢຸດເຊົາàºàº²àº™àºªàº»à»ˆàº‡àºàº´àº”ຈະàºà»àº²" +blockThisInstance: "ຂັດຂວາງຕົວຢ່າງນີ້" +operations: "àºàº²àº™àº”ຳເນີນງານ" +software: "ຊàºàºšà»àº§" +version: "ສະບັບ" +metadata: "Metadata" +monitor: "ຈà»àºžàº²àºš" +cpuAndMemory: "CPU à»àº¥àº° ຫນ່ວàºàº„ວາມຈà»àº²" +network: "ເຄືàºàº‚່າàº" +disk: "ດິສàºà»Œ" instanceInfo: "àºàºµàº™àºªàº°à»àº•ນ" statistics: "ສະຖິຕິ" clearQueue: "ລ້າງຄິວ" @@ -178,6 +200,7 @@ nsfw: "NSFW" accept: "àºàº°àº™àº¸àºàº²àº”" pinnedNotes: "ບັນທຶàºàº—ີ່ປັàºà»àº¸àº”ໄວ້" userList: "ລາàºàºàº²àº™" +smtpHost: "ໂຮດສ" smtpUser: "ຊື່ຜູ້ໃຊ້" smtpPass: "ລະຫັດຜ່ານ" clearCache: "ລຶບລ້າງà»àº„ສ" @@ -195,11 +218,14 @@ _sfx: note: "ບັນທຶàº" notification: "àºàº²àº™à»àºˆà»‰àº‡à»€àº•ືàºàº™" chat: "à»àºŠà»‹àº”" +_2fa: + renewTOTPCancel: "ບà»à»ˆâ€‹à»àº¡à»ˆàº™â€‹àº•àºàº™â€‹àº™àºµà»‰" _widgets: profile: "ໂພຼຟາàº" instanceInfo: "àºàºµàº™àºªàº°à»àº•ນ" notifications: "àºàº²àº™à»àºˆà»‰àº‡à»€àº•ືàºàº™" timeline: "​ເສັ້ນàºàº³â€‹àº™àº»àº”​ເວ​ລາ​" + federation: "ສະຫະພັນ" _userList: chooseList: "ເລືàºàºàºšàº±àº™àºŠàºµàº¥àº²àºàºàº²àº™" _cw: @@ -214,6 +240,8 @@ _exportOrImport: muteList: "ປີດສຽງ" blockingList: "ບ໋àºàº" userLists: "ລາàºàºàº²àº™" +_charts: + federation: "ສະຫະພັນ" _timelines: home: "ໜ້າຫຼັàº" _pages: diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml index a05d106652..3d33b5227e 100644 --- a/locales/nl-NL.yml +++ b/locales/nl-NL.yml @@ -376,13 +376,10 @@ about: "Over" aboutMisskey: "Over Misskey" administrator: "Beheerder" token: "Token" -twoStepAuthentication: "Tweestapsverificatie" moderator: "Moderator" moderation: "Moderatie" nUsersMentioned: "Vermeld door {n} gebruikers" securityKey: "Beveiligingssleutel" -securityKeyName: "Sleutelnaam" -registerSecurityKey: "Zekerheids-Sleutel registreren" lastUsed: "Laatst gebruikt" unregister: "Uitschrijven" passwordLessLogin: "Inloggen zonder wachtwoord" @@ -399,8 +396,6 @@ markAsReadAllTalkMessages: "Markeer alle berichten als gelezen" help: "Help" inputMessageHere: "Voer hier je bericht in" close: "Sluiten" -group: "Groep" -groups: "Groepen" invites: "Uitnodigen" invitations: "Uitnodigen" sound: "Geluid" @@ -435,6 +430,8 @@ _sfx: note: "Notities" notification: "Meldingen" chat: "Chat" +_2fa: + renewTOTPCancel: "Nee, bedankt" _widgets: profile: "Profiel" instanceInfo: "Serverinformatie" diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index 0874dded9c..3003e93254 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -385,13 +385,10 @@ about: "Informacje" aboutMisskey: "O Misskey" administrator: "Admin" token: "Token" -twoStepAuthentication: "Uwierzytelnianie dwuskÅ‚adnikowe" moderator: "Moderator" moderation: "Moderacja" nUsersMentioned: "{n} wspomnianych użytkowników" securityKey: "Klucz bezpieczeÅ„stwa" -securityKeyName: "Nazwa klucza" -registerSecurityKey: "Zarejestruj klucz bezpieczeÅ„stwa" lastUsed: "Ostatnio używane" unregister: "Cofnij rejestracjÄ™" passwordLessLogin: "Skonfiguruj logowanie bez użycia hasÅ‚a" @@ -409,24 +406,15 @@ markAsReadAllTalkMessages: "Oznacz wszystkie wiadomoÅ›ci jako przeczytane" help: "Pomoc" inputMessageHere: "Wprowadź wiadomość tutaj" close: "Zamknij" -group: "Grupy" -groups: "Grupy" -createGroup: "Utwórz grupÄ™" -ownedGroups: "Posiadane grupy" -joinedGroups: "CzÅ‚onkostwa w grupach" invites: "ZaproÅ›" -groupName: "Nazwa grupy" members: "CzÅ‚onkowie" transfer: "Transfer" -messagingWithUser: "Rozmowy z innym użytkownikiem" -messagingWithGroup: "Rozmowy wewnÄ…trz grupy" title: "TytuÅ‚" text: "Tekst" enable: "Włącz" next: "Dalej" retype: "Wprowadź ponownie" noteOf: "Wpisy {user}" -inviteToGroup: "ZaproÅ› do grupy" quoteAttached: "Zacytowano" quoteQuestion: "Czy na pewno chcesz umieÅ›cić cytat?" noMessagesYet: "Nie napisano jeszcze wiadomoÅ›ci" @@ -448,17 +436,13 @@ passwordMatched: "Pasuje" passwordNotMatched: "HasÅ‚a nie pasujÄ… do siebie" signinWith: "Zaloguj siÄ™ z {x}" signinFailed: "Nie udaÅ‚o siÄ™ zalogować. Wprowadzona nazwa użytkownika lub hasÅ‚o sÄ… nieprawidÅ‚owe." -tapSecurityKey: "Wybierz swój klucz bezpieczeÅ„stwa" or: "Lub" language: "JÄ™zyk" uiLanguage: "JÄ™zyk wyÅ›wietlania UI" -groupInvited: "Zaproszony(-a) do grupy" aboutX: "O {x}" emojiStyle: "Styl emoji" native: "Natywny" disableDrawer: "Nie używaj menu w stylu szuflady" -youHaveNoGroups: "Nie masz żadnych grup" -joinOrCreateGroup: "Uzyskaj zaproszenie do dołączenia do grupy lub utwórz wÅ‚asnÄ… grupÄ™." noHistory: "Brak historii" signinHistory: "Historia logowania" doing: "Przetwarzanie..." @@ -816,8 +800,6 @@ deleteAccountConfirm: "Spowoduje to nieodwracalne usuniÄ™cie Twojego konta. Kont incorrectPassword: "NieprawidÅ‚owe hasÅ‚o." voteConfirm: "Potwierdzić swój gÅ‚os na \"{choice}\"?" hide: "Ukryj" -leaveGroup: "Opuść grupÄ™" -leaveGroupConfirm: "Czy na pewno chcesz opuÅ›cić \"{name}\"?" useDrawerReactionPickerForMobile: "WyÅ›wietlaj wybornik reakcji jako szufladÄ™ na urzÄ…dzeniach mobilnych" welcomeBackWithName: "Witaj z powrotem, {name}" clickToFinishEmailVerification: "Kliknij [{ok}], aby zakoÅ„czyć weryfikacjÄ™ e-mail." @@ -1112,12 +1094,12 @@ _tutorial: step8_3: "Ustawienia powiadomieÅ„ można zmienić później." _2fa: alreadyRegistered: "ZarejestrowaÅ‚eÅ› już urzÄ…dzenie do uwierzytelniania dwuskÅ‚adnikowego." - registerDevice: "Zarejestruj nowe urzÄ…dzenie" - registerKey: "Zarejestruj klucz bezpieczeÅ„stwa" step1: "Najpierw, zainstaluj aplikacjÄ™ uwierzytelniajÄ…cÄ… (takÄ… jak {a} lub {b}) na swoim urzÄ…dzeniu." step2: "NastÄ™pnie, zeskanuje kod QR z ekranu." step3: "Wprowadź token podany w aplikacji, aby ukoÅ„czyć konfiguracjÄ™." step4: "Od teraz, przy każdej próbie logowania otrzymasz proÅ›bÄ™ o token logowania." + removeKeyConfirm: "Usunąć kopiÄ™ zapasowÄ… {name}?" + renewTOTPCancel: "Nie teraz" _permissions: "read:account": "WyÅ›wietl informacje o swoim koncie" "write:account": "Edytuj swoje informacje o koncie" @@ -1328,12 +1310,9 @@ _notification: youGotReply: "{name} odpowiedziaÅ‚(a) Tobie" youGotQuote: "{name} zacytowaÅ‚(a) Ciebie" youRenoted: "{name} udostÄ™pniÅ‚(a) Twój wpis" - youGotMessagingMessageFromUser: "{name} wysÅ‚aÅ‚(a) Ci wiadomość" - youGotMessagingMessageFromGroup: "ZostaÅ‚a wysÅ‚ana wiadomość do grupy {name}" youWereFollowed: "ZaobserwowaÅ‚(a) CiÄ™" youReceivedFollowRequest: "OtrzymaÅ‚eÅ›(-aÅ›) proÅ›bÄ™ o możliwość obserwacji" yourFollowRequestAccepted: "Twoja proÅ›ba o możliwość obserwacji zostaÅ‚a przyjÄ™ta" - youWereInvitedToGroup: "Zaproszony(-a) do grupy" pollEnded: "Wyniki ankiety staÅ‚y siÄ™ dostÄ™pne" unreadAntennaNote: "Antena {name}" emptyPushNotificationMessage: "Powiadomienia push zostaÅ‚y zaktualizowane" @@ -1347,7 +1326,6 @@ _notification: reaction: "Reakcja" receiveFollowRequest: "Otrzymano proÅ›bÄ™ o możliwość obserwacji" followRequestAccepted: "PrzyjÄ™to proÅ›bÄ™ o możliwość obserwacji" - groupInvited: "Zaproszono do grup" app: "Powiadomienia z aplikacji" _actions: followBack: "zaobserwowaÅ‚ ciÄ™ z powrotem" diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml index 6ca41a395f..40b4aee7e6 100644 --- a/locales/pt-PT.yml +++ b/locales/pt-PT.yml @@ -382,12 +382,9 @@ about: "Informações" aboutMisskey: "Sobre Misskey" administrator: "Administrador" token: "SÃmbolo" -twoStepAuthentication: "Verificação em duas etapas" moderator: "Moderador" nUsersMentioned: "Postado por {n} pessoas" securityKey: "Chave de segurança" -securityKeyName: "Nome chave" -registerSecurityKey: "Registre a chave de segurança" lastUsed: "Último uso" unregister: "Cancelar registro" passwordLessLogin: "Entrar sem senha" @@ -405,10 +402,6 @@ markAsReadAllTalkMessages: "Marcar todas as conversas como lidas" help: "Ajuda" inputMessageHere: "Escrever mensagem aqui" close: "Fechar" -group: "Grupos" -groups: "Grupos" -createGroup: "Criar grupo" -ownedGroups: "Grupo próprio" invites: "Convidar" invitations: "Convidar" tags: "Etiquetas" @@ -522,12 +515,9 @@ _notification: youGotMention: "{name} te mencionou" youGotReply: "{name} te respondeu" youGotQuote: "{name} te citou" - youGotMessagingMessageFromUser: "{name} te mandou uma mensagem de bate-papo" - youGotMessagingMessageFromGroup: "Uma mensagem foi mandada para o grupo {name}" youWereFollowed: "Você tem um novo seguidor" youReceivedFollowRequest: "Você recebeu um pedido de seguimento" yourFollowRequestAccepted: "Seu pedido de seguimento foi aceito" - youWereInvitedToGroup: "{userName} te convidou para um grupo" pollEnded: "Os resultados da enquete agora estão disponÃveis" emptyPushNotificationMessage: "As notificações de alerta foram atualizadas" _types: @@ -541,7 +531,6 @@ _notification: pollEnded: "Enquetes terminando" receiveFollowRequest: "Recebeu pedidos de seguimento" followRequestAccepted: "Aceitou pedidos de seguimento" - groupInvited: "Convites de grupo" app: "Notificações de aplicativos conectados" _actions: followBack: "te seguiu de volta" diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml index 551652ef46..c3438172ca 100644 --- a/locales/ro-RO.yml +++ b/locales/ro-RO.yml @@ -382,12 +382,9 @@ about: "Despre" aboutMisskey: "Despre Misskey" administrator: "Administrator" token: "Token" -twoStepAuthentication: "Autentificare în doi paÈ™i" moderator: "Moderator" nUsersMentioned: "MenÈ›ionat de {n} utilizatori" securityKey: "Cheie de securitate" -securityKeyName: "Numele cheii" -registerSecurityKey: "ÃŽnregistrează o cheie de securitate" lastUsed: "Ultima utilizată" unregister: "Dezînregistrează" passwordLessLogin: "Autentificare fără parolă" @@ -405,24 +402,15 @@ markAsReadAllTalkMessages: "Marchează toate mesajele drept citit" help: "Ajutor" inputMessageHere: "Introdu un mesaj aici" close: "ÃŽnchide" -group: "Grup" -groups: "Grupuri" -createGroup: "Crează un grup" -ownedGroups: "Grupuri deÈ›inute" -joinedGroups: "Grupuri alăturate" invites: "Invită" -groupName: "Numele grupului" members: "Membri" transfer: "Transferă" -messagingWithUser: "Chat privat" -messagingWithGroup: "Chat de grup" title: "Titlu" text: "Text" enable: "Activează" next: "Următorul" retype: "Introdu din nou" noteOf: "Notă de {user}" -inviteToGroup: "Invită în grup" quoteAttached: "Citat" quoteQuestion: "Vrei să adaugi ca citat?" noMessagesYet: "Niciun mesaj încă" @@ -444,15 +432,11 @@ passwordMatched: "Se potriveÈ™te!" passwordNotMatched: "Nu se potriveÈ™te" signinWith: "Autentifică-te cu {x}" signinFailed: "Nu se poate autentifica. Numele de utilizator sau parola introduse sunt incorecte." -tapSecurityKey: "Apasă pe cheia ta de securitate." or: "Sau" language: "Limbă" uiLanguage: "Limba interfeÈ›ei" -groupInvited: "Ai fost invitat într-un grup" aboutX: "Despre {x}" disableDrawer: "Nu folosi meniuri în stil sertar" -youHaveNoGroups: "Nu ai niciun grup" -joinOrCreateGroup: "PrimeÈ™te o invitaÈ›ie într-un grup sau creează unul nou." noHistory: "Nu există istoric" signinHistory: "Istoric autentificări" doing: "Se procesează..." @@ -699,7 +683,6 @@ _pages: image: "Imagini" _notification: youWereFollowed: "te-a urmărit" - youWereInvitedToGroup: "Ai fost invitat într-un grup" _types: follow: "UrmăreÈ™ti" mention: "MenÈ›iune" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 1abd79c983..70839694b4 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -8,7 +8,7 @@ search: "ПоиÑк" notifications: "УведомлениÑ" username: "Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ" password: "Пароль" -forgotPassword: "Пароль забыт" +forgotPassword: "Забыли пароль?" fetchingAsApObject: "Приём Ñ Ð´Ñ€ÑƒÐ³Ð¸Ñ… Ñайтов" ok: "Окей" gotIt: "ЯÑно!" @@ -103,6 +103,8 @@ renoted: "РепоÑÑ‚ Ñовершён." cantRenote: "Ðто Ð½ÐµÐ»ÑŒÐ·Ñ Ñ€ÐµÐ¿Ð¾Ñтить." cantReRenote: "Ðевозможно репоÑтить репоÑÑ‚." quote: "Цитата" +inChannelRenote: "Ð’ канале" +inChannelQuote: "Заметки в канале" pinnedNote: "Ð—Ð°ÐºÑ€ÐµÐ¿Ð»Ñ‘Ð½Ð½Ð°Ñ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ°" pinned: "Закрепить в профиле" you: "Ð’Ñ‹" @@ -129,6 +131,7 @@ unblockConfirm: "Разблокировать Ñтот аккаунт?" suspendConfirm: "Заморозить Ñтот аккаунт?" unsuspendConfirm: "Разморозить Ñтот аккаунт?" selectList: "Выберите ÑпиÑок" +selectChannel: "Выберите канал" selectAntenna: "Выберите антенну" selectWidget: "Выберите виджет" editWidgets: "Редактировать виджеты" @@ -256,6 +259,8 @@ noMoreHistory: "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð·Ð°ÐºÐ¾Ð½Ñ‡Ð¸Ð»Ð°ÑÑŒ" startMessaging: "Ðачать общение" nUsersRead: "Прочитали {n}" agreeTo: "Я ÑоглашаюÑÑŒ Ñ {0}" +agreeBelow: "СоглаÑен Ñо Ñледующими" +basicNotesBeforeCreateAccount: "ЗапиÑи, перед Ñозданием аккаунта" tos: "ПользовательÑкое Ñоглашение" start: "Ðачать" home: "ГлавнаÑ" @@ -351,6 +356,8 @@ recaptcha: "reCAPTCHA" enableRecaptcha: "Включить reCAPTCHA" recaptchaSiteKey: "Ключ Ñайта" recaptchaSecretKey: "Секретный ключ" +turnstile: "Ð¡ÐµÑ€Ð²Ð¸Ñ Turnstile" +enableTurnstile: "Включить Turnstile" turnstileSiteKey: "Ключ Ñайта" turnstileSecretKey: "Секретный ключ" avoidMultiCaptchaConfirm: "ÐеÑколько ÑпоÑобов проверки могут мешать друг другу. Подтвердите, еÑли хотите отключить другие ÑпоÑобы. Или нажмите «Отмена», чтобы оÑтавить их включёнными." @@ -386,13 +393,10 @@ about: "ОпиÑание" aboutMisskey: "О Misskey" administrator: "ÐдминиÑтратор" token: "Токен" -twoStepAuthentication: "Ð”Ð²ÑƒÑ…Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð°Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ" moderator: "Модератор" moderation: "МодерациÑ" nUsersMentioned: "УпомÑнуло пользователей: {n}" securityKey: "Ключ безопаÑноÑти" -securityKeyName: "Ð˜Ð¼Ñ ÐºÐ»ÑŽÑ‡Ð°" -registerSecurityKey: "ЗарегиÑтрировать защитный ключ" lastUsed: "ПоÑледнее иÑпользование" unregister: "ОтпиÑатьÑÑ" passwordLessLogin: "ÐаÑтроить вход без паролÑ" @@ -410,24 +414,15 @@ markAsReadAllTalkMessages: "Отметить вÑе реплики как про help: "Помощь" inputMessageHere: "Введите Ñообщение здеÑÑŒ" close: "Закрыть" -group: "Группа" -groups: "Группы" -createGroup: "Создать группу" -ownedGroups: "СобÑтвенные группы" -joinedGroups: "УчаÑтие в группах" invites: "ПриглашениÑ" -groupName: "Ðазвание группы" members: "УчаÑтники" transfer: "Отдать" -messagingWithUser: "Общение Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼ пользователем" -messagingWithGroup: "Общение в группе" title: "Заголовок" text: "ТекÑÑ‚" enable: "Включить" next: "Дальше" retype: "Введите ещё раз" noteOf: "Что пишет {user}" -inviteToGroup: "ПриглаÑить в группу" quoteAttached: "Цитата" quoteQuestion: "Хотите добавить цитату?" noMessagesYet: "Пока ни одного ÑообщениÑ" @@ -449,19 +444,17 @@ passwordMatched: "Совпали" passwordNotMatched: "Ðе Ñовпадают" signinWith: "ИÑпользовать {x} Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ð°" signinFailed: "Ðевозможно войти в ÑиÑтему. Введенное вами Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸Ð»Ð¸ пароль неверны." -tapSecurityKey: "Ðажмите на Ñвой Ñлектронный ключ" or: "или" language: "Язык" uiLanguage: "Язык интерфейÑа" -groupInvited: "Приглашение в группу" aboutX: "ОпиÑание {x}" emojiStyle: "Стиль Ñмодзи" native: "СиÑтемные" disableDrawer: "Ðе иÑпользовать выдвижные меню" -youHaveNoGroups: "У Ð²Ð°Ñ Ð½ÐµÑ‚ ни одной группы" -joinOrCreateGroup: "Получайте Ð¿Ñ€Ð¸Ð³Ð»Ð°ÑˆÐµÐ½Ð¸Ñ Ð² группы или Ñоздавайте Ñвои ÑобÑтвенные" noHistory: "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð¿Ð¾ÐºÐ° пуÑта" signinHistory: "Журнал поÑещений" +enableAdvancedMfm: "Включить раÑширенный MFM" +enableAnimatedMfm: "Включить анимированную разметку MFM" doing: "Ð’ процеÑÑе" category: "КатегориÑ" tags: "Метки" @@ -831,8 +824,6 @@ deleteAccountConfirm: "Ð£Ñ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ будет безвозвра incorrectPassword: "Пароль неверен." voteConfirm: "Отдать Ð³Ð¾Ð»Ð¾Ñ Ð·Ð° «{choice}»?" hide: "СпрÑтать" -leaveGroup: "Покинуть группу" -leaveGroupConfirm: "Покинуть группу «{name}»?" useDrawerReactionPickerForMobile: "Ð’Ñ‹Ð´Ð²Ð¸Ð¶Ð½Ð°Ñ Ð¿Ð°Ð»Ð¸Ñ‚Ñ€Ð° на мобильном уÑтройÑтве" welcomeBackWithName: "С возвращением, {name}!" clickToFinishEmailVerification: "ПожалуйÑта, нажмите [{ok}], чтобы завершить подтверждение адреÑа Ñлектронной почты." @@ -855,8 +846,11 @@ oneDay: "1 день" oneWeek: "1 неделÑ" reflectMayTakeTime: "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ занÑть Ð²Ñ€ÐµÐ¼Ñ Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ" failedToFetchAccountInformation: "Ðе удалоÑÑŒ получить информацию об аккаунте" +rateLimitExceeded: "Ограничение ÑкороÑти превышено" cropImage: "Кадрирование" cropImageAsk: "Ðужно ли кадрировать изображение?" +cropYes: "Обрезать" +cropNo: "Ðе обрезать" file: "Файлы" recentNHours: "ПоÑледние {n} ч" recentNDays: "ПоÑледние {n} Ñут" @@ -883,6 +877,8 @@ refreshInterval: "Интервал перезагрузки" label: "Метка" type: "Тип" speed: "СкороÑть" +slow: "МедленнаÑ" +fast: "БыÑтраÑ" sensitiveMediaDetection: "Определение Ñодержимого деликатного характера" localOnly: "Локально" remoteOnly: "Только удалённо" @@ -933,6 +929,16 @@ cannotPerformTemporaryDescription: "Ðто дейÑтвие временно нРpreset: "Шаблоны" selectFromPresets: "Выбрать из шаблонов" achievements: "ДоÑтижениÑ" +gotInvalidResponseError: "Сервер ответил ошибкой" +gotInvalidResponseErrorDescription: "Сервер временно не доÑтупен. Возможно проводÑÑ‚ÑÑ Ñ‚ÐµÑ…Ð½Ð¸Ñ‡ÐµÑкие работы, или Ñервер отключен." +thisPostMayBeAnnoying: "Ðто Ñообщение может быть неприÑтным." +thisPostMayBeAnnoyingHome: "Ðтот поÑÑ‚ может быть отправлен на главную" +thisPostMayBeAnnoyingCancel: "Ðтот поÑÑ‚ не может быть отменен." +thisPostMayBeAnnoyingIgnore: "Ðтот поÑÑ‚ может быть проигнорирован " +collapseRenotes: "Свернуть репоÑты" +internalServerError: "ВнутреннÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ° Ñервера" +internalServerErrorDescription: "Внутри Ñервера произошла Ð½ÐµÐ¿Ñ€ÐµÐ´Ð²Ð¸Ð´ÐµÐ½Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°." +copyErrorInfo: "Скопировать код ошибки" _achievements: earnedAt: "Разблокировано в" _types: @@ -1189,6 +1195,9 @@ _role: baseRole: "Шаблон роли" useBaseValue: "ИÑпользовать значение из шаблона" chooseRoleToAssign: "Выберите роль, которую хотите выдать" + iconUrl: "ÐÐ´Ñ€ÐµÑ Ð½Ð° иконку роли" + asBadge: "Показывать как значок" + descriptionOfAsBadge: "ОпиÑание значка" canEditMembersByModerator: "Могут назначать модераторы" descriptionOfCanEditMembersByModerator: "ЕÑли включено, на Ñту роль могут назначать пользователей как админиÑтраторы, так и модераторы. ЕÑли выключено, назначать могут только админиÑтраторы." priority: "Приоритет" @@ -1206,6 +1215,7 @@ _role: pinMax: "ДоÑтупное количеÑтво закреплённых заметок" antennaMax: "ДоÑтупное количеÑтво антенн" wordMuteMax: "ДоÑтупное количеÑтво знаков в ÑпиÑке ÑÐºÑ€Ñ‹Ñ‚Ð¸Ñ Ñлов" + webhookMax: "МакÑимум web-хуков" clipMax: "МакÑимальное количеÑтво подборок" noteEachClipsMax: "МакÑимальное количеÑтво заметок в подборке" userListMax: "МакÑимальное количеÑтво ÑпиÑков аккаунтов" @@ -1227,7 +1237,12 @@ _role: not: "Кроме тех, у кого…" _sensitiveMediaDetection: description: "Машинное обучение может быть иÑпользовано Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑкого Ð¾Ð±Ð½Ð°Ñ€ÑƒÐ¶ÐµÐ½Ð¸Ñ Ñ‡ÑƒÐ²Ñтвительных медиа Ð´Ð»Ñ Ð¼Ð¾Ð´ÐµÑ€Ð°Ñ†Ð¸Ð¸. Ðагрузка на Ñервер увеличиваетÑÑ Ð½ÐµÐ·Ð½Ð°Ñ‡Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾." + sensitivity: "ЧувÑтвительноÑть обнаружениÑ" + sensitivityDescription: "Более Ð½Ð¸Ð·ÐºÐ°Ñ Ñ‡ÑƒÐ²ÑтвительноÑть уменьшает количеÑтво ложных Ñрабатываний (false positives). Повышение чувÑтвительноÑти уменьшает утечку при обнаружении (ложноотрицательные результаты)." setSensitiveFlagAutomatically: "УÑтановить флаг NSFW" + setSensitiveFlagAutomaticallyDescription: "Даже еÑли Ñтот параметр отключен, результат оценки ÑохранÑетÑÑ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ ÑиÑтемы." + analyzeVideos: "Ðнализировать видео?" + analyzeVideosDescription: "Ðнализируйте видео в дополнение к неподвижным изображениÑм. Ðагрузка на Ñервер немного увеличиваетÑÑ." _emailUnavailable: used: "Уже иÑпользуетÑÑ" format: "Ðеверный формат" @@ -1470,14 +1485,14 @@ _tutorial: step8_3: "Ðту наÑтройку вы вÑегда Ñможете поменÑть" _2fa: alreadyRegistered: "Ð”Ð²ÑƒÑ…Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð°Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ ÑƒÐ¶Ðµ наÑтроена." - registerDevice: "ЗарегиÑтрируйте ваше уÑтройÑтво" - registerKey: "ЗарегиÑтрировать ключ" step1: "Прежде вÑего, уÑтановите на уÑтройÑтво приложение Ð´Ð»Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸, например, {a} или {b}." step2: "Далее отÑканируйте отображаемый QR-код при помощи приложениÑ." step2Url: "ЕÑли пользуетеÑÑŒ приложением на компьютере, можете ввеÑти в него Ñту Ñтроку (URL):" step3: "И наконец, введите код, который покажет приложение." step4: "Теперь при каждом входе на Ñайт вам нужно будет вводить код из Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð°Ð½Ð°Ð»Ð¾Ð³Ð¸Ñ‡Ð½Ñ‹Ð¼ образом." securityKeyInfo: "Ð’Ñ‹ можете наÑтроить вход Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ аппаратного ключа безопаÑноÑти, поддерживающего FIDO2, или отпечатка пальца или PIN-кода на уÑтройÑтве." + removeKeyConfirm: "Удалить резервную копию «{name}»?" + renewTOTPCancel: "Ðет, ÑпаÑибо" _permissions: "read:account": "ПроÑматривать данные учётной запиÑи" "write:account": "ИзменÑть данные учётной запиÑи" @@ -1512,18 +1527,20 @@ _permissions: "read:gallery-likes": "ПроÑмотр ÑпиÑка понравившегоÑÑ Ð² галерее" "write:gallery-likes": "Изменение ÑпиÑка понравившегоÑÑ Ð² галерее" _auth: + shareAccessTitle: "Ð Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ð¹" shareAccess: "Дать доÑтуп Ð´Ð»Ñ Â«{name}» к вашей учётной запиÑи?" shareAccessAsk: "Уверены, что хотите дать приложению доÑтуп к Ñвоей учётной запиÑи?" + permission: "{name} Запрашивает Ñледующие разрешениÑ:" permissionAsk: "Приложение запрашивает Ñледующие разрешениÑ:" pleaseGoBack: "ВернитеÑÑŒ, пожалуйÑта, в приложение" callback: "Возврат в приложение" denied: "ДоÑтуп закрыт" + pleaseLogin: "Ð’Ñ‹ должны войти в ÑиÑтему, чтобы дать разрешение приложению." _antennaSources: all: "Ð’Ñе заметки" homeTimeline: "Заметки тех на которых вы подпиÑаны" users: "Заметки выбранных пользователей" userList: "Заметки пользователей из выбранных ÑпиÑков" - userGroup: "Заметки от пользователей из заданной группы" _weekday: sunday: "ВоÑкреÑенье" monday: "Понедельник" @@ -1598,8 +1615,6 @@ _visibility: followersDescription: "Только вашим подпиÑчикам" specified: "Личное" specifiedDescription: "Тем, кого укажете" - localOnly: "Локально" - localOnlyDescription: "Только Ð´Ð»Ñ Ñтого Ñайта" _postForm: replyPlaceholder: "Ответ на заметку..." quotePlaceholder: "ПоÑÑнение к цитате..." @@ -1664,7 +1679,16 @@ _timelines: social: "СоциальнаÑ" global: "Ð’ÑеобщаÑ" _play: + new: "Создать приложение " + edit: "Редактировать приложение" + created: "Приложение Ñоздано" + updated: "Приложение обновлено" + deleted: "Приложение удалено" + pageSetting: "ÐаÑтройки приложениÑ" + editThisPage: "Отредактировать Ñтраницу" viewSource: "ПроÑмотр иÑходника" + my: "Мои Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ " + liked: "ПонравилоÑÑŒ" featured: "ПопулÑрные" title: "Заголовок" script: "Скрипт" @@ -1728,13 +1752,11 @@ _notification: youGotReply: "{name} отвечает вам." youGotQuote: "{name} цитирует ваÑ." youRenoted: "{name} передаёт вашу заметку." - youGotMessagingMessageFromUser: "{name} пишет вам." - youGotMessagingMessageFromGroup: "Ðовое Ñообщение в группе «{name}»." youWereFollowed: "У Ð²Ð°Ñ Ð½Ð¾Ð²Ñ‹Ð¹ подпиÑчик." youReceivedFollowRequest: "У Ð²Ð°Ñ Ð½Ð¾Ð²Ñ‹Ð¹ Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° подпиÑку." yourFollowRequestAccepted: "Ваш Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° подпиÑку одобрен." - youWereInvitedToGroup: "Ð’Ñ‹ приглашены в группу." pollEnded: "Подведены окончательные итоги опроÑа" + unreadAntennaNote: "Ðнтенна {name}" emptyPushNotificationMessage: "Обновлены push-уведомлениÑ" achievementEarned: "Получено доÑтижение" _types: @@ -1748,7 +1770,6 @@ _notification: pollEnded: "ÐžÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ Ð¾Ð¿Ñ€Ð¾Ñов" receiveFollowRequest: "Получен Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° подпиÑку" followRequestAccepted: "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° подпиÑку одобрен" - groupInvited: "Приглашение в группы" app: "Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¸Ð· приложений" _actions: followBack: "отвечает взаимной подпиÑкой" diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index 844243ffec..f55fdf9795 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -388,13 +388,10 @@ about: "Informácie" aboutMisskey: "O Misskey" administrator: "Administrátor" token: "Token" -twoStepAuthentication: "Dvojfaktorová autentifikácia" moderator: "Moderátor" moderation: "Moderovanie" nUsersMentioned: "{n} použÃvateľov spomenulo" securityKey: "BezpeÄnostný kľúÄ" -securityKeyName: "Názov kľúÄa" -registerSecurityKey: "RegistrovaÅ¥ bezpeÄnostný kľúÄ" lastUsed: "Naposledy použité" unregister: "OdregistrovaÅ¥" passwordLessLogin: "NastaviÅ¥ bezheslové prihlásenie" @@ -412,24 +409,15 @@ markAsReadAllTalkMessages: "OznaÄiÅ¥ vÅ¡etky správy ako preÄÃtané" help: "Pomoc" inputMessageHere: "Sem napÃÅ¡te správu" close: "ZavrieÅ¥" -group: "Skupina" -groups: "Skupiny" -createGroup: "VytvoriÅ¥ skupinu" -ownedGroups: "Vlastnené skupiny" -joinedGroups: "ÄŒlenstvo v skupinách" invites: "PozvaÅ¥" -groupName: "Názov skupiny" members: "ÄŒlenovia" transfer: "Presun" -messagingWithUser: "Súkromný chat" -messagingWithGroup: "Skupinový chat" title: "Nadpis" text: "Text" enable: "PovoliÅ¥" next: "ÄŽalÅ¡Ã" retype: "Zadajte znovu" noteOf: "Poznámky použÃvateľa {user}" -inviteToGroup: "PozvaÅ¥ do skupiny" quoteAttached: "Citované" quoteQuestion: "PripojiÅ¥ ako citát?" noMessagesYet: "Zatiaľ žiadne správy" @@ -451,17 +439,13 @@ passwordMatched: "Heslá sú rovnaké" passwordNotMatched: "Heslá nie sú rovnaké" signinWith: "PrihlásiÅ¥ sa použitÃm {x}" signinFailed: "Nedá sa prihlásiÅ¥. Skontrolujte prosÃm meno použÃvateľa a heslo." -tapSecurityKey: "Ťuknite na bezpeÄnostný kľúÄ" or: "Alebo" language: "Jazyk" uiLanguage: "Jazyk použÃvateľského prostredia" -groupInvited: "PozvaÅ¥ do skupiny" aboutX: "O {x}" emojiStyle: "Å týl emoji" native: "NatÃvne" disableDrawer: "NepoužÃvaÅ¥ Å¡uflÃkové menu" -youHaveNoGroups: "Nemáte žiadne skupiny" -joinOrCreateGroup: "Požiadajte o pozvanie do existujúcej skupiny alebo vytvorte novú." noHistory: "Žiadna história" signinHistory: "História prihlásenÃ" doing: "Pracujem..." @@ -832,8 +816,6 @@ deleteAccountConfirm: "Toto nezvrátiteľne vymaže váš úÄet. PokraÄovaÅ¥?" incorrectPassword: "Nesprávne heslo." voteConfirm: "Potvrdzujete svoj hlas za \"{choice}\"?" hide: "SkryÅ¥" -leaveGroup: "OpustiÅ¥ skupiny" -leaveGroupConfirm: "Naozaj chcete opustiÅ¥ \"{name}\"?" useDrawerReactionPickerForMobile: "ZobraziÅ¥ výber reakcià ako Å¡uflÃk na mobile" welcomeBackWithName: "Vitajte späť, {name}" clickToFinishEmailVerification: "KliknutÃm na [{ok}] dokonÄÃte overeniu emailu." @@ -1174,14 +1156,14 @@ _tutorial: step8_3: "Nastavenia notifikácià môžete neskôr zmeniÅ¥." _2fa: alreadyRegistered: "Už ste zaregistrovali 2-faktorové autentifikaÄné zariadenie." - registerDevice: "RegistrovaÅ¥ nové zariadenie" - registerKey: "RegistrovaÅ¥ bezpeÄnostný kľúÄ" step1: "Najprv si nainÅ¡talujte autentifikaÄnú aplikáciu (naprÃklad {a} alebo {b}) na svoje zariadenie." step2: "Potom, naskenujte QR kód zobrazený na obrazovke." step2Url: "Do aplikácie zadajte nasledujúcu URL adresu:" step3: "Nastavenie dokonÄÃte zadanÃm tokenu z vaÅ¡ej aplikácie." step4: "Od teraz, vÅ¡etky ÄalÅ¡ie prihlásenia budú vyžadovaÅ¥ prihlasovacà token." securityKeyInfo: "Okrem odtlaÄku prsta alebo PIN autentifikácie si môžete nastaviÅ¥ autentifikáciu cez hardvérový bezpeÄnostný kÄ¾ÃºÄ podporujúci FIDO2 a tak eÅ¡te viac zabezpeÄiÅ¥ svoj úÄet." + removeKeyConfirm: "Naozaj chcete odstrániÅ¥ \"{name}\"?" + renewTOTPCancel: "Nie, Äakujem" _permissions: "read:account": "VidieÅ¥ informácie o vaÅ¡om úÄte" "write:account": "UpraviÅ¥ informácie o vaÅ¡om úÄte" @@ -1227,7 +1209,6 @@ _antennaSources: homeTimeline: "Poznámky od sledovaného použÃvateľa" users: "Poznámky od konkrétneho použÃvateľa" userList: "Poznámky od použÃvateľov v zozname" - userGroup: "Poznámky od použÃvateľov z konkrétnej skupiny." _weekday: sunday: "Nedeľa" monday: "Pondelok" @@ -1299,8 +1280,6 @@ _visibility: followersDescription: "Viditeľné iba tým, ktorà vás sledujú" specified: "Priame" specifiedDescription: "Viditeľné iba pre konkrétnych použÃvateľov" - localOnly: "Iba lokálne" - localOnlyDescription: "Vzdialený použÃvatelia nebudú vidieÅ¥" _postForm: replyPlaceholder: "OdpoveÄ na túto poznámku..." quotePlaceholder: "Citovanie tejto poznámky..." @@ -1428,12 +1407,9 @@ _notification: youGotReply: "{name} vám odpovedal/a" youGotQuote: "{name} vás citoval/a" youRenoted: "{name} preposlal/a vaÅ¡u poznámku" - youGotMessagingMessageFromUser: "{name} vám poslal/a správu" - youGotMessagingMessageFromGroup: "PriÅ¡la správa do skupiny {name}" youWereFollowed: "Máte nového sledujúceho" youReceivedFollowRequest: "Dostali ste žiadosÅ¥ o sledovanie" yourFollowRequestAccepted: "VaÅ¡a žiadosÅ¥ o sledovanie bola prijatá" - youWereInvitedToGroup: "PozvaÅ¥ do skupiny" pollEnded: "Výsledky hlasovania sú k dispozÃcii." unreadAntennaNote: "Anténa {name}" emptyPushNotificationMessage: "Push notifikácie aktualizované" @@ -1448,7 +1424,6 @@ _notification: pollEnded: "Hlasovanie skonÄilo" receiveFollowRequest: "DoruÄené žiadosti o sledovanie" followRequestAccepted: "Schválené žiadosti o sledovanie" - groupInvited: "Pozvánky do skupÃn" app: "Oznámenia z prepojených aplikáciÃ" _actions: followBack: "SledovaÅ¥ späť\n" diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml index 184db2e907..5e66df2076 100644 --- a/locales/sv-SE.yml +++ b/locales/sv-SE.yml @@ -383,12 +383,13 @@ _sfx: notification: "Notifikationer" chat: "Chatt" antenna: "Antenner" +_2fa: + renewTOTPCancel: "Nej tack" _antennaSources: all: "Alla noter" homeTimeline: "Noter frÃ¥n följda användare" users: "Noter frÃ¥n specifika användare" userList: "Noter frÃ¥n en specificerad lista av användare" - userGroup: "Noter frÃ¥n användare i en specificerad grupp" _widgets: profile: "Profil" instanceInfo: "Instansinformation" diff --git a/locales/th-TH.yml b/locales/th-TH.yml index bea19c3706..2c721e45e1 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -103,6 +103,8 @@ renoted: "รีโน้ตเà¸à¸²à¹„ว้" cantRenote: "โพสต์นี้ไม่สามารถรีโน้ตไว้ใหม่ได้นะ" cantReRenote: "ไม่สามารถรีโน้ตเà¸à¸²à¹„ว้ใหม่ได้นะ" quote: "à¸à¹‰à¸²à¸‡à¸„ำพูด" +inChannelRenote: "รีโน้ตช่à¸à¸‡à¹à¸Šà¸¥à¹à¸™à¸¥à¹€à¸—่านั้น" +inChannelQuote: "à¸à¹‰à¸²à¸‡à¸Šà¹ˆà¸à¸‡à¹€à¸—่านั้น" pinnedNote: "โน้ตที่ปัà¸à¸«à¸¡à¸¸à¸”เà¸à¸²à¹„ว้" pinned: "ปัà¸à¸«à¸¡à¸¸à¸”ไปยังโปรไฟล์" you: "ตัวเà¸à¸‡" @@ -257,6 +259,8 @@ noMoreHistory: "ในนั้นไม่มีประวัติà¸à¸µà¸à startMessaging: "เริ่มà¸à¸²à¸£à¸ªà¸™à¸—นา" nUsersRead: "à¸à¹ˆà¸²à¸™à¹‚ดย {n}" agreeTo: "ฉันยà¸à¸¡à¸£à¸±à¸šà¸—ี่จะ {0}" +agreeBelow: "ฉันยà¸à¸¡à¸£à¸±à¸šà¸–ึงด้านล่าง" +basicNotesBeforeCreateAccount: "หมายเหตุสำคัà¸" tos: "ข้à¸à¸à¸³à¸«à¸™à¸”à¹à¸¥à¸°à¹€à¸‡à¸·à¹ˆà¸à¸™à¹„ข" start: "เริ่มต้น​ใช้งาน​" home: "หน้าà¹à¸£à¸" @@ -389,13 +393,10 @@ about: "เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸š" aboutMisskey: "เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸š Misskey" administrator: "ผู้ดูà¹à¸¥à¸£à¸°à¸šà¸š" token: "โทเค็น" -twoStepAuthentication: "ยืนยันตัวตน 2 ชั้น" moderator: "ผู้ควบคุม" moderation: "à¸à¸²à¸£à¸à¸¥à¸±à¹ˆà¸™à¸à¸£à¸à¸‡" nUsersMentioned: "à¸à¸¥à¹ˆà¸²à¸§à¸–ึงโดยผู้ใช้ {n} รายนี้" securityKey: "à¸à¸¸à¸à¹à¸ˆà¸„วามปลà¸à¸”ภัย" -securityKeyName: "ชื่à¸à¸„ีย์" -registerSecurityKey: "ลงทะเบียนรหัสความปลà¸à¸”ภัยคีย์" lastUsed: "ใช้ล่าสุด" unregister: "เลิà¸à¸•ิดตาม" passwordLessLogin: "เข้าสู่ระบบà¹à¸šà¸šà¹„ม่ใช้รหัสผ่าน" @@ -413,24 +414,15 @@ markAsReadAllTalkMessages: "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸‚้à¸à¸„à help: "ช่วยเหลืà¸" inputMessageHere: "พิมพ์ข้à¸à¸„วามที่นี่" close: "ปิด" -group: "à¸à¸¥à¸¸à¹ˆà¸¡" -groups: "à¸à¸¥à¸¸à¹ˆà¸¡" -createGroup: "สร้างà¸à¸¥à¸¸à¹ˆà¸¡" -ownedGroups: "à¸à¸¥à¸¸à¹ˆà¸¡à¸—ี่เป็นเจ้าขà¸à¸‡" -joinedGroups: "เข้าร่วมà¸à¸¥à¸¸à¹ˆà¸¡" invites: "เชิà¸à¸Šà¸§à¸™" -groupName: "ชื่à¸à¸à¸¥à¸¸à¹ˆà¸¡" members: "สมาชิà¸" transfer: "ถ่ายโà¸à¸™" -messagingWithUser: "à¹à¸Šà¸—ส่วนตัว" -messagingWithGroup: "à¹à¸Šà¸—à¸à¸¥à¸¸à¹ˆà¸¡" title: "หัวข้à¸" text: "ข้à¸à¸„วาม" enable: "เปิดใช้งาน" next: "ถัด​ไป" retype: "พิมพ์รหัสà¸à¸µà¸à¸„รั้ง" noteOf: "โน้ต โดย {ผู้ใช้งาน}" -inviteToGroup: "ชวนเข้าà¸à¸¥à¸¸à¹ˆà¸¡" quoteAttached: "à¸à¹‰à¸²à¸‡à¸à¸´à¸‡" quoteQuestion: "นายต้à¸à¸‡à¸à¸²à¸£à¸—ี่จะà¸à¹‰à¸²à¸‡à¸à¸´à¸‡à¸«à¸£à¸?" noMessagesYet: "ยังไม่มีข้à¸à¸„วามนะ" @@ -452,19 +444,17 @@ passwordMatched: "ถูà¸à¸•้à¸à¸‡!" passwordNotMatched: "ไม่ถูà¸à¸•้à¸à¸‡" signinWith: "ลงชื่à¸à¹€à¸‚้าใช้ด้วย {x}" signinFailed: "ไม่สามารถลงชื่à¸à¸œà¸¹à¹‰à¹€à¸‚้าใช้ได้ เนื่à¸à¸‡à¸ˆà¸²à¸ ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸«à¸£à¸·à¸à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¸—ี่คุณป้à¸à¸™à¸™à¸±à¹‰à¸™à¹„ม่ถูà¸à¸•้à¸à¸‡à¸™à¸°" -tapSecurityKey: "à¹à¸•ะคีย์ความปลà¸à¸”ภัย" or: "หรืà¸" language: "ภาษา" uiLanguage: "ภาษาà¸à¸´à¸™à¹€à¸—à¸à¸£à¹Œà¹€à¸Ÿà¸‹à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" -groupInvited: "คุณได้รับเชิà¸à¹ƒà¸«à¹‰à¹€à¸‚้าร่วมà¸à¸¥à¸¸à¹ˆà¸¡" aboutX: "เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸š {x}" emojiStyle: "สไตล์à¸à¸´à¹‚มจิ" native: "ภาษาà¹à¸¡à¹ˆ" disableDrawer: "à¸à¸¢à¹ˆà¸²à¹ƒà¸Šà¹‰à¸¥à¸´à¹‰à¸™à¸Šà¸±à¸à¸ªà¹„ตล์เมนู" -youHaveNoGroups: "คุณยังไม่มีà¸à¸¥à¸¸à¹ˆà¸¡" -joinOrCreateGroup: "รับเชิà¸à¹€à¸‚้าร่วมà¸à¸¥à¸¸à¹ˆà¸¡à¸«à¸£à¸·à¸à¸ªà¸£à¹‰à¸²à¸‡à¸à¸¥à¸¸à¹ˆà¸¡à¸‚à¸à¸‡à¸„ุณเà¸à¸‡à¹€à¸¥à¸¢à¸™à¸°" noHistory: "ไม่มีรายà¸à¸²à¸£" signinHistory: "ประวัติà¸à¸²à¸£à¹€à¸‚้าสู่ระบบ" +enableAdvancedMfm: "เปิดใช้งาน MFM ขั้นสูง" +enableAnimatedMfm: "เปิดà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ MFM ด้วยà¹à¸à¸™à¸´à¹€à¸¡à¸Šà¸±à¹ˆà¸™" doing: "à¸à¸³à¸¥à¸±à¸‡à¸›à¸£à¸°à¸¡à¸§à¸¥à¸œà¸¥......" category: "หมวดหมู่" tags: "à¹à¸—็à¸" @@ -834,8 +824,6 @@ deleteAccountConfirm: "à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸™à¸µà¹‰à¸ˆà¸°à¸¥à¸š incorrectPassword: "รหัสผ่านไม่ถูà¸à¸•้à¸à¸‡" voteConfirm: "ยืนยันà¸à¸²à¸£à¹‚หวต \"{choice}\" มั้ย?" hide: "ซ่à¸à¸™" -leaveGroup: "à¸à¸à¸à¸ˆà¸²à¸à¸à¸¥à¸¸à¹ˆà¸¡" -leaveGroupConfirm: "คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸à¸§à¹ˆà¸²à¸•้à¸à¸‡à¸à¸²à¸£à¸à¸à¸à¸ˆà¸²à¸ \"{name}\"" useDrawerReactionPickerForMobile: "à¹à¸ªà¸”งผล ตัวเลืà¸à¸à¸›à¸à¸´à¸à¸´à¸£à¸´à¸¢à¸²à¹€à¸›à¹‡à¸™à¸¥à¸´à¹‰à¸™à¸Šà¸±à¸à¸šà¸™à¸¡à¸·à¸à¸–ืà¸" welcomeBackWithName: "ยินดีต้à¸à¸™à¸£à¸±à¸šà¸à¸²à¸£à¸à¸¥à¸±à¸šà¸¡à¸²à¸™à¸°à¸„่ะ, {name}" clickToFinishEmailVerification: "à¸à¸£à¸¸à¸“าคลิภ[{ok}] เพื่à¸à¸”ำเนินà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸à¸µà¹€à¸¡à¸¥à¹ƒà¸«à¹‰à¹€à¸ªà¸£à¹‡à¸ˆà¸ªà¸¡à¸šà¸¹à¸£à¸“์นะ" @@ -861,6 +849,8 @@ failedToFetchAccountInformation: "ไม่สามารถเรียà¸à¸”à rateLimitExceeded: "เà¸à¸´à¸™à¸‚ีดจำà¸à¸±à¸”à¸à¸±à¸•รา" cropImage: "ครà¸à¸šà¸•ัดรูปภาพ" cropImageAsk: "คุณต้à¸à¸‡à¸à¸²à¸£à¸„รà¸à¸šà¸•ัดรูปภาพนี้à¸à¸¢à¹ˆà¸²à¸‡à¸‡à¸±à¹‰à¸™à¸«à¸£à¸·à¸?" +cropYes: "ครà¸à¸šà¸•ัด" +cropNo: "ใช้ตามที่เป็นà¸à¸¢à¸¹à¹ˆ" file: "ไฟล์" recentNHours: "ล่าสุด {n} ชั่วโมงที่à¹à¸¥à¹‰à¸§" recentNDays: "ล่าสุด {n} วันที่à¹à¸¥à¹‰à¸§" @@ -941,6 +931,14 @@ selectFromPresets: "เลืà¸à¸à¸ˆà¸²à¸à¸à¸²à¸£à¸žà¸£à¸µà¹€à¸‹à¹‡à¸•" achievements: "ความสำเร็จ" gotInvalidResponseError: "à¸à¸²à¸£à¸•à¸à¸šà¸ªà¸™à¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¹„ม่ถูà¸à¸•้à¸à¸‡" gotInvalidResponseErrorDescription: "เซิร์ฟเวà¸à¸£à¹Œà¸à¸²à¸ˆà¹„ม่สามารถเข้าถึงได้หรืà¸à¸à¸²à¸ˆà¸ˆà¸°à¸à¸³à¸¥à¸±à¸‡à¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¸£à¸°à¸«à¸§à¹ˆà¸²à¸‡à¸›à¸£à¸±à¸šà¸›à¸£à¸¸à¸‡ à¸à¸£à¸¸à¸“าลà¸à¸‡à¹ƒà¸«à¸¡à¹ˆà¸à¸µà¸à¸„รั้งในภายหลังนะคะ" +thisPostMayBeAnnoying: "โน้ตนี้à¸à¸²à¸ˆà¸ˆà¸°à¹€à¸›à¹‡à¸™à¸à¸²à¸£à¸£à¸šà¸à¸§à¸™à¸œà¸¹à¹‰à¸à¸·à¹ˆà¸™à¸™à¸°à¸„ะ" +thisPostMayBeAnnoyingHome: "โพสต์ไปยังบ้านไทม์ไลน์" +thisPostMayBeAnnoyingCancel: "เลิà¸" +thisPostMayBeAnnoyingIgnore: "โพสต์ยังไงà¸à¹‡à¹à¸¥à¹‰à¸§à¹à¸•่" +collapseRenotes: "ยุบ renotes ที่คุณได้เห็นà¹à¸¥à¹‰à¸§" +internalServerError: "เซิร์ฟเวà¸à¸£à¹Œà¸ ายในเà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาด" +internalServerErrorDescription: "เซิร์ฟเวà¸à¸£à¹Œà¸£à¸±à¸™à¸„้นพบข้à¸à¸œà¸´à¸”พลาดที่ไม่คาดคิด" +copyErrorInfo: "คัดลà¸à¸à¸£à¸²à¸¢à¸¥à¸°à¹€à¸à¸µà¸¢à¸”ข้à¸à¸œà¸´à¸”พลาด" _achievements: earnedAt: "ได้รับเมื่à¸" _types: @@ -1487,14 +1485,14 @@ _tutorial: step8_3: "คุณสามารถเปลี่ยนà¸à¸²à¸£à¸•ั้งค่านี้ในภายหลังได้ตลà¸à¸”เวลานะ" _2fa: alreadyRegistered: "คุณได้ลงทะเบียนà¸à¸¸à¸›à¸à¸£à¸“์ยืนยันตัวตนà¹à¸šà¸š 2 ชั้นà¹à¸¥à¹‰à¸§" - registerDevice: "ลงทะเบียนà¸à¸¸à¸›à¸à¸£à¸“์ใหม่" - registerKey: "ลงทะเบียนรหัสความปลà¸à¸”ภัย" step1: "ขั้นตà¸à¸™à¹à¸£à¸ ติดตั้งà¹à¸à¸›à¸¢à¸·à¸™à¸¢à¸±à¸™à¸•ัวตน (เช่น {a} หรืภ{b}) บนà¸à¸¸à¸›à¸à¸£à¸“์ขà¸à¸‡à¸„ุณ" step2: "จาà¸à¸™à¸±à¹‰à¸™à¸ªà¹à¸à¸™à¸£à¸«à¸±à¸ª QR ที่à¹à¸ªà¸”งบนหน้าจà¸à¸™à¸µà¹‰" step2Url: "คุณยังสามารถป้à¸à¸™à¸šà¸™ URL นี้หาà¸à¸„ุณใช้โปรà¹à¸à¸£à¸¡à¹€à¸”สà¸à¹Œà¸—็à¸à¸›:" step3: "ป้à¸à¸™à¹‚ทเค็นที่à¹à¸à¸›à¸‚à¸à¸‡à¸„ุณให้มาเพื่à¸à¹€à¸ªà¸£à¹‡à¸ˆà¸ªà¸´à¹‰à¸™à¸à¸²à¸£à¸•ั้งค่า" step4: "นับจาà¸à¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¸•้นไปà¸à¸²à¸£à¸žà¸¢à¸²à¸¢à¸²à¸¡à¹€à¸‚้าสู่ระบบในà¸à¸™à¸²à¸„ตนั้น à¸à¸²à¸ˆà¸ˆà¸°à¸•้à¸à¸‡à¸‚à¸à¹‚ทเค็นในà¸à¸²à¸£à¹€à¸‚้าสู่ระบบดังà¸à¸¥à¹ˆà¸²à¸§" securityKeyInfo: "นà¸à¸à¸ˆà¸²à¸à¸™à¸µà¹‰à¸à¸²à¸£à¸•รวจสà¸à¸šà¸„วามถูà¸à¸•้à¸à¸‡à¸”้วยลายนิ้วมืà¸à¸«à¸£à¸·à¸ PIN à¹à¸¥à¹‰à¸§ คุณยังสามารถตั้งค่าà¸à¸²à¸£à¸•รวจสà¸à¸šà¸ªà¸´à¸—ธิ์ผ่านคีย์ความปลà¸à¸”ภัยขà¸à¸‡à¸®à¸²à¸£à¹Œà¸”à¹à¸§à¸£à¹Œà¸—ี่รà¸à¸‡à¸£à¸±à¸š FIDO2 เพื่à¸à¹€à¸žà¸´à¹ˆà¸¡à¸„วามปลà¸à¸”ภัยให้à¸à¸±à¸šà¸šà¸±à¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณ" + removeKeyConfirm: "ลบข้à¸à¸¡à¸¹à¸¥à¸ªà¸³à¸£à¸à¸‡ {name} มั้ย?" + renewTOTPCancel: "ไม่เป็นไร" _permissions: "read:account": "ดูข้à¸à¸¡à¸¹à¸¥à¸šà¸±à¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณ" "write:account": "à¹à¸à¹‰à¹„ขข้à¸à¸¡à¸¹à¸¥à¸šà¸±à¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณ" @@ -1543,7 +1541,6 @@ _antennaSources: homeTimeline: "โน้ตจาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่ติดตาม" users: "โน้ตจาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่เฉพาะเจาะจง" userList: "โน้ตจาà¸à¸£à¸²à¸¢à¸Šà¸·à¹ˆà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸—ี่ระบุ" - userGroup: "โน้ตจาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¹ƒà¸™à¸à¸¥à¸¸à¹ˆà¸¡à¸—ี่ระบุ" _weekday: sunday: "วันà¸à¸²à¸—ิตย์" monday: "วันจันทร์" @@ -1618,8 +1615,6 @@ _visibility: followersDescription: "ทำให้ผู้ติดตามนั้นมà¸à¸‡à¹€à¸«à¹‡à¸™à¹à¸„่คุณเท่านั้น" specified: "ไดเร็ค" specifiedDescription: "ทำให้มà¸à¸‡à¹€à¸«à¹‡à¸™à¹„ด้เฉพาะผู้ใช้ที่ระบุเท่านั้น" - localOnly: "เฉพาะท้à¸à¸‡à¸–ิ่น" - localOnlyDescription: "ผู้ใช้ระยะไà¸à¸¥à¸™à¸±à¹‰à¸™à¹„ม่สามารถมà¸à¸‡à¹€à¸«à¹‡à¸™à¹„ด้" _postForm: replyPlaceholder: "ตà¸à¸šà¸à¸¥à¸±à¸šà¹‚น้ตนี้..." quotePlaceholder: "à¸à¹‰à¸²à¸‡à¹‚น้ตนี้..." @@ -1757,12 +1752,9 @@ _notification: youGotReply: "{name} ตà¸à¸šà¸à¸¥à¸±à¸šà¸–ึงคุณ" youGotQuote: "{name} à¸à¹‰à¸²à¸‡à¸–ึงคุณ" youRenoted: "รีโน้ตจาภ{name}" - youGotMessagingMessageFromUser: "{name} ได้ส่งข้à¸à¸„วามà¹à¸Šà¸—ถึงคุณ" - youGotMessagingMessageFromGroup: "ข้à¸à¸„วามà¹à¸Šà¸—ถูà¸à¸ªà¹ˆà¸‡à¹„ปยัง {name} à¸à¸¥à¸¸à¹ˆà¸¡" youWereFollowed: "ได้ติดตามคุณ" youReceivedFollowRequest: "คุณมีคำขà¸à¸•ิดตามใหม่น่ะ" yourFollowRequestAccepted: "คำขà¸à¸•ิดตามขà¸à¸‡à¸„ุณได้รับà¸à¸²à¸£à¸¢à¸à¸¡à¸£à¸±à¸šà¹à¸¥à¹‰à¸§à¸™à¹ˆà¸°" - youWereInvitedToGroup: "{userName} ได้เชิà¸à¸„ุณเข้าà¸à¸¥à¸¸à¹ˆà¸¡" pollEnded: "โพลสำรวจความคิดเห็นผลลัพธ์มีพร้à¸à¸¡à¹ƒà¸Šà¹‰à¸‡à¸²à¸™" unreadAntennaNote: "เสาà¸à¸²à¸à¸²à¸¨ {name}" emptyPushNotificationMessage: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¹à¸šà¸šà¸žà¸¸à¸Šà¹„ด้รับà¸à¸²à¸£à¸à¸±à¸žà¹€à¸”ทà¹à¸¥à¹‰à¸§" @@ -1778,7 +1770,6 @@ _notification: pollEnded: "โพลนี้สิ้นสุดลงà¹à¸¥à¹‰à¸§" receiveFollowRequest: "ได้รับคำขà¸à¸•ิดตาม\n" followRequestAccepted: "ยà¸à¸¡à¸£à¸±à¸šà¸„ำขà¸à¸•ิดตาม" - groupInvited: "ได้รับคำเชิà¸à¹€à¸‚้าà¸à¸¥à¸¸à¹ˆà¸¡" app: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸ˆà¸²à¸à¹à¸à¸›à¸—ี่มีลิงà¸à¹Œ" _actions: followBack: "ติดตามà¸à¸¥à¸±à¸šà¸”้วย" diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 27882e50f6..79ec11f421 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -387,13 +387,10 @@ about: "ІнформаціÑ" aboutMisskey: "Про Misskey" administrator: "Ðдмін" token: "Токен" -twoStepAuthentication: "Двохфакторна аутентифікаціÑ" moderator: "Модератор" moderation: "МодераціÑ" nUsersMentioned: "Згадали: {n}" securityKey: "Ключ захиÑту" -securityKeyName: "Ðазва ключа" -registerSecurityKey: "ЗареєÑтрувати ключ захиÑту" lastUsed: "ВоÑтаннє викориÑтано" unregister: "СкаÑувати реєÑтрацію" passwordLessLogin: "Ðалаштувати вхід без паролÑ" @@ -411,24 +408,15 @@ markAsReadAllTalkMessages: "Позначити вÑÑ– Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñ help: "Допомога" inputMessageHere: "Введіть Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñ‚ÑƒÑ‚" close: "Закрити" -group: "Група" -groups: "Групи" -createGroup: "Створити групу" -ownedGroups: "ВлаÑні групи" -joinedGroups: "ЧленÑтво в групах" invites: "ЗапроÑити" -groupName: "Ðазва групи" members: "УчаÑники" transfer: "Передача" -messagingWithUser: "Чат з кориÑтувачами" -messagingWithGroup: "Чат з групою" title: "Тема" text: "ТекÑÑ‚" enable: "Увімкнути" next: "Далі" retype: "Введіть ще раз" noteOf: "Ðотатка {user}" -inviteToGroup: "Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ групи" quoteAttached: "Цитата" quoteQuestion: "Ви хочете додати цитату?" noMessagesYet: "Ще немає повідомлень" @@ -450,15 +438,11 @@ passwordMatched: "Ð’Ñе вірно" passwordNotMatched: "Паролі не Ñпівпадають" signinWith: "Увійти за допомогою {x}" signinFailed: "Ðе вдалоÑÑ ÑƒÐ²Ñ–Ð¹Ñ‚Ð¸. Введені Ñ–Ð¼â€™Ñ ÐºÐ¾Ñ€Ð¸Ñтувача або пароль неправильнi." -tapSecurityKey: "ТоркнітьÑÑ ÐºÐ»ÑŽÑ‡Ð° безпеки" or: "або" language: "Мова" uiLanguage: "Мова інтерфейÑу" -groupInvited: "Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ групи" aboutX: "Про {x}" disableDrawer: "Ðе викориÑтовувати виÑувні меню" -youHaveNoGroups: "Ðемає груп" -joinOrCreateGroup: "Отримуйте Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ груп або Ñтворюйте Ñвої влаÑні групи." noHistory: "ІÑÑ‚Ð¾Ñ€Ñ–Ñ Ð¿Ð¾Ñ€Ð¾Ð¶Ð½Ñ" signinHistory: "ІÑÑ‚Ð¾Ñ€Ñ–Ñ Ð²Ñ…Ð¾Ð´Ñ–Ð²" doing: "ВиконуєтьÑÑ" @@ -825,8 +809,6 @@ deleteAccountConfirm: "Це незворотно видалить ваш Ð°ÐºÐ°Ñ incorrectPassword: "Ðеправильний пароль." voteConfirm: "Підтверджуєте Ñвій Ð³Ð¾Ð»Ð¾Ñ Ð·Ð° \"{choice}\"?" hide: "Сховати" -leaveGroup: "Залишити групу" -leaveGroupConfirm: "Залишити \"{name}\"?" welcomeBackWithName: "З поверненнÑм, {name}!" clickToFinishEmailVerification: "ÐатиÑніть [{ok}], щоб завершити перевірку email." overridedDeviceKind: "Тип приÑтрою" @@ -1113,6 +1095,8 @@ _achievements: _loggedInOnNewYearsDay: title: "З Ðовим роком!" description: "Увійшли в перший день року" + _cookieClicked: + flavor: "Чекайте, це вірний Ñайт?" _brainDiver: title: "Brain Diver" description: "Відправити поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° \"Brain Diver\"" @@ -1347,13 +1331,12 @@ _tutorial: step8_3: "Ви завжди можете змінити цей параметр пізніше." _2fa: alreadyRegistered: "Двофакторна Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ Ð²Ð¶Ðµ налаштована." - registerDevice: "ЗареєÑтрувати новий приÑтрій" - registerKey: "ЗареєÑтрувати новий ключ безпеки" step1: "Спершу вÑтановіть на Ñвій приÑтрій програму автентифікації (наприклад {a} або {b})." step2: "Потім відÑкануйте QR-код, Ñкий відображаєтьÑÑ Ð½Ð° цьому екрані." step2Url: "Ви також можете ввеÑти цю URL-адреÑу, Ñкщо викориÑтовуєте програму Ð´Ð»Ñ ÐŸÐš:" step3: "Щоб завершити налаштуваннÑ, введіть токен, наданий вашою програмою." step4: "Відтепер будь-Ñкі майбутні Ñпроби входу вимагатимуть такого токена." + renewTOTPCancel: "Ðе зараз" _permissions: "read:account": "ПереглÑдати дані профілю" "write:account": "Змінити дані акаунту" @@ -1462,8 +1445,6 @@ _visibility: followersDescription: "Тільки Ð´Ð»Ñ Ð¿Ñ–Ð´Ð¿Ð¸Ñників" specified: "ОÑобиÑто" specifiedDescription: "Лише Ð´Ð»Ñ Ð¿ÐµÐ²Ð½Ð¸Ñ… кориÑтувачів" - localOnly: "Локально" - localOnlyDescription: "Приховано Ð´Ð»Ñ Ð²Ñ–Ð´Ð´Ð°Ð»ÐµÐ½Ð¸Ñ… кориÑтувачів" _postForm: replyPlaceholder: "Відповідь на цю нотатку..." quotePlaceholder: "Прокоментуйте цю нотатку..." @@ -1588,12 +1569,9 @@ _notification: youGotReply: "{name} відповідає" youGotQuote: "{name} цитує ваÑ" youRenoted: "{name} поширює" - youGotMessagingMessageFromUser: "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ {name}" - youGotMessagingMessageFromGroup: "Ðове Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð² групі {name}" youWereFollowed: "Ðовий підпиÑник" youReceivedFollowRequest: "Ви отримали запит на підпиÑку" yourFollowRequestAccepted: "Запит на підпиÑку прийнÑто" - youWereInvitedToGroup: "Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ групи" achievementEarned: "ДоÑÑÐ³Ð½ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¾" _types: all: "Ð’Ñе" @@ -1605,7 +1583,6 @@ _notification: reaction: "Реакції" receiveFollowRequest: "Запити на підпиÑку" followRequestAccepted: "ПрийнÑті підпиÑки" - groupInvited: "Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ груп" app: "Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ додатків" _actions: reply: "ВідповіÑти" diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index 594052dfce..44aed8bc37 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -383,13 +383,10 @@ about: "Giá»›i thiệu" aboutMisskey: "Vá» Misskey" administrator: "Quản trị viên" token: "Token" -twoStepAuthentication: "Xác minh 2 bước" moderator: "Kiểm duyệt viên" moderation: "Kiểm duyệt" nUsersMentioned: "Dùng bởi {n} ngưá»i" securityKey: "Khóa bảo máºt" -securityKeyName: "Tên khoá" -registerSecurityKey: "Äăng ký khóa bảo máºt" lastUsed: "Dùng lần cuối" unregister: "Há»§y đăng ký" passwordLessLogin: "Äăng nháºp không máºt khẩu" @@ -407,24 +404,15 @@ markAsReadAllTalkMessages: "Äánh dấu tất cả các tin nhắn là đã Ä‘á help: "Trợ giúp" inputMessageHere: "Nháºp ná»™i dung tin nhắn" close: "Äóng" -group: "Nhóm" -groups: "Các nhóm" -createGroup: "Tạo nhóm" -ownedGroups: "Nhóm tôi quản lý" -joinedGroups: "Nhóm tôi tham gia" invites: "Má»i" -groupName: "Tên nhóm" members: "Thà nh viên" transfer: "Chuyển giao" -messagingWithUser: "Nhắn riêng" -messagingWithGroup: "Chat nhóm" title: "Tá»±a Ä‘á»" text: "Ná»™i dung" enable: "Báºt" next: "Kế tiếp" retype: "Nháºp lại" noteOf: "Tút cá»§a {user}" -inviteToGroup: "Má»i và o nhóm" quoteAttached: "TrÃch dẫn" quoteQuestion: "TrÃch dẫn lại?" noMessagesYet: "Chưa có tin nhắn" @@ -446,15 +434,11 @@ passwordMatched: "Trùng khá»›p" passwordNotMatched: "Không trùng khá»›p" signinWith: "Äăng nháºp bằng {x}" signinFailed: "Không thể đăng nháºp. Vui lòng kiểm tra tên ngưá»i dùng và máºt khẩu cá»§a bạn." -tapSecurityKey: "Nhấn mã bảo máºt cá»§a bạn" or: "Hoặc" language: "Ngôn ngữ" uiLanguage: "Ngôn ngữ giao diện" -groupInvited: "Bạn đã được má»i tham gia nhóm" aboutX: "Giá»›i thiệu {x}" disableDrawer: "Không dùng menu thanh bên" -youHaveNoGroups: "Không có nhóm nà o" -joinOrCreateGroup: "Tham gia hoặc tạo má»™t nhóm má»›i." noHistory: "Không có dữ liệu" signinHistory: "Lịch sỠđăng nháºp" doing: "Äang xá» lý..." @@ -825,8 +809,6 @@ deleteAccountConfirm: "Äiá»u nà y sẽ khiến tà i khoản bị xóa vÄ©nh vi incorrectPassword: "Sai máºt khẩu." voteConfirm: "Xác nháºn bình chá»n \"{choice}\"?" hide: "Ẩn" -leaveGroup: "Rá»i khá»i nhóm" -leaveGroupConfirm: "Bạn có chắc muốn rá»i khá»i nhóm \"{name}\"?" useDrawerReactionPickerForMobile: "Hiện bá»™ chá»n biểu cảm dạng xổ ra trên Ä‘iện thoại" welcomeBackWithName: "Chà o mừng trở lại, {name}" clickToFinishEmailVerification: "Vui lòng nhấn [{ok}] để hoà n tất việc đăng ký." @@ -1150,14 +1132,13 @@ _tutorial: step7_3: "Bây giá», chúc may mắn và vui vẻ vá»›i Misskey! 🚀" _2fa: alreadyRegistered: "Bạn đã đăng ký thiết bị xác minh 2 bước." - registerDevice: "Äăng ký má»™t thiết bị" - registerKey: "Äăng ký má»™t mã bảo vệ" step1: "Trước tiên, hãy cà i đặt má»™t ứng dụng xác minh (chẳng hạn như {a} hoặc {b}) trên thiết bị cá»§a bạn." step2: "Sau đó, quét mã QR hiển thị trên mà n hình nà y." step2Url: "Bạn cÅ©ng có thể nháºp URL nà y nếu sá» dụng má»™t chương trình máy tÃnh:" step3: "Nháºp mã token do ứng dụng cá»§a bạn cung cấp để hoà n tất thiết láºp." step4: "Kể từ bây giá», những lần đăng nháºp trong tương lai sẽ yêu cầu mã token đăng nháºp đó." securityKeyInfo: "Bên cạnh xác minh bằng vân tay hoặc mã PIN, bạn cÅ©ng có thể thiết láºp xác minh thông qua khóa bảo máºt phần cứng há»— trợ FIDO2 để bảo máºt hÆ¡n nữa cho tà i khoản cá»§a mình." + removeKeyConfirm: "Xóa bản sao lưu {name}?" _permissions: "read:account": "Xem thông tin tà i khoản cá»§a bạn" "write:account": "Sá»a thông tin tà i khoản cá»§a bạn" @@ -1203,7 +1184,6 @@ _antennaSources: homeTimeline: "Tút từ những ngưá»i đã theo dõi" users: "Tút từ những ngưá»i cụ thể" userList: "Tút từ danh sách ngưá»i dùng cụ thể" - userGroup: "Tút từ ngưá»i dùng trong má»™t nhóm cụ thể" _weekday: sunday: "Chá»§ Nháºt" monday: "Thứ Hai" @@ -1275,8 +1255,6 @@ _visibility: followersDescription: "Dà nh riêng cho ngưá»i theo dõi" specified: "Nhắn riêng" specifiedDescription: "Chỉ ngưá»i được nhắc đến má»›i thấy" - localOnly: "Chỉ trên máy chá»§" - localOnlyDescription: "Không hiển thị vá»›i ngưá»i ở máy chá»§ khác" _postForm: replyPlaceholder: "Trả lá»i tút nà y" quotePlaceholder: "TrÃch dẫn tút nà y" @@ -1404,12 +1382,9 @@ _notification: youGotReply: "{name} trả lá»i bạn" youGotQuote: "{name} trÃch dẫn tút cá»§a bạn" youRenoted: "{name} đăng lại tút cá»§a bạn" - youGotMessagingMessageFromUser: "{name} nhắn tin cho bạn" - youGotMessagingMessageFromGroup: "Má»™t tin nhắn trong nhóm {name}" youWereFollowed: "đã theo dõi bạn" youReceivedFollowRequest: "Bạn vừa có má»™t yêu cầu theo dõi" yourFollowRequestAccepted: "Yêu cầu theo dõi cá»§a bạn đã được chấp nháºn" - youWereInvitedToGroup: "Bạn đã được má»i tham gia nhóm" pollEnded: "Cuá»™c bình chá»n đã kết thúc" emptyPushNotificationMessage: "Äã cáºp nháºt thông báo đẩy" _types: @@ -1423,7 +1398,6 @@ _notification: pollEnded: "Bình chá»n kết thúc" receiveFollowRequest: "Yêu cầu theo dõi" followRequestAccepted: "Yêu cầu theo dõi được chấp nháºn" - groupInvited: "Má»i và o nhóm" app: "Từ app liên kết" _actions: followBack: "đã theo dõi lại bạn" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 71ca55d9e5..7ba8956394 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -103,6 +103,8 @@ renoted: "已转å‘。" cantRenote: "è¯¥å¸–æ— æ³•è½¬å‘。" cantReRenote: "è½¬å‘æ— æ³•è¢«å†æ¬¡è½¬å‘。" quote: "引用" +inChannelRenote: "在频é“内转å‘" +inChannelQuote: "在频é“内引用" pinnedNote: "已置顶的帖å" pinned: "置顶" you: "您" @@ -391,13 +393,10 @@ about: "关于" aboutMisskey: "关于 Misskey" administrator: "管ç†å‘˜" token: "Token (令牌)" -twoStepAuthentication: "两æ¥éªŒè¯" moderator: "监察员" moderation: "管ç†" nUsersMentioned: "{n} 被æåˆ°" securityKey: "安全密钥" -securityKeyName: "密钥åç§°" -registerSecurityKey: "注册硬件安全密钥" lastUsed: "最åŽä½¿ç”¨ï¼š" unregister: "åˆ é™¤è´¦æˆ·" passwordLessLogin: "æ— å¯†ç 登录" @@ -415,24 +414,15 @@ markAsReadAllTalkMessages: "将所有èŠå¤©æ ‡è®°ä¸ºå·²è¯»" help: "帮助" inputMessageHere: "在æ¤é”®å…¥ä¿¡æ¯" close: "å…³é—" -group: "群组" -groups: "群组" -createGroup: "创建群组" -ownedGroups: "拥有的群组" -joinedGroups: "å·²åŠ å…¥çš„ç¾¤ç»„" invites: "邀请" -groupName: "群组å" members: "æˆå‘˜" transfer: "转让" -messagingWithUser: "与用户èŠå¤©" -messagingWithGroup: "与群组èŠå¤©" title: "æ ‡é¢˜" text: "文本" enable: "å¯ç”¨" next: "下一个" retype: "釿–°è¾“å…¥" noteOf: "{user}的帖å" -inviteToGroup: "群组邀请" quoteAttached: "已引用" quoteQuestion: "是å¦å¼•用æ¤é“¾æŽ¥å†…容?" noMessagesYet: "现在没有新的èŠå¤©" @@ -454,17 +444,13 @@ passwordMatched: "密ç 一致" passwordNotMatched: "密ç ä¸ä¸€è‡´" signinWith: "以{x}登录" signinFailed: "æ— æ³•ç™»å½•ï¼Œè¯·æ£€æŸ¥æ‚¨çš„ç”¨æˆ·åå’Œå¯†ç æ˜¯å¦æ£ç¡®ã€‚" -tapSecurityKey: "轻触硬件安全密钥" or: "或者" language: "è¯è¨€" uiLanguage: "显示è¯è¨€" -groupInvited: "您有新的群组邀请" aboutX: "关于 {x}" emojiStyle: "emoji çš„æ ·å¼" native: "原生" disableDrawer: "䏿˜¾ç¤ºæŠ½å±‰èœå•" -youHaveNoGroups: "没有群组" -joinOrCreateGroup: "è¯·åŠ å…¥ä¸€ä¸ªçŽ°æœ‰çš„ç¾¤ç»„ï¼Œæˆ–è€…åˆ›å»ºæ–°ç¾¤ç»„ã€‚" noHistory: "没有历å²è®°å½•" signinHistory: "登录历å²" enableAdvancedMfm: "å¯ç”¨æ‰©å±•MFM" @@ -683,7 +669,7 @@ driveFilesCount: "网盘的文件数" driveUsage: "网盘的空间用é‡" noCrawle: "è¦æ±‚æœç´¢å¼•擎ä¸ç´¢å¼•该用户" noCrawleDescription: "è¦æ±‚æœç´¢å¼•擎ä¸è¦æ”¶å½•(索引)您的用户页é¢ï¼Œå¸–å,页é¢ç‰ã€‚" -lockedAccountInfo: "å³ä½¿é€šè¿‡äº†å…³æ³¨è¯·æ±‚,åªè¦æ‚¨ä¸å°†å¸–åå¯è§èŒƒå›´è®¾ç½®æˆâ€œå…³æ³¨è€…â€ï¼Œä»»ä½•人都å¯ä»¥çœ‹åˆ°æ‚¨çš„帖å。" +lockedAccountInfo: "å³ä½¿å¯ç”¨è¯¥åŠŸèƒ½ï¼Œåªè¦æ‚¨ä¸å°†å¸–åå¯è§èŒƒå›´è®¾ç½®ä¸ºâ€œä»…关注者â€ï¼Œä»»ä½•人都还是å¯ä»¥çœ‹åˆ°æ‚¨çš„帖å。" alwaysMarkSensitive: "é»˜è®¤å°†åª’ä½“æ–‡ä»¶æ ‡è®°ä¸ºæ•æ„Ÿå†…容" loadRawImages: "æ·»åŠ é™„ä»¶å›¾åƒçš„缩略图时使用原始图åƒè´¨é‡" disableShowingAnimatedImages: "䏿’放动画" @@ -838,8 +824,6 @@ deleteAccountConfirm: "å°†è¦åˆ 除账户。是å¦ç¡®è®¤ï¼Ÿ" incorrectPassword: "密ç 错误" voteConfirm: "确定投给“{choice}†?" hide: "éšè—" -leaveGroup: "离开群组" -leaveGroupConfirm: "确定离开「{name}ã€ï¼Ÿ" useDrawerReactionPickerForMobile: "在移动设备上使用抽屉显示" welcomeBackWithName: "欢迎回æ¥ï¼Œ{name}" clickToFinishEmailVerification: "点击 [{ok}] 完æˆç”µå邮件地å€è®¤è¯ã€‚" @@ -951,6 +935,10 @@ thisPostMayBeAnnoying: "这个帖åå¯èƒ½ä¼šè®©å…¶ä»–人感到困扰。" thisPostMayBeAnnoyingHome: "å‘到首页" thisPostMayBeAnnoyingCancel: "å–æ¶ˆ" thisPostMayBeAnnoyingIgnore: "å°±è¿™æ ·å‘布" +collapseRenotes: "çœç•¥æ˜¾ç¤ºå·²ç»çœ‹è¿‡çš„转å‘内容" +internalServerError: "内部æœåŠ¡å™¨é”™è¯¯" +internalServerErrorDescription: "内部æœåС噍å‘生了预期外的错误" +copyErrorInfo: "å¤åˆ¶é”™è¯¯ä¿¡æ¯" _achievements: earnedAt: "è¾¾æˆæ—¶é—´" _types: @@ -1497,14 +1485,14 @@ _tutorial: step8_3: "您也å¯ä»¥ç¨åŽå†æ›´æ”¹é€šçŸ¥è®¾ç½®ã€‚" _2fa: alreadyRegistered: "æ¤è®¾å¤‡å·²è¢«æ³¨å†Œ" - registerDevice: "注册设备" - registerKey: "注册密钥" step1: "首先,在您的设备上安装验è¯åº”用,例如{a}或{b}。" step2: "ç„¶åŽï¼Œæ‰«æå±å¹•上显示的二维ç 。" step2Url: "在桌é¢åº”用程åºä¸è¾“入以下URL:" step3: "输入您的应用æä¾›çš„动æ€å£ä»¤ä»¥å®Œæˆè®¾ç½®ã€‚" step4: "从现在开始,任何登录æ“ä½œéƒ½å°†è¦æ±‚您æä¾›åЍæ€å£ä»¤ã€‚" securityKeyInfo: "您å¯ä»¥è®¾ç½®ä½¿ç”¨æ”¯æŒFIDO2的硬件安全密钥ã€è®¾å¤‡ä¸Šçš„æŒ‡çº¹æˆ–PINæ¥ä¿æŠ¤æ‚¨çš„登录过程。" + removeKeyConfirm: "您确定è¦åˆ 除 {name} å—?" + renewTOTPCancel: "ä¸ç”¨ï¼Œè°¢è°¢" _permissions: "read:account": "查看账户信æ¯" "write:account": "æ›´æ”¹å¸æˆ·ä¿¡æ¯" @@ -1553,7 +1541,6 @@ _antennaSources: homeTimeline: "已关注用户的帖å" users: "æ¥è‡ªæŒ‡å®šç”¨æˆ·çš„帖å" userList: "æ¥è‡ªæŒ‡å®šåˆ—表ä¸çš„帖å" - userGroup: "æ¥è‡ªæŒ‡å®šç¾¤ç»„ä¸ç”¨æˆ·çš„帖å" _weekday: sunday: "星期日" monday: "星期一" @@ -1628,8 +1615,6 @@ _visibility: followersDescription: "ä»…å‘é€è‡³å…³æ³¨è€…" specified: "指定用户" specifiedDescription: "ä»…å‘é€è‡³æŒ‡å®šç”¨æˆ·" - localOnly: "仅陿œ¬åœ°" - localOnlyDescription: "对远程用户ä¸å¯è§" _postForm: replyPlaceholder: "回å¤è¿™ä¸ªå¸–å..." quotePlaceholder: "引用这个帖å..." @@ -1767,12 +1752,9 @@ _notification: youGotReply: "æ¥è‡ª{name}的回å¤" youGotQuote: "æ¥è‡ª{name}的引用" youRenoted: "æ¥è‡ª{name}的转å‘" - youGotMessagingMessageFromUser: "æ¥è‡ª{name}çš„èŠå¤©" - youGotMessagingMessageFromGroup: "æ¥è‡ª{name}的群èŠ" youWereFollowed: "å…³æ³¨äº†ä½ ã€‚" youReceivedFollowRequest: "您有新的关注请求" yourFollowRequestAccepted: "您的关注请求已通过" - youWereInvitedToGroup: "您有新的群组邀请" pollEnded: "é—®å·è°ƒæŸ¥ç»“果已生æˆã€‚" unreadAntennaNote: "天线 {name}" emptyPushNotificationMessage: "推é€é€šçŸ¥å·²æ›´æ–°" @@ -1788,7 +1770,6 @@ _notification: pollEnded: "é—®å·è°ƒæŸ¥ç»“æŸ" receiveFollowRequest: "收到关注请求" followRequestAccepted: "关注请求已通过" - groupInvited: "åŠ å…¥ç¾¤ç»„é‚€è¯·" app: "å…³è”应用的通知" _actions: followBack: "回关" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index f99ca91e63..1dd2ae4cec 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -103,13 +103,15 @@ renoted: "轉傳æˆåŠŸ" cantRenote: "無法轉發æ¤è²¼æ–‡ã€‚" cantReRenote: "無法轉傳之å‰å·²ç¶“轉傳éŽçš„內容。" quote: "引用" +inChannelRenote: "åœ¨é »é“內轉發" +inChannelQuote: "åœ¨é »é“內引用" pinnedNote: "å·²ç½®é ‚çš„è²¼æ–‡" pinned: "ç½®é ‚" you: "您" clickToShow: "按一下以顯示" sensitive: "æ•æ„Ÿå…§å®¹" add: "新增" -reaction: "情感" +reaction: "忇‰" reactions: "情感" reactionSetting: "åœ¨é¸æ“‡å™¨ä¸é¡¯ç¤ºå應" reactionSettingDescription2: "æ‹–å‹•ä»¥é‡æ–°åˆ—åºï¼Œé»žæ“Šä»¥åˆªé™¤ï¼ŒæŒ‰ä¸‹ + æ·»åŠ ã€‚" @@ -391,13 +393,10 @@ about: "關於" aboutMisskey: "關於 Misskey" administrator: "管ç†å“¡" token: "權æ–" -twoStepAuthentication: "兩階段驗è‰" moderator: "審查員" moderation: "審查" nUsersMentioned: "æåˆ°äº†{n}" securityKey: "安全金鑰" -securityKeyName: "金鑰å稱" -registerSecurityKey: "註冊安全金鑰" lastUsed: "上次使用" unregister: "註銷帳號" passwordLessLogin: "è¨ç½®ç„¡å¯†ç¢¼ç™»å…¥" @@ -415,24 +414,15 @@ markAsReadAllTalkMessages: "標記所有訊æ¯ç‚ºå·²è®€" help: "幫助" inputMessageHere: "在æ¤è¼¸å…¥è¨Šæ¯" close: "關閉" -group: "群組" -groups: "群組" -createGroup: "創建群組" -ownedGroups: "æ“æœ‰çš„群組" -joinedGroups: "群組æˆå“¡" invites: "邀請" -groupName: "群組å稱" members: "æˆå“¡" transfer: "轉讓" -messagingWithUser: "與其他使用者èŠå¤©" -messagingWithGroup: "發é€è¨Šæ¯è‡³ç¾¤çµ„" title: "標題" text: "æ–‡å—" enable: "啟用" next: "下一æ¥" -retype: "釿–°è¼¸å…¥" +retype: "冿¬¡è¼¸å…¥" noteOf: "{user}的貼文" -inviteToGroup: "邀請至群組" quoteAttached: "引用" quoteQuestion: "是å¦è¦å¼•用?" noMessagesYet: "沒有訊æ¯" @@ -454,17 +444,13 @@ passwordMatched: "密碼一致" passwordNotMatched: "密碼ä¸ä¸€è‡´" signinWith: "以{x}登錄" signinFailed: "登入失敗。 請檢查使用者å稱和密碼。" -tapSecurityKey: "點擊安全密鑰" or: "或者" language: "語言" uiLanguage: "介é¢èªžè¨€" -groupInvited: "您有新的群組邀請" aboutX: "關於{x}" emojiStyle: "è¡¨æƒ…ç¬¦è™Ÿçš„é¢¨æ ¼" native: "原生" disableDrawer: "ä¸é¡¯ç¤ºä¸‹æ‹‰å¼é¸å–®" -youHaveNoGroups: "找ä¸åˆ°ç¾¤çµ„" -joinOrCreateGroup: "è«‹åŠ å…¥ç¾æœ‰ç¾¤çµ„,或創建新群組。" noHistory: "沒有æ·å²ç´€éŒ„" signinHistory: "登入æ·å²" enableAdvancedMfm: "啟用高級MFM" @@ -673,8 +659,8 @@ repliedCount: "回覆數é‡" renotedCount: "轉發次數" followingCount: "æ£åœ¨è·Ÿéš¨çš„用戶數é‡" followersCount: "跟隨者數é‡" -sentReactionsCount: "æƒ…æ„Ÿç™¼é€æ¬¡æ•¸" -receivedReactionsCount: "情感收到次數" +sentReactionsCount: "忇‰ç™¼é€æ¬¡æ•¸" +receivedReactionsCount: "æ”¶åˆ°åæ‡‰æ¬¡æ•¸" pollVotesCount: "已統計的投票數" pollVotedCount: "已投票數" yes: "確定" @@ -838,8 +824,6 @@ deleteAccountConfirm: "å°‡è¦åˆªé™¤å¸³æˆ¶ã€‚是å¦ç¢ºå®šï¼Ÿ" incorrectPassword: "密碼錯誤。" voteConfirm: "確定投給「{choice}ã€ï¼Ÿ" hide: "éš±è—" -leaveGroup: "離開群組" -leaveGroupConfirm: "確定離開「{name}ã€ï¼Ÿ" useDrawerReactionPickerForMobile: "在移動è¨å‚™ä¸Šä½¿ç”¨æŠ½å±œé¡¯ç¤º" welcomeBackWithName: "æ¡è¿Žå›žä¾†ï¼Œ{name}" clickToFinishEmailVerification: "點擊 [{ok}] 完æˆé›»å郵件地å€èªè‰ã€‚" @@ -952,6 +936,9 @@ thisPostMayBeAnnoyingHome: "發布到首é " thisPostMayBeAnnoyingCancel: "退出" thisPostMayBeAnnoyingIgnore: "直接發布貼文" collapseRenotes: "çœç•¥é¡¯ç¤ºå·²çœ‹éŽçš„轉發貼文" +internalServerError: "內部伺æœå™¨éŒ¯èª¤" +internalServerErrorDescription: "內部伺æœå™¨ç™¼ç”Ÿäº†éžé 期的錯誤。" +copyErrorInfo: "複製錯誤資訊" _achievements: earnedAt: "ç²å¾—日期" _types: @@ -1488,7 +1475,7 @@ _tutorial: step5_3: "想è¦è¿½éš¨å…¶ä»–人,åªè¦é»žæ“Šä»–們的大é 貼並按「追隨ã€å³å¯ã€‚" step5_4: "如果使用者的åå—æ—有鎖é çš„åœ–ç¤ºï¼Œä»£è¡¨ä»–å€‘éœ€è¦æ‰‹å‹•æ ¸å‡†ä½ çš„è¿½éš¨è«‹æ±‚ã€‚" step6_1: "ç¾åœ¨ä½ å¯ä»¥åœ¨æ™‚間軸上看到其他用戶的貼文。" - step6_2: "ä½ ä¹Ÿå¯ä»¥å°åˆ¥äººçš„貼文作出「情感ã€ï¼Œä½œå‡ºç°¡å–®çš„回覆。" + step6_2: "ä½ ä¹Ÿå¯ä»¥å°åˆ¥äººçš„è²¼æ–‡ä½œå‡ºã€Œåæ‡‰ã€ï¼Œä½œå‡ºç°¡å–®çš„回覆。" step6_3: "在他人的貼文按下\"+\"圖標,å³å¯é¸æ“‡å–œå¥½çš„表情符號進行回應。" step7_1: "以上為Misskey的基本æ“作說明,教å¸åœ¨æ¤å‘Šä¸€æ®µè½ã€‚辛苦了。" step7_2: "æ¡è¿Žåˆ°{help}來çžè§£æ›´å¤šMisskey相關介紹。" @@ -1498,14 +1485,14 @@ _tutorial: step8_3: "通知的è¨å®šå¯ä»¥åœ¨ä¹‹å¾Œè®Šæ›´ã€‚" _2fa: alreadyRegistered: "æ¤è¨å‚™å·²ç¶“被註冊éŽäº†" - registerDevice: "註冊è£ç½®" - registerKey: "註冊éµ" step1: "首先,在您的è¨å‚™ä¸Šå®‰è£äºŒæ¥é©—è‰ç¨‹å¼ï¼Œä¾‹å¦‚{a}或{b}。" step2: "然後,掃æèž¢å¹•上的QR code。" step2Url: "在桌é¢ç‰ˆæ‡‰ç”¨ä¸ï¼Œè«‹è¼¸å…¥ä»¥ä¸‹çš„URL:" step3: "輸入您的Appæä¾›çš„æ¬Šæ–以完æˆè¨å®šã€‚" step4: "從ç¾åœ¨é–‹å§‹ï¼Œä»»ä½•登入æ“ä½œéƒ½å°‡è¦æ±‚您æä¾›æ¬Šæ–。" securityKeyInfo: "您å¯ä»¥è¨å®šä½¿ç”¨æ”¯æ´FIDO2的硬體安全鎖ã€çµ‚端è¨å‚™çš„æŒ‡çº¹èªè‰æˆ–者PIN碼來登入。" + removeKeyConfirm: "è¦åˆªé™¤{name}嗎?" + renewTOTPCancel: "ç¾åœ¨ä¸è¦" _permissions: "read:account": "查看我的帳戶資訊" "write:account": "更改我的帳戶資訊" @@ -1524,8 +1511,8 @@ _permissions: "write:notes": "撰寫或刪除貼文" "read:notifications": "查看通知" "write:notifications": "編輯通知" - "read:reactions": "查看情感" - "write:reactions": "編輯情感" + "read:reactions": "æŸ¥çœ‹åæ‡‰" + "write:reactions": "ç·¨è¼¯åæ‡‰" "write:votes": "投票" "read:pages": "顯示é é¢" "write:pages": "編輯é é¢" @@ -1554,7 +1541,6 @@ _antennaSources: homeTimeline: "來自已追隨使用者的貼文" users: "來自特定使用者的貼文" userList: "來自特定清單ä¸çš„貼文" - userGroup: "來自特定群組的貼文" _weekday: sunday: "週日" monday: "週一" @@ -1629,8 +1615,6 @@ _visibility: followersDescription: "僅發é€è‡³é—œæ³¨è€…" specified: "指定使用者" specifiedDescription: "僅發é€è‡³æŒ‡å®šä½¿ç”¨è€…" - localOnly: "åƒ…é™æœ¬åœ°" - localOnlyDescription: "å°é 端使用者隱è—" _postForm: replyPlaceholder: "回覆æ¤è²¼æ–‡..." quotePlaceholder: "引用æ¤è²¼æ–‡..." @@ -1768,12 +1752,9 @@ _notification: youGotReply: "{name}回覆了您" youGotQuote: "{name}引用了您" youRenoted: "{name} è½‰ç™¼äº†ä½ çš„è²¼æ–‡" - youGotMessagingMessageFromUser: "{name}發é€çµ¦æ‚¨çš„訊æ¯" - youGotMessagingMessageFromGroup: "{name}發é€çµ¦æ‚¨çš„訊æ¯" youWereFollowed: "您有新的追隨者" youReceivedFollowRequest: "您有新的追隨請求" yourFollowRequestAccepted: "您的追隨請求已通éŽ" - youWereInvitedToGroup: "您有新的群組邀請" pollEnded: "å•å·èª¿æŸ¥å·²ç”¢ç”Ÿçµæžœ" unreadAntennaNote: "天線 {name}" emptyPushNotificationMessage: "推é€é€šçŸ¥å·²æ›´æ–°" @@ -1789,7 +1770,6 @@ _notification: pollEnded: "å•å·èª¿æŸ¥çµæŸ" receiveFollowRequest: "已收到追隨請求" followRequestAccepted: "追隨請求已接å—" - groupInvited: "åŠ å…¥ç¤¾ç¾¤é‚€è«‹" app: "應用程å¼é€šçŸ¥" _actions: followBack: "回關" diff --git a/package.json b/package.json index 5a6b807d66..8d5334433b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.6.1", + "version": "13.7.5", "codename": "nasubi", "repository": { "type": "git", @@ -16,10 +16,11 @@ "scripts": { "build-pre": "node ./scripts/build-pre.js", "build": "pnpm build-pre && pnpm -r build && pnpm gulp", - "start": "cd packages/backend && node ./built/boot/index.js", + "start": "pnpm check:connect && cd packages/backend && node ./built/boot/index.js", "start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/index.js", "init": "pnpm migrate", "migrate": "cd packages/backend && pnpm migrate", + "check:connect": "cd packages/backend && pnpm check:connect", "migrateandstart": "pnpm migrate && pnpm start", "gulp": "pnpm exec gulp build", "watch": "pnpm dev", @@ -54,12 +55,12 @@ "devDependencies": { "@types/gulp": "4.0.10", "@types/gulp-rename": "2.0.1", - "@typescript-eslint/eslint-plugin": "5.51.0", - "@typescript-eslint/parser": "5.51.0", + "@typescript-eslint/eslint-plugin": "5.52.0", + "@typescript-eslint/parser": "5.52.0", "cross-env": "7.0.3", - "cypress": "12.5.1", - "eslint": "8.33.0", - "start-server-and-test": "1.15.3" + "cypress": "12.6.0", + "eslint": "8.34.0", + "start-server-and-test": "1.15.4" }, "optionalDependencies": { "@tensorflow/tfjs-core": "4.2.0" diff --git a/packages/backend/.swcrc b/packages/backend/.swcrc index 55a88456ef..08d4222d01 100644 --- a/packages/backend/.swcrc +++ b/packages/backend/.swcrc @@ -1,25 +1,23 @@ { - "$schema": "https://json.schemastore.org/swcrc", - "jsc": { - "parser": { - "syntax": "typescript", - "dynamicImport": true, - "decorators": true - }, - "transform": { - "legacyDecorator": true, - "decoratorMetadata": true - }, + "$schema": "https://json.schemastore.org/swcrc", + "jsc": { + "parser": { + "syntax": "typescript", + "dynamicImport": true, + "decorators": true + }, + "transform": { + "legacyDecorator": true, + "decoratorMetadata": true + }, "experimental": { "keepImportAssertions": true }, - "baseUrl": ".", + "baseUrl": "src", "paths": { - "@/*": [ - "./src/*" - ] + "@/*": ["*"] }, "target": "es2021" - }, - "minify": false + }, + "minify": false } diff --git a/packages/backend/assets/apple-touch-icon.png b/packages/backend/assets/apple-touch-icon.png Binary files differindex 947c513bbb..06ad3f1bb4 100644 --- a/packages/backend/assets/apple-touch-icon.png +++ b/packages/backend/assets/apple-touch-icon.png diff --git a/packages/backend/assets/icons/192.png b/packages/backend/assets/icons/192.png Binary files differindex 606b46d87c..15fd1e3731 100644 --- a/packages/backend/assets/icons/192.png +++ b/packages/backend/assets/icons/192.png diff --git a/packages/backend/assets/icons/512.png b/packages/backend/assets/icons/512.png Binary files differindex ba51546427..f2169ec9b0 100644 --- a/packages/backend/assets/icons/512.png +++ b/packages/backend/assets/icons/512.png diff --git a/packages/backend/check_connect.js b/packages/backend/check_connect.js new file mode 100644 index 0000000000..8bf134a105 --- /dev/null +++ b/packages/backend/check_connect.js @@ -0,0 +1,10 @@ +import {loadConfig} from './built/config.js'; +import {createRedisConnection} from "./built/redis.js"; + +const config = loadConfig(); +const redis = createRedisConnection(config); + +redis.on('connect', () => redis.disconnect()); +redis.on('error', (e) => { + throw e; +}); diff --git a/packages/backend/migration/1676434944993-drop-group.js b/packages/backend/migration/1676434944993-drop-group.js index bdf3c1034e..c856046eb9 100644 --- a/packages/backend/migration/1676434944993-drop-group.js +++ b/packages/backend/migration/1676434944993-drop-group.js @@ -10,19 +10,11 @@ export class dropGroup1676434944993 { await queryRunner.query(`CREATE TYPE "public"."antenna_src_enum" AS ENUM('home', 'all', 'users', 'list')`); await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "public"."antenna_src_enum" USING "src"::"text"::"public"."antenna_src_enum"`); await queryRunner.query(`DROP TYPE "public"."antenna_src_enum_old"`); - await queryRunner.query(`ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`); - await queryRunner.query(`CREATE TYPE "public"."notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app')`); - await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum" USING "type"::"text"::"public"."notification_type_enum"`); - await queryRunner.query(`DROP TYPE "public"."notification_type_enum_old"`); await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "emailNotificationTypes" SET DEFAULT '["follow","receiveFollowRequest"]'`); } async down(queryRunner) { await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "emailNotificationTypes" SET DEFAULT '["follow", "receiveFollowRequest", "groupInvited"]'`); - await queryRunner.query(`CREATE TYPE "public"."notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app')`); - await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum_old" USING "type"::"text"::"public"."notification_type_enum_old"`); - await queryRunner.query(`DROP TYPE "public"."notification_type_enum"`); - await queryRunner.query(`ALTER TYPE "public"."notification_type_enum_old" RENAME TO "notification_type_enum"`); await queryRunner.query(`CREATE TYPE "public"."antenna_src_enum_old" AS ENUM('home', 'all', 'users', 'list', 'group')`); await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "public"."antenna_src_enum_old" USING "src"::"text"::"public"."antenna_src_enum_old"`); await queryRunner.query(`DROP TYPE "public"."antenna_src_enum"`); diff --git a/packages/backend/migration/1676438468213-ad3.js b/packages/backend/migration/1676438468213-ad3.js index bf1f384adc..18f56e8d36 100644 --- a/packages/backend/migration/1676438468213-ad3.js +++ b/packages/backend/migration/1676438468213-ad3.js @@ -4,6 +4,6 @@ export class ad1676438468213 { await queryRunner.query(`ALTER TABLE "ad" ADD "startsAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); } async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "startsAt"`); + await queryRunner.query(`ALTER TABLE "ad" DROP COLUMN "startsAt"`); } } diff --git a/packages/backend/package.json b/packages/backend/package.json index 52b0d74ad0..4d1d37efff 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -7,48 +7,62 @@ "start": "node ./built/index.js", "start:test": "NODE_ENV=test node ./built/index.js", "migrate": "pnpm typeorm migration:run -d ormconfig.js", - "build:swc": "swc src -d built -D", + "check:connect": "node ./check_connect.js", + "build": "swc src -d built -D", "watch:swc": "swc src -d built -D -w", - "build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json", + "build:tsc": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json", "watch": "node watch.mjs", "typecheck": "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 --runInBand", - "jest-and-coverage": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --runInBand", + "jest": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --runInBand --detectOpenHandles", + "jest-and-coverage": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --runInBand --detectOpenHandles", "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-and-coverage": "pnpm jest-and-coverage" }, "optionalDependencies": { + "@swc/core-android-arm64": "^1.3.11", + "@swc/core-darwin-arm64": "^1.3.36", + "@swc/core-darwin-x64": "^1.3.36", + "@swc/core-linux-arm-gnueabihf": "^1.3.36", + "@swc/core-linux-arm64-gnu": "^1.3.36", + "@swc/core-linux-arm64-musl": "^1.3.36", + "@swc/core-linux-x64-gnu": "^1.3.36", + "@swc/core-linux-x64-musl": "^1.3.36", + "@swc/core-win32-arm64-msvc": "^1.3.36", + "@swc/core-win32-ia32-msvc": "^1.3.36", + "@swc/core-win32-x64-msvc": "^1.3.36", "@tensorflow/tfjs": "4.2.0", "@tensorflow/tfjs-node": "4.2.0" }, "dependencies": { - "@bull-board/api": "4.11.1", - "@bull-board/fastify": "4.11.1", - "@bull-board/ui": "4.11.1", + "@bull-board/api": "4.12.1", + "@bull-board/fastify": "4.12.1", + "@bull-board/ui": "4.12.1", "@discordapp/twemoji": "14.0.2", "@fastify/accepts": "4.1.0", "@fastify/cookie": "8.3.0", "@fastify/cors": "8.2.0", "@fastify/http-proxy": "8.4.0", - "@fastify/multipart": "7.4.0", - "@fastify/static": "6.8.0", + "@fastify/multipart": "7.4.1", + "@fastify/static": "6.9.0", "@fastify/view": "7.4.1", - "@nestjs/common": "9.3.7", - "@nestjs/core": "9.3.7", - "@nestjs/testing": "9.3.7", + "@nestjs/common": "9.3.9", + "@nestjs/core": "9.3.9", + "@nestjs/testing": "9.3.9", "@peertube/http-signature": "1.7.0", "@sinonjs/fake-timers": "10.0.2", + "@swc/cli": "0.1.62", + "@swc/core": "1.3.36", "accepts": "1.3.8", "ajv": "8.12.0", "archiver": "5.3.1", "autwh": "0.1.0", - "aws-sdk": "2.1295.0", + "aws-sdk": "2.1318.0", "bcryptjs": "2.4.3", - "blurhash": "2.0.4", - "bull": "4.10.3", + "blurhash": "2.0.5", + "bull": "4.10.4", "cacheable-lookup": "6.1.0", "cbor": "8.1.0", "chalk": "5.2.0", @@ -60,12 +74,13 @@ "date-fns": "2.29.3", "deep-email-validator": "0.1.21", "escape-regexp": "0.0.1", - "fastify": "4.12.0", + "fastify": "4.13.0", "feed": "4.2.2", - "file-type": "18.2.0", + "file-type": "18.2.1", "fluent-ffmpeg": "2.1.2", "form-data": "4.0.0", "got": "12.5.3", + "happy-dom": "^8.7.0", "hpagent": "1.2.0", "ioredis": "4.28.5", "ip-cidr": "3.1.0", @@ -85,6 +100,7 @@ "nsfwjs": "2.4.2", "oauth": "0.10.0", "os-utils": "0.0.14", + "otpauth": "^9.0.2", "parse5": "7.1.2", "pg": "8.9.0", "private-ip": "3.0.0", @@ -104,15 +120,14 @@ "rss-parser": "3.12.0", "rxjs": "7.8.0", "s-age": "1.1.2", - "sanitize-html": "2.9.0", + "sanitize-html": "2.10.0", "seedrandom": "3.0.5", "semver": "7.3.8", "sharp": "0.31.3", - "speakeasy": "2.0.0", "strict-event-emitter-types": "2.0.0", "stringz": "2.1.0", "summaly": "github:misskey-dev/summaly", - "systeminformation": "5.17.8", + "systeminformation": "5.17.9", "tinycolor2": "1.6.0", "tmp": "0.2.1", "tsc-alias": "1.8.2", @@ -126,14 +141,12 @@ "vary": "1.1.2", "web-push": "3.5.0", "websocket": "1.0.34", - "ws": "8.12.0", + "ws": "8.12.1", "xev": "3.0.2" }, "devDependencies": { - "@jest/globals": "29.4.2", + "@jest/globals": "29.4.3", "@redocly/openapi-core": "1.0.0-beta.123", - "@swc/cli": "0.1.61", - "@swc/core": "1.3.34", "@swc/jest": "0.2.24", "@types/accepts": "1.3.5", "@types/archiver": "5.3.1", @@ -151,7 +164,7 @@ "@types/jsonld": "1.5.8", "@types/jsrsasign": "10.5.5", "@types/mime-types": "2.1.1", - "@types/node": "18.13.0", + "@types/node": "18.14.0", "@types/node-fetch": "3.0.3", "@types/nodemailer": "6.4.7", "@types/oauth": "0.9.1", @@ -167,7 +180,6 @@ "@types/semver": "7.3.13", "@types/sharp": "0.31.1", "@types/sinonjs__fake-timers": "8.1.2", - "@types/speakeasy": "2.0.7", "@types/tinycolor2": "1.4.3", "@types/tmp": "0.2.3", "@types/unzipper": "0.10.5", @@ -176,13 +188,13 @@ "@types/web-push": "3.3.2", "@types/websocket": "1.0.5", "@types/ws": "8.5.4", - "@typescript-eslint/eslint-plugin": "5.51.0", - "@typescript-eslint/parser": "5.51.0", + "@typescript-eslint/eslint-plugin": "5.52.0", + "@typescript-eslint/parser": "5.52.0", "cross-env": "7.0.3", - "eslint": "8.33.0", + "eslint": "8.34.0", "eslint-plugin-import": "2.27.5", "execa": "6.1.0", - "jest": "29.4.2", - "jest-mock": "29.4.2" + "jest": "29.4.3", + "jest-mock": "29.4.3" } -} +}
\ No newline at end of file diff --git a/packages/backend/src/@types/redis-lock.d.ts b/packages/backend/src/@types/redis-lock.d.ts new file mode 100644 index 0000000000..9242656a98 --- /dev/null +++ b/packages/backend/src/@types/redis-lock.d.ts @@ -0,0 +1,8 @@ +declare module 'redis-lock' { + import type Redis from 'ioredis'; + + type Lock = (lockName: string, timeout?: number, taskToPerform?: () => Promise<void>) => void; + function redisLock(client: Redis.Redis, retryDelay: number): Lock; + + export = redisLock; +} diff --git a/packages/backend/src/core/AppLockService.ts b/packages/backend/src/core/AppLockService.ts index 5f3072a415..ee179b7f01 100644 --- a/packages/backend/src/core/AppLockService.ts +++ b/packages/backend/src/core/AppLockService.ts @@ -12,7 +12,7 @@ const retryDelay = 100; @Injectable() export class AppLockService { - private lock: (key: string, timeout?: number) => Promise<() => void>; + private lock: (key: string, timeout?: number, _?: (() => Promise<void>) | undefined) => Promise<() => void>; constructor( @Inject(DI.redis) diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index 8d961452c7..9b2d5dc0ff 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -1,7 +1,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import * as parse5 from 'parse5'; -import { JSDOM } from 'jsdom'; +import { Window } from 'happy-dom'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { intersperse } from '@/misc/prelude/array.js'; @@ -235,7 +235,7 @@ export class MfmService { return null; } - const { window } = new JSDOM(''); + const { window } = new Window(); const doc = window.document; @@ -300,7 +300,7 @@ export class MfmService { hashtag: (node) => { const a = doc.createElement('a'); - a.href = `${this.config.url}/tags/${node.props.hashtag}`; + a.setAttribute('href', `${this.config.url}/tags/${node.props.hashtag}`); a.textContent = `#${node.props.hashtag}`; a.setAttribute('rel', 'tag'); return a; @@ -326,7 +326,7 @@ export class MfmService { link: (node) => { const a = doc.createElement('a'); - a.href = node.props.url; + a.setAttribute('href', node.props.url); appendChildren(node.children, a); return a; }, @@ -335,7 +335,7 @@ export class MfmService { const a = doc.createElement('a'); const { username, host, acct } = node.props; const remoteUserInfo = mentionedRemoteUsers.find(remoteUser => remoteUser.username === username && remoteUser.host === host); - a.href = remoteUserInfo ? (remoteUserInfo.url ? remoteUserInfo.url : remoteUserInfo.uri) : `${this.config.url}/${acct}`; + a.setAttribute('href', remoteUserInfo ? (remoteUserInfo.url ? remoteUserInfo.url : remoteUserInfo.uri) : `${this.config.url}/${acct}`); a.className = 'u-url mention'; a.textContent = acct; return a; @@ -360,14 +360,14 @@ export class MfmService { url: (node) => { const a = doc.createElement('a'); - a.href = node.props.url; + a.setAttribute('href', node.props.url); a.textContent = node.props.url; return a; }, search: (node) => { const a = doc.createElement('a'); - a.href = `https://www.google.com/search?q=${node.props.query}`; + a.setAttribute('href', `https://www.google.com/search?q=${node.props.query}`); a.textContent = node.props.content; return a; }, diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts index 75bf4b0e01..2cad1bc07e 100644 --- a/packages/backend/src/core/PushNotificationService.ts +++ b/packages/backend/src/core/PushNotificationService.ts @@ -9,7 +9,7 @@ import { MetaService } from '@/core/MetaService.js'; import { bindThis } from '@/decorators.js'; // Defined also packages/sw/types.ts#L13 -type pushNotificationsTypes = { +type PushNotificationsTypes = { 'notification': Packed<'Notification'>; 'unreadAntennaNote': { antenna: { id: string, name: string }; @@ -22,8 +22,8 @@ type pushNotificationsTypes = { }; // Reduce length because push message servers have character limits -function truncateBody<T extends keyof pushNotificationsTypes>(type: T, body: pushNotificationsTypes[T]): pushNotificationsTypes[T] { - if (body === undefined) return body; +function truncateBody<T extends keyof PushNotificationsTypes>(type: T, body: PushNotificationsTypes[T]): PushNotificationsTypes[T] { + if (typeof body !== 'object') return body; return { ...body, @@ -56,7 +56,7 @@ export class PushNotificationService { } @bindThis - public async pushNotification<T extends keyof pushNotificationsTypes>(userId: string, type: T, body: pushNotificationsTypes[T]) { + public async pushNotification<T extends keyof PushNotificationsTypes>(userId: string, type: T, body: PushNotificationsTypes[T]) { const meta = await this.metaService.fetch(); if (!meta.enableServiceWorker || meta.swPublicKey == null || meta.swPrivateKey == null) return; diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts index 9bd80b6066..6d9569bce2 100644 --- a/packages/backend/src/core/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -450,9 +450,11 @@ export class ApInboxService { return `skip: delete actor ${actor.uri} !== ${uri}`; } - const user = await this.usersRepository.findOneByOrFail({ id: actor.id }); - if (user.isDeleted) { - this.logger.info('skip: already deleted'); + const user = await this.usersRepository.findOneBy({ id: actor.id }); + if (user == null) { + return 'skip: actor not found'; + } else if (user.isDeleted) { + return 'skip: already deleted'; } const job = await this.queueService.createDeleteAccountJob(actor); diff --git a/packages/backend/src/core/activitypub/ApRequestService.ts b/packages/backend/src/core/activitypub/ApRequestService.ts index bfd53dfabf..71fbc29476 100644 --- a/packages/backend/src/core/activitypub/ApRequestService.ts +++ b/packages/backend/src/core/activitypub/ApRequestService.ts @@ -28,31 +28,15 @@ type PrivateKey = { keyId: string; }; -@Injectable() -export class ApRequestService { - private logger: Logger; - - constructor( - @Inject(DI.config) - private config: Config, - - private userKeypairStoreService: UserKeypairStoreService, - private httpRequestService: HttpRequestService, - private loggerService: LoggerService, - ) { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - this.logger = this.loggerService?.getLogger('ap-request'); // ãªãœã‹ TypeError: Cannot read properties of undefined (reading 'getLogger') ã¨è¨€ã‚れる - } - - @bindThis - private createSignedPost(args: { key: PrivateKey, url: string, body: string, additionalHeaders: Record<string, string> }): Signed { +export class ApRequestCreator { + static createSignedPost(args: { key: PrivateKey, url: string, body: 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 request: Request = { url: u.href, method: 'POST', - headers: this.objectAssignWithLcKey({ + headers: this.#objectAssignWithLcKey({ 'Date': new Date().toUTCString(), 'Host': u.host, 'Content-Type': 'application/activity+json', @@ -60,7 +44,7 @@ export class ApRequestService { }, args.additionalHeaders), }; - const result = this.signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'digest']); + const result = this.#signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'digest']); return { request, @@ -70,21 +54,20 @@ export class ApRequestService { }; } - @bindThis - private createSignedGet(args: { key: PrivateKey, url: string, additionalHeaders: Record<string, string> }): Signed { + static createSignedGet(args: { key: PrivateKey, url: string, additionalHeaders: Record<string, string> }): Signed { const u = new URL(args.url); const request: Request = { url: u.href, method: 'GET', - headers: this.objectAssignWithLcKey({ + headers: this.#objectAssignWithLcKey({ 'Accept': 'application/activity+json, application/ld+json', 'Date': new Date().toUTCString(), 'Host': new URL(args.url).host, }, args.additionalHeaders), }; - const result = this.signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'accept']); + const result = this.#signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'accept']); return { request, @@ -94,13 +77,12 @@ export class ApRequestService { }; } - @bindThis - private signToRequest(request: Request, key: PrivateKey, includeHeaders: string[]): Signed { - const signingString = this.genSigningString(request, includeHeaders); + static #signToRequest(request: Request, key: PrivateKey, includeHeaders: string[]): Signed { + const signingString = this.#genSigningString(request, includeHeaders); const signature = crypto.sign('sha256', Buffer.from(signingString), key.privateKeyPem).toString('base64'); const signatureHeader = `keyId="${key.keyId}",algorithm="rsa-sha256",headers="${includeHeaders.join(' ')}",signature="${signature}"`; - request.headers = this.objectAssignWithLcKey(request.headers, { + request.headers = this.#objectAssignWithLcKey(request.headers, { Signature: signatureHeader, }); // node-fetch will generate this for us. if we keep 'Host', it won't change with redirects! @@ -114,9 +96,8 @@ export class ApRequestService { }; } - @bindThis - private genSigningString(request: Request, includeHeaders: string[]): string { - request.headers = this.lcObjectKey(request.headers); + static #genSigningString(request: Request, includeHeaders: string[]): string { + request.headers = this.#lcObjectKey(request.headers); const results: string[] = []; @@ -131,16 +112,31 @@ export class ApRequestService { return results.join('\n'); } - @bindThis - private lcObjectKey(src: Record<string, string>): Record<string, string> { + static #lcObjectKey(src: Record<string, string>): Record<string, string> { const dst: Record<string, string> = {}; for (const key of Object.keys(src).filter(x => x !== '__proto__' && typeof src[x] === 'string')) dst[key.toLowerCase()] = src[key]; return dst; } - @bindThis - private objectAssignWithLcKey(a: Record<string, string>, b: Record<string, string>): Record<string, string> { - return Object.assign(this.lcObjectKey(a), this.lcObjectKey(b)); + static #objectAssignWithLcKey(a: Record<string, string>, b: Record<string, string>): Record<string, string> { + return Object.assign(this.#lcObjectKey(a), this.#lcObjectKey(b)); + } +} + +@Injectable() +export class ApRequestService { + private logger: Logger; + + constructor( + @Inject(DI.config) + private config: Config, + + private userKeypairStoreService: UserKeypairStoreService, + private httpRequestService: HttpRequestService, + private loggerService: LoggerService, + ) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + this.logger = this.loggerService?.getLogger('ap-request'); // ãªãœã‹ TypeError: Cannot read properties of undefined (reading 'getLogger') ã¨è¨€ã‚れる } @bindThis @@ -149,7 +145,7 @@ export class ApRequestService { const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); - const req = this.createSignedPost({ + const req = ApRequestCreator.createSignedPost({ key: { privateKeyPem: keypair.privateKey, keyId: `${this.config.url}/users/${user.id}#main-key`, @@ -176,7 +172,7 @@ export class ApRequestService { public async signedGet(url: string, user: { id: User['id'] }) { const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); - const req = this.createSignedGet({ + const req = ApRequestCreator.createSignedGet({ key: { privateKeyPem: keypair.privateKey, keyId: `${this.config.url}/users/${user.id}#main-key`, diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts index 268bf99119..7f2ca9c05e 100644 --- a/packages/backend/src/core/activitypub/type.ts +++ b/packages/backend/src/core/activitypub/type.ts @@ -1,8 +1,8 @@ -export type obj = { [x: string]: any }; +export type Obj = { [x: string]: any }; export type ApObject = IObject | string | (IObject | string)[]; export interface IObject { - '@context'?: string | string[] | obj | obj[]; + '@context'?: string | string[] | Obj | Obj[]; type: string | string[]; id?: string; name?: string | null; diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index 34ff52ede8..33c76c6937 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -94,13 +94,6 @@ export class NotificationEntityService implements OnModuleInit { }), reaction: notification.reaction, } : {}), - ...(notification.type === 'pollVote' ? { // TODO: ãã®ã†ã¡æ¶ˆã™ - note: this.noteEntityService.pack(notification.note ?? notification.noteId!, { id: notification.notifieeId }, { - detail: true, - _hint_: options._hintForEachNotes_, - }), - choice: notification.choice, - } : {}), ...(notification.type === 'pollEnded' ? { note: this.noteEntityService.pack(notification.note ?? notification.noteId!, { id: notification.notifieeId }, { detail: true, diff --git a/packages/backend/src/core/entities/RoleEntityService.ts b/packages/backend/src/core/entities/RoleEntityService.ts index f2ba642375..80ef5ac1fa 100644 --- a/packages/backend/src/core/entities/RoleEntityService.ts +++ b/packages/backend/src/core/entities/RoleEntityService.ts @@ -25,14 +25,7 @@ export class RoleEntityService { public async pack( src: Role['id'] | Role, me?: { id: User['id'] } | null | undefined, - options?: { - detail?: boolean; - }, ) { - const opts = Object.assign({ - detail: true, - }, options); - const role = typeof src === 'object' ? src : await this.rolesRepository.findOneByOrFail({ id: src }); const assigns = await this.roleAssignmentsRepository.findBy({ @@ -65,9 +58,6 @@ export class RoleEntityService { canEditMembersByModerator: role.canEditMembersByModerator, policies: policies, usersCount: assigns.length, - ...(opts.detail ? { - users: this.userEntityService.packMany(assigns.map(x => x.userId), me), - } : {}), }); } @@ -75,11 +65,8 @@ export class RoleEntityService { public packMany( roles: any[], me: { id: User['id'] }, - options?: { - detail?: boolean; - }, ) { - return Promise.all(roles.map(x => this.pack(x, me, options))); + return Promise.all(roles.map(x => this.pack(x, me))); } } diff --git a/packages/backend/src/models/entities/Notification.ts b/packages/backend/src/models/entities/Notification.ts index 105f0d0407..51117efba5 100644 --- a/packages/backend/src/models/entities/Notification.ts +++ b/packages/backend/src/models/entities/Notification.ts @@ -1,5 +1,5 @@ import { Entity, Index, JoinColumn, ManyToOne, Column, PrimaryColumn } from 'typeorm'; -import { notificationTypes } from '@/types.js'; +import { notificationTypes, obsoleteNotificationTypes } from '@/types.js'; import { id } from '../id.js'; import { User } from './User.js'; import { Note } from './Note.js'; @@ -58,7 +58,6 @@ export class Notification { * renote - 投稿ãŒRenoteã•れ㟠* quote - 投稿ãŒå¼•用Renoteã•れ㟠* reaction - 投稿ã«ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã•れ㟠- * pollVote - 投稿ã®ã‚¢ãƒ³ã‚±ãƒ¼ãƒˆã«æŠ•票ã•れ㟠(廃æ¢) * pollEnded - 自分ã®ã‚¢ãƒ³ã‚±ãƒ¼ãƒˆã‚‚ã—ãã¯è‡ªåˆ†ãŒæŠ•票ã—ãŸã‚¢ãƒ³ã‚±ãƒ¼ãƒˆãŒçµ‚了ã—㟠* receiveFollowRequest - フォãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã•れ㟠* followRequestAccepted - 自分ã®é€ã£ãŸãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒæ‰¿èªã•れ㟠@@ -67,7 +66,10 @@ export class Notification { */ @Index() @Column('enum', { - enum: notificationTypes, + enum: [ + ...notificationTypes, + ...obsoleteNotificationTypes, + ], comment: 'The type of the Notification.', }) public type: typeof notificationTypes[number]; diff --git a/packages/backend/src/models/entities/UserProfile.ts b/packages/backend/src/models/entities/UserProfile.ts index 3d35b4cb5a..60c1c55de5 100644 --- a/packages/backend/src/models/entities/UserProfile.ts +++ b/packages/backend/src/models/entities/UserProfile.ts @@ -1,5 +1,5 @@ import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; -import { ffVisibility, notificationTypes } from '@/types.js'; +import { obsoleteNotificationTypes, ffVisibility, notificationTypes } from '@/types.js'; import { id } from '../id.js'; import { User } from './User.js'; import { Page } from './Page.js'; @@ -205,7 +205,7 @@ export class UserProfile { enum: [ ...notificationTypes, // マイグレーションã§å‰Šé™¤ãŒå›°é›£ãªã®ã§å¤ã„enumã¯æ®‹ã—ã¦ãŠã - 'groupInvited', + ...obsoleteNotificationTypes, ], array: true, default: [], diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index cc27b36966..347fa59d36 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -108,9 +108,9 @@ export class ApiCallService implements OnApplicationShutdown { const [path] = await createTemp(); await pump(multipartData.file, fs.createWriteStream(path)); - const fields = {} as Record<string, string | undefined>; + const fields = {} as Record<string, unknown>; for (const [k, v] of Object.entries(multipartData.fields)) { - fields[k] = v.value; + fields[k] = typeof v === 'object' && 'value' in v ? v.value : undefined; } const token = fields['i']; diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 30101a2c60..d3e2219bd5 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -66,6 +66,7 @@ import * as ep___admin_roles_update from './endpoints/admin/roles/update.js'; import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js'; import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'; import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js'; +import * as ep___admin_roles_users from './endpoints/admin/roles/users.js'; import * as ep___announcements from './endpoints/announcements.js'; import * as ep___antennas_create from './endpoints/antennas/create.js'; import * as ep___antennas_delete from './endpoints/antennas/delete.js'; @@ -170,6 +171,7 @@ import * as ep___i_2fa_keyDone from './endpoints/i/2fa/key-done.js'; import * as ep___i_2fa_passwordLess from './endpoints/i/2fa/password-less.js'; import * as ep___i_2fa_registerKey from './endpoints/i/2fa/register-key.js'; import * as ep___i_2fa_register from './endpoints/i/2fa/register.js'; +import * as ep___i_2fa_updateKey from './endpoints/i/2fa/update-key.js'; import * as ep___i_2fa_removeKey from './endpoints/i/2fa/remove-key.js'; import * as ep___i_2fa_unregister from './endpoints/i/2fa/unregister.js'; import * as ep___i_apps from './endpoints/i/apps.js'; @@ -276,6 +278,9 @@ import * as ep___flash_myLikes from './endpoints/flash/my-likes.js'; import * as ep___ping from './endpoints/ping.js'; import * as ep___pinnedUsers from './endpoints/pinned-users.js'; import * as ep___promo_read from './endpoints/promo/read.js'; +import * as ep___roles_list from './endpoints/roles/list.js'; +import * as ep___roles_show from './endpoints/roles/show.js'; +import * as ep___roles_users from './endpoints/roles/users.js'; import * as ep___requestResetPassword from './endpoints/request-reset-password.js'; import * as ep___resetDb from './endpoints/reset-db.js'; import * as ep___resetPassword from './endpoints/reset-password.js'; @@ -382,6 +387,7 @@ const $admin_roles_update: Provider = { provide: 'ep:admin/roles/update', useCla const $admin_roles_assign: Provider = { provide: 'ep:admin/roles/assign', useClass: ep___admin_roles_assign.default }; const $admin_roles_unassign: Provider = { provide: 'ep:admin/roles/unassign', useClass: ep___admin_roles_unassign.default }; const $admin_roles_updateDefaultPolicies: Provider = { provide: 'ep:admin/roles/update-default-policies', useClass: ep___admin_roles_updateDefaultPolicies.default }; +const $admin_roles_users: Provider = { provide: 'ep:admin/roles/users', useClass: ep___admin_roles_users.default }; const $announcements: Provider = { provide: 'ep:announcements', useClass: ep___announcements.default }; const $antennas_create: Provider = { provide: 'ep:antennas/create', useClass: ep___antennas_create.default }; const $antennas_delete: Provider = { provide: 'ep:antennas/delete', useClass: ep___antennas_delete.default }; @@ -486,6 +492,7 @@ const $i_2fa_keyDone: Provider = { provide: 'ep:i/2fa/key-done', useClass: ep___ const $i_2fa_passwordLess: Provider = { provide: 'ep:i/2fa/password-less', useClass: ep___i_2fa_passwordLess.default }; const $i_2fa_registerKey: Provider = { provide: 'ep:i/2fa/register-key', useClass: ep___i_2fa_registerKey.default }; const $i_2fa_register: Provider = { provide: 'ep:i/2fa/register', useClass: ep___i_2fa_register.default }; +const $i_2fa_updateKey: Provider = { provide: 'ep:i/2fa/update-key', useClass: ep___i_2fa_updateKey.default }; const $i_2fa_removeKey: Provider = { provide: 'ep:i/2fa/remove-key', useClass: ep___i_2fa_removeKey.default }; const $i_2fa_unregister: Provider = { provide: 'ep:i/2fa/unregister', useClass: ep___i_2fa_unregister.default }; const $i_apps: Provider = { provide: 'ep:i/apps', useClass: ep___i_apps.default }; @@ -592,6 +599,9 @@ const $flash_myLikes: Provider = { provide: 'ep:flash/my-likes', useClass: ep___ const $ping: Provider = { provide: 'ep:ping', useClass: ep___ping.default }; const $pinnedUsers: Provider = { provide: 'ep:pinned-users', useClass: ep___pinnedUsers.default }; const $promo_read: Provider = { provide: 'ep:promo/read', useClass: ep___promo_read.default }; +const $roles_list: Provider = { provide: 'ep:roles/list', useClass: ep___roles_list.default }; +const $roles_show: Provider = { provide: 'ep:roles/show', useClass: ep___roles_show.default }; +const $roles_users: Provider = { provide: 'ep:roles/users', useClass: ep___roles_users.default }; const $requestResetPassword: Provider = { provide: 'ep:request-reset-password', useClass: ep___requestResetPassword.default }; const $resetDb: Provider = { provide: 'ep:reset-db', useClass: ep___resetDb.default }; const $resetPassword: Provider = { provide: 'ep:reset-password', useClass: ep___resetPassword.default }; @@ -702,6 +712,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_roles_assign, $admin_roles_unassign, $admin_roles_updateDefaultPolicies, + $admin_roles_users, $announcements, $antennas_create, $antennas_delete, @@ -806,6 +817,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $i_2fa_passwordLess, $i_2fa_registerKey, $i_2fa_register, + $i_2fa_updateKey, $i_2fa_removeKey, $i_2fa_unregister, $i_apps, @@ -912,6 +924,9 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $ping, $pinnedUsers, $promo_read, + $roles_list, + $roles_show, + $roles_users, $requestResetPassword, $resetDb, $resetPassword, @@ -1016,6 +1031,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_roles_assign, $admin_roles_unassign, $admin_roles_updateDefaultPolicies, + $admin_roles_users, $announcements, $antennas_create, $antennas_delete, @@ -1120,6 +1136,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $i_2fa_passwordLess, $i_2fa_registerKey, $i_2fa_register, + $i_2fa_updateKey, $i_2fa_removeKey, $i_2fa_unregister, $i_apps, @@ -1226,6 +1243,9 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $ping, $pinnedUsers, $promo_read, + $roles_list, + $roles_show, + $roles_users, $requestResetPassword, $resetDb, $resetPassword, diff --git a/packages/backend/src/server/api/GetterService.ts b/packages/backend/src/server/api/GetterService.ts index c7f9916f97..c94884a78c 100644 --- a/packages/backend/src/server/api/GetterService.ts +++ b/packages/backend/src/server/api/GetterService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { NotesRepository, UsersRepository } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; -import type { User } from '@/models/entities/User.js'; +import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -45,7 +45,7 @@ export class GetterService { throw new IdentifiableError('15348ddd-432d-49c2-8a5a-8069753becff', 'No such user.'); } - return user; + return user as LocalUser | RemoteUser; } /** diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index f1164b9957..bd3d8a28da 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -1,7 +1,7 @@ import { randomBytes } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; -import * as speakeasy from 'speakeasy'; +import * as OTPAuth from 'otpauth'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { UserSecurityKeysRepository, SigninsRepository, UserProfilesRepository, AttestationChallengesRepository, UsersRepository } from '@/models/index.js'; @@ -155,19 +155,19 @@ export class SigninApiService { }); } - const verified = (speakeasy as any).totp.verify({ - secret: profile.twoFactorSecret, - encoding: 'base32', - token: token, - window: 2, + const delta = OTPAuth.TOTP.validate({ + secret: OTPAuth.Secret.fromBase32(profile.twoFactorSecret!), + digits: 6, + token, + window: 1, }); - if (verified) { - return this.signinService.signin(request, reply, user); - } else { + if (delta === null) { return await fail(403, { id: 'cdf1235b-ac71-46d4-a3a6-84ccce48df6f', }); + } else { + return this.signinService.signin(request, reply, user); } } else if (body.credentialId && body.clientDataJSON && body.authenticatorData && body.signature) { if (!same && !profile.usePasswordLessLogin) { diff --git a/packages/backend/src/server/api/endpoint-base.ts b/packages/backend/src/server/api/endpoint-base.ts index 115526d997..ed283eb834 100644 --- a/packages/backend/src/server/api/endpoint-base.ts +++ b/packages/backend/src/server/api/endpoint-base.ts @@ -20,14 +20,14 @@ type File = { }; // TODO: paramsã®åž‹ã‚’T['params']ã®ã‚¹ã‚ーマ定義ã‹ã‚‰æŽ¨è«–ã™ã‚‹ -type executor<T extends IEndpointMeta, Ps extends Schema> = +type Executor<T extends IEndpointMeta, Ps extends Schema> = (params: SchemaType<Ps>, user: T['requireCredential'] extends true ? LocalUser : LocalUser | null, token: AccessToken | null, file?: File, cleanup?: () => any, ip?: string | null, headers?: Record<string, string> | null) => Promise<T['res'] extends undefined ? Response : SchemaType<NonNullable<T['res']>>>; export abstract class Endpoint<T extends IEndpointMeta, Ps extends Schema> { public exec: (params: any, user: T['requireCredential'] extends true ? LocalUser : LocalUser | null, token: AccessToken | null, file?: File, ip?: string | null, headers?: Record<string, string> | null) => Promise<any>; - constructor(meta: T, paramDef: Ps, cb: executor<T, Ps>) { + constructor(meta: T, paramDef: Ps, cb: Executor<T, Ps>) { const validate = ajv.compile(paramDef); this.exec = (params: any, user: T['requireCredential'] extends true ? LocalUser : LocalUser | null, token: AccessToken | null, file?: File, ip?: string | null, headers?: Record<string, string> | null) => { diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index d05005b078..4d5ed9fb62 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -66,6 +66,7 @@ import * as ep___admin_roles_update from './endpoints/admin/roles/update.js'; import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js'; import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'; import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js'; +import * as ep___admin_roles_users from './endpoints/admin/roles/users.js'; import * as ep___announcements from './endpoints/announcements.js'; import * as ep___antennas_create from './endpoints/antennas/create.js'; import * as ep___antennas_delete from './endpoints/antennas/delete.js'; @@ -170,6 +171,7 @@ import * as ep___i_2fa_keyDone from './endpoints/i/2fa/key-done.js'; import * as ep___i_2fa_passwordLess from './endpoints/i/2fa/password-less.js'; import * as ep___i_2fa_registerKey from './endpoints/i/2fa/register-key.js'; import * as ep___i_2fa_register from './endpoints/i/2fa/register.js'; +import * as ep___i_2fa_updateKey from './endpoints/i/2fa/update-key.js'; import * as ep___i_2fa_removeKey from './endpoints/i/2fa/remove-key.js'; import * as ep___i_2fa_unregister from './endpoints/i/2fa/unregister.js'; import * as ep___i_apps from './endpoints/i/apps.js'; @@ -276,6 +278,9 @@ import * as ep___flash_myLikes from './endpoints/flash/my-likes.js'; import * as ep___ping from './endpoints/ping.js'; import * as ep___pinnedUsers from './endpoints/pinned-users.js'; import * as ep___promo_read from './endpoints/promo/read.js'; +import * as ep___roles_list from './endpoints/roles/list.js'; +import * as ep___roles_show from './endpoints/roles/show.js'; +import * as ep___roles_users from './endpoints/roles/users.js'; import * as ep___requestResetPassword from './endpoints/request-reset-password.js'; import * as ep___resetDb from './endpoints/reset-db.js'; import * as ep___resetPassword from './endpoints/reset-password.js'; @@ -380,6 +385,7 @@ const eps = [ ['admin/roles/assign', ep___admin_roles_assign], ['admin/roles/unassign', ep___admin_roles_unassign], ['admin/roles/update-default-policies', ep___admin_roles_updateDefaultPolicies], + ['admin/roles/users', ep___admin_roles_users], ['announcements', ep___announcements], ['antennas/create', ep___antennas_create], ['antennas/delete', ep___antennas_delete], @@ -484,6 +490,7 @@ const eps = [ ['i/2fa/password-less', ep___i_2fa_passwordLess], ['i/2fa/register-key', ep___i_2fa_registerKey], ['i/2fa/register', ep___i_2fa_register], + ['i/2fa/update-key', ep___i_2fa_updateKey], ['i/2fa/remove-key', ep___i_2fa_removeKey], ['i/2fa/unregister', ep___i_2fa_unregister], ['i/apps', ep___i_apps], @@ -590,6 +597,9 @@ const eps = [ ['ping', ep___ping], ['pinned-users', ep___pinnedUsers], ['promo/read', ep___promo_read], + ['roles/list', ep___roles_list], + ['roles/show', ep___roles_show], + ['roles/users', ep___roles_users], ['request-reset-password', ep___requestResetPassword], ['reset-db', ep___resetDb], ['reset-password', ep___resetPassword], 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 6376cb153c..85b566aabe 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 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository, UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; @@ -161,6 +161,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + private roleService: RoleService, ) { super(meta, paramDef, async (ps, me) => { @@ -178,7 +181,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw new ApiError(meta.errors.noSuchFile); } - const isModerator = await this.roleService.isModerator(me); + const owner = file.userId ? await this.usersRepository.findOneByOrFail({ + id: file.userId, + }) : null; + + const iAmModerator = await this.roleService.isModerator(me); + const ownerIsModerator = owner ? await this.roleService.isModerator(owner) : false; return { id: file.id, @@ -207,8 +215,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { name: file.name, md5: file.md5, createdAt: file.createdAt.toISOString(), - requestIp: isModerator ? file.requestIp : null, - requestHeaders: isModerator ? file.requestHeaders : null, + requestIp: iAmModerator ? file.requestIp : null, + requestHeaders: iAmModerator && !ownerIsModerator ? file.requestHeaders : null, }; }); } 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 ac56de56b9..edaf638ea9 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/list.ts @@ -32,7 +32,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const roles = await this.rolesRepository.find({ order: { lastUsedAt: 'DESC' }, }); - return await this.roleEntityService.packMany(roles, me, { detail: false }); + return await this.roleEntityService.packMany(roles, me); }); } } 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 c83f96191d..01028a086f 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/show.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/show.ts @@ -39,12 +39,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private roleEntityService: RoleEntityService, ) { - super(meta, paramDef, async (ps) => { + super(meta, paramDef, async (ps, me) => { const role = await this.rolesRepository.findOneBy({ id: ps.roleId }); if (role == null) { throw new ApiError(meta.errors.noSuchRole); } - return await this.roleEntityService.pack(role); + return await this.roleEntityService.pack(role, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts new file mode 100644 index 0000000000..bb016a8425 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts @@ -0,0 +1,71 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { RoleAssignmentsRepository, RolesRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/core/QueryService.js'; +import { DI } from '@/di-symbols.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { ApiError } from '../../../error.js'; + +export const meta = { + tags: ['admin', 'role', 'users'], + + requireCredential: false, + requireAdmin: true, + + errors: { + noSuchRole: { + message: 'No such role.', + code: 'NO_SUCH_ROLE', + id: '224eff5e-2488-4b18-b3e7-f50d94421648', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + roleId: { type: 'string', format: 'misskey:id' }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + }, + required: ['roleId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + @Inject(DI.roleAssignmentsRepository) + private roleAssignmentsRepository: RoleAssignmentsRepository, + + private queryService: QueryService, + private userEntityService: UserEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const role = await this.rolesRepository.findOneBy({ + id: ps.roleId, + }); + + if (role == null) { + throw new ApiError(meta.errors.noSuchRole); + } + + const query = this.queryService.makePaginationQuery(this.roleAssignmentsRepository.createQueryBuilder('assign'), ps.sinceId, ps.untilId) + .andWhere('assign.roleId = :roleId', { roleId: role.id }) + .innerJoinAndSelect('assign.user', 'user'); + + const assigns = await query + .take(ps.limit) + .getMany(); + + return await Promise.all(assigns.map(async assign => ({ + id: assign.id, + user: await this.userEntityService.pack(assign.user!, me, { detail: true }), + }))); + }); + } +} 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 823af6d8be..9d19efbbcf 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -59,12 +59,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw new Error('cannot show info of admin'); } - if (!await this.roleService.isAdministrator(_me)) { - return { - isSuspended: user.isSuspended, - }; - } - const signins = await this.signinsRepository.findBy({ userId: user.id }); const roles = await this.roleService.getUserRoles(user.id); @@ -89,7 +83,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { moderationNote: profile.moderationNote, signins, policies: await this.roleService.getUserPolicies(user.id), - roles: await this.roleEntityService.packMany(roles, me, { detail: false }), + roles: await this.roleEntityService.packMany(roles, me), }; }); } 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 ec9ac1ef90..6c31075e05 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts @@ -1,7 +1,10 @@ -import * as speakeasy from 'speakeasy'; +import * as OTPAuth from 'otpauth'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { UserProfilesRepository } from '@/models/index.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -22,8 +25,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( + @Inject(DI.config) + private config: Config, + @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + + private userEntityService: UserEntityService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { const token = ps.token.replace(/\s/g, ''); @@ -34,13 +43,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw new Error('二段階èªè¨¼ã®è¨å®šãŒé–‹å§‹ã•れã¦ã„ã¾ã›ã‚“'); } - const verified = (speakeasy as any).totp.verify({ - secret: profile.twoFactorTempSecret, - encoding: 'base32', - token: token, + const delta = OTPAuth.TOTP.validate({ + secret: OTPAuth.Secret.fromBase32(profile.twoFactorTempSecret), + digits: 6, + token, + window: 1, }); - if (!verified) { + if (delta === null) { throw new Error('not verified'); } @@ -48,6 +58,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { twoFactorSecret: profile.twoFactorTempSecret, twoFactorEnabled: true, }); + + // Publish meUpdated event + this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { + detail: true, + 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 6e0849f2b2..ad33398da6 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 @@ -25,7 +25,7 @@ export const paramDef = { attestationObject: { type: 'string' }, password: { type: 'string' }, challengeId: { type: 'string' }, - name: { type: 'string' }, + name: { type: 'string', minLength: 1, maxLength: 30 }, }, required: ['clientDataJSON', 'attestationObject', 'password', 'challengeId', 'name'], } as const; 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 0655a86350..0ee9f556a8 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,12 +1,23 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UserProfilesRepository } from '@/models/index.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/index.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../../error.js'; export const meta = { requireCredential: true, secure: true, + + errors: { + noKey: { + message: 'No security key.', + code: 'NO_SECURITY_KEY', + id: 'f9c54d7f-d4c2-4d3c-9a8g-a70daac86512', + }, + }, } as const; export const paramDef = { @@ -23,11 +34,45 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + + @Inject(DI.userSecurityKeysRepository) + private userSecurityKeysRepository: UserSecurityKeysRepository, + + private userEntityService: UserEntityService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { + if (ps.value === true) { + // ã‚»ã‚ュリティã‚ーãŒãªã‘れã°ãƒ‘スワードレスを有効ã«ã¯ã§ããªã„ + const keyCount = await this.userSecurityKeysRepository.count({ + where: { + userId: me.id, + }, + select: { + id: true, + name: true, + lastUsed: true, + }, + }); + + if (keyCount === 0) { + await this.userProfilesRepository.update(me.id, { + usePasswordLessLogin: false, + }); + + throw new ApiError(meta.errors.noKey); + } + } + await this.userProfilesRepository.update(me.id, { usePasswordLessLogin: ps.value, }); + + // Publish meUpdated event + this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { + detail: true, + includeSecrets: true, + })); }); } } 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 a539c5c221..eb4d7f9c14 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 @@ import bcrypt from 'bcryptjs'; -import * as speakeasy from 'speakeasy'; +import * as OTPAuth from 'otpauth'; import * as QRCode from 'qrcode'; import { Inject, Injectable } from '@nestjs/common'; import type { UserProfilesRepository } from '@/models/index.js'; @@ -42,25 +42,24 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } // Generate user's secret key - const secret = speakeasy.generateSecret({ - length: 32, - }); + const secret = new OTPAuth.Secret(); await this.userProfilesRepository.update(me.id, { twoFactorTempSecret: secret.base32, }); // Get the data URL of the authenticator URL - const url = speakeasy.otpauthURL({ - secret: secret.base32, - encoding: 'base32', + const totp = new OTPAuth.TOTP({ + secret, + digits: 6, label: me.username, issuer: this.config.host, }); - const dataUrl = await QRCode.toDataURL(url); + const url = totp.toString(); + const qr = await QRCode.toDataURL(url); return { - qr: dataUrl, + qr, url, secret: secret.base32, label: me.username, 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 0f2b0fd7ee..4b726aed80 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 @@ -50,6 +50,24 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { id: ps.credentialId, }); + // 使ã‚れã¦ã„ã‚‹ã‚ーãŒãªããªã£ãŸã‚‰ãƒ‘スワードレスãƒã‚°ã‚¤ãƒ³ã‚’ã‚„ã‚ã‚‹ + const keyCount = await this.userSecurityKeysRepository.count({ + where: { + userId: me.id, + }, + select: { + id: true, + name: true, + lastUsed: true, + }, + }); + + if (keyCount === 0) { + await this.userProfilesRepository.update(me.id, { + usePasswordLessLogin: false, + }); + } + // Publish meUpdated event this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { detail: 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 4c5b151f78..e0e7ba6658 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -1,7 +1,9 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { UserProfilesRepository } from '@/models/index.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -24,6 +26,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + + private userEntityService: UserEntityService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); @@ -38,7 +43,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { await this.userProfilesRepository.update(me.id, { twoFactorSecret: null, twoFactorEnabled: false, + usePasswordLessLogin: false, }); + + // Publish meUpdated event + this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { + detail: true, + 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 new file mode 100644 index 0000000000..d98f60fa5f --- /dev/null +++ b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts @@ -0,0 +1,78 @@ +import bcrypt from 'bcryptjs'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/index.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../../error.js'; + +export const meta = { + requireCredential: true, + + secure: true, + + errors: { + noSuchKey: { + message: 'No such key.', + code: 'NO_SUCH_KEY', + id: 'f9c5467f-d492-4d3c-9a8g-a70dacc86512', + }, + + accessDenied: { + message: 'You do not have edit privilege of the channel.', + code: 'ACCESS_DENIED', + id: '1fb7cb09-d46a-4fff-b8df-057708cce513', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + name: { type: 'string', minLength: 1, maxLength: 30 }, + credentialId: { type: 'string' }, + }, + required: ['name', 'credentialId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.userSecurityKeysRepository) + private userSecurityKeysRepository: UserSecurityKeysRepository, + + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, + + private userEntityService: UserEntityService, + private globalEventService: GlobalEventService, + ) { + super(meta, paramDef, async (ps, me) => { + const key = await this.userSecurityKeysRepository.findOneBy({ + id: ps.credentialId, + }); + + if (key == null) { + throw new ApiError(meta.errors.noSuchKey); + } + + if (key.userId !== me.id) { + throw new ApiError(meta.errors.accessDenied); + } + + await this.userSecurityKeysRepository.update(key.id, { + name: ps.name, + }); + + // Publish meUpdated event + this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { + detail: true, + includeSecrets: true, + })); + + return {}; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 706e0d2089..e3897d38bd 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -1,7 +1,7 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { UsersRepository, FollowingsRepository, MutingsRepository, UserProfilesRepository, NotificationsRepository } from '@/models/index.js'; -import { notificationTypes } from '@/types.js'; +import { obsoleteNotificationTypes, notificationTypes } from '@/types.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteReadService } from '@/core/NoteReadService.js'; @@ -41,11 +41,12 @@ export const paramDef = { following: { type: 'boolean', default: false }, unreadOnly: { type: 'boolean', default: false }, markAsRead: { type: 'boolean', default: true }, + // 後方互æ›ã®ãŸã‚ã€å»ƒæ¢ã•れãŸé€šçŸ¥ã‚¿ã‚¤ãƒ—ã‚‚å—ã‘付ã‘ã‚‹ includeTypes: { type: 'array', items: { - type: 'string', enum: notificationTypes, + type: 'string', enum: [...notificationTypes, ...obsoleteNotificationTypes], } }, excludeTypes: { type: 'array', items: { - type: 'string', enum: notificationTypes, + type: 'string', enum: [...notificationTypes, ...obsoleteNotificationTypes], } }, }, required: [], @@ -84,6 +85,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { if (notificationTypes.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 followingQuery = this.followingsRepository.createQueryBuilder('following') .select('following.followeeId') .where('following.followerId = :followerId', { followerId: me.id }); @@ -143,10 +148,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { query.setParameters(followingQuery.getParameters()); } - if (ps.includeTypes && ps.includeTypes.length > 0) { - query.andWhere('notification.type IN (:...includeTypes)', { includeTypes: ps.includeTypes }); - } else if (ps.excludeTypes && ps.excludeTypes.length > 0) { - query.andWhere('notification.type NOT IN (:...excludeTypes)', { excludeTypes: ps.excludeTypes }); + if (includeTypes && includeTypes.length > 0) { + query.andWhere('notification.type IN (:...includeTypes)', { includeTypes }); + } else if (excludeTypes && excludeTypes.length > 0) { + query.andWhere('notification.type NOT IN (:...excludeTypes)', { excludeTypes }); } if (ps.unreadOnly) { diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 593444968e..f4c5a84a4f 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -79,6 +79,12 @@ export const meta = { code: 'YOU_HAVE_BEEN_BLOCKED', id: 'b390d7e1-8a5e-46ed-b625-06271cafd3d3', }, + + noSuchFile: { + message: 'Some files are not found.', + code: 'NO_SUCH_FILE', + id: 'b6992544-63e7-67f0-fa7f-32444b1b5306', + }, }, } as const; @@ -207,6 +213,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { .orderBy('array_position(ARRAY[:...fileIds], "id"::text)') .setParameters({ fileIds }) .getMany(); + + if (files.length !== fileIds.length) { + throw new ApiError(meta.errors.noSuchFile); + } } let renote: Note | null = null; diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 8eff8fdb22..cf939f6631 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -28,6 +28,7 @@ export const paramDef = { properties: { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, offset: { type: 'integer', default: 0 }, + channelId: { type: 'string', nullable: true, format: 'misskey:id' }, }, required: [], } as const; @@ -63,12 +64,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); + if (ps.channelId) query.andWhere('note.channelId = :channelId', { channelId: ps.channelId }); + if (me) this.queryService.generateMutedUserQuery(query, me); if (me) this.queryService.generateBlockedUserQuery(query, me); let notes = await query .orderBy('note.score', 'DESC') - .take(ps.limit) + .take(50) .getMany(); notes.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); diff --git a/packages/backend/src/server/api/endpoints/roles/list.ts b/packages/backend/src/server/api/endpoints/roles/list.ts new file mode 100644 index 0000000000..d61c6b8dc6 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/roles/list.ts @@ -0,0 +1,37 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { RolesRepository } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; +import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; + +export const meta = { + tags: ['role'], + + requireCredential: true, +} as const; + +export const paramDef = { + type: 'object', + properties: { + }, + required: [ + ], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + private roleEntityService: RoleEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const roles = await this.rolesRepository.findBy({ + isPublic: true, + }); + return await this.roleEntityService.packMany(roles, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/roles/show.ts b/packages/backend/src/server/api/endpoints/roles/show.ts new file mode 100644 index 0000000000..cc755dcc76 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/roles/show.ts @@ -0,0 +1,52 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { RolesRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['role', 'users'], + + requireCredential: false, + + errors: { + noSuchRole: { + message: 'No such role.', + code: 'NO_SUCH_ROLE', + id: 'de5502bf-009a-4639-86c1-fec349e46dcb', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + roleId: { type: 'string', format: 'misskey:id' }, + }, + required: ['roleId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + private roleEntityService: RoleEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const role = await this.rolesRepository.findOneBy({ + id: ps.roleId, + isPublic: true, + }); + + if (role == null) { + throw new ApiError(meta.errors.noSuchRole); + } + + return await this.roleEntityService.pack(role, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/roles/users.ts b/packages/backend/src/server/api/endpoints/roles/users.ts new file mode 100644 index 0000000000..6e221b6c67 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/roles/users.ts @@ -0,0 +1,71 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { RoleAssignmentsRepository, RolesRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/core/QueryService.js'; +import { DI } from '@/di-symbols.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['role', 'users'], + + requireCredential: false, + + errors: { + noSuchRole: { + message: 'No such role.', + code: 'NO_SUCH_ROLE', + id: '30aaaee3-4792-48dc-ab0d-cf501a575ac5', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + roleId: { type: 'string', format: 'misskey:id' }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + }, + required: ['roleId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + @Inject(DI.roleAssignmentsRepository) + private roleAssignmentsRepository: RoleAssignmentsRepository, + + private queryService: QueryService, + private userEntityService: UserEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const role = await this.rolesRepository.findOneBy({ + id: ps.roleId, + isPublic: true, + }); + + if (role == null) { + throw new ApiError(meta.errors.noSuchRole); + } + + const query = this.queryService.makePaginationQuery(this.roleAssignmentsRepository.createQueryBuilder('assign'), ps.sinceId, ps.untilId) + .andWhere('assign.roleId = :roleId', { roleId: role.id }) + .innerJoinAndSelect('assign.user', 'user'); + + const assigns = await query + .take(ps.limit) + .getMany(); + + return await Promise.all(assigns.map(async assign => ({ + id: assign.id, + user: await this.userEntityService.pack(assign.user!, me, { detail: true }), + }))); + }); + } +} 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 8b22f913d2..1cefcf2707 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,6 +1,7 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { UsersRepository, FollowingsRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type { User } from '@/models/entities/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -36,13 +37,13 @@ export const paramDef = { properties: { username: { type: 'string', nullable: true }, }, - required: ['username'] + required: ['username'], }, { properties: { host: { type: 'string', nullable: true }, }, - required: ['host'] + required: ['host'], }, ], } as const; @@ -53,6 +54,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( + @Inject(DI.config) + private config: Config, + @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -62,79 +66,76 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, me) => { - const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30æ—¥ - - if (ps.host) { - const q = this.usersRepository.createQueryBuilder('user') - .where('user.isSuspended = FALSE') - .andWhere('user.host LIKE :host', { host: sqlLikeEscape(ps.host.toLowerCase()) + '%' }); - + const setUsernameAndHostQuery = (query = this.usersRepository.createQueryBuilder('user')) => { if (ps.username) { - q.andWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.username.toLowerCase()) + '%' }); + query.andWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.username.toLowerCase()) + '%' }); } - q.andWhere('user.updatedAt IS NOT NULL'); - q.orderBy('user.updatedAt', 'DESC'); + if (ps.host) { + if (ps.host === this.config.hostname || ps.host === '.') { + query.andWhere('user.host IS NULL'); + } else { + query.andWhere('user.host LIKE :host', { + host: sqlLikeEscape(ps.host.toLowerCase()) + '%', + }); + } + } - const users = await q.take(ps.limit).getMany(); + return query; + }; - return await this.userEntityService.packMany(users, me, { detail: ps.detail }); - } else if (ps.username) { - let users: User[] = []; + const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30æ—¥ - if (me) { - const followingQuery = this.followingsRepository.createQueryBuilder('following') - .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: me.id }); + let users: User[] = []; - const query = this.usersRepository.createQueryBuilder('user') - .where(`user.id IN (${ followingQuery.getQuery() })`) - .andWhere('user.id != :meId', { meId: me.id }) - .andWhere('user.isSuspended = FALSE') - .andWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.username.toLowerCase()) + '%' }) - .andWhere(new Brackets(qb => { qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); - })); + if (me) { + const followingQuery = this.followingsRepository.createQueryBuilder('following') + .select('following.followeeId') + .where('following.followerId = :followerId', { followerId: me.id }); - query.setParameters(followingQuery.getParameters()); + const query = setUsernameAndHostQuery() + .andWhere(`user.id IN (${ followingQuery.getQuery() })`) + .andWhere('user.id != :meId', { meId: me.id }) + .andWhere('user.isSuspended = FALSE') + .andWhere(new Brackets(qb => { qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + })); - users = await query - .orderBy('user.usernameLower', 'ASC') - .take(ps.limit) - .getMany(); + query.setParameters(followingQuery.getParameters()); - if (users.length < ps.limit) { - const otherQuery = await this.usersRepository.createQueryBuilder('user') - .where(`user.id NOT IN (${ followingQuery.getQuery() })`) - .andWhere('user.id != :meId', { meId: me.id }) - .andWhere('user.isSuspended = FALSE') - .andWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.username.toLowerCase()) + '%' }) - .andWhere('user.updatedAt IS NOT NULL'); + users = await query + .orderBy('user.usernameLower', 'ASC') + .take(ps.limit) + .getMany(); - otherQuery.setParameters(followingQuery.getParameters()); + if (users.length < ps.limit) { + const otherQuery = setUsernameAndHostQuery() + .andWhere(`user.id NOT IN (${ followingQuery.getQuery() })`) + .andWhere('user.isSuspended = FALSE') + .andWhere('user.updatedAt IS NOT NULL'); - const otherUsers = await otherQuery - .orderBy('user.updatedAt', 'DESC') - .take(ps.limit - users.length) - .getMany(); + otherQuery.setParameters(followingQuery.getParameters()); - users = users.concat(otherUsers); - } - } else { - users = await this.usersRepository.createQueryBuilder('user') - .where('user.isSuspended = FALSE') - .andWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.username.toLowerCase()) + '%' }) - .andWhere('user.updatedAt IS NOT NULL') + const otherUsers = await otherQuery .orderBy('user.updatedAt', 'DESC') .take(ps.limit - users.length) .getMany(); + + users = users.concat(otherUsers); } + } else { + const query = setUsernameAndHostQuery() + .andWhere('user.isSuspended = FALSE') + .andWhere('user.updatedAt IS NOT NULL'); - return await this.userEntityService.packMany(users, me, { detail: !!ps.detail }); + users = await query + .orderBy('user.updatedAt', 'DESC') + .take(ps.limit - users.length) + .getMany(); } - return []; + return await this.userEntityService.packMany(users, me, { detail: !!ps.detail }); }); } } diff --git a/packages/backend/src/server/web/manifest.json b/packages/backend/src/server/web/manifest.json index 48030a2980..41171d62a1 100644 --- a/packages/backend/src/server/web/manifest.json +++ b/packages/backend/src/server/web/manifest.json @@ -9,16 +9,26 @@ { "src": "/static-assets/icons/192.png", "sizes": "192x192", - "type": "image/png" + "type": "image/png", + "purpose": "maskable" }, { "src": "/static-assets/icons/512.png", "sizes": "512x512", - "type": "image/png" + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/static-assets/splash.png", + "sizes": "300x300", + "type": "image/png", + "purpose": "any" } ], "share_target": { "action": "/share/", + "method": "GET", + "enctype": "application/x-www-form-urlencoded", "params": { "title": "title", "text": "text", diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 8ac65a021c..7c6a1e5199 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -1,4 +1,5 @@ -export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] as const; +export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] as const; +export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; diff --git a/packages/backend/test/tests/mfm.ts b/packages/backend/test/tests/mfm.ts deleted file mode 100644 index 884f39d7fb..0000000000 --- a/packages/backend/test/tests/mfm.ts +++ /dev/null @@ -1,89 +0,0 @@ -import * as assert from 'assert'; -import * as mfm from 'mfm-js'; - -import { toHtml } from '../../src/mfm/to-html.js'; -import { fromHtml } from '../../src/mfm/from-html.js'; - -describe('toHtml', () => { - test('br', () => { - const input = 'foo\nbar\nbaz'; - const output = '<p><span>foo<br>bar<br>baz</span></p>'; - assert.equal(toHtml(mfm.parse(input)), output); - }); - - test('br alt', () => { - const input = 'foo\r\nbar\rbaz'; - const output = '<p><span>foo<br>bar<br>baz</span></p>'; - assert.equal(toHtml(mfm.parse(input)), output); - }); -}); - -describe('fromHtml', () => { - test('p', () => { - assert.deepStrictEqual(fromHtml('<p>a</p><p>b</p>'), 'a\n\nb'); - }); - - test('block element', () => { - assert.deepStrictEqual(fromHtml('<div>a</div><div>b</div>'), 'a\nb'); - }); - - test('inline element', () => { - assert.deepStrictEqual(fromHtml('<ul><li>a</li><li>b</li></ul>'), 'a\nb'); - }); - - test('block code', () => { - assert.deepStrictEqual(fromHtml('<pre><code>a\nb</code></pre>'), '```\na\nb\n```'); - }); - - test('inline code', () => { - assert.deepStrictEqual(fromHtml('<code>a</code>'), '`a`'); - }); - - test('quote', () => { - assert.deepStrictEqual(fromHtml('<blockquote>a\nb</blockquote>'), '> a\n> b'); - }); - - test('br', () => { - assert.deepStrictEqual(fromHtml('<p>abc<br><br/>d</p>'), 'abc\n\nd'); - }); - - test('link with different text', () => { - assert.deepStrictEqual(fromHtml('<p>a <a href="https://example.com/b">c</a> d</p>'), 'a [c](https://example.com/b) d'); - }); - - test('link with different text, but not encoded', () => { - assert.deepStrictEqual(fromHtml('<p>a <a href="https://example.com/ä">c</a> d</p>'), 'a [c](<https://example.com/ä>) d'); - }); - - test('link with same text', () => { - assert.deepStrictEqual(fromHtml('<p>a <a href="https://example.com/b">https://example.com/b</a> d</p>'), 'a https://example.com/b d'); - }); - - test('link with same text, but not encoded', () => { - assert.deepStrictEqual(fromHtml('<p>a <a href="https://example.com/ä">https://example.com/ä</a> d</p>'), 'a <https://example.com/ä> d'); - }); - - test('link with no url', () => { - assert.deepStrictEqual(fromHtml('<p>a <a href="b">c</a> d</p>'), 'a [c](b) d'); - }); - - test('link without href', () => { - assert.deepStrictEqual(fromHtml('<p>a <a>c</a> d</p>'), 'a c d'); - }); - - test('link without text', () => { - assert.deepStrictEqual(fromHtml('<p>a <a href="https://example.com/b"></a> d</p>'), 'a https://example.com/b d'); - }); - - test('link without both', () => { - assert.deepStrictEqual(fromHtml('<p>a <a></a> d</p>'), 'a d'); - }); - - test('mention', () => { - assert.deepStrictEqual(fromHtml('<p>a <a href="https://example.com/@user" class="u-url mention">@user</a> d</p>'), 'a @user@example.com d'); - }); - - test('hashtag', () => { - assert.deepStrictEqual(fromHtml('<p>a <a href="https://example.com/tags/a">#a</a> d</p>', ['#a']), 'a #a d'); - }); -}); diff --git a/packages/backend/test/tests/reaction-lib.ts b/packages/backend/test/tests/reaction-lib.ts deleted file mode 100644 index 2e767f7697..0000000000 --- a/packages/backend/test/tests/reaction-lib.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* -import * as assert from 'assert'; - -import { toDbReaction } from '../src/misc/reaction-lib.js'; - -describe('toDbReaction', async () => { - test('æ—¢å˜ã®æ–‡å—列リアクションã¯ãã®ã¾ã¾', async () => { - assert.strictEqual(await toDbReaction('like'), 'like'); - }); - - test('Unicodeプリンã¯å¯¿å¸åŒ–ä¸èƒ½ã¨ã™ã‚‹ãŸã‚æ–‡å—列化ã—ãªã„', async () => { - assert.strictEqual(await toDbReaction('ðŸ®'), 'ðŸ®'); - }); - - test('ãƒ—ãƒªãƒ³ä»¥å¤–ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ like', async () => { - assert.strictEqual(await toDbReaction('ðŸ‘'), 'like'); - }); - - test('ãƒ—ãƒªãƒ³ä»¥å¤–ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ love', async () => { - assert.strictEqual(await toDbReaction('â¤ï¸'), 'love'); - }); - - test('ãƒ—ãƒªãƒ³ä»¥å¤–ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ love 異体å—セレクタãªã—', async () => { - assert.strictEqual(await toDbReaction('â¤'), 'love'); - }); - - test('ãƒ—ãƒªãƒ³ä»¥å¤–ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ laugh', async () => { - assert.strictEqual(await toDbReaction('😆'), 'laugh'); - }); - - test('ãƒ—ãƒªãƒ³ä»¥å¤–ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ hmm', async () => { - assert.strictEqual(await toDbReaction('🤔'), 'hmm'); - }); - - test('ãƒ—ãƒªãƒ³ä»¥å¤–ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ surprise', async () => { - assert.strictEqual(await toDbReaction('😮'), 'surprise'); - }); - - test('ãƒ—ãƒªãƒ³ä»¥å¤–ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ congrats', async () => { - assert.strictEqual(await toDbReaction('🎉'), 'congrats'); - }); - - test('ãƒ—ãƒªãƒ³ä»¥å¤–ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ angry', async () => { - assert.strictEqual(await toDbReaction('💢'), 'angry'); - }); - - test('ãƒ—ãƒªãƒ³ä»¥å¤–ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ confused', async () => { - assert.strictEqual(await toDbReaction('😥'), 'confused'); - }); - - test('ãƒ—ãƒªãƒ³ä»¥å¤–ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ rip', async () => { - assert.strictEqual(await toDbReaction('😇'), 'rip'); - }); - - test('ãれ以外ã¯Unicodeã®ã¾ã¾', async () => { - assert.strictEqual(await toDbReaction('ðŸ…'), 'ðŸ…'); - }); - - test('異体å—セレクタ除去', async () => { - assert.strictEqual(await toDbReaction('㊗ï¸'), '㊗'); - }); - - test('異体å—セレクタ除去 å¿…è¦ãªã—', async () => { - assert.strictEqual(await toDbReaction('㊗'), '㊗'); - }); - - test('fallback - undefined', async () => { - assert.strictEqual(await toDbReaction(undefined), 'like'); - }); - - test('fallback - null', async () => { - assert.strictEqual(await toDbReaction(null), 'like'); - }); - - test('fallback - empty', async () => { - assert.strictEqual(await toDbReaction(''), 'like'); - }); - - test('fallback - unknown', async () => { - assert.strictEqual(await toDbReaction('unknown'), 'like'); - }); -}); -*/ diff --git a/packages/backend/test/tsconfig.json b/packages/backend/test/tsconfig.json index 5d91d0923a..da82ddc4a1 100644 --- a/packages/backend/test/tsconfig.json +++ b/packages/backend/test/tsconfig.json @@ -37,6 +37,7 @@ }, "compileOnSave": false, "include": [ - "./**/*.ts" + "./**/*.ts", + "../src/@types/**/*.ts", ] } diff --git a/packages/backend/test/unit/MfmService.ts b/packages/backend/test/unit/MfmService.ts new file mode 100644 index 0000000000..5496738778 --- /dev/null +++ b/packages/backend/test/unit/MfmService.ts @@ -0,0 +1,102 @@ +import * as assert from 'assert'; +import * as mfm from 'mfm-js'; +import { Test } from '@nestjs/testing'; + +import { CoreModule } from '@/core/CoreModule.js'; +import { MfmService } from '@/core/MfmService.js'; +import { GlobalModule } from '@/GlobalModule.js'; + +describe('MfmService', () => { + let mfmService: MfmService; + + beforeAll(async () => { + const app = await Test.createTestingModule({ + imports: [GlobalModule, CoreModule], + }).compile(); + mfmService = app.get<MfmService>(MfmService); + }); + + describe('toHtml', () => { + test('br', () => { + const input = 'foo\nbar\nbaz'; + const output = '<p><span>foo<br>bar<br>baz</span></p>'; + assert.equal(mfmService.toHtml(mfm.parse(input)), output); + }); + + test('br alt', () => { + const input = 'foo\r\nbar\rbaz'; + const output = '<p><span>foo<br>bar<br>baz</span></p>'; + assert.equal(mfmService.toHtml(mfm.parse(input)), output); + }); + }); + + describe('fromHtml', () => { + test('p', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a</p><p>b</p>'), 'a\n\nb'); + }); + + test('block element', () => { + assert.deepStrictEqual(mfmService.fromHtml('<div>a</div><div>b</div>'), 'a\nb'); + }); + + test('inline element', () => { + assert.deepStrictEqual(mfmService.fromHtml('<ul><li>a</li><li>b</li></ul>'), 'a\nb'); + }); + + test('block code', () => { + assert.deepStrictEqual(mfmService.fromHtml('<pre><code>a\nb</code></pre>'), '```\na\nb\n```'); + }); + + test('inline code', () => { + assert.deepStrictEqual(mfmService.fromHtml('<code>a</code>'), '`a`'); + }); + + test('quote', () => { + assert.deepStrictEqual(mfmService.fromHtml('<blockquote>a\nb</blockquote>'), '> a\n> b'); + }); + + test('br', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>abc<br><br/>d</p>'), 'abc\n\nd'); + }); + + test('link with different text', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="https://example.com/b">c</a> d</p>'), 'a [c](https://example.com/b) d'); + }); + + test('link with different text, but not encoded', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="https://example.com/ä">c</a> d</p>'), 'a [c](<https://example.com/ä>) d'); + }); + + test('link with same text', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="https://example.com/b">https://example.com/b</a> d</p>'), 'a https://example.com/b d'); + }); + + test('link with same text, but not encoded', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="https://example.com/ä">https://example.com/ä</a> d</p>'), 'a <https://example.com/ä> d'); + }); + + test('link with no url', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="b">c</a> d</p>'), 'a [c](b) d'); + }); + + test('link without href', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a>c</a> d</p>'), 'a c d'); + }); + + test('link without text', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="https://example.com/b"></a> d</p>'), 'a https://example.com/b d'); + }); + + test('link without both', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a></a> d</p>'), 'a d'); + }); + + test('mention', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="https://example.com/@user" class="u-url mention">@user</a> d</p>'), 'a @user@example.com d'); + }); + + test('hashtag', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="https://example.com/tags/a">#a</a> d</p>', ['#a']), 'a #a d'); + }); + }); +}); diff --git a/packages/backend/test/unit/ReactionService.ts b/packages/backend/test/unit/ReactionService.ts new file mode 100644 index 0000000000..6a20a1e08e --- /dev/null +++ b/packages/backend/test/unit/ReactionService.ts @@ -0,0 +1,92 @@ +import * as assert from 'assert'; +import { Test } from '@nestjs/testing'; + +import { CoreModule } from '@/core/CoreModule.js'; +import { ReactionService } from '@/core/ReactionService.js'; +import { GlobalModule } from '@/GlobalModule.js'; + +describe('ReactionService', () => { + let reactionService: ReactionService; + + beforeAll(async () => { + const app = await Test.createTestingModule({ + imports: [GlobalModule, CoreModule], + }).compile(); + reactionService = app.get<ReactionService>(ReactionService); + }); + + describe('toDbReaction', () => { + test('絵文å—リアクションã¯ãã®ã¾ã¾', async () => { + assert.strictEqual(await reactionService.toDbReaction('ðŸ‘'), 'ðŸ‘'); + assert.strictEqual(await reactionService.toDbReaction('ðŸ…'), 'ðŸ…'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ pudding', async () => { + assert.strictEqual(await reactionService.toDbReaction('pudding'), 'ðŸ®'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ like', async () => { + assert.strictEqual(await reactionService.toDbReaction('like'), 'ðŸ‘'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ love', async () => { + assert.strictEqual(await reactionService.toDbReaction('love'), 'â¤'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ laugh', async () => { + assert.strictEqual(await reactionService.toDbReaction('laugh'), '😆'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ hmm', async () => { + assert.strictEqual(await reactionService.toDbReaction('hmm'), '🤔'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ surprise', async () => { + assert.strictEqual(await reactionService.toDbReaction('surprise'), '😮'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ congrats', async () => { + assert.strictEqual(await reactionService.toDbReaction('congrats'), '🎉'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ angry', async () => { + assert.strictEqual(await reactionService.toDbReaction('angry'), '💢'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ confused', async () => { + assert.strictEqual(await reactionService.toDbReaction('confused'), '😥'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ rip', async () => { + assert.strictEqual(await reactionService.toDbReaction('rip'), '😇'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ star', async () => { + assert.strictEqual(await reactionService.toDbReaction('star'), 'â'); + }); + + test('異体å—セレクタ除去', async () => { + assert.strictEqual(await reactionService.toDbReaction('㊗ï¸'), '㊗'); + }); + + test('異体å—セレクタ除去 å¿…è¦ãªã—', async () => { + assert.strictEqual(await reactionService.toDbReaction('㊗'), '㊗'); + }); + + test('fallback - undefined', async () => { + assert.strictEqual(await reactionService.toDbReaction(undefined), 'ðŸ‘'); + }); + + test('fallback - null', async () => { + assert.strictEqual(await reactionService.toDbReaction(null), 'ðŸ‘'); + }); + + test('fallback - empty', async () => { + assert.strictEqual(await reactionService.toDbReaction(''), 'ðŸ‘'); + }); + + test('fallback - unknown', async () => { + assert.strictEqual(await reactionService.toDbReaction('unknown'), 'ðŸ‘'); + }); + }); +}); diff --git a/packages/backend/test/tests/ap-request.ts b/packages/backend/test/unit/ap-request.ts index 8c586861ad..98f352e1c6 100644 --- a/packages/backend/test/tests/ap-request.ts +++ b/packages/backend/test/unit/ap-request.ts @@ -1,7 +1,8 @@ import * as assert from 'assert'; import httpSignature from '@peertube/http-signature'; -import { genRsaKeyPair } from '../../src/misc/gen-key-pair.js'; -import { createSignedPost, createSignedGet } from '../../src/activitypub/ap-request.js'; + +import { genRsaKeyPair } from '@/misc/gen-key-pair.js'; +import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js'; export const buildParsedSignature = (signingString: string, signature: string, algorithm: string) => { return { @@ -9,7 +10,7 @@ export const buildParsedSignature = (signingString: string, signature: string, a params: { keyId: 'KeyID', // dummy, not used for verify algorithm: algorithm, - headers: [ '(request-target)', 'date', 'host', 'digest' ], // dummy, not used for verify + headers: ['(request-target)', 'date', 'host', 'digest'], // dummy, not used for verify signature: signature, }, signingString: signingString, @@ -29,7 +30,7 @@ describe('ap-request', () => { 'User-Agent': 'UA', }; - const req = createSignedPost({ key, url, body, additionalHeaders: headers }); + const req = ApRequestCreator.createSignedPost({ key, url, body, additionalHeaders: headers }); const parsed = buildParsedSignature(req.signingString, req.signature, 'rsa-sha256'); @@ -45,7 +46,7 @@ describe('ap-request', () => { 'User-Agent': 'UA', }; - const req = createSignedGet({ key, url, additionalHeaders: headers }); + const req = ApRequestCreator.createSignedGet({ key, url, additionalHeaders: headers }); const parsed = buildParsedSignature(req.signingString, req.signature, 'rsa-sha256'); diff --git a/packages/backend/test/unit/chart.ts b/packages/backend/test/unit/chart.ts index 1e9a51bc88..5ac4cc18a2 100644 --- a/packages/backend/test/unit/chart.ts +++ b/packages/backend/test/unit/chart.ts @@ -19,7 +19,7 @@ import Logger from '@/logger.js'; describe('Chart', () => { const config = loadConfig(); const appLockService = { - getChartInsertLock: jest.fn().mockImplementation(() => Promise.resolve(() => {})), + getChartInsertLock: () => () => Promise.resolve(() => {}), } as unknown as jest.Mocked<AppLockService>; let db: DataSource | undefined; diff --git a/packages/backend/test/tests/extract-mentions.ts b/packages/backend/test/unit/extract-mentions.ts index e81d04c2db..66d32be1c5 100644 --- a/packages/backend/test/tests/extract-mentions.ts +++ b/packages/backend/test/unit/extract-mentions.ts @@ -1,11 +1,11 @@ import * as assert from 'assert'; import { parse } from 'mfm-js'; -import { extractMentions } from '../../src/misc/extract-mentions.js'; +import { extractMentions } from '@/misc/extract-mentions.js'; describe('Extract mentions', () => { test('simple', () => { - const ast = parse('@foo @bar @baz')!; + const ast = parse('@foo @bar @baz'); const mentions = extractMentions(ast); assert.deepStrictEqual(mentions, [{ username: 'foo', @@ -23,7 +23,7 @@ describe('Extract mentions', () => { }); test('nested', () => { - const ast = parse('@foo **@bar** @baz')!; + const ast = parse('@foo **@bar** @baz'); const mentions = extractMentions(ast); assert.deepStrictEqual(mentions, [{ username: 'foo', diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json index 544b529e94..6f335a2442 100644 --- a/packages/backend/tsconfig.json +++ b/packages/backend/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "allowJs": true, - "noEmitOnError": false, + "noEmitOnError": true, "noImplicitAny": true, "noImplicitReturns": true, "noUnusedParameters": false, diff --git a/packages/frontend/.eslintrc.js b/packages/frontend/.eslintrc.js index 6c3bfb5a6e..e8e0e57d2a 100644 --- a/packages/frontend/.eslintrc.js +++ b/packages/frontend/.eslintrc.js @@ -55,6 +55,7 @@ module.exports = { 'vue/multi-word-component-names': 'warn', 'vue/require-v-for-key': 'warn', 'vue/no-unused-components': 'warn', + 'vue/no-unused-vars': 'warn', 'vue/valid-v-for': 'warn', 'vue/return-in-computed-property': 'warn', 'vue/no-setup-props-destructure': 'warn', diff --git a/packages/frontend/assets/about-icon.png b/packages/frontend/assets/about-icon.png Binary files differindex afc1f0c728..15fd1e3731 100644 --- a/packages/frontend/assets/about-icon.png +++ b/packages/frontend/assets/about-icon.png diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 61c0135ba3..24f8d9b6a6 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -19,11 +19,11 @@ "@vue/compiler-sfc": "3.2.47", "autobind-decorator": "2.4.0", "autosize": "5.0.2", - "blurhash": "2.0.4", + "blurhash": "2.0.5", "broadcast-channel": "4.20.2", - "browser-image-resizer": "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.3", + "browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3", "canvas-confetti": "1.6.0", - "chart.js": "4.2.0", + "chart.js": "4.2.1", "chartjs-adapter-date-fns": "3.0.0", "chartjs-chart-matrix": "2.0.1", "chartjs-plugin-gradient": "0.6.1", @@ -38,7 +38,7 @@ "insert-text-at-cursor": "0.3.0", "is-file-animated": "1.0.2", "json5": "2.2.3", - "matter-js": "0.18.0", + "matter-js": "0.19.0", "mfm-js": "0.23.3", "misskey-js": "0.0.15", "photoswipe": "5.3.5", @@ -46,13 +46,12 @@ "punycode": "2.3.0", "querystring": "0.2.1", "rndstr": "1.0.0", - "rollup": "3.14.0", + "rollup": "3.17.2", "s-age": "1.1.2", - "sanitize-html": "2.9.0", - "sass": "1.58.0", + "sanitize-html": "2.10.0", + "sass": "1.58.3", "seedrandom": "3.0.5", "strict-event-emitter-types": "2.0.0", - "stringz": "2.1.0", "syuilo-password-strength": "0.0.1", "textarea-caret": "3.1.0", "three": "0.149.0", @@ -64,9 +63,9 @@ "typescript": "4.9.5", "uuid": "9.0.0", "vanilla-tilt": "1.8.0", - "vue-plyr": "7.0.0", - "vite": "4.1.1", + "vite": "4.1.2", "vue": "3.2.47", + "vue-plyr": "7.0.0", "vue-prism-editor": "2.0.0-alpha.2", "vuedraggable": "next" }, @@ -76,7 +75,7 @@ "@types/gulp": "4.0.10", "@types/gulp-rename": "2.0.1", "@types/matter-js": "0.18.2", - "@types/node": "18.13.0", + "@types/node": "18.14.0", "@types/punycode": "2.1.0", "@types/sanitize-html": "2.8.0", "@types/seedrandom": "3.0.4", @@ -85,16 +84,16 @@ "@types/uuid": "9.0.0", "@types/websocket": "1.0.5", "@types/ws": "8.5.4", - "@typescript-eslint/eslint-plugin": "5.51.0", - "@typescript-eslint/parser": "5.51.0", + "@typescript-eslint/eslint-plugin": "5.52.0", + "@typescript-eslint/parser": "5.52.0", "@vue/runtime-core": "3.2.47", "cross-env": "7.0.3", - "cypress": "12.5.1", - "eslint": "8.33.0", + "cypress": "12.6.0", + "eslint": "8.34.0", "eslint-plugin-import": "2.27.5", "eslint-plugin-vue": "9.9.0", - "start-server-and-test": "1.15.3", + "start-server-and-test": "1.15.4", "vue-eslint-parser": "9.1.0", - "vue-tsc": "1.0.24" + "vue-tsc": "1.1.4" } -} +}
\ No newline at end of file diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue index a76a1e0f54..9f2bf99338 100644 --- a/packages/frontend/src/components/MkAbuseReportWindow.vue +++ b/packages/frontend/src/components/MkAbuseReportWindow.vue @@ -43,7 +43,7 @@ const emit = defineEmits<{ }>(); const uiWindow = shallowRef<InstanceType<typeof MkWindow>>(); -const comment = ref(props.initialComment || ''); +const comment = ref(props.initialComment ?? ''); function send() { os.apiWithDialog('users/report-abuse', { diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 7e5432434f..663c57623d 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -209,7 +209,7 @@ function exec() { } } else if (props.type === 'hashtag') { if (!props.q || props.q === '') { - hashtags.value = JSON.parse(miLocalStorage.getItem('hashtags') || '[]'); + hashtags.value = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'); fetching.value = false; } else { const cacheKey = `autocomplete:hashtag:${props.q}`; diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue index 8db2e54e88..c72cc2ab1b 100644 --- a/packages/frontend/src/components/MkCaptcha.vue +++ b/packages/frontend/src/components/MkCaptcha.vue @@ -69,7 +69,7 @@ const captcha = computed<Captcha>(() => window[variable.value] || {} as unknown if (loaded) { available.value = true; } else { - (document.getElementById(scriptId.value) || document.head.appendChild(Object.assign(document.createElement('script'), { + (document.getElementById(scriptId.value) ?? document.head.appendChild(Object.assign(document.createElement('script'), { async: true, id: scriptId.value, src: src.value, diff --git a/packages/frontend/src/components/MkClickerGame.vue b/packages/frontend/src/components/MkClickerGame.vue index 2283e652db..da6439fd2c 100644 --- a/packages/frontend/src/components/MkClickerGame.vue +++ b/packages/frontend/src/components/MkClickerGame.vue @@ -22,9 +22,6 @@ import * as game from '@/scripts/clicker-game'; import number from '@/filters/number'; import { claimAchievement } from '@/scripts/achievements'; -defineProps<{ -}>(); - const saveData = game.saveData; const cookies = computed(() => saveData.value?.cookies); let cps = $ref(0); diff --git a/packages/frontend/src/components/MkContextMenu.vue b/packages/frontend/src/components/MkContextMenu.vue index f0ea984c4e..21cccaabde 100644 --- a/packages/frontend/src/components/MkContextMenu.vue +++ b/packages/frontend/src/components/MkContextMenu.vue @@ -32,6 +32,8 @@ let rootEl = $shallowRef<HTMLDivElement>(); let zIndex = $ref<number>(os.claimZIndex('high')); +const SCROLLBAR_THICKNESS = 16; + onMounted(() => { let left = props.ev.pageX + 1; // é–“é•ã£ã¦å³ãƒ€ãƒ–ルクリックã—ãŸå ´åˆã«æ„図ã›ãšã‚¢ã‚¤ãƒ†ãƒ ãŒã‚¯ãƒªãƒƒã‚¯ã•れるã®ã‚’防ããŸã‚ + 1 let top = props.ev.pageY + 1; // é–“é•ã£ã¦å³ãƒ€ãƒ–ルクリックã—ãŸå ´åˆã«æ„図ã›ãšã‚¢ã‚¤ãƒ†ãƒ ãŒã‚¯ãƒªãƒƒã‚¯ã•れるã®ã‚’防ããŸã‚ + 1 @@ -39,12 +41,12 @@ onMounted(() => { const width = rootEl.offsetWidth; const height = rootEl.offsetHeight; - if (left + width - window.pageXOffset > window.innerWidth) { - left = window.innerWidth - width + window.pageXOffset; + if (left + width - window.pageXOffset >= (window.innerWidth - SCROLLBAR_THICKNESS)) { + left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset; } - if (top + height - window.pageYOffset > window.innerHeight) { - top = window.innerHeight - height + window.pageYOffset; + if (top + height - window.pageYOffset >= (window.innerHeight - SCROLLBAR_THICKNESS)) { + top = (window.innerHeight - SCROLLBAR_THICKNESS) - height + window.pageYOffset; } if (top < 0) { diff --git a/packages/frontend/src/components/MkCwButton.vue b/packages/frontend/src/components/MkCwButton.vue index e0885f5550..7d5579040a 100644 --- a/packages/frontend/src/components/MkCwButton.vue +++ b/packages/frontend/src/components/MkCwButton.vue @@ -7,7 +7,6 @@ <script lang="ts" setup> import { computed } from 'vue'; -import { length } from 'stringz'; import * as misskey from 'misskey-js'; import { concat } from '@/scripts/array'; import { i18n } from '@/i18n'; @@ -23,7 +22,7 @@ const emit = defineEmits<{ const label = computed(() => { return concat([ - props.note.text ? [i18n.t('_cw.chars', { count: length(props.note.text) })] : [], + props.note.text ? [i18n.t('_cw.chars', { count: props.note.text.length })] : [], props.note.files && props.note.files.length !== 0 ? [i18n.t('_cw.files', { count: props.note.files.length })] : [], props.note.poll != null ? [i18n.ts.poll] : [], ] as string[][]).join(' / '); diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue index 9690353432..863ea702cd 100644 --- a/packages/frontend/src/components/MkDialog.vue +++ b/packages/frontend/src/components/MkDialog.vue @@ -14,8 +14,12 @@ </div> <header v-if="title" :class="$style.title"><Mfm :text="title"/></header> <div v-if="text" :class="$style.text"><Mfm :text="text"/></div> - <MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" @keydown="onInputKeydown"> + <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="ti ti-lock"></i></template> + <template #caption> + <span v-if="okButtonDisabled && disabledReason === 'charactersExceeded'" v-text="i18n.t('_dialog.charactersExceeded', { current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })" /> + <span v-else-if="okButtonDisabled && disabledReason === 'charactersBelow'" v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })" /> + </template> </MkInput> <MkSelect v-if="select" v-model="selectedValue" autofocus> <template v-if="select.items"> @@ -28,7 +32,7 @@ </template> </MkSelect> <div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons"> - <MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton> + <MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" :disabled="okButtonDisabled" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton> <MkButton v-if="showCancelButton || input || select" inline @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton> </div> <div v-if="actions" :class="$style.buttons"> @@ -47,9 +51,12 @@ import MkSelect from '@/components/MkSelect.vue'; import { i18n } from '@/i18n'; type Input = { - type: HTMLInputElement['type']; + type: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local'; placeholder?: string | null; - default: any | null; + autocomplete?: string; + default: string | number | null; + minLength?: number; + maxLength?: number; }; type Select = { @@ -98,8 +105,28 @@ const emit = defineEmits<{ const modal = shallowRef<InstanceType<typeof MkModal>>(); -const inputValue = ref(props.input?.default || null); -const selectedValue = ref(props.select?.default || null); +const inputValue = ref<string | number | null>(props.input?.default ?? null); +const selectedValue = ref(props.select?.default ?? null); + +let disabledReason = $ref<null | 'charactersExceeded' | 'charactersBelow'>(null); +const okButtonDisabled = $computed<boolean>(() => { + if (props.input) { + if (props.input.minLength) { + if ((inputValue.value || inputValue.value === '') && (inputValue.value as string).length < props.input.minLength) { + disabledReason = 'charactersBelow'; + return true; + } + } + if (props.input.maxLength) { + if (inputValue.value && (inputValue.value as string).length > props.input.maxLength) { + disabledReason = 'charactersExceeded'; + return true; + } + } + } + + return false; +}); function done(canceled: boolean, result?) { emit('done', { canceled, result }); diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue index a1d7210d7e..b97e36cd5f 100644 --- a/packages/frontend/src/components/MkFolder.vue +++ b/packages/frontend/src/components/MkFolder.vue @@ -1,13 +1,20 @@ <template> <div ref="rootEl" :class="[$style.root, { [$style.opened]: opened }]"> <div :class="$style.header" class="_button" @click="toggle"> - <span :class="$style.headerIcon"><slot name="icon"></slot></span> - <span :class="$style.headerText"><slot name="label"></slot></span> - <span :class="$style.headerRight"> + <div :class="$style.headerIcon"><slot name="icon"></slot></div> + <div :class="$style.headerText"> + <div :class="$style.headerTextMain"> + <slot name="label"></slot> + </div> + <div :class="$style.headerTextSub"> + <slot name="caption"></slot> + </div> + </div> + <div :class="$style.headerRight"> <span :class="$style.headerRightText"><slot name="suffix"></slot></span> <i v-if="opened" class="ti ti-chevron-up icon"></i> <i v-else class="ti ti-chevron-down icon"></i> - </span> + </div> </div> <div v-if="openedAtLeastOnce" :class="[$style.body, { [$style.bgSame]: bgSame }]" :style="{ maxHeight: maxHeight ? `${maxHeight}px` : null }"> <Transition @@ -139,6 +146,17 @@ onMounted(() => { } } +.headerUpper { + display: flex; + align-items: center; +} + +.headerLower { + color: var(--fgTransparentWeak); + font-size: .85em; + padding-left: 4px; +} + .headerIcon { margin-right: 0.75em; flex-shrink: 0; @@ -161,6 +179,15 @@ onMounted(() => { padding-right: 12px; } +.headerTextMain { + +} + +.headerTextSub { + color: var(--fgTransparentWeak); + font-size: .85em; +} + .headerRight { margin-left: auto; opacity: 0.7; diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue index da6177c2f9..3e3d7354c1 100644 --- a/packages/frontend/src/components/MkInput.vue +++ b/packages/frontend/src/components/MkInput.vue @@ -23,7 +23,7 @@ @input="onInput" > <datalist v-if="datalist" :id="id"> - <option v-for="data in datalist" :value="data"/> + <option v-for="data in datalist" :key="data" :value="data"/> </datalist> <div ref="suffixEl" class="suffix"><slot name="suffix"></slot></div> </div> @@ -41,7 +41,7 @@ import { useInterval } from '@/scripts/use-interval'; import { i18n } from '@/i18n'; const props = defineProps<{ - modelValue: string | number; + modelValue: string | number | null; type?: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local'; required?: boolean; readonly?: boolean; @@ -49,7 +49,7 @@ const props = defineProps<{ pattern?: string; placeholder?: string; autofocus?: boolean; - autocomplete?: boolean; + autocomplete?: string; spellcheck?: boolean; step?: any; datalist?: string[]; diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue index e957d8f56c..a12bb78e35 100644 --- a/packages/frontend/src/components/MkMediaList.vue +++ b/packages/frontend/src/components/MkMediaList.vue @@ -45,8 +45,8 @@ onMounted(() => { src: media.url, w: media.properties.width, h: media.properties.height, - alt: media.comment || media.name, - comment: media.comment || media.name, + alt: media.comment ?? media.name, + comment: media.comment ?? media.name, }; if (media.properties.orientation != null && media.properties.orientation >= 5) { [item.w, item.h] = [item.h, item.w]; @@ -90,8 +90,8 @@ onMounted(() => { [itemData.w, itemData.h] = [itemData.h, itemData.w]; } itemData.msrc = file.thumbnailUrl; - itemData.alt = file.comment || file.name; - itemData.comment = file.comment || file.name; + itemData.alt = file.comment ?? file.name; + itemData.comment = file.comment ?? file.name; itemData.thumbCropped = true; }); diff --git a/packages/frontend/src/components/MkMenu.child.vue b/packages/frontend/src/components/MkMenu.child.vue index cdd9d96b96..e0935efbe7 100644 --- a/packages/frontend/src/components/MkMenu.child.vue +++ b/packages/frontend/src/components/MkMenu.child.vue @@ -1,11 +1,11 @@ <template> -<div ref="el" class="sfhdhdhr"> - <MkMenu ref="menu" :items="items" :align="align" :width="width" :as-drawer="false" @close="onChildClosed"/> +<div ref="el" :class="$style.root"> + <MkMenu :items="items" :align="align" :width="width" :as-drawer="false" @close="onChildClosed"/> </div> </template> <script lang="ts" setup> -import { nextTick, onMounted, shallowRef, watch } from 'vue'; +import { nextTick, onMounted, onUnmounted, shallowRef, watch } from 'vue'; import MkMenu from './MkMenu.vue'; import { MenuItem } from '@/types/menu'; @@ -25,11 +25,21 @@ const emit = defineEmits<{ const el = shallowRef<HTMLElement>(); const align = 'left'; +const SCROLLBAR_THICKNESS = 16; + function setPosition() { const rootRect = props.rootElement.getBoundingClientRect(); - const rect = props.targetElement.getBoundingClientRect(); - const left = props.targetElement.offsetWidth; - const top = (rect.top - rootRect.top) - 8; + const parentRect = props.targetElement.getBoundingClientRect(); + const myRect = el.value.getBoundingClientRect(); + + let left = props.targetElement.offsetWidth; + let top = (parentRect.top - rootRect.top) - 8; + if (rootRect.left + left + myRect.width >= (window.innerWidth - SCROLLBAR_THICKNESS)) { + left = -myRect.width; + } + if (rootRect.top + top + myRect.height >= (window.innerHeight - SCROLLBAR_THICKNESS)) { + top = top - ((rootRect.top + top + myRect.height) - (window.innerHeight - SCROLLBAR_THICKNESS)); + } el.value.style.left = left + 'px'; el.value.style.top = top + 'px'; } @@ -46,13 +56,22 @@ watch(() => props.targetElement, () => { setPosition(); }); +const ro = new ResizeObserver((entries, observer) => { + setPosition(); +}); + onMounted(() => { + ro.observe(el.value); setPosition(); nextTick(() => { setPosition(); }); }); +onUnmounted(() => { + ro.disconnect(); +}); + defineExpose({ checkHit: (ev: MouseEvent) => { return (ev.target === el.value || el.value.contains(ev.target)); @@ -60,8 +79,8 @@ defineExpose({ }); </script> -<style lang="scss" scoped> -.sfhdhdhr { +<style lang="scss" module> +.root { position: absolute; } </style> diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index 52aba58455..09d530c4ea 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -56,7 +56,7 @@ </template> <script lang="ts" setup> -import { defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, watch } from 'vue'; +import { defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'; import { focusPrev, focusNext } from '@/scripts/focus'; import MkSwitch from '@/components/MkSwitch.vue'; import { MenuItem, InnerMenuItem, MenuPending, MenuAction } from '@/types/menu'; @@ -111,11 +111,11 @@ watch(() => props.items, () => { immediate: true, }); -let childMenu = $ref<MenuItem[] | null>(); +let childMenu = ref<MenuItem[] | null>(); let childTarget = $shallowRef<HTMLElement | null>(); function closeChild() { - childMenu = null; + childMenu.value = null; childShowingItem = null; } @@ -140,13 +140,31 @@ function onItemMouseLeave(item) { if (childCloseTimer) window.clearTimeout(childCloseTimer); } +let childrenCache = new WeakMap(); async function showChildren(item: MenuItem, ev: MouseEvent) { + const children = ref([]); + if (childrenCache.has(item)) { + children.value = childrenCache.get(item); + } else { + if (typeof item.children === 'function') { + children.value = [{ + type: 'pending', + }]; + item.children().then(x => { + children.value = x; + childrenCache.set(item, x); + }); + } else { + children.value = item.children; + } + } + if (props.asDrawer) { - os.popupMenu(item.children, ev.currentTarget ?? ev.target); + os.popupMenu(children, ev.currentTarget ?? ev.target); close(); } else { childTarget = ev.currentTarget ?? ev.target; - childMenu = item.children; + childMenu = children; childShowingItem = item; } } diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue index eba0f5847d..dbad02fb7e 100644 --- a/packages/frontend/src/components/MkModal.vue +++ b/packages/frontend/src/components/MkModal.vue @@ -125,7 +125,7 @@ function onBgClick() { } if (type === 'drawer') { - maxHeight = window.innerHeight / 1.5; + maxHeight = (window.innerHeight - SCROLLBAR_THICKNESS) / 1.5; } const keymap = { @@ -133,6 +133,7 @@ const keymap = { }; const MARGIN = 16; +const SCROLLBAR_THICKNESS = 16; const align = () => { if (props.src == null) return; @@ -170,15 +171,15 @@ const align = () => { if (fixed) { // ç”»é¢ã‹ã‚‰æ¨ªã«ã¯ã¿å‡ºã‚‹å ´åˆ - if (left + width > window.innerWidth) { - left = window.innerWidth - width; + if (left + width > (window.innerWidth - SCROLLBAR_THICKNESS)) { + left = (window.innerWidth - SCROLLBAR_THICKNESS) - width; } - const underSpace = (window.innerHeight - MARGIN) - top; + const underSpace = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - top; const upperSpace = (srcRect.top - MARGIN); // ç”»é¢ã‹ã‚‰ç¸¦ã«ã¯ã¿å‡ºã‚‹å ´åˆ - if (top + height > (window.innerHeight - MARGIN)) { + if (top + height > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) { if (props.noOverlap && props.anchor.x === 'center') { if (underSpace >= (upperSpace / 3)) { maxHeight = underSpace; @@ -187,22 +188,22 @@ const align = () => { top = (upperSpace + MARGIN) - height; } } else { - top = (window.innerHeight - MARGIN) - height; + top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height; } } else { maxHeight = underSpace; } } else { // ç”»é¢ã‹ã‚‰æ¨ªã«ã¯ã¿å‡ºã‚‹å ´åˆ - if (left + width - window.pageXOffset > window.innerWidth) { - left = window.innerWidth - width + window.pageXOffset - 1; + if (left + width - window.pageXOffset > (window.innerWidth - SCROLLBAR_THICKNESS)) { + left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset - 1; } - const underSpace = (window.innerHeight - MARGIN) - (top - window.pageYOffset); + const underSpace = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - (top - window.pageYOffset); const upperSpace = (srcRect.top - MARGIN); // ç”»é¢ã‹ã‚‰ç¸¦ã«ã¯ã¿å‡ºã‚‹å ´åˆ - if (top + height - window.pageYOffset > (window.innerHeight - MARGIN)) { + if (top + height - window.pageYOffset > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) { if (props.noOverlap && props.anchor.x === 'center') { if (underSpace >= (upperSpace / 3)) { maxHeight = underSpace; @@ -211,7 +212,7 @@ const align = () => { top = window.pageYOffset + ((upperSpace + MARGIN) - height); } } else { - top = (window.innerHeight - MARGIN) - height + window.pageYOffset - 1; + top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height + window.pageYOffset - 1; } } else { maxHeight = underSpace; diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index f4c044e0bd..1040dac12e 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -31,7 +31,7 @@ <i v-else-if="note.visibility === 'followers'" class="ti ti-lock"></i> <i v-else-if="note.visibility === 'specified'" ref="specified" class="ti ti-mail"></i> </span> - <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['localOnly']"><i class="ti ti-world-off"></i></span> + <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-world-off"></i></span> </div> </div> <div v-if="renoteCollapsed" :class="$style.collapsedRenoteTarget"> @@ -155,7 +155,6 @@ import { deepClone } from '@/scripts/clone'; import { useTooltip } from '@/scripts/use-tooltip'; import { claimAchievement } from '@/scripts/achievements'; import { getNoteSummary } from '@/scripts/get-note-summary'; -import { shownNoteIds } from '@/os'; import { MenuItem } from '@/types/menu'; const props = defineProps<{ @@ -195,6 +194,8 @@ const isMyRenote = $i && ($i.id === note.userId); const showContent = ref(false); const urls = appearNote.text ? extractUrlFromMfm(mfm.parse(appearNote.text)) : null; const isLong = (appearNote.cw == null && appearNote.text != null && ( + (appearNote.text.includes('$[x3')) || + (appearNote.text.includes('$[x4')) || (appearNote.text.split('\n').length > 9) || (appearNote.text.length > 500) || (appearNote.files.length >= 5) || @@ -207,9 +208,7 @@ const translation = ref<any>(null); const translating = ref(false); const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance); const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || appearNote.userId === $i.id); -let renoteCollapsed = $ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.userId)) || shownNoteIds.has(appearNote.id))); - -shownNoteIds.add(appearNote.id); +let renoteCollapsed = $ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.userId)) || (appearNote.myReaction != null))); const keymap = { 'r': () => reply(true), @@ -256,7 +255,7 @@ function renote(viaKeyboard = false) { text: i18n.ts.inChannelRenote, icon: 'ti ti-repeat', action: () => { - os.api('notes/create', { + os.apiWithDialog('notes/create', { renoteId: appearNote.id, channelId: appearNote.channelId, }); @@ -277,7 +276,7 @@ function renote(viaKeyboard = false) { text: i18n.ts.renote, icon: 'ti ti-repeat', action: () => { - os.api('notes/create', { + os.apiWithDialog('notes/create', { renoteId: appearNote.id, }); }, @@ -674,9 +673,17 @@ function showReactions(): void { opacity: 0.7; } -@container (max-width: 500px) { +@container (max-width: 580px) { .root { - font-size: 0.9em; + font-size: 0.95em; + } + + .renote { + padding: 12px 26px 0 26px; + } + + .article { + padding: 24px 26px 14px; } .avatar { @@ -685,7 +692,21 @@ function showReactions(): void { } } -@container (max-width: 450px) { +@container (max-width: 500px) { + .root { + font-size: 0.9em; + } + + .renote { + padding: 10px 22px 0 22px; + } + + .article { + padding: 20px 22px 12px; + } +} + +@container (max-width: 480px) { .renote { padding: 8px 16px 0 16px; } @@ -702,7 +723,9 @@ function showReactions(): void { .article { padding: 14px 16px 9px; } +} +@container (max-width: 450px) { .avatar { margin: 0 10px 8px 0; width: 46px; @@ -711,7 +734,7 @@ function showReactions(): void { } } -@container (max-width: 350px) { +@container (max-width: 400px) { .footerButton { &:not(:last-child) { margin-right: 18px; @@ -719,6 +742,14 @@ function showReactions(): void { } } +@container (max-width: 350px) { + .footerButton { + &:not(:last-child) { + margin-right: 12px; + } + } +} + @container (max-width: 300px) { .avatar { width: 44px; @@ -727,7 +758,7 @@ function showReactions(): void { .footerButton { &:not(:last-child) { - margin-right: 12px; + margin-right: 8px; } } } diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 82e0f3e689..2eebe999a5 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -30,7 +30,7 @@ <i v-else-if="note.visibility === 'followers'" class="ti ti-lock"></i> <i v-else-if="note.visibility === 'specified'" ref="specified" class="ti ti-mail"></i> </span> - <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['localOnly']"><i class="ti ti-world-off"></i></span> + <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-world-off"></i></span> </div> </div> <article class="article" @contextmenu.stop="onContextmenu"> @@ -48,7 +48,7 @@ <i v-else-if="appearNote.visibility === 'followers'" class="ti ti-lock"></i> <i v-else-if="appearNote.visibility === 'specified'" ref="specified" class="ti ti-mail"></i> </span> - <span v-if="appearNote.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['localOnly']"><i class="ti ti-world-off"></i></span> + <span v-if="appearNote.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-world-off"></i></span> </div> </div> <div class="username"><MkAcct :user="appearNote.user"/></div> @@ -250,7 +250,7 @@ function renote(viaKeyboard = false) { text: i18n.ts.inChannelRenote, icon: 'ti ti-repeat', action: () => { - os.api('notes/create', { + os.apiWithDialog('notes/create', { renoteId: appearNote.id, channelId: appearNote.channelId, }); @@ -271,7 +271,7 @@ function renote(viaKeyboard = false) { text: i18n.ts.renote, icon: 'ti ti-repeat', action: () => { - os.api('notes/create', { + os.apiWithDialog('notes/create', { renoteId: appearNote.id, }); }, diff --git a/packages/frontend/src/components/MkNoteHeader.vue b/packages/frontend/src/components/MkNoteHeader.vue index 32998e1a70..ffd9a20ef7 100644 --- a/packages/frontend/src/components/MkNoteHeader.vue +++ b/packages/frontend/src/components/MkNoteHeader.vue @@ -17,7 +17,7 @@ <i v-else-if="note.visibility === 'followers'" class="ti ti-lock"></i> <i v-else-if="note.visibility === 'specified'" ref="specified" class="ti ti-mail"></i> </span> - <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['localOnly']"><i class="ti ti-world-off"></i></span> + <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-world-off"></i></span> </div> </header> </template> diff --git a/packages/frontend/src/components/MkNotificationSettingWindow.vue b/packages/frontend/src/components/MkNotificationSettingWindow.vue index e303403872..f6d0e5681d 100644 --- a/packages/frontend/src/components/MkNotificationSettingWindow.vue +++ b/packages/frontend/src/components/MkNotificationSettingWindow.vue @@ -6,7 +6,7 @@ :with-ok-button="true" :ok-button-disabled="false" @ok="ok()" - @close="dialog.close()" + @close="dialog?.close()" @closed="emit('closed')" > <template #header>{{ i18n.ts.notificationSetting }}</template> @@ -25,7 +25,7 @@ <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]">{{ i18n.t(`_notification._types.${ntype}`) }}</MkSwitch> + <MkSwitch v-for="ntype in notificationTypes" :key="ntype" v-model="typesMap[ntype].value">{{ i18n.t(`_notification._types.${ntype}`) }}</MkSwitch> </template> </div> </MkSpacer> @@ -33,14 +33,16 @@ </template> <script lang="ts" setup> -import { } from 'vue'; -import { notificationTypes } from 'misskey-js'; +import { ref, Ref } from 'vue'; import MkSwitch from './MkSwitch.vue'; import MkInfo from './MkInfo.vue'; import MkButton from './MkButton.vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; +import { notificationTypes } from '@/const'; import { i18n } from '@/i18n'; +type TypesMap = Record<typeof notificationTypes[number], Ref<boolean>> + const emit = defineEmits<{ (ev: 'done', v: { includingTypes: string[] | null }): void, (ev: 'closed'): void, @@ -54,39 +56,35 @@ const props = withDefaults(defineProps<{ showGlobalToggle: true, }); -let includingTypes = $computed(() => props.includingTypes || []); +let includingTypes = $computed(() => props.includingTypes?.filter(x => notificationTypes.includes(x)) ?? []); const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); -let typesMap = $ref<Record<typeof notificationTypes[number], boolean>>({}); +const typesMap: TypesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(includingTypes.includes(t)) }), {} as any); let useGlobalSetting = $ref((includingTypes === null || includingTypes.length === 0) && props.showGlobalToggle); -for (const ntype of notificationTypes) { - typesMap[ntype] = includingTypes.includes(ntype); -} - function ok() { if (useGlobalSetting) { emit('done', { includingTypes: null }); } else { emit('done', { includingTypes: (Object.keys(typesMap) as typeof notificationTypes[number][]) - .filter(type => typesMap[type]), + .filter(type => typesMap[type].value), }); } - dialog.close(); + if (dialog) dialog.close(); } function disableAll() { - for (const type in typesMap) { - typesMap[type as typeof notificationTypes[number]] = false; + for (const type of notificationTypes) { + typesMap[type].value = false; } } function enableAll() { - for (const type in typesMap) { - typesMap[type as typeof notificationTypes[number]] = true; + for (const type of notificationTypes) { + typesMap[type].value = true; } } </script> diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue index 37ce7635a3..93b1c37055 100644 --- a/packages/frontend/src/components/MkNotifications.vue +++ b/packages/frontend/src/components/MkNotifications.vue @@ -18,7 +18,6 @@ <script lang="ts" setup> import { onUnmounted, onMounted, computed, shallowRef } from 'vue'; -import { notificationTypes } from 'misskey-js'; import MkPagination, { Paging } from '@/components/MkPagination.vue'; import XNotification from '@/components/MkNotification.vue'; import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue'; @@ -26,6 +25,7 @@ import XNote from '@/components/MkNote.vue'; import { stream } from '@/stream'; import { $i } from '@/account'; import { i18n } from '@/i18n'; +import { notificationTypes } from '@/const'; const props = defineProps<{ includeTypes?: typeof notificationTypes[number][]; diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue index 98115dd424..02ce58451d 100644 --- a/packages/frontend/src/components/MkPageWindow.vue +++ b/packages/frontend/src/components/MkPageWindow.vue @@ -18,7 +18,7 @@ </template> <div :class="$style.root" :style="{ background: pageMetadata?.value?.bg }" style="container-type: inline-size;"> - <RouterView :router="router"/> + <RouterView :key="reloadCount" :router="router"/> </div> </MkWindow> </template> @@ -67,6 +67,10 @@ const buttonsLeft = $computed(() => { }); const buttonsRight = $computed(() => { const buttons = [{ + icon: 'ti ti-reload', + title: i18n.ts.reload, + onClick: reload, + }, { icon: 'ti ti-player-eject', title: i18n.ts.showInPage, onClick: expand, @@ -74,6 +78,7 @@ const buttonsRight = $computed(() => { return buttons; }); +let reloadCount = $ref(0); router.addListener('push', ctx => { history.push({ path: ctx.path, key: ctx.key }); @@ -115,6 +120,10 @@ function back() { router.replace(history[history.length - 1].path, history[history.length - 1].key); } +function reload() { + reloadCount++; +} + function close() { windowEl.close(); } diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index 224a42cdc2..378d0ac020 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -42,6 +42,7 @@ import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeUnmount, o import * as misskey from 'misskey-js'; import * as os from '@/os'; import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll'; +import { useDocumentVisibility } from '@/scripts/use-document-visibility'; import MkButton from '@/components/MkButton.vue'; import { defaultStore } from '@/store'; import { MisskeyEntity } from '@/types/date-separated-list'; @@ -104,9 +105,15 @@ const { enableInfiniteScroll, } = defaultStore.reactiveState; -const contentEl = $computed(() => props.pagination.pageEl || rootEl); +const contentEl = $computed(() => props.pagination.pageEl ?? rootEl); const scrollableElement = $computed(() => getScrollContainer(contentEl)); +const visibility = useDocumentVisibility(); + +let isPausingUpdate = false; +let timerForSetPause: number | null = null; +const BACKGROUND_PAUSE_WAIT_SEC = 10; + // å…ˆé ãŒè¡¨ç¤ºã•れã¦ã„ã‚‹ã‹ã©ã†ã‹ã‚’検出 // https://qiita.com/mkataigi/items/0154aefd2223ce23398e let scrollObserver = $ref<IntersectionObserver>(); @@ -279,6 +286,28 @@ const fetchMoreAhead = async (): Promise<void> => { }); }; +const isTop = (): boolean => isBackTop.value || (props.pagination.reversed ? isBottomVisible : isTopVisible)(contentEl, TOLERANCE); + +watch(visibility, () => { + if (visibility.value === 'hidden') { + timerForSetPause = window.setTimeout(() => { + isPausingUpdate = true; + timerForSetPause = null; + }, + BACKGROUND_PAUSE_WAIT_SEC * 1000); + } else { // 'visible' + if (timerForSetPause) { + clearTimeout(timerForSetPause); + timerForSetPause = null; + } else { + isPausingUpdate = false; + if (isTop()) { + executeQueue(); + } + } + } +}); + const prepend = (item: MisskeyEntity): void => { // åˆå›žè¡¨ç¤ºæ™‚ã¯unshiftã ã‘ã§OK if (!rootEl) { @@ -286,9 +315,7 @@ const prepend = (item: MisskeyEntity): void => { return; } - const isTop = isBackTop.value || (props.pagination.reversed ? isBottomVisible : isTopVisible)(contentEl, TOLERANCE); - - if (isTop) unshiftItems([item]); + if (isTop() && !isPausingUpdate) unshiftItems([item]); else prependQueue(item); }; @@ -357,6 +384,10 @@ onMounted(() => { }); onBeforeUnmount(() => { + if (timerForSetPause) { + clearTimeout(timerForSetPause); + timerForSetPause = null; + } scrollObserver.disconnect(); }); diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 44462f8ff2..f73eab5b86 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -45,6 +45,7 @@ <button class="_buttonPrimary" style="padding: 4px; border-radius: 8px;" @click="addVisibleUser"><i class="ti ti-plus ti-fw"></i></button> </div> </div> + <MkInfo v-if="localOnly && channel == null" warn :class="$style.disableFederationWarn">{{ i18n.ts.disableFederationWarn }}</MkInfo> <MkInfo v-if="hasNotSpecifiedMentions" warn :class="$style.hasNotSpecifiedMentions">{{ i18n.ts.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ i18n.ts.add }}</button></MkInfo> <input v-show="useCw" ref="cwInputEl" v-model="cw" :class="$style.cw" :placeholder="i18n.ts.annotation" @keydown="onKeydown"> <textarea ref="textareaEl" v-model="text" :class="[$style.text, { [$style.withCw]: useCw }]" :disabled="posting || posted" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/> @@ -73,7 +74,6 @@ import { inject, watch, nextTick, onMounted, defineAsyncComponent } from 'vue'; import * as mfm from 'mfm-js'; import * as misskey from 'misskey-js'; import insertTextAtCursor from 'insert-text-at-cursor'; -import { length } from 'stringz'; import { toASCII } from 'punycode/'; import * as Acct from 'misskey-js/built/acct'; import MkNoteSimple from '@/components/MkNoteSimple.vue'; @@ -155,7 +155,7 @@ let autocomplete = $ref(null); let draghover = $ref(false); let quoteId = $ref(null); let hasNotSpecifiedMentions = $ref(false); -let recentHashtags = $ref(JSON.parse(miLocalStorage.getItem('hashtags') || '[]')); +let recentHashtags = $ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]')); let imeText = $ref(''); const draftKey = $computed((): string => { @@ -201,7 +201,7 @@ const submitText = $computed((): string => { }); const textLength = $computed((): number => { - return length((text + imeText).trim()); + return (text + imeText).trim().length; }); const maxTextLength = $computed((): number => { @@ -534,7 +534,7 @@ function onDrop(ev): void { } function saveDraft() { - const draftData = JSON.parse(miLocalStorage.getItem('drafts') || '{}'); + const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); draftData[draftKey] = { updatedAt: new Date(), @@ -643,7 +643,7 @@ async function post(ev?: MouseEvent) { emit('posted'); if (postData.text && postData.text !== '') { const hashtags_ = mfm.parse(postData.text).filter(x => x.type === 'hashtag').map(x => x.props.hashtag); - const history = JSON.parse(miLocalStorage.getItem('hashtags') || '[]') as string[]; + const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[]; miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history)))); } posting = false; @@ -747,7 +747,7 @@ onMounted(() => { nextTick(() => { // 書ãã‹ã‘ã®æŠ•ç¨¿ã‚’å¾©å…ƒ if (!props.instant && !props.mention && !props.specified) { - const draft = JSON.parse(miLocalStorage.getItem('drafts') || '{}')[draftKey]; + const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey]; if (draft) { text = draft.data.text; useCw = draft.data.useCw; @@ -942,6 +942,10 @@ defineExpose({ background: var(--X4); } +.disableFederationWarn { + margin: 0 20px 16px 20px; +} + .hasNotSpecifiedMentions { margin: 0 20px 16px 20px; } diff --git a/packages/frontend/src/components/MkRolePreview.vue b/packages/frontend/src/components/MkRolePreview.vue index 8c1d7af190..2f5866f340 100644 --- a/packages/frontend/src/components/MkRolePreview.vue +++ b/packages/frontend/src/components/MkRolePreview.vue @@ -1,10 +1,15 @@ <template> -<MkA v-adaptive-bg :to="`/admin/roles/${role.id}`" class="_panel" :class="$style.root" tabindex="-1" :style="{ '--color': role.color }"> +<MkA v-adaptive-bg :to="forModeration ? `/admin/roles/${role.id}` : `/roles/${role.id}`" class="_panel" :class="$style.root" tabindex="-1" :style="{ '--color': role.color }"> <div :class="$style.title"> <span :class="$style.icon"> - <i v-if="role.isAdministrator" class="ti ti-crown" style="color: var(--accent);"></i> - <i v-else-if="role.isModerator" class="ti ti-shield" style="color: var(--accent);"></i> - <i v-else class="ti ti-user" style="opacity: 0.7;"></i> + <template v-if="role.iconUrl"> + <img :class="$style.badge" :src="role.iconUrl"/> + </template> + <template v-else> + <i v-if="role.isAdministrator" class="ti ti-crown" style="color: var(--accent);"></i> + <i v-else-if="role.isModerator" class="ti ti-shield" style="color: var(--accent);"></i> + <i v-else class="ti ti-user" style="opacity: 0.7;"></i> + </template> </span> <span :class="$style.name">{{ role.name }}</span> <span v-if="role.target === 'manual'" :class="$style.users">{{ role.usersCount }} users</span> @@ -20,6 +25,7 @@ import { i18n } from '@/i18n'; const props = defineProps<{ role: any; + forModeration: boolean; }>(); </script> @@ -38,6 +44,11 @@ const props = defineProps<{ margin-right: 8px; } +.badge { + height: 1.3em; + vertical-align: -20%; +} + .name { font-weight: bold; } diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue index cb64b1e484..2de890186a 100644 --- a/packages/frontend/src/components/MkSelect.vue +++ b/packages/frontend/src/components/MkSelect.vue @@ -34,7 +34,7 @@ import { useInterval } from '@/scripts/use-interval'; import { i18n } from '@/i18n'; const props = defineProps<{ - modelValue: string; + modelValue: string | null; required?: boolean; readonly?: boolean; disabled?: boolean; @@ -48,7 +48,7 @@ const props = defineProps<{ const emit = defineEmits<{ (ev: 'change', _ev: KeyboardEvent): void; - (ev: 'update:modelValue', value: string): void; + (ev: 'update:modelValue', value: string | null): void; }>(); const slots = useSlots(); diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue index ae4f38e56c..ffc5e82b56 100644 --- a/packages/frontend/src/components/MkSignin.vue +++ b/packages/frontend/src/components/MkSignin.vue @@ -10,7 +10,7 @@ <template #prefix>@</template> <template #suffix>@{{ host }}</template> </MkInput> - <MkInput v-if="!user || user && !user.usePasswordLessLogin" v-model="password" :placeholder="i18n.ts.password" type="password" :with-password-toggle="true" required data-cy-signin-password> + <MkInput v-if="!user || user && !user.usePasswordLessLogin" v-model="password" :placeholder="i18n.ts.password" type="password" autocomplete="current-password" :with-password-toggle="true" required data-cy-signin-password> <template #prefix><i class="ti ti-lock"></i></template> <template #caption><button class="_textButton" type="button" @click="resetPassword">{{ i18n.ts.forgotPassword }}</button></template> </MkInput> @@ -28,11 +28,11 @@ </div> <div class="twofa-group totp-group"> <p style="margin-bottom:0;">{{ i18n.ts.twoStepAuthentication }}</p> - <MkInput v-if="user && user.usePasswordLessLogin" v-model="password" type="password" :with-password-toggle="true" required> + <MkInput v-if="user && user.usePasswordLessLogin" v-model="password" type="password" autocomplete="current-password" :with-password-toggle="true" required> <template #label>{{ i18n.ts.password }}</template> <template #prefix><i class="ti ti-lock"></i></template> </MkInput> - <MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" :spellcheck="false" required> + <MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="one-time-code" :spellcheck="false" required> <template #label>{{ i18n.ts.token }}</template> <template #prefix><i class="ti ti-123"></i></template> </MkInput> diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 1ba48bf77d..87f7c61a92 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -1,10 +1,10 @@ <template> -<XNotes ref="tlComponent" :no-gap="!$store.state.showGapBetweenNotesInTimeline" :pagination="pagination" @queue="emit('queue', $event)"/> +<MkNotes ref="tlComponent" :no-gap="!$store.state.showGapBetweenNotesInTimeline" :pagination="pagination" @queue="emit('queue', $event)"/> </template> <script lang="ts" setup> import { computed, provide, onUnmounted } from 'vue'; -import XNotes from '@/components/MkNotes.vue'; +import MkNotes from '@/components/MkNotes.vue'; import { stream } from '@/stream'; import * as sound from '@/scripts/sound'; import { $i } from '@/account'; @@ -24,7 +24,7 @@ const emit = defineEmits<{ provide('inChannel', computed(() => props.src === 'channel')); -const tlComponent: InstanceType<typeof XNotes> = $ref(); +const tlComponent: InstanceType<typeof MkNotes> = $ref(); const prepend = note => { tlComponent.pagingComponent?.prepend(note); diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue index b97b7cf07b..5381ecbfa5 100644 --- a/packages/frontend/src/components/MkUrlPreview.vue +++ b/packages/frontend/src/components/MkUrlPreview.vue @@ -1,12 +1,25 @@ <template> -<div v-if="playerEnabled" :class="$style.player" :style="`padding: ${(player.height || 0) / (player.width || 1) * 100}% 0 0`"> - <button :class="$style.disablePlayer" :title="i18n.ts.disablePlayer" @click="playerEnabled = false"><i class="ti ti-x"></i></button> - <iframe v-if="player.url.startsWith('http://') || player.url.startsWith('https://')" :class="$style.playerIframe" :src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')" :width="player.width || '100%'" :heigth="player.height || 250" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen/> - <span v-else>invalid url</span> -</div> -<div v-else-if="tweetId && tweetExpanded" ref="twitter" :class="$style.twitter"> - <iframe ref="tweet" scrolling="no" frameborder="no" :style="{ position: 'relative', width: '100%', height: `${tweetHeight}px` }" :src="`https://platform.twitter.com/embed/index.html?embedId=${embedId}&hideCard=false&hideThread=false&lang=en&theme=${$store.state.darkMode ? 'dark' : 'light'}&id=${tweetId}`"></iframe> -</div> +<template v-if="playerEnabled"> + <div :class="$style.player" :style="`padding: ${(player.height || 0) / (player.width || 1) * 100}% 0 0`"> + <iframe v-if="player.url.startsWith('http://') || player.url.startsWith('https://')" :class="$style.playerIframe" :src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')" :width="player.width || '100%'" :heigth="player.height || 250" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen/> + <span v-else>invalid url</span> + </div> + <div :class="$style.action"> + <MkButton :small="true" inline @click="playerEnabled = false"> + <i class="ti ti-x"></i> {{ i18n.ts.disablePlayer }} + </MkButton> + </div> +</template> +<template v-else-if="tweetId && tweetExpanded"> + <div ref="twitter" :class="$style.twitter"> + <iframe ref="tweet" scrolling="no" frameborder="no" :style="{ position: 'relative', width: '100%', height: `${tweetHeight}px` }" :src="`https://platform.twitter.com/embed/index.html?embedId=${embedId}&hideCard=false&hideThread=false&lang=en&theme=${$store.state.darkMode ? 'dark' : 'light'}&id=${tweetId}`"></iframe> + </div> + <div :class="$style.action"> + <MkButton :small="true" inline @click="tweetExpanded = false"> + <i class="ti ti-x"></i> {{ i18n.ts.close }} + </MkButton> + </div> +</template> <div v-else :class="$style.urlPreview"> <component :is="self ? 'MkA' : 'a'" :class="[$style.link, { [$style.compact]: compact }]" :[attr]="self ? url.substr(local.length) : url" rel="nofollow noopener" :target="target" :title="url"> <div v-if="thumbnail" :class="$style.thumbnail" :style="`background-image: url('${thumbnail}')`"> diff --git a/packages/frontend/src/components/MkUserList.vue b/packages/frontend/src/components/MkUserList.vue index dd683fcc23..51eb426e97 100644 --- a/packages/frontend/src/components/MkUserList.vue +++ b/packages/frontend/src/components/MkUserList.vue @@ -7,9 +7,9 @@ </div> </template> - <template #default="{ items: users }"> + <template #default="{ items }"> <div class="efvhhmdq"> - <MkUserInfo v-for="user in users" :key="user.id" class="user" :user="user"/> + <MkUserInfo v-for="item in items" :key="item.id" class="user" :user="extractor(item)"/> </div> </template> </MkPagination> @@ -20,10 +20,13 @@ import MkUserInfo from '@/components/MkUserInfo.vue'; import MkPagination, { Paging } from '@/components/MkPagination.vue'; import { i18n } from '@/i18n'; -const props = defineProps<{ +const props = withDefaults(defineProps<{ pagination: Paging; noGap?: boolean; -}>(); + extractor?: (item: any) => any; +}>(), { + extractor: (item) => item, +}); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue index 981ae56e6c..dc78bbf42d 100644 --- a/packages/frontend/src/components/MkUserSelectDialog.vue +++ b/packages/frontend/src/components/MkUserSelectDialog.vue @@ -16,7 +16,7 @@ <template #label>{{ i18n.ts.username }}</template> <template #prefix>@</template> </MkInput> - <MkInput v-model="host" @update:model-value="search"> + <MkInput v-model="host" :datalist="[hostname]" @update:model-value="search"> <template #label>{{ i18n.ts.host }}</template> <template #prefix>@</template> </MkInput> @@ -61,6 +61,7 @@ import * as os from '@/os'; import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; import { $i } from '@/account'; +import { hostname } from '@/config'; const emit = defineEmits<{ (ev: 'ok', selected: misskey.entities.UserDetailed): void; @@ -115,7 +116,7 @@ onMounted(() => { os.api('users/show', { userIds: defaultStore.state.recentlyUsedUsers, }).then(users => { - if (props.includeSelf) { + if (props.includeSelf && users.find(x => $i ? x.id === $i.id : true) == null) { recentUsers = [$i, ...users]; } else { recentUsers = users; diff --git a/packages/frontend/src/components/MkVisibilityPicker.vue b/packages/frontend/src/components/MkVisibilityPicker.vue index 516b88c13d..703c75c7d0 100644 --- a/packages/frontend/src/components/MkVisibilityPicker.vue +++ b/packages/frontend/src/components/MkVisibilityPicker.vue @@ -33,8 +33,8 @@ <button key="localOnly" class="_button" :class="[$style.item, $style.localOnly, { [$style.active]: localOnly }]" data-index="5" @click="localOnly = !localOnly"> <div :class="$style.icon"><i class="ti ti-world-off"></i></div> <div :class="$style.body"> - <span :class="$style.itemTitle">{{ i18n.ts._visibility.localOnly }}</span> - <span :class="$style.itemDescription">{{ i18n.ts._visibility.localOnlyDescription }}</span> + <span :class="$style.itemTitle">{{ i18n.ts._visibility.disableFederation }}</span> + <span :class="$style.itemDescription">{{ i18n.ts._visibility.disableFederationDescription }}</span> </div> <div :class="$style.toggle"><i :class="localOnly ? 'ti ti-toggle-right' : 'ti ti-toggle-left'"></i></div> </button> diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue index e6dedd0354..84aae1cff8 100644 --- a/packages/frontend/src/components/global/MkCustomEmoji.vue +++ b/packages/frontend/src/components/global/MkCustomEmoji.vue @@ -24,7 +24,7 @@ const rawUrl = computed(() => { return props.url; } if (props.host == null && !customEmojiName.value.includes('@')) { - return customEmojis.value.find(x => x.name === customEmojiName.value)?.url || null; + return customEmojis.value.find(x => x.name === customEmojiName.value)?.url ?? null; } return props.host ? `/emoji/${customEmojiName.value}@${props.host}.webp` : `/emoji/${customEmojiName.value}.webp`; }); @@ -32,7 +32,7 @@ const rawUrl = computed(() => { const url = computed(() => defaultStore.reactiveState.disableShowingAnimatedImages.value && rawUrl.value ? getStaticImageUrl(rawUrl.value) - : rawUrl.value + : rawUrl.value, ); const alt = computed(() => `:${customEmojiName.value}:`); @@ -41,7 +41,7 @@ let errored = $ref(url.value == null); <style lang="scss" module> .root { - height: 2.5em; + height: 2em; vertical-align: middle; transition: transform 0.2s ease; diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue index b181b62986..42760da08f 100644 --- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue +++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue @@ -1,23 +1,33 @@ <template> - <div ref="el" :class="$style.tabs" @wheel="onTabWheel"> - <div :class="$style.tabsInner"> - <button v-for="t in tabs" :ref="(el) => tabRefs[t.key] = (el as HTMLElement)" v-tooltip.noDelay="t.title" - class="_button" :class="[$style.tab, { [$style.active]: t.key != null && t.key === props.tab, [$style.animate]: defaultStore.reactiveState.animation.value }]" - @mousedown="(ev) => onTabMousedown(t, ev)" @click="(ev) => onTabClick(t, ev)"> - <div :class="$style.tabInner"> - <i v-if="t.icon" :class="[$style.tabIcon, t.icon]"></i> - <div v-if="!t.iconOnly || (!defaultStore.reactiveState.animation.value && t.key === tab)" - :class="$style.tabTitle">{{ t.title }}</div> - <Transition v-else mode="in-out" @enter="enter" @after-enter="afterEnter" @leave="leave" - @after-leave="afterLeave"> - <div v-show="t.key === tab" :class="[$style.tabTitle, $style.animate]">{{ t.title }}</div> - </Transition> +<div ref="el" :class="$style.tabs" @wheel="onTabWheel"> + <div :class="$style.tabsInner"> + <button + v-for="t in tabs" :ref="(el) => tabRefs[t.key] = (el as HTMLElement)" v-tooltip.noDelay="t.title" + class="_button" :class="[$style.tab, { [$style.active]: t.key != null && t.key === props.tab, [$style.animate]: defaultStore.reactiveState.animation.value }]" + @mousedown="(ev) => onTabMousedown(t, ev)" @click="(ev) => onTabClick(t, ev)" + > + <div :class="$style.tabInner"> + <i v-if="t.icon" :class="[$style.tabIcon, t.icon]"></i> + <div + v-if="!t.iconOnly || (!defaultStore.reactiveState.animation.value && t.key === tab)" + :class="$style.tabTitle" + > + {{ t.title }} </div> - </button> - </div> - <div ref="tabHighlightEl" - :class="[$style.tabHighlight, { [$style.animate]: defaultStore.reactiveState.animation.value }]"></div> + <Transition + v-else mode="in-out" @enter="enter" @after-enter="afterEnter" @leave="leave" + @after-leave="afterLeave" + > + <div v-show="t.key === tab" :class="[$style.tabTitle, $style.animate]">{{ t.title }}</div> + </Transition> + </div> + </button> </div> + <div + ref="tabHighlightEl" + :class="[$style.tabHighlight, { [$style.animate]: defaultStore.reactiveState.animation.value }]" + ></div> +</div> </template> <script lang="ts"> @@ -93,7 +103,7 @@ function onTabWheel(ev: WheelEvent) { ev.stopPropagation(); (ev.currentTarget as HTMLElement).scrollBy({ left: ev.deltaY, - behavior: 'smooth', + behavior: 'instant', }); } return false; @@ -206,8 +216,8 @@ onUnmounted(() => { align-items: center; } -.tabIcon+.tabTitle { - padding-left: 8px; +.tabIcon + .tabTitle { + padding-left: 4px; } .tabTitle { diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index 98233b02e0..589ca92d75 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -2,9 +2,9 @@ <div v-if="show" ref="el" :class="[$style.root]" :style="{ background: bg }"> <div :class="[$style.upper, { [$style.slim]: narrow, [$style.thin]: thin_ }]"> <div v-if="!thin_ && narrow && props.displayMyAvatar && $i" class="_button" :class="$style.buttonsLeft" @click="openAccountMenu"> - <MkAvatar :class="$style.avatar" :user="$i" /> + <MkAvatar :class="$style.avatar" :user="$i"/> </div> - <div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttonsLeft" /> + <div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttonsLeft"/> <template v-if="metadata"> <div v-if="!hideTitle" :class="$style.titleContainer" @click="top"> @@ -36,11 +36,11 @@ <script lang="ts" setup> import { onMounted, onUnmounted, ref, inject } from 'vue'; import tinycolor from 'tinycolor2'; +import XTabs, { Tab } from './MkPageHeader.tabs.vue'; import { scrollToTop } from '@/scripts/scroll'; import { globalEvents } from '@/events'; import { injectPageMetadata } from '@/scripts/page-metadata'; import { $i, openAccountMenu as openAccountMenu_ } from '@/account'; -import XTabs, { Tab } from './MkPageHeader.tabs.vue'; const props = withDefaults(defineProps<{ tabs?: Tab[]; @@ -96,7 +96,7 @@ function onTabClick(): void { } const calcBg = () => { - const rawBg = metadata?.bg || 'var(--bg)'; + const rawBg = metadata?.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(); @@ -147,10 +147,7 @@ onUnmounted(() => { .tabs:first-child { margin-left: auto; - } - .tabs:not(:first-child) { - padding-left: 16px; - mask-image: linear-gradient(90deg, rgba(0,0,0,0), rgb(0,0,0) 16px, rgb(0,0,0) 100%); + padding: 0 12px; } .tabs { margin-right: auto; diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue index 66c0bd5135..3fa8bb9adc 100644 --- a/packages/frontend/src/components/global/MkTime.vue +++ b/packages/frontend/src/components/global/MkTime.vue @@ -1,6 +1,7 @@ <template> <time :title="absolute"> - <template v-if="mode === 'relative'">{{ relative }}</template> + <template v-if="invalid">{{ i18n.ts._ago.invalid }}</template> + <template v-else-if="mode === 'relative'">{{ relative }}</template> <template v-else-if="mode === 'absolute'">{{ absolute }}</template> <template v-else-if="mode === 'detail'">{{ absolute }} ({{ relative }})</template> </time> @@ -12,18 +13,24 @@ import { i18n } from '@/i18n'; import { dateTimeFormat } from '@/scripts/intl-const'; const props = withDefaults(defineProps<{ - time: Date | string; + time: Date | string | number | null; mode?: 'relative' | 'absolute' | 'detail'; }>(), { mode: 'relative', }); -const _time = typeof props.time === 'string' ? new Date(props.time) : props.time; -const absolute = dateTimeFormat.format(_time); +const _time = props.time == null ? NaN : + typeof props.time === 'number' ? props.time : + (props.time instanceof Date ? props.time : new Date(props.time)).getTime(); +const invalid = Number.isNaN(_time); +const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid; -let now = $shallowRef(new Date()); -const relative = $computed(() => { - const ago = (now.getTime() - _time.getTime()) / 1000/*ms*/; +let now = $ref((new Date()).getTime()); +const relative = $computed<string>(() => { + if (props.mode === 'absolute') return ''; // absoluteã§ã¯relativeを使ã‚ãªã„ã®ã§è¨ˆç®—ã—ãªã„ + if (invalid) return i18n.ts._ago.invalid; + + const ago = (now - _time) / 1000/*ms*/; return ( ago >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago / 31536000).toString() }) : ago >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago / 2592000).toString() }) : @@ -39,8 +46,8 @@ const relative = $computed(() => { let tickId: number; function tick() { - now = new Date(); - const ago = (now.getTime() - _time.getTime()) / 1000/*ms*/; + now = (new Date()).getTime(); + const ago = (now - _time) / 1000/*ms*/; const next = ago < 60 ? 10000 : ago < 3600 ? 60000 : 180000; tickId = window.setTimeout(tick, next); diff --git a/packages/frontend/src/components/mfm.ts b/packages/frontend/src/components/mfm.ts index 1b1d27ea2a..e84eabcbcc 100644 --- a/packages/frontend/src/components/mfm.ts +++ b/packages/frontend/src/components/mfm.ts @@ -278,7 +278,7 @@ export default defineComponent({ case 'hashtag': { return [h(MkA, { key: Math.random(), - to: this.isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/explore/tags/${encodeURIComponent(token.props.hashtag)}`, + to: this.isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/user-tags/${encodeURIComponent(token.props.hashtag)}`, style: 'color:var(--hashtag);', }, `#${token.props.hashtag}`)]; } diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index 77366cf07b..1d44786a63 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -43,3 +43,6 @@ https://github.com/sindresorhus/file-type/blob/main/supported.js https://github.com/sindresorhus/file-type/blob/main/core.js https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers */ + +export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] as const; +export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const; diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts index 8c657295f9..0a626b36c6 100644 --- a/packages/frontend/src/init.ts +++ b/packages/frontend/src/init.ts @@ -36,7 +36,6 @@ import { $i, refreshAccount, login, updateAccount, signout } from '@/account'; import { defaultStore, ColdDeviceStorage } from '@/store'; import { fetchInstance, instance } from '@/instance'; import { makeHotkey } from '@/scripts/hotkey'; -import { search } from '@/scripts/search'; import { deviceKind } from '@/scripts/device-kind'; import { initializeSw } from '@/scripts/initialize-sw'; import { reloadChannel } from '@/scripts/unison-reload'; @@ -47,6 +46,7 @@ import { deckStore } from './ui/deck/deck-store'; import { miLocalStorage } from './local-storage'; import { claimAchievement, claimedAchievements } from './scripts/achievements'; import { fetchCustomEmojis } from './custom-emojis'; +import { mainRouter } from './router'; console.info(`Misskey v${version}`); @@ -352,7 +352,9 @@ const hotkeys = { 'd': (): void => { defaultStore.set('darkMode', !defaultStore.state.darkMode); }, - 's': search, + 's': (): void => { + mainRouter.push('/search'); + } }; if ($i) { diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index 95bf6e8181..48bece05fa 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -1,7 +1,6 @@ import { computed, reactive } from 'vue'; import { $i } from './account'; import { miLocalStorage } from './local-storage'; -import { search } from '@/scripts/search'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { ui } from '@/config'; @@ -42,7 +41,7 @@ export const navbarItemDef = reactive({ search: { title: i18n.ts.search, icon: 'ti ti-search', - action: () => search(), + to: '/search', }, lists: { title: i18n.ts.lists, diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index 6bff12661f..7e38fe5f6d 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -246,7 +246,10 @@ export function inputText(props: { title?: string | null; text?: string | null; placeholder?: string | null; + autocomplete?: string; default?: string | null; + minLength?: number; + maxLength?: number; }): Promise<{ canceled: true; result: undefined; } | { canceled: false; result: string; }> { @@ -257,7 +260,10 @@ export function inputText(props: { input: { type: props.type, placeholder: props.placeholder, + autocomplete: props.autocomplete, default: props.default, + minLength: props.minLength, + maxLength: props.maxLength, }, }, { done: result => { @@ -271,6 +277,7 @@ export function inputNumber(props: { title?: string | null; text?: string | null; placeholder?: string | null; + autocomplete?: string; default?: number | null; }): Promise<{ canceled: true; result: undefined; } | { canceled: false; result: number; @@ -282,6 +289,7 @@ export function inputNumber(props: { input: { type: 'number', placeholder: props.placeholder, + autocomplete: props.autocomplete, default: props.default, }, }, { @@ -595,9 +603,3 @@ export function checkExistence(fileData: ArrayBuffer): Promise<any> { }); }); }*/ - -export const shownNoteIds = new Set(); - -window.setInterval(() => { - shownNoteIds.clear(); -}, 1000 * 60 * 5); diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index bc3d248193..782fe9fdb2 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -84,6 +84,10 @@ </div> <p>{{ i18n.ts._aboutMisskey.morePatrons }}</p> </FormSection> + <FormSection> + <template #label>Credits</template> + <p>Misskeyã§ä½¿ã‚れる画åƒã®ä¸€éƒ¨ã¯ã€è¨±å¯ã‚’å¾—ã¦ã€Œã‚ã®åãŒã“ã£ã¡ã‚’見ã¦ã‚‹ãƒ¡ãƒ¼ã‚«ãƒ¼ã€ã§ä½œæˆã—ãŸã‚‚ã®ãŒå«ã¾ã‚Œã¾ã™ã€‚</p> + </FormSection> </div> </MkSpacer> </div> @@ -111,6 +115,12 @@ const patronsWithIcon = [{ }, { name: 'ã れã‹ã•ã‚“', icon: 'https://misskey-hub.net/patrons/f7409b5e5a88477a9b9d740c408de125.jpg', +}, { + name: 'narazaka', + icon: 'https://misskey-hub.net/patrons/e3affff31ffb4877b1196c7360abc3e5.jpg', +}, { + name: 'ã²ã¨ã…', + icon: 'https://misskey-hub.net/patrons/8cc0d0a0a6d84c88bca1aedabf6ed5ab.jpg', }]; const patrons = [ diff --git a/packages/frontend/src/pages/admin/_header_.vue b/packages/frontend/src/pages/admin/_header_.vue index 599b16e465..372c63ff4c 100644 --- a/packages/frontend/src/pages/admin/_header_.vue +++ b/packages/frontend/src/pages/admin/_header_.vue @@ -113,7 +113,7 @@ function onTabClick(tab: Tab, ev: MouseEvent): void { } const calcBg = () => { - const rawBg = metadata?.bg || 'var(--bg)'; + const rawBg = metadata?.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/ads.vue b/packages/frontend/src/pages/admin/ads.vue index e5431b177c..828bfe6007 100644 --- a/packages/frontend/src/pages/admin/ads.vue +++ b/packages/frontend/src/pages/admin/ads.vue @@ -44,6 +44,9 @@ <MkButton class="button" inline danger @click="remove(ad)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> </div> </div> + <MkButton class="button" @click="more()"> + <i class="ti ti-reload"></i>{{ i18n.ts.more }} + </MkButton> </div> </MkSpacer> </MkStickyContainer> @@ -123,7 +126,21 @@ function save(ad) { }); } } - +function more() { + os.api('admin/ad/list', { untilId: ads.reduce((acc, ad) => ad.id != null ? ad : acc).id }).then(adsResponse => { + ads = ads.concat(adsResponse.map(r => { + const exdate = new Date(r.expiresAt); + const stdate = new Date(r.startsAt); + exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff); + stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff); + return { + ...r, + expiresAt: exdate.toISOString().slice(0, 16), + startsAt: stdate.toISOString().slice(0, 16), + }; + })); + }); +} const headerActions = $computed(() => [{ asFullButton: true, icon: 'ti ti-plus', diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue index 0365165b5d..e7d57ad4f0 100644 --- a/packages/frontend/src/pages/admin/roles.role.vue +++ b/packages/frontend/src/pages/admin/roles.role.vue @@ -16,16 +16,29 @@ <MkFolder v-if="role.target === 'manual'" default-open> <template #icon><i class="ti ti-users"></i></template> <template #label>{{ i18n.ts.users }}</template> - <template #suffix>{{ role.users.length }}</template> + <template #suffix>{{ role.usersCount }}</template> <div class="_gaps"> <MkButton primary rounded @click="assign"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton> - <div v-for="user in role.users" :key="user.id" :class="$style.userItem"> - <MkA :class="$style.user" :to="`/user-info/${user.id}`"> - <MkUserCardMini :user="user"/> - </MkA> - <button class="_button" :class="$style.unassign" @click="unassign(user, $event)"><i class="ti ti-x"></i></button> - </div> + <MkPagination :pagination="usersPagination"> + <template #empty> + <div class="_fullinfo"> + <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/> + <div>{{ i18n.ts.noUsers }}</div> + </div> + </template> + + <template #default="{ items }"> + <div class="_gaps_s"> + <div v-for="item in items" :key="item.user.id" :class="$style.userItem"> + <MkA :class="$style.user" :to="`/user-info/${item.user.id}`"> + <MkUserCardMini :user="item.user"/> + </MkA> + <button class="_button" :class="$style.unassign" @click="unassign(item.user, $event)"><i class="ti ti-x"></i></button> + </div> + </div> + </template> + </MkPagination> </div> </MkFolder> <MkInfo v-else>{{ i18n.ts._role.isConditionalRole }}</MkInfo> @@ -47,6 +60,7 @@ import { useRouter } from '@/router'; import MkButton from '@/components/MkButton.vue'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkInfo from '@/components/MkInfo.vue'; +import MkPagination, { Paging } from '@/components/MkPagination.vue'; const router = useRouter(); @@ -54,6 +68,14 @@ const props = defineProps<{ id?: string; }>(); +const usersPagination = { + endpoint: 'admin/roles/users' as const, + limit: 20, + params: computed(() => ({ + roleId: props.id, + })), +}; + const role = reactive(await os.api('admin/roles/show', { roleId: props.id, })); @@ -114,6 +136,7 @@ definePageMetadata(computed(() => ({ .user { flex: 1; + min-width: 0; } .unassign { diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 19a0f6617f..d89f0d2a7d 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -133,7 +133,7 @@ </div> </MkFolder> <div class="_gaps_s"> - <MkRolePreview v-for="role in roles" :key="role.id" :role="role"/> + <MkRolePreview v-for="role in roles" :key="role.id" :role="role" :for-moderation="true"/> </div> </div> </MkSpacer> diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue index 44c79b623a..cf803d6c7f 100644 --- a/packages/frontend/src/pages/antenna-timeline.vue +++ b/packages/frontend/src/pages/antenna-timeline.vue @@ -4,7 +4,7 @@ <div ref="rootEl" v-hotkey.global="keymap" class="tqmomfks"> <div v-if="queue > 0" class="new"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div> <div class="tl"> - <XTimeline + <MkTimeline ref="tlEl" :key="antennaId" class="tl" src="antenna" @@ -19,7 +19,7 @@ <script lang="ts" setup> import { computed, watch } from 'vue'; -import XTimeline from '@/components/MkTimeline.vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import { scroll } from '@/scripts/scroll'; import * as os from '@/os'; import { useRouter } from '@/router'; @@ -35,7 +35,7 @@ const props = defineProps<{ let antenna = $ref(null); let queue = $ref(0); let rootEl = $shallowRef<HTMLElement>(); -let tlEl = $shallowRef<InstanceType<typeof XTimeline>>(); +let tlEl = $shallowRef<InstanceType<typeof MkTimeline>>(); const keymap = $computed(() => ({ 't': focus, })); diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue index 50afffc460..4f8afb9ea2 100644 --- a/packages/frontend/src/pages/auth.vue +++ b/packages/frontend/src/pages/auth.vue @@ -1,12 +1,12 @@ <template> <MkStickyContainer> - <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs" /></template> + <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="500"> <div v-if="state == 'fetch-session-error'"> <p>{{ i18n.ts.somethingHappened }}</p> </div> <div v-else-if="$i && !session"> - <MkLoading /> + <MkLoading/> </div> <div v-else-if="$i && session"> <XForm @@ -21,15 +21,16 @@ </div> <div v-if="state == 'accepted' && session"> <h1>{{ session.app.isAuthorized ? $t('already-authorized') : i18n.ts.allowed }}</h1> - <p v-if="session.app.callbackUrl">{{ i18n.ts._auth.callback }} - <MkEllipsis /> + <p v-if="session.app.callbackUrl"> + {{ i18n.ts._auth.callback }} + <MkEllipsis/> </p> <p v-if="!session.app.callbackUrl">{{ i18n.ts._auth.pleaseGoBack }}</p> </div> </div> <div v-else> <p :class="$style.loginMessage">{{ i18n.ts._auth.pleaseLogin }}</p> - <MkSignin @login="onLogin" /> + <MkSignin @login="onLogin"/> </div> </MkSpacer> </MkStickyContainer> @@ -37,12 +38,12 @@ <script lang="ts" setup> import { onMounted } from 'vue'; +import { AuthSession } from 'misskey-js/built/entities'; import XForm from './auth.form.vue'; import MkSignin from '@/components/MkSignin.vue'; import * as os from '@/os'; import { $i, login } from '@/account'; import { definePageMetadata } from '@/scripts/page-metadata'; -import { AuthSession } from 'misskey-js/built/entities'; import { i18n } from '@/i18n'; const props = defineProps<{ @@ -82,7 +83,7 @@ onMounted(async () => { } else { state = 'waiting'; } - } catch (e) { + } catch (err) { state = 'fetch-session-error'; } }); diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue index 0bfc772353..6b4fcb32f8 100644 --- a/packages/frontend/src/pages/channel.vue +++ b/packages/frontend/src/pages/channel.vue @@ -1,9 +1,9 @@ <template> <MkStickyContainer> - <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> + <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700"> - <div v-if="channel"> - <div class="wpgynlbz _panel _margin" :class="{ hide: !showBanner }"> + <div v-if="channel && tab === 'timeline'" class="_gaps"> + <div class="wpgynlbz _panel" :class="{ hide: !showBanner }"> <XChannelFollowButton :channel="channel" :full="true" class="subscribe"/> <button class="_button toggle" @click="() => showBanner = !showBanner"> <template v-if="showBanner"><i class="ti ti-chevron-up"></i></template> @@ -23,9 +23,13 @@ </div> </div> - <MkPostForm v-if="$i" :channel="channel" class="post-form _panel _margin" fixed/> + <!-- スマホ・タブレットã®å ´åˆã€ã‚ーボードãŒè¡¨ç¤ºã•ã‚Œã‚‹ã¨æŠ•ç¨¿ãŒè¦‹ã¥ã‚‰ããªã‚‹ã®ã§ã€ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—å ´åˆã®ã¿è‡ªå‹•ã§ãƒ•ォーカスを当ã¦ã‚‹ --> + <MkPostForm v-if="$i" :channel="channel" class="post-form _panel" fixed :autofocus="deviceKind === 'desktop'"/> - <XTimeline :key="channelId" class="_margin" src="channel" :channel="channelId" @before="before" @after="after"/> + <MkTimeline :key="channelId" src="channel" :channel="channelId" @before="before" @after="after"/> + </div> + <div v-else-if="tab === 'featured'"> + <MkNotes :pagination="featuredPagination"/> </div> </MkSpacer> </MkStickyContainer> @@ -34,13 +38,15 @@ <script lang="ts" setup> import { computed, watch } from 'vue'; import MkPostForm from '@/components/MkPostForm.vue'; -import XTimeline from '@/components/MkTimeline.vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import XChannelFollowButton from '@/components/MkChannelFollowButton.vue'; import * as os from '@/os'; import { useRouter } from '@/router'; import { $i } from '@/account'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { deviceKind } from '@/scripts/device-kind'; +import MkNotes from '@/components/MkNotes.vue'; const router = useRouter(); @@ -48,15 +54,17 @@ const props = defineProps<{ channelId: string; }>(); +let tab = $ref('timeline'); let channel = $ref(null); let showBanner = $ref(true); -const pagination = { - endpoint: 'channels/timeline' as const, +const featuredPagination = $computed(() => ({ + endpoint: 'notes/featured' as const, limit: 10, - params: computed(() => ({ + offsetMode: true, + params: { channelId: props.channelId, - })), -}; + }, +})); watch(() => props.channelId, async () => { channel = await os.api('channels/show', { @@ -74,7 +82,15 @@ const headerActions = $computed(() => channel && channel.userId ? [{ handler: edit, }] : null); -const headerTabs = $computed(() => []); +const headerTabs = $computed(() => [{ + key: 'timeline', + title: i18n.ts.timeline, + icon: 'ti ti-home', +}, { + key: 'featured', + title: i18n.ts.featured, + icon: 'ti ti-bolt', +}]); definePageMetadata(computed(() => channel ? { title: channel.name, diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue index cd9cec0d4f..d4e8f27005 100644 --- a/packages/frontend/src/pages/clip.vue +++ b/packages/frontend/src/pages/clip.vue @@ -12,7 +12,7 @@ </div> </div> - <XNotes :pagination="pagination" :detail="true"/> + <MkNotes :pagination="pagination" :detail="true"/> </div> </MkSpacer> </MkStickyContainer> @@ -21,7 +21,7 @@ <script lang="ts" setup> import { computed, watch, provide } from 'vue'; import * as misskey from 'misskey-js'; -import XNotes from '@/components/MkNotes.vue'; +import MkNotes from '@/components/MkNotes.vue'; import { $i } from '@/account'; import { i18n } from '@/i18n'; import * as os from '@/os'; diff --git a/packages/frontend/src/pages/explore.featured.vue b/packages/frontend/src/pages/explore.featured.vue index 18a371a086..a972ae04ec 100644 --- a/packages/frontend/src/pages/explore.featured.vue +++ b/packages/frontend/src/pages/explore.featured.vue @@ -4,13 +4,13 @@ <option value="notes">{{ i18n.ts.notes }}</option> <option value="polls">{{ i18n.ts.poll }}</option> </MkTab> - <XNotes v-if="tab === 'notes'" :pagination="paginationForNotes"/> - <XNotes v-else-if="tab === 'polls'" :pagination="paginationForPolls"/> + <MkNotes v-if="tab === 'notes'" :pagination="paginationForNotes"/> + <MkNotes v-else-if="tab === 'polls'" :pagination="paginationForPolls"/> </MkSpacer> </template> <script lang="ts" setup> -import XNotes from '@/components/MkNotes.vue'; +import MkNotes from '@/components/MkNotes.vue'; import MkTab from '@/components/MkTab.vue'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/pages/explore.roles.vue b/packages/frontend/src/pages/explore.roles.vue new file mode 100644 index 0000000000..8be11008c2 --- /dev/null +++ b/packages/frontend/src/pages/explore.roles.vue @@ -0,0 +1,22 @@ +<template> +<MkSpacer :content-max="1200"> + <div class="_gaps_s"> + <MkRolePreview v-for="role in roles" :key="role.id" :role="role" :for-moderation="false"/> + </div> +</MkSpacer> +</template> + +<script lang="ts" setup> +import { } from 'vue'; +import MkRolePreview from '@/components/MkRolePreview.vue'; +import * as os from '@/os'; + +let roles = $ref(); + +os.api('roles/list', { + limit: 30, +}).then(res => { + roles = res; +}); +</script> + diff --git a/packages/frontend/src/pages/explore.users.vue b/packages/frontend/src/pages/explore.users.vue index 4d6ac7d710..c441407d97 100644 --- a/packages/frontend/src/pages/explore.users.vue +++ b/packages/frontend/src/pages/explore.users.vue @@ -8,19 +8,19 @@ <template v-if="tag == null"> <MkFoldableSection class="_margin" persist-key="explore-pinned-users"> <template #header><i class="ti ti-bookmark ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.pinnedUsers }}</template> - <XUserList :pagination="pinnedUsers"/> + <MkUserList :pagination="pinnedUsers"/> </MkFoldableSection> <MkFoldableSection class="_margin" persist-key="explore-popular-users"> <template #header><i class="ti ti-chart-line ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template> - <XUserList :pagination="popularUsers"/> + <MkUserList :pagination="popularUsers"/> </MkFoldableSection> <MkFoldableSection class="_margin" persist-key="explore-recently-updated-users"> <template #header><i class="ti ti-message ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template> - <XUserList :pagination="recentlyUpdatedUsers"/> + <MkUserList :pagination="recentlyUpdatedUsers"/> </MkFoldableSection> <MkFoldableSection class="_margin" persist-key="explore-recently-registered-users"> <template #header><i class="ti ti-plus ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyRegisteredUsers }}</template> - <XUserList :pagination="recentlyRegisteredUsers"/> + <MkUserList :pagination="recentlyRegisteredUsers"/> </MkFoldableSection> </template> </div> @@ -29,28 +29,28 @@ <template #header><i class="ti ti-hash ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularTags }}</template> <div class="vxjfqztj"> - <MkA v-for="tag in tagsLocal" :key="'local:' + tag.tag" :to="`/explore/tags/${tag.tag}`" class="local">{{ tag.tag }}</MkA> - <MkA v-for="tag in tagsRemote" :key="'remote:' + tag.tag" :to="`/explore/tags/${tag.tag}`">{{ tag.tag }}</MkA> + <MkA v-for="tag in tagsLocal" :key="'local:' + tag.tag" :to="`/user-tags/${tag.tag}`" class="local">{{ tag.tag }}</MkA> + <MkA v-for="tag in tagsRemote" :key="'remote:' + tag.tag" :to="`/user-tags/${tag.tag}`">{{ tag.tag }}</MkA> </div> </MkFoldableSection> <MkFoldableSection v-if="tag != null" :key="`${tag}`" class="_margin"> <template #header><i class="ti ti-hash ti-fw" style="margin-right: 0.5em;"></i>{{ tag }}</template> - <XUserList :pagination="tagUsers"/> + <MkUserList :pagination="tagUsers"/> </MkFoldableSection> <template v-if="tag == null"> <MkFoldableSection class="_margin"> <template #header><i class="ti ti-chart-line ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template> - <XUserList :pagination="popularUsersF"/> + <MkUserList :pagination="popularUsersF"/> </MkFoldableSection> <MkFoldableSection class="_margin"> <template #header><i class="ti ti-message ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template> - <XUserList :pagination="recentlyUpdatedUsersF"/> + <MkUserList :pagination="recentlyUpdatedUsersF"/> </MkFoldableSection> <MkFoldableSection class="_margin"> <template #header><i class="ti ti-rocket ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyDiscoveredUsers }}</template> - <XUserList :pagination="recentlyRegisteredUsersF"/> + <MkUserList :pagination="recentlyRegisteredUsersF"/> </MkFoldableSection> </template> </div> @@ -59,7 +59,7 @@ <script lang="ts" setup> import { watch } from 'vue'; -import XUserList from '@/components/MkUserList.vue'; +import MkUserList from '@/components/MkUserList.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkTab from '@/components/MkTab.vue'; import * as os from '@/os'; diff --git a/packages/frontend/src/pages/explore.vue b/packages/frontend/src/pages/explore.vue index dd1685f272..2131188dde 100644 --- a/packages/frontend/src/pages/explore.vue +++ b/packages/frontend/src/pages/explore.vue @@ -8,22 +8,8 @@ <div v-else-if="tab === 'users'"> <XUsers/> </div> - <div v-else-if="tab === 'search'"> - <MkSpacer :content-max="1200"> - <div> - <MkInput v-model="searchQuery" :debounce="true" type="search"> - <template #prefix><i class="ti ti-search"></i></template> - <template #label>{{ i18n.ts.searchUser }}</template> - </MkInput> - <MkRadios v-model="searchOrigin"> - <option value="combined">{{ i18n.ts.all }}</option> - <option value="local">{{ i18n.ts.local }}</option> - <option value="remote">{{ i18n.ts.remote }}</option> - </MkRadios> - </div> - - <XUserList v-if="searchQuery" ref="searchEl" class="_margin" :pagination="searchPagination"/> - </MkSpacer> + <div v-else-if="tab === 'roles'"> + <XRoles/> </div> </div> </MkStickyContainer> @@ -33,12 +19,10 @@ import { computed, watch } from 'vue'; 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 MkInput from '@/components/MkInput.vue'; -import MkRadios from '@/components/MkRadios.vue'; import { definePageMetadata } from '@/scripts/page-metadata'; import { i18n } from '@/i18n'; -import XUserList from '@/components/MkUserList.vue'; const props = withDefaults(defineProps<{ tag?: string; @@ -49,22 +33,11 @@ const props = withDefaults(defineProps<{ let tab = $ref(props.initialTab); let tagsEl = $shallowRef<InstanceType<typeof MkFoldableSection>>(); -let searchQuery = $ref(null); -let searchOrigin = $ref('combined'); watch(() => props.tag, () => { if (tagsEl) tagsEl.toggleContent(props.tag == null); }); -const searchPagination = { - endpoint: 'users/search' as const, - limit: 10, - params: computed(() => (searchQuery && searchQuery !== '') ? { - query: searchQuery, - origin: searchOrigin, - } : null), -}; - const headerActions = $computed(() => []); const headerTabs = $computed(() => [{ @@ -76,8 +49,9 @@ const headerTabs = $computed(() => [{ icon: 'ti ti-users', title: i18n.ts.users, }, { - key: 'search', - title: i18n.ts.search, + key: 'roles', + icon: 'ti ti-badges', + title: i18n.ts.roles, }]); definePageMetadata(computed(() => ({ diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue index 297286176b..165e357ebd 100644 --- a/packages/frontend/src/pages/note.vue +++ b/packages/frontend/src/pages/note.vue @@ -6,7 +6,7 @@ <Transition :name="$store.state.animation ? 'fade' : ''" mode="out-in"> <div v-if="note" class="note"> <div v-if="showNext" class="_margin"> - <XNotes class="" :pagination="nextPagination" :no-gap="true"/> + <MkNotes class="" :pagination="nextPagination" :no-gap="true"/> </div> <div class="main _margin"> @@ -29,7 +29,7 @@ </div> <div v-if="showPrev" class="_margin"> - <XNotes class="" :pagination="prevPagination" :no-gap="true"/> + <MkNotes class="" :pagination="prevPagination" :no-gap="true"/> </div> </div> <MkError v-else-if="error" @retry="fetchNote()"/> @@ -44,7 +44,7 @@ import { computed, watch } from 'vue'; import * as misskey from 'misskey-js'; import XNoteDetailed from '@/components/MkNoteDetailed.vue'; -import XNotes from '@/components/MkNotes.vue'; +import MkNotes from '@/components/MkNotes.vue'; import MkRemoteCaution from '@/components/MkRemoteCaution.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue index 7106951de2..da64a4c1e0 100644 --- a/packages/frontend/src/pages/notifications.vue +++ b/packages/frontend/src/pages/notifications.vue @@ -6,10 +6,10 @@ <XNotifications class="notifications" :include-types="includeTypes" :unread-only="unreadOnly"/> </div> <div v-else-if="tab === 'mentions'"> - <XNotes :pagination="mentionsPagination"/> + <MkNotes :pagination="mentionsPagination"/> </div> <div v-else-if="tab === 'directNotes'"> - <XNotes :pagination="directNotesPagination"/> + <MkNotes :pagination="directNotesPagination"/> </div> </MkSpacer> </MkStickyContainer> @@ -17,12 +17,12 @@ <script lang="ts" setup> import { computed } from 'vue'; -import { notificationTypes } from 'misskey-js'; import XNotifications from '@/components/MkNotifications.vue'; -import XNotes from '@/components/MkNotes.vue'; +import MkNotes from '@/components/MkNotes.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { notificationTypes } from '@/const'; let tab = $ref('all'); let includeTypes = $ref<string[] | null>(null); diff --git a/packages/frontend/src/pages/role.vue b/packages/frontend/src/pages/role.vue new file mode 100644 index 0000000000..2e9d3d6169 --- /dev/null +++ b/packages/frontend/src/pages/role.vue @@ -0,0 +1,47 @@ +<template> +<MkStickyContainer> + <template #header><MkPageHeader/></template> + + <MkSpacer :content-max="1200"> + <div class="_gaps_s"> + <div v-if="role">{{ role.description }}</div> + <MkUserList :pagination="users" :extractor="(item) => item.user"/> + </div> + </MkSpacer> +</MkStickyContainer> +</template> + +<script lang="ts" setup> +import { computed, watch } from 'vue'; +import * as os from '@/os'; +import MkUserList from '@/components/MkUserList.vue'; +import { definePageMetadata } from '@/scripts/page-metadata'; + +const props = defineProps<{ + role: string; +}>(); + +let role = $ref(); + +watch(() => props.role, () => { + os.api('roles/show', { + roleId: props.role, + }).then(res => { + role = res; + }); +}, { immediate: true }); + +const users = $computed(() => ({ + endpoint: 'roles/users' as const, + limit: 30, + params: { + roleId: props.role, + }, +})); + +definePageMetadata(computed(() => ({ + title: role?.name, + icon: 'ti ti-badge', +}))); +</script> + diff --git a/packages/frontend/src/pages/search.vue b/packages/frontend/src/pages/search.vue index 7918f9f577..d32bdcd78e 100644 --- a/packages/frontend/src/pages/search.vue +++ b/packages/frontend/src/pages/search.vue @@ -2,18 +2,41 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="800"> - <XNotes ref="notes" :pagination="pagination"/> + <MkInput v-model="searchQuery" :large="true" :autofocus="true" :debounce="true" type="search" style="margin-bottom: var(--margin);" @update:model-value="search()"> + <template #prefix><i class="ti ti-search"></i></template> + </MkInput> + <MkTab v-model="searchType" style="margin-bottom: var(--margin);" @update:model-value="search()"> + <option value="note">{{ i18n.ts.note }}</option> + <option value="user">{{ i18n.ts.user }}</option> + </MkTab> + + <div v-if="searchType === 'note'"> + <MkNotes v-if="searchQuery" ref="notes" :pagination="notePagination"/> + </div> + <div v-else> + <MkRadios v-model="searchOrigin" style="margin-bottom: var(--margin);" @update:model-value="search()"> + <option value="combined">{{ i18n.ts.all }}</option> + <option value="local">{{ i18n.ts.local }}</option> + <option value="remote">{{ i18n.ts.remote }}</option> + </MkRadios> + + <MkUserList v-if="searchQuery" ref="users" :pagination="userPagination"/> + </div> </MkSpacer> </MkStickyContainer> </template> <script lang="ts" setup> -import { computed } from 'vue'; -import XNotes from '@/components/MkNotes.vue'; +import { computed, onMounted } from 'vue'; +import MkNotes from '@/components/MkNotes.vue'; +import MkUserList from '@/components/MkUserList.vue'; +import MkInput from '@/components/MkInput.vue'; +import MkTab from '@/components/MkTab.vue'; +import MkRadios from '@/components/MkRadios.vue'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; import * as os from '@/os'; -import { useRouter } from '@/router'; +import { useRouter, mainRouter } from '@/router'; import { $i } from '@/account'; const router = useRouter(); @@ -21,14 +44,63 @@ const router = useRouter(); const props = defineProps<{ query: string; channel?: string; + type?: string; + origin?: string; }>(); -const query = props.query; +let searchQuery = $ref(''); +let searchType = $ref('note'); +let searchOrigin = $ref('combined'); + +onMounted(() => { + searchQuery = props.query ?? ''; + searchType = props.type ?? 'note'; + searchOrigin = props.origin ?? 'combined'; + + if (searchQuery) { + search(); + } +}); + +const search = async () => { + const query = searchQuery.toString().trim(); + + if (query == null || query === '') return; + + if (query.startsWith('@') && !query.includes(' ')) { + mainRouter.push(`/${query}`); + return; + } + + if (query.startsWith('#')) { + mainRouter.push(`/tags/${encodeURIComponent(query.substr(1))}`); + return; + } + + // like 2018/03/12 + if (/^[0-9]{4}\/[0-9]{2}\/[0-9]{2}/.test(query.replace(/-/g, '/'))) { + const date = new Date(query.replace(/-/g, '/')); + + // 日付ã—ã‹æŒ‡å®šã•れã¦ãªã„å ´åˆã€ä¾‹ãˆã° 2018/03/12 ãªã‚‰ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ + // 2018/03/12 ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„を「å«ã‚€ã€çµæžœã«ãªã‚‹ã“ã¨ã‚’期待ã™ã‚‹ã¯ãšãªã®ã§ + // 23時間59分進ã‚ã‚‹(ãã®ã¾ã¾ã 㨠2018/03/12 00:00:00 「ã¾ã§ã€ã® + // çµæžœã«ãªã£ã¦ã—ã¾ã„ã€2018/03/12 ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã¯å«ã¾ã‚Œãªã„) + if (query.replace(/-/g, '/').match(/^[0-9]{4}\/[0-9]{2}\/[0-9]{2}$/)) { + date.setHours(23, 59, 59, 999); + } + + // TODO + //v.$root.$emit('warp', date); + os.alert({ + icon: 'ti ti-history', + iconOnly: true, autoClose: true, + }); + return; + } -if ($i != null) { - if (query.startsWith('https://') || (query.startsWith('@') && !query.includes(' '))) { + if (query.startsWith('https://')) { const promise = os.api('ap/show', { - uri: props.query, + uri: query, }); os.promiseDialog(promise, null, null, i18n.ts.fetchingAsApObject); @@ -36,28 +108,58 @@ if ($i != null) { const res = await promise; if (res.type === 'User') { - router.replace(`/@${res.object.username}@${res.object.host}`); + mainRouter.push(`/@${res.object.username}@${res.object.host}`); } else if (res.type === 'Note') { - router.replace(`/notes/${res.object.id}`); + mainRouter.push(`/notes/${res.object.id}`); } + + return; } -} -const pagination = { + if ($i != null) { + if (query.startsWith('https://') || (query.startsWith('@') && !query.includes(' '))) { + const promise = os.api('ap/show', { + uri: query, + }); + + os.promiseDialog(promise, null, null, i18n.ts.fetchingAsApObject); + + const res = await promise; + + if (res.type === 'User') { + router.replace(`/@${res.object.username}@${res.object.host}`); + } else if (res.type === 'Note') { + router.replace(`/notes/${res.object.id}`); + } + } + } + + window.history.replaceState('', '', `/search?q=${encodeURIComponent(query)}&type=${searchType}${searchType === 'user' ? `&origin=${searchOrigin}` : ''}`); +}; + +const notePagination = { endpoint: 'notes/search' as const, limit: 10, params: computed(() => ({ - query: props.query, + query: searchQuery, channelId: props.channel, })), }; +const userPagination = { + endpoint: 'users/search' as const, + limit: 10, + params: computed(() => ({ + query: searchQuery, + origin: searchOrigin, + })), +}; const headerActions = $computed(() => []); const headerTabs = $computed(() => []); definePageMetadata(computed(() => ({ - title: i18n.t('searchWith', { q: props.query }), + title: searchQuery ? i18n.t('searchWith', { q: searchQuery }) : i18n.ts.search, icon: 'ti ti-search', }))); </script> diff --git a/packages/frontend/src/pages/settings/2fa.qrdialog.vue b/packages/frontend/src/pages/settings/2fa.qrdialog.vue new file mode 100644 index 0000000000..1d836db5f5 --- /dev/null +++ b/packages/frontend/src/pages/settings/2fa.qrdialog.vue @@ -0,0 +1,82 @@ +<template> +<MkModal + ref="dialogEl" + :prefer-type="'dialog'" + :z-priority="'low'" + @click="cancel" + @close="cancel" + @closed="emit('closed')" +> + <div :class="$style.root" class="_gaps_m"> + <I18n :src="i18n.ts._2fa.step1" tag="div"> + <template #a> + <a href="https://authy.com/" rel="noopener" target="_blank" class="_link">Authy</a> + </template> + <template #b> + <a href="https://support.google.com/accounts/answer/1066447" rel="noopener" target="_blank" class="_link">Google Authenticator</a> + </template> + </I18n> + <div> + {{ i18n.ts._2fa.step2 }}<br> + {{ i18n.ts._2fa.step2Click }} + </div> + <a :href="twoFactorData.url"><img :class="$style.qr" :src="twoFactorData.qr"></a> + <MkKeyValue :copy="twoFactorData.url"> + <template #key>{{ i18n.ts._2fa.step2Url }}</template> + <template #value>{{ twoFactorData.url }}</template> + </MkKeyValue> + <div class="_buttons"> + <MkButton primary @click="ok">{{ i18n.ts.next }}</MkButton> + <MkButton @click="cancel">{{ i18n.ts.cancel }}</MkButton> + </div> + </div> +</MkModal> +</template> + +<script lang="ts" setup> +import MkButton from '@/components/MkButton.vue'; +import MkModal from '@/components/MkModal.vue'; +import MkKeyValue from '@/components/MkKeyValue.vue'; +import { i18n } from '@/i18n'; + +defineProps<{ + twoFactorData: { + qr: string; + url: string; + }; +}>(); + +const emit = defineEmits<{ + (ev: 'ok'): void; + (ev: 'cancel'): void; + (ev: 'closed'): void; +}>(); + +const cancel = () => { + emit('cancel'); + emit('closed'); +}; + +const ok = () => { + emit('ok'); + emit('closed'); +}; +</script> + +<style lang="scss" module> +.root { + position: relative; + margin: auto; + padding: 32px; + min-width: 320px; + max-width: calc(100svw - 64px); + box-sizing: border-box; + background: var(--panel); + border-radius: var(--radius); +} + +.qr { + width: 20em; + max-width: 100%; +} +</style> diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue index e6ef09668c..891934d706 100644 --- a/packages/frontend/src/pages/settings/2fa.vue +++ b/packages/frontend/src/pages/settings/2fa.vue @@ -1,216 +1,258 @@ <template> -<div> - <MkButton v-if="!twoFactorData && !$i.twoFactorEnabled" @click="register">{{ i18n.ts._2fa.registerDevice }}</MkButton> - <template v-if="$i.twoFactorEnabled"> - <p>{{ i18n.ts._2fa.alreadyRegistered }}</p> - <MkButton @click="unregister">{{ i18n.ts.unregister }}</MkButton> +<FormSection :first="first"> + <template #label>{{ i18n.ts['2fa'] }}</template> - <template v-if="supportsCredentials"> - <hr class="totp-method-sep"> - - <h2 class="heading">{{ i18n.ts.securityKey }}</h2> - <p>{{ i18n.ts._2fa.securityKeyInfo }}</p> - <div class="key-list"> - <div v-for="key in $i.securityKeysList" class="key"> - <h3>{{ key.name }}</h3> - <div class="last-used">{{ i18n.ts.lastUsed }}<MkTime :time="key.lastUsed"/></div> - <MkButton @click="unregisterKey(key)">{{ i18n.ts.unregister }}</MkButton> - </div> + <div v-if="$i" class="_gaps_s"> + <MkFolder> + <template #icon><i class="ti ti-shield-lock"></i></template> + <template #label>{{ i18n.ts.totp }}</template> + <template #caption>{{ i18n.ts.totpDescription }}</template> + <div v-if="$i.twoFactorEnabled" class="_gaps_s"> + <div v-text="i18n.ts._2fa.alreadyRegistered"/> + <template v-if="$i.securityKeysList.length > 0"> + <MkButton @click="renewTOTP">{{ i18n.ts._2fa.renewTOTP }}</MkButton> + <MkInfo>{{ i18n.ts._2fa.whyTOTPOnlyRenew }}</MkInfo> + </template> + <MkButton v-else @click="unregisterTOTP">{{ i18n.ts.unregister }}</MkButton> </div> - <MkSwitch v-if="$i.securityKeysList.length > 0" v-model="usePasswordLessLogin" @update:model-value="updatePasswordLessLogin">{{ i18n.ts.passwordLessLogin }}</MkSwitch> + <MkButton v-else-if="!twoFactorData && !$i.twoFactorEnabled" @click="registerTOTP">{{ i18n.ts._2fa.registerTOTP }}</MkButton> + </MkFolder> + + <MkFolder> + <template #icon><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.securityKeyAndPasskey }}</template> + <div class="_gaps_s"> + <MkInfo> + {{ i18n.ts._2fa.securityKeyInfo }}<br> + <br> + {{ i18n.ts._2fa.chromePasskeyNotSupported }} + </MkInfo> - <MkInfo v-if="registration && registration.error" warn>{{ i18n.ts.error }} {{ registration.error }}</MkInfo> - <MkButton v-if="!registration || registration.error" @click="addSecurityKey">{{ i18n.ts._2fa.registerKey }}</MkButton> + <MkInfo v-if="!supportsCredentials" warn> + {{ i18n.ts._2fa.securityKeyNotSupported }} + </MkInfo> - <ol v-if="registration && !registration.error"> - <li v-if="registration.stage >= 0"> - {{ i18n.ts.tapSecurityKey }} - <MkLoading v-if="registration.saving && registration.stage == 0" :em="true"/> - </li> - <li v-if="registration.stage >= 1"> - <MkForm :disabled="registration.stage != 1 || registration.saving"> - <MkInput v-model="keyName" :max="30"> - <template #label>{{ i18n.ts.securityKeyName }}</template> - </MkInput> - <MkButton :disabled="keyName.length == 0" @click="registerKey">{{ i18n.ts.registerSecurityKey }}</MkButton> - <MkLoading v-if="registration.saving && registration.stage == 1" :em="true"/> - </MkForm> - </li> - </ol> - </template> - </template> - <div v-if="twoFactorData && !$i.twoFactorEnabled"> - <ol style="margin: 0; padding: 0 0 0 1em;"> - <li> - <I18n :src="i18n.ts._2fa.step1" tag="span"> - <template #a> - <a href="https://authy.com/" rel="noopener" target="_blank" class="_link">Authy</a> - </template> - <template #b> - <a href="https://support.google.com/accounts/answer/1066447" rel="noopener" target="_blank" class="_link">Google Authenticator</a> - </template> - </I18n> - </li> - <li>{{ i18n.ts._2fa.step2 }}<br><img :src="twoFactorData.qr"><p>{{ $ts._2fa.step2Url }}<br>{{ twoFactorData.url }}</p></li> - <li> - {{ i18n.ts._2fa.step3 }}<br> - <MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" :spellcheck="false"><template #label>{{ i18n.ts.token }}</template></MkInput> - <MkButton primary @click="submit">{{ i18n.ts.done }}</MkButton> - </li> - </ol> - <MkInfo>{{ i18n.ts._2fa.step4 }}</MkInfo> + <MkInfo v-else-if="supportsCredentials && !$i.twoFactorEnabled" warn> + {{ i18n.ts._2fa.registerTOTPBeforeKey }} + </MkInfo> + + <template v-else> + <MkButton primary @click="addSecurityKey">{{ i18n.ts._2fa.registerSecurityKey }}</MkButton> + <MkFolder v-for="key in $i.securityKeysList" :key="key.id"> + <template #label>{{ key.name }}</template> + <template #suffix><I18n :src="i18n.ts.lastUsedAt"><template #t><MkTime :time="key.lastUsed"/></template></I18n></template> + <div class="_buttons"> + <MkButton @click="renameKey(key)"><i class="ti ti-forms"></i> {{ i18n.ts.rename }}</MkButton> + <MkButton danger @click="unregisterKey(key)"><i class="ti ti-trash"></i> {{ i18n.ts.unregister }}</MkButton> + </div> + </MkFolder> + </template> + </div> + </MkFolder> + + <MkSwitch :disabled="!$i.twoFactorEnabled || $i.securityKeysList.length === 0" :model-value="usePasswordLessLogin" @update:model-value="v => updatePasswordLessLogin(v)"> + <template #label>{{ i18n.ts.passwordLessLogin }}</template> + <template #caption>{{ i18n.ts.passwordLessLoginDescription }}</template> + </MkSwitch> </div> -</div> +</FormSection> </template> <script lang="ts" setup> -import { ref } from 'vue'; +import { ref, defineAsyncComponent } from 'vue'; import { hostname } from '@/config'; import { byteify, hexify, stringify } from '@/scripts/2fa'; import MkButton from '@/components/MkButton.vue'; import MkInfo from '@/components/MkInfo.vue'; -import MkInput from '@/components/MkInput.vue'; import MkSwitch from '@/components/MkSwitch.vue'; +import FormSection from '@/components/form/section.vue'; +import MkFolder from '@/components/MkFolder.vue'; import * as os from '@/os'; import { $i } from '@/account'; import { i18n } from '@/i18n'; +// メモ: å„エンドãƒã‚¤ãƒ³ãƒˆã¯meUpdatedを発行ã™ã‚‹ãŸã‚ã€refreshAccountã¯ä¸è¦ + +withDefaults(defineProps<{ + first?: boolean; +}>(), { + first: false, +}); + const twoFactorData = ref<any>(null); const supportsCredentials = ref(!!navigator.credentials); -const usePasswordLessLogin = ref($i!.usePasswordLessLogin); -const registration = ref<any>(null); -const keyName = ref(''); -const token = ref(null); +const usePasswordLessLogin = $computed(() => $i!.usePasswordLessLogin); -function register() { - os.inputText({ - title: i18n.ts.password, +async function registerTOTP() { + const password = await os.inputText({ + title: i18n.ts._2fa.registerTOTP, + text: i18n.ts._2fa.passwordToTOTP, type: 'password', - }).then(({ canceled, result: password }) => { - if (canceled) return; - os.api('i/2fa/register', { - password: password, - }).then(data => { - twoFactorData.value = data; - }); + autocomplete: 'current-password', + }); + if (password.canceled) return; + + const twoFactorData = await os.apiWithDialog('i/2fa/register', { + password: password.result, + }); + + const qrdialog = await new Promise<boolean>(res => { + os.popup(defineAsyncComponent(() => import('./2fa.qrdialog.vue')), { + twoFactorData, + }, { + 'ok': () => res(true), + 'cancel': () => res(false), + }, 'closed'); + }); + if (!qrdialog) return; + + const token = await os.inputNumber({ + title: i18n.ts._2fa.step3Title, + text: i18n.ts._2fa.step3, + autocomplete: 'one-time-code', + }); + if (token.canceled) return; + + await os.apiWithDialog('i/2fa/done', { + token: token.result.toString(), + }); + + await os.alert({ + type: 'success', + text: i18n.ts._2fa.step4, }); } -function unregister() { +function unregisterTOTP() { os.inputText({ title: i18n.ts.password, type: 'password', + autocomplete: 'current-password', }).then(({ canceled, result: password }) => { if (canceled) return; - os.api('i/2fa/unregister', { + os.apiWithDialog('i/2fa/unregister', { password: password, - }).then(() => { - usePasswordLessLogin.value = false; - updatePasswordLessLogin(); - }).then(() => { - os.success(); - $i!.twoFactorEnabled = false; + }).catch(error => { + os.alert({ + type: 'error', + text: error, + }); }); }); } -function submit() { - os.api('i/2fa/done', { - token: token.value, - }).then(() => { - os.success(); - $i!.twoFactorEnabled = true; - }).catch(err => { - os.alert({ - type: 'error', - text: err, - }); +function renewTOTP() { + os.confirm({ + type: 'question', + title: i18n.ts._2fa.renewTOTP, + text: i18n.ts._2fa.renewTOTPConfirm, + okText: i18n.ts._2fa.renewTOTPOk, + cancelText: i18n.ts._2fa.renewTOTPCancel, + }).then(({ canceled }) => { + if (canceled) return; + registerTOTP(); }); } -function registerKey() { - registration.value.saving = true; - os.api('i/2fa/key-done', { - password: registration.value.password, - name: keyName.value, - challengeId: registration.value.challengeId, - // we convert each 16 bits to a string to serialise - clientDataJSON: stringify(registration.value.credential.response.clientDataJSON), - attestationObject: hexify(registration.value.credential.response.attestationObject), - }).then(key => { - registration.value = null; - key.lastUsed = new Date(); - os.success(); +async function unregisterKey(key) { + const confirm = await os.confirm({ + type: 'question', + title: i18n.ts._2fa.removeKey, + text: i18n.t('_2fa.removeKeyConfirm', { name: key.name }), }); -} + if (confirm.canceled) return; -function unregisterKey(key) { - os.inputText({ + const password = await os.inputText({ title: i18n.ts.password, type: 'password', - }).then(({ canceled, result: password }) => { - if (canceled) return; - return os.api('i/2fa/remove-key', { - password, - credentialId: key.id, - }).then(() => { - usePasswordLessLogin.value = false; - updatePasswordLessLogin(); - }).then(() => { - os.success(); - }); + autocomplete: 'current-password', + }); + if (password.canceled) return; + + await os.apiWithDialog('i/2fa/remove-key', { + password: password.result, + credentialId: key.id, }); + os.success(); } -function addSecurityKey() { - os.inputText({ +async function renameKey(key) { + const name = await os.inputText({ + title: i18n.ts.rename, + default: key.name, + type: 'text', + minLength: 1, + maxLength: 30, + }); + if (name.canceled) return; + + await os.apiWithDialog('i/2fa/update-key', { + name: name.result, + credentialId: key.id, + }); +} + +async function addSecurityKey() { + const password = await os.inputText({ title: i18n.ts.password, type: 'password', - }).then(({ canceled, result: password }) => { - if (canceled) return; - os.api('i/2fa/register-key', { - password, - }).then(reg => { - registration.value = { - password, - challengeId: reg!.challengeId, - stage: 0, - publicKeyOptions: { - challenge: byteify(reg!.challenge, 'base64'), - rp: { - id: hostname, - name: 'Misskey', - }, - user: { - id: byteify($i!.id, 'ascii'), - name: $i!.username, - displayName: $i!.name, - }, - pubKeyCredParams: [{ alg: -7, type: 'public-key' }], - timeout: 60000, - attestation: 'direct', - }, - saving: true, - }; - return navigator.credentials.create({ - publicKey: registration.value.publicKeyOptions, - }); - }).then(credential => { - registration.value.credential = credential; - registration.value.saving = false; - registration.value.stage = 1; - }).catch(err => { - console.warn('Error while registering?', err); - registration.value.error = err.message; - registration.value.stage = -1; - }); + autocomplete: 'current-password', + }); + if (password.canceled) return; + + const challenge: any = await os.apiWithDialog('i/2fa/register-key', { + password: password.result, + }); + + const name = await os.inputText({ + title: i18n.ts._2fa.registerSecurityKey, + text: i18n.ts._2fa.securityKeyName, + type: 'text', + minLength: 1, + maxLength: 30, + }); + if (name.canceled) return; + + const webAuthnCreation = navigator.credentials.create({ + publicKey: { + challenge: byteify(challenge.challenge, 'base64'), + rp: { + id: hostname, + name: 'Misskey', + }, + user: { + id: byteify($i!.id, 'ascii'), + name: $i!.username, + displayName: $i!.name, + }, + pubKeyCredParams: [{ alg: -7, type: 'public-key' }], + timeout: 60000, + attestation: 'direct', + }, + }) as Promise<PublicKeyCredential & { response: AuthenticatorAttestationResponse; } | null>; + + const credential = await os.promiseDialog( + webAuthnCreation, + null, + () => {}, // ユーザーã®ã‚ャンセルã¯rejectãªã®ã§ã‚¨ãƒ©ãƒ¼ãƒ€ã‚¤ã‚¢ãƒã‚°ã‚’出ã•ãªã„ + i18n.ts._2fa.tapSecurityKey, + ); + if (!credential) return; + + await os.apiWithDialog('i/2fa/key-done', { + password: password.result, + name: name.result, + challengeId: challenge.challengeId, + // we convert each 16 bits to a string to serialise + clientDataJSON: stringify(credential.response.clientDataJSON), + attestationObject: hexify(credential.response.attestationObject), }); } -async function updatePasswordLessLogin() { - await os.api('i/2fa/password-less', { - value: !!usePasswordLessLogin.value, +async function updatePasswordLessLogin(value: boolean) { + await os.apiWithDialog('i/2fa/password-less', { + value, }); } </script> diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index e6752460a8..a5619eab86 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -181,7 +181,7 @@ const menuDef = computed(() => [{ miLocalStorage.removeItem('theme'); miLocalStorage.removeItem('emojis'); miLocalStorage.removeItem('lastEmojisFetchedAt'); - await fetchCustomEmojis(); + await fetchCustomEmojis(true); unisonReload(); }, }, { diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index f64202fff2..2cf2f6d7f6 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -27,7 +27,6 @@ <script lang="ts" setup> import { defineAsyncComponent } from 'vue'; -import { notificationTypes } from 'misskey-js'; import FormLink from '@/components/form/link.vue'; import FormSection from '@/components/form/section.vue'; import MkSwitch from '@/components/MkSwitch.vue'; @@ -36,6 +35,7 @@ import { $i } from '@/account'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue'; +import { notificationTypes } from '@/const'; let allowButton = $shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>(); let pushRegistrationInServer = $computed(() => allowButton?.pushRegistrationInServer); diff --git a/packages/frontend/src/pages/settings/plugin.vue b/packages/frontend/src/pages/settings/plugin.vue index b3459a2e2e..8b57dceefb 100644 --- a/packages/frontend/src/pages/settings/plugin.vue +++ b/packages/frontend/src/pages/settings/plugin.vue @@ -4,27 +4,29 @@ <FormSection> <template #label>{{ i18n.ts.manage }}</template> - <div v-for="plugin in plugins" :key="plugin.id" class="_panel _gaps_s" style="padding: 20px;"> - <span style="display: flex;"><b>{{ plugin.name }}</b><span style="margin-left: auto;">v{{ plugin.version }}</span></span> + <div class="_gaps_s"> + <div v-for="plugin in plugins" :key="plugin.id" class="_panel _gaps_s" style="padding: 20px;"> + <span style="display: flex;"><b>{{ plugin.name }}</b><span style="margin-left: auto;">v{{ plugin.version }}</span></span> - <MkSwitch :model-value="plugin.active" @update:model-value="changeActive(plugin, $event)">{{ i18n.ts.makeActive }}</MkSwitch> + <MkSwitch :model-value="plugin.active" @update:model-value="changeActive(plugin, $event)">{{ i18n.ts.makeActive }}</MkSwitch> - <MkKeyValue> - <template #key>{{ i18n.ts.author }}</template> - <template #value>{{ plugin.author }}</template> - </MkKeyValue> - <MkKeyValue> - <template #key>{{ i18n.ts.description }}</template> - <template #value>{{ plugin.description }}</template> - </MkKeyValue> - <MkKeyValue> - <template #key>{{ i18n.ts.permission }}</template> - <template #value>{{ plugin.permission }}</template> - </MkKeyValue> + <MkKeyValue> + <template #key>{{ i18n.ts.author }}</template> + <template #value>{{ plugin.author }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>{{ i18n.ts.description }}</template> + <template #value>{{ plugin.description }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>{{ i18n.ts.permission }}</template> + <template #value>{{ plugin.permission }}</template> + </MkKeyValue> - <div class="_buttons"> - <MkButton v-if="plugin.config" inline @click="config(plugin)"><i class="ti ti-settings"></i> {{ i18n.ts.settings }}</MkButton> - <MkButton inline danger @click="uninstall(plugin)"><i class="ti ti-trash"></i> {{ i18n.ts.uninstall }}</MkButton> + <div class="_buttons"> + <MkButton v-if="plugin.config" inline @click="config(plugin)"><i class="ti ti-settings"></i> {{ i18n.ts.settings }}</MkButton> + <MkButton inline danger @click="uninstall(plugin)"><i class="ti ti-trash"></i> {{ i18n.ts.uninstall }}</MkButton> + </div> </div> </div> </FormSection> diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue index 5692ce80cb..c83c48d5ad 100644 --- a/packages/frontend/src/pages/settings/privacy.vue +++ b/packages/frontend/src/pages/settings/privacy.vue @@ -46,7 +46,7 @@ <option value="followers">{{ i18n.ts._visibility.followers }}</option> <option value="specified">{{ i18n.ts._visibility.specified }}</option> </MkSelect> - <MkSwitch v-model="defaultNoteLocalOnly">{{ i18n.ts._visibility.localOnly }}</MkSwitch> + <MkSwitch v-model="defaultNoteLocalOnly">{{ i18n.ts._visibility.disableFederation }}</MkSwitch> </div> </MkFolder> </div> diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index a492a82588..41563c441f 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -124,11 +124,11 @@ function saveFields() { function save() { os.apiWithDialog('i/update', { - name: profile.name || null, - description: profile.description || null, - location: profile.location || null, - birthday: profile.birthday || null, - lang: profile.lang || null, + name: profile.name ?? null, + description: profile.description ?? null, + location: profile.location ?? null, + birthday: profile.birthday ?? null, + lang: profile.lang ?? null, isBot: !!profile.isBot, isCat: !!profile.isCat, showTimelineReplies: !!profile.showTimelineReplies, diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue index b09c4ffd2f..0cc2df09c5 100644 --- a/packages/frontend/src/pages/settings/security.vue +++ b/packages/frontend/src/pages/settings/security.vue @@ -5,11 +5,8 @@ <MkButton primary @click="change()">{{ i18n.ts.changePassword }}</MkButton> </FormSection> - <FormSection> - <template #label>{{ i18n.ts.twoStepAuthentication }}</template> - <X2fa/> - </FormSection> - + <X2fa/> + <FormSection> <template #label>{{ i18n.ts.signinHistory }}</template> <MkPagination :pagination="pagination" disable-auto-load> @@ -56,18 +53,21 @@ async function change() { const { canceled: canceled1, result: currentPassword } = await os.inputText({ title: i18n.ts.currentPassword, type: 'password', + autocomplete: 'current-password', }); if (canceled1) return; const { canceled: canceled2, result: newPassword } = await os.inputText({ title: i18n.ts.newPassword, type: 'password', + autocomplete: 'new-password', }); if (canceled2) return; const { canceled: canceled3, result: newPassword2 } = await os.inputText({ title: i18n.ts.newPasswordRetype, type: 'password', + autocomplete: 'new-password', }); if (canceled3) return; @@ -109,7 +109,7 @@ definePageMetadata({ <style lang="scss" scoped> .timnmucd { - padding: 16px; + padding: 12px; &:first-child { border-top-left-radius: 6px; diff --git a/packages/frontend/src/pages/tag.vue b/packages/frontend/src/pages/tag.vue index 5d6d01d2ae..511052c424 100644 --- a/packages/frontend/src/pages/tag.vue +++ b/packages/frontend/src/pages/tag.vue @@ -2,14 +2,14 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="800"> - <XNotes class="" :pagination="pagination"/> + <MkNotes class="" :pagination="pagination"/> </MkSpacer> </MkStickyContainer> </template> <script lang="ts" setup> import { computed } from 'vue'; -import XNotes from '@/components/MkNotes.vue'; +import MkNotes from '@/components/MkNotes.vue'; import { definePageMetadata } from '@/scripts/page-metadata'; const props = defineProps<{ diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index f9ad609f56..d982a76d03 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -8,7 +8,7 @@ <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> <div :class="$style.tl"> - <XTimeline + <MkTimeline ref="tlComponent" :key="src" :src="src" @@ -24,7 +24,7 @@ <script lang="ts" setup> import { defineAsyncComponent, computed, watch, provide } from 'vue'; import type { Tab } from '@/components/global/MkPageHeader.tabs.vue'; -import XTimeline from '@/components/MkTimeline.vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import MkPostForm from '@/components/MkPostForm.vue'; import { scroll } from '@/scripts/scroll'; import * as os from '@/os'; @@ -44,7 +44,7 @@ const keymap = { 't': focus, }; -const tlComponent = $shallowRef<InstanceType<typeof XTimeline>>(); +const tlComponent = $shallowRef<InstanceType<typeof MkTimeline>>(); const rootEl = $shallowRef<HTMLElement>(); let queue = $ref(0); diff --git a/packages/frontend/src/pages/user-info.vue b/packages/frontend/src/pages/user-info.vue index b8ed5787cf..13a06286f6 100644 --- a/packages/frontend/src/pages/user-info.vue +++ b/packages/frontend/src/pages/user-info.vue @@ -112,7 +112,7 @@ <MkButton v-if="user.host == null && iAmModerator" primary rounded @click="assignRole"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton> <div v-for="role in info.roles" :key="role.id" :class="$style.roleItem"> - <MkRolePreview :class="$style.role" :role="role"/> + <MkRolePreview :class="$style.role" :role="role" :for-moderation="true"/> <button v-if="role.target === 'manual'" class="_button" :class="$style.roleUnassign" @click="unassignRole(role, $event)"><i class="ti ti-x"></i></button> <button v-else class="_button" :class="$style.roleUnassign" disabled><i class="ti ti-ban"></i></button> </div> diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue index 2bfda12a6e..acf7ea9b2c 100644 --- a/packages/frontend/src/pages/user-list-timeline.vue +++ b/packages/frontend/src/pages/user-list-timeline.vue @@ -4,7 +4,7 @@ <div ref="rootEl" class="eqqrhokj"> <div v-if="queue > 0" class="new"><button class="_buttonPrimary" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> <div class="tl"> - <XTimeline + <MkTimeline ref="tlEl" :key="listId" class="tl" src="list" @@ -19,7 +19,7 @@ <script lang="ts" setup> import { computed, watch } from 'vue'; -import XTimeline from '@/components/MkTimeline.vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import { scroll } from '@/scripts/scroll'; import * as os from '@/os'; import { useRouter } from '@/router'; @@ -34,7 +34,7 @@ const props = defineProps<{ let list = $ref(null); let queue = $ref(0); -let tlEl = $shallowRef<InstanceType<typeof XTimeline>>(); +let tlEl = $shallowRef<InstanceType<typeof MkTimeline>>(); let rootEl = $shallowRef<HTMLElement>(); watch(() => props.listId, async () => { diff --git a/packages/frontend/src/pages/user-tag.vue b/packages/frontend/src/pages/user-tag.vue new file mode 100644 index 0000000000..fac7593e9c --- /dev/null +++ b/packages/frontend/src/pages/user-tag.vue @@ -0,0 +1,38 @@ +<template> +<MkStickyContainer> + <template #header><MkPageHeader/></template> + + <MkSpacer :content-max="1200"> + <div class="_gaps_s"> + <MkUserList :pagination="tagUsers"/> + </div> + </MkSpacer> +</MkStickyContainer> +</template> + +<script lang="ts" setup> +import { computed, watch } from 'vue'; +import * as os from '@/os'; +import MkUserList from '@/components/MkUserList.vue'; +import { definePageMetadata } from '@/scripts/page-metadata'; + +const props = defineProps<{ + tag: string; +}>(); + +const tagUsers = $computed(() => ({ + endpoint: 'hashtags/users' as const, + limit: 30, + params: { + tag: props.tag, + origin: 'combined', + sort: '+follower', + }, +})); + +definePageMetadata(computed(() => ({ + title: props.tag, + icon: 'ti ti-user-search', +}))); +</script> + diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 66c754c452..441b19440c 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -100,7 +100,7 @@ <XPhotos :key="user.id" :user="user"/> <XActivity :key="user.id" :user="user"/> </template> - <XNotes :class="$style.tl" :no-gap="true" :pagination="pagination"/> + <MkNotes :class="$style.tl" :no-gap="true" :pagination="pagination"/> </div> </div> <div v-if="!narrow" class="sub _gaps" style="container-type: inline-size;"> @@ -130,7 +130,7 @@ import { i18n } from '@/i18n'; import { $i } from '@/account'; import { dateString } from '@/filters/date'; import { confetti } from '@/scripts/confetti'; -import XNotes from '@/components/MkNotes.vue'; +import MkNotes from '@/components/MkNotes.vue'; const XPhotos = defineAsyncComponent(() => import('./index.photos.vue')); const XActivity = defineAsyncComponent(() => import('./index.activity.vue')); diff --git a/packages/frontend/src/pages/user/index.timeline.vue b/packages/frontend/src/pages/user/index.timeline.vue index aae55c5f1c..d8fc253910 100644 --- a/packages/frontend/src/pages/user/index.timeline.vue +++ b/packages/frontend/src/pages/user/index.timeline.vue @@ -8,7 +8,7 @@ <option value="files">{{ i18n.ts.withFiles }}</option> </MkTab> </template> - <XNotes :no-gap="true" :pagination="pagination" :class="$style.tl"/> + <MkNotes :no-gap="true" :pagination="pagination" :class="$style.tl"/> </MkStickyContainer> </MkSpacer> </template> @@ -16,7 +16,7 @@ <script lang="ts" setup> import { ref, computed } from 'vue'; import * as misskey from 'misskey-js'; -import XNotes from '@/components/MkNotes.vue'; +import MkNotes from '@/components/MkNotes.vue'; import MkTab from '@/components/MkTab.vue'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue index 05c55fdf31..f62a6461c5 100644 --- a/packages/frontend/src/pages/welcome.entrance.a.vue +++ b/packages/frontend/src/pages/welcome.entrance.a.vue @@ -1,18 +1,18 @@ <template> <div v-if="meta" class="rsqzvsbo"> - <div class="top"> - <MkFeaturedPhotos class="bg"/> - <XTimeline class="tl"/> - <div class="shape1"></div> - <div class="shape2"></div> - <img src="/client-assets/misskey.svg" class="misskey"/> - <div class="emojis"> - <MkEmoji :normal="true" :no-style="true" emoji="ðŸ‘"/> - <MkEmoji :normal="true" :no-style="true" emoji="â¤"/> - <MkEmoji :normal="true" :no-style="true" emoji="😆"/> - <MkEmoji :normal="true" :no-style="true" emoji="🎉"/> - <MkEmoji :normal="true" :no-style="true" emoji="ðŸ®"/> - </div> + <MkFeaturedPhotos class="bg"/> + <XTimeline class="tl"/> + <div class="shape1"></div> + <div class="shape2"></div> + <img src="/client-assets/misskey.svg" class="misskey"/> + <div class="emojis"> + <MkEmoji :normal="true" :no-style="true" emoji="ðŸ‘"/> + <MkEmoji :normal="true" :no-style="true" emoji="â¤"/> + <MkEmoji :normal="true" :no-style="true" emoji="😆"/> + <MkEmoji :normal="true" :no-style="true" emoji="🎉"/> + <MkEmoji :normal="true" :no-style="true" emoji="ðŸ®"/> + </div> + <div class="contents"> <div class="main"> <img :src="$instance.iconUrl || $instance.faviconUrl || '/favicon.ico'" alt="" class="icon"/> <button class="_button _acrylic menu" @click="showMenu"><i class="ti ti-dots"></i></button> @@ -26,37 +26,46 @@ <!-- eslint-disable-next-line vue/no-v-html --> <div class="desc" v-html="meta.description || i18n.ts.headlineMisskey"></div> </div> - <div class="action"> - <MkButton inline rounded gradate data-cy-signup style="margin-right: 12px;" @click="signup()">{{ i18n.ts.signup }}</MkButton> - <MkButton inline rounded data-cy-signin @click="signin()">{{ i18n.ts.login }}</MkButton> + <div class="action _gaps_s"> + <MkButton full rounded gradate data-cy-signup style="margin-right: 12px;" @click="signup()">{{ i18n.ts.joinThisServer }}</MkButton> + <MkButton full rounded @click="exploreOtherServers()">{{ i18n.ts.exploreOtherServers }}</MkButton> + <MkButton full rounded data-cy-signin @click="signin()">{{ i18n.ts.login }}</MkButton> </div> </div> </div> - <div v-if="instances && instances.length > 0" class="federation"> - <MarqueeText :duration="40"> - <MkA v-for="instance in instances" :key="instance.id" :class="$style.federationInstance" :to="`/instance-info/${instance.host}`" behavior="window"> - <!--<MkInstanceCardMini :instance="instance"/>--> - <img v-if="instance.iconUrl" class="icon" :src="instance.iconUrl" alt=""/> - <span class="name _monospace">{{ instance.host }}</span> - </MkA> - </MarqueeText> + <div v-if="instance.policies.ltlAvailable" class="tl"> + <div class="title">{{ i18n.ts.letsLookAtTimeline }}</div> + <div class="body"> + <MkTimeline src="local"/> + </div> </div> </div> + <div v-if="instances && instances.length > 0" class="federation"> + <MarqueeText :duration="40"> + <MkA v-for="instance in instances" :key="instance.id" :class="$style.federationInstance" :to="`/instance-info/${instance.host}`" behavior="window"> + <!--<MkInstanceCardMini :instance="instance"/>--> + <img v-if="instance.iconUrl" class="icon" :src="instance.iconUrl" alt=""/> + <span class="name _monospace">{{ instance.host }}</span> + </MkA> + </MarqueeText> + </div> </div> </template> <script lang="ts" setup> import { } from 'vue'; +import { Instance } from 'misskey-js/built/entities'; import XTimeline from './welcome.timeline.vue'; import MarqueeText from '@/components/MkMarquee.vue'; import XSigninDialog from '@/components/MkSigninDialog.vue'; import XSignupDialog from '@/components/MkSignupDialog.vue'; import MkButton from '@/components/MkButton.vue'; import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import { instanceName } from '@/config'; import * as os from '@/os'; import { i18n } from '@/i18n'; -import { Instance } from 'misskey-js/built/entities'; +import { instance } from '@/instance'; let meta = $ref<Instance>(); let instances = $ref<any[]>(); @@ -105,100 +114,103 @@ function showMenu(ev) { }, }], ev.currentTarget ?? ev.target); } + +function exploreOtherServers() { + // TODO: 言語をよã—ãªã« + window.open('https://join.misskey.page/ja-JP/instances', '_blank'); +} </script> <style lang="scss" scoped> .rsqzvsbo { - > .top { - display: flex; - text-align: center; - min-height: 100vh; - box-sizing: border-box; - padding: 16px; + > .bg { + position: fixed; + top: 0; + right: 0; + width: 80vw; // 100%ã‹ã‚‰shapeã®å¹…を引ã„ã¦ã„ã‚‹ + height: 100vh; + } - > .bg { - position: absolute; - top: 0; - right: 0; - width: 80%; // 100%ã‹ã‚‰shapeã®å¹…を引ã„ã¦ã„ã‚‹ - height: 100%; + > .tl { + position: fixed; + top: 0; + bottom: 0; + right: 64px; + margin: auto; + padding: 128px 0; + width: 500px; + height: calc(100% - 256px); + overflow: hidden; + -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%); + mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%); + + @media (max-width: 1200px) { + display: none; } + } - > .tl { - position: absolute; - top: 0; - bottom: 0; - right: 64px; - margin: auto; - padding: 128px 0; - width: 500px; - height: calc(100% - 256px); - overflow: hidden; - -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%); - mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%); + > .shape1 { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: var(--accent); + clip-path: polygon(0% 0%, 45% 0%, 20% 100%, 0% 100%); + } + > .shape2 { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: var(--accent); + clip-path: polygon(0% 0%, 25% 0%, 35% 100%, 0% 100%); + opacity: 0.5; + } - @media (max-width: 1200px) { - display: none; - } - } + > .misskey { + position: fixed; + top: 42px; + left: 42px; + width: 140px; - > .shape1 { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: var(--accent); - clip-path: polygon(0% 0%, 45% 0%, 20% 100%, 0% 100%); - } - > .shape2 { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: var(--accent); - clip-path: polygon(0% 0%, 25% 0%, 35% 100%, 0% 100%); - opacity: 0.5; + @media (max-width: 450px) { + width: 130px; } + } - > .misskey { - position: absolute; - top: 42px; - left: 42px; - width: 140px; + > .emojis { + position: fixed; + bottom: 32px; + left: 35px; - @media (max-width: 450px) { - width: 130px; - } + > * { + margin-right: 8px; } - > .emojis { - position: absolute; - bottom: 32px; - left: 35px; + @media (max-width: 1200px) { + display: none; + } + } - > * { - margin-right: 8px; - } + > .contents { + position: relative; + width: min(430px, calc(100% - 32px)); + margin-left: 128px; + padding: 150px 0 100px 0; - @media (max-width: 1200px) { - display: none; - } + @media (max-width: 1200px) { + margin: auto; } > .main { position: relative; - width: min(480px, 100%); - margin: auto auto auto 128px; background: var(--panel); border-radius: var(--radius); box-shadow: 0 12px 32px rgb(0 0 0 / 25%); - - @media (max-width: 1200px) { - margin: auto; - } - + text-align: center; + > .icon { width: 85px; margin-top: -47px; @@ -247,25 +259,44 @@ function showMenu(ev) { } } - > .federation { - position: absolute; - bottom: 16px; - left: 0; - right: 0; - margin: auto; - background: var(--acrylicPanel); - -webkit-backdrop-filter: var(--blur, blur(15px)); - backdrop-filter: var(--blur, blur(15px)); - border-radius: 999px; + > .tl { + position: relative; + background: var(--panel); + border-radius: var(--radius); overflow: clip; - width: 800px; - padding: 8px 0; + box-shadow: 0 12px 32px rgb(0 0 0 / 25%); + margin-top: 16px; + + > .title { + padding: 12px 16px; + border-bottom: solid 1px var(--divider); + } - @media (max-width: 900px) { - display: none; + > .body { + height: 350px; + overflow: auto; } } } + + > .federation { + position: fixed; + bottom: 16px; + left: 0; + right: 0; + margin: auto; + background: var(--acrylicPanel); + -webkit-backdrop-filter: var(--blur, blur(15px)); + backdrop-filter: var(--blur, blur(15px)); + border-radius: 999px; + overflow: clip; + width: 800px; + padding: 8px 0; + + @media (max-width: 900px) { + display: none; + } + } } </style> diff --git a/packages/frontend/src/pizzax.ts b/packages/frontend/src/pizzax.ts index 2ca89b7351..2616a8a1d5 100644 --- a/packages/frontend/src/pizzax.ts +++ b/packages/frontend/src/pizzax.ts @@ -48,8 +48,8 @@ export class Storage<T extends StateDef> { // 簡易的ã«ã‚ューイングã—ã¦å 有ãƒãƒƒã‚¯ã¨ã™ã‚‹ private currentIdbJob: Promise<any> = Promise.resolve(); private addIdbSetJob<T>(job: () => Promise<T>) { - const promise = this.currentIdbJob.then(job, e => { - console.error('Pizzax failed to save data to idb!', e); + const promise = this.currentIdbJob.then(job, err => { + console.error('Pizzax failed to save data to idb!', err); return job(); }); this.currentIdbJob = promise; @@ -130,22 +130,22 @@ export class Storage<T extends StateDef> { await defaultStore.ready; api('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']][]) { - if (v.where === 'account') { - if (Object.prototype.hasOwnProperty.call(kvs, k)) { - this.reactiveState[k].value = this.state[k] = (kvs as Partial<T>)[k]; - cache[k] = (kvs as Partial<T>)[k]; - } else { - this.reactiveState[k].value = this.state[k] = v.default; + .then(kvs => { + const cache: Partial<T> = {}; + for (const [k, v] of Object.entries(this.def) as [keyof T, T[keyof T]['default']][]) { + if (v.where === 'account') { + if (Object.prototype.hasOwnProperty.call(kvs, k)) { + this.reactiveState[k].value = this.state[k] = (kvs as Partial<T>)[k]; + cache[k] = (kvs as Partial<T>)[k]; + } else { + this.reactiveState[k].value = this.state[k] = v.default; + } } } - } - return set(this.registryCacheKeyName, cache); - }) - .then(() => resolve()); + return set(this.registryCacheKeyName, cache); + }) + .then(() => resolve()); }, 1); } else { resolve(); diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index ff380df639..70576688b1 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -198,8 +198,11 @@ export const routes = [{ component: page(() => import('./pages/theme-editor.vue')), loginRequired: true, }, { - path: '/explore/tags/:tag', - component: page(() => import('./pages/explore.vue')), + 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')), @@ -210,6 +213,8 @@ export const routes = [{ query: { q: 'query', channel: 'channel', + type: 'type', + origin: 'origin', }, }, { path: '/authorize-follow', diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index 48b740f4c3..9c0ff3d1b2 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -9,6 +9,7 @@ import copyToClipboard from '@/scripts/copy-to-clipboard'; import { url } from '@/config'; import { noteActions } from '@/store'; import { miLocalStorage } from '@/local-storage'; +import { getUserMenu } from '@/scripts/get-user-menu'; export function getNoteMenu(props: { note: misskey.entities.Note; @@ -99,66 +100,6 @@ export function getNoteMenu(props: { }); } - async function clip(): Promise<void> { - const clips = await os.api('clips/list'); - os.popupMenu([{ - icon: 'ti ti-plus', - text: i18n.ts.createNew, - action: async () => { - const { canceled, result } = await os.form(i18n.ts.createNewClip, { - name: { - type: 'string', - label: i18n.ts.name, - }, - description: { - type: 'string', - required: false, - multiline: true, - label: i18n.ts.description, - }, - isPublic: { - type: 'boolean', - label: i18n.ts.public, - default: false, - }, - }); - if (canceled) return; - - const clip = await os.apiWithDialog('clips/create', result); - - claimAchievement('noteClipped1'); - os.apiWithDialog('clips/add-note', { clipId: clip.id, noteId: appearNote.id }); - }, - }, null, ...clips.map(clip => ({ - text: clip.name, - action: () => { - claimAchievement('noteClipped1'); - os.promiseDialog( - os.api('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 }), - }); - if (!confirm.canceled) { - os.apiWithDialog('clips/remove-note', { clipId: clip.id, noteId: appearNote.id }); - if (props.currentClipPage?.value.id === clip.id) props.isDeleted.value = true; - } - } else { - os.alert({ - type: 'error', - text: err.message + '\n' + err.id, - }); - } - }, - ); - }, - }))], props.menuButton.value, { - }).then(focus); - } - async function unclip(): Promise<void> { os.apiWithDialog('clips/remove-note', { clipId: props.currentClipPage.value.id, noteId: appearNote.id }); props.isDeleted.value = true; @@ -200,7 +141,7 @@ export function getNoteMenu(props: { props.translating.value = true; const res = await os.api('notes/translate', { noteId: appearNote.id, - targetLang: miLocalStorage.getItem('lang') || navigator.language, + targetLang: miLocalStorage.getItem('lang') ?? navigator.language, }); props.translating.value = false; props.translation.value = res; @@ -240,7 +181,7 @@ export function getNoteMenu(props: { icon: 'ti ti-external-link', text: i18n.ts.showOnRemote, action: () => { - window.open(appearNote.url || appearNote.uri, '_blank'); + window.open(appearNote.url ?? appearNote.uri, '_blank'); }, } : undefined, { @@ -264,9 +205,67 @@ export function getNoteMenu(props: { action: () => toggleFavorite(true), }), { + type: 'parent', icon: 'ti ti-paperclip', text: i18n.ts.clip, - action: () => clip(), + children: async () => { + const clips = await os.api('clips/list'); + return [{ + icon: 'ti ti-plus', + text: i18n.ts.createNew, + action: async () => { + const { canceled, result } = await os.form(i18n.ts.createNewClip, { + name: { + type: 'string', + label: i18n.ts.name, + }, + description: { + type: 'string', + required: false, + multiline: true, + label: i18n.ts.description, + }, + isPublic: { + type: 'boolean', + label: i18n.ts.public, + default: false, + }, + }); + if (canceled) return; + + const clip = await os.apiWithDialog('clips/create', result); + + claimAchievement('noteClipped1'); + os.apiWithDialog('clips/add-note', { clipId: clip.id, noteId: appearNote.id }); + }, + }, null, ...clips.map(clip => ({ + text: clip.name, + action: () => { + claimAchievement('noteClipped1'); + os.promiseDialog( + os.api('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 }), + }); + if (!confirm.canceled) { + os.apiWithDialog('clips/remove-note', { clipId: clip.id, noteId: appearNote.id }); + if (props.currentClipPage?.value.id === clip.id) props.isDeleted.value = true; + } + } else { + os.alert({ + type: 'error', + text: err.message + '\n' + err.id, + }); + } + }, + ); + }, + }))]; + }, }, statePromise.then(state => state.isMutedThread ? { icon: 'ti ti-message-off', @@ -286,6 +285,15 @@ export function getNoteMenu(props: { text: i18n.ts.pin, action: () => togglePin(true), } : undefined, + appearNote.userId !== $i.id ? { + type: 'parent', + icon: 'ti ti-user', + text: i18n.ts.user, + children: async () => { + const user = await os.api('users/show', { userId: appearNote.userId }); + return getUserMenu(user); + }, + } : undefined, /* ...($i.isModerator || $i.isAdmin ? [ null, @@ -302,7 +310,7 @@ export function getNoteMenu(props: { icon: 'ti ti-exclamation-circle', text: i18n.ts.reportAbuse, action: () => { - const u = appearNote.url || appearNote.uri || `${url}/notes/${appearNote.id}`; + const u = appearNote.url ?? appearNote.uri ?? `${url}/notes/${appearNote.id}`; os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { user: appearNote.user, initialComment: `Note: ${u}\n-----\n`, @@ -344,7 +352,7 @@ export function getNoteMenu(props: { icon: 'ti ti-external-link', text: i18n.ts.showOnRemote, action: () => { - window.open(appearNote.url || appearNote.uri, '_blank'); + window.open(appearNote.url ?? appearNote.uri, '_blank'); }, } : undefined] .filter(x => x !== undefined); diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index 557b257f62..69d0ed085d 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -1,4 +1,5 @@ import { defineAsyncComponent } from 'vue'; +import * as misskey from 'misskey-js'; import { i18n } from '@/i18n'; import copyToClipboard from '@/scripts/copy-to-clipboard'; import { host } from '@/config'; @@ -8,7 +9,7 @@ import { $i, iAmModerator } from '@/account'; import { mainRouter } from '@/router'; import { Router } from '@/nirax'; -export function getUserMenu(user, router: Router = mainRouter) { +export function getUserMenu(user: misskey.entities.UserDetailed, router: Router = mainRouter) { const meId = $i ? $i.id : null; async function pushList() { @@ -102,6 +103,8 @@ export function getUserMenu(user, router: Router = mainRouter) { } async function invalidateFollow() { + if (!await getConfirmed(i18n.ts.breakFollowConfirm)) return; + os.apiWithDialog('following/invalidate', { userId: user.id, }).then(() => { @@ -113,7 +116,7 @@ export function getUserMenu(user, router: Router = mainRouter) { icon: 'ti ti-at', text: i18n.ts.copyUsername, action: () => { - copyToClipboard(`@${user.username}@${user.host || host}`); + copyToClipboard(`@${user.username}@${user.host ?? host}`); }, }, { icon: 'ti ti-info-circle', @@ -166,12 +169,6 @@ export function getUserMenu(user, router: Router = mainRouter) { if (iAmModerator) { menu = menu.concat([null, { - icon: 'ti ti-user-exclamation', - text: i18n.ts.moderation, - action: () => { - router.push('/user-info/' + user.id + '#moderation'); - }, - }, { icon: 'ti ti-badges', text: i18n.ts.roles, action: async () => { diff --git a/packages/frontend/src/scripts/get-user-name.ts b/packages/frontend/src/scripts/get-user-name.ts index d499ea0203..4daf203e06 100644 --- a/packages/frontend/src/scripts/get-user-name.ts +++ b/packages/frontend/src/scripts/get-user-name.ts @@ -1,3 +1,3 @@ export default function(user: { name?: string | null, username: string }): string { - return user.name || user.username; + return user.name === '' ? user.username : user.name ?? user.username; } diff --git a/packages/frontend/src/scripts/hpml/index.ts b/packages/frontend/src/scripts/hpml/index.ts index 5c07a08315..587c6a36c8 100644 --- a/packages/frontend/src/scripts/hpml/index.ts +++ b/packages/frontend/src/scripts/hpml/index.ts @@ -58,7 +58,7 @@ export class HpmlScope { constructor(layerdStates: HpmlScope['layerdStates'], name?: HpmlScope['name']) { this.layerdStates = layerdStates; - this.name = name || 'anonymous'; + this.name = name ?? 'anonymous'; } @autobind diff --git a/packages/frontend/src/scripts/hpml/type-checker.ts b/packages/frontend/src/scripts/hpml/type-checker.ts index 24c9ed8bcb..692826fc90 100644 --- a/packages/frontend/src/scripts/hpml/type-checker.ts +++ b/packages/frontend/src/scripts/hpml/type-checker.ts @@ -63,7 +63,7 @@ export class HpmlTypeChecker { @autobind public getExpectedType(v: Expr, slot: number): Type { - const def = funcDefs[v.type || '']; + const def = funcDefs[v.type ?? '']; if (def == null) { throw new Error('Unknown type: ' + v.type); } @@ -107,7 +107,7 @@ export class HpmlTypeChecker { return pageVar.type; } - const envVar = envVarsDef[v.value || '']; + const envVar = envVarsDef[v.value ?? '']; if (envVar !== undefined) { return envVar; } diff --git a/packages/frontend/src/scripts/scroll.ts b/packages/frontend/src/scripts/scroll.ts index e3d9dc00c2..a002f02b5a 100644 --- a/packages/frontend/src/scripts/scroll.ts +++ b/packages/frontend/src/scripts/scroll.ts @@ -10,7 +10,7 @@ export function getScrollContainer(el: HTMLElement | null): HTMLElement | null { } } -export function getStickyTop(el: HTMLElement, container: HTMLElement | null = null, top: number = 0) { +export function getStickyTop(el: HTMLElement, container: HTMLElement | null = null, top = 0) { if (!el.parentElement) return top; const data = el.dataset.stickyContainerHeaderHeight; const newTop = data ? Number(data) + top : top; @@ -23,14 +23,14 @@ export function getScrollPosition(el: HTMLElement | null): number { return container == null ? window.scrollY : container.scrollTop; } -export function onScrollTop(el: HTMLElement, cb: () => unknown, tolerance: number = 1, once: boolean = false) { +export function onScrollTop(el: HTMLElement, cb: () => unknown, tolerance = 1, once = false) { // ã¨ã‚Šã‚ãˆãšè©•価ã—ã¦ã¿ã‚‹ if (isTopVisible(el)) { cb(); if (once) return null; } - const container = getScrollContainer(el) || window; + const container = getScrollContainer(el) ?? window; const onScroll = ev => { if (!document.body.contains(el)) return; @@ -45,7 +45,7 @@ export function onScrollTop(el: HTMLElement, cb: () => unknown, tolerance: numbe return removeListener; } -export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance: number = 1, once: boolean = false) { +export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance = 1, once = false) { const container = getScrollContainer(el); // ã¨ã‚Šã‚ãˆãšè©•価ã—ã¦ã¿ã‚‹ @@ -54,7 +54,7 @@ export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance: nu if (once) return null; } - const containerOrWindow = container || window; + const containerOrWindow = container ?? window; const onScroll = ev => { if (!document.body.contains(el)) return; if (isBottomVisible(el, 1, container)) { @@ -104,12 +104,12 @@ export function scrollToBottom( } else { window.scroll({ top: (el.scrollHeight - window.innerHeight + getStickyTop(el, container) + (window.innerWidth <= 500 ? 96 : 0)) || 0, - ...options + ...options, }); } } -export function isTopVisible(el: HTMLElement, tolerance: number = 1): boolean { +export function isTopVisible(el: HTMLElement, tolerance = 1): boolean { const scrollTop = getScrollPosition(el); return scrollTop <= tolerance; } @@ -124,6 +124,6 @@ export function getBodyScrollHeight() { return Math.max( document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, document.documentElement.offsetHeight, - document.body.clientHeight, document.documentElement.clientHeight + document.body.clientHeight, document.documentElement.clientHeight, ); } diff --git a/packages/frontend/src/scripts/search.ts b/packages/frontend/src/scripts/search.ts deleted file mode 100644 index 69f1586b77..0000000000 --- a/packages/frontend/src/scripts/search.ts +++ /dev/null @@ -1,63 +0,0 @@ -import * as os from '@/os'; -import { i18n } from '@/i18n'; -import { mainRouter } from '@/router'; - -export async function search() { - const { canceled, result: query } = await os.inputText({ - title: i18n.ts.search, - }); - if (canceled || query == null || query === '') return; - - const q = query.trim(); - - if (q.startsWith('@') && !q.includes(' ')) { - mainRouter.push(`/${q}`); - return; - } - - if (q.startsWith('#')) { - mainRouter.push(`/tags/${encodeURIComponent(q.substr(1))}`); - return; - } - - // like 2018/03/12 - if (/^[0-9]{4}\/[0-9]{2}\/[0-9]{2}/.test(q.replace(/-/g, '/'))) { - const date = new Date(q.replace(/-/g, '/')); - - // 日付ã—ã‹æŒ‡å®šã•れã¦ãªã„å ´åˆã€ä¾‹ãˆã° 2018/03/12 ãªã‚‰ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ - // 2018/03/12 ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„を「å«ã‚€ã€çµæžœã«ãªã‚‹ã“ã¨ã‚’期待ã™ã‚‹ã¯ãšãªã®ã§ - // 23時間59分進ã‚ã‚‹(ãã®ã¾ã¾ã 㨠2018/03/12 00:00:00 「ã¾ã§ã€ã® - // çµæžœã«ãªã£ã¦ã—ã¾ã„ã€2018/03/12 ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã¯å«ã¾ã‚Œãªã„) - if (q.replace(/-/g, '/').match(/^[0-9]{4}\/[0-9]{2}\/[0-9]{2}$/)) { - date.setHours(23, 59, 59, 999); - } - - // TODO - //v.$root.$emit('warp', date); - os.alert({ - icon: 'ti ti-history', - iconOnly: true, autoClose: true, - }); - return; - } - - if (q.startsWith('https://')) { - const promise = os.api('ap/show', { - uri: q, - }); - - os.promiseDialog(promise, null, null, i18n.ts.fetchingAsApObject); - - const res = await promise; - - if (res.type === 'User') { - mainRouter.push(`/@${res.object.username}@${res.object.host}`); - } else if (res.type === 'Note') { - mainRouter.push(`/notes/${res.object.id}`); - } - - return; - } - - mainRouter.push(`/search?q=${encodeURIComponent(q)}`); -} diff --git a/packages/frontend/src/scripts/use-document-visibility.ts b/packages/frontend/src/scripts/use-document-visibility.ts new file mode 100644 index 0000000000..47e91dd937 --- /dev/null +++ b/packages/frontend/src/scripts/use-document-visibility.ts @@ -0,0 +1,19 @@ +import { onMounted, onUnmounted, ref, Ref } from 'vue'; + +export function useDocumentVisibility(): Ref<DocumentVisibilityState> { + const visibility = ref(document.visibilityState); + + const onChange = (): void => { + visibility.value = document.visibilityState; + }; + + onMounted(() => { + document.addEventListener('visibilitychange', onChange); + }); + + onUnmounted(() => { + document.removeEventListener('visibilitychange', onChange); + }); + + return visibility; +} diff --git a/packages/frontend/src/theme-store.ts b/packages/frontend/src/theme-store.ts index aa1244665b..580c7da007 100644 --- a/packages/frontend/src/theme-store.ts +++ b/packages/frontend/src/theme-store.ts @@ -1,13 +1,13 @@ -import { api } from '@/os'; -import { $i } from '@/account'; import { Theme } from './scripts/theme'; import { miLocalStorage } from './local-storage'; +import { api } from '@/os'; +import { $i } from '@/account'; const lsCacheKey = $i ? `themes:${$i.id}` as const : null; export function getThemes(): Theme[] { if ($i == null) return []; - return JSON.parse(miLocalStorage.getItem(lsCacheKey!) || '[]'); + return JSON.parse(miLocalStorage.getItem(lsCacheKey!) ?? '[]'); } export async function fetchThemes(): Promise<void> { diff --git a/packages/frontend/src/ui/classic.header.vue b/packages/frontend/src/ui/classic.header.vue index 34ddfa1d32..3dfb371d32 100644 --- a/packages/frontend/src/ui/classic.header.vue +++ b/packages/frontend/src/ui/classic.header.vue @@ -45,11 +45,11 @@ import { defineAsyncComponent, defineComponent } from 'vue'; import { openInstanceMenu } from './_common_/common'; import { host } from '@/config'; -import { search } from '@/scripts/search'; import * as os from '@/os'; import { navbarItemDef } from '@/navbar'; import { openAccountMenu } from '@/account'; import MkButton from '@/components/MkButton.vue'; +import { mainRouter } from '@/router'; export default defineComponent({ components: { @@ -103,7 +103,7 @@ export default defineComponent({ }, search() { - search(); + mainRouter.push('/search'); }, more(ev) { diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue index a11c2ba10e..6fff233ac5 100644 --- a/packages/frontend/src/ui/classic.sidebar.vue +++ b/packages/frontend/src/ui/classic.sidebar.vue @@ -44,12 +44,12 @@ import { defineAsyncComponent, defineComponent } from 'vue'; import { openInstanceMenu } from './_common_/common'; import { host } from '@/config'; -import { search } from '@/scripts/search'; import * as os from '@/os'; import { navbarItemDef } from '@/navbar'; import { openAccountMenu } from '@/account'; import MkButton from '@/components/MkButton.vue'; import { StickySidebar } from '@/scripts/sticky-sidebar'; +import { mainRouter } from '@/router'; //import MisskeyLogo from '@assets/client/misskey.svg'; export default defineComponent({ @@ -120,7 +120,7 @@ export default defineComponent({ }, search() { - search(); + mainRouter.push('/search'); }, more(ev) { diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue index 92997ffb66..a359463d9b 100644 --- a/packages/frontend/src/ui/classic.vue +++ b/packages/frontend/src/ui/classic.vue @@ -125,7 +125,7 @@ function onAiClick(ev) { if (window.innerWidth < 1024) { const currentUI = miLocalStorage.getItem('ui'); - miLocalStorage.setItem('ui_temp', currentUI || 'default'); + miLocalStorage.setItem('ui_temp', currentUI ?? 'default'); miLocalStorage.setItem('ui', 'default'); location.reload(); } diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue index ef29b2e72f..be168b4282 100644 --- a/packages/frontend/src/ui/deck.vue +++ b/packages/frontend/src/ui/deck.vue @@ -4,7 +4,7 @@ <div :class="$style.main"> <XStatusBars/> - <div ref="columnsEl" :class="[$style.columns, deckStore.reactiveState.columnAlign.value]" @contextmenu.self.prevent="onContextmenu"> + <div ref="columnsEl" :class="[$style.columns, deckStore.reactiveState.columnAlign.value, { [$style.snapScroll]: snapScroll }]" @contextmenu.self.prevent="onContextmenu"> <template v-for="ids in layout"> <!-- sectionを利用ã—ã¦ã„ã‚‹ã®ã¯ã€deck.vueå´ã§columnã«å¯¾ã—ã¦first-of-typeを効ã‹ã›ã‚‹ãŸã‚ --> <section @@ -98,6 +98,7 @@ import { $i } from '@/account'; import { i18n } from '@/i18n'; import { mainRouter } from '@/router'; import { unisonReload } from '@/scripts/unison-reload'; +import { deviceKind } from '@/scripts/device-kind'; const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue')); mainRouter.navHook = (path, flag): boolean => { @@ -115,6 +116,7 @@ window.addEventListener('resize', () => { isMobile.value = window.innerWidth <= 500; }); +const snapScroll = deviceKind === 'smartphone' || deviceKind === 'tablet'; const drawerMenuShowing = ref(false); const route = 'TODO'; @@ -297,9 +299,14 @@ async function deleteProfile() { margin-right: auto; } } + + &.snapScroll { + scroll-snap-type: x mandatory; + } } .column { + scroll-snap-align: start; flex-shrink: 0; border-right: solid var(--deckDividerThickness) var(--deckDivider); diff --git a/packages/frontend/src/ui/deck/antenna-column.vue b/packages/frontend/src/ui/deck/antenna-column.vue index 4c69c8e8e8..76a8b6e760 100644 --- a/packages/frontend/src/ui/deck/antenna-column.vue +++ b/packages/frontend/src/ui/deck/antenna-column.vue @@ -4,7 +4,7 @@ <i class="ti ti-antenna"></i><span style="margin-left: 8px;">{{ column.name }}</span> </template> - <XTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId" @after="() => emit('loaded')"/> + <MkTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId" @after="() => emit('loaded')"/> </XColumn> </template> @@ -12,7 +12,7 @@ import { onMounted } from 'vue'; import XColumn from './column.vue'; import { updateColumn, Column } from './deck-store'; -import XTimeline from '@/components/MkTimeline.vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; @@ -26,7 +26,7 @@ const emit = defineEmits<{ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void; }>(); -let timeline = $shallowRef<InstanceType<typeof XTimeline>>(); +let timeline = $shallowRef<InstanceType<typeof MkTimeline>>(); onMounted(() => { if (props.column.antennaId == null) { diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue index 169d2c4056..4c6b41e42e 100644 --- a/packages/frontend/src/ui/deck/channel-column.vue +++ b/packages/frontend/src/ui/deck/channel-column.vue @@ -8,7 +8,7 @@ <div style="padding: 8px; text-align: center;"> <MkButton primary gradate rounded inline @click="post"><i class="ti ti-pencil"></i></MkButton> </div> - <XTimeline ref="timeline" src="channel" :channel="column.channelId" @after="() => emit('loaded')"/> + <MkTimeline ref="timeline" src="channel" :channel="column.channelId" @after="() => emit('loaded')"/> </template> </XColumn> </template> @@ -17,7 +17,7 @@ import { } from 'vue'; import XColumn from './column.vue'; import { updateColumn, Column } from './deck-store'; -import XTimeline from '@/components/MkTimeline.vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; @@ -32,7 +32,7 @@ const emit = defineEmits<{ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void; }>(); -let timeline = $shallowRef<InstanceType<typeof XTimeline>>(); +let timeline = $shallowRef<InstanceType<typeof MkTimeline>>(); if (props.column.channelId == null) { setChannel(); diff --git a/packages/frontend/src/ui/deck/direct-column.vue b/packages/frontend/src/ui/deck/direct-column.vue index 75b018cacd..15b76c4d92 100644 --- a/packages/frontend/src/ui/deck/direct-column.vue +++ b/packages/frontend/src/ui/deck/direct-column.vue @@ -2,15 +2,15 @@ <XColumn :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)"> <template #header><i class="ti ti-mail" style="margin-right: 8px;"></i>{{ column.name }}</template> - <XNotes :pagination="pagination"/> + <MkNotes :pagination="pagination"/> </XColumn> </template> <script lang="ts" setup> import { } from 'vue'; import XColumn from './column.vue'; -import XNotes from '@/components/MkNotes.vue'; import { Column } from './deck-store'; +import MkNotes from '@/components/MkNotes.vue'; defineProps<{ column: Column; diff --git a/packages/frontend/src/ui/deck/list-column.vue b/packages/frontend/src/ui/deck/list-column.vue index 58633e5672..352c1d246a 100644 --- a/packages/frontend/src/ui/deck/list-column.vue +++ b/packages/frontend/src/ui/deck/list-column.vue @@ -4,7 +4,7 @@ <i class="ti ti-list"></i><span style="margin-left: 8px;">{{ column.name }}</span> </template> - <XTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" @after="() => emit('loaded')"/> + <MkTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" @after="() => emit('loaded')"/> </XColumn> </template> @@ -12,7 +12,7 @@ import { } from 'vue'; import XColumn from './column.vue'; import { updateColumn, Column } from './deck-store'; -import XTimeline from '@/components/MkTimeline.vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; @@ -26,7 +26,7 @@ const emit = defineEmits<{ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void; }>(); -let timeline = $shallowRef<InstanceType<typeof XTimeline>>(); +let timeline = $shallowRef<InstanceType<typeof MkTimeline>>(); if (props.column.listId == null) { setList(); diff --git a/packages/frontend/src/ui/deck/mentions-column.vue b/packages/frontend/src/ui/deck/mentions-column.vue index 16962956a0..852d7a8f7e 100644 --- a/packages/frontend/src/ui/deck/mentions-column.vue +++ b/packages/frontend/src/ui/deck/mentions-column.vue @@ -2,15 +2,15 @@ <XColumn :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)"> <template #header><i class="ti ti-at" style="margin-right: 8px;"></i>{{ column.name }}</template> - <XNotes :pagination="pagination"/> + <MkNotes :pagination="pagination"/> </XColumn> </template> <script lang="ts" setup> import { } from 'vue'; import XColumn from './column.vue'; -import XNotes from '@/components/MkNotes.vue'; import { Column } from './deck-store'; +import MkNotes from '@/components/MkNotes.vue'; defineProps<{ column: Column; diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue index 8b1b920fa8..a947e27e57 100644 --- a/packages/frontend/src/ui/deck/tl-column.vue +++ b/packages/frontend/src/ui/deck/tl-column.vue @@ -15,7 +15,7 @@ </p> <p :class="$style.disabledDescription">{{ $t('disabled-timeline.description') }}</p> </div> - <XTimeline v-else-if="column.tl" ref="timeline" :key="column.tl" :src="column.tl" @after="() => emit('loaded')"/> + <MkTimeline v-else-if="column.tl" ref="timeline" :key="column.tl" :src="column.tl" @after="() => emit('loaded')"/> </XColumn> </template> @@ -23,7 +23,7 @@ import { onMounted } from 'vue'; import XColumn from './column.vue'; import { removeColumn, updateColumn, Column } from './deck-store'; -import XTimeline from '@/components/MkTimeline.vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import * as os from '@/os'; import { $i } from '@/account'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/ui/visitor/a.vue b/packages/frontend/src/ui/visitor/a.vue index 9494b1b705..023b7fdb94 100644 --- a/packages/frontend/src/ui/visitor/a.vue +++ b/packages/frontend/src/ui/visitor/a.vue @@ -40,7 +40,6 @@ import { defineComponent } from 'vue'; import XHeader from './header.vue'; import { host, instanceName } from '@/config'; -import { search } from '@/scripts/search'; import * as os from '@/os'; import MkButton from '@/components/MkButton.vue'; import { ColdDeviceStorage } from '@/store'; @@ -77,7 +76,9 @@ export default defineComponent({ if (ColdDeviceStorage.get('syncDeviceDarkMode')) return; this.$store.set('darkMode', !this.$store.state.darkMode); }, - 's': search, + 's': () => { + mainRouter.push('/search'); + }, 'h|/': this.help, }; }, diff --git a/packages/frontend/src/ui/visitor/b.vue b/packages/frontend/src/ui/visitor/b.vue index e36f36f153..e2168768e8 100644 --- a/packages/frontend/src/ui/visitor/b.vue +++ b/packages/frontend/src/ui/visitor/b.vue @@ -11,7 +11,10 @@ <div class="contents"> <XHeader v-if="!root" class="header"/> - <main style="container-type: inline-size;"> + <main v-if="!root" style="container-type: inline-size;"> + <RouterView/> + </main> + <main v-else> <RouterView/> </main> <div v-if="!root" class="powered-by"> @@ -55,7 +58,6 @@ import { ComputedRef, onMounted, provide } from 'vue'; import XHeader from './header.vue'; import XKanban from './kanban.vue'; import { host, instanceName } from '@/config'; -import { search } from '@/scripts/search'; import * as os from '@/os'; import { instance } from '@/instance'; import XSigninDialog from '@/components/MkSigninDialog.vue'; @@ -94,7 +96,9 @@ const keymap = $computed(() => { if (ColdDeviceStorage.get('syncDeviceDarkMode')) return; defaultStore.set('darkMode', !defaultStore.state.darkMode); }, - 's': search, + 's': () => { + mainRouter.push('/search'); + }, }; }); diff --git a/packages/frontend/src/ui/visitor/header.vue b/packages/frontend/src/ui/visitor/header.vue index 2647d0e62a..aaa7e77e90 100644 --- a/packages/frontend/src/ui/visitor/header.vue +++ b/packages/frontend/src/ui/visitor/header.vue @@ -27,7 +27,7 @@ import XSigninDialog from '@/components/MkSigninDialog.vue'; import XSignupDialog from '@/components/MkSignupDialog.vue'; import * as os from '@/os'; import { instance } from '@/instance'; -import { search } from '@/scripts/search'; +import { mainRouter } from '@/router'; export default defineComponent({ data() { @@ -55,7 +55,9 @@ export default defineComponent({ }, {}, 'closed'); }, - search, + search() { + mainRouter.push('/search'); + }, }, }); </script> diff --git a/packages/frontend/src/widgets/WidgetOnlineUsers.vue b/packages/frontend/src/widgets/WidgetOnlineUsers.vue index a096cc8fe8..7949fc4a93 100644 --- a/packages/frontend/src/widgets/WidgetOnlineUsers.vue +++ b/packages/frontend/src/widgets/WidgetOnlineUsers.vue @@ -1,7 +1,7 @@ <template> <div class="mkw-onlineUsers data-cy-mkw-onlineUsers" :class="{ _panel: !widgetProps.transparent, pad: !widgetProps.transparent }"> <I18n v-if="onlineUsersCount" :src="i18n.ts.onlineUsersCount" text-tag="span" class="text"> - <template #n><b>{{ onlineUsersCount }}</b></template> + <template #n><b>{{ number(onlineUsersCount) }}</b></template> </I18n> </div> </template> @@ -13,6 +13,7 @@ import { GetFormResultType } from '@/scripts/form'; import * as os from '@/os'; import { useInterval } from '@/scripts/use-interval'; import { i18n } from '@/i18n'; +import number from '@/filters/number'; const name = 'onlineUsers'; diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue index fde7048b00..d6be6532a6 100644 --- a/packages/frontend/src/widgets/WidgetTimeline.vue +++ b/packages/frontend/src/widgets/WidgetTimeline.vue @@ -16,7 +16,7 @@ </template> <div> - <XTimeline :key="widgetProps.src === 'list' ? `list:${widgetProps.list.id}` : widgetProps.src === 'antenna' ? `antenna:${widgetProps.antenna.id}` : widgetProps.src" :src="widgetProps.src" :list="widgetProps.list ? widgetProps.list.id : null" :antenna="widgetProps.antenna ? widgetProps.antenna.id : null"/> + <MkTimeline :key="widgetProps.src === 'list' ? `list:${widgetProps.list.id}` : widgetProps.src === 'antenna' ? `antenna:${widgetProps.antenna.id}` : widgetProps.src" :src="widgetProps.src" :list="widgetProps.list ? widgetProps.list.id : null" :antenna="widgetProps.antenna ? widgetProps.antenna.id : null"/> </div> </MkContainer> </template> @@ -27,7 +27,7 @@ import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import * as os from '@/os'; import MkContainer from '@/components/MkContainer.vue'; -import XTimeline from '@/components/MkTimeline.vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import { i18n } from '@/i18n'; const name = 'timeline'; diff --git a/packages/shared/.eslintrc.js b/packages/shared/.eslintrc.js index 6d38a9fb9f..7c979a93dc 100644 --- a/packages/shared/.eslintrc.js +++ b/packages/shared/.eslintrc.js @@ -77,6 +77,17 @@ module.exports = { '@typescript-eslint/prefer-nullish-coalescing': [ 'error', ], + '@typescript-eslint/naming-convention': [ + 'error', + { + "selector": "typeLike", + "format": ["PascalCase"] + }, + { + "selector": "typeParameter", + "format": [] + } + ], 'import/no-unresolved': ['off'], 'import/no-default-export': ['warn'], 'import/order': ['warn', { diff --git a/packages/sw/package.json b/packages/sw/package.json index 4de260fcfc..7fab90a243 100644 --- a/packages/sw/package.json +++ b/packages/sw/package.json @@ -14,9 +14,9 @@ "misskey-js": "0.0.15" }, "devDependencies": { - "@typescript-eslint/parser": "5.51.0", + "@typescript-eslint/parser": "5.52.0", "@typescript/lib-webworker": "npm:@types/serviceworker@0.0.62", - "eslint": "8.33.0", + "eslint": "8.34.0", "eslint-plugin-import": "2.27.5", "typescript": "4.9.5" } diff --git a/packages/sw/src/scripts/get-user-name.ts b/packages/sw/src/scripts/get-user-name.ts index ccc38c298e..4daf203e06 100644 --- a/packages/sw/src/scripts/get-user-name.ts +++ b/packages/sw/src/scripts/get-user-name.ts @@ -1,7 +1,3 @@ export default function(user: { name?: string | null, username: string }): string { - // Show username if name is empty. - // XXX: typescript-eslint has no configuration to allow using `||` against string. - // https://github.com/typescript-eslint/typescript-eslint/issues/4906 - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - return user.name || user.username; + return user.name === '' ? user.username : user.name ?? user.username; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b5e52ef9d2..6273952eab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,11 +11,11 @@ importers: '@tensorflow/tfjs-core': 4.2.0 '@types/gulp': 4.0.10 '@types/gulp-rename': 2.0.1 - '@typescript-eslint/eslint-plugin': 5.51.0 - '@typescript-eslint/parser': 5.51.0 + '@typescript-eslint/eslint-plugin': 5.52.0 + '@typescript-eslint/parser': 5.52.0 cross-env: 7.0.3 - cypress: 12.5.1 - eslint: 8.33.0 + cypress: 12.6.0 + eslint: 8.34.0 execa: 5.1.1 gulp: 4.0.2 gulp-cssnano: 2.1.3 @@ -23,7 +23,7 @@ importers: gulp-replace: 1.1.4 gulp-terser: 2.1.0 js-yaml: 4.1.0 - start-server-and-test: 1.15.3 + start-server-and-test: 1.15.4 typescript: 4.9.5 dependencies: execa: 5.1.1 @@ -39,35 +39,46 @@ importers: devDependencies: '@types/gulp': 4.0.10 '@types/gulp-rename': 2.0.1 - '@typescript-eslint/eslint-plugin': 5.51.0_b635kmla6dsb4frxfihkw4m47e - '@typescript-eslint/parser': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/eslint-plugin': 5.52.0_6cfvjsbua5ptj65675bqcn6oza + '@typescript-eslint/parser': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm cross-env: 7.0.3 - cypress: 12.5.1 - eslint: 8.33.0 - start-server-and-test: 1.15.3 + cypress: 12.6.0 + eslint: 8.34.0 + start-server-and-test: 1.15.4 packages/backend: specifiers: - '@bull-board/api': 4.11.1 - '@bull-board/fastify': 4.11.1 - '@bull-board/ui': 4.11.1 + '@bull-board/api': 4.12.1 + '@bull-board/fastify': 4.12.1 + '@bull-board/ui': 4.12.1 '@discordapp/twemoji': 14.0.2 '@fastify/accepts': 4.1.0 '@fastify/cookie': 8.3.0 '@fastify/cors': 8.2.0 '@fastify/http-proxy': 8.4.0 - '@fastify/multipart': 7.4.0 - '@fastify/static': 6.8.0 + '@fastify/multipart': 7.4.1 + '@fastify/static': 6.9.0 '@fastify/view': 7.4.1 - '@jest/globals': 29.4.2 - '@nestjs/common': 9.3.7 - '@nestjs/core': 9.3.7 - '@nestjs/testing': 9.3.7 + '@jest/globals': 29.4.3 + '@nestjs/common': 9.3.9 + '@nestjs/core': 9.3.9 + '@nestjs/testing': 9.3.9 '@peertube/http-signature': 1.7.0 '@redocly/openapi-core': 1.0.0-beta.123 '@sinonjs/fake-timers': 10.0.2 - '@swc/cli': 0.1.61 - '@swc/core': 1.3.34 + '@swc/cli': 0.1.62 + '@swc/core': 1.3.36 + '@swc/core-android-arm64': ^1.3.11 + '@swc/core-darwin-arm64': ^1.3.36 + '@swc/core-darwin-x64': ^1.3.36 + '@swc/core-linux-arm-gnueabihf': ^1.3.36 + '@swc/core-linux-arm64-gnu': ^1.3.36 + '@swc/core-linux-arm64-musl': ^1.3.36 + '@swc/core-linux-x64-gnu': ^1.3.36 + '@swc/core-linux-x64-musl': ^1.3.36 + '@swc/core-win32-arm64-msvc': ^1.3.36 + '@swc/core-win32-ia32-msvc': ^1.3.36 + '@swc/core-win32-x64-msvc': ^1.3.36 '@swc/jest': 0.2.24 '@tensorflow/tfjs': 4.2.0 '@tensorflow/tfjs-node': 4.2.0 @@ -87,7 +98,7 @@ importers: '@types/jsonld': 1.5.8 '@types/jsrsasign': 10.5.5 '@types/mime-types': 2.1.1 - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/node-fetch': 3.0.3 '@types/nodemailer': 6.4.7 '@types/oauth': 0.9.1 @@ -103,7 +114,6 @@ importers: '@types/semver': 7.3.13 '@types/sharp': 0.31.1 '@types/sinonjs__fake-timers': 8.1.2 - '@types/speakeasy': 2.0.7 '@types/tinycolor2': 1.4.3 '@types/tmp': 0.2.3 '@types/unzipper': 0.10.5 @@ -112,16 +122,16 @@ importers: '@types/web-push': 3.3.2 '@types/websocket': 1.0.5 '@types/ws': 8.5.4 - '@typescript-eslint/eslint-plugin': 5.51.0 - '@typescript-eslint/parser': 5.51.0 + '@typescript-eslint/eslint-plugin': 5.52.0 + '@typescript-eslint/parser': 5.52.0 accepts: 1.3.8 ajv: 8.12.0 archiver: 5.3.1 autwh: 0.1.0 - aws-sdk: 2.1295.0 + aws-sdk: 2.1318.0 bcryptjs: 2.4.3 - blurhash: 2.0.4 - bull: 4.10.3 + blurhash: 2.0.5 + bull: 4.10.4 cacheable-lookup: 6.1.0 cbor: 8.1.0 chalk: 5.2.0 @@ -134,21 +144,22 @@ importers: date-fns: 2.29.3 deep-email-validator: 0.1.21 escape-regexp: 0.0.1 - eslint: 8.33.0 + eslint: 8.34.0 eslint-plugin-import: 2.27.5 execa: 6.1.0 - fastify: 4.12.0 + fastify: 4.13.0 feed: 4.2.2 - file-type: 18.2.0 + file-type: 18.2.1 fluent-ffmpeg: 2.1.2 form-data: 4.0.0 got: 12.5.3 + happy-dom: ^8.7.0 hpagent: 1.2.0 ioredis: 4.28.5 ip-cidr: 3.1.0 is-svg: 4.3.2 - jest: 29.4.2 - jest-mock: 29.4.2 + jest: 29.4.3 + jest-mock: 29.4.3 js-yaml: 4.1.0 jsdom: 21.1.0 json5: 2.2.3 @@ -164,6 +175,7 @@ importers: nsfwjs: 2.4.2 oauth: 0.10.0 os-utils: 0.0.14 + otpauth: ^9.0.2 parse5: 7.1.2 pg: 8.9.0 private-ip: 3.0.0 @@ -183,15 +195,14 @@ importers: rss-parser: 3.12.0 rxjs: 7.8.0 s-age: 1.1.2 - sanitize-html: 2.9.0 + sanitize-html: 2.10.0 seedrandom: 3.0.5 semver: 7.3.8 sharp: 0.31.3 - speakeasy: 2.0.0 strict-event-emitter-types: 2.0.0 stringz: 2.1.0 summaly: github:misskey-dev/summaly - systeminformation: 5.17.8 + systeminformation: 5.17.9 tinycolor2: 1.6.0 tmp: 0.2.1 tsc-alias: 1.8.2 @@ -205,33 +216,35 @@ importers: vary: 1.1.2 web-push: 3.5.0 websocket: 1.0.34 - ws: 8.12.0 + ws: 8.12.1 xev: 3.0.2 dependencies: - '@bull-board/api': 4.11.1 - '@bull-board/fastify': 4.11.1 - '@bull-board/ui': 4.11.1 + '@bull-board/api': 4.12.1 + '@bull-board/fastify': 4.12.1 + '@bull-board/ui': 4.12.1 '@discordapp/twemoji': 14.0.2 '@fastify/accepts': 4.1.0 '@fastify/cookie': 8.3.0 '@fastify/cors': 8.2.0 '@fastify/http-proxy': 8.4.0 - '@fastify/multipart': 7.4.0 - '@fastify/static': 6.8.0 + '@fastify/multipart': 7.4.1 + '@fastify/static': 6.9.0 '@fastify/view': 7.4.1 - '@nestjs/common': 9.3.7_mnr6j2del53muneqly5h4y27ai - '@nestjs/core': 9.3.7_ojttuksvundfk3amlyo674va74 - '@nestjs/testing': 9.3.7_zmctrozi6xxh3beaccl6ebop6u + '@nestjs/common': 9.3.9_mnr6j2del53muneqly5h4y27ai + '@nestjs/core': 9.3.9_jrq2rdgfp2sx67wmylmrqliwxe + '@nestjs/testing': 9.3.9_77foi4w27ghy47yutmnzv7krjy '@peertube/http-signature': 1.7.0 '@sinonjs/fake-timers': 10.0.2 + '@swc/cli': 0.1.62_wyduggqpvtv2oy5nc7cgtozgpy + '@swc/core': 1.3.36 accepts: 1.3.8 ajv: 8.12.0 archiver: 5.3.1 autwh: 0.1.0 - aws-sdk: 2.1295.0 + aws-sdk: 2.1318.0 bcryptjs: 2.4.3 - blurhash: 2.0.4 - bull: 4.10.3 + blurhash: 2.0.5 + bull: 4.10.4 cacheable-lookup: 6.1.0 cbor: 8.1.0 chalk: 5.2.0 @@ -243,12 +256,13 @@ importers: date-fns: 2.29.3 deep-email-validator: 0.1.21 escape-regexp: 0.0.1 - fastify: 4.12.0 + fastify: 4.13.0 feed: 4.2.2 - file-type: 18.2.0 + file-type: 18.2.1 fluent-ffmpeg: 2.1.2 form-data: 4.0.0 got: 12.5.3 + happy-dom: 8.7.0 hpagent: 1.2.0 ioredis: 4.28.5 ip-cidr: 3.1.0 @@ -268,6 +282,7 @@ importers: nsfwjs: 2.4.2_@tensorflow+tfjs@4.2.0 oauth: 0.10.0 os-utils: 0.0.14 + otpauth: 9.0.2 parse5: 7.1.2 pg: 8.9.0 private-ip: 3.0.0 @@ -287,15 +302,14 @@ importers: rss-parser: 3.12.0 rxjs: 7.8.0 s-age: 1.1.2 - sanitize-html: 2.9.0 + sanitize-html: 2.10.0 seedrandom: 3.0.5 semver: 7.3.8 sharp: 0.31.3 - speakeasy: 2.0.0 strict-event-emitter-types: 2.0.0 stringz: 2.1.0 - summaly: github.com/misskey-dev/summaly/5684f116c92f1fd122badc7aee062494bdb43b36 - systeminformation: 5.17.8 + summaly: github.com/misskey-dev/summaly/51f3870e1ff5e0b22102e804112b10cb72f3c494 + systeminformation: 5.17.9 tinycolor2: 1.6.0 tmp: 0.2.1 tsc-alias: 1.8.2 @@ -309,17 +323,26 @@ importers: vary: 1.1.2 web-push: 3.5.0 websocket: 1.0.34 - ws: 8.12.0 + ws: 8.12.1 xev: 3.0.2 optionalDependencies: + '@swc/core-android-arm64': 1.3.11 + '@swc/core-darwin-arm64': 1.3.36 + '@swc/core-darwin-x64': 1.3.36 + '@swc/core-linux-arm-gnueabihf': 1.3.36 + '@swc/core-linux-arm64-gnu': 1.3.36 + '@swc/core-linux-arm64-musl': 1.3.36 + '@swc/core-linux-x64-gnu': 1.3.36 + '@swc/core-linux-x64-musl': 1.3.36 + '@swc/core-win32-arm64-msvc': 1.3.36 + '@swc/core-win32-ia32-msvc': 1.3.36 + '@swc/core-win32-x64-msvc': 1.3.36 '@tensorflow/tfjs': 4.2.0_seedrandom@3.0.5 '@tensorflow/tfjs-node': 4.2.0_seedrandom@3.0.5 devDependencies: - '@jest/globals': 29.4.2 + '@jest/globals': 29.4.3 '@redocly/openapi-core': 1.0.0-beta.123 - '@swc/cli': 0.1.61_nh7rjdlljrxbrljvydz3oxxjt4 - '@swc/core': 1.3.34 - '@swc/jest': 0.2.24_@swc+core@1.3.34 + '@swc/jest': 0.2.24_@swc+core@1.3.36 '@types/accepts': 1.3.5 '@types/archiver': 5.3.1 '@types/bcryptjs': 2.4.2 @@ -336,7 +359,7 @@ importers: '@types/jsonld': 1.5.8 '@types/jsrsasign': 10.5.5 '@types/mime-types': 2.1.1 - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/node-fetch': 3.0.3 '@types/nodemailer': 6.4.7 '@types/oauth': 0.9.1 @@ -352,7 +375,6 @@ importers: '@types/semver': 7.3.13 '@types/sharp': 0.31.1 '@types/sinonjs__fake-timers': 8.1.2 - '@types/speakeasy': 2.0.7 '@types/tinycolor2': 1.4.3 '@types/tmp': 0.2.3 '@types/unzipper': 0.10.5 @@ -361,14 +383,14 @@ importers: '@types/web-push': 3.3.2 '@types/websocket': 1.0.5 '@types/ws': 8.5.4 - '@typescript-eslint/eslint-plugin': 5.51.0_b635kmla6dsb4frxfihkw4m47e - '@typescript-eslint/parser': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/eslint-plugin': 5.52.0_6cfvjsbua5ptj65675bqcn6oza + '@typescript-eslint/parser': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm cross-env: 7.0.3 - eslint: 8.33.0 - eslint-plugin-import: 2.27.5_yzj2n2b43wonjwaifya6xmk2zy + eslint: 8.34.0 + eslint-plugin-import: 2.27.5_mcvs2y73sfmcxqzpjj5lr7a2m4 execa: 6.1.0 - jest: 29.4.2_@types+node@18.13.0 - jest-mock: 29.4.2 + jest: 29.4.3_@types+node@18.14.0 + jest-mock: 29.4.3 packages/frontend: specifiers: @@ -383,7 +405,7 @@ importers: '@types/gulp': 4.0.10 '@types/gulp-rename': 2.0.1 '@types/matter-js': 0.18.2 - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/punycode': 2.1.0 '@types/sanitize-html': 2.8.0 '@types/seedrandom': 3.0.4 @@ -392,18 +414,18 @@ importers: '@types/uuid': 9.0.0 '@types/websocket': 1.0.5 '@types/ws': 8.5.4 - '@typescript-eslint/eslint-plugin': 5.51.0 - '@typescript-eslint/parser': 5.51.0 + '@typescript-eslint/eslint-plugin': 5.52.0 + '@typescript-eslint/parser': 5.52.0 '@vitejs/plugin-vue': 4.0.0 '@vue/compiler-sfc': 3.2.47 '@vue/runtime-core': 3.2.47 autobind-decorator: 2.4.0 autosize: 5.0.2 - blurhash: 2.0.4 + blurhash: 2.0.5 broadcast-channel: 4.20.2 - browser-image-resizer: git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.3 + browser-image-resizer: github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3 canvas-confetti: 1.6.0 - chart.js: 4.2.0 + chart.js: 4.2.1 chartjs-adapter-date-fns: 3.0.0 chartjs-chart-matrix: 2.0.1 chartjs-plugin-gradient: 0.6.1 @@ -411,10 +433,10 @@ importers: compare-versions: 5.0.1 cropperjs: 2.0.0-beta.2 cross-env: 7.0.3 - cypress: 12.5.1 + cypress: 12.6.0 date-fns: 2.29.3 escape-regexp: 0.0.1 - eslint: 8.33.0 + eslint: 8.34.0 eslint-plugin-import: 2.27.5 eslint-plugin-vue: 9.9.0 eventemitter3: 5.0.0 @@ -423,7 +445,7 @@ importers: insert-text-at-cursor: 0.3.0 is-file-animated: 1.0.2 json5: 2.2.3 - matter-js: 0.18.0 + matter-js: 0.19.0 mfm-js: 0.23.3 misskey-js: 0.0.15 photoswipe: 5.3.5 @@ -431,14 +453,13 @@ importers: punycode: 2.3.0 querystring: 0.2.1 rndstr: 1.0.0 - rollup: 3.14.0 + rollup: 3.17.2 s-age: 1.1.2 - sanitize-html: 2.9.0 - sass: 1.58.0 + sanitize-html: 2.10.0 + sass: 1.58.3 seedrandom: 3.0.5 - start-server-and-test: 1.15.3 + start-server-and-test: 1.15.4 strict-event-emitter-types: 2.0.0 - stringz: 2.1.0 syuilo-password-strength: 0.0.1 textarea-caret: 3.1.0 three: 0.149.0 @@ -450,33 +471,33 @@ importers: typescript: 4.9.5 uuid: 9.0.0 vanilla-tilt: 1.8.0 - vite: 4.1.1 + vite: 4.1.2 vue: 3.2.47 vue-eslint-parser: 9.1.0 vue-plyr: 7.0.0 vue-prism-editor: 2.0.0-alpha.2 - vue-tsc: 1.0.24 + vue-tsc: 1.1.4 vuedraggable: next dependencies: '@discordapp/twemoji': 14.0.2 - '@rollup/plugin-alias': 4.0.3_rollup@3.14.0 - '@rollup/plugin-json': 6.0.0_rollup@3.14.0 - '@rollup/pluginutils': 5.0.2_rollup@3.14.0 + '@rollup/plugin-alias': 4.0.3_rollup@3.17.2 + '@rollup/plugin-json': 6.0.0_rollup@3.17.2 + '@rollup/pluginutils': 5.0.2_rollup@3.17.2 '@syuilo/aiscript': 0.12.4 '@tabler/icons-webfont': 2.2.0 - '@vitejs/plugin-vue': 4.0.0_vite@4.1.1+vue@3.2.47 + '@vitejs/plugin-vue': 4.0.0_vite@4.1.2+vue@3.2.47 '@vue/compiler-sfc': 3.2.47 autobind-decorator: 2.4.0 autosize: 5.0.2 - blurhash: 2.0.4 + blurhash: 2.0.5 broadcast-channel: 4.20.2 browser-image-resizer: github.com/misskey-dev/browser-image-resizer/0227e860621e55cbed0aabe6dc601096a7748c4a canvas-confetti: 1.6.0 - chart.js: 4.2.0 - chartjs-adapter-date-fns: 3.0.0_n6szoxj4ax2zhp2sxsxxj6zdla - chartjs-chart-matrix: 2.0.1_chart.js@4.2.0 - chartjs-plugin-gradient: 0.6.1_chart.js@4.2.0 - chartjs-plugin-zoom: 2.0.0_chart.js@4.2.0 + chart.js: 4.2.1 + chartjs-adapter-date-fns: 3.0.0_rid3rx6orfiwps7kg2r43n6mvu + chartjs-chart-matrix: 2.0.1_chart.js@4.2.1 + chartjs-plugin-gradient: 0.6.1_chart.js@4.2.1 + chartjs-plugin-zoom: 2.0.0_chart.js@4.2.1 compare-versions: 5.0.1 cropperjs: 2.0.0-beta.2 date-fns: 2.29.3 @@ -487,7 +508,7 @@ importers: insert-text-at-cursor: 0.3.0 is-file-animated: 1.0.2 json5: 2.2.3 - matter-js: 0.18.0 + matter-js: 0.19.0 mfm-js: 0.23.3 misskey-js: 0.0.15 photoswipe: 5.3.5 @@ -495,13 +516,12 @@ importers: punycode: 2.3.0 querystring: 0.2.1 rndstr: 1.0.0 - rollup: 3.14.0 + rollup: 3.17.2 s-age: 1.1.2 - sanitize-html: 2.9.0 - sass: 1.58.0 + sanitize-html: 2.10.0 + sass: 1.58.3 seedrandom: 3.0.5 strict-event-emitter-types: 2.0.0 - stringz: 2.1.0 syuilo-password-strength: 0.0.1 textarea-caret: 3.1.0 three: 0.149.0 @@ -513,7 +533,7 @@ importers: typescript: 4.9.5 uuid: 9.0.0 vanilla-tilt: 1.8.0 - vite: 4.1.1_gyrp4zacqcjjrmgvdzgac5epyy + vite: 4.1.2_hlkwzk2izwsolfmdrejei4vrty vue: 3.2.47 vue-plyr: 7.0.0 vue-prism-editor: 2.0.0-alpha.2_vue@3.2.47 @@ -524,7 +544,7 @@ importers: '@types/gulp': 4.0.10 '@types/gulp-rename': 2.0.1 '@types/matter-js': 0.18.2 - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/punycode': 2.1.0 '@types/sanitize-html': 2.8.0 '@types/seedrandom': 3.0.4 @@ -533,24 +553,24 @@ importers: '@types/uuid': 9.0.0 '@types/websocket': 1.0.5 '@types/ws': 8.5.4 - '@typescript-eslint/eslint-plugin': 5.51.0_b635kmla6dsb4frxfihkw4m47e - '@typescript-eslint/parser': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/eslint-plugin': 5.52.0_6cfvjsbua5ptj65675bqcn6oza + '@typescript-eslint/parser': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm '@vue/runtime-core': 3.2.47 cross-env: 7.0.3 - cypress: 12.5.1 - eslint: 8.33.0 - eslint-plugin-import: 2.27.5_yzj2n2b43wonjwaifya6xmk2zy - eslint-plugin-vue: 9.9.0_eslint@8.33.0 - start-server-and-test: 1.15.3 - vue-eslint-parser: 9.1.0_eslint@8.33.0 - vue-tsc: 1.0.24_typescript@4.9.5 + cypress: 12.6.0 + eslint: 8.34.0 + eslint-plugin-import: 2.27.5_mcvs2y73sfmcxqzpjj5lr7a2m4 + eslint-plugin-vue: 9.9.0_eslint@8.34.0 + start-server-and-test: 1.15.4 + vue-eslint-parser: 9.1.0_eslint@8.34.0 + vue-tsc: 1.1.4_typescript@4.9.5 packages/sw: specifiers: - '@typescript-eslint/parser': 5.51.0 + '@typescript-eslint/parser': 5.52.0 '@typescript/lib-webworker': npm:@types/serviceworker@0.0.62 esbuild: 0.14.42 - eslint: 8.33.0 + eslint: 8.34.0 eslint-plugin-import: 2.27.5 idb-keyval: 6.2.0 misskey-js: 0.0.15 @@ -560,10 +580,10 @@ importers: idb-keyval: 6.2.0 misskey-js: 0.0.15 devDependencies: - '@typescript-eslint/parser': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/parser': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm '@typescript/lib-webworker': /@types/serviceworker/0.0.62 - eslint: 8.33.0 - eslint-plugin-import: 2.27.5_yzj2n2b43wonjwaifya6xmk2zy + eslint: 8.34.0 + eslint-plugin-import: 2.27.5_mcvs2y73sfmcxqzpjj5lr7a2m4 typescript: 4.9.5 packages: @@ -732,7 +752,6 @@ packages: /@babel/parser/7.20.7: resolution: {integrity: sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==} engines: {node: '>=6.0.0'} - hasBin: true dependencies: '@babel/types': 7.20.7 @@ -911,26 +930,26 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true - /@bull-board/api/4.11.1: - resolution: {integrity: sha512-x+CKpLKZ4yJ1CETvoX9cIOMM1ZfsXr6N9pOsRj8/lh0Mo/rbQWbfdlZR1jiALMAKu1RzJnffZcn6uKeFoXMRBQ==} + /@bull-board/api/4.12.1: + resolution: {integrity: sha512-cJk7LhphNZHwbN4yON5rYiDgob3D7cwFGrxf6LlnR1w/a6zyap1x/o74MfV/0T6NpUpvUSf7ccbj3knU6DNYVw==} dependencies: redis-info: 3.1.0 dev: false - /@bull-board/fastify/4.11.1: - resolution: {integrity: sha512-xBTgSZKeLIwjLWsdW3vOJTubO6ZfiOKQSjtZ7n9ZUoiO4gg4m0MWfTBDgBFSgIqB5FjgdsIO6aP7QGsHI1wmng==} + /@bull-board/fastify/4.12.1: + resolution: {integrity: sha512-MAecvp+xTePdEHm4uCooojAlj2isdznfC9XuCjjGHHNCczjm/Wx14cDhKLKwkOFp5ZLhqgtOEulT5YVfVCu+EQ==} dependencies: - '@bull-board/api': 4.11.1 - '@bull-board/ui': 4.11.1 - '@fastify/static': 6.8.0 + '@bull-board/api': 4.12.1 + '@bull-board/ui': 4.12.1 + '@fastify/static': 6.9.0 '@fastify/view': 7.4.1 ejs: 3.1.8 dev: false - /@bull-board/ui/4.11.1: - resolution: {integrity: sha512-jILv2JN8cfgtHRHYk9GNpQh3rI2zaiX6bEfa1yyFRjLKcY4g409MTOOkZvkXlN5I4mBs+V3aob8cmvS2ixESDA==} + /@bull-board/ui/4.12.1: + resolution: {integrity: sha512-nNTjxitLEre+UdZjwMHwlKKGxpg3OV/Sf7dNOlhJYoRjO7aYPb63M+BfGVrYgupk3V73Q0qvl1emtsnF/qz/Wg==} dependencies: - '@bull-board/api': 4.11.1 + '@bull-board/api': 4.12.1 dev: false /@chainsafe/is-ip/2.0.1: @@ -1359,14 +1378,14 @@ packages: resolution: {integrity: sha512-H8nwsmawFtKKRE6uhh1BtF1gQi/l147SmLsDGxB0HdYTHzjXz6uSQO3lEVmY7unKMzbArRjdoJQkEGpScszdSw==} dependencies: '@fastify/reply-from': 8.3.1 - ws: 8.12.0 + ws: 8.12.1 transitivePeerDependencies: - bufferutil - utf-8-validate dev: false - /@fastify/multipart/7.4.0: - resolution: {integrity: sha512-jl8KCMOjzniAMnF2/VdYFhGB03Oqtl24plxcnsKpdnRLu/ihVz4cNPz9bPn8mLUQW4r3dUlh6emINtZdJczkbg==} + /@fastify/multipart/7.4.1: + resolution: {integrity: sha512-PgBJIg/1krx1nWIXGZSUFQMPHXeFv559TmpaswzmNkK6V/yn1xi4nkATqBetv7s5XbXhxzc75uaJgXWdUefxvQ==} dependencies: '@fastify/busboy': 1.1.0 '@fastify/deepmerge': 1.3.0 @@ -1400,14 +1419,14 @@ packages: mime: 3.0.0 dev: false - /@fastify/static/6.8.0: - resolution: {integrity: sha512-MNQp7KM0NIC+722OPN3MholnfvM+Vg2ao4OwbWWNJhAJEWOKGe4fJsEjIh3OkN0z5ymhklc7EXGCG0zDaIU5ZQ==} + /@fastify/static/6.9.0: + resolution: {integrity: sha512-9SBVNJi2+KTnfiW1WjiVXDsmUxliNI54OF1eOiaop264dh8FwXSuLmO62JXvx7+VD0vQXEqsyRbFCYUJ9aJxng==} dependencies: '@fastify/accept-negotiator': 1.0.0 '@fastify/send': 2.0.1 content-disposition: 0.5.4 fastify-plugin: 4.5.0 - glob: 8.0.3 + glob: 8.1.0 p-limit: 3.1.0 readable-stream: 4.3.0 dev: false @@ -1472,20 +1491,20 @@ packages: engines: {node: '>=8'} dev: true - /@jest/console/29.4.2: - resolution: {integrity: sha512-0I/rEJwMpV9iwi9cDEnT71a5nNGK9lj8Z4+1pRAU2x/thVXCDnaTGrvxyK+cAqZTFVFCiR+hfVrP4l2m+dCmQg==} + /@jest/console/29.4.3: + resolution: {integrity: sha512-W/o/34+wQuXlgqlPYTansOSiBnuxrTv61dEVkA6HNmpcgHLUjfaUbdqt6oVvOzaawwo9IdW9QOtMgQ1ScSZC4A==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.4.2 - '@types/node': 18.13.0 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 chalk: 4.1.2 - jest-message-util: 29.4.2 - jest-util: 29.4.2 + jest-message-util: 29.4.3 + jest-util: 29.4.3 slash: 3.0.0 dev: true - /@jest/core/29.4.2: - resolution: {integrity: sha512-KGuoQah0P3vGNlaS/l9/wQENZGNKGoWb+OPxh3gz+YzG7/XExvYu34MzikRndQCdM2S0tzExN4+FL37i6gZmCQ==} + /@jest/core/29.4.3: + resolution: {integrity: sha512-56QvBq60fS4SPZCuM7T+7scNrkGIe7Mr6PVIXUpu48ouvRaWOFqRPV91eifvFM0ay2HmfswXiGf97NGUN5KofQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -1493,32 +1512,32 @@ packages: node-notifier: optional: true dependencies: - '@jest/console': 29.4.2 - '@jest/reporters': 29.4.2 - '@jest/test-result': 29.4.2 - '@jest/transform': 29.4.2 - '@jest/types': 29.4.2 - '@types/node': 18.13.0 + '@jest/console': 29.4.3 + '@jest/reporters': 29.4.3 + '@jest/test-result': 29.4.3 + '@jest/transform': 29.4.3 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.7.1 exit: 0.1.2 graceful-fs: 4.2.10 - jest-changed-files: 29.4.2 - jest-config: 29.4.2_@types+node@18.13.0 - jest-haste-map: 29.4.2 - jest-message-util: 29.4.2 - jest-regex-util: 29.4.2 - jest-resolve: 29.4.2 - jest-resolve-dependencies: 29.4.2 - jest-runner: 29.4.2 - jest-runtime: 29.4.2 - jest-snapshot: 29.4.2 - jest-util: 29.4.2 - jest-validate: 29.4.2 - jest-watcher: 29.4.2 + jest-changed-files: 29.4.3 + jest-config: 29.4.3_@types+node@18.14.0 + jest-haste-map: 29.4.3 + jest-message-util: 29.4.3 + jest-regex-util: 29.4.3 + jest-resolve: 29.4.3 + jest-resolve-dependencies: 29.4.3 + jest-runner: 29.4.3 + jest-runtime: 29.4.3 + jest-snapshot: 29.4.3 + jest-util: 29.4.3 + jest-validate: 29.4.3 + jest-watcher: 29.4.3 micromatch: 4.0.5 - pretty-format: 29.4.2 + pretty-format: 29.4.3 slash: 3.0.0 strip-ansi: 6.0.1 transitivePeerDependencies: @@ -1533,14 +1552,14 @@ packages: '@jest/types': 27.5.1 dev: true - /@jest/environment/29.4.2: - resolution: {integrity: sha512-JKs3VUtse0vQfCaFGJRX1bir9yBdtasxziSyu+pIiEllAQOe4oQhdCYIf3+Lx+nGglFktSKToBnRJfD5QKp+NQ==} + /@jest/environment/29.4.3: + resolution: {integrity: sha512-dq5S6408IxIa+lr54zeqce+QgI+CJT4nmmA+1yzFgtcsGK8c/EyiUb9XQOgz3BMKrRDfKseeOaxj2eO8LlD3lA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/fake-timers': 29.4.2 - '@jest/types': 29.4.2 - '@types/node': 18.13.0 - jest-mock: 29.4.2 + '@jest/fake-timers': 29.4.3 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 + jest-mock: 29.4.3 dev: true /@jest/expect-utils/29.3.1: @@ -1550,49 +1569,49 @@ packages: jest-get-type: 29.4.2 dev: true - /@jest/expect-utils/29.4.2: - resolution: {integrity: sha512-Dd3ilDJpBnqa0GiPN7QrudVs0cczMMHtehSo2CSTjm3zdHx0RcpmhFNVEltuEFeqfLIyWKFI224FsMSQ/nsJQA==} + /@jest/expect-utils/29.4.3: + resolution: {integrity: sha512-/6JWbkxHOP8EoS8jeeTd9dTfc9Uawi+43oLKHfp6zzux3U2hqOOVnV3ai4RpDYHOccL6g+5nrxpoc8DmJxtXVQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - jest-get-type: 29.4.2 + jest-get-type: 29.4.3 dev: true - /@jest/expect/29.4.2: - resolution: {integrity: sha512-NUAeZVApzyaeLjfWIV/64zXjA2SS+NuUPHpAlO7IwVMGd5Vf9szTl9KEDlxY3B4liwLO31os88tYNHl6cpjtKQ==} + /@jest/expect/29.4.3: + resolution: {integrity: sha512-iktRU/YsxEtumI9zsPctYUk7ptpC+AVLLk1Ax3AsA4g1C+8OOnKDkIQBDHtD5hA/+VtgMd5AWI5gNlcAlt2vxQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - expect: 29.4.2 - jest-snapshot: 29.4.2 + expect: 29.4.3 + jest-snapshot: 29.4.3 transitivePeerDependencies: - supports-color dev: true - /@jest/fake-timers/29.4.2: - resolution: {integrity: sha512-Ny1u0Wg6kCsHFWq7A/rW/tMhIedq2siiyHyLpHCmIhP7WmcAmd2cx95P+0xtTZlj5ZbJxIRQi4OPydZZUoiSQQ==} + /@jest/fake-timers/29.4.3: + resolution: {integrity: sha512-4Hote2MGcCTWSD2gwl0dwbCpBRHhE6olYEuTj8FMowdg3oQWNKr2YuxenPQYZ7+PfqPY1k98wKDU4Z+Hvd4Tiw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.4.2 + '@jest/types': 29.4.3 '@sinonjs/fake-timers': 10.0.2 - '@types/node': 18.13.0 - jest-message-util: 29.4.2 - jest-mock: 29.4.2 - jest-util: 29.4.2 + '@types/node': 18.14.0 + jest-message-util: 29.4.3 + jest-mock: 29.4.3 + jest-util: 29.4.3 dev: true - /@jest/globals/29.4.2: - resolution: {integrity: sha512-zCk70YGPzKnz/I9BNFDPlK+EuJLk21ur/NozVh6JVM86/YYZtZHqxFFQ62O9MWq7uf3vIZnvNA0BzzrtxD9iyg==} + /@jest/globals/29.4.3: + resolution: {integrity: sha512-8BQ/5EzfOLG7AaMcDh7yFCbfRLtsc+09E1RQmRBI4D6QQk4m6NSK/MXo+3bJrBN0yU8A2/VIcqhvsOLFmziioA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 29.4.2 - '@jest/expect': 29.4.2 - '@jest/types': 29.4.2 - jest-mock: 29.4.2 + '@jest/environment': 29.4.3 + '@jest/expect': 29.4.3 + '@jest/types': 29.4.3 + jest-mock: 29.4.3 transitivePeerDependencies: - supports-color dev: true - /@jest/reporters/29.4.2: - resolution: {integrity: sha512-10yw6YQe75zCgYcXgEND9kw3UZZH5tJeLzWv4vTk/2mrS1aY50A37F+XT2hPO5OqQFFnUWizXD8k1BMiATNfUw==} + /@jest/reporters/29.4.3: + resolution: {integrity: sha512-sr2I7BmOjJhyqj9ANC6CTLsL4emMoka7HkQpcoMRlhCbQJjz2zsRzw0BDPiPyEFDXAbxKgGFYuQZiSJ1Y6YoTg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -1601,12 +1620,12 @@ packages: optional: true dependencies: '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.4.2 - '@jest/test-result': 29.4.2 - '@jest/transform': 29.4.2 - '@jest/types': 29.4.2 + '@jest/console': 29.4.3 + '@jest/test-result': 29.4.3 + '@jest/transform': 29.4.3 + '@jest/types': 29.4.3 '@jridgewell/trace-mapping': 0.3.17 - '@types/node': 18.13.0 + '@types/node': 18.14.0 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -1617,9 +1636,9 @@ packages: istanbul-lib-report: 3.0.0 istanbul-lib-source-maps: 4.0.1 istanbul-reports: 3.1.5 - jest-message-util: 29.4.2 - jest-util: 29.4.2 - jest-worker: 29.4.2 + jest-message-util: 29.4.3 + jest-util: 29.4.3 + jest-worker: 29.4.3 slash: 3.0.0 string-length: 4.0.2 strip-ansi: 6.0.1 @@ -1635,8 +1654,15 @@ packages: '@sinclair/typebox': 0.25.21 dev: true - /@jest/source-map/29.4.2: - resolution: {integrity: sha512-tIoqV5ZNgYI9XCKXMqbYe5JbumcvyTgNN+V5QW4My033lanijvCD0D4PI9tBw4pRTqWOc00/7X3KVvUh+qnF4Q==} + /@jest/schemas/29.4.3: + resolution: {integrity: sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.25.21 + dev: true + + /@jest/source-map/29.4.3: + resolution: {integrity: sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jridgewell/trace-mapping': 0.3.17 @@ -1644,41 +1670,41 @@ packages: graceful-fs: 4.2.10 dev: true - /@jest/test-result/29.4.2: - resolution: {integrity: sha512-HZsC3shhiHVvMtP+i55MGR5bPcc3obCFbA5bzIOb8pCjwBZf11cZliJncCgaVUbC5yoQNuGqCkC0Q3t6EItxZA==} + /@jest/test-result/29.4.3: + resolution: {integrity: sha512-Oi4u9NfBolMq9MASPwuWTlC5WvmNRwI4S8YrQg5R5Gi47DYlBe3sh7ILTqi/LGrK1XUE4XY9KZcQJTH1WJCLLA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/console': 29.4.2 - '@jest/types': 29.4.2 + '@jest/console': 29.4.3 + '@jest/types': 29.4.3 '@types/istanbul-lib-coverage': 2.0.4 collect-v8-coverage: 1.0.1 dev: true - /@jest/test-sequencer/29.4.2: - resolution: {integrity: sha512-9Z2cVsD6CcObIVrWigHp2McRJhvCxL27xHtrZFgNC1RwnoSpDx6fZo8QYjJmziFlW9/hr78/3sxF54S8B6v8rg==} + /@jest/test-sequencer/29.4.3: + resolution: {integrity: sha512-yi/t2nES4GB4G0mjLc0RInCq/cNr9dNwJxcGg8sslajua5Kb4kmozAc+qPLzplhBgfw1vLItbjyHzUN92UXicw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/test-result': 29.4.2 + '@jest/test-result': 29.4.3 graceful-fs: 4.2.10 - jest-haste-map: 29.4.2 + jest-haste-map: 29.4.3 slash: 3.0.0 dev: true - /@jest/transform/29.4.2: - resolution: {integrity: sha512-kf1v5iTJHn7p9RbOsBuc/lcwyPtJaZJt5885C98omWz79NIeD3PfoiiaPSu7JyCyFzNOIzKhmMhQLUhlTL9BvQ==} + /@jest/transform/29.4.3: + resolution: {integrity: sha512-8u0+fBGWolDshsFgPQJESkDa72da/EVwvL+II0trN2DR66wMwiQ9/CihaGfHdlLGFzbBZwMykFtxuwFdZqlKwg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/core': 7.20.12 - '@jest/types': 29.4.2 + '@jest/types': 29.4.3 '@jridgewell/trace-mapping': 0.3.17 babel-plugin-istanbul: 6.1.1 chalk: 4.1.2 convert-source-map: 2.0.0 fast-json-stable-stringify: 2.1.0 graceful-fs: 4.2.10 - jest-haste-map: 29.4.2 - jest-regex-util: 29.4.2 - jest-util: 29.4.2 + jest-haste-map: 29.4.3 + jest-regex-util: 29.4.3 + jest-util: 29.4.3 micromatch: 4.0.5 pirates: 4.0.5 slash: 3.0.0 @@ -1693,19 +1719,19 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/yargs': 16.0.5 chalk: 4.1.2 dev: true - /@jest/types/29.4.2: - resolution: {integrity: sha512-CKlngyGP0fwlgC1BRUtPZSiWLBhyS9dKwKmyGxk8Z6M82LBEGB2aLQSg+U1MyLsU+M7UjnlLllBM2BLWKVm/Uw==} + /@jest/types/29.4.3: + resolution: {integrity: sha512-bPYfw8V65v17m2Od1cv44FH+SiKW7w2Xu7trhcdTLUmSv85rfKsP+qXSjO4KGJr4dtPSzl/gvslZBXctf1qGEA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/schemas': 29.4.2 + '@jest/schemas': 29.4.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/yargs': 17.0.19 chalk: 4.1.2 dev: true @@ -1766,7 +1792,6 @@ packages: /@mapbox/node-pre-gyp/1.0.9: resolution: {integrity: sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==} - hasBin: true dependencies: detect-libc: 2.0.1 https-proxy-agent: 5.0.1 @@ -1795,7 +1820,7 @@ packages: filenamify: 5.1.1 got: 11.8.5 os-filter-obj: 2.0.0 - dev: true + dev: false /@msgpackr-extract/msgpackr-extract-darwin-arm64/2.2.0: resolution: {integrity: sha512-Z9LFPzfoJi4mflGWV+rv7o7ZbMU5oAU9VmzCgL240KnqDW65Y2HFCT3MW06/ITJSnbVLacmcEJA8phywK7JinQ==} @@ -1839,8 +1864,8 @@ packages: requiresBuild: true optional: true - /@nestjs/common/9.3.7_mnr6j2del53muneqly5h4y27ai: - resolution: {integrity: sha512-7hwY2lIkmB+K8wPSP9T8auzKPSlO15Gzujl3/ZxLX9SBt6B7N5Niv5E+5AzOcfL+h2X8JGysMG620hiGbUsT9A==} + /@nestjs/common/9.3.9_mnr6j2del53muneqly5h4y27ai: + resolution: {integrity: sha512-GshTD9Xz+wD2em6NyzU4NXw5IXMUmapgDgD+iuj6XL0258hvDwODmNk37mBBnZvTZlqER+krvIUKnS34etqF/A==} peerDependencies: cache-manager: <=5 class-transformer: '*' @@ -1862,8 +1887,8 @@ packages: uid: 2.0.1 dev: false - /@nestjs/core/9.3.7_ojttuksvundfk3amlyo674va74: - resolution: {integrity: sha512-FXGVivZiujZl1aJF6jdPpg1XnLKp7kDhVGhWhJtnpv2IW/cz/YQHD2uMz/o+GZ9TCZxsGlxg79jbcuJITG11iQ==} + /@nestjs/core/9.3.9_jrq2rdgfp2sx67wmylmrqliwxe: + resolution: {integrity: sha512-9g1A1G9eirLXEpH21rc6dKb08zHc2+adhCRz8NW39hbejcsxxD72FApJzt4QBQAKvu862ixt/tdpStnFT7lOSw==} requiresBuild: true peerDependencies: '@nestjs/common': ^9.0.0 @@ -1880,7 +1905,7 @@ packages: '@nestjs/websockets': optional: true dependencies: - '@nestjs/common': 9.3.7_mnr6j2del53muneqly5h4y27ai + '@nestjs/common': 9.3.9_mnr6j2del53muneqly5h4y27ai '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -1893,8 +1918,8 @@ packages: - encoding dev: false - /@nestjs/testing/9.3.7_zmctrozi6xxh3beaccl6ebop6u: - resolution: {integrity: sha512-tNJuNYfDXMJzTgQXRwnQtLdd7PIIqrSsZ//TP/wnqPnbUpTkWMCSK5exWfPWvdyS8P3Fy2wy5hNj8Rvp9+KvYw==} + /@nestjs/testing/9.3.9_77foi4w27ghy47yutmnzv7krjy: + resolution: {integrity: sha512-+mPvSVvSC2SAkYgZZv1mOI2xsdGc1pmq7/sem7iin/JDoFtlvoGSK+pfZHD3IV3EpYtq1v/8/5gi+UFH9yZnDg==} peerDependencies: '@nestjs/common': ^9.0.0 '@nestjs/core': ^9.0.0 @@ -1906,8 +1931,8 @@ packages: '@nestjs/platform-express': optional: true dependencies: - '@nestjs/common': 9.3.7_mnr6j2del53muneqly5h4y27ai - '@nestjs/core': 9.3.7_ojttuksvundfk3amlyo674va74 + '@nestjs/common': 9.3.9_mnr6j2del53muneqly5h4y27ai + '@nestjs/core': 9.3.9_jrq2rdgfp2sx67wmylmrqliwxe tslib: 2.5.0 dev: false @@ -1940,7 +1965,6 @@ packages: /@npmcli/move-file/2.0.1: resolution: {integrity: sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This functionality has been moved to @npmcli/fs dependencies: mkdirp: 1.0.4 rimraf: 3.0.2 @@ -1967,7 +1991,6 @@ packages: /@nuxtjs/opencollective/0.3.2: resolution: {integrity: sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==} engines: {node: '>=8.0.0', npm: '>=5.0.0'} - hasBin: true dependencies: chalk: 4.1.2 consola: 2.15.3 @@ -2061,7 +2084,7 @@ packages: - encoding dev: true - /@rollup/plugin-alias/4.0.3_rollup@3.14.0: + /@rollup/plugin-alias/4.0.3_rollup@3.17.2: resolution: {integrity: sha512-ZuDWE1q4PQDhvm/zc5Prun8sBpLJy41DMptYrS6MhAy9s9kL/doN1613BWfEchGVfKxzliJ3BjbOPizXX38DbQ==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2070,11 +2093,11 @@ packages: rollup: optional: true dependencies: - rollup: 3.14.0 + rollup: 3.17.2 slash: 4.0.0 dev: false - /@rollup/plugin-json/6.0.0_rollup@3.14.0: + /@rollup/plugin-json/6.0.0_rollup@3.17.2: resolution: {integrity: sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2083,11 +2106,11 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.0.2_rollup@3.14.0 - rollup: 3.14.0 + '@rollup/pluginutils': 5.0.2_rollup@3.17.2 + rollup: 3.17.2 dev: false - /@rollup/pluginutils/5.0.2_rollup@3.14.0: + /@rollup/pluginutils/5.0.2_rollup@3.17.2: resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2099,7 +2122,7 @@ packages: '@types/estree': 1.0.0 estree-walker: 2.0.2 picomatch: 2.3.1 - rollup: 3.14.0 + rollup: 3.17.2 dev: false /@sideway/address/4.1.4: @@ -2123,7 +2146,7 @@ packages: /@sindresorhus/is/4.6.0: resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} - dev: true + dev: false /@sindresorhus/is/5.3.0: resolution: {integrity: sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==} @@ -2144,8 +2167,8 @@ packages: resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} dev: false - /@swc/cli/0.1.61_nh7rjdlljrxbrljvydz3oxxjt4: - resolution: {integrity: sha512-HeYMJ+8gKfJzM9xgcZqTpAHJYAJVGSljBSmWRUx2B6UiGraLsLjEcqxITwi6/t6Af+QboBMiQX5Wwll89oPK7g==} + /@swc/cli/0.1.62_wyduggqpvtv2oy5nc7cgtozgpy: + resolution: {integrity: sha512-kOFLjKY3XH1DWLfXL1/B5MizeNorHR8wHKEi92S/Zi9Md/AK17KSqR8MgyRJ6C1fhKHvbBCl8wboyKAFXStkYw==} engines: {node: '>= 12.13'} hasBin: true peerDependencies: @@ -2156,133 +2179,139 @@ packages: optional: true dependencies: '@mole-inc/bin-wrapper': 8.0.1 - '@swc/core': 1.3.34 + '@swc/core': 1.3.36 chokidar: 3.5.3 commander: 7.2.0 fast-glob: 3.2.12 semver: 7.3.8 slash: 3.0.0 source-map: 0.7.4 - dev: true + dev: false + + /@swc/core-android-arm64/1.3.11: + resolution: {integrity: sha512-M7FamR3kFpVTyTw73FzKcOZmS7/TWHX75eqtwBTaU9fW4shf0KTLr/h9DnMxNKAnwUMeub/lqlINUe5EKFIKwQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [android] + requiresBuild: true + dependencies: + '@swc/wasm': 1.2.130 + dev: false + optional: true - /@swc/core-darwin-arm64/1.3.34: - resolution: {integrity: sha512-m7+kybVLO9uo/TiGBlf/ISmpmm27I/NrFEBGOVBF2xNOs5BY1XHHM6ddbPPckQa38C19nWeAzdJPwGzJw+qO3A==} + /@swc/core-darwin-arm64/1.3.36: + resolution: {integrity: sha512-lsP+C8p9cC/Vd9uAbtxpEnM8GoJI/MMnVuXak7OlxOtDH9/oTwmAcAQTfNGNaH19d2FAIRwf+5RbXCPnxa2Zjw==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] requiresBuild: true - dev: true optional: true - /@swc/core-darwin-x64/1.3.34: - resolution: {integrity: sha512-arH7LtcOhuC1wy88qgbCO/E5NnBThbxv9HhjScDfoUPRunyvT9whEvSK0eXCXxGvDAiAtXIrW3blIrteOsQaOQ==} + /@swc/core-darwin-x64/1.3.36: + resolution: {integrity: sha512-jaLXsozWN5xachl9fPxDMi5nbWq1rRxPAt6ISeiYB6RJk0MQKH1634pOweBBem2pUDDzwDFXFw6f22LTm/cFvA==} engines: {node: '>=10'} cpu: [x64] os: [darwin] requiresBuild: true - dev: true optional: true - /@swc/core-linux-arm-gnueabihf/1.3.34: - resolution: {integrity: sha512-+pvjXsXTBzSxEL3U9869y3Am/3yo6kQfU6VGVAebgLv+pjM+mIHywbgo1Uxw+pgpTuD38BsrtYcaPNeBICN/wA==} + /@swc/core-linux-arm-gnueabihf/1.3.36: + resolution: {integrity: sha512-vcBdTHjoEpvJDbFlgto+S6VwAHzLA9GyCiuNcTU2v4KNQlFzhbO4A4PMfMCb/Z0RLJEr16tirfHdWIxjU3h8nw==} engines: {node: '>=10'} cpu: [arm] os: [linux] requiresBuild: true - dev: true optional: true - /@swc/core-linux-arm64-gnu/1.3.34: - resolution: {integrity: sha512-hOV1n1j+mDAgp19J+aeAnW4itMTWbaPbSbhEvLsNbVB00LoL6q6pUkWvCi+UbrejV6BIyyE9t7F5fU26SdKR8A==} + /@swc/core-linux-arm64-gnu/1.3.36: + resolution: {integrity: sha512-o7f5OsvwWppJo+qIZmrGO5+XC6DPt6noecSbRHjF6o1YAcR13ETPC14k1eC9H1YbQwpyCFNVAFXyNcUbCeQyrQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] requiresBuild: true - dev: true optional: true - /@swc/core-linux-arm64-musl/1.3.34: - resolution: {integrity: sha512-r2/Hegp1DRSzG+kg36Tujdn+WX+gO/2wQpVj/g6RPxIPdjy53OOf+UwvJ23Ecn5ZbyJcgKhhTN6H6/ZNHQPqjQ==} + /@swc/core-linux-arm64-musl/1.3.36: + resolution: {integrity: sha512-FSHPngMi3c0fuGt9yY2Ubn5UcELi3EiPLJxBSC3X8TF9atI/WHZzK9PE9Gtn0C/LyRh4CoyOugDtSOPzGYmLQg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] requiresBuild: true - dev: true optional: true - /@swc/core-linux-x64-gnu/1.3.34: - resolution: {integrity: sha512-jPxxAo7XlAT7bdIi49PtYN/K1TAxvpVS7otteJLhThOPPTVblIDg63U94ivp3mVQJb3WFH4KNYatEXypyvXppQ==} + /@swc/core-linux-x64-gnu/1.3.36: + resolution: {integrity: sha512-PHSsH2rek5pr3e0K09VgWAbrWK2vJhaI7MW9TPoTjyACYjcs3WwjcjQ30MghXUs2Dc/bXjWAOi9KFTjq/uCyFg==} engines: {node: '>=10'} cpu: [x64] os: [linux] requiresBuild: true - dev: true optional: true - /@swc/core-linux-x64-musl/1.3.34: - resolution: {integrity: sha512-eJaUuhvnNtcwpK9Mil4hZTSYZqG519gX5AQQ2VZOhrWBEBJi+jM0RXAvWdESsaXpS7W0CRtbmEXqeUff6UEgpQ==} + /@swc/core-linux-x64-musl/1.3.36: + resolution: {integrity: sha512-4LfMYQHzozHCKkIcmQy83b+4SpI+mOp6sYNbXqSRz5dYvTVjegKZXe596P1U/87cK2cgR4uYvkgkgBXquaWvwQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] requiresBuild: true - dev: true optional: true - /@swc/core-win32-arm64-msvc/1.3.34: - resolution: {integrity: sha512-KFdeC5bXDcxIQ/1J5Pjj8BOblRFjh89TTJxujxAhKdoD1k0NV9BKEZACja2cTBz0hWD4cYlBX0cESVdL2rkm3w==} + /@swc/core-win32-arm64-msvc/1.3.36: + resolution: {integrity: sha512-7y3dDcun79TAjCyk3Iv0eOMw1X/KNQbkVyKOGqnEgq9g22F8F1FoUGKHNTzUqVdzpHeJSsHgW5PlkEkl3c/d9w==} engines: {node: '>=10'} cpu: [arm64] os: [win32] requiresBuild: true - dev: true optional: true - /@swc/core-win32-ia32-msvc/1.3.34: - resolution: {integrity: sha512-MgWkAQDiWIHfJL5b5aoogenGIt3qcqBSvwLnDQqSWEhkodZjHyCWpQFuaa7Y6ER3pKUIZ5kR8O9aAkDmF39awQ==} + /@swc/core-win32-ia32-msvc/1.3.36: + resolution: {integrity: sha512-zK0VR3B4LX5hzQ+7eD+K+FkxJlJg5Lo36BeahMzQ+/i0IURpnuyFlW88sdkFkMsc2swdU6bpvxLZeIRQ3W4OUg==} engines: {node: '>=10'} cpu: [ia32] os: [win32] requiresBuild: true - dev: true optional: true - /@swc/core-win32-x64-msvc/1.3.34: - resolution: {integrity: sha512-UhaikgVRYBZZdMI7Zo4/eUyYLnjGrC6QAn9aggt1+PiFIM9tXpX8aONUL3LoLkhQhd+6iWygfQ298RRxjKAKuw==} + /@swc/core-win32-x64-msvc/1.3.36: + resolution: {integrity: sha512-2bIjr9DhAckGiXZEvj6z2z7ECPcTimG+wD0VuQTvr+wkx46uAJKl5Kq+Zk+dd15ErL7JGUtCet1T7bf1k4FwvQ==} engines: {node: '>=10'} cpu: [x64] os: [win32] requiresBuild: true - dev: true optional: true - /@swc/core/1.3.34: - resolution: {integrity: sha512-kaOCGRpciMEs2FpCUFaPJSNHgggFteOGZToM88uL5k/CEy0nU/6wzl8kUO5J+rI/8/8vN7qyhM1Ajhyj3WCSsw==} + /@swc/core/1.3.36: + resolution: {integrity: sha512-Ogrd9uRNIj7nHjXxG66UlKBIcXESUenJ7OD6K2a8p82qlg6ne7Ne5Goiipm/heHYhSfVmjcnRWL9ZJ4gv+YCPA==} engines: {node: '>=10'} requiresBuild: true optionalDependencies: - '@swc/core-darwin-arm64': 1.3.34 - '@swc/core-darwin-x64': 1.3.34 - '@swc/core-linux-arm-gnueabihf': 1.3.34 - '@swc/core-linux-arm64-gnu': 1.3.34 - '@swc/core-linux-arm64-musl': 1.3.34 - '@swc/core-linux-x64-gnu': 1.3.34 - '@swc/core-linux-x64-musl': 1.3.34 - '@swc/core-win32-arm64-msvc': 1.3.34 - '@swc/core-win32-ia32-msvc': 1.3.34 - '@swc/core-win32-x64-msvc': 1.3.34 - dev: true + '@swc/core-darwin-arm64': 1.3.36 + '@swc/core-darwin-x64': 1.3.36 + '@swc/core-linux-arm-gnueabihf': 1.3.36 + '@swc/core-linux-arm64-gnu': 1.3.36 + '@swc/core-linux-arm64-musl': 1.3.36 + '@swc/core-linux-x64-gnu': 1.3.36 + '@swc/core-linux-x64-musl': 1.3.36 + '@swc/core-win32-arm64-msvc': 1.3.36 + '@swc/core-win32-ia32-msvc': 1.3.36 + '@swc/core-win32-x64-msvc': 1.3.36 - /@swc/jest/0.2.24_@swc+core@1.3.34: + /@swc/jest/0.2.24_@swc+core@1.3.36: resolution: {integrity: sha512-fwgxQbM1wXzyKzl1+IW0aGrRvAA8k0Y3NxFhKigbPjOJ4mCKnWEcNX9HQS3gshflcxq8YKhadabGUVfdwjCr6Q==} engines: {npm: '>= 7.0.0'} peerDependencies: '@swc/core': '*' dependencies: '@jest/create-cache-key-function': 27.5.1 - '@swc/core': 1.3.34 + '@swc/core': 1.3.36 jsonc-parser: 3.2.0 dev: true + /@swc/wasm/1.2.130: + resolution: {integrity: sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==} + requiresBuild: true + dev: false + optional: true + /@syuilo/aiscript/0.12.4: resolution: {integrity: sha512-fIWEAHsnw9vMWTcjmSM2rCsadhb+BLirLDLW6lnRw3PFC8LV76Np8Ih8ssfAb00udh6oiGBSj+WiBdkMagbjmQ==} dependencies: @@ -2297,7 +2326,7 @@ packages: engines: {node: '>=10'} dependencies: defer-to-connect: 2.0.1 - dev: true + dev: false /@szmarczak/http-timer/5.0.1: resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} @@ -2352,7 +2381,6 @@ packages: /@tensorflow/tfjs-core/4.2.0: resolution: {integrity: sha512-uuHkiWVC8b00ngFbHvAV7J7haRlN/9PEdeenCi0CzBjgKd7aN25wPWaoN0TSQcU+GT4FJ8mofMZ9VBYZ/s/WLg==} engines: {yarn: '>= 1.3.2'} - requiresBuild: true dependencies: '@types/long': 4.0.2 '@types/offscreencanvas': 2019.7.0 @@ -2411,7 +2439,6 @@ packages: /@tensorflow/tfjs/4.2.0_seedrandom@3.0.5: resolution: {integrity: sha512-iZmtyGC9IJkx+TpFnkgDol8BHv2BU3zJ01HyNcuvnm1w1EqoNe+1n8bwvLzI/sxHMcHTqzuu7VugMaphryxE+A==} - hasBin: true dependencies: '@tensorflow/tfjs-backend-cpu': 4.2.0_tkoh6rxfpzme3tc2ndqbqcrg7y '@tensorflow/tfjs-backend-webgl': 4.2.0_tkoh6rxfpzme3tc2ndqbqcrg7y @@ -2431,6 +2458,7 @@ packages: /@tokenizer/token/0.3.0: resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + dev: false /@tootallnate/once/2.0.0: resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} @@ -2440,7 +2468,7 @@ packages: /@types/accepts/1.3.5: resolution: {integrity: sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/archiver/5.3.1: @@ -2484,9 +2512,8 @@ packages: /@types/bull/4.10.0: resolution: {integrity: sha512-RkYW8K2H3J76HT6twmHYbzJ0GtLDDotpLP9ah9gtiA7zfF6peBH1l5fEiK0oeIZ3/642M7Jcb9sPmor8Vf4w6g==} - deprecated: This is a stub types definition. bull provides its own type definitions, so you do not need this installed. dependencies: - bull: 4.10.3 + bull: 4.10.4 transitivePeerDependencies: - supports-color dev: true @@ -2496,13 +2523,12 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/responselike': 1.0.0 - dev: true + dev: false /@types/cbor/6.0.0: resolution: {integrity: sha512-mGQ1lbYOwVti5Xlarn1bTeBZqgY0kstsdjnkoEovgohYKdBjGejHyNGXHdMBeqyQazIv32Jjp33+5pBEaSRy2w==} - deprecated: This is a stub types definition. cbor provides its own type definitions, so you do not need this installed. dependencies: cbor: 8.1.0 dev: true @@ -2539,34 +2565,34 @@ packages: /@types/fluent-ffmpeg/2.1.20: resolution: {integrity: sha512-B+OvhCdJ3LgEq2PhvWNOiB/EfwnXLElfMCgc4Z1K5zXgSfo9I6uGKwR/lqmNPFQuebNnes7re3gqkV77SyypLg==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/glob-stream/6.1.1: resolution: {integrity: sha512-AGOUTsTdbPkRS0qDeyeS+6KypmfVpbT5j23SN8UPG63qjKXNKjXn6V9wZUr8Fin0m9l8oGYaPK8b2WUMF8xI1A==} dependencies: '@types/glob': 8.0.1 - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/glob/8.0.0: resolution: {integrity: sha512-l6NQsDDyQUVeoTynNpC9uRvCUint/gSUXQA2euwmTuWGvPY5LSDUu6tkCtJB2SvGQlJQzLaKqcGZP4//7EDveA==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/glob/8.0.1: resolution: {integrity: sha512-8bVUjXZvJacUFkJXHdyZ9iH1Eaj5V7I8c4NdH5sQJsdXkqT4CA5Dhb4yb4VE/3asyx4L9ayZr1NIhTsWHczmMw==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/graceful-fs/4.1.6: resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/gulp-rename/2.0.1: @@ -2586,11 +2612,12 @@ packages: /@types/http-cache-semantics/4.0.1: resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==} + dev: false /@types/ioredis/4.28.10: resolution: {integrity: sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/istanbul-lib-coverage/2.0.4: @@ -2623,7 +2650,7 @@ packages: /@types/jsdom/21.1.0: resolution: {integrity: sha512-leWreJOdnuIxq9Y70tBVm/bvTuh31DSlF/r4l7Cfi4uhVQqLHD0Q4v301GMisEMwwbMgF7ZKxuZ+Jbd4NcdmRw==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/tough-cookie': 4.0.2 parse5: 7.1.2 dev: true @@ -2647,8 +2674,8 @@ packages: /@types/keyv/3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 18.13.0 - dev: true + '@types/node': 18.14.0 + dev: false /@types/long/4.0.2: resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} @@ -2669,13 +2696,12 @@ packages: /@types/node-fetch/2.6.2: resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 form-data: 3.0.1 dev: false /@types/node-fetch/3.0.3: resolution: {integrity: sha512-HhggYPH5N+AQe/OmN6fmhKmRRt2XuNJow+R3pQwJxOOF9GuwM7O2mheyGeIrs5MOIeNjDEdgdoyHBOrFeJBR3g==} - deprecated: This is a stub types definition. node-fetch provides its own type definitions, so you do not need this installed. dependencies: node-fetch: 3.3.0 dev: true @@ -2687,19 +2713,19 @@ packages: /@types/node/18.11.18: resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} - /@types/node/18.13.0: - resolution: {integrity: sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==} + /@types/node/18.14.0: + resolution: {integrity: sha512-5EWrvLmglK+imbCJY0+INViFWUHg1AHel1sq4ZVSfdcNqGy9Edv3UB9IIzzg+xPaUcAgZYcfVs2fBcwDeZzU0A==} /@types/nodemailer/6.4.7: resolution: {integrity: sha512-f5qCBGAn/f0qtRcd4SEn88c8Fp3Swct1731X4ryPKqS61/A3LmmzN8zaEz7hneJvpjFbUUgY7lru/B/7ODTazg==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/oauth/0.9.1: resolution: {integrity: sha512-a1iY62/a3yhZ7qH7cNUsxoI3U/0Fe9+RnuFrpTKr+0WVOzbKlSLojShCKe20aOD1Sppv+i8Zlq0pLDuTJnwS4A==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/offscreencanvas/2019.3.0: @@ -2713,7 +2739,7 @@ packages: /@types/pg/8.6.6: resolution: {integrity: sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 pg-protocol: 1.5.0 pg-types: 2.2.0 dev: true @@ -2733,7 +2759,7 @@ packages: /@types/qrcode/1.5.0: resolution: {integrity: sha512-x5ilHXRxUPIMfjtM+1vf/GPTRWZ81nqscursm5gMznJeK9M0YnZ1c3bEvRLQ0zSSgedLx1J6MGL231ObQGGhaA==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/random-seed/0.3.3: @@ -2746,7 +2772,6 @@ packages: /@types/redis/4.0.11: resolution: {integrity: sha512-bI+gth8La8Wg/QCR1+V1fhrL9+LZUSWfcqpOj2Kc80ZQ4ffbdL173vQd5wovmoV9i071FU9oP2g6etLuEwb6Rg==} - deprecated: This is a stub types definition. redis provides its own type definitions, so you do not need this installed. dependencies: redis: 4.5.1 dev: true @@ -2758,8 +2783,8 @@ packages: /@types/responselike/1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 18.13.0 - dev: true + '@types/node': 18.14.0 + dev: false /@types/sanitize-html/2.8.0: resolution: {integrity: sha512-Uih6caOm3DsBYnVGOYn0A9NoTNe1c4aPStmHC/YA2JrpP9kx//jzaRcIklFvSpvVQEcpl/ZCr4DgISSf/YxTvg==} @@ -2786,7 +2811,7 @@ packages: /@types/sharp/0.31.1: resolution: {integrity: sha512-5nWwamN9ZFHXaYEincMSuza8nNfOof8nmO+mcI+Agx1uMUk4/pQnNIcix+9rLPXzKrm1pS34+6WRDbDV0Jn7ag==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/sinonjs__fake-timers/8.1.1: @@ -2801,12 +2826,6 @@ packages: resolution: {integrity: sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==} dev: true - /@types/speakeasy/2.0.7: - resolution: {integrity: sha512-JEcOhN2SQCoX86ZfiZEe8px84sVJtivBXMZfOVyARTYEj0hrwwbj1nF0FwEL3nJSoEV6uTbcdLllMKBgAYHWCQ==} - dependencies: - '@types/node': 18.13.0 - dev: true - /@types/stack-utils/2.0.1: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} dev: true @@ -2834,7 +2853,7 @@ packages: /@types/undertaker/1.2.8: resolution: {integrity: sha512-gW3PRqCHYpo45XFQHJBhch7L6hytPsIe0QeLujlnFsjHPnXLhJcPdN6a9368d7aIQgH2I/dUTPFBlGeSNA3qOg==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/undertaker-registry': 1.0.1 async-done: 1.3.2 dev: true @@ -2842,7 +2861,7 @@ packages: /@types/unzipper/0.10.5: resolution: {integrity: sha512-NrLJb29AdnBARpg9S/4ktfPEisbJ0AvaaAr3j7Q1tg8AgcEUsq2HqbNzvgLRoWyRtjzeLEv7vuL39u1mrNIyNA==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/uuid/9.0.0: @@ -2852,14 +2871,14 @@ packages: /@types/vary/1.1.0: resolution: {integrity: sha512-LQWqrIa0dvEOOH37lGksMEXbypRLUFqu6Gx0pmX7zIUisD2I/qaVgEX/vJ/PSVSW0Hk6yz1BNkFpqg6dZm3Wug==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/vinyl-fs/2.4.12: resolution: {integrity: sha512-LgBpYIWuuGsihnlF+OOWWz4ovwCYlT03gd3DuLwex50cYZLmX3yrW+sFF9ndtmh7zcZpS6Ri47PrIu+fV+sbXw==} dependencies: '@types/glob-stream': 6.1.1 - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/vinyl': 2.0.7 dev: true @@ -2867,12 +2886,12 @@ packages: resolution: {integrity: sha512-4UqPv+2567NhMQuMLdKAyK4yzrfCqwaTt6bLhHEs8PFcxbHILsrxaY63n4wgE/BRLDWDQeI+WcTmkXKExh9hQg==} dependencies: '@types/expect': 1.20.4 - '@types/node': 18.13.0 + '@types/node': 18.14.0 /@types/web-push/3.3.2: resolution: {integrity: sha512-JxWGVL/m7mWTIg4mRYO+A6s0jPmBkr4iJr39DqJpRJAc+jrPiEe1/asmkwerzRon8ZZDxaZJpsxpv0Z18Wo9gw==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/webgl-ext/0.0.30: @@ -2882,13 +2901,13 @@ packages: /@types/websocket/1.0.5: resolution: {integrity: sha512-NbsqiNX9CnEfC1Z0Vf4mE1SgAJ07JnRYcNex7AJ9zAVzmiGHmjKFEk7O4TJIsgv2B1sLEb6owKFZrACwdYngsQ==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/ws/8.5.4: resolution: {integrity: sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/yargs-parser/21.0.0: @@ -2911,12 +2930,12 @@ packages: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true optional: true - /@typescript-eslint/eslint-plugin/5.51.0_b635kmla6dsb4frxfihkw4m47e: - resolution: {integrity: sha512-wcAwhEWm1RgNd7dxD/o+nnLW8oH+6RK1OGnmbmkj/GGoDPV1WWMVP0FXYQBivKHdwM1pwii3bt//RC62EriIUQ==} + /@typescript-eslint/eslint-plugin/5.52.0_6cfvjsbua5ptj65675bqcn6oza: + resolution: {integrity: sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -2926,12 +2945,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 - '@typescript-eslint/scope-manager': 5.51.0 - '@typescript-eslint/type-utils': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 - '@typescript-eslint/utils': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/parser': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm + '@typescript-eslint/scope-manager': 5.52.0 + '@typescript-eslint/type-utils': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm + '@typescript-eslint/utils': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm debug: 4.3.4 - eslint: 8.33.0 + eslint: 8.34.0 grapheme-splitter: 1.0.4 ignore: 5.2.4 natural-compare-lite: 1.4.0 @@ -2943,8 +2962,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser/5.51.0_4vsywjlpuriuw3tl5oq6zy5a64: - resolution: {integrity: sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA==} + /@typescript-eslint/parser/5.52.0_7kw3g6rralp5ps6mg3uyzz6azm: + resolution: {integrity: sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -2953,26 +2972,26 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 5.51.0 - '@typescript-eslint/types': 5.51.0 - '@typescript-eslint/typescript-estree': 5.51.0_typescript@4.9.5 + '@typescript-eslint/scope-manager': 5.52.0 + '@typescript-eslint/types': 5.52.0 + '@typescript-eslint/typescript-estree': 5.52.0_typescript@4.9.5 debug: 4.3.4 - eslint: 8.33.0 + eslint: 8.34.0 typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager/5.51.0: - resolution: {integrity: sha512-gNpxRdlx5qw3yaHA0SFuTjW4rxeYhpHxt491PEcKF8Z6zpq0kMhe0Tolxt0qjlojS+/wArSDlj/LtE69xUJphQ==} + /@typescript-eslint/scope-manager/5.52.0: + resolution: {integrity: sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.51.0 - '@typescript-eslint/visitor-keys': 5.51.0 + '@typescript-eslint/types': 5.52.0 + '@typescript-eslint/visitor-keys': 5.52.0 dev: true - /@typescript-eslint/type-utils/5.51.0_4vsywjlpuriuw3tl5oq6zy5a64: - resolution: {integrity: sha512-QHC5KKyfV8sNSyHqfNa0UbTbJ6caB8uhcx2hYcWVvJAZYJRBo5HyyZfzMdRx8nvS+GyMg56fugMzzWnojREuQQ==} + /@typescript-eslint/type-utils/5.52.0_7kw3g6rralp5ps6mg3uyzz6azm: + resolution: {integrity: sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -2981,23 +3000,23 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.51.0_typescript@4.9.5 - '@typescript-eslint/utils': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/typescript-estree': 5.52.0_typescript@4.9.5 + '@typescript-eslint/utils': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm debug: 4.3.4 - eslint: 8.33.0 + eslint: 8.34.0 tsutils: 3.21.0_typescript@4.9.5 typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types/5.51.0: - resolution: {integrity: sha512-SqOn0ANn/v6hFn0kjvLwiDi4AzR++CBZz0NV5AnusT2/3y32jdc0G4woXPWHCumWtUXZKPAS27/9vziSsC9jnw==} + /@typescript-eslint/types/5.52.0: + resolution: {integrity: sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree/5.51.0_typescript@4.9.5: - resolution: {integrity: sha512-TSkNupHvNRkoH9FMA3w7TazVFcBPveAAmb7Sz+kArY6sLT86PA5Vx80cKlYmd8m3Ha2SwofM1KwraF24lM9FvA==} + /@typescript-eslint/typescript-estree/5.52.0_typescript@4.9.5: + resolution: {integrity: sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -3005,8 +3024,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.51.0 - '@typescript-eslint/visitor-keys': 5.51.0 + '@typescript-eslint/types': 5.52.0 + '@typescript-eslint/visitor-keys': 5.52.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 @@ -3017,82 +3036,82 @@ packages: - supports-color dev: true - /@typescript-eslint/utils/5.51.0_4vsywjlpuriuw3tl5oq6zy5a64: - resolution: {integrity: sha512-76qs+5KWcaatmwtwsDJvBk4H76RJQBFe+Gext0EfJdC3Vd2kpY2Pf//OHHzHp84Ciw0/rYoGTDnIAr3uWhhJYw==} + /@typescript-eslint/utils/5.52.0_7kw3g6rralp5ps6mg3uyzz6azm: + resolution: {integrity: sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: '@types/json-schema': 7.0.11 '@types/semver': 7.3.13 - '@typescript-eslint/scope-manager': 5.51.0 - '@typescript-eslint/types': 5.51.0 - '@typescript-eslint/typescript-estree': 5.51.0_typescript@4.9.5 - eslint: 8.33.0 + '@typescript-eslint/scope-manager': 5.52.0 + '@typescript-eslint/types': 5.52.0 + '@typescript-eslint/typescript-estree': 5.52.0_typescript@4.9.5 + eslint: 8.34.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0_eslint@8.33.0 + eslint-utils: 3.0.0_eslint@8.34.0 semver: 7.3.8 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys/5.51.0: - resolution: {integrity: sha512-Oh2+eTdjHjOFjKA27sxESlA87YPSOJafGCR0md5oeMdh1ZcCfAGCIOL216uTBAkAIptvLIfKQhl7lHxMJet4GQ==} + /@typescript-eslint/visitor-keys/5.52.0: + resolution: {integrity: sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.51.0 + '@typescript-eslint/types': 5.52.0 eslint-visitor-keys: 3.3.0 dev: true - /@vitejs/plugin-vue/4.0.0_vite@4.1.1+vue@3.2.47: + /@vitejs/plugin-vue/4.0.0_vite@4.1.2+vue@3.2.47: resolution: {integrity: sha512-e0X4jErIxAB5oLtDqbHvHpJe/uWNkdpYV83AOG2xo2tEVSzCzewgJMtREZM30wXnM5ls90hxiOtAuVU6H5JgbA==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.0.0 vue: ^3.2.25 dependencies: - vite: 4.1.1_gyrp4zacqcjjrmgvdzgac5epyy + vite: 4.1.2_hlkwzk2izwsolfmdrejei4vrty vue: 3.2.47 dev: false - /@volar/language-core/1.0.24: - resolution: {integrity: sha512-vTN+alJiWwK0Pax6POqrmevbtFW2dXhjwWiW/MW4f48eDYPLdyURWcr8TixO7EN/nHsUBj2udT7igFKPtjyAKg==} + /@volar/language-core/1.2.0-alpha.16: + resolution: {integrity: sha512-aIktWe9Zg0M+u/RIXHCuL+IoLRHTrpsbTib8olrg4etlurHDXahoVhoEnH9wmlliray0iigIo2z5vwueYInp3g==} dependencies: - '@volar/source-map': 1.0.24 - muggle-string: 0.1.0 + '@volar/source-map': 1.2.0-alpha.16 dev: true - /@volar/source-map/1.0.24: - resolution: {integrity: sha512-Qsv/tkplx18pgBr8lKAbM1vcDqgkGKQzbChg6NW+v0CZc3G7FLmK+WrqEPzKlN7Cwdc6XVL559Nod8WKAfKr4A==} + /@volar/source-map/1.2.0-alpha.16: + resolution: {integrity: sha512-/AK3VqnFqONd221COI2ZnEvfgBulfoQkjA/ZjPOXpsOkWri99TLcfZY/NTQRytp7Hx6EP/1p1DDeyGuMCUYjgA==} dependencies: - muggle-string: 0.1.0 + muggle-string: 0.2.2 dev: true - /@volar/typescript/1.0.24: - resolution: {integrity: sha512-f8hCSk+PfKR1/RQHxZ79V1NpDImHoivqoizK+mstphm25tn/YJ/JnKNjZHB+o21fuW0yKlI26NV3jkVb2Cc/7A==} + /@volar/typescript/1.2.0-alpha.16: + resolution: {integrity: sha512-ltlTLHIkLxgmTVBZmOnhmnlNzEj2lpvlBmmaV2GWYTrBUMt0z1OgeCq0Utlj9HjjrGPhwWxZNkv86ZABgrMA3Q==} dependencies: - '@volar/language-core': 1.0.24 + '@volar/language-core': 1.2.0-alpha.16 dev: true - /@volar/vue-language-core/1.0.24: - resolution: {integrity: sha512-2NTJzSgrwKu6uYwPqLiTMuAzi7fAY3yFy5PJ255bGJc82If0Xr+cW8pC80vpjG0D/aVLmlwAdO4+Ya2BI8GdDg==} + /@volar/vue-language-core/1.1.4: + resolution: {integrity: sha512-2C2CwHvaT5AzNzDbYZQ85lNr4jACZARoZMZBLuU5+JyIwhWeAfxvyAeoE3VbgfgycN5t6X4uBqx/Wzh1QLgD8Q==} dependencies: - '@volar/language-core': 1.0.24 - '@volar/source-map': 1.0.24 + '@volar/language-core': 1.2.0-alpha.16 + '@volar/source-map': 1.2.0-alpha.16 '@vue/compiler-dom': 3.2.47 '@vue/compiler-sfc': 3.2.47 '@vue/reactivity': 3.2.47 '@vue/shared': 3.2.47 - minimatch: 5.1.2 + minimatch: 6.2.0 + muggle-string: 0.2.2 vue-template-compiler: 2.7.14 dev: true - /@volar/vue-typescript/1.0.24: - resolution: {integrity: sha512-9a25oHDvGaNC0okRS47uqJI6FxY4hUQZUsxeOUFHcqVxZEv8s17LPuP/pMMXyz7jPygrZubB/qXqHY5jEu/akA==} + /@volar/vue-typescript/1.1.4: + resolution: {integrity: sha512-x5i5TUUXb1PM0rM80Y8XUeMBUcoS3/TjR3WTxvvEUIol9uEOPp6uxxQQ67uSv7ocN6vB0LugJqS6FA7Z93oL0Q==} dependencies: - '@volar/typescript': 1.0.24 - '@volar/vue-language-core': 1.0.24 + '@volar/typescript': 1.2.0-alpha.16 + '@volar/vue-language-core': 1.1.4 dev: true /@vue/compiler-core/3.2.47: @@ -3232,13 +3251,11 @@ packages: /acorn/7.4.1: resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} engines: {node: '>=0.4.0'} - hasBin: true dev: false /acorn/8.8.1: resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==} engines: {node: '>=0.4.0'} - hasBin: true /adm-zip/0.5.10: resolution: {integrity: sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==} @@ -3414,7 +3431,6 @@ packages: /arch/2.2.0: resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} - dev: true /archiver-utils/2.1.0: resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} @@ -3647,7 +3663,6 @@ packages: /atob/2.1.2: resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} engines: {node: '>= 4.5.0'} - hasBin: true dev: false /atomic-sleep/1.0.0: @@ -3696,8 +3711,8 @@ packages: - supports-color dev: false - /aws-sdk/2.1295.0: - resolution: {integrity: sha512-HVYoFCyfiL8gzL/c0lSRTg8tWBLfqAEDfwzGe338ww/LahpmC6C07S71SBBIvtGq3dpd7IwEobAbubZDijrA0Q==} + /aws-sdk/2.1318.0: + resolution: {integrity: sha512-xRCKqx4XWXUIpjDCVHmdOSINEVCIC5+yhmgUGR9A6VfxfPs59HbxKyd5LB+CmXhVbwVUM4SRWG5O+agQj+w7Eg==} engines: {node: '>= 10.0.0'} dependencies: buffer: 4.9.2 @@ -3721,7 +3736,7 @@ packages: /axios/0.24.0: resolution: {integrity: sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==} dependencies: - follow-redirects: 1.15.2_debug@4.3.4 + follow-redirects: 1.15.2 transitivePeerDependencies: - debug dev: false @@ -3729,23 +3744,23 @@ packages: /axios/0.27.2_debug@4.3.4: resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} dependencies: - follow-redirects: 1.15.2_debug@4.3.4 + follow-redirects: 1.15.2 form-data: 4.0.0 transitivePeerDependencies: - debug dev: true - /babel-jest/29.4.2_@babel+core@7.20.12: - resolution: {integrity: sha512-vcghSqhtowXPG84posYkkkzcZsdayFkubUgbE3/1tuGbX7AQtwCkkNA/wIbB0BMjuCPoqTkiDyKN7Ty7d3uwNQ==} + /babel-jest/29.4.3_@babel+core@7.20.12: + resolution: {integrity: sha512-o45Wyn32svZE+LnMVWv/Z4x0SwtLbh4FyGcYtR20kIWd+rdrDZ9Fzq8Ml3MYLD+mZvEdzCjZsCnYZ2jpJyQ+Nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.8.0 dependencies: '@babel/core': 7.20.12 - '@jest/transform': 29.4.2 + '@jest/transform': 29.4.3 '@types/babel__core': 7.1.20 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.4.2_@babel+core@7.20.12 + babel-preset-jest: 29.4.3_@babel+core@7.20.12 chalk: 4.1.2 graceful-fs: 4.2.10 slash: 3.0.0 @@ -3766,8 +3781,8 @@ packages: - supports-color dev: true - /babel-plugin-jest-hoist/29.4.2: - resolution: {integrity: sha512-5HZRCfMeWypFEonRbEkwWXtNS1sQK159LhRVyRuLzyfVBxDy/34Tr/rg4YVi0SScSJ4fqeaR/OIeceJ/LaQ0pQ==} + /babel-plugin-jest-hoist/29.4.3: + resolution: {integrity: sha512-mB6q2q3oahKphy5V7CpnNqZOCkxxZ9aokf1eh82Dy3jQmg4xvM1tGrh5y6BQUJh4a3Pj9+eLfwvAZ7VNKg7H8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/template': 7.20.7 @@ -3796,14 +3811,14 @@ packages: '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.20.12 dev: true - /babel-preset-jest/29.4.2_@babel+core@7.20.12: - resolution: {integrity: sha512-ecWdaLY/8JyfUDr0oELBMpj3R5I1L6ZqG+kRJmwqfHtLWuPrJStR0LUkvUhfykJWTsXXMnohsayN/twltBbDrQ==} + /babel-preset-jest/29.4.3_@babel+core@7.20.12: + resolution: {integrity: sha512-gWx6COtSuma6n9bw+8/F+2PCXrIgxV/D1TJFnp6OyBK2cxPWg0K9p/sriNYeifKjpUkMViWQ09DSWtzJQRETsw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.20.12 - babel-plugin-jest-hoist: 29.4.2 + babel-plugin-jest-hoist: 29.4.3 babel-preset-current-node-syntax: 1.0.1_@babel+core@7.20.12 dev: true @@ -3849,10 +3864,6 @@ packages: pascalcase: 0.1.1 dev: false - /base32.js/0.0.1: - resolution: {integrity: sha512-EGHIRiegFa62/SsA1J+Xs2tIzludPdzM064N9wjbiEgHnGnJ1V0WEpA4pEwCYT5nDvZk3ubf0shqaCS7k6xeUQ==} - dev: false - /base64-js/1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -3876,7 +3887,7 @@ packages: dependencies: execa: 0.7.0 executable: 4.1.1 - dev: true + dev: false /bin-version-check/5.0.0: resolution: {integrity: sha512-Q3FMQnS5eZmrBGqmDXLs4dbAn/f+52voP6ykJYmweSA60t6DyH4UTSwZhtbK5UH+LBoWvDljILUQMLRUtsynsA==} @@ -3885,7 +3896,7 @@ packages: bin-version: 6.0.0 semver: 7.3.8 semver-truncate: 2.0.0 - dev: true + dev: false /bin-version/6.0.0: resolution: {integrity: sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==} @@ -3893,7 +3904,7 @@ packages: dependencies: execa: 5.1.1 find-versions: 5.1.0 - dev: true + dev: false /binary-extensions/2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} @@ -3931,8 +3942,8 @@ packages: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} dev: true - /blurhash/2.0.4: - resolution: {integrity: sha512-r/As72u2FbucLoK5NTegM/GucxJc3d8GvHc4ngo13IO/nt2HU4gONxNLq1XPN6EM/V8Y9URIa7PcSz2RZu553A==} + /blurhash/2.0.5: + resolution: {integrity: sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==} dev: false /bn.js/4.12.0: @@ -3989,8 +4000,6 @@ packages: /browserslist/1.7.7: resolution: {integrity: sha512-qHJblDE2bXVRYzuDetv/wAeHOJyO97+9wxC1cdCtyzgNuSozOyRCiiLaCR1f71AN66lQdVVBipWm63V+a7bPOw==} - deprecated: Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools. - hasBin: true dependencies: caniuse-db: 1.0.30001443 electron-to-chromium: 1.4.284 @@ -3999,7 +4008,6 @@ packages: /browserslist/4.21.4: resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true dependencies: caniuse-lite: 1.0.30001443 electron-to-chromium: 1.4.284 @@ -4072,8 +4080,8 @@ packages: node-gyp-build: 4.6.0 dev: false - /bull/4.10.3: - resolution: {integrity: sha512-pp403srpkn9tYi7Z3Mu0sozehZ7rEEFGNJnN+nLxQwml6MySzefC9bPeCYedZoCkXdZ6VbIB8uNkMZg+hN/dAg==} + /bull/4.10.4: + resolution: {integrity: sha512-o9m/7HjS/Or3vqRd59evBlWCXd9Lp+ALppKseoSKHaykK46SmRjAilX98PgmOz1yeVaurt8D5UtvEt4bUjM3eA==} engines: {node: '>=12'} dependencies: cron-parser: 4.7.1 @@ -4138,7 +4146,7 @@ packages: /cacheable-lookup/5.0.4: resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} engines: {node: '>=10.6.0'} - dev: true + dev: false /cacheable-lookup/6.1.0: resolution: {integrity: sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==} @@ -4174,7 +4182,7 @@ packages: lowercase-keys: 2.0.0 normalize-url: 6.1.0 responselike: 2.0.1 - dev: true + dev: false /cachedir/2.3.0: resolution: {integrity: sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==} @@ -4295,45 +4303,45 @@ packages: is-regex: 1.1.4 dev: false - /chart.js/4.2.0: - resolution: {integrity: sha512-wbtcV+QKeH0F7gQZaCJEIpsNriFheacouJQTVIjITi3eQA8bTlIBoknz0+dgV79aeKLNMAX+nDslIVE/nJ3rzA==} + /chart.js/4.2.1: + resolution: {integrity: sha512-6YbpQ0nt3NovAgOzbkSSeeAQu/3za1319dPUQTXn9WcOpywM8rGKxJHrhS8V8xEkAlk8YhEfjbuAPfUyp6jIsw==} engines: {pnpm: ^7.0.0} dependencies: '@kurkle/color': 0.3.2 dev: false - /chartjs-adapter-date-fns/3.0.0_n6szoxj4ax2zhp2sxsxxj6zdla: + /chartjs-adapter-date-fns/3.0.0_rid3rx6orfiwps7kg2r43n6mvu: resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==} peerDependencies: chart.js: '>=2.8.0' date-fns: '>=2.0.0' dependencies: - chart.js: 4.2.0 + chart.js: 4.2.1 date-fns: 2.29.3 dev: false - /chartjs-chart-matrix/2.0.1_chart.js@4.2.0: + /chartjs-chart-matrix/2.0.1_chart.js@4.2.1: resolution: {integrity: sha512-BGfeY+/PHnITyDlc7WfnKJ1RyOfgOzIqWp/gxzzl7pUjyoGzHDcw51qd2xJF9gdT9Def7ZwOnOMm8GJUXDxI0w==} peerDependencies: chart.js: '>=3.0.0' dependencies: - chart.js: 4.2.0 + chart.js: 4.2.1 dev: false - /chartjs-plugin-gradient/0.6.1_chart.js@4.2.0: + /chartjs-plugin-gradient/0.6.1_chart.js@4.2.1: resolution: {integrity: sha512-TGHNIh8KqQMLdb+UfY80cBHYRyOC47eeokmgkeajRdKGbFt462lJiyiq4ZJ25fiM7BGsmzoBLhmVyEw4B3gQxw==} peerDependencies: chart.js: '>=2.6.0' dependencies: - chart.js: 4.2.0 + chart.js: 4.2.1 dev: false - /chartjs-plugin-zoom/2.0.0_chart.js@4.2.0: + /chartjs-plugin-zoom/2.0.0_chart.js@4.2.1: resolution: {integrity: sha512-bqpi7DGy9a5hX7ThKl/xQaLzXvneSwhS0w/lNimZ8AJaoRVMKz5JfUoqwciJYV5ixKXJbgyvwC9HcJnyVsYmjg==} peerDependencies: chart.js: '>=3.2.0' dependencies: - chart.js: 4.2.0 + chart.js: 4.2.1 hammerjs: 2.0.8 dev: false @@ -4429,7 +4437,6 @@ packages: /cli-highlight/2.1.11: resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} engines: {node: '>=8.0.0', npm: '>=5.0.0'} - hasBin: true dependencies: chalk: 4.1.2 highlight.js: 10.7.3 @@ -4497,7 +4504,7 @@ packages: resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} dependencies: mimic-response: 1.0.1 - dev: true + dev: false /clone-stats/1.0.0: resolution: {integrity: sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==} @@ -4600,7 +4607,6 @@ packages: /color-support/1.1.3: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} - hasBin: true dev: false /color/0.11.4: @@ -4658,7 +4664,7 @@ packages: /commander/7.2.0: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} engines: {node: '>= 10'} - dev: true + dev: false /commander/9.5.0: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} @@ -4721,6 +4727,7 @@ packages: engines: {node: '>= 0.6'} dependencies: safe-buffer: 5.2.1 + dev: false /convert-source-map/1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} @@ -4761,7 +4768,6 @@ packages: /crc-32/1.2.2: resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} engines: {node: '>=0.8'} - hasBin: true dev: false /crc32-stream/4.0.2: @@ -4788,7 +4794,6 @@ packages: /cross-env/7.0.3: resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} - hasBin: true dependencies: cross-spawn: 7.0.3 dev: true @@ -4799,7 +4804,7 @@ packages: lru-cache: 4.1.5 shebang-command: 1.2.0 which: 1.3.1 - dev: true + dev: false /cross-spawn/7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} @@ -4828,10 +4833,13 @@ packages: engines: {node: '>= 6'} dev: false + /css.escape/1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + dev: false + /cssesc/3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} - hasBin: true dev: true /cssnano/3.10.0: @@ -4874,7 +4882,6 @@ packages: /csso/2.3.2: resolution: {integrity: sha512-FmCI/hmqDeHHLaIQckMhMZneS84yzUZdrWDAvJVVxOwcKE1P1LF9FGmzr1ktIQSxOw6fl3PaQsmfg+GN+VvR3w==} engines: {node: '>=0.10.0'} - hasBin: true dependencies: clap: 1.2.3 source-map: 0.5.7 @@ -4913,10 +4920,9 @@ packages: uniq: 1.0.1 dev: false - /cypress/12.5.1: - resolution: {integrity: sha512-ZmCmJ3lsyeOpBfh410m5+AO2CO1AxAzFBt7k6/uVbNcrNZje1vdiwYTpj2ksPKg9mjr9lR6V8tmlDNMvr4H/YQ==} + /cypress/12.6.0: + resolution: {integrity: sha512-WdHSVaS1lumSd5XpVTslZd8ui9GIGphrzvXq9+3DtVhqjRZC5M70gu5SW/Y/SLPq3D1wiXGZoHC6HJ7ESVE2lw==} engines: {node: ^14.0.0 || ^16.0.0 || >=18.0.0} - hasBin: true requiresBuild: true dependencies: '@cypress/request': 2.88.11 @@ -5085,6 +5091,7 @@ packages: engines: {node: '>=10'} dependencies: mimic-response: 3.1.0 + dev: false /dedent/0.7.0: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} @@ -5128,6 +5135,7 @@ packages: /defer-to-connect/2.0.1: resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} engines: {node: '>=10'} + dev: false /define-properties/1.1.4: resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} @@ -5204,8 +5212,8 @@ packages: engines: {node: '>=8'} dev: true - /diff-sequences/29.4.2: - resolution: {integrity: sha512-R6P0Y6PrsH3n4hUXxL3nns0rbRk6Q33js3ygJBeEpbzLzgcNuJ61+u0RXasFpTKISw99TxUzFnumSnRLsjhLaw==} + /diff-sequences/29.4.3: + resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true @@ -5317,7 +5325,6 @@ packages: /ejs/3.1.8: resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==} engines: {node: '>=0.10.0'} - hasBin: true dependencies: jake: 10.8.5 dev: false @@ -5653,7 +5660,6 @@ packages: /esbuild/0.14.42: resolution: {integrity: sha512-V0uPZotCEHokJdNqyozH6qsaQXqmZEOiZWrXnds/zaH/0SyrIayRXWRB98CENO73MIZ9T3HBIOsmds5twWtmgw==} engines: {node: '>=12'} - hasBin: true requiresBuild: true optionalDependencies: esbuild-android-64: 0.14.42 @@ -5681,7 +5687,6 @@ packages: /esbuild/0.16.17: resolution: {integrity: sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==} engines: {node: '>=12'} - hasBin: true requiresBuild: true optionalDependencies: '@esbuild/android-arm': 0.16.17 @@ -5736,12 +5741,11 @@ packages: /escape-string-regexp/5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} - dev: true + dev: false /escodegen/2.0.0: resolution: {integrity: sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==} engines: {node: '>=6.0'} - hasBin: true dependencies: esprima: 4.0.1 estraverse: 5.3.0 @@ -5761,7 +5765,7 @@ packages: - supports-color dev: true - /eslint-module-utils/2.7.4_fwto6vsnn2m6f5yglaeo6vhd5y: + /eslint-module-utils/2.7.4_npjqex3ey3rgd34fjcuucz7la4: resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} engines: {node: '>=4'} peerDependencies: @@ -5782,15 +5786,15 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/parser': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm debug: 3.2.7 - eslint: 8.33.0 + eslint: 8.34.0 eslint-import-resolver-node: 0.3.7 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import/2.27.5_yzj2n2b43wonjwaifya6xmk2zy: + /eslint-plugin-import/2.27.5_mcvs2y73sfmcxqzpjj5lr7a2m4: resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} engines: {node: '>=4'} peerDependencies: @@ -5800,15 +5804,15 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/parser': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm array-includes: 3.1.6 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.33.0 + eslint: 8.34.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.7.4_fwto6vsnn2m6f5yglaeo6vhd5y + eslint-module-utils: 2.7.4_npjqex3ey3rgd34fjcuucz7la4 has: 1.0.3 is-core-module: 2.11.0 is-glob: 4.0.3 @@ -5823,19 +5827,19 @@ packages: - supports-color dev: true - /eslint-plugin-vue/9.9.0_eslint@8.33.0: + /eslint-plugin-vue/9.9.0_eslint@8.34.0: resolution: {integrity: sha512-YbubS7eK0J7DCf0U2LxvVP7LMfs6rC6UltihIgval3azO3gyDwEGVgsCMe1TmDiEkl6GdMKfRpaME6QxIYtzDQ==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 dependencies: - eslint: 8.33.0 - eslint-utils: 3.0.0_eslint@8.33.0 + eslint: 8.34.0 + eslint-utils: 3.0.0_eslint@8.34.0 natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 6.0.11 semver: 7.3.8 - vue-eslint-parser: 9.1.0_eslint@8.33.0 + vue-eslint-parser: 9.1.0_eslint@8.34.0 xml-name-validator: 4.0.0 transitivePeerDependencies: - supports-color @@ -5857,13 +5861,13 @@ packages: estraverse: 5.3.0 dev: true - /eslint-utils/3.0.0_eslint@8.33.0: + /eslint-utils/3.0.0_eslint@8.34.0: resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} peerDependencies: eslint: '>=5' dependencies: - eslint: 8.33.0 + eslint: 8.34.0 eslint-visitor-keys: 2.1.0 dev: true @@ -5877,10 +5881,9 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint/8.33.0: - resolution: {integrity: sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==} + /eslint/8.34.0: + resolution: {integrity: sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true dependencies: '@eslint/eslintrc': 1.4.1 '@humanwhocodes/config-array': 0.11.8 @@ -5893,7 +5896,7 @@ packages: doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.1.1 - eslint-utils: 3.0.0_eslint@8.33.0 + eslint-utils: 3.0.0_eslint@8.34.0 eslint-visitor-keys: 3.3.0 espree: 9.4.1 esquery: 1.4.0 @@ -5937,13 +5940,11 @@ packages: /esprima/2.7.3: resolution: {integrity: sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==} engines: {node: '>=0.10.0'} - hasBin: true dev: false /esprima/4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} - hasBin: true /esquery/1.4.0: resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} @@ -6025,7 +6026,7 @@ packages: p-finally: 1.0.0 signal-exit: 3.0.7 strip-eof: 1.0.0 - dev: true + dev: false /execa/4.1.0: resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} @@ -6076,7 +6077,6 @@ packages: engines: {node: '>=4'} dependencies: pify: 2.3.0 - dev: true /exit/0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} @@ -6121,15 +6121,15 @@ packages: jest-util: 29.4.2 dev: true - /expect/29.4.2: - resolution: {integrity: sha512-+JHYg9O3hd3RlICG90OPVjRkPBoiUH7PxvDVMnRiaq1g6JUgZStX514erMl0v2Dc5SkfVbm7ztqbd6qHHPn+mQ==} + /expect/29.4.3: + resolution: {integrity: sha512-uC05+Q7eXECFpgDrHdXA4k2rpMyStAYPItEDLyQDo5Ta7fVkJnNA/4zh/OIVkVVNZ1oOK1PipQoyNjuZ6sz6Dg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/expect-utils': 29.4.2 - jest-get-type: 29.4.2 - jest-matcher-utils: 29.4.2 - jest-message-util: 29.4.2 - jest-util: 29.4.2 + '@jest/expect-utils': 29.4.3 + jest-get-type: 29.4.3 + jest-matcher-utils: 29.4.3 + jest-message-util: 29.4.3 + jest-util: 29.4.3 dev: true /ext-list/2.2.2: @@ -6137,7 +6137,7 @@ packages: engines: {node: '>=0.10.0'} dependencies: mime-db: 1.52.0 - dev: true + dev: false /ext-name/5.0.0: resolution: {integrity: sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==} @@ -6145,7 +6145,7 @@ packages: dependencies: ext-list: 2.2.2 sort-keys-length: 1.0.1 - dev: true + dev: false /ext/1.7.0: resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} @@ -6190,7 +6190,6 @@ packages: /extract-zip/2.0.1_supports-color@8.1.1: resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} - hasBin: true dependencies: debug: 4.3.4_supports-color@8.1.1 get-stream: 5.2.0 @@ -6278,7 +6277,6 @@ packages: /fast-xml-parser/3.21.1: resolution: {integrity: sha512-FTFVjYoBOZTJekiUsawGsSYV9QL0A+zDYCRj7y34IO6Jg+2IMYEtQa+bbictpdpV8dHxXywqU7C0gRDEOFtBFg==} - hasBin: true dependencies: strnum: 1.0.5 dev: false @@ -6287,8 +6285,8 @@ packages: resolution: {integrity: sha512-79ak0JxddO0utAXAQ5ccKhvs6vX2MGyHHMMsmZkBANrq3hXc1CHzvNPHOcvTsVMEPl5I+NT+RO4YKMGehOfSIg==} dev: false - /fastify/4.12.0: - resolution: {integrity: sha512-Hh2GCsOCqnOuewWSvqXlpq5V/9VA+/JkVoooQWUhrU6gryO9+/UGOoF/dprGcKSDxkM/9TkMXSffYp8eA/YhYQ==} + /fastify/4.13.0: + resolution: {integrity: sha512-p9ibdFWH3pZ7KPgmfHPKGUy2W4EWU2TEpwlcu58w4CwGyU3ARFfh2kwq6zpZ5W2ZGVbufi4tZbqHIHAlX/9Z/A==} dependencies: '@fastify/ajv-compiler': 3.5.0 '@fastify/error': 3.2.0 @@ -6361,10 +6359,10 @@ packages: readable-web-to-node-stream: 3.0.2 strtok3: 7.0.0 token-types: 5.0.1 - dev: true + dev: false - /file-type/18.2.0: - resolution: {integrity: sha512-M3RQMWY3F2ykyWZ+IHwNCjpnUmukYhtdkGGC1ZVEUb0ve5REGF7NNJ4Q9ehCUabtQKtSVFOMbFTXgJlFb0DQIg==} + /file-type/18.2.1: + resolution: {integrity: sha512-Yw5MtnMv7vgD2/6Bjmmuegc8bQEVA9GmAyaR18bMYWKqsWDG9wgYZ1j4I6gNMF5Y5JBDcUcjRQqNQx7Y8uotcg==} engines: {node: '>=14.16'} dependencies: readable-web-to-node-stream: 3.0.2 @@ -6381,7 +6379,7 @@ packages: /filename-reserved-regex/3.0.0: resolution: {integrity: sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + dev: false /filenamify/5.1.1: resolution: {integrity: sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==} @@ -6390,7 +6388,7 @@ packages: filename-reserved-regex: 3.0.0 strip-outer: 2.0.0 trim-repeated: 2.0.0 - dev: true + dev: false /fill-range/4.0.0: resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==} @@ -6445,7 +6443,7 @@ packages: engines: {node: '>=12'} dependencies: semver-regex: 4.0.5 - dev: true + dev: false /findup-sync/2.0.0: resolution: {integrity: sha512-vs+3unmJT45eczmcAZ6zMJtxN3l/QXeccaXQx5cu/MeJMhewVfoWZqibRkOxPnmoR59+Zy5hjabfQc6JLSah4g==} @@ -6501,7 +6499,6 @@ packages: /flatten/1.0.3: resolution: {integrity: sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==} - deprecated: flatten is deprecated in favor of utility frameworks such as lodash. dev: false /fluent-ffmpeg/2.1.2: @@ -6519,7 +6516,7 @@ packages: readable-stream: 2.3.7 dev: false - /follow-redirects/1.15.2_debug@4.3.4: + /follow-redirects/1.15.2: resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} engines: {node: '>=4.0'} peerDependencies: @@ -6527,8 +6524,6 @@ packages: peerDependenciesMeta: debug: optional: true - dependencies: - debug: 4.3.4 /for-each/0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} @@ -6768,14 +6763,13 @@ packages: /get-stream/3.0.0: resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==} engines: {node: '>=4'} - dev: true + dev: false /get-stream/5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} dependencies: pump: 3.0.0 - dev: true /get-stream/6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} @@ -6877,17 +6871,6 @@ packages: once: 1.4.0 path-is-absolute: 1.0.1 - /glob/8.0.3: - resolution: {integrity: sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==} - engines: {node: '>=12'} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 5.1.2 - once: 1.4.0 - dev: false - /glob/8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} @@ -6982,7 +6965,7 @@ packages: lowercase-keys: 2.0.0 p-cancelable: 2.1.1 responselike: 2.0.1 - dev: true + dev: false /got/12.5.3: resolution: {integrity: sha512-8wKnb9MGU8IPGRIo+/ukTy9XLJBwDiCpIf5TVzQ9Cpol50eMTpBq2GAuDsuDIz7hTYmZgMgC1e9ydr6kSDWs3w==} @@ -7015,7 +6998,6 @@ packages: /gulp-cli/2.3.0: resolution: {integrity: sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==} engines: {node: '>= 0.10'} - hasBin: true dependencies: ansi-colors: 1.1.0 archy: 1.0.0 @@ -7078,7 +7060,6 @@ packages: /gulp/4.0.2: resolution: {integrity: sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==} engines: {node: '>= 0.10'} - hasBin: true dependencies: glob-watcher: 5.0.5 gulp-cli: 2.3.0 @@ -7100,6 +7081,19 @@ packages: engines: {node: '>=0.8.0'} dev: false + /happy-dom/8.7.0: + resolution: {integrity: sha512-F/mH5l8aQwlfeByB0nU6Lg7a0FBax/nPCYNYg8tn/abdKCmiIJH+gU/5MVysf5XoM6KjJsvkbIaXAmS/8HxXLA==} + dependencies: + css.escape: 1.5.1 + he: 1.2.0 + node-fetch: 2.6.7 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + transitivePeerDependencies: + - encoding + dev: false + /har-schema/2.0.0: resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} engines: {node: '>=4'} @@ -7108,7 +7102,6 @@ packages: /har-validator/5.1.5: resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==} engines: {node: '>=6'} - deprecated: this library is no longer supported dependencies: ajv: 6.12.6 har-schema: 2.0.0 @@ -7201,8 +7194,6 @@ packages: /he/1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - dev: true /hexoid/1.0.0: resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==} @@ -7258,6 +7249,7 @@ packages: /http-cache-semantics/4.1.0: resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==} + dev: false /http-errors/2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} @@ -7305,7 +7297,7 @@ packages: dependencies: quick-lru: 5.1.1 resolve-alpn: 1.2.1 - dev: true + dev: false /http2-wrapper/2.2.0: resolution: {integrity: sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==} @@ -7409,7 +7401,6 @@ packages: /import-local/3.1.0: resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} engines: {node: '>=8'} - hasBin: true dependencies: pkg-dir: 4.2.0 resolve-cwd: 3.0.0 @@ -7455,7 +7446,6 @@ packages: /install-artifact-from-github/1.3.2: resolution: {integrity: sha512-yCFcLvqk0yQdxx0uJz4t9Z3adDMLAYrcGYv546uRXCSvxE+GqNYhhz/KmrGcUKGI/gVLR9n/e/zM9jX/+ASMJQ==} - hasBin: true dev: false /internal-slot/1.0.3: @@ -7628,7 +7618,6 @@ packages: /is-ci/3.0.1: resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} - hasBin: true dependencies: ci-info: 3.7.1 dev: true @@ -7800,6 +7789,7 @@ packages: /is-plain-obj/1.1.0: resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} engines: {node: '>=0.10.0'} + dev: false /is-plain-object/2.0.4: resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} @@ -7844,7 +7834,7 @@ packages: /is-stream/1.1.0: resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} engines: {node: '>=0.10.0'} - dev: true + dev: false /is-stream/2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} @@ -8017,7 +8007,6 @@ packages: /jake/10.8.5: resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==} engines: {node: '>=10'} - hasBin: true dependencies: async: 3.2.4 chalk: 4.1.2 @@ -8025,43 +8014,43 @@ packages: minimatch: 3.1.2 dev: false - /jest-changed-files/29.4.2: - resolution: {integrity: sha512-Qdd+AXdqD16PQa+VsWJpxR3kN0JyOCX1iugQfx5nUgAsI4gwsKviXkpclxOK9ZnwaY2IQVHz+771eAvqeOlfuw==} + /jest-changed-files/29.4.3: + resolution: {integrity: sha512-Vn5cLuWuwmi2GNNbokPOEcvrXGSGrqVnPEZV7rC6P7ck07Dyw9RFnvWglnupSh+hGys0ajGtw/bc2ZgweljQoQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: execa: 5.1.1 p-limit: 3.1.0 dev: true - /jest-circus/29.4.2: - resolution: {integrity: sha512-wW3ztp6a2P5c1yOc1Cfrt5ozJ7neWmqeXm/4SYiqcSriyisgq63bwFj1NuRdSR5iqS0CMEYwSZd89ZA47W9zUg==} + /jest-circus/29.4.3: + resolution: {integrity: sha512-Vw/bVvcexmdJ7MLmgdT3ZjkJ3LKu8IlpefYokxiqoZy6OCQ2VAm6Vk3t/qHiAGUXbdbJKJWnc8gH3ypTbB/OBw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 29.4.2 - '@jest/expect': 29.4.2 - '@jest/test-result': 29.4.2 - '@jest/types': 29.4.2 - '@types/node': 18.13.0 + '@jest/environment': 29.4.3 + '@jest/expect': 29.4.3 + '@jest/test-result': 29.4.3 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 is-generator-fn: 2.1.0 - jest-each: 29.4.2 - jest-matcher-utils: 29.4.2 - jest-message-util: 29.4.2 - jest-runtime: 29.4.2 - jest-snapshot: 29.4.2 - jest-util: 29.4.2 + jest-each: 29.4.3 + jest-matcher-utils: 29.4.3 + jest-message-util: 29.4.3 + jest-runtime: 29.4.3 + jest-snapshot: 29.4.3 + jest-util: 29.4.3 p-limit: 3.1.0 - pretty-format: 29.4.2 + pretty-format: 29.4.3 slash: 3.0.0 stack-utils: 2.0.6 transitivePeerDependencies: - supports-color dev: true - /jest-cli/29.4.2_@types+node@18.13.0: - resolution: {integrity: sha512-b+eGUtXq/K2v7SH3QcJvFvaUaCDS1/YAZBYz0m28Q/Ppyr+1qNaHmVYikOrbHVbZqYQs2IeI3p76uy6BWbXq8Q==} + /jest-cli/29.4.3_@types+node@18.14.0: + resolution: {integrity: sha512-PiiAPuFNfWWolCE6t3ZrDXQc6OsAuM3/tVW0u27UWc1KE+n/HSn5dSE6B2juqN7WP+PP0jAcnKtGmI4u8GMYCg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true peerDependencies: @@ -8070,16 +8059,16 @@ packages: node-notifier: optional: true dependencies: - '@jest/core': 29.4.2 - '@jest/test-result': 29.4.2 - '@jest/types': 29.4.2 + '@jest/core': 29.4.3 + '@jest/test-result': 29.4.3 + '@jest/types': 29.4.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.10 import-local: 3.1.0 - jest-config: 29.4.2_@types+node@18.13.0 - jest-util: 29.4.2 - jest-validate: 29.4.2 + jest-config: 29.4.3_@types+node@18.14.0 + jest-util: 29.4.3 + jest-validate: 29.4.3 prompts: 2.4.2 yargs: 17.6.2 transitivePeerDependencies: @@ -8088,8 +8077,8 @@ packages: - ts-node dev: true - /jest-config/29.4.2_@types+node@18.13.0: - resolution: {integrity: sha512-919CtnXic52YM0zW4C1QxjG6aNueX1kBGthuMtvFtRTAxhKfJmiXC9qwHmi6o2josjbDz8QlWyY55F1SIVmCWA==} + /jest-config/29.4.3_@types+node@18.14.0: + resolution: {integrity: sha512-eCIpqhGnIjdUCXGtLhz4gdDoxKSWXKjzNcc5r+0S1GKOp2fwOipx5mRcwa9GB/ArsxJ1jlj2lmlD9bZAsBxaWQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@types/node': '*' @@ -8101,26 +8090,26 @@ packages: optional: true dependencies: '@babel/core': 7.20.12 - '@jest/test-sequencer': 29.4.2 - '@jest/types': 29.4.2 - '@types/node': 18.13.0 - babel-jest: 29.4.2_@babel+core@7.20.12 + '@jest/test-sequencer': 29.4.3 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 + babel-jest: 29.4.3_@babel+core@7.20.12 chalk: 4.1.2 ci-info: 3.7.1 deepmerge: 4.2.2 glob: 7.2.3 graceful-fs: 4.2.10 - jest-circus: 29.4.2 - jest-environment-node: 29.4.2 - jest-get-type: 29.4.2 - jest-regex-util: 29.4.2 - jest-resolve: 29.4.2 - jest-runner: 29.4.2 - jest-util: 29.4.2 - jest-validate: 29.4.2 + jest-circus: 29.4.3 + jest-environment-node: 29.4.3 + jest-get-type: 29.4.3 + jest-regex-util: 29.4.3 + jest-resolve: 29.4.3 + jest-runner: 29.4.3 + jest-util: 29.4.3 + jest-validate: 29.4.3 micromatch: 4.0.5 parse-json: 5.2.0 - pretty-format: 29.4.2 + pretty-format: 29.4.3 slash: 3.0.0 strip-json-comments: 3.1.1 transitivePeerDependencies: @@ -8132,39 +8121,49 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: chalk: 4.1.2 - diff-sequences: 29.4.2 - jest-get-type: 29.4.2 - pretty-format: 29.4.2 + diff-sequences: 29.4.3 + jest-get-type: 29.4.3 + pretty-format: 29.4.3 dev: true - /jest-docblock/29.4.2: - resolution: {integrity: sha512-dV2JdahgClL34Y5vLrAHde3nF3yo2jKRH+GIYJuCpfqwEJZcikzeafVTGAjbOfKPG17ez9iWXwUYp7yefeCRag==} + /jest-diff/29.4.3: + resolution: {integrity: sha512-YB+ocenx7FZ3T5O9lMVMeLYV4265socJKtkwgk/6YUz/VsEzYDkiMuMhWzZmxm3wDRQvayJu/PjkjjSkjoHsCA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.4.3 + jest-get-type: 29.4.3 + pretty-format: 29.4.3 + dev: true + + /jest-docblock/29.4.3: + resolution: {integrity: sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: detect-newline: 3.1.0 dev: true - /jest-each/29.4.2: - resolution: {integrity: sha512-trvKZb0JYiCndc55V1Yh0Luqi7AsAdDWpV+mKT/5vkpnnFQfuQACV72IoRV161aAr6kAVIBpmYzwhBzm34vQkA==} + /jest-each/29.4.3: + resolution: {integrity: sha512-1ElHNAnKcbJb/b+L+7j0/w7bDvljw4gTv1wL9fYOczeJrbTbkMGQ5iQPFJ3eFQH19VPTx1IyfePdqSpePKss7Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.4.2 + '@jest/types': 29.4.3 chalk: 4.1.2 - jest-get-type: 29.4.2 - jest-util: 29.4.2 - pretty-format: 29.4.2 + jest-get-type: 29.4.3 + jest-util: 29.4.3 + pretty-format: 29.4.3 dev: true - /jest-environment-node/29.4.2: - resolution: {integrity: sha512-MLPrqUcOnNBc8zTOfqBbxtoa8/Ee8tZ7UFW7hRDQSUT+NGsvS96wlbHGTf+EFAT9KC3VNb7fWEM6oyvmxtE/9w==} + /jest-environment-node/29.4.3: + resolution: {integrity: sha512-gAiEnSKF104fsGDXNkwk49jD/0N0Bqu2K9+aMQXA6avzsA9H3Fiv1PW2D+gzbOSR705bWd2wJZRFEFpV0tXISg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 29.4.2 - '@jest/fake-timers': 29.4.2 - '@jest/types': 29.4.2 - '@types/node': 18.13.0 - jest-mock: 29.4.2 - jest-util: 29.4.2 + '@jest/environment': 29.4.3 + '@jest/fake-timers': 29.4.3 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 + jest-mock: 29.4.3 + jest-util: 29.4.3 dev: true /jest-get-type/29.2.0: @@ -8177,31 +8176,36 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true - /jest-haste-map/29.4.2: - resolution: {integrity: sha512-WkUgo26LN5UHPknkezrBzr7lUtV1OpGsp+NfXbBwHztsFruS3gz+AMTTBcEklvi8uPzpISzYjdKXYZQJXBnfvw==} + /jest-get-type/29.4.3: + resolution: {integrity: sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-haste-map/29.4.3: + resolution: {integrity: sha512-eZIgAS8tvm5IZMtKlR8Y+feEOMfo2pSQkmNbufdbMzMSn9nitgGxF1waM/+LbryO3OkMcKS98SUb+j/cQxp/vQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.4.2 + '@jest/types': 29.4.3 '@types/graceful-fs': 4.1.6 - '@types/node': 18.13.0 + '@types/node': 18.14.0 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.10 - jest-regex-util: 29.4.2 - jest-util: 29.4.2 - jest-worker: 29.4.2 + jest-regex-util: 29.4.3 + jest-util: 29.4.3 + jest-worker: 29.4.3 micromatch: 4.0.5 walker: 1.0.8 optionalDependencies: fsevents: 2.3.2 dev: true - /jest-leak-detector/29.4.2: - resolution: {integrity: sha512-Wa62HuRJmWXtX9F00nUpWlrbaH5axeYCdyRsOs/+Rb1Vb6+qWTlB5rKwCCRKtorM7owNwKsyJ8NRDUcZ8ghYUA==} + /jest-leak-detector/29.4.3: + resolution: {integrity: sha512-9yw4VC1v2NspMMeV3daQ1yXPNxMgCzwq9BocCwYrRgXe4uaEJPAN0ZK37nFBhcy3cUwEVstFecFLaTHpF7NiGA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - jest-get-type: 29.4.2 - pretty-format: 29.4.2 + jest-get-type: 29.4.3 + pretty-format: 29.4.3 dev: true /jest-matcher-utils/29.3.1: @@ -8211,17 +8215,17 @@ packages: chalk: 4.1.2 jest-diff: 29.4.2 jest-get-type: 29.4.2 - pretty-format: 29.4.2 + pretty-format: 29.4.3 dev: true - /jest-matcher-utils/29.4.2: - resolution: {integrity: sha512-EZaAQy2je6Uqkrm6frnxBIdaWtSYFoR8SVb2sNLAtldswlR/29JAgx+hy67llT3+hXBaLB0zAm5UfeqerioZyg==} + /jest-matcher-utils/29.4.3: + resolution: {integrity: sha512-TTciiXEONycZ03h6R6pYiZlSkvYgT0l8aa49z/DLSGYjex4orMUcafuLXYyyEDWB1RKglq00jzwY00Ei7yFNVg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: chalk: 4.1.2 - jest-diff: 29.4.2 - jest-get-type: 29.4.2 - pretty-format: 29.4.2 + jest-diff: 29.4.3 + jest-get-type: 29.4.3 + pretty-format: 29.4.3 dev: true /jest-message-util/29.3.1: @@ -8229,41 +8233,41 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/code-frame': 7.18.6 - '@jest/types': 29.4.2 + '@jest/types': 29.4.3 '@types/stack-utils': 2.0.1 chalk: 4.1.2 graceful-fs: 4.2.10 micromatch: 4.0.5 - pretty-format: 29.4.2 + pretty-format: 29.4.3 slash: 3.0.0 stack-utils: 2.0.6 dev: true - /jest-message-util/29.4.2: - resolution: {integrity: sha512-SElcuN4s6PNKpOEtTInjOAA8QvItu0iugkXqhYyguRvQoXapg5gN+9RQxLAkakChZA7Y26j6yUCsFWN+hlKD6g==} + /jest-message-util/29.4.3: + resolution: {integrity: sha512-1Y8Zd4ZCN7o/QnWdMmT76If8LuDv23Z1DRovBj/vcSFNlGCJGoO8D1nJDw1AdyAGUk0myDLFGN5RbNeJyCRGCw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/code-frame': 7.18.6 - '@jest/types': 29.4.2 + '@jest/types': 29.4.3 '@types/stack-utils': 2.0.1 chalk: 4.1.2 graceful-fs: 4.2.10 micromatch: 4.0.5 - pretty-format: 29.4.2 + pretty-format: 29.4.3 slash: 3.0.0 stack-utils: 2.0.6 dev: true - /jest-mock/29.4.2: - resolution: {integrity: sha512-x1FSd4Gvx2yIahdaIKoBjwji6XpboDunSJ95RpntGrYulI1ByuYQCKN/P7hvk09JB74IonU3IPLdkutEWYt++g==} + /jest-mock/29.4.3: + resolution: {integrity: sha512-LjFgMg+xed9BdkPMyIJh+r3KeHt1klXPJYBULXVVAkbTaaKjPX1o1uVCAZADMEp/kOxGTwy/Ot8XbvgItOrHEg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.4.2 - '@types/node': 18.13.0 - jest-util: 29.4.2 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 + jest-util: 29.4.3 dev: true - /jest-pnp-resolver/1.2.3_jest-resolve@29.4.2: + /jest-pnp-resolver/1.2.3_jest-resolve@29.4.3: resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} engines: {node: '>=6'} peerDependencies: @@ -8272,101 +8276,100 @@ packages: jest-resolve: optional: true dependencies: - jest-resolve: 29.4.2 + jest-resolve: 29.4.3 dev: true - /jest-regex-util/29.4.2: - resolution: {integrity: sha512-XYZXOqUl1y31H6VLMrrUL1ZhXuiymLKPz0BO1kEeR5xER9Tv86RZrjTm74g5l9bPJQXA/hyLdaVPN/sdqfteig==} + /jest-regex-util/29.4.3: + resolution: {integrity: sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true - /jest-resolve-dependencies/29.4.2: - resolution: {integrity: sha512-6pL4ptFw62rjdrPk7rRpzJYgcRqRZNsZTF1VxVTZMishbO6ObyWvX57yHOaNGgKoADtAHRFYdHQUEvYMJATbDg==} + /jest-resolve-dependencies/29.4.3: + resolution: {integrity: sha512-uvKMZAQ3nmXLH7O8WAOhS5l0iWyT3WmnJBdmIHiV5tBbdaDZ1wqtNX04FONGoaFvSOSHBJxnwAVnSn1WHdGVaw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - jest-regex-util: 29.4.2 - jest-snapshot: 29.4.2 + jest-regex-util: 29.4.3 + jest-snapshot: 29.4.3 transitivePeerDependencies: - supports-color dev: true - /jest-resolve/29.4.2: - resolution: {integrity: sha512-RtKWW0mbR3I4UdkOrW7552IFGLYQ5AF9YrzD0FnIOkDu0rAMlA5/Y1+r7lhCAP4nXSBTaE7ueeqj6IOwZpgoqw==} + /jest-resolve/29.4.3: + resolution: {integrity: sha512-GPokE1tzguRyT7dkxBim4wSx6E45S3bOQ7ZdKEG+Qj0Oac9+6AwJPCk0TZh5Vu0xzeX4afpb+eDmgbmZFFwpOw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: chalk: 4.1.2 graceful-fs: 4.2.10 - jest-haste-map: 29.4.2 - jest-pnp-resolver: 1.2.3_jest-resolve@29.4.2 - jest-util: 29.4.2 - jest-validate: 29.4.2 + jest-haste-map: 29.4.3 + jest-pnp-resolver: 1.2.3_jest-resolve@29.4.3 + jest-util: 29.4.3 + jest-validate: 29.4.3 resolve: 1.22.1 resolve.exports: 2.0.0 slash: 3.0.0 dev: true - /jest-runner/29.4.2: - resolution: {integrity: sha512-wqwt0drm7JGjwdH+x1XgAl+TFPH7poowMguPQINYxaukCqlczAcNLJiK+OLxUxQAEWMdy+e6nHZlFHO5s7EuRg==} + /jest-runner/29.4.3: + resolution: {integrity: sha512-GWPTEiGmtHZv1KKeWlTX9SIFuK19uLXlRQU43ceOQ2hIfA5yPEJC7AMkvFKpdCHx6pNEdOD+2+8zbniEi3v3gA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/console': 29.4.2 - '@jest/environment': 29.4.2 - '@jest/test-result': 29.4.2 - '@jest/transform': 29.4.2 - '@jest/types': 29.4.2 - '@types/node': 18.13.0 + '@jest/console': 29.4.3 + '@jest/environment': 29.4.3 + '@jest/test-result': 29.4.3 + '@jest/transform': 29.4.3 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.10 - jest-docblock: 29.4.2 - jest-environment-node: 29.4.2 - jest-haste-map: 29.4.2 - jest-leak-detector: 29.4.2 - jest-message-util: 29.4.2 - jest-resolve: 29.4.2 - jest-runtime: 29.4.2 - jest-util: 29.4.2 - jest-watcher: 29.4.2 - jest-worker: 29.4.2 + jest-docblock: 29.4.3 + jest-environment-node: 29.4.3 + jest-haste-map: 29.4.3 + jest-leak-detector: 29.4.3 + jest-message-util: 29.4.3 + jest-resolve: 29.4.3 + jest-runtime: 29.4.3 + jest-util: 29.4.3 + jest-watcher: 29.4.3 + jest-worker: 29.4.3 p-limit: 3.1.0 source-map-support: 0.5.13 transitivePeerDependencies: - supports-color dev: true - /jest-runtime/29.4.2: - resolution: {integrity: sha512-3fque9vtpLzGuxT9eZqhxi+9EylKK/ESfhClv4P7Y9sqJPs58LjVhTt8jaMp/pRO38agll1CkSu9z9ieTQeRrw==} + /jest-runtime/29.4.3: + resolution: {integrity: sha512-F5bHvxSH+LvLV24vVB3L8K467dt3y3dio6V3W89dUz9nzvTpqd/HcT9zfYKL2aZPvD63vQFgLvaUX/UpUhrP6Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 29.4.2 - '@jest/fake-timers': 29.4.2 - '@jest/globals': 29.4.2 - '@jest/source-map': 29.4.2 - '@jest/test-result': 29.4.2 - '@jest/transform': 29.4.2 - '@jest/types': 29.4.2 - '@types/node': 18.13.0 + '@jest/environment': 29.4.3 + '@jest/fake-timers': 29.4.3 + '@jest/globals': 29.4.3 + '@jest/source-map': 29.4.3 + '@jest/test-result': 29.4.3 + '@jest/transform': 29.4.3 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 chalk: 4.1.2 cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 glob: 7.2.3 graceful-fs: 4.2.10 - jest-haste-map: 29.4.2 - jest-message-util: 29.4.2 - jest-mock: 29.4.2 - jest-regex-util: 29.4.2 - jest-resolve: 29.4.2 - jest-snapshot: 29.4.2 - jest-util: 29.4.2 - semver: 7.3.8 + jest-haste-map: 29.4.3 + jest-message-util: 29.4.3 + jest-mock: 29.4.3 + jest-regex-util: 29.4.3 + jest-resolve: 29.4.3 + jest-snapshot: 29.4.3 + jest-util: 29.4.3 slash: 3.0.0 strip-bom: 4.0.0 transitivePeerDependencies: - supports-color dev: true - /jest-snapshot/29.4.2: - resolution: {integrity: sha512-PdfubrSNN5KwroyMH158R23tWcAXJyx4pvSvWls1dHoLCaUhGul9rsL3uVjtqzRpkxlkMavQjGuWG1newPgmkw==} + /jest-snapshot/29.4.3: + resolution: {integrity: sha512-NGlsqL0jLPDW91dz304QTM/SNO99lpcSYYAjNiX0Ou+sSGgkanKBcSjCfp/pqmiiO1nQaOyLp6XQddAzRcx3Xw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/core': 7.20.12 @@ -8375,23 +8378,23 @@ packages: '@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.20.12 '@babel/traverse': 7.20.12 '@babel/types': 7.20.7 - '@jest/expect-utils': 29.4.2 - '@jest/transform': 29.4.2 - '@jest/types': 29.4.2 + '@jest/expect-utils': 29.4.3 + '@jest/transform': 29.4.3 + '@jest/types': 29.4.3 '@types/babel__traverse': 7.18.3 '@types/prettier': 2.7.2 babel-preset-current-node-syntax: 1.0.1_@babel+core@7.20.12 chalk: 4.1.2 - expect: 29.4.2 + expect: 29.4.3 graceful-fs: 4.2.10 - jest-diff: 29.4.2 - jest-get-type: 29.4.2 - jest-haste-map: 29.4.2 - jest-matcher-utils: 29.4.2 - jest-message-util: 29.4.2 - jest-util: 29.4.2 + jest-diff: 29.4.3 + jest-get-type: 29.4.3 + jest-haste-map: 29.4.3 + jest-matcher-utils: 29.4.3 + jest-message-util: 29.4.3 + jest-util: 29.4.3 natural-compare: 1.4.0 - pretty-format: 29.4.2 + pretty-format: 29.4.3 semver: 7.3.8 transitivePeerDependencies: - supports-color @@ -8401,52 +8404,64 @@ packages: resolution: {integrity: sha512-wKnm6XpJgzMUSRFB7YF48CuwdzuDIHenVuoIb1PLuJ6F+uErZsuDkU+EiExkChf6473XcawBrSfDSnXl+/YG4g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.4.2 - '@types/node': 18.13.0 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 + chalk: 4.1.2 + ci-info: 3.7.1 + graceful-fs: 4.2.10 + picomatch: 2.3.1 + dev: true + + /jest-util/29.4.3: + resolution: {integrity: sha512-ToSGORAz4SSSoqxDSylWX8JzkOQR7zoBtNRsA7e+1WUX5F8jrOwaNpuh1YfJHJKDHXLHmObv5eOjejUd+/Ws+Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.4.3 + '@types/node': 18.14.0 chalk: 4.1.2 ci-info: 3.7.1 graceful-fs: 4.2.10 picomatch: 2.3.1 dev: true - /jest-validate/29.4.2: - resolution: {integrity: sha512-tto7YKGPJyFbhcKhIDFq8B5od+eVWD/ySZ9Tvcp/NGCvYA4RQbuzhbwYWtIjMT5W5zA2W0eBJwu4HVw34d5G6Q==} + /jest-validate/29.4.3: + resolution: {integrity: sha512-J3u5v7aPQoXPzaar6GndAVhdQcZr/3osWSgTeKg5v574I9ybX/dTyH0AJFb5XgXIB7faVhf+rS7t4p3lL9qFaw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.4.2 + '@jest/types': 29.4.3 camelcase: 6.3.0 chalk: 4.1.2 - jest-get-type: 29.4.2 + jest-get-type: 29.4.3 leven: 3.1.0 - pretty-format: 29.4.2 + pretty-format: 29.4.3 dev: true - /jest-watcher/29.4.2: - resolution: {integrity: sha512-onddLujSoGiMJt+tKutehIidABa175i/Ays+QvKxCqBwp7fvxP3ZhKsrIdOodt71dKxqk4sc0LN41mWLGIK44w==} + /jest-watcher/29.4.3: + resolution: {integrity: sha512-zwlXH3DN3iksoIZNk73etl1HzKyi5FuQdYLnkQKm5BW4n8HpoG59xSwpVdFrnh60iRRaRBGw0gcymIxjJENPcA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/test-result': 29.4.2 - '@jest/types': 29.4.2 - '@types/node': 18.13.0 + '@jest/test-result': 29.4.3 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 - jest-util: 29.4.2 + jest-util: 29.4.3 string-length: 4.0.2 dev: true - /jest-worker/29.4.2: - resolution: {integrity: sha512-VIuZA2hZmFyRbchsUCHEehoSf2HEl0YVF8SDJqtPnKorAaBuh42V8QsLnde0XP5F6TyCynGPEGgBOn3Fc+wZGw==} + /jest-worker/29.4.3: + resolution: {integrity: sha512-GLHN/GTAAMEy5BFdvpUfzr9Dr80zQqBrh0fz1mtRMe05hqP45+HfQltu7oTBfduD0UeZs09d+maFtFYAXFWvAA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 18.13.0 - jest-util: 29.4.2 + '@types/node': 18.14.0 + jest-util: 29.4.3 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest/29.4.2_@types+node@18.13.0: - resolution: {integrity: sha512-+5hLd260vNIHu+7ZgMIooSpKl7Jp5pHKb51e73AJU3owd5dEo/RfVwHbA/na3C/eozrt3hJOLGf96c7EWwIAzg==} + /jest/29.4.3_@types+node@18.14.0: + resolution: {integrity: sha512-XvK65feuEFGZT8OO0fB/QAQS+LGHvQpaadkH5p47/j3Ocqq3xf2pK9R+G0GzgfuhXVxEv76qCOOcMb5efLk6PA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true peerDependencies: @@ -8455,10 +8470,10 @@ packages: node-notifier: optional: true dependencies: - '@jest/core': 29.4.2 - '@jest/types': 29.4.2 + '@jest/core': 29.4.3 + '@jest/types': 29.4.3 import-local: 3.1.0 - jest-cli: 29.4.2_@types+node@18.13.0 + jest-cli: 29.4.3_@types+node@18.14.0 transitivePeerDependencies: - '@types/node' - supports-color @@ -8511,7 +8526,6 @@ packages: /js-yaml/3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true dependencies: argparse: 1.0.10 esprima: 4.0.1 @@ -8519,7 +8533,6 @@ packages: /js-yaml/3.7.0: resolution: {integrity: sha512-eIlkGty7HGmntbV6P/ZlAsoncFLGsNoM27lkTzS+oneY/EiNhj+geqD9ezg/ip+SW6Var0BJU2JtV0vEUZpWVQ==} - hasBin: true dependencies: argparse: 1.0.10 esprima: 2.7.3 @@ -8527,7 +8540,6 @@ packages: /js-yaml/4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true dependencies: argparse: 2.0.1 @@ -8576,7 +8588,7 @@ packages: whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 whatwg-url: 11.0.0 - ws: 8.12.0 + ws: 8.12.1 xml-name-validator: 4.0.0 transitivePeerDependencies: - bufferutil @@ -8587,11 +8599,11 @@ packages: /jsesc/2.5.2: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} - hasBin: true dev: true /json-buffer/3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: false /json-parse-even-better-errors/2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} @@ -8614,7 +8626,6 @@ packages: /json5/1.0.1: resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==} - hasBin: true dependencies: minimist: 1.2.7 dev: true @@ -8622,7 +8633,6 @@ packages: /json5/2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} - hasBin: true /jsonc-parser/3.2.0: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} @@ -8686,6 +8696,10 @@ packages: resolution: {integrity: sha512-emiQ05haY9CRj1Ho/LiuCqr/+8RgJuWdiHYNglIg2Qjfz0n+pnUq9I2QHplXuOMO2EnAW1oCGC1++aU5VoWSlw==} dev: false + /jssha/3.3.0: + resolution: {integrity: sha512-w9OtT4ALL+fbbwG3gw7erAO0jvS5nfvrukGPMWIAoea359B26ALXGpzy4YJSp9yGnpUvuvOw1nSjSoHDfWSr1w==} + dev: false + /jstransformer/1.0.0: resolution: {integrity: sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==} dependencies: @@ -8716,6 +8730,7 @@ packages: resolution: {integrity: sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==} dependencies: json-buffer: 3.0.1 + dev: false /kind-of/3.2.2: resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} @@ -8970,7 +8985,7 @@ packages: /lowercase-keys/2.0.0: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} - dev: true + dev: false /lowercase-keys/3.0.0: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} @@ -8982,7 +8997,7 @@ packages: dependencies: pseudomap: 1.0.2 yallist: 2.1.2 - dev: true + dev: false /lru-cache/5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -9090,8 +9105,8 @@ packages: resolution: {integrity: sha512-4vRUvPyxdO8cWULGTh9dZWL2tZK6LDBvj+OGHBER7poH9Qdt7kXEoj20wiz4lQUbUXQZFjPbe5mVDo9nutizCw==} dev: false - /matter-js/0.18.0: - resolution: {integrity: sha512-/ZVem4WygUnbmo/iE4oHZpZS97btfBtYy5Iwn1396vUZU7YhgVEN8J4UWwfZwY1ZqoTYlPgjvSw9WXauuXL0mg==} + /matter-js/0.19.0: + resolution: {integrity: sha512-v2huwvQGOHTGOkMqtHd2hercCG3f6QAObTisPPHg8TZqq2lz7eIY/5i/5YUV8Ibf3mEioFEmwibcPUF2/fnKKQ==} dev: false /merge-stream/2.0.0: @@ -9148,7 +9163,6 @@ packages: /mime/3.0.0: resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} engines: {node: '>=10.0.0'} - hasBin: true dev: false /mimic-fn/2.1.0: @@ -9163,11 +9177,12 @@ packages: /mimic-response/1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} - dev: true + dev: false /mimic-response/3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} + dev: false /mimic-response/4.0.0: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} @@ -9189,6 +9204,13 @@ packages: dependencies: brace-expansion: 2.0.1 + /minimatch/6.2.0: + resolution: {integrity: sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimist/1.2.7: resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} @@ -9290,7 +9312,6 @@ packages: /mkdirp/0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true dependencies: minimist: 1.2.7 dev: false @@ -9298,7 +9319,6 @@ packages: /mkdirp/1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} - hasBin: true dev: false /mnemonist/0.39.5: @@ -9324,7 +9344,6 @@ packages: /msgpackr-extract/2.2.0: resolution: {integrity: sha512-0YcvWSv7ZOGl9Od6Y5iJ3XnPww8O7WLcpYMDwX+PAA/uXLDtyw94PJv9GLQV/nnp3cWlDhMoyKZIQLrx33sWog==} - hasBin: true requiresBuild: true dependencies: node-gyp-build-optional-packages: 5.0.3 @@ -9342,8 +9361,8 @@ packages: optionalDependencies: msgpackr-extract: 2.2.0 - /muggle-string/0.1.0: - resolution: {integrity: sha512-Tr1knR3d2mKvvWthlk7202rywKbiOm4rVFLsfAaSIhJ6dt9o47W4S+JMtWhd/PW9Wrdew2/S2fSvhz3E2gkfEg==} + /muggle-string/0.2.2: + resolution: {integrity: sha512-YVE1mIJ4VpUMqZObFndk9CJu6DBJR/GB13p3tXuNbwD4XExaI5EOuRl6BHeIDxIqXZVxSfAC+y6U1Z/IxCfKUg==} dev: true /multi-integer-range/3.0.0: @@ -9375,7 +9394,6 @@ packages: /nanoid/3.3.4: resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true /nanomatch/1.2.13: resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==} @@ -9438,7 +9456,6 @@ packages: /needle/2.9.1: resolution: {integrity: sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==} engines: {node: '>= 4.4.x'} - hasBin: true dependencies: debug: 3.2.7 iconv-lite: 0.4.24 @@ -9506,18 +9523,15 @@ packages: /node-gyp-build-optional-packages/5.0.3: resolution: {integrity: sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==} - hasBin: true optional: true /node-gyp-build/4.6.0: resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==} - hasBin: true dev: false /node-gyp/9.3.1: resolution: {integrity: sha512-4Q16ZCqq3g8awk6UplT7AuxQ35XN4R/yf/+wSAwcBUAjg7l58RTactWaP8fIDTi0FzI7YcVLujwExakZlfWkXg==} engines: {node: ^12.13 || ^14.13 || >=16} - hasBin: true dependencies: env-paths: 2.2.1 glob: 7.2.3 @@ -9554,7 +9568,6 @@ packages: /nopt/5.0.0: resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} engines: {node: '>=6'} - hasBin: true dependencies: abbrev: 1.1.1 dev: false @@ -9563,7 +9576,6 @@ packages: /nopt/6.0.0: resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - hasBin: true dependencies: abbrev: 1.1.1 dev: false @@ -9606,7 +9618,7 @@ packages: /normalize-url/6.1.0: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} - dev: true + dev: false /normalize-url/8.0.0: resolution: {integrity: sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==} @@ -9625,7 +9637,7 @@ packages: engines: {node: '>=4'} dependencies: path-key: 2.0.1 - dev: true + dev: false /npm-run-path/4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} @@ -9815,7 +9827,6 @@ packages: /opentype.js/0.4.11: resolution: {integrity: sha512-GthxucX/6aftfLdeU5Ho7o7zmQcC8uVtqdcelVq12X++ndxwBZG8Xb5rFEKT7nEcWDD2P1x+TNuJ70jtj1Mbpw==} - hasBin: true dev: false /optionator/0.8.3: @@ -9853,7 +9864,7 @@ packages: engines: {node: '>=4'} dependencies: arch: 2.2.0 - dev: true + dev: false /os-locale/1.4.0: resolution: {integrity: sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==} @@ -9870,10 +9881,16 @@ packages: resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==} dev: true + /otpauth/9.0.2: + resolution: {integrity: sha512-0TzpkJYg24VvIK3/K91HKpTtMlwm73UoThhcGY8fZsXcwHDrqf008rfdOjj3NnQuyuT11+vHyyO//qRzi6OZ9A==} + dependencies: + jssha: 3.3.0 + dev: false + /p-cancelable/2.1.1: resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} engines: {node: '>=8'} - dev: true + dev: false /p-cancelable/3.0.0: resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} @@ -9883,6 +9900,7 @@ packages: /p-finally/1.0.0: resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} engines: {node: '>=4'} + dev: false /p-limit/2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} @@ -10049,7 +10067,7 @@ packages: /path-key/2.0.1: resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} engines: {node: '>=4'} - dev: true + dev: false /path-key/3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} @@ -10101,6 +10119,7 @@ packages: /peek-readable/5.0.0: resolution: {integrity: sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==} engines: {node: '>=14.16'} + dev: false /pend/1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -10208,7 +10227,6 @@ packages: /pino/8.8.0: resolution: {integrity: sha512-cF8iGYeu2ODg2gIwgAHcPrtR63ILJz3f7gkogaHC/TXVVXxZgInmNYiIpDYEwgEkxZti2Se6P2W2DxlBIZe6eQ==} - hasBin: true dependencies: atomic-sleep: 1.0.0 fast-redact: 3.1.2 @@ -10524,7 +10542,6 @@ packages: /prebuild-install/7.1.1: resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} engines: {node: '>=10'} - hasBin: true dependencies: detect-libc: 2.0.1 expand-template: 2.0.3 @@ -10569,11 +10586,11 @@ packages: react-is: 18.2.0 dev: true - /pretty-format/29.4.2: - resolution: {integrity: sha512-qKlHR8yFVCbcEWba0H0TOC8dnLlO4vPlyEjRPw31FZ2Rupy9nLa8ZLbYny8gWEl8CkEhJqAE6IzdNELTBVcBEg==} + /pretty-format/29.4.3: + resolution: {integrity: sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/schemas': 29.4.2 + '@jest/schemas': 29.4.3 ansi-styles: 5.2.0 react-is: 18.2.0 dev: true @@ -10685,14 +10702,13 @@ packages: /ps-tree/1.2.0: resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} engines: {node: '>= 0.10'} - hasBin: true dependencies: event-stream: 3.3.4 dev: true /pseudomap/1.0.2: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} - dev: true + dev: false /psl/1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} @@ -10834,7 +10850,6 @@ packages: /qrcode/1.5.1: resolution: {integrity: sha512-nS8NJ1Z3md8uTjKtP+SGGhfqmTCs5flU/xR623oI0JX+Wepz9R8UrRVCTBTJm3qGw3rH6jJ6MUHjkDx15cxSSg==} engines: {node: '>=10.13.0'} - hasBin: true dependencies: dijkstrajs: 1.0.2 encode-utf8: 1.0.3 @@ -10865,13 +10880,11 @@ packages: /querystring/0.2.0: resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==} engines: {node: '>=0.4.x'} - deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. dev: false /querystring/0.2.1: resolution: {integrity: sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==} engines: {node: '>=0.4.x'} - deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. dev: false /querystringify/2.2.0: @@ -10892,6 +10905,7 @@ packages: /quick-lru/5.1.1: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} + dev: false /random-seed/0.3.0: resolution: {integrity: sha512-y13xtn3kcTlLub3HKWXxJNeC2qK4mB59evwZ5EkeRlolx+Bp2ztF7LbcZmyCnOqlHQrLnfuNbi1sVmm9lPDlDA==} @@ -10914,7 +10928,6 @@ packages: /rc/1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true dependencies: deep-extend: 0.6.0 ini: 1.3.8 @@ -10990,6 +11003,7 @@ packages: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 + dev: false /readable-stream/4.3.0: resolution: {integrity: sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==} @@ -11006,6 +11020,7 @@ packages: engines: {node: '>=8'} dependencies: readable-stream: 3.6.0 + dev: false /readdir-glob/1.1.2: resolution: {integrity: sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA==} @@ -11185,7 +11200,6 @@ packages: /request/2.88.2: resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==} engines: {node: '>= 6'} - deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 dependencies: aws-sign2: 0.7.0 aws4: 1.12.0 @@ -11231,6 +11245,7 @@ packages: /resolve-alpn/1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + dev: false /resolve-cwd/3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} @@ -11266,7 +11281,6 @@ packages: /resolve-url/0.2.1: resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==} - deprecated: https://github.com/lydell/resolve-url#deprecated dev: false /resolve.exports/2.0.0: @@ -11276,7 +11290,6 @@ packages: /resolve/1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} - hasBin: true dependencies: is-core-module: 2.11.0 path-parse: 1.0.7 @@ -11286,7 +11299,7 @@ packages: resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} dependencies: lowercase-keys: 2.0.0 - dev: true + dev: false /responselike/3.0.0: resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} @@ -11327,14 +11340,12 @@ packages: /rimraf/2.7.1: resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - hasBin: true dependencies: glob: 7.2.3 dev: false /rimraf/3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - hasBin: true dependencies: glob: 7.2.3 @@ -11345,10 +11356,9 @@ packages: seedrandom: 2.4.2 dev: false - /rollup/3.14.0: - resolution: {integrity: sha512-o23sdgCLcLSe3zIplT9nQ1+r97okuaiR+vmAPZPTDYB7/f3tgWIYNyiQveMsZwshBT0is4eGax/HH83Q7CG+/Q==} + /rollup/3.17.2: + resolution: {integrity: sha512-qMNZdlQPCkWodrAZ3qnJtvCAl4vpQ8q77uEujVCCbC/6CLB7Lcmvjq7HyiOSnf4fxTT9XgsE36oLHJBH49xjqA==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} - hasBin: true optionalDependencies: fsevents: 2.3.2 dev: false @@ -11413,8 +11423,8 @@ packages: /safer-buffer/2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - /sanitize-html/2.9.0: - resolution: {integrity: sha512-KY1hpSbqFNcpoLf+nP7iStbP5JfQZ2Nd19ZEE7qFsQqRdp+sO5yX/e5+HoG9puFAcSTEpzQuihfKUltDcLlQjg==} + /sanitize-html/2.10.0: + resolution: {integrity: sha512-JqdovUd81dG4k87vZt6uA6YhDfWkUGruUu/aPmXLxXi45gZExnt9Bnw/qeQU8oGf82vPyaE0vO4aH0PbobB9JQ==} dependencies: deepmerge: 4.2.2 escape-string-regexp: 4.0.0 @@ -11424,10 +11434,9 @@ packages: postcss: 8.4.21 dev: false - /sass/1.58.0: - resolution: {integrity: sha512-PiMJcP33DdKtZ/1jSjjqVIKihoDc6yWmYr9K/4r3fVVIEDAluD0q7XZiRKrNJcPK3qkLRF/79DND1H5q1LBjgg==} + /sass/1.58.3: + resolution: {integrity: sha512-Q7RaEtYf6BflYrQ+buPudKR26/lH+10EmO9bBqbmPh/KeLqv8bjpTNqxe71ocONqXq+jYiCbpPUmQMS+JJPk4A==} engines: {node: '>=12.0.0'} - hasBin: true dependencies: chokidar: 3.5.3 immutable: 4.2.2 @@ -11471,28 +11480,25 @@ packages: /semver-regex/4.0.5: resolution: {integrity: sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==} engines: {node: '>=12'} - dev: true + dev: false /semver-truncate/2.0.0: resolution: {integrity: sha512-Rh266MLDYNeML5h90ttdMwfXe1+Nc4LAWd9X1KdJe8pPHP4kFmvLZALtsMNHNdvTyQygbEC0D59sIz47DIaq8w==} engines: {node: '>=8'} dependencies: semver: 6.3.0 - dev: true + dev: false /semver/5.7.1: resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} - hasBin: true dev: false /semver/6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} - hasBin: true /semver/7.3.8: resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} engines: {node: '>=10'} - hasBin: true dependencies: lru-cache: 6.0.0 @@ -11524,7 +11530,6 @@ packages: /sha.js/2.4.11: resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} - hasBin: true dependencies: inherits: 2.0.4 safe-buffer: 5.2.1 @@ -11550,7 +11555,7 @@ packages: engines: {node: '>=0.10.0'} dependencies: shebang-regex: 1.0.0 - dev: true + dev: false /shebang-command/2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} @@ -11561,7 +11566,7 @@ packages: /shebang-regex/1.0.0: resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} engines: {node: '>=0.10.0'} - dev: true + dev: false /shebang-regex/3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} @@ -11694,13 +11699,14 @@ packages: engines: {node: '>=0.10.0'} dependencies: sort-keys: 1.1.2 - dev: true + dev: false /sort-keys/1.1.2: resolution: {integrity: sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==} engines: {node: '>=0.10.0'} dependencies: is-plain-obj: 1.1.0 + dev: false /sortablejs/1.14.0: resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==} @@ -11712,7 +11718,6 @@ packages: /source-map-resolve/0.5.3: resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==} - deprecated: See https://github.com/lydell/source-map-resolve#deprecated dependencies: atob: 2.1.2 decode-uri-component: 0.2.2 @@ -11737,7 +11742,6 @@ packages: /source-map-url/0.4.1: resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==} - deprecated: See https://github.com/lydell/source-map-url#deprecated dev: false /source-map/0.5.7: @@ -11752,11 +11756,10 @@ packages: /source-map/0.7.4: resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} engines: {node: '>= 8'} - dev: true + dev: false /sourcemap-codec/1.4.8: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} - deprecated: Please use @jridgewell/sourcemap-codec instead /sparkles/1.0.1: resolution: {integrity: sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==} @@ -11785,13 +11788,6 @@ packages: resolution: {integrity: sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==} dev: false - /speakeasy/2.0.0: - resolution: {integrity: sha512-lW2A2s5LKi8rwu77ewisuUOtlCydF/hmQSOJjpTqTj1gZLkNgTaYnyvfxy2WBr4T/h+9c4g8HIITfj83OkFQFw==} - engines: {node: '>= 0.10.0'} - dependencies: - base32.js: 0.0.1 - dev: false - /split-string/3.1.0: resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} engines: {node: '>=0.10.0'} @@ -11820,7 +11816,6 @@ packages: /sshpk/1.17.0: resolution: {integrity: sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==} engines: {node: '>=0.10.0'} - hasBin: true dependencies: asn1: 0.2.6 assert-plus: 1.0.0 @@ -11853,10 +11848,9 @@ packages: /standard-as-callback/2.1.0: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} - /start-server-and-test/1.15.3: - resolution: {integrity: sha512-4GqkqghvUR9cJ8buvtgkyT0AHgVwCJ5EN8eDEhe9grTChGwWUxGm2nqfSeE9+0PZkLRdFqcwTwxVHe1y3ViutQ==} + /start-server-and-test/1.15.4: + resolution: {integrity: sha512-ucQtp5+UCr0m4aHlY+aEV2JSYNTiMZKdSKK/bsIr6AlmwAWDYDnV7uGlWWEtWa7T4XvRI5cPYcPcQgeLqpz+Tg==} engines: {node: '>=6'} - hasBin: true dependencies: arg: 5.0.2 bluebird: 3.7.2 @@ -11978,6 +11972,7 @@ packages: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: safe-buffer: 5.2.1 + dev: false /stringz/2.1.0: resolution: {integrity: sha512-KlywLT+MZ+v0IRepfMxRtnSvDCMc3nR1qqCs3m/qIbSOWkNZYT8XHQA31rS3TnKp0c5xjZu3M4GY/2aRKSi/6A==} @@ -12017,7 +12012,7 @@ packages: /strip-eof/1.0.0: resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} engines: {node: '>=0.10.0'} - dev: true + dev: false /strip-final-newline/2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} @@ -12041,7 +12036,7 @@ packages: /strip-outer/2.0.0: resolution: {integrity: sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + dev: false /strnum/1.0.5: resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} @@ -12053,6 +12048,7 @@ packages: dependencies: '@tokenizer/token': 0.3.0 peek-readable: 5.0.0 + dev: false /supports-color/2.0.0: resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} @@ -12100,8 +12096,6 @@ packages: /svgo/0.7.2: resolution: {integrity: sha512-jT/g9FFMoe9lu2IT6HtAxTA7RR2XOrmcrmCtGnyB/+GQnV6ZjNn+KOHZbZ35yL81+1F/aB6OeEsJztzBQ2EEwA==} engines: {node: '>=0.10.0'} - deprecated: This SVGO version is no longer supported. Upgrade to v2.x.x. - hasBin: true dependencies: coa: 1.0.4 colors: 1.1.2 @@ -12116,11 +12110,10 @@ packages: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} dev: false - /systeminformation/5.17.8: - resolution: {integrity: sha512-kMQsCOFQXqEjoDWKpYkC+ezJImrOnyTL3FZnYDZdWLTZcMW7Gomdp8zH9ztGlSo2L1iCAQXZ8HlV1bI8/Ts64g==} + /systeminformation/5.17.9: + resolution: {integrity: sha512-inxwRLI/4qpx4o85R54/zdhNagdBGBgs0la7Vl3qBorRVKRDk0nNsDTCGzG4lOITsw1gl7LRWeG4Zsp1pC8nfg==} engines: {node: '>=8.0.0'} os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] - hasBin: true dev: false /syuilo-password-strength/0.0.1: @@ -12176,7 +12169,6 @@ packages: /terser/5.16.1: resolution: {integrity: sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==} engines: {node: '>=10'} - hasBin: true dependencies: '@jridgewell/source-map': 0.3.2 acorn: 8.8.1 @@ -12358,6 +12350,7 @@ packages: dependencies: '@tokenizer/token': 0.3.0 ieee754: 1.2.1 + dev: false /tough-cookie/2.5.0: resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} @@ -12399,11 +12392,10 @@ packages: engines: {node: '>=12'} dependencies: escape-string-regexp: 5.0.0 - dev: true + dev: false /tsc-alias/1.8.2: resolution: {integrity: sha512-ukBkcNekOgwtnSWYLD5QsMX3yQWg7JviAs8zg3qJGgu4LGtY3tsV4G6vnqvOXIDkbC+XL9vbhObWSpRA5/6wbg==} - hasBin: true dependencies: chokidar: 3.5.3 commander: 9.5.0 @@ -12594,7 +12586,6 @@ packages: /typescript/4.9.5: resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} engines: {node: '>=4.2.0'} - hasBin: true /uid/2.0.1: resolution: {integrity: sha512-PF+1AnZgycpAIEmNtjxGBVmKbZAQguaa4pBUq6KNaGEcpzZ2klCNZLM34tsjp76maN00TttiiUf6zkIBpJQm2A==} @@ -12605,7 +12596,6 @@ packages: /ulid/2.3.0: resolution: {integrity: sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==} - hasBin: true dev: false /unbox-primitive/1.0.2: @@ -12754,7 +12744,6 @@ packages: /urix/0.1.0: resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==} - deprecated: Please see https://github.com/lydell/urix#deprecated dev: false /url-parse/1.5.10: @@ -12807,22 +12796,17 @@ packages: /uuid/3.4.0: resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} - deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. - hasBin: true dev: false /uuid/8.0.0: resolution: {integrity: sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==} - hasBin: true dev: false /uuid/8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true /uuid/9.0.0: resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} - hasBin: true dev: false /v8-to-istanbul/9.0.1: @@ -12928,8 +12912,8 @@ packages: replace-ext: 1.0.1 dev: false - /vite/4.1.1_gyrp4zacqcjjrmgvdzgac5epyy: - resolution: {integrity: sha512-LM9WWea8vsxhr782r9ntg+bhSFS06FJgCvvB0+8hf8UWtvaiDagKYWXndjfX6kGl74keHJUcpzrQliDXZlF5yg==} + /vite/4.1.2_hlkwzk2izwsolfmdrejei4vrty: + resolution: {integrity: sha512-MWDb9Rfy3DI8omDQySbMK93nQqStwbsQWejXRY2EBzEWKmLAXWb1mkI9Yw2IJrc+oCvPCI1Os5xSSIBYY6DEAw==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true peerDependencies: @@ -12953,12 +12937,12 @@ packages: terser: optional: true dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 esbuild: 0.16.17 postcss: 8.4.21 resolve: 1.22.1 - rollup: 3.14.0 - sass: 1.58.0 + rollup: 3.17.2 + sass: 1.58.3 optionalDependencies: fsevents: 2.3.2 dev: false @@ -12968,14 +12952,14 @@ packages: engines: {node: '>=0.10.0'} dev: false - /vue-eslint-parser/9.1.0_eslint@8.33.0: + /vue-eslint-parser/9.1.0_eslint@8.34.0: resolution: {integrity: sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: '>=6.0.0' dependencies: debug: 4.3.4 - eslint: 8.33.0 + eslint: 8.34.0 eslint-scope: 7.1.1 eslint-visitor-keys: 3.3.0 espree: 9.4.1 @@ -13009,14 +12993,14 @@ packages: he: 1.2.0 dev: true - /vue-tsc/1.0.24_typescript@4.9.5: - resolution: {integrity: sha512-mmU1s5SAqE1nByQAiQnao9oU4vX+mSdsgI8H57SfKH6UVzq/jP9+Dbi2GaV+0b4Cn361d2ln8m6xeU60ApiEXg==} + /vue-tsc/1.1.4_typescript@4.9.5: + resolution: {integrity: sha512-CMG8KZsBBPyulYie05XxJCfq/yAPiB/uMMeHmog09aLm2Kml82C6tUSRgQz8ujM4JoCrpDqVCd9G0NuM9aLt1g==} hasBin: true peerDependencies: typescript: '*' dependencies: - '@volar/vue-language-core': 1.0.24 - '@volar/vue-typescript': 1.0.24 + '@volar/vue-language-core': 1.1.4 + '@volar/vue-typescript': 1.1.4 typescript: 4.9.5 dev: true @@ -13056,7 +13040,6 @@ packages: /wait-on/7.0.1_debug@4.3.4: resolution: {integrity: sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog==} engines: {node: '>=12.0.0'} - hasBin: true dependencies: axios: 0.27.2_debug@4.3.4 joi: 17.7.0 @@ -13076,7 +13059,6 @@ packages: /web-push/3.5.0: resolution: {integrity: sha512-JC0V9hzKTqlDYJ+LTZUXtW7B175qwwaqzbbMSWDxHWxZvd3xY0C2rcotMGDavub2nAAFw+sXTsqR65/KY2A5AQ==} engines: {node: '>= 6'} - hasBin: true dependencies: asn1.js: 5.4.1 http_ece: 1.1.0 @@ -13177,14 +13159,13 @@ packages: /which/1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true dependencies: isexe: 2.0.0 + dev: false /which/2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} - hasBin: true dependencies: isexe: 2.0.0 @@ -13243,8 +13224,8 @@ packages: signal-exit: 3.0.7 dev: true - /ws/8.12.0: - resolution: {integrity: sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==} + /ws/8.12.1: + resolution: {integrity: sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -13262,7 +13243,6 @@ packages: /xml-js/1.6.11: resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==} - hasBin: true dependencies: sax: 1.2.4 dev: false @@ -13323,7 +13303,7 @@ packages: /yallist/2.1.2: resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} - dev: true + dev: false /yallist/3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -13445,8 +13425,8 @@ packages: version: 2.2.1-misskey.3 dev: false - github.com/misskey-dev/summaly/5684f116c92f1fd122badc7aee062494bdb43b36: - resolution: {tarball: https://codeload.github.com/misskey-dev/summaly/tar.gz/5684f116c92f1fd122badc7aee062494bdb43b36} + github.com/misskey-dev/summaly/51f3870e1ff5e0b22102e804112b10cb72f3c494: + resolution: {tarball: https://codeload.github.com/misskey-dev/summaly/tar.gz/51f3870e1ff5e0b22102e804112b10cb72f3c494} name: summaly version: 3.0.4 dependencies: |